Ontwikkeling van een windowed OS voor een homebrew Am29000-computer

Er was eens, in de mysterieuze landen van Mexico, tussen het najaar van 1996 en de lente van 1997, dat ik een windowed besturingssysteem codeerde in 32-bit machinecode. Ik wist het op een floppydisk te passen en sprong van vreugde toen het systeem opstartte.

Natuurlijk vond ik het een geweldige prestatie omdat je Windows 3.1 of GEM destijds niet vanaf een floppydisk kon booten, maar ik besefte toen nog niet hoe cool het eigenlijk was. Helaas was het niets wat ik zomaar met iedereen kon delen, aangezien het geschreven was voor een homebrew-computer gebaseerd op de Am29000-processor.

Laten we teruggaan naar mijn herinneringen en aantekeningen om te ontdekken wat ik in de loop der tijd was vergeten.

De snelste processor ter wereld

Toen ik bezig was met mijn reeks artikelen over de transputer, voelde ik me zeer tevreden met mijn besturingssysteem en had ik de grenzen van dat systeem bereikt. Mijn vader was ondertussen bezig met de ontwikkeling van een nieuw computersysteem gebaseerd op een nieuwe 32-bit processor. In 1994 was deze processor voor ons nieuw, maar de AMD Am29000 kwam eigenlijk al in 1987 uit. Hij werd destijds gepresenteerd als de snelste processor ter wereld (een typische meme avant la lettre) en kon één instructie per cyclus verwerken, wat hem inderdaad erg snel maakte.

De Am29000-processor en zijn opvolger, de Am29050, werden gebruikt in diverse Postscript-taaluitbreidingen voor printers, video NuBus-kaarten voor Quadra Macs, arcade-games zoals F-15 Strike Eagle en Battle of the Solar System (beide van Microprose), en volgens Wikipedia zelfs in avionica op dual-processor boards voor redundantie.

Deze snelheid was echter gebaseerd op aannames, zoals het gebruik van voldoende snel RAM- of ROM-geheugen of zeer snelle TTL-chips. In het begin van de jaren '90 waren deze componenten nog erg duur. Bovendien maakten bepaalde ontwerpbeslissingen voor de Am29k-familie, zoals een aparte data- en instructiebus, de interfacing erg omslachtig. Uiteindelijk vereiste elke nuttige toepassing met de Am29k DRAM-geheugen, maar dat was traag, waardoor het snelheidvoordeel verloren ging.

Mijn vader vertrouwde echter op statisch RAM-geheugen (SRAM) en bemachtigte tientallen 128 KB SRAM-chips met een 8-bit bus. Surface-mount technologie begon toen net op te komen; sommige chips waren gebouwd met vier 32K RAM-chips op kleine groene printplaatjes.

De eerste Am29000-computer die mijn vader bouwde, draaide op 6 MHz en had een enorme geheugencapaciteit van 512 KB RAM en 128 KB EPROM (via twee 16-bit chips). Het systeem beschikte over ISA-connectoren, een Cirrus Logic GD5429 videokaart, een standaard parallel/serieel/floppy-disk controllerkaart (multiport card) en hergebruikte de keyboardchip van een oude pc voor AT-toetsenborden. Deze computer werd de G11 genoemd.

Het was teamwork: hij deed de initiële programmering om de processor te starten, ik vertaalde mijn Z280-code voor het initialiseren van de Cirrus Logic VGA, maakte de keyboarddecoder en de diskroutines. Toen dit stabiel was, werkte ik aan het monitorprogramma voor het tonen van het geheugen en het opslaan van data. Omdat de instructieset complex was, integreerde ik ook een micro-disassembler en een micro-assembler. Hierdoor konden we coderen met echte mnemonics, hoewel labels niet gebruikt konden worden en het verplaatsen van code een enorme klus was.

We moesten terug naar de Z280-machine om de EPROM's te programmeren, maar alles ging goed. Alleen besefte ik dat ik alles moest porten van de Z280 naar deze machine; we hadden ongeveer 200 programma's aan applicaties, utilities en games. Bovendien waren de Z280-programma's inmiddels verouderd, waardoor ik ze niet simpelweg kon repliceren.

