Siirry pääsisältöön

IBM 1800 - Se toimii sittenkin


Pahoitteluni lukijoilleni alkuun blogin luvattoman pitkästä hiljaiselosta. Lupasin reilu kuukausi sitten kommenttiosiossa juttua viikonlopuksi (...tosin en sanonut mille viikonlopulle ;), mutta yksinkertaisesti aika on ollut sen verran kortilla, ettei näihin harrastuksiin ole ehtinyt paneutumaan. No nyt sitä juttua kuitenkin tulee kahden kuukauden ajalta, kahdessa osassa!


Ensimmäinen ohjelma

Pessimisti ei pety, ei edes tälläkään kertaa; vastoin kaikkia odotuksia ja/tai todennnäköisyyksiä, Heili ajoi jo ensimmäiset testiohjelmat parisen kuukautta sitten. Ei kylläkään kovin kummoisia, sillä interface PC:n ja Heilin välillä on vielä kesken. Ohjelmat pitää vieläkin naputella taulun kytkimien kautta binäärinä ja ymmärrettävistä syistä, Y-sukupolven edustajan pää ei yksinkertaisesti kestä (vaikka kytkimien ketkuttelu onkin kivaa aikansa).

1130/1800:n yhden rekisterin arkkitehtuuri (accumulator-based architecture) on vähänkään moderneihin koneisiin tottuneelle hiukan omituinen. Ohjelmoijalla näkyy vain yksi rekisteri, A-rekisteri, jota voi käyttää datansiirtoon tai laskemiseen.

Vielä kerran se sama CPU:n lohkokaavio.

Kaikki laskutoimitukset ja datansiirrot tapahtuvat A-rekisterin ja jonkin (relatiivisen, indeksoidun ja/tai suoran/epäsuoran) muistipaikan välillä. Välitöntä osoitusta CPU ei tunne, muuten kuin parissa erikoistapauksessa. Esimerkiksi välitön osoitus "siirrä A-rekisteriin kymmenen" ei onnistu, pitää näyttää osoite jossa arvo "kymmenen" sijaitsee.

Se ensimmäinen. Vasemmassa laidassa näkyy muistiosoite, seuraavana käsky. Oikeassa laidassa sitten sama selvemmässä muodossa.

Yllä ensimmäisen ohjelman listaus 1130/1800-assemblerin (Windows-versiolla) käännettynä. Assembleri on ohjelma, joka kääntää symbolisen konekielen, siis ihmisen ymmärtämän version, todelliseksi binääriseksi konekieleksi, jota kone suorittaa.

Alkujaan homma on mennyt seuraavasti: ohjelmoija on naputellut lähdekoodin (symbolisen version) 029 reikäkorttikirjoittimella, yhdistänyt syntyneen korttinpakan assemberin korttipakkaan ja lukenut reikäkortinlukijalla koko setin tietokoneelle. Tietokone on suorittanut ensin assemblerin, joka puolestaan on lukenut lähdekorttipakan. Lopputuloksena uusi assemblerin luoma korttipakka, jossa lähdekoodi on käännettynä koneen ymmärtämässä konekielimuodossa. Tämän jälkeen vasta ohjelman pystyi suorittamaan lataamalla sen uudestaan reikäkorttilukijalla muistiin. Yksi virhe ja koko prosessi alusta. Voi vain kuvitella paljon reikäkortteja kului noihin aikoihin.

Ensimmäiset rivit eivät ole itsessään ohjelmaa, ABS ja ORG eivät tässä yhteydessä ole tärkeitä, ne vain ilmaisevat assemblerille, että ohjelma on absoluuttinen ja alkaa osoitteesta /0000. Kenoviiva ei tarkoita muuta kuin heksadesimaalista esitystapaa luvuille ja DC ilmaisee että kyseessä on muuttuja(arvo). Itsessään ohjelmassa ei ole kuin kaksi käskyä:

LOOP             Add YKSI
                        Branch or Skip on Condition (always) Long LOOP
...sekä yksi arvo:
YKSI                DC /0001

Ensimmäinen käsky on "laske yhteen" ja toinen käsky on "hyppää osoitteeseen", toisinsanoen ohjelma laskee A-rekisteriin ykkösen lisää päättymättömässä luupissa. Vaikuttaa yksinkertaiselta? Itseasiassa näin vähän tekevä ohjelma vaatii melkoista toiminnallisuutta CPU:lta.


