Metronäyttö

Kuvia:
Videoita:
Infoa:
Lokia:
Disasm flowchart, ISO, 5355x7578 px:
Emulaattorilla kaapatut IO-porttitapahtumat (Dezgeg 29.03):
LCD-väylän formaatti:
Emulaattorin tulosteesta sai reverse-engineerattua väyläformaatin.
Sanomalla esim. sort -u 3-ulam-hog.txt | grep 'out' saa kaikki ohjelmassa käytetyt PC-portti-arvo-kolmikot, joissa on tapahtunut IO-kirjoitus. Tuosta voi sitten päätellä esim. että ohjelman kohdassa PC = 4036 on kirjoitettu porttiin vaan ykkösbittiä. Tuolla tavoin saa filtteröityä pois kaikki ei-databittikirjoitukset.

Databitit saa siis eristettyä sanomalla "cat 3-ulam-hog.txt | egrep '408a|409b|40b4|40c5'".
Yhteen normi-riviin (eli vaikkapa bootin ULAM-HOG) menee 2208 = 4 * 24 * 23 bittiä.
Yhdessä paneelissahan on siis 24 saraketta, sarakkeessa 23 erimuotoista kolmiopikseliä, ja normirivillähän on se 4 paneelia. Kuvasta http://www.cs.helsinki.fi/u/tmtynkky/boot-io-traces/bittijarjestys.jpg ilmenee pikseleiden muodot, joskaan suhteet eivät ole ihan oikein.

Ohjausta varten on jokainen sarake jaettu keskeltä kahtia, joten bittivirta on seuraavanlainen:
  1. oikealta vasemmalle jokaisen sarakkeen (5 * 24) 12 ylintä pikseliä
  2. vasemmalta oikealle jokaisen sarakkeen (5 * 24) 11 alinta pikseliä
  Edellä mainitussa kuvassa on numeroitu mikä bitti vastaa mitäkin pikseliä sarakkeessa.
  
  http://www.cs.helsinki.fi/u/tmtynkky/boot-io-traces/printline_decoder.py on ohjelma,
  jolla voi dekoodata bittivirran kuvaksi. Sieltä voi myös viime kädessä lukea miten tuo 
  formaatti menee, mikäli selityksestä ei saanut selvää :). Screenshot:
  http://www.cs.helsinki.fi/u/tmtynkky/boot-io-traces/screeshot.png
  Vaatii pygamen, ohjeet kommenteissa. 

Fonttidata:
Yleisesti fontista: merkin maksimileveys on 5 saraketta.
Jokaisesta merkistä voi olla useita erileveyksisiä variantteja.
Piirtokoodi valitsee fontista sellaiset variaatiot sekä merkkien välisen tyhjän tilan,
että teksti mahtuu näytölle mahdollisimman kauniisti.
Videosta http://www.youtube.com/watch?v=HZdKBqu0UWA voi nähdä, että
esimerkiksi 'n' - kirjain esiintyy kolmella eri leveydellä.
Merkkien kaventaminen tapahtuu jättämällä tietyt sarakkeet piirtämättä kokonaan.

Osoitteessa 0xD800 on fonttitaulukko. Joka merkille on 24 tavua, indeksoidaan yksinkertaisesti ascii-koodilla. Jokaista merkkiä varten on seuraavanlainen structi:
- offset 0: merkin ASCII-koodi
- offset 1: seuraavanlainen bittikenttä:
        - 6: onko merkkille fonttidataa vai ei
        - 0-2 ja 3-5: bittimaski välistystä (kerning) varten.
                          Toinen merkin vasenta puolta ja toinen oikeaa puolta varten.
                          (Ei varmaa, että kumpi on kumpi).
                          Kahden merkin välinen tyhjä sarake voidaan jättää pois,
                          mikäli binäärinen AND vasemman merkin oikean puolen bittimaskin
                          ja oikean merkin vasemman puolen bittimaskin kanssa on 0
- offset 2-3: Tuntematon 16-bittinen sana.
                  Ainoastaan alemman tavun ylemmällä nybblellä merkitys.
- offset 4-13: 5 16-bittistä sanaa, yksi jokaista merkin saraketta kohti.
                    Ylemmät 4 bittiä sisältävät metadataa,
                    joka kertoo mitkä sarakkeet voidaan tarvittaessa jättää piirtämättä.
                    Tarkka formaatti tuntematon.
                    Alemmat 12 bittiä kertoo mitkä pikselit ovat päällä kyseisen
                    sarakkeen yläpuoliskossa (ks. yllä lcd-väylän kuvauksesta)
