Cartouche PCjr QuickSilver
hexdump -C QUIKSILV.ROM > quiksilv.hex
hexdump -C SYSTEM.ROM > system.hex
00000000 31 35 30 34 30 33 36 20 43 4f 50 52 2e 20 49 42 |1504036 COPR. IB|
00000010 4d 20 31 39 38 31 2c 31 39 38 33 49 01 57 01 6d |M 1981,1983I.W.m|
00000020 01 86 01 ba 01 20 4b 42 47 0a 47 0a bb 0a 84 0a |..... KBG.G.....|
00000030 45 52 52 4f 52 41 42 43 44 45 46 47 48 78 03 78 |ERRORABCDEFGHx.x|
00000040 02 ef f7 b0 00 e6 a0 fe c8 e6 10 e4 a0 fa b8 8f |................|
00000050 10 ba c0 00 b9 04 00 0a c4 ee 80 c4 20 e2 f8 b0 |............ ...|
...
$ diff -u system.hex quiksilv.hex
--- system.hex 2020-10-03 13:39:02.227589679 +0900
+++ quiksilv.hex 2020-10-03 13:38:47.920171210 +0900
@@ -139,7 +139,7 @@
000008a0 80 e6 f2 c6 06 84 00 00 bf 78 00 1e 07 b8 14 14 |.........x......|
000008b0 ab ab b8 01 01 ab ab e4 21 24 fe e6 21 1e b8 50 |........!$..!..P|
000008c0 00 8e d8 80 3e 18 00 00 1f 74 10 b2 02 e8 3c 11 |....>....t....<.|
-000008d0 b4 00 cd 16 80 fc 1c 75 f7 eb 05 b2 01 e8 2c 11 |.......u......,.|
+000008d0 b4 00 cd 16 80 fc 1c 75 f7 eb 05 eb 03 ee 2c 11 |.......u......,.|
000008e0 bd 3d 00 33 f6 2e 8b 56 00 b0 aa ee 1e ec 1f 3c |.=.3...V.......<|
000008f0 aa 75 06 89 94 08 00 46 46 45 45 83 fd 41 75 e5 |.u.....FFEE..Au.|
00000900 33 db ba fa 03 ec a8 f8 75 08 c7 87 00 00 f8 03 |3.......u.......|
@@ -183,7 +183,7 @@
00000b60 8b 1e 72 04 81 fb 34 12 8c c2 8e da 75 0b f3 ab |..r...4.....u...|
00000b70 8e d8 89 1e 72 04 8e da c3 81 fb 21 43 74 ef 88 |....r......!Ct..|
00000b80 05 8a 05 32 c4 74 03 e9 82 00 fe c4 8a c4 75 ef |...2.t........u.|
-00000b90 8b e9 b8 aa aa 8b d8 ba 55 55 f3 ab 4f 4f fd 8b |........UU..OO..|
+00000b90 eb dc b8 aa aa 8b d8 ba 55 55 f3 ab 4f 4f fd 8b |........UU..OO..|
00000ba0 f7 8b cd ad 33 c3 75 64 8b c2 ab e2 f6 8b cd fc |....3.ud........|
00000bb0 46 46 8b fe 8b da ba ff 00 ad 33 c3 75 4e 8b c2 |FF........3.uN..|
00000bc0 ab e2 f6 8b cd fd 4e 4e 8b fe 8b da f7 d2 0a d2 |......NN........|
@@ -340,11 +340,11 @@
00001530 ff 20 ff 54 55 56 57 58 59 5a 5b 5c 5d 68 69 6a |. .TUVWXYZ[\]hij|
00001540 6b 6c 6d 6e 6f 70 71 37 38 39 2d 34 35 36 2b 31 |klmnopq789-456+1|
00001550 32 33 30 2e 47 48 49 ff 4b ff 4d ff 4f 50 51 52 |230.GHI.K.M.OPQR|
-00001560 53 fb 50 53 51 52 56 57 1e 06 fc e8 1d fe 8a e0 |S.PSQRVW........|
-00001570 3c ff 75 1b bb 80 00 b9 48 00 e8 b8 ca 80 26 17 |<.u.....H.....&.|
-00001580 00 f0 80 26 18 00 0f 80 26 88 00 1f e9 bb 00 24 |...&....&......$|
-00001590 7f 0e 07 bf 5c 14 b9 08 00 f2 ae 8a c4 74 03 e9 |....\........t..|
-000015a0 98 00 81 ef 5d 14 2e 8a a5 64 14 a8 80 75 51 80 |....]....d...uQ.|
+00001560 53 fb 50 53 51 52 56 57 1e 06 fc e8 1d fe e4 60 |S.PSQRVW.......`|
+00001570 8a e0 3c ff 75 1b bb 80 00 b9 48 00 e8 b6 ca 80 |..<.u.....H.....|
+00001580 26 17 00 f0 80 26 18 00 0f 80 26 88 00 1f e9 b9 |&....&....&.....|
+00001590 00 24 7f 0e 07 bf 5c 14 b9 08 00 f2 ae 8a c4 75 |.$....\........u|
+000015a0 1f 90 81 ef 5d 14 2e 8a a5 64 14 a8 80 75 51 80 |....]....d...uQ.|
000015b0 fc 10 73 07 08 26 17 00 e9 8f 00 f6 06 17 00 04 |..s..&..........|
000015c0 75 78 3c 52 75 22 f6 06 17 00 08 75 6d f6 06 17 |ux<Ru".....um...|
000015d0 00 20 75 0d f6 06 17 00 03 74 0d b8 30 52 e9 0b |. u......t..0R..|
Les différences sont à l'intérieur des premiers 32k. La cartouche peut donc
se soucier uniquement de CS6 (F0000-F7FFF) et peut mettre un niveau logique
bas sur BASE2 pour prendre le contrôle du BUS lorsque l'ordinateur accède
à des zones devant être modifiées. Parlant de zones, voici desquelles il s'agit:
...
08C9 74 10 JE F15A_0 ; Continue if no error
08CB B2 02 MOV DL,2 ; 2 Short beeps (error)
08CD E8 1A0C R CALL ERR_BEEP
0BD0 ERR_WAIT:
0BD0 B4 00 MOV AH, 00
0BD2 CD 16 INT 16H
0BD4 B0 FC 1C CMP AH, 1CH
0BD7 75 F7 JNE ERR_WAIT
0BD9 EB 05 JMP SHORT F15C
08DB B2 01 F15A_0: MOV DL,1 ; 1 SHORT BEEP (NO ERRORS)
0BDD E8 1A0C R CALL ERR_BEEP
; -- Setup printer and rs232 base addresses if device attached
08E0 BD 003D R F15C: MOV BP,OFFSET F4
Alors le jump equals à l'adresse 08C9 dirige le CPU vers F15_A (adresse 08DB)
lorsqu'il n'y a pas eu d'erreur. À cet endroit, DL est mis à 1 afin que l'appel à
ERR_BEEP génère une seule impulsion sonore.
08DB EB 03 F15A0_0: JMP SHORT F15C
0BDD EE 2C 11 ; Skipped non-sense (EE is OUT DX, al), 2C 11 is (SUB AL, 11H)
; -- Setup printer and rs232 base addresses if device attached
08E0 BD 003D R F15C: MOV BP,OFFSET F4
Le "MOV DL, 1" qui prépare normalement l'argument pour ERR_BEEP (nombre d'impulsions) est simplement remplacé par un saut vers F15C (adresse 08E0). Ce qui suit n'est jamais
exécuté et ne semble pas très utile. Je me demande tout de même pourquoi ce troisième octet a été modifié. Pourquoi EE (OUT DX, AL)? Est-ce un ajustement pour qu'un checksum fonctionne encore? (Est-ce qu'il y a un test du checksum du BIOS sur PCjr?)
0B59 Start of the memory test proc here
... setup, warm-start test, etc. will jump to P1 (0879) on cold boot. ...
0B6E F3/ AB P12: REP STOSW ; Simple fill with 0 on warm-start
... some clean-up code not shown ...
0B78 C3 RET ; And Exit
0879 81 FB 4321 P1: CMD BX, 4321H ;
... does a short test
...
0B90 8B E9 MOV BP, CX ; Save word count
... Do tests using all 256 data patters (slow)
Le test de mémoire commence à 0B59. Après quelques opérations le code saute
à l'adresse P1 (adresse 0879) lorsqu'il s'agit d'un test de mémoire
à froid (contrairement au test "à chaud" qui a lieu lors d'un reboot par
CTRL+ALT+DEL par exemple).
0B90 EB DC JMP SHORT P12 (0B6E)
Donc la cartouche remplace le MOV BP,CX à l'adresse 0B90 par un jump. Ce jump a lieu
juste après le test court et évite donc de passer du temps à tester la mémoire
en utilisant les 256 valeurs d'octet possibles. Le code cible à P12 rempli
la mémoire avec des zéros, comme s'il s'agissait d'un redémarrage à chaud,
restaure une série de registres et retourne.
; ------ Keyboard Interrupt Routine
1651 KB_INT PROC FAR
1561 FB STI
1562 ... a bunch of register pushes ...
156A FC CLD
156B E8 138B R CALL DDS
156E 8A E0 MOV AH, AL
Les octets 8A E0 dans le code original sont remplacés par E4 60. Devinez ce que c'est?
156E E4 60 IN AL, 60H
Exactement. Mais ceci est en fait un ajout de deux octets au début d'une
série de code pratiquement identique. En fait, la routine est pratiquement
identique, tel que mis en évidence en alignant les deux version ainsi:
Original code: 8a e0 3c ff 75 1b bb 80 00 b9 48 00 e8 b8 ca 80 26 17 00 f0 80 26 18 00 0f 80 26 88 00 1f e9 bb 00 24 7f 0e 07 bf 5c 14 b9 08 00 f2 ae 8a c4 74 03 e9 98 00
Patched code: e4 60 8a e0 3c ff 75 1b bb 80 00 b9 48 00 e8 b6 ca 80 26 17 00 f0 80 26 18 00 0f 80 26 88 00 1f e9 b9 00 24 7f 0e 07 bf 5c 14 b9 08 00 f2 ae 8a c4 75 1f 90
L'endroit où "E8 B8 CA" devient "E8 B6 CA" est simplement un CALL avec adressage relatif qui a du être ajusté car l'appelant est situé deux octets plus bas qu'avant.
Même chose pour le "E9 BB 00" qui devient "E9 B9 00" (cette fois c'est la cible d'un jump qui est ajustée).
159D 74 03 JE K17 ; Jump if match found
159F E9 163A R JMP K25 ; If no match, then no shift found
; ----- Shift key found
15A2 81 EF 145D R K17: SUB DI, OFFSET K6+1
...
...
163A K25:
...
Donc les 5 derniers octets étaient occupées par un "jump si égal" et un "jump" inconditionnel. La nouvelle série d'instruction doit occuper un maximum de 3 octets, et c'est le cas. Il s'agit désormais d'un "jump si non-égal" et d'un NOP qui ne fait rien.
159F 75 1F JNE 0x21 (15B4) ; Jump if no match found (shift not found)
15A1 90 NOP ; Fallthrough if match found
....
15A2 81 EF 145D R K17: SUB DI, OFFSET K6+1
15B4 08 26 0017 R OR KB_FLAG, AH ; Turn on shift bit
15B8 E9 164A R JMP K26 ; Interrupt return
...
15C0 75 7B JNZ K25
...
163A K25: ; test for hold state, test for break key and stuff
...
164A K26:
... series of POPs, then IRET.
Je ne m'explique pas complètement cette modification. Bon, je comprends l'idée
de remplacer le JE, JMP par la logique inverse ("jump" si pas égal, ou passe tout droit),
mais la raison pourquoi la cible du JNE est 15B4 n'est pas claire. Le code original
aurait sauté vers K25. Peut-être que K25 est trop éloigné pour être la cible d'un JNE, mais alors, pourquoi ne pas sauter vers 15C0, seulement 12 octets plus loin et pouvant parfaitement être la cible de ce JNE avec son argument 8 bit signé? 15C0 est en plein
milieu d'une routine, mais par chance, il s'agit d'une instruction JNZ K25. Le CPU
sauterait alors vers K25 immédiatement, puisque JNZ et JNE sont équivalents et la
condition préalable au saut (ZF à zéro) demeure.
0000 1000 1101 1011 08DB
0000 1000 1101 1100 08DC
0000 1000 1101 1xxx
Zone choisie quand [A14..3] est 0x08D8 (8 octets). (08D8 - 08DF). 5 octets originaux.
0000 1011 1001 0000 0B90
0000 1011 1001 0001 0B91
0000 1011 1001 000x
Zone choisie quand [A14..1] est 0x0B90 (2 octets). (0B90 - 0B91). Aucuns octets originaux.
0001 0101 0110 1110 156E
0001 0101 1010 0001 15A1
0001 0101 xxxx xxxx
Zone choisie quand [A14..8] est 0x1500 (256 octets). (1500 - 15FF). 204 octets originaux.