(Ik zou graag een foto van de G11V1 willen tonen, maar digitale camera's bestonden vóór 1999 nauwelijks en ik weet niet waar de machine is. De beschikbare foto is van een G11V2-computer, herkenbaar aan vier 512KBx8 geheugens en een PCI-bus, die in de V1 nog ontbrak).

Tussenpauze: Inspiratie en Architectuur

Toen de G11 klaar was, nam mijn vader hem over om te onderzoeken hoe elk speciale register van de processor werkte en waarom deze bepaalde waarden hadden. Ik was verbaasd, want ik dacht dat dit al in het databook stond, maar ik hielp hem uiteindelijk door zijn aantekeningen uit te typen en te printen.

Ik kon me nu concentreren op de transputer, maar schakelde af en toe over naar de G11 om het floppy-diskbesturingssysteem te verbeteren. Ik begon games te coderen, een teksteditor voor onze nieuwe Canon BJC-600 bubblejet printer en zelfs een MSX-emulator (waarmee ik voor het eerst Gradius zag draaien).

Tegelijkertijd zag ik dat Windows 3.1 nuttig leek met al zijn iconen in felle kleuren (een resultaat van het CGA-palet). Ik wilde ontdekken hoe een window-systeem werkte, maar kon geen programmeerboek voor Windows 3.1 vinden. Uiteindelijk vond ik een goedkoop boek over OS/2-programmering. De auteur introduceerde OS/2 op een uitstekende manier: vensters openen, tekenen en sluiten was eenvoudig.

De echte ontdekking was de message pipeline. Elk proces met vensters heeft een message pipeline. Bij het maken van een venster wijs je een interface-handler toe (de window class), die berichten leest en acties uitvoert op basis van gebruikersinteractie. Interacties beginnen simpel, zoals een muisklik of het bewegen van de muis, maar worden complexer bij dubbelklikken. Je kunt vensters in vensters maken; dit zijn controls zoals knoppen, schuifregelaars, iconen en statusbalken.

Terwijl ik onderzoek deed voor dit artikel, ontdekte ik dat ik nog een doos had met twee databooks die AMD op 7 juni 1990 had verzonden.

Het Window-systeem

Voor de realisatie van het systeem waren drie zaken essentieel:

1. Multitasking Ik begon met coöperatieve multitasking. Het was simpel: de huidige staat opslaan, de nieuwe staat laden en springen naar de nieuwe locatie. Ik hield geen rekening met complexe OS-ontwerpnormen; ik wilde gewoon dat het werkte.

2. Vensters (Windows) Om te begrijpen hoe je elementen over elkaar heen plaatst, bestudeerde ik de broncode van X11R5 op een CD-ROM. Hoewel mijn computer geen netwerk of C-compiler had, was één bestand verlichtend: XRegion.c.

Het basisconcept van het algoritme is dat elk venster een lijst met rechthoeken heeft voor weergave. Als een venster wordt overlapt, wordt de overlappende rechthoek "weggesneden". Als een rechthoek in het midden wordt gesneden, ontstaan er vier nieuwe rechthoeken. Voor de weergave moet je dus in die vier rechthoeken tekenen. Een zwaar "geclipped" venster wordt hierdoor langzaam getekend. (Apple gebruikt hiervoor een clipping-bitmap, maar dat is een gepatenteerde techniek).

3. API Specificaties Ik maakte een vereenvoudigde API in mijn notitieboek, gebaseerd op het OS/2-boek. Waar OS/2 en Windows 3.1 een grafische context gebruiken met pentypes en vulpatronen, vond ik dat te omslachtig en liet ik de gebruiker direct in pixels in het venster tekenen.

Met deze drie bouwstenen begon ik in het najaar van 1996 aan het coderen van mijn windowing-systeem in machinecode. Ik was toen 18 jaar oud. Omdat mijn micro-assembler en micro-disassembler zo goed werkten, kon ik zeer snel routines schrijven.

De schijven uit 1997 en de weg naar emulatie

Dit artikel is mogelijk omdat ik twee diskettes heb met de vroege machinecode-versie uit 1997. Aangezien niemand meer een Am29000-computer heeft, besloot ik een emulator te bouwen.

De uitdagingen van de Am29000

Na het vinden van de handleiding op archive.org, stuitte ik op enkele eigenaardigheden:

  • Relational-assert instructies: In mijn ogen verspilling van silicium, waarschijnlijk bedoeld voor de Ada-taal vanwege militaire eisen.
  • Boolean waarden: Vergelijkingsoperatoren geven 0 of 0x80000000. Voor een C-stijl boolean waarde is een 31-bit shift naar rechts nodig.
  • Rekeninstructies: Er zijn verschillende varianten voor optellen (ADD, ADDU, ADDS) en aftrekken, inclusief versies met omgekeerde operandvolgorde (bijv. SUBRCU).

Reverse Engineering van de Boot-sequentie

Ik ontdekte via de boot-melding "Arrancando" dat de bootdata geladen moet worden op 0xbfff0000. De code was big-endian.

Tijdens het ontwikkelen van de emulator liep ik tegen een onbekende instructie 0xe2 aan. Pas na het raadplegen van de Am29050-handleiding ontdekte ik dat dit MULTIPLU is. AMD had bij de overgang naar de Am29050 de trapped instruction set herdefinieerd. Om multiplicatie op de Am29000 te laten werken, implementeerde ik een trap die 32 opeenvolgende MUL-instructies uitvoert via bit-shifting.

Interrupts en Hardware

Ik moest ook de timerregisters van de processor emuleren voor context switching. Daarnaast stuitte ik op toegang tot poort 0xfd0. Dit was verbonden met de ISA-bus; door A2 aan ISA A0 te koppelen, kwam dit uit op 0x03f4, de poort voor de diskcontroller.

In plaats van een volledige NEC 765 Floppy Disk Controller chip te emuleren, maakte ik een patch die direct vanuit de emulator naar de schijf communiceerde.

De SCSI-abstractie en de FAT-bug

Het systeem hanteert een abstractielaag waarbij alle schijven via dezelfde interface worden aangestuurd: SCSI-commando's (zoals 0x1a Mode Sense of 0x28 Read 10). De floppydisk was gemaskeerd als SCSI-unit -2, omdat units 0 tot 7 gereserveerd waren voor echte SCSI-schijven (een erfenis van mijn transputer OS).

Tijdens het testen crashte het systeem herhaaldelijk. Na uren debuggen ontdekte ik dat de File Allocation Table (FAT) corrupt raakte. De boosdoener was de instructie EXHWS (extract half-word with sign).

De foutieve implementatie in mijn emulator was:

if (d >= 0x8000)
    d = 0x10000 - d; // Dit geeft de absolute waarde, niet de sign-extension

De correcte routine had moeten zijn:

if (d >= 0x8000)
    d -= 0x10000;

Een enkele regel code zorgde ervoor dat ik drie dagen lang aan het debuggen was.

Visualisatie en Lettertypen

Toen de basis werkte, wilde ik het beeld terugzien. De computer gebruikte een Cirrus Logic GD5429 videokaart (1 MB VRAM, 800x600 pixels in 16-bit kleur). Ik implementeerde een beperkte emulatie van deze kaart in C, inclusief de RGB 5-6-5 modus en het banking-register schema voor de video RAM.

Een bijzonder detail: ik had geen C-compiler tot mijn beschikking toen ik het OS schreef. Daarom heb ik de Type 1 rasterizer (voor Postscript-lettertypen) handmatig van C naar machinecode vertaald. Voor de uiteindelijke distributie hebde ik deze vervangen door gratis Luxi-lettertypen.

De finale: Windows Fénix

Het resultaat is een besturingssysteem voor de Am29000, volledig geschreven in machinecode, genaamd Windows Fénix (genoemd naar het personage Phoenix uit X-Men '97).

Functionaliteiten van de Distributie:

  • Arrancador: Een launcher voor diverse programma's.
  • Colores: Voor het bewerken van de systeemskleuren.
  • Depurador: Een eenvoudige debugger die globale geheugenlocaties en taken kan disassembleren.
  • Editor: Een teksteditor met printfunctionaliteit (hoewel enigszins glitchy).
  • Promedio: Toont processorgebruik en vrij geheugen.
  • Tipografía: Testen van lettertypen en het wijzigen van de actieve fonts.
  • Circuito impreso: Een demo van vectorgrafieken.

Interne Architectuur (Services)

De services worden aangeroepen door het functienummer in gr121 te laden en een CALL uit te voeren naar ASNEQ 0x42,gr1,gr1. De services zijn als volgt verdeeld:

  • 0x42: Windowing system
  • 0x43: Video controller
  • 0x44: Keyboard controller
  • 0x45: Memory management
  • 0x46: SCSI controller
  • 0x47: Serial port controller
  • 0x48: Filesystem interface
  • 0x49: Mouse controller
  • 0x4a: Font manager
  • 0x4b: Message boxes
  • 0x4d: Configuration services
  • 0x4e: Printer services

Postmortem

Dit was de laatste keer dat ik iets van deze omvang in machinecode schrijf. Het vereist te veel documentatie en elke wijziging is een enorme klus omdat correcties vaak niet in de oorspronkelijke ruimte passen, wat leidt tot chaos.

Tussen eind 1996 en begin 1998 heb ik dit systeem ontwikkeld. In 1997 stapte ik voor de G11V2 over op little-endian en plaatste ik het OS in een 1 MB EPROM. Pas in juni 1998 was mijn eigen C-compiler (geport van de transputer) operationeel, wat me hielp om sneller te coderen en uiteindelijk leidde tot mijn eerste internetbrowser in 1999.

Wanneer ik nu terugkijk op QNX uit 1999, dat vanaf een floppydisk op een PC bootte, denk ik: "Gosh! Ik deed het eerst."