- offset 14-23: 5 16-bittistä sanaa joka saraketta kohti.
                     Kertoo vastaavasti kunkin sarakkeen alapuoliskon päällä
                     olevat pikselit.

Tämä voitaisin kirjoittaa puhtaaksi yhdessä jossain välissä:

Reverse engineering: 

Kaikenlaista noista soodan keräämistä tiedoistakin irtoaa. Tällaisia päätelmiä ja arvauksia hardwaresta, näitä voisi todistella oikeiksi tai vääriksi todellisen hw:n äärellä:

Kortilla ei ole muuta EEPROMia kuin 28C64, nuo pienet piirit ovat TTL-PROMmeja (kertaohjelmoitava fuse-PROM). Isompi PROM näköjään sisältää oletustekstit. Pienempi tekee osoitteen dekoodauksen.

      Muistiavaruuden osoitekartta:
      0000H...7FFFH -> EPROM  (32 kB)
      8000H...BFFFH -> RAM    (blokki 16 kB, piiri 8 kB)
      C000H...DFFFH -> EEPROM (8 kB)
      E000H...FFFFH -> kalustamaton paikka (8 kB)

      I/O-avaruus on dekoodattu HCT138:lla. Piirien osoitteet ja ohjelman tekemät asetukset:
      10H...1FH = UART 1
          10H = data
          11H = control  (7-E-1, 16x baud, RTS on, RXEN, TXEN)
          Skoopilla mitaten tämä on 600 baud
          Menee modeemille, joka on toisella kortilla
      20H...2FH = UART 2
          20H = data
          21H = control (8-N-1, 16x baud, RTS on, RXEN, TXEN)
          9600 baudilla näkyy pc:llä juttuja
      30H...3FH = 8255
          30H = PA
          31H = PB
          32H = PC
          33H = mode (PA out, PB out, PC in)
      40H...4FH = 8155
          40H = mode (PA in, PB out, PC out)
          41H = PA
          42H = PB
          43H = PC
          44H = timer hi
          45H = timer lo

8155:n PB ja PC hoitavat ilmeisesti näytöille lähtevää väylää. Ohjelmassa on useita pieniä funktioita, joilla voi pulssittaa yksittäisiä C-portin bittejä, lähettää annetun datan sarjamuodossa bitti kerrallaan + strobe -tyylillä jostain portin bitistä ulos, heittää PB-portista one-hot-tyyppisiä valintoja ulos jne. Vaatii tarkempaa kaivelua, mikä 8155:n pinni menee mihinkäkin väylällä.

UARTtien baudinopeus pitää mitata. Tehdään mahdollisesti 8155:n timerillä + jakajilla, voi tulla suoraan 8085:n systeemikellostakin hard-wired, koska jakajia on riittävästi (4020 = 14-stage).
- Luultavasti ei 8155:ltä, koska koodi resetoi laskurin arvon vakioksi tietyin välein.

t. jari

Toiselta PCB:ltä mielenkiintoisinta on MC14412 - "Universal Low Speed Modem (0-600 bps)" sekä UART-jännitetasomuuntimet. LTG-portti menee tälle piirilevylle, ja se oli ainoa näytön kontrolliboksin ulkopuolelle mennyt piuha, joten aikamoisella varmuudella tuo modeemi on yhteydessä UART1:een. Etupaneelin testpointeilla pitäisi päästä tuohon UART:iin kiinni suoraan.

Koodissa luetaan useasti turhaan 11h-porttia, siihen lienee yhdistettynä
jokin watchdog-toiminnallisuus?

Tämä herättää ihmetystä, ei pitäisi olla tuollaista ominaisuutta 8251:ssä, eikä muutenkaan vaadi jatkuvaa pollausta.Tuo portti on UART1:n status-tavu.

- Jep, muuta järjellistä en tuolle keksinyt. Ehkä ajamalla tuo WD-test selviää mitä tapahtuu
(sen testin lopussa on ikuinen luuppi).
- WD-test sisältää muutakin debugprintiä (mm. taikanumeroita ja fontin nimen, sekä lopuksi piirtää koko näytön täyteen mustaa), ja looppaa loputtuaan taas alusta (ainakin toisen kerran, en katsonut pidempään) -sooda
- => siellä ihan oikeasti on jossain joku watchdog-viritys