Laske yhteen (0000: 8002)


I1-vaihe (käskynnouto):
  • I-rekisteri (ohjelmaosoitin) siirretään M-rekisteriin (muistiosoitin), muisti aloittaa datan noudon. I-rekisteriin lisätään yksi sen omalla piiristöllä*.
  • A-rekisteri siirretään U-rekisteriin (väliaikaisrekisteri, näkymätön ohjelmoijalle). Muisti siirtää B-rekisteriin.
  • Käskyn dekoodauksen ensimmäinen vaihe alkaa alkaa, B-rekisteri siirretään käskynpurkuun ja selvitetään onko käskyn osoitusmuoto relatiivinen, indeksoitu, epäsuora, jne. Tässä tapauksessa käsky on relatiivinen, sillä käskyn muoto ei ole LONG, eikä indeksirekisteri-tagi on "00". B-rekisterin vähemmän merkitsevä tavu (8-bittiä) siirretään D-rekisteriin. Arith-valo syttyy tauluun.
  • Aloitetaan yhteenlaskuoperaatio, jolla lasketaan vaikuttava osoite (siis ohjelmaosoitin /0001* + /0002 = /0003, YKSI). Yhteenlasku tehdään iteroimalla D- ja A-rekisteriä parilla yksinkertaisella säännöllä, kunnes D-rekisterin arvo on nolla. Yhteenlasku kestää vähintään T7:ään asti. Mikäli D ei ole nollassa, T7-syklejä jatketaan niin kauan kunnes se on.
  • A-rekisteri siirretään M-rekisteriin. 
E1-vaihe (suoritus):
  • Homma menee kuten I1:ssa, mutta I-rekisteriin ei lisätä mitään, U-rekisteri siirretään alussa takaisin A-rekisteriin. M-rekisteri osoittaa nyt paikkaa, josta haetaan arvo laskutoimitusta varten. Ensin se siirtyy B-rekisteriin, josta sen jälkeen D-rekisteriin. Tulos lasketaan samalla tavalla kuten osoitekin, omituisesti iteroimalla.

Hyppää (absoluuttiseen) osoitteeseen (0001: C400 0000)


Käsky on pitkässä kahden sanan formaatissa. Ensimmäinen sana on käsky, seuraava osoite jonne pitää hypätä.

I1-vaihe (käskynnouto):
  • Kuten aikaisemman käskyn vaihe, mutta koska käskyn muoto on LONG, on käsky kahden sanan mittainen eikä osoitetta tarvitse laskea. Käsky tulkataan Branch or Skip on Condition -käskyksi. Käskyn ehtoina on Branch Always, joten hyppy tapahtuu, oli CPU:n tai A-rekisterin tila mikä tahansa.
I2-vaihe (käskynnouto, toinen sana):
  • Normaali muistinnouto, B-rekisteri D-rekisteriin.
E1-vaihe (suoritusvaihe):
  • D-rekisteri siirretään I-rekisteriin, joka pakottaa CPU:n hyppäämään kyseiseen osoitteeseen (/0000 = LOOP) seuraavan I1-vaiheen alkaessa. Koko kierros alkaa alusta.


Tässäpä sitten lopputulos. Viimeinen bitti (eniten merkitsevä bitti tai etumerkki) valo kun käy päällä ja pois, on kahden käskyn ohjelma käynyt läpi noin 65 000 kertaa. 

Jokainen I- tai E-vaihe kestää vähintään 9 kellojaksoa (T0...T7 + yksi ylimääräinen T1 lisämuistillisen Heilin tapauksessa), joskus enemmän riippuen millaisia lukuja lasketaan yhteen/vähennetään (tai kerrotaan/jaetaan). Jokainen kellojakso 250ns ja näistä osa rullaa tyhjää, joten on aika selvää, ettei toteutusta ole ihan älyttömän tiukaksi optimoitu. Omituinen yhteenlasku/vähennystapa on kuin jäänne sarjakoneajalta, joka onkin sitten ihan oman arkkitehtuuri-postauksen aihe.


Muistin dumppaamista

Dumppaaminen tietokonemaailmassa tarkoittaa muistin kopioimista, muistivedosta. Nykykoneessakin voi törmätä termiin, ainakin "blue screen of deathin" napsahtaessa ruudulle, Windows kun tekee kaatuamishetkeltä muistivedoksen kiintolevylle (crash dump).

