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 0E → 90 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 C0 → A8 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 F3 → 24 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: JZ → JMP 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
- F000:FFFF nicht anfassen — trotz Standard-Checksum-Byte wird es als Machine-Sub-Model-ID gelesen. Änderung = Black Screen.
- „Unbenutzter“ ROM-Space ist nicht unbenutzt — F000:7AEE sah in der Analyse wie freier Platz aus, wird auf echter Hardware aber gelesen.
- Code patchen, nicht Daten — wenn das Checksum-Byte tabu ist, die Prüfroutine umgehen.
- 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…