4020 on ainut sen tyyppinen piiri, josta voisi saada WD:n. Arvaus: UART1:n chip select on viety 4020:n resettiin, ja jos 4020 pääsee laskemaan tarpeeksi pitkälle, se resetoi prosessorin. -- Ei toimi, 4020:n reset on ylhäällä aktiivinen, ja kortilla ei ole inverttereitä.

Joku viritys siellä on, joka vaatii UART1:n statuksen lukemista vähän väliä (IN 11H). Ilman tätä tulee noutaja.


8155 PA (portti 41h) on mapattu DIP-switcheihin. Niillä on seuraava funktio:
    bit 7 -   testitila. Pitäisi tulostaa joka segmenttiin jotain kamaa, sekä lopuksi
               WD-test eli watchdog test.
    bit 4-6 - laitteen ID johon se reagoi UART1-väylällä
    bit 1-3 - lisää ID-bittejä
    bit 0 - poistaa käytöstä XOR-checksummin UART1-komentojen yhteydessä

Konsta taisi sanoa saaneensa 9600 baudilla dataa ulos RX/TX merkityillä pinneistä ("@I.@E-.@E+.@E+.@E+."), ja softa printtaileekin juuri tuommoista dataa UART2:een.
Mitään tuon ihmeempää ei UART2:sta sitten saakkaan, kaikki oikea info menee UART1:n päällä. Kuten ylempänä, UART1:n konffi ei ole se tavanomaisin, vaan 7 data-bittiä, 1 stop-bitti, ja even parity. Baudrate lienee sama.

Ok, hieman yksityiskohtaisemmin, UART2:lla voi tehdä kaksi asiaa.
- Bootissa tulee viesti "SPA-test N",
    missä N laskee kakkosesta nollaan. Heittämällä rivinvaihdon 0xC UART2:een
    tuon testin saa keskeytettyä ja jatkettua boottia.
-   E-komennon yhteydessä (alempana selitetty) saa UART2:een jonkun muuttujan
    tulosteen, jos sieltä löytyy tavu ':'.

UART1:n kommunikaatio on hyvin pitkälle ASCII-pohjaista. Eli esim. boolean-arvot kulkee väylällä ASCII-'0' ja ASCII-'1':nä, kuten kaikki muutkin lukuarvot.

UART1:stä luvussa on kolme vaihetta:
    - osoitettavan laitteen valinta
    - komentopuskurin lataus (0x2)
    - komennon suoritus (0x4)

Osoite muodostuu seuraavasti:
    - laitteen ID ASCII-numerona, eli '0'-'7'. Tämä tulee DIP-switchin biteistä 4-6
    - toinen ID ASCIIna (DIPin biteistä 1-3) LSL 1. Alin bitti kertoo nyt luetaanko(0) vaiko kirjoitetaanko (1) näytöltä/näytölle
        - esim. jos DIP[1:3] = '001' ja halutaan kirjoittaa näytölle, niin 2. tavuksi halutaan '3'
    - ASCII ENQ (0x5) päättää osoitevaiheen

Kirjoitustilassa laite jää odottelemaan ASCII STXää (0x2), jonka jälkeen luetaan komentoja terminoituna ASCII ETX:llä (0x3) sekä checksum-tavu.
Checksum on yksinkertainen XOR komentotavuista.
(ETX lasketaan checksummiin, STX ei, TOISIN KUIN ENNEN MAINITSIN)
Tämän checksum-tarkastuksen saa dip-switchin nollabitillä pois.

Komentopuskurin tavu #8 kertoo mitä tapahtuu:

'E':
    Valitsee mitä ruudulla näkyy, sisältö tulee 8kb EEPROMista

    Data bytes have the following meaning:
    #00: constant 0x2, the command byte
    #01: unknown, NOT A STRING VALUE AS WAS MENTIONED BEFORE!
    #02: unknown value related to clock+blinker. Compared to '0' and '1'.
    #03-#05 expected version number of the string ROM. If incorrect,
            error code '3' is set.
    ...
    #08: constant 'E', the command byte
    #09: Tenths digit of the minutes to train ETA (ASCII)
    #0a: Ones digit of the minutes to train ETA (ASCII)
    #0b: Unknown, not used?
    #0c: Animation frame of the blinker (ASCII number), 0 = empty
    #0d: HiByte of the first (finnish) text line's key.
    #0e: LoByte of the first (finnish) text line's key.
    #0f: HiByte of the second (swedish) text line's key.
    #10: LoByte of the second (swedish) text line's key.
    #11: Generate a debug message (@E+.) to UART2 if not '0'.
         Then, if UART2 contains ':', print some more (unknown) data to UART2