Itselleni kehittyi pieni pakkomielle muistin talteen saattamisesta jo ennenkuin Heili saapui pajalle, ihan tietokonearkeologisista syistä. Sen aikainen muistiteknologia, ferriittirengasmuisti, kun ei menetä sisältöään teoriassa ikinä, ja Heilin tapauksessa se on pysähtyneenä vuoteen 1986, kun iso kytkin vedettiin alas.

Käytännössä temppu hoituu kytkemällä jokin nopeaan datakeräykseen kykenevä laite, kuten logiikka-analysaattori, johonkin rekisteriin. Tempun toinen osa on ajaa itse koneella ohjelma, joka koplaa koko muistin kronologisesti läpi, logikka-analysaattorin noukkiessa talteen rekisterissä poikkeavaa dataa omaan muistiinsa aina kun sopiva hetki koittaa.

Mutta tehtävä ei ollut ihan yksinkertainen, sillä CPU:ssa kaikkea käytetään vähän kaikkeen, etenkin sitä ainoaa A-rekisteriä. CPU:n sisus on yhtä signaalien ilotulitusta, mikään ei pysy Heilissä paikallaan normaalin ohjelmansuorituksen aikana. Ainoastaan indeksirekisterit ovat muuttumattomina ilman erillistä kirjoitusta, mutta näistä mikään ei ole mitattavissa taustapinneistä.

Dumppailua kaaoksen keskellä.

LD-käskyn (lataa A-rekisteri) aikana tapahtuva lukeminen ei paljon lohduttaisi, sillä Heili lukee A:n suurinpiirtein joka käskyvaiheen aikana. Mistä saisi signaalin, joka oli uniikki vain kun oikeasti A-rekisterissä on pala muistia?

Dumppiohjelman listaus jälkeenpäin assemblerilla käännettynä. Alkuperäisen käänsin ruutupaperilla binääriksi, jonka naputtelin taulun kytkimien kautta, ihan kuten sen ensimmäisenkin ohjelman.

Ohjelma toimii seuraavasti: ensin A-rekisteri ja XR1-indeksirekisteri nollataan. Seuraavaksi siirretään XR1:n osoittama muistipaikka A-rekisteriin ja heti perään A-rekisteri tallennetaan osoitteeseen DUMMY /000A. Indeksirekisteri kasvaa yhdellä ja kierros alkaa uudestaan, erikoisemmalla LDX-käskyn käytöllä (LDX tarkoittaa lataa indeksirekisteri, mutta jos indeksirekisteriksi on asettu nolla, tarkoittaa se suoraan ohjelmasosoittimeen kirjoittamista). Siis ihan alakoulukamaa. Taika on siinä, että STO L DUMMY tuottaa "A to B"-signaalin ennen muistiin kirjoittamista, jota ei tapahdu missään muussa yhteydessä ohjelman ajon aikana. Tämä myös tarkoittaa sitä, että A-rekisterissä on sillä hetkellä todellista muistin sisältöä. Koska lisämuistikaappi (1803) ei ollut kiinni näiden ensimmäisten dumppisessioiden aikana, ohjelma loppui automaattisesti paritteetivirheeseen 48kilotavun jälkeen.

Oskillosskooppi/logiikka-analysaattori A-rekisteriin kytkettynä.

Logiikka-analysaattorin piuhat kiinni A-rekisteriin, CH1:n triggeriksi RUN-kiikusta ja CH2 kellosignaaliksi "A to B". Skooppi kertaliipaisulle, 50ms ikkunaan ja START-nappulaa Heilin taulusta. Olisihan tämä ollut aivan liian helppoa...

Siinähän sitä, sen kun saisi vielä ulos matolaatikosta.


48 kilotavua 1971 = 90 Megatavua 2016

Hankin Rigolin MSO1074Z-S:n Batronixilta hiukan Heilin muistin dumppaamista silmällä pitäen. Skooppina aivan mahtava vehje, eikä hinta päätä huimaa. Mutta logiikka-analysaattorissa on yksi perustavanlaatuinen ongelma. Dekoodaus kellosignaalin kanssa onnistuu näytöllä, mutta dekoodattua dataa ei voi saada skoopista ulos tikulle. Toinen asia mikä pisti silmäkulman nykimään, oli mukana tuleva UltraScope-ohjelmisto; siinä ei ole logiikka-analysaattorille mitään toimintoja, eikä ole kuulemma tulossa ihan hetkeen. Toki tällä kätevällä ohjelmalla voit käyttää skooppiasi PC:n näytöltä, joka onkin äärettömän hyödyllinen toiminto, kun se skooppi on noin 1.5m piuhan päässä siitä PC:stä...

