Vorige keer legde ik een drietal plannen uit om vanuit het niets een computer iets zinnig te laten doen. In deze aflevering geef ik u de gereedschapskist om een virtueel exemplaar van een historische computer te bootstrappen.
Henk van de Kamer
In de vorige aflevering vertelde ik hoe een computer vanuit niets iets zinnigs kan doen. Het maken van die stap was in de begindagen van de computer redelijk simpel, maar is tegenwoordig een zwarte doos. Zelf proberen om zo’n zwarte doos te maken, is moeizaam omdat de simpele hardware aan het verdwijnen is. Om u als lezer zonder investeringen in hardware te laten zien wat er nodig is, ga ik nu plan D uitleggen. De voorgaande drie zijn reeds besproken.
Cassette BASIC op een virtuele IBM model 5150 |
Cassette BASIC
Op het internet zijn dumps van ROM’s uit oude computers terug te vinden. Waarschijnlijk niet helemaal legaal, maar blijkbaar vinden de rechthebbenden deze sites geen probleem. Op zo’n site vond ik cassette BASIC die in IBM model 5150 aanwezig was. In totaal 32 kilobyte naast de 8 kilobyte voor het BIOS. IBM hinkte in die tijd op twee gedachten en deze ‘concurrent’ in de homecomputerhausse zoals de Commodore 64 is hopeloos mislukt. Ofwel weinig mensen zullen cassette BASIC in werking hebben gezien. Ik in ieder geval niet...
Op het internet zijn vele emulatoren te vinden zoals VirtualBox, VM ware en meer. Omdat ik tegenwoordig alleen onder Linux werk, ben ik overgestapt naar QEMU, die ook andere processoren dan de 80x86 kan emuleren. Mijn eerste poging was als volgt:
$ cat basicc11.f6 basicc11.f8 basicc11.fa basicc11.fc pc102782.bin > basic_bios.bin
$ qemu-system-i386 -bios basic_bios.bin qemu: could not load PC BIOS ‘basic_bios.bin’
Het genoemde IBM-model gebruikte 8 kilobyte ROM-chips en dat verklaart de vijf genoemde bestanden in de eerste regel. Deze moeten we in de juiste volgorde samenvoegen tot één bestand, want dat is wat QEMU verwacht. Zoals u in bovenstaande uitvoer ziet, werkt dit niet. Verder onderzoek leert dat QEMU een BIOS-bestand van 128 kilobyte verwacht, ofwel mijn tweede poging was als volgt:
$ dd if=/dev/zero of=zero.bin bs=1k count=88
$ cat zero.bin basicc11.f6 basicc11.f8 basicc11.fa basicc11.fc pc102782.bin > basic_bios.bin
$ qemu-system-i386 -bios basic_bios.bin
In de vorige aflevering vertelde ik dat de 80x86 zijn eerste opdracht op locatie $f000:fff0 verwacht, vandaar het aanvullen met nullen aan het begin. Nu gaat het starten weliswaar goed, maar nog steeds niet met het gewenste resultaat zoals te zien in de screendump in mijn labjournaal (https://www.hetlab.tk/virtueel/ibm-cassette-basic).
PCem
De melding “Guest has not initialized the display (yet)” is duidelijk, het BIOS uit 1982 initialiseert niet het beeldscherm. Logisch, want QEMU emuleert alleen VGA en die standaard ontstond pas in 1987. Een zoektocht naar een emulator voor oudere computers leverde PCem (https://pcem-emulator.co.uk/index.html) op. Deze moeten we zelf compileren en voor Debian Stretch kunt u mijn recept (https://www.hetlab.tk/virtueel/pcem-compileren) gebruiken. Persoonlijk vind ik experimenteren onder Windows een ramp. Ofwel als u halsstarrig dat besturingssysteem wilt blijven gebruiken, zult u het compileren zelf moeten uitzoeken. Grijns...
Na het compileren en uitzoeken waar de gevonden ROM’s verwacht worden, kon ik al snel een virtuele IBM model 5150 met een realistische configuratie samenstellen (https://www.hetlab.tk/virtueel/pcem-ibm-model-5150). Zolang we geen floppy plaatsen, wordt automatisch cassette BASIC gestart. Over floppy’s gesproken, de 5,25” 360k in de configuratie is verwarrend. De originele hardware kan alleen overweg met 160 kilobyte floppy’s en virtuele exemplaren worden ondanks de verwarrende instelling prima gelezen (https://www.hetlab.tk/virtueel/pcem-160-kib-diskette-images) zoals ik een paar weken later ontdekte tijdens het booten van PC DOS 1.00.
Assembly
Het succesvol starten van cassette BASIC geeft aan dat ons virtueel oudje werkt zonder een bootable floppy. We hebben nu een 8 kilobyte grote ROM die de computer initialiseert en vervolgens besluit wat er gestart moet worden. Zoals gezegd kan dat naast cassette BASIC ook een floppy met de allereerste DOS-versie zijn.
Met deze controle kan ik beginnen met het uitleggen van plan D. Stel dat we de gebruikte bestanden niet op het internet hadden gevonden. Inderdaad dan zijn we terug bij een virtuele ‘dode’ computer en moeten we deze zelf instructies gaan geven. Een processor snapt alleen series van enen en nullen ofwel machinetaal. Voor ons mensen is dat onleesbaar, ofwel we gaan assembly gebruiken. Via een assembler kan deze worden omgezet – geassembleerd – naar machinetaal. Laten we beginnen met de meest simpele ROM in assembly:
cpu 8086
org 0e000h
main:
jmp $
times 8176-($-$$) db 0
reset_vector:
jmp 0f000h:main
IBMdb " 24/01/2023"
Waarschijnlijk is dit nog steeds abracadabra, maar het is in ieder geval leesbaar. De eerste regel geeft aan voor welke processor we willen assembleren. De 8086 – het luxere broertje van de 8088 – kan overweg met één megabyte aan geheugen. Dat is meer dan de 16 bits die de interne registers gebruiken. Intel heeft daarom in een ver verleden de notatie f000:abcd bedacht om een absolute geheugenlocatie aan te duiden. Dat opsplitsen in twee zestien bits getallen kan op meerdere manieren ofwel fa00:0bcd, fab0:00cd en fabc:000d zijn dezelfde geheugenlocatie.
Reset vector
Vorige maand vertelde ik over het initialiseren van de processor en voor de 8086 is dat beginnen met het uitvoeren van de instructies vanaf f000:fff0. Dat zijn de laatste 16 bytes in het genoemde maximale één megabyte. Dat is niet veel, ofwel het is gebruikelijk om op deze locatie een sprong – jmp in 8086 assembly – te plaatsen naar het begin van de ROM. Zoals we hebben gezien is deze in de IBM model 5150 slechts 8 kilobyte groot, ofwel geheugenlocatie f000:e000 en dat verklaart de tweede regel en het eerste stuk van de jmp-instructie. Omdat alles wat met een letter begint, wordt opgevat als een label, moeten we in nasm – de assembler die we gaan gebruiken – een extra voorloop nul gebruiken. Om onderscheid te maken tussen decimale en hexadecimale getallen voegen we in de laatste op het einde ook nog eens een h toe.
Zoals gezegd maakt een assembler het programmeren een stuk eenvoudiger. Zo kunnen we labels gebruiken in plaats van hardgecodeerde geheugenlocaties en dat verklaart de vierde regel en de jmp 0f000h:main. De $ in de vijfde regel is gelijk aan de opgegeven org in de tweede regel en $$ is de grootte van het geassembleerde programma. In ons geval willen we exact 8.192 bytes waarvan de laatste zestien beginnen met de jmp-instructie. Ofwel we willen het niet gebruikte deel opvullen met een waarde en ik heb gekozen voor een nul byte. Met al deze informatie is hopelijk ook de zevende regel duidelijk. De laatste jmp-instructie is vijf bytes groot, ofwel de string in de twaalfde regel vult de resterende elf bytes op.
Assembleren
Na het wegschrijven als een bestand, kunnen we onze code assembleren naar een 8 kilobyte groot ‘BIOS’. Na starten wordt, totdat we het geheel uit zijn lijden verlossen, de jmp in vijfde regel uitgevoerd. Niet zinnig, maar wel een controle of PCem niet gaat klagen:
$ nasm -f bin hetlab1.asm -o bios.bin
$ cd pcem/roms/ibmpc
$ mv pc102782.bin pc102782.bin.orig
$ ln -s ~/bios.bin pc102782.bin
$ cd ../..
$ ./pcem
De eerste regel start de assembler met de code in hetlab1.asm en als uitvoer een bios.bin bestand. De tweede regel gaat naar de locatie waarin PCem de ROMS voor cassette BASIC en het BIOS van de door ons gewenste machine verwacht. De derde regel stelt het originele BIOS veilig en met de vierde maken we een symbolische link – lees snelkoppeling voor alle Windows-gebruikers – naar het door ons gemaakte bestand. Met deze truc kunnen we vanaf nu gewoon nieuwe versies assembleren en direct testen. De laatste stap start PCem en zoals verwacht zien we een zwart scherm.
Het begin van een eigen ROM |
Eigen ROM
Om zeker te weten dat er iets gebeurt, wilde ik vervolgens iets op het virtuele scherm schrijven. Dat betekent het initialiseren van de virtuele MDA-videokaart en dat was een flinke zoektocht (https://www.hetlab.tk/virtueel/pcem-eigen-bios). In dat bericht ook de code om het scherm vol te schrijven met de eerste letter van mijn naam. De code kan waarschijnlijk ingekort worden, maar is dan minder inzichtelijk. Het resultaat is de getoonde screendump in een historisch correcte kleur.
Als we even de computer met daarop Linux vergeten, hebben we nu de basis om een virtuele computer te bootstrappen. Het aansturen van het beeldscherm is nu bekend en de volgende stap is natuurlijk het toetsenbord. Waarna ons eerste programma een monitor zal zijn om de geheugenlocaties te inspecteren, aan te passen en vervolgens uit te voeren. De rest laat ik aan de fantasie van de lezer over...