'D':
    This subcommand can modify the String EEPROM.
    Data bytes:
        - If bytes #01, #02, #06, #07 are all '0',
          all String EEPROM data WILL BE ERASED after the execute command (4)
        - Otherwise, a new key-value pair is appended to the string EEPROM.
          Bytes #01, #02, #06, #07 contain the four-byte key.
          All the bytes at #09 are copied as the String value,
          terminated by the ASCII ETX (3) that also ended the command buffer!.
          Then the ROM version is set to bytes #3-#5 and the version
          is checksummed. (See below for definition of ROM version)

'T':
    This prints a string!!
    Data bytes:
        #00: 0x2, the STX
        #01-#02: Segment parameter! (See later for definition)
        #06: Some display param? Checks for one of "sbn".
        #07: Positioning? Checks for one of "lrmz".
        #08: The command byte 'T'.
        #09: The string! Terminated by ASCII ETX (0x3), which also
               ended the command buffer!

'N':
    Clears the screen?
    Data bytes:
        #01-#02: Segment parameter.

Komentopuskurin antamisen jälkeen komento suoritetaan kirjoittamalla ASCII EOT (0x4).
EOT:n kirjoittaminen väylälle palauttaa näytön aina alkutilaan (eli odottamaan näytön osoitetta). Samaten jonkinnäköinen timeout siellä on.

Komentopuskurin luonti ja sen suoritus on erikseen luultavasti siksi, että jotkut operaatiot voivat asettaa virhekoodin komentopuskurin antovaiheessa (esim jos EEPROMilta loppuu tila D-komennon yhteydessä).

Virhekoodin saa luettua sanomalla osoitevaiheessa (tokan tavun lsb = 0), mutta vastauksen  rakenne riippuu siitä, mikä komento aktivoitiin viimeisessä komentopuskurissa. Valitsemalla lukuoperaation pitäisi UART1:een tulla tulostetta, ja samalla päätyy johonkin moodiin, jossa
 komentotavut on eroteltu 0x10- ja 0x31-tavuilla (vastaavasti kuin kirjoitustilassa komentotavut erottaa STX ja ETX). Nähdäkseni tuossa koodissa on kuitenkin bugi (ehdollisissa hypyissä pari ehtoa väärinpäin), eikä kyseeisessä moodissa voi tehdä mitään hyödyllistä.

Komennon antaminen muuttaa myös softan sisäistä tilaa siinä mielessä, että 
E-komennon antamisen jälkeen laite osaa itse vilkuttaa sekunttivilkutinta ja päivittää minuuttilaskuria. Tämä feature menee pois päältä T-komennon annettaessa.
Näyttänee tosin myös siltä, että jos UART1:ssä ei liiku dataa hetkeen, resetoituu
laite näyttämään Ruoholahtea jonkin ajan (3 minuuttia?) päästä.

Eli esimerkkikirjoituskomento (lista tavuja)
    [DIP[4:6] | '0', (DIP[1:3] << 1) | 1 | '0', ENQ, STX, segmentHi, segmentLo, ?, ?, ?, ?, ?, 'N', ETX, checksum, EOT]
    ? = (lähes) mielivaltainen tavu, vaikkapa 'a' on turvallinen

Esimerkkilukukomento:
    [DIP[4:6] | '0', (DIP[1:3] << 1) | '0', ENQ]
    Tällä pitäisi siis jotain tulostua. Kannattaa odotella hetki ennen uusien komentojen laittamista jos tätä yrittää.

LCD segment parameter:
    The PrintString takes in HL a word describing which segment to fill. Values used are:
    (Also, the N and T commands)h
        - 0x101 for the finnish text
        - 0x201 for the swedish text
        - 0x301 for the clock?
        - 0x302 for the blinking thingy? NOT SURE