Oikea data mikä pitäisi ottaa talteen, muu turhaa.

Ainoa mitä skoopista saa ulos, on raakadata halutuilta kanavilta. Kolmen ensimmäisen CPU:ssa sijaitsevan muistilohkon nappaaminen vie 90MB tilaa CSV-muodossa, siitä onkin hyvä tulkata 48kilotavua todellista muistia käsin ulos.

Turhat pois.

Mutta kädellinen keksii tavat millä päästä helpolla, tässä tapauksessa se oli siistiä tiedostosta kaikki skeida pois. Tein melko hätäisen&likaisen ohjelman: ohjelma lukee Rigolin luomaa CSV-taulukkoa, kunnes oikeat merkit osuvat kohdalleen (pilkku, miinumerkki, piste ja x, unohtamatta suodatusta) ja sen jälkeen ASCII-heksasta dekoodataan oikeaa binääriä toiseen tiedostoon.

Dumppiohjelma näkyy heti muistin alussa, niin kuin pitääkin.

Ja kas, sehän toimi, dumpin alusta löytyy itse dumppiohjelma! Muisti on selvästi korruptoitunut ajan ja omien kokeitteni kautta, mutta jotain järjellistä seasta löytyi kuten aakkoset (plotteria varten?) ja tekstit BULK0...4 sekä VOL111, jotka voisivat olla tiedostojen nimiä.

Ei tästä ensimmäisestä 48kilosta mitään  erityisen mielenkiintoista löytynyt. 80Kt vielä jäljellä tutkittavaa.

Jos joku haluaa ihmetellä ensimmäisen 48kilotavun dumppia, niin sen saa täältä: dump.hex
(Huom. kaikki teksti on tälläisissä koneissa EBCDIC-koodattua, HxD Edit ainakin osaa formaatin)


Assembleri

Ehdin hetken jo miettiä pitääkö tässä alkaa assembleria ohjelmoimaan 1800:lle, mutta sattumoisin yksi ibm1130.org -sivuston perustajista olikin jo veivannut komentokehoitepohjaisen assemblerin (kiitos vinkistä Pasille, en itse tajunnut etsiä sitä 1130-emulaattorin kansioista... ;). Y-sukupolvelaisella ei pää kestä myöskään komentokehoitteita 2010-luvun laitteissa, joten tein pikaisesti käyttöliittymän Visual C#:llä (tod.näk. maailman epäintuitiiviselle ohjelmointikielellä...). En ollut vuosiin käyttänyt Visualia ja kyllä jokunen hius tuli menetettyä muutaman tunnin ohjelmoinnin aikana, mutta eipä tarvi enää komentokehotetta.

Siihen loppuivat komentokehoitteet, prkl.

Ja loppuun vielä...

Ei voi kuin hämmästellä miten näin vanha laite voi toimia vieläkin täysin kuten sen pitääkin. Tosin ei ihan ilman kommelluksia ja korjauksia tähänkään pisteeseen olisi selvitty, mm. tuli tuhottua pari SLT-piiriä huolimattomalla skoopin käytöllä (hauenleuka kilahti väärään paikkaan + maatasoero = boom). Lopputuloksena yksi toimimaton bitti väylässä, jonka korjaamiseen onneksi löytyivät piirit ylimääräisestä 1826 IO-adapterikaapista ja ne vaihtuivat korjausasemalla käden käänteessä. Itse vian etsiminen ei triviaalia ollut, sen kun seurasi väylää vian lähteelle ja vaihtoi kortista piirit, jotka asiaan vaikuttivat.

Single-bit-failure (yksi toimimaton bitti muistissa) vaivasi tiettyä muistialuetta ensimmäisen parin tunnin käytön jälkeen, mutta hannuhanhimaisesti tästäkin ongelmasta selvisi puhdistamalla sense-kortin muistilohkosta. Lika kortin pinnalla riitti sotkemaan ferriittirenkaan hennon signaalin, kaikkea uutta sitä oppiikin.

Viimeinen ongelma ratkaistavaksi oli pariteettivirheiden poisto muistista. Ehdin jo miettiä erilaisia tekniikoita, miten virheen voisi ohittaa (dumppiohjelma kun olisi pysähtynyt aina virheeseen), mutta onneksi tuli iltalukemiseksi otettua IBM 1800 Operator's Manual. Siellä ohjeistettiin miten muisti siistitään automaattisesti taulun nappuloilla: CLEAR STOR + DISPLAY + WCR ON. Kai näitä olennaisia ohjekirjoja jätetään lukematta paremmissakin piireissä...

Seuraavassa osassa sitten lisämuistin kytkemistä, kadonnut virtalähde ja kommunikointia Heilin ja nykylaitteiden välillä!

EDIT: fiksasin tekstiä hiukan paremmin ymmärrettävään muotoon, lisäsin yhden selventävän kuvan, poistin isommat typot... Ei pitäisi puoliunessa näitä kirjoitella...


Ps. Puuttuvat valot ja nappulat tuli saatua ibmsystem3.nl -sivustoa ylläpitävältä Henkiltä (Thanks Henk, I owe you one!)

Tässä ne vaihtuu...

...ja tässä lopputulos.



Kommentit

  1. Hyvää kesää ja toivottavasti se tuo tullessaan myös aikaa jatkaa tätä kiehtovaa tarinaa koneen herättelystä! :)

    VastaaPoista
    Vastaukset
    1. Laitoin juuri postauksen uudesta löydöstäni, toivottavasti sillä pärjää hetken! Töitä on ollut molemmille käsille ja jaloille, varmaan enemmän kuin koskaan, joten blogi on ollut vähän tyhjäkäynnillä. Tuskin ehdin tietokojeksiini paneutumaan kunnolla ennen elokuuta, vaikka paljon olisi kerrottavaa...mutta katsotaan jos vaikka jostain muutama tunti irtoaisi ja siitä sitten syntyisi taas uusi posti ennen kesän loppua.

      Hyvää kesää sinnekin, toivotaan että ilmat lämpenee jussiksi!

      Poista
  2. Tuo laite on käyttöliittymäinsinöörin märkä uni. Ei ole Lahden suunnalla tullut hetkeen kuljettua, mutta tuohan alkaa näyttämään siltä, että sitä pitää lähteä ihan varta vasten katsomaan.

    VastaaPoista
  3. 48kt ferriittirengasmuisti kyllä riittäisi laskemaan 64bitin liukulukuja yhden tavun exponentilla, tosin tämä lähdekoodini on z80 prosessorille ja vaivannäkö muuttaa se 16b arkkitehtuurille etenkin kun käytössä on vain A-rekisteri. z80 on A,BC,DE,HL, IX, IY rekisterit ja vieläpä kahdennettuina sekä SP ja PC. Sitäpaitsi ajatus käyttää 40kw tehoista tietokonetta ohjaamaan aurinkopaneelin jalustaa olisi ideana järjetön.

    VastaaPoista
    Vastaukset
    1. Tulikohan ihan oikeaan osoitteeseen viesti? ;)

      Mutta osaisin arvata, että kyse on Heilistä jossain toisessa keskustelussa, ja voisin tuoda pari pointtia 1800:n arkkitehtuurista nähden 70-luvun puolivälin mikroprosessoreihin. 1800:n on 16-bittinen ja jokainen käsky suoritetaan muisti-rekisteri välillä (poikkeuksiakin toki on), voi ajatella että muisti toimii itsessään rekisteriavaruutena.

      Toinen merkittävä ero on 1800:n kyky laskea suoraan raudalla 32-bittisiä laskutoimituksia, sekä kerto- ja jakolaskuja. Nopeinkin 16/32-bittinen algoritmi 8-bittiselle CPU:lle on monta (kymmentä?) kertaa hitaampi kuin 1800:n rauta.

      Käskykanta on noin 30 käskyä, mutta jos nämä lasketaan samalla kaavalla kuin 70-luvun CPU valmistajat tekivät (eli laskemalla käskyn jokaisen osoitustavan omaksi käskyksi) päästään aikalailla samoihin lukuihin.

      Suorituskyvyn vertaaminen onkin oma juttunsa, mutta perstuntumalta sanoisin että 70-luvun pyhä kolminaisuus alkuperäisessä setupissaan (6502, Z80, 8080) jää jälkeen Heilistä, kun aletaan oikeasti laskemaan, johtuen jo pelkästään leveämmästä dataväylästä.

      Poista

Lähetä kommentti