Compaq Deskpro XE 466: Non-Parity RAM Patch — 18 Binary Patches für ein 30 Jahre altes BIOS

Das Problem

Der Compaq Deskpro XE 466 stammt aus einer Zeit, als Parity-RAM (9-Bit SIMMs) der Standard war. Das BIOS prüft bei jedem Speicherzugriff das neunte Paritätsbit — fehlt es (wie bei modernen Non-Parity SIMMs), werden fatale Fehler ausgelöst: POST-Abbrüche mit „Parity Check“-Meldungen und NMI-Freezes zur Laufzeit.

Parity-SIMMs sind heute selten und teuer. Ziel dieses Projekts: das Original-BIOS so patchen, dass Non-Parity-RAM akzeptiert wird — ohne das BIOS komplett neu zu schreiben.

Der Angriffsvektor: Port 0x61

Auf IBM PC-kompatiblen Systemen läuft die gesamte Parity-Prüfung über einen einzigen I/O-Port:

  • Port 0x61 (System Control Port B): Bit 7 = Memory Parity Error, Bit 6 = I/O Channel Check, Bit 2 = Parity Check Enable
  • NMI (INT 2): Wird vom Chipset bei Parity-Fehlern ausgelöst

Das bedeutet: jeder parity-relevante Code im BIOS muss durch Port 0x61. Das ist der Flaschenhals — und unser Ansatzpunkt.

Schritt 1: Alle Port-0x61-Zugriffe finden

ndisasm -b 16 -o 0xF0000 compaq-dumped-flash.bin -e 0x10000 \
  | grep -E "(in|out).*0x61"

~50 Treffer. Jeder davon ein Kandidat für parity-relevanten Code.

Schritt 2: Parity-Fehlermeldungen lokalisieren

strings -a -t x compaq-dumped-flash.bin | grep -i parity

Drei Treffer:

  • "Parity Check 1" bei F000:DEE6
  • "Parity Check 2" bei F000:DF1D
  • "Cache Parity Check" bei F000:DF2D

Die 4 Patch-Kategorien

Kategorie 1: NMI-Handler (1 Patch)

Der NMI-Handler bei F000:E1BA liest Port 0x61, testet Bit 7 und springt bei gesetztem Bit in den Fatal-Error-Pfad:

F000:E1C4  TEST AL, 0x80     ; Bit 7 = Memory Parity Error?
F000:E1C6  JNZ  +0x0E        ; Ja → fataler Halt

Patch: 75 0E90 90 (NOP NOP) — Parity-NMIs werden ignoriert, IOCHECK bleibt aktiv.

Kategorie 2: POST Memory Tests (11 Patches)

Nach jedem Speichertest-Zyklus prüft das BIOS:

IN  AL, 0x61       ; Port lesen
TEST AL, 0xC0      ; Bits 7+6 testen (Parity + IOCHECK)
JNZ  error_handler

11 Stellen im ROM folgen diesem Muster — vom initialen POST-Check über die Hauptspeicher-Testschleife (8 Stellen) bis zum LFSR-Test und einer dedizierten Parity-Testroutine.

Patch: A8 C0A8 40 — nur noch Bit 6 (IOCHECK) prüfen, Bit 7 (Parity) ignorieren.

Kategorie 3: Parity-Latch-Reset (5 Patches)

Die Standard-Sequenz zum Zurücksetzen des Parity-Latches:

IN  AL, 0x61
OR  AL, 0x0C      ; Bits 2+3 setzen (Parity + IOCHECK disable)
OUT 0x61, AL      ; Latch zurücksetzen
AND AL, 0xF3      ; Bits 2+3 löschen (wieder aktivieren)
OUT 0x61, AL

Patch: 24 F324 F7 — Bit 3 wird gelöscht (IOCHECK wieder aktiv), aber Bit 2 bleibt gesetzt: Parity-Checking bleibt dauerhaft deaktiviert.

Kategorie 4: ROM-Checksum-Bypass (2 Patches)

Hier wurde es richtig tricky. Die 17 Parity-Patches verändern die Byte-Summe des F000-Segments — die Checksum-Prüfung bei F000:5660 schlägt fehl → „101-ROM Error“.

Was NICHT funktioniert hat

Versuch Methode Ergebnis
1 Kein Checksum-Fix 1 langer + 1 kurzer Beep → „101-ROM Error“
2 Checksum-Byte bei F000:7AEE Schwarzer Bildschirm, keine Beeps (komplett tot)
3 Checksum-Byte bei F000:FFFF Schwarzer Bildschirm, keine Beeps (komplett tot)

Erkenntnis: F000:FFFF ist zwar das Standard-Checksum-Byte (Originalwert 0x20), wird aber vom Compaq-Chipset während des frühen POST als Machine-Sub-Model-ID gelesen — bevor die Video-Initialisierung stattfindet. Änderung = sofortiger Hang. Dasselbe gilt für F000:7AEE.

Die Lösung: Code statt Daten patchen

Statt das Checksum-Byte zu korrigieren, wird die Prüfung selbst überbrückt:

; Original:
F000:568E  0F 84 16 00    JZ  +0x16  ; wenn Summe = 0 → weiter

; Patch:
F000:568E  30 DB          XOR BL, BL ; BL = 0, Zero-Flag = 1
F000:5690  74 16          JZ  +0x16  ; springt IMMER (ZF gesetzt)

Zusätzlich wird ein zweiter Checksum-Check für das E000-Segment (erste 64KB des ROMs) überbrückt: JZJMP bei F000:70B1.

Patch-Übersicht

# Kategorie Adresse Alt Neu Effekt
1 NMI F000:E1C6 75 0E 90 90 Parity-NMI ignorieren
2–12 POST-Test 11 Stellen A8 C0 A8 40 Parity-Bit in TEST überspringen
13–17 Latch 5 Stellen 24 F3 24 F7 Parity dauerhaft deaktiviert
18 Checksum F000 F000:568E 0F 84 16 00 30 DB 74 16 Checksum-Bypass
19 Checksum E000 F000:70B1 74 18 EB 18 Checksum-Bypass

19 Patches. 24 Bytes geändert. F000:FFFF unangetastet.

Lessons Learned

  1. F000:FFFF nicht anfassen — trotz Standard-Checksum-Byte wird es als Machine-Sub-Model-ID gelesen. Änderung = Black Screen.
  2. „Unbenutzter“ ROM-Space ist nicht unbenutzt — F000:7AEE sah in der Analyse wie freier Platz aus, wird auf echter Hardware aber gelesen.
  3. Code patchen, nicht Daten — wenn das Checksum-Byte tabu ist, die Prüfroutine umgehen.
  4. Port 0x61 ist der Flaschenhals — die gesamte Parity-Architektur des PC läuft über einen einzigen Port. Das macht systematisches Patching möglich.

Status

Die gepatchte ROM-Datei (compaq-noparity.bin) ist fertig und wartet auf den Hardware-Test. Das Python-Script patch-noparity.py wendet alle 19 Patches automatisch auf den originalen ROM-Dump an.

Wird fortgesetzt…

Nach oben scrollen