Ohjelmakoodi on kaksiosainen: itse LCD:lle kommunikaatio on kirjoitettu C:llä, alkaa osoitteesta 0x4000, ja koko muu hela hoito on assembleria, alkaa 0x0:sta.
Tuo C-osuus on niin hirveää luettavaa, etten ole siihen perehtynyt.
Tuon assembler-osuuden sen sijaan selvittänyt koodin osalta melkeinpä perinpohjaisesti,
muuttujia en ihan niin paljoa.

Tietääkseni 8085:lle ei ollut mitään asiallista (tai asiatontakaan) C-kääntäjää tarjolla vuonna 90, veikkaan PL/M:ää. Korkean tason kielestä käännettyä kuitenkin.

Merkkijono-EEPROMin formaatti

#00:         seuraavan kuuden tavun XOR-checksum. Jos ei mätsää,
                koko EEPROM tyhjennetään 'e':ksi.
#01-03:    Merkkijono-EEPROMin versionumero, ASCII-numeroita.
               E-komento vertaa komentopuskurissa annettua versionumeroa,
               ja antaa virheen jos nämä eivät ole samat. D-komennolla
               EEPROMin versionumero voidaan asettaa kirjoituksen yhteydessä.
               (Dumpatulta teksti-EEPROMilta löytyy muistaakseni '004')
#04-05:    Osoitin seuraavaan vapaaseen tavuun EEPROMille. Tähän kohtaan D-komento
               appendaa lisättävän avain-arvoparin.
...#0f       Taikanumero 'MHELS\x17'.
#10          0x2, aloittaa merkkijonolistan
#11..        n kpl avain-merkkijono-pareja eroteltuna 0x2:lla. Avain on aina 4 ASCII-tavua,        
                esim 1B01, ja tuota muotoa. 1Bxx on ekan rivin avain ja 2Bxx tokan rivin.
                Listan päättää erotin 0x2. Loput on oletuksena täytetty 'e'-merkillä.
#1ffe-#1fff: Taikanumerot 0x17. Nämä ilmaisevat merkkijononkopiointirutiineille
                ROM:n loppumisen. Samaan tapaan MHELS-signaturen 0x17
                kertoo ROM:n toisen pään, sillä hakurutiini tekee läpikäynnin lopusta alkuun.

Disclaimer, ylläoleva on *puhtaasti* disassembloimalla saatua tietoa, ihme jos tuolla ei jotain virheitä ole. Assembly-puolen koodista vapaasti saa kysellä, varmaan ehdin vastatakin joskus.

-Dezgeg (Tuomas)


--------------------------------------------------------

14.3. session tuloksia

EPROMin disassembly käännettiin netistä löytyneellä 8085-assemblerilla. Muutaman korjailun jälkeen (myös kääntäjä itse vaati korjailua) kääntäjän tuottama intel-hex on identtinen alkuperäisen EPROMista luetun kanssa.

Ohjaimen piirilevyllä oleva EPROM korvattiin Romulaattorilla (ROM-emulaattori) johon ladattiin kääntäjän tuotos. Näyttö jatkoi toimintaansa samoin kun ennenkin, eli siis toimii Romulaattorin kanssa. 

Koodia muokkaamalla saatiin näytölle ensin omia kiinteitä tekstejä. Seuraavaksi tehtiin yksinkertainen skrolleri, josta on alussa linkitetty video. Lopuksi muokattu koodi ohjelmoitiin EPROMiin.

Havaintoja ohjelmasta:

-- jari

Mä olen purakunut osoitteesta 4000H alkavaa koodia

L4000: hyppää suoraan L59A1
L59A1: kopioi parametrit pinoon jäejestyksessä L, H, DE
           kutsu rutiinia L56E4
           poista L, H ja DE pinosta ja palaa
L56E4: Varaa tilaa pinosta (lokaaleille muuttujille) 6 tavua (eli 3 sanaa)
           Tarkista, että L <=3 ja H<= 2. Jos ei poista 6 tavua pinosta ja palaa
           Jos parametrit on oikein, hyppää labeliin L571B
L570E: edellisen apulabeli (nollaa Zero flagin)
L5711: L56E4:n apulabel (jos Zero flagi ei asetettu, poista varatut 6 tavua pinosta ja palaa)
L5B52: hl <= de paluuarvo Zero flagissa (0=false, 1=true) toteutettu vaihtamalla rekisterit
L5B53: de <= hl paluuarvo kuten ylhäällä
L5B62: edellisen apulabeli (erisuuruuden tarkastelu jos hl de erimerkkiset)

--juha