Je n'ai découvert ce défi que tout récemment. En faisant une recherche sur le site de Deckard pour tout autre chose, je suis arrivé par hasard sur la page consacrée à la saga des Archives et Thunderdocs, sur laquelle bien évidemment est faite une petite allusion à ce fameux défi du chapitre 10 des Archives ! Deckard n'y va d'ailleurs pas par quatre chemins puisqu'il annonce clairement que ce défi (je le cite) "n'a jamais été remporté par qui que ce soit à (sa) connaissance ! !". Il n'en fallait pas plus pour que je me jette à l'eau...
À noter, que bien évidemment, je n'ai pas récupéré la disquette de sources que Deckard propose sur son site et qui, je le suppose, doit bien aider, mais totalement fausser le défi. L'objectif ici n'étant pas de parader (le défi étant fini depuis à peine 25 ans hein...) mais bien de montrer tout le cheminement pour y arriver (donc sans raccourcis ni tricheries).
Comme vous en avez l'habitude maintenant (là je m'adresse aux 2 lecteurs des précédents défis...), on part de zéro et je montre tout le processus  jusqu'à la résolution complète (enfin je l'espère), étape par étape (quitte parfois à partir sur  des fausses pistes ou à faire des trucs inutiles).
Je vais scinder le tout en deux articles : un premier (celui-ci) qui traitera de la partie recherche/localisation/compréhension du code du défi. Et un second qui traitera spécifiquement de sa résolution concrète  (programmation/écriture sur disque etc.).

Étape 1 : lancement de Archives 10 et de son défi (ça devrait être l'étape la plus facile).

Cela semble tout con mais avant de partir tête baissée (dans le mur), plusieurs petites choses, qui peuvent s'avérer utiles, sont à remarquer :

  • Pour accéder à la page même du défi, il faut appuyer sur ESC lors du boot.
  • La page texte s'affiche par scrolling vertical venant du bas de l'écran avec un petit effet sonore.
  • Trois indications dans le texte en lui-même : "Ne modifier que les datas !", "signer en INVERSE dans le rectangle" et "WARNING : C'EST CHIANT". Bon bah au moins on est prévenu. Visiblement ça va être galère, on peut s'attendre à du cryptage (et donc il faudra recrypter notre propre texte, on n'a pas le droit de tout virrer pour mettre un petit "coucou" affiché par un PRINT par exemple, mais ça on s'en doutait un peu). Quant au "INVERSE dans le rectangle", un peu plus mystérieux en fait... On verra peut-être plus tard pourquoi en INVERSE particulièrement.

Étape 2 : le Boot 0. Qu'est-ce qui se passe en $801 ?

Deux options possibles. On peut éditer le secteur 0, piste 0 directement sous DiskFixer pour checker le code à partir de $801. Ou on "boot trace" classiquement. On va utiliser la méthode DiskFixer (pour changer un peu).
On lance donc DiskFixer, on lit le Secteur 00/ Piste 00. On se positionne en $01 et on appuie sur L pour avoir le code. On a donc sous nos yeux (ébahis), ce qui va se charger lors du boot en mémoire en $801+ :

0801-   A9 60       LDA   #$60
0803-   8D 01 08    STA   $0801
0806-   A9 FF       LDA   #$FF
0808-   8D FB 04    STA   $04FB
080B-   8D F3 03    STA   $03F3
080E-   2C 52 C0    BIT   $C052
0811-   8D 00 C0    STA   $C000
0814-   8D 02 C0    STA   $C002
0817-   8D 04 C0    STA   $C004
081A-   8D 0C C0    STA   $C00C
081D-   8D 0E C0    STA   $C00E
0820-   8D 81 C0    STA   $C081
0823-   20 2F FB    JSR   $FB2F
0826-   20 58 FC    JSR   $FC58
0829-   20 84 FE    JSR   $FE84
082C-   20 93 FE    JSR   $FE93
082F-   A9 0F       LDA   #$0F
0831-   85 50       STA   $50
0833-   A4 50       LDY   $50
0835-   A6 2B       LDX   $2B
0837-   B9 5B 08    LDA   $085B,Y
083A-   85 3D       STA   $3D
083C-   B9 6B 08    LDA   $086B,Y
083F-   F0 05       BEQ   $0846
0841-   85 27       STA   $27
0843-   20 5C C6    JSR   $C65C
0846-   C6 50       DEC   $50
0848-   A5 50       LDA   $50
084A-   C9 06       CMP   #$06
084C-   D0 E5       BNE   $0833
084E-   A6 2B       LDX   $2B
0850-   8E 39 6C    STX   $6C39
0853-   20 7B 08    JSR   $087B ; saut vers routine d'affichage du message Boot
0856-   A9 00       LDA   #$00
0858-   4C B6 08    JMP   $08B6
 
085B-   00          BRK
085C-   0D 0B 09    ORA   $090B
085F-   07          ???
0860-   05 03       ORA   $03
0862-   01 0E       ORA   ($0E,X)
0864-   0C          ???
0865-   0A          ASL
0866-   08          PHP
0867-   06 04       ASL   $04
0869-   02          ???
086A-   0F          ???
086B-   00          BRK
086C-   00          BRK
086D-   00          BRK
086E-   00          BRK
086F-   00          BRK
0870-   00          BRK
0871-   00          BRK
0872-   A0 04       LDY   #$04
0874-   05 06       ORA   $06
0876-   07          ???
0877-   BA          TSX
0878-   9B          ???
0879-   B6 B7       LDX   $B7,Y
 
087B-   A2 00       LDX   #$00 ; affichage msg Boot
087D-   BD 8F 08    LDA   $088F,X
0880-   9D 00 04    STA   $0400,X
0883-   E8          INX
0884-   E0 27       CPX   #$27
0886-   D0 F5       BNE   $087D
0888-   A2 60       LDX   #$60
088A-   2C 10 C0    BIT   $C010
088D-   60          RTS
 
088E-   20 20 20    JSR   $2020 ; msg boot
0891-   D3          ???
0892-   0D 0F 0F    ORA   $0F0F
0895-   14          ???
0896-   08          PHP
0897-   20 C6 01    JSR   $01C6
089A-   13          ???
089B-   14          ???
089C-   20 C2 0F    JSR   $0FC2
089F-   0F          ???
08A0-   14          ???
08A1-   20 20 20    JSR   $2020
08A4-   28          PLP
08A5-   EB          ???
08A6-   29 20       AND   #$20
08A8-   C5 04       CMP   $04
08AA-   04          ???
08AB-   09 05       ORA   #$05
08AD-   20 C8 01    JSR   $01C8
08B0-   17          ???
08B1-   0B          ???
08B2-   20 20 20    JSR   $2020
08B5-   20 
 
08B6-   8D F2 03    STA   $03F2 ; vecteur RESET
08B9-   A9 BA       LDA   #$BA
08BB-   8D F3 03    STA   $03F3 ; idem
08BE-   49 A5       EOR   #$A5
08C0-   8D F4 03    STA   $03F4 ; idem
08C3-   A9 20       LDA   #$20
08C5-   85 E6       STA   $E6
08C7-   20 F2 F3    JSR   $F3F2 ; efface page courante HGR
08CA-   2C 10 C0    BIT   $C010 ; strobe Keyboard
08CD-   8D 02 C0    STA   $C002 ; R mémoire principale
08D0-   8D 04 C0    STA   $C004 ; W mémoire principale
08D3-   8D 08 C0    STA   $C008 ; pile/P0 mémoire principale
08D6-   20 00 B6    JSR   $B600 ; lecture écran intro
08D9-   04          ???
08DA-   EA          NOP
08DB-   AD 00 C0    LDA   $C000 ; lecture clavier
08DE-   C9 9B       CMP   #$9B  ; appui sur ESC ?
08E0-   D0 0D       BNE   $08EF
08E2-   20 00 B6    JSR   $B600 ; oui
08E5-   00          BRK
08E6-   20 44 83    JSR   $8344 ; <== ICI !
08E9-   2C 10 C0    BIT   $C010
08EC-   4C D6 08    JMP   $08D6
08EF-   20 00 B6    JSR   $B600
08F2-   03          ???   <=
08F3-   AD 61 C0    LDA   $C061
08F6-   30 03       BMI   $08FB
08F8-   4C 00 9B    JMP   $9B00
08FB-   4C B4 9B    JMP   $9BB4
08FE-   00          BRK
08FF-   00          BRK

Voici donc tout ce qui se trouve entre $801 et $8FF. Ici on a deux choses bien distinctes. Tout d'abord le Fast Boot en lui-même, avec les initialisations, le chargement de sa RWTS et ses appels (les JSR $B600) avec son passage de paramètres particuliers (valeur qui suit le JSR). Et ensuite les modifications effectuées sur ce Fast Boot pour y incorporer le défi :
On repère immédiatement la lecture Clavier rajoutée avec le test de l'appui sur ESC. Si oui la RWTS du Fast Boot charge quelque chose et on saute en $8344. On voit que si on n'appuie sur aucune touche (ou si ce n'est pas ESC), on passe par dessus ce $8344.

Étape 3 : préparation du disque pour nous faciliter la vie.

Puisque nous sommes sous DiskFixer, on va donc modifier immédiatement (sur une copie bien évidemment) quelques petits trucs de façon à travailler de manière plus détendue...

On va déjà supprimer le test du ESC pour avoir systématiquement l'affichage du défi (et nous économiser un peu) : $8E0 : EA EA
Ensuite, on va remplacer le JSR $8344 par un joli BRK : $8E6 : 00

L'objectif ici est de récupérer la main tout de suite avant le lancement (ou supposé être) du défi. On sauvegarde les modifications et on relance Archives.

Comme prévu, on récupère la main après l'écran d'intro. Et on vérifie tout de suite par un $8344G ce que l'on supposait : petit chargement et affichage du défi. OK on est sur la bonne voie !

Étape 4 : $8344

On relance et cette fois au lieu d'exécuter, on désassemble ce qui se trouve en $8344. Comme on a la main, aucun problème. On obtient :

8344-   A2 00       LDX   #$00
8346-   BD 00 02    LDA   $0200,X ; sauvegarde du buffer d'entrée
8349-   9D 00 7C    STA   $7C00,X ; on travaille proprement môsieur !
834C-   E8          INX
834D-   D0 F7       BNE   $8346
 
834F-   A2 00       LDX   #$00
8351-   BD 61 83    LDA   $8361,X
8354-   49 55       EOR   #$55    ; décryptage du code
8356-   9D 00 02    STA   $0200,X ; écriture dans le buffer d'entrée
8359-   E8          INX
835A-   E0 37       CPX   #$37
835C-   D0 F3       BNE   $8351
835E-   4C 00 02    JMP   $0200   ; exécution

Cette routine sert à décrypter une portion de code vers le buffer d'entrée/clavier et à l'exécuter.
Problème, même si on empêche le JMP $0200, on ne pourra pas voir ce qui s'y passe, car en tant que buffer d'entrée, et bien comme son nom l'indique, dès que l'on tape quelque chose au clavier, son contenu est modifié !
Deckard utilise ici une technique courante à l'époque dans la protection des logiciels, en utilisant ce buffer temporairement pour son propre compte.
On va donc modifier ici le STA $0200,X pour que le code décrypté (que l'on veut étudier) soit écrit dans une zone facilement accessible. Disons $2000 (il n'y a pas d'utilisation pour le moment de la page HGR1). Let's go...
On modifie directement en mémoire :
8356 : 9D 00 20 ; STA $2000,X
835E : 00             ; BRK (pour ne pas jumper en $200 évidemment)
8344G                  ; on lance le décodage

On récupère la main grâce au BRK et on n'a plus qu'à aller voir ce qui a été décrypté en $2000.

Étape 5 : $2000 (ce qui aurait dû être en $200 pour ceux qui ne dorment pas encore)

0200-   A9 00       LDA   #$00
0202-   85 06       STA   $06
0204-   A9 80       LDA   #$80
0206-   85 07       STA   $07
0208-   A9 55       LDA   #$55
020A-   85 1A       STA   $1A
020C-   09 CC       ORA   #$CC
020E-   29 A2       AND   #$A2
0210-   8D 36 02    STA   $0236
0213-   18          CLC
0214-   6E 34 02    ROR   $0234
0217-   38          SEC
0218-   6E 34 02    ROR   $0234
021B-   6E 34 02    ROR   $0234
021E-   A2 00       LDX   #$00
0220-   A0 00       LDY   #$00
0222-   B1 06       LDA   ($06),Y ; on décode
0224-   45 1A       EOR   $1A     ; par un EOR
0226-   91 06       STA   ($06),Y ; ce qui est en $8000-$8300
0228-   85 1A       STA   $1A
022A-   C8          INY
022B-   D0 F5       BNE   $0222
022D-   E6 07       INC   $07
022F-   E8          INX
0230-   E0 04       CPX   #$04
0232-   D0 EE       BNE   $0222
0234-   60          RTS  ; attention ça va changer ça :)
0235-   00          BRK  ;
0236-   00          BRK  ; ça aussi

Attention, ici j'ai remis les adresses de base (c'est à dire $200+) pour faciliter la compréhension du code. Mais nous sommes toujours en fait, en temps réel, en $2000 après détournement de la routine précédente.

Le gros de la routine consiste encore en une phase de décryptage par un EOR. À la limite on s'en fout un peu de la façon dont c'est crypté car ce qui nous intéresse c'est le résultat. Ici la subtilité réside dans la modification du RTS de fin de routine. N'oubliez pas qu'on est censé arriver là par un JMP $200 donc si on rencontrait un vrai RTS en $236, on sortirait tout simplement de l'appel qui a commencé bien plus haut vers $8344 et donc du défi ! Pourtant, on est encore loin d'être arrivé au bout là (si si je vous assure), donc qu'est ce qui se passe ?
Et bien tout simplement, le début de la routine modifie ce RTS pour le transformer en un JMP $8000 ! Évidemment l'ami Deckard s'est fait plaisir et n'a pas utilisé de simples LDA/STA trop vulgaires... non non ! Voyons un peu ça en détails :

0208-   A9 55       LDA   #$55  ; $55 dans A
020A-   85 1A       STA   $1A
020C-   09 CC       ORA   #$CC  ; $55 ORA $CC = $DD
020E-   29 A2       AND   #$A2  ; $DD AND $A2 = $80
0210-   8D 36 02    STA   $0236 ; $80 en $236
0213-   18          CLC         ; vide la retenue
0214-   6E 34 02    ROR   $0234 ; rotation à droite du $60 (pas de retenue)
                                ; 01100000 devient 00110000
0217-   38          SEC         ; retenue à 1
0218-   6E 34 02    ROR   $0234 ; nouvelle rotation à droite (avec retenue)
                                ; 00110000 devient 10011000
021B-   6E 34 02    ROR   $0234 ; et encore une (pas de retenue)
                                ; 10011000 devient 01001100 = $4C !

Et voilà comment 60 00 00 devient 4C 00 80 soit un RTS qui devient un JMP $8000 !
La routine après décryptage saute donc en $8000 (logique puisque c'est justement ce qui aura été décrypté). Là encore, il faut intercepter ce dernier JMP pour avoir la main après le décryptage du code. Le plus simple c'est tout simplement de noper les 3 ROR $0234. Le $60 n'étant plus modifié nous rendra alors la main à la fin de la routine.

$2014 : EA EA EA
$2018 : EA EA EA
$201B : EA EA EA
$2000G

On peut aussi juste mettre un $2C (BIT) en $2014, $2018 et $201B. C'est moins long à taper et ça fait plus l33t ! Voyons voir le résultat en $8000...

Étape 6 : $8000  - Les choses sérieuses commencent...

8000-   A9 6E       LDA   #$6E
8002-   85 27       STA   $27  ; buffer Hi
8004-   A9 00       LDA   #$00
8006-   85 26       STA   $26
8008-   A9 22       LDA   #$22 ;
800A-   A2 00       LDX   #$00
800C-   A0 0C       LDY   #$0C ;
800E-   85 41       STA   $41  ; piste ($22)
8010-   86 FE       STX   $FE  ; secteur
8012-   84 FF       STY   $FF  ; nb Secteurs à charger ($0C)
8014-   2C E9 C0    BIT   $C0E9 ; drive on
8017-   20 ED 80    JSR   $80ED ; temporisation
801A-   A2 60       LDX   #$60
801C-   BD 8E C0    LDA   $C08E,X ; lecture on
801F-   A9 D0       LDA   #$D0
8021-   A2 E4       LDX   #$E4
8023-   A0 60       LDY   #$60
8025-   20 94 80    JSR   $8094 ; autopatch1
8028-   A9 60       LDA   #$60
802A-   8D 14 81    STA   $8114 ; RTS en 8114
802D-   20 24 81    JSR   $8124 ; check headers address
8030-   20 FB 80    JSR   $80FB ; on récupère la piste courante
8033-   8D 9E 80    STA   $809E ; sauvegarde
8036-   0A          ASL
8037-   8D 78 04    STA   $0478 ; piste actuelle
803A-   A9 EA       LDA   #$EA
803C-   8D 14 81    STA   $8114 ; NOP en 8114
803F-   AA          TAX
8040-   A8          TAY
8041-   20 94 80    JSR   $8094 ; NOP en 8140/41/42 (autopatch2)
 
=======================================
     routine / boucle principale
=======================================
 
8044-   A4 FF       LDY   $FF ; $0C +
8046-   A5 27       LDA   $27 ; $6E
8048-   18          CLC
8049-   65 FF       ADC   $FF ; = $7A
804B-   85 27       STA   $27
804D-   E6 27       INC   $27 ; = $7B = buffer (Hi) (fin)
804F-   88          DEY
8050-   D0 06       BNE   $8058
 
8052-   20 AF 80    JSR   $80AF ; positionnement sur piste
8055-   4C 6C 80    JMP   $806C ; lecture
 
8058-   E6 FE       INC   $FE ; on positionne le secteur de départ
                              ; (00 + nbSecteurs à lire - 1)
805A-   A5 FE       LDA   $FE
805C-   C9 10       CMP   #$10 ; > 15
805E-   D0 06       BNE   $8066
8060-   A9 00       LDA   #$00
8062-   85 FE       STA   $FE
8064-   E6 41       INC   $41 ; piste suivante (si besoin)
8066-   88          DEY       ;
8067-   D0 EF       BNE   $8058
8069-   4C 52 80    JMP   $8052
 
806C-   A4 FE       LDY   $FE
806E-   B9 9F 80    LDA   $809F,Y ; table secteur dos 3.3/physique
8071-   85 3D       STA   $3D  ; secteur physique
8073-   20 22 81    JSR   $8122 ; Lecture data
8076-   C6 FE       DEC   $FE
8078-   10 06       BPL   $8080
807A-   A9 0F       LDA   #$0F
807C-   85 FE       STA   $FE
807E-   C6 41       DEC   $41
8080-   C6 27       DEC   $27
8082-   C6 FF       DEC   $FF ; nb secteur à lire - 1
8084-   D0 CC       BNE   $8052 ; il en reste ?
 
8086-   AD 9E 80    LDA   $809E ; piste sauvée à l'entrée de la routine
8089-   85 41       STA   $41   ;
808B-   20 AF 80    JSR   $80AF ; on se repositionne dessus !
808E-   2C E8 C0    BIT   $C0E8 ; arrêt drive
8091-   4C 98 81    JMP   $8198 ; suite
 
8094-   8D 40 81    STA   $8140
8097-   8E 41 81    STX   $8141
809A-   8C 42 81    STY   $8142
809D-   60          RTS   
 
809E-   00          BRK   
 
=======================================
Table Dos 3.3 Sector -> Physical Sector
=======================================
809F-   00          BRK
80A0-   0D 0B 09    ORA   $090B
80A3-   07          ???
80A4-   05 03       ORA   $03
80A6-   01 0E       ORA   ($0E,X)
80A8-   0C          ???
80A9-   0A          ASL
80AA-   08          PHP
80AB-   06 04       ASL   $04
80AD-   02          ???
80AE-   0F          ???
=======================================
  routine de déplacement tête lecture
=======================================
80AF-   A2 60       LDX   #$60
80B1-   A5 41       LDA   $41    ; piste destination
80B3-   0A          ASL          ;
80B4-   85 43       STA   $43    ; dans $43
80B6-   AD 78 04    LDA   $0478  ; piste actuelle
80B9-   85 FD       STA   $FD
80BB-   38          SEC
80BC-   E5 43       SBC   $43    ; piste destination
80BE-   F0 2C       BEQ   $80EC
80C0-   B0 05       BCS   $80C7
80C2-   EE 78 04    INC   $0478
80C5-   90 03       BCC   $80CA
80C7-   CE 78 04    DEC   $0478
80CA-   20 E0 80    JSR   $80E0
80CD-   20 ED 80    JSR   $80ED  ; tempo
80D0-   A5 FD       LDA   $FD    ; piste actuelle
80D2-   29 03       AND   #$03
80D4-   0A          ASL
80D5-   09 60       ORA   #$60
80D7-   A8          TAY
80D8-   B9 80 C0    LDA   $C080,Y
80DB-   20 ED 80    JSR   $80ED  ; tempo
80DE-   F0 D6       BEQ   $80B6
 
80E0-   AD 78 04    LDA   $0478
80E3-   29 03       AND   #$03
80E5-   0A          ASL
80E6-   09 60       ORA   #$60
80E8-   A8          TAY
80E9-   B9 81 C0    LDA   $C081,Y
80EC-   60          RTS
 
=======================================
      routine de temporisation
=======================================
80ED-   A9 28       LDA   #$28
80EF-   38          SEC
80F0-   48          PHA
80F1-   E9 01       SBC   #$01
80F3-   D0 FC       BNE   $80F1
80F5-   68          PLA
80F6-   E9 01       SBC   #$01
80F8-   D0 F6       BNE   $80F0
80FA-   60          RTS
=======================================
          routine lecture et
     décodage 4:4 nibbles address
=======================================
80FB-   A0 02       LDY   #$02 ; 2 passages (param : volume/piste)
80FD-   2C *A0 03   BIT   $03A0 ; dummy code (BIT)
 
80FE-  *A0 03       LDY   #$03 ; 3 passages (param : volume/piste/secteur)
8100-   85 40       STA   $40 ; (avant dernier param)
8102-   BD 8C C0    LDA   $C08C,X ;
8105-   10 FB       BPL   $8102
8107-   2A          ROL
8108-   85 3C       STA   $3C
810A-   BD 8C C0    LDA   $C08C,X ;
810D-   10 FB       BPL   $810A
810F-   25 3C       AND   $3C
8111-   88          DEY
8112-   D0 EC       BNE   $8100
8114-   60          RTS  ; <= modifié (EA)
=======================================
 
8115-   28          PLP
8116-   C5 3D       CMP   $3D   ; est-ce le secteur voulu ?
8118-   D0 08       BNE   $8122 ; non on passe au secteur suivant
811A-   A5 40       LDA   $40   ; est-ce qu'on est sur la bonne piste ?
811C-   C5 41       CMP   $41   ;
811E-   D0 02       BNE   $8122 ; non
8120-   B0 01       BCS   $8123
 
=======================================
  routine de lecture Disk bas niveau
=======================================
8122-   18          CLC   ; set carry (pour que la boucle se fasse plus bas)
8123-   08          PHP
=======================================
8124-   A2 60       LDX   #$60
8126-   BD 8C C0    LDA   $C08C,X
8129-   10 FB       BPL   $8126
812B-   49 D5       EOR   #$D5
812D-   D0 F7       BNE   $8126
812F-   BD 8C C0    LDA   $C08C,X
8132-   10 FB       BPL   $812F
8134-   C9 AA       CMP   #$AA
8136-   D0 F3       BNE   $812B
8138-   EA          NOP
8139-   BD 8C C0    LDA   $C08C,X
813C-   10 FB       BPL   $8139
813E-   C9 96       CMP   #$96
8140-   D0 E4       BNE   $8126 ; modifiés
8142-   60          RTS         ; "
 
8143-   F0 B9       BEQ   $80FE ; décodage données adresses
8145-   28          PLP
8146-   90 DA       BCC   $8122 ; boucle si Carry Set ! (check headers address)
8148-   49 AD       EOR   #$AD ; 3eme header champ data
814A-   F0 02       BEQ   $814E
814C-   D0 D4       BNE   $8122
--------------------------------------- ; Post "nibblelisation" 6:2
814E-   A0 56       LDY   #$56
8150-   84 3C       STY   $3C
8152-   BC 8C C0    LDY   $C08C,X
8155-   10 FB       BPL   $8152
8157-   59 FE 81    EOR   $81FE,Y ; Table
815A-   A4 3C       LDY   $3C
815C-   88          DEY
815D-   99 28 82    STA   $8228,Y ; Buffer secondaire
8160-   D0 EE       BNE   $8150
8162-   84 3C       STY   $3C
8164-   BC 8C C0    LDY   $C08C,X
8167-   10 FB       BPL   $8164
8169-   59 FE 81    EOR   $81FE,Y
816C-   A4 3C       LDY   $3C
816E-   91 26       STA   ($26),Y ; Buffer primaire
8170-   C8          INY
8171-   D0 EF       BNE   $8162
8173-   BC 8C C0    LDY   $C08C,X
8176-   10 FB       BPL   $8173
8178-   59 FE 81    EOR   $81FE,Y
817B-   D0 A5       BNE   $8122
817D-   A0 00       LDY   #$00
817F-   A2 56       LDX   #$56
8181-   CA          DEX
8182-   30 FB       BMI   $817F
8184-   84 1A       STY   $1A
8186-   B1 26       LDA   ($26),Y
8188-   5E 28 82    LSR   $8228,X
818B-   2A          ROL
818C-   5E 28 82    LSR   $8228,X
818F-   2A          ROL
8190-   45 1A       EOR   $1A	; <== ohohoho ! 
8192-   91 26       STA   ($26),Y
8194-   C8          INY
8195-   D0 EA       BNE   $8181
8197-   60          RTS
=======================================
    routine de décodage "type" 4:4
         7000 et 7600 => 7000
=======================================
8198-   A9 00       LDA   #$00
819A-   85 EC       STA   $EC
819C-   A9 60       LDA   #$60
819E-   85 ED       STA   $ED
81A0-   A9 00       LDA   #$00
81A2-   85 EE       STA   $EE
81A4-   A9 63       LDA   #$63
81A6-   85 EF       STA   $EF
81A8-   A9 00       LDA   #$00
81AA-   85 06       STA   $06
81AC-   A9 70       LDA   #$70
81AE-   85 07       STA   $07
81B0-   A9 00       LDA   #$00
81B2-   85 08       STA   $08
81B4-   A9 76       LDA   #$76
81B6-   85 09       STA   $09
81B8-   A2 00       LDX   #$00
81BA-   A0 00       LDY   #$00
 
81BC-   B1 06       LDA   ($06),Y  ; $7000
81BE-   38          SEC
81BF-   2A          ROL
81C0-   85 1A       STA   $1A
81C2-   B1 08       LDA   ($08),Y  ; $7600
81C4-   25 1A       AND   $1A
81C6-   91 06       STA   ($06),Y  ; $7000
81C8-   C8          INY
81C9-   D0 F1       BNE   $81BC
81CB-   E6 07       INC   $07
81CD-   E6 09       INC   $09
81CF-   E8          INX
81D0-   E0 06       CPX   #$06
81D2-   D0 E8       BNE   $81BC
 
=======================================
 routine qui dispatche un octet sur 2
 depuis 7000 jusqu'à 7600 vers
          6000+ et 6300+
=======================================
 
81D4-   A9 02       LDA   #$02
81D6-   85 1A       STA   $1A
81D8-   20 F2 81    JSR   $81F2
81DB-   E6 ED       INC   $ED
81DD-   E6 EF       INC   $EF
81DF-   EE 05 82    INC   $8205
81E2-   EE 0B 82    INC   $820B
81E5-   20 F2 81    JSR   $81F2
81E8-   C6 1A       DEC   $1A
81EA-   D0 EF       BNE   $81DB
81EC-   20 00 60    JSR   $6000  ; vers décodage texte etc.
81EF-   4C 30 83    JMP   $8330  ; sortie
 
81F2-   A0 00       LDY   #$00
81F4-   20 01 82    JSR   $8201
81F7-   EE 05 82    INC   $8205
81FA-   EE 0B 82    INC   $820B
81FD-   20 01 82    JSR   $8201
8200-   60          RTS   
 
8201-   A2 00       LDX   #$00
8203-   BD 00 70    LDA   $7000,X
8206-   91 EC       STA   ($EC),Y ; $6000
8208-   E8          INX
8209-   BD 00 70    LDA   $7000,X
820C-   91 EE       STA   ($EE),Y ; $6300
820E-   C8          INY
820F-   E8          INX
8210-   D0 F1       BNE   $8203
8212-   60          RTS   
 
=======================================
 
8213-   AF          ???
8214-   C4 C5       CPY   $C5
8216-   C3          ???
8217-   CB          ???
8218-   C1 D2       CMP   ($D2,X)
821A-   C4 AF       CPY   $AF
821C-   CC CF C1    CPY   $C1CF
821F-   C4 C5       CPY   $C5
8221-   D2          ???
8222-   C3          ???
8223-   B6 B0       LDX   $B0,Y
8225-   B0 A3       BCS   $81CA
8227-   B2          ???
8228-   00          BRK
8229-   00          BRK
822A-   00          BRK
822B-   00          BRK
822C-   00          BRK
822D-   00          BRK   
 
=======================================
   data pour la routine Move ($FE2C)
=======================================
8328-   FD FA 1E    SBC   $1EFA,X
832B-   FE 60 FE    INC   $FE60,X
832E-   00          BRK
832F-   80          ???   
 
=======================================
    routine de fin -  "nettoyage"
=======================================
8330-   A2 00       LDX   #$00
8332-   BD 28 83    LDA   $8328,X
8335-   95 3C       STA   $3C,X
8337-   E8          INX
8338-   E0 08       CPX   #$08
833A-   D0 F6       BNE   $8332
833C-   A0 00       LDY   #$00
833E-   20 2C FE    JSR   $FE2C ; Move vers $8000
8341-   4C 09 61    JMP   $6109 ; fin + sortie "défi"

Et oui ça pique un peu ! On se retrouve ici en fait face à une des deux routines principales du défi !
Et c'est carrément une routine complète de lecture bas niveau de Disk à laquelle on a affaire. Avec déplacement de la tête de lecture, table de conversion secteurs physiques/secteurs logiques, lecture de nibbles, et enfin post-nibblelisation ! Plus quelques petites routines de déplacements de données et de décodages histoire de ne pas avoir fait le voyage jusqu'ici pour rien...

Ce que fait ce code :

  • lecture de 12 secteurs à partir du secteur $00 de la piste $22 vers un buffer situé en $7000.  En fait plus exactement, la routine lit les secteurs de façon décroissante vers un buffer décroissant lui aussi. Ce qui revient au même...
  • EOR effectué entre chaque octet lu et la valeur de sa position dans le secteur (exemple : le premier octet de chaque secteur sera EORé avec $00, le suivant avec $01, etc.). À noter que ce EOR est intégré à la routine de lecture même (en $8190).
  • décodage des données qui ont été lues : c'est un décodage de type 4:4 (un octet final étant codé sur 2 octets) "réunissant" les données en $7000+ et $7600+, le tout remis en $7000+.
    ($7000/7600->$7000, $7001/$7601->$7001,$7002/$7602->$7002 etc.).
  • Les données en $7000-$7600 ainsi obtenues sont dispatchées un octet sur deux entre $6000+ et $6300+. ($7000->$6000, $7001->$6300, $7002->$6001,$7003->$6301, etc.).
  • la routine fait un appel en $6000 ($81EC : JSR $6000) qui est la routine de décodage du texte en lui-même.
  • au retour, un peu de nettoyage est fait (utilisation de la routine Monitor Move) et on JuMP en $6109 pour la suite et fin du défi (affichage).
  • c'est tout ! (comment ça bobo tête ? !)

 

Voilà pour les grandes lignes, avec quelques petites choses à noter en plus :

Deckard a optimisé son code au maximum. Celui-ci s'autopatche afin de modifier certaines parties qui seront ainsi réutilisées plus tard. Cela rend le suivi des évènements un peu alambiqué certes mais c'est particulièrement bien foutu il faut avouer.

Autre astuce utilisée, le coup du dummy code (utilisation de l'opcode BIT). Exemple :

80FB-   A0 02      LDY   #$02
80FD-   2C *A0 03  BIT   $03A0
8100-   85 40      STA   $40 
 
*80FE-  A0 03      LDY   #$03
8100-   85 40      STA   $40

Quand on passe la première fois sur le BIT $03A0  en $80FD, l'instruction ne change rien et on continue normalement (on a donc $02 dans Y). Par contre si on arrive en $80FE par un saut, on tombe sur un A0 03 soit LDY #$03 ! Et le code continue de la même façon. La même routine est utilisée, mais suivant où on arrive, Y en sortira avec une valeur différente, le tout sans JMP supplémentaire !

J'ai essayé de détailler et de commenter l'ensemble du code le plus possible. N'hésitez donc pas à y jeter un œil attentif !
Il est temps maintenant de passer à la suite... C'est à dire, de voir un peu ce qui se passe en $6000.

On place donc un BRK en $81EC pour shunter le JSR et on lance la routine ($8000G) pour effectuer la lecture de la suite et son décodage.

Étape 7 : $6000 - This is the End...

Voici donc la dernière partie du défi. Tout ce qui se trouve en $6000+ et qui aura été directement lu/décodé depuis la partie précédente :

6000-   A9 A7       LDA   #$A7
6002-   85 06       STA   $06
6004-   A9 61       LDA   #$61
6006-   85 07       STA   $07
6008-   A9 00       LDA   #$00
600A-   85 08       STA   $08
600C-   A9 65       LDA   #$65
600E-   85 09       STA   $09
6010-   A9 80       LDA   #$80
6012-   85 1A       STA   $1A  ; valeur ORA
6014-   A9 00       LDA   #$00
6016-   85 ED       STA   $ED ; index "codage"
6018-   85 EF       STA   $EF ; index source
601A-   85 EE       STA   $EE ; index destination
601C-   A8          TAY   
 
601D-   20 64 60    JSR   $6064 ; retourne $EC
6020-   A5 EC       LDA   $EC ;
6022-   F0 19       BEQ   $603D ; si 00 changement de codage
6024-   84 EF       STY   $EF ; sauvegarde index Buffer source
6026-   A4 EE       LDY   $EE ; index Y destination
6028-   A6 EC       LDX   $EC ; index X table
602A-   BD BD 60    LDA   $60BD,X ; table
602D-   05 1A       ORA   $1A ; attribut affichage
602F-   91 08       STA   ($08),Y ; $6500+ destination finale (texte décodé)
6031-   C8          INY
6032-   D0 02       BNE   $6036
6034-   E6 09       INC   $09
6036-   84 EE       STY   $EE
6038-   A4 EF       LDY   $EF ; index Y source
603A-   4C 1D 60    JMP   $601D
 
=======================================
    changement de la valeur de $1A
 (fixe les 3 derniers bits / attribut)
=======================================
603D-   20 64 60    JSR   $6064
6040-   A5 EC       LDA   $EC
6042-   F0 19       BEQ   $605D ; quit si second 00
6044-   4A          LSR
6045-   B0 09       BCS   $6050 ; $EC = $xxxxxxx1
6047-   4A          LSR
6048-   B0 09       BCS   $6053 ; $EC = $xxxxxx10
604A-   4A          LSR
604B-   B0 09       BCS   $6056 ; $EC = $xxxxx100
604D-   90 0A       BCC   $6059 ; tout le reste
 
604F-   2C *A9 80   BIT   $80A9
6050-   *A9 80      LDA   #$80 ; 10000000 ; 
 
6052-   2C *A9 00   BIT   $00A9
6053-   *A9 00      LDA   #$00 ; 00000000 ; 
 
6055-   2C *A9 40   BIT   $40A9
6056-   *A9 40      LDA   #$40 ; 01000000 ; 
 
6058-   2C *A9 E0   BIT   $E0A9
6059-   *A9 E0      LDA   #$E0 ; 11100000 ; 
 
605B-   2C A9 *60   BIT   $60A9
605D-   *60         RTS   
 
605E-   85 1A       STA   $1A
6060-   4C 1D 60    JMP   $601D
 
6063-   2C          ???
 
=======================================
     renvoie l'index de table ($EC)
     depuis le buffer $61A7 (codé)
     + incrémente index type codage
=======================================
6064-   A5 ED       LDA   $ED ; compteur type codage (0-3)
6066-   F0 18       BEQ   $6080
6068-   C9 01       CMP   #$01
606A-   F0 1D       BEQ   $6089
606C-   C9 02       CMP   #$02
606E-   F0 36       BEQ   $60A6
 
6070-   B1 06       LDA   ($06),Y ; type 3
6072-   29 3F       AND   #$3F ; 00111111 => 00xxxxxxxx
6074-   85 EC       STA   $EC
6076-   C8          INY
6077-   D0 02       BNE   $607B
6079-   E6 07       INC   $07
607B-   A9 00       LDA   #$00
607D-   85 ED       STA   $ED
607F-   60          RTS   
 
6080-   B1 06       LDA   ($06),Y ; type 0
6082-   4A          LSR   ; 0xxxxxxx
6083-   4A          LSR   ; 00xxxxxx
6084-   85 EC       STA   $EC
6086-   E6 ED       INC   $ED
6088-   60          RTS   
 
6089-   B1 06       LDA   ($06),Y ; type 1
608B-   29 03       AND   #$03 ; 00000011 => 000000xx
608D-   0A          ASL   ; 00000xx0
608E-   0A          ASL   ; 0000xx00
608F-   0A          ASL   ; 000xx000
6090-   0A          ASL   ; 00xx0000 (1)
6091-   85 EC       STA   $EC
6093-   C8          INY
6094-   D0 02       BNE   $6098
6096-   E6 07       INC   $07
6098-   B1 06       LDA   ($06),Y ; xxxxxxxx
609A-   4A          LSR   ; 0xxxxxxx
609B-   4A          LSR   ; 00xxxxxx
609C-   4A          LSR   ; 000xxxxx
609D-   4A          LSR   ; 0000xxxx (2)
609E-   18          CLC
 
609F-   65 EC       ADC   $EC
60A1-   85 EC       STA   $EC (1)+(2)/(3)+(4) = 00xxxxxx
60A3-   E6 ED       INC   $ED
60A5-   60          RTS   
 
60A6-   B1 06       LDA   ($06),Y ; type 2
60A8-   29 0F       AND   #$0F ; 00001111 => 0000xxxx
60AA-   0A          ASL        ; 000xxxx0
60AB-   0A          ASL        ; 00xxxx00 (3)
60AC-   85 EC       STA   $EC
60AE-   C8          INY
60AF-   D0 02       BNE   $60B3
60B1-   E6 07       INC   $07
60B3-   B1 06       LDA   ($06),Y
60B5-   29 C0       AND   #$C0 ; 11000000 => xx000000
60B7-   18          CLC        ;
60B8-   2A          ROL        ; x0000000
60B9-   2A          ROL        ; 0000000x
60BA-   2A          ROL        ; 000000xx (4)
60BB-   90 E2       BCC   $609F
=======================================
60BD-   00          BRK   
 
=======================================
  Table décryptage Texte (caractères)
=======================================
60BE-   10 35       BPL   $60F5
60C0-   17          ???
60C1-   14          ???
60C2-   32          ???
60C3-   08          PHP
60C4-   02          ???
60C5-   2B          ???
60C6-   15 0B       ORA   $0B,X
60C8-   1B          ???
60C9-   01 3E       ORA   ($3E,X)
60CB-   27          ???
60CC-   09 07       ORA   #$07
60CE-   39 3A 05    AND   $053A,Y
60D1-   21 11       AND   ($11,X)
60D3-   3F          ???
60D4-   12          ???
60D5-   1A          ???
60D6-   30 3B       BMI   $6113
60D8-   3C          ???
60D9-   33          ???
60DA-   23          ???
60DB-   31 5D       AND   ($5D),Y
60DD-   22          ???
60DE-   19 25 0D    ORA   $0D25,Y
60E1-   06 38       ASL   $38
60E3-   1F          ???
60E4-   0A          ASL
60E5-   1C          ???
60E6-   0C          ???
60E7-   29 2A       AND   #$2A
60E9-   37          ???
60EA-   2E 36 04    ROL   $0436
60ED-   0E 0F 1D    ASL   $1D0F
60F0-   24 2D       BIT   $2D
60F2-   18          CLC
60F3-   2F          ???
60F4-   3D 20 34    AND   $3420,X
60F7-   03          ???
60F8-   2C 13 28    BIT   $2813
60FB-   16 5B       ASL   $5B,X
 
60FD-   C4 C3       CPY   $C3
60FF-   CB          ???
6100-   C4          ???
 
=======================================
Paramètres pour le Move Monitor ($FE2C)
=======================================
6101-   FD          ???
6102-   FB          ???
6103-   5F          ???
6104-   FD 60 FE    SBC   $FE60,X
6107-   00          BRK
6108-   60          RTS
=======================================
  routine de fin / sortie / nettoyage
     + Affichage du texte à l'écran
=======================================
6109-   A2 00       LDX   #$00
610B-   BD F6 FE    LDA   $FEF6,X
610E-   9D 00 60    STA   $6000,X ; écrasement routine
6111-   E8          INX
6112-   D0 F7       BNE   $610B
6114-   20 58 FC    JSR   $FC58 ; Home
6117-   A9 00       LDA   #$00
6119-   85 08       STA   $08
611B-   A9 65       LDA   #$65
611D-   85 09       STA   $09
611F-   A0 00       LDY   #$00
6121-   84 ED       STY   $ED
6123-   84 EC       STY   $EC
 
6125-   A2 00       LDX   #$00
6127-   B1 08       LDA   ($08),Y ; $6500+ vers
6129-   9D D0 07    STA   $07D0,X ; dernière ligne Page 1 Text
612C-   A9 00       LDA   #$00
612E-   91 08       STA   ($08),Y ; nettoyage
6130-   99 A7 61    STA   $61A7,Y ; bis
6133-   C8          INY
6134-   D0 02       BNE   $6138
6136-   E6 09       INC   $09
6138-   E8          INX
6139-   E0 28       CPX   #$28 ; toutes les colonnes ? (40)
613B-   D0 EA       BNE   $6127
613D-   84 ED       STY   $ED
613F-   E6 EC       INC   $EC
6141-   A4 EC       LDY   $EC
6143-   C0 18       CPY   #$18 ; toutes les lignes ? (24)
6145-   F0 0B       BEQ   $6152
6147-   20 70 FC    JSR   $FC70 ; scroll vers le haut
614A-   2C 30 C0    BIT   $C030 ; son
614D-   A4 ED       LDY   $ED
614F-   4C 25 61    JMP   $6125
 
6152-   A2 00       LDX   #$00
6154-   BD 01 61    LDA   $6101,X ; chargement param pour le Move
6157-   95 3C       STA   $3C,X
6159-   E8          INX
615A-   E0 08       CPX   #$08
615C-   D0 F6       BNE   $6154
 
615E-   A0 00       LDY   #$00
6160-   20 2C FE    JSR   $FE2C ; Move ($FBFD-$FD5F) -> $6000
6163-   2C 10 C0    BIT   $C010 ; initialisation clavier
6166-   A9 00       LDA   #$00
6168-   8D 00 C0    STA   $C000 ; test clavier
616B-   AD 00 C0    LDA   $C000
616E-   10 FB       BPL   $616B
6170-   A2 00       LDX   #$00
6172-   BD 00 7C    LDA   $7C00,X ; on restaure le buffer clavier
6175-   9D 00 02    STA   $0200,X
6178-   E8          INX
6179-   D0 F7       BNE   $6172
617B-   60          RTS   ; voilà c'est fini, on sort !
=======================================

Deux parties distinctes ici :

- en $6000+, le décodage du texte en lui-même (oui on y arrive enfin !).
- en $6109+, l'affichage par un scrolling (routine monitor) du texte ligne par ligne avec le petit effet sonore qui va bien. Et encore un peu de nettoyage (grosso modo, on écrase les routines de décodage) avant de sortir définitivement du défi et de continuer le boot du disk.

On va donc s'attarder sur la partie qui nous intéresse directement, à savoir le décryptage du texte.

Ce que fait la routine en $6000+ :

  • génère une valeur d'index à partir des données du buffer source ($61A7+) en utilisant des AND (pour isoler les bits) et des décallages/rotations (pour les positionner). Chaque valeur index est codée sur 6 bits. L’algorithme utilise un système de codage "tournant" :
  1. le 1er index généré utilise les 6 bits forts d'un même octet source (xxxxxxyy) après décalage ce qui donne : 00xxxxxx (1er index).
  2. le second index généré utilise les bits 0 et 1 de l'octet source précédent (xxxxxxyy) et les 4 bits forts de l'octet source suivant (zzzzwwww) ce qui donne (après décalage et addition) : 00yyzzzz (2nd index).
  3. le troisième index généré utilise les 4 bits faibles non utilisés de l'octet source (zzzzwwww) et les deux bits forts (6 et 7) de l'octet source suivant (vvuuuuuu) ce qui donne (encore une fois après décalage et addition) : 00wwwwvv (3ème index).
  4. le quatrième index généré utilise les 6 bits faibles restants de l'octet source précédent (vvuuuuuu) ce qui donne, après décalage : 00uuuuuu (4ème index).
  5. l'index suivant est généré de manière identique au premier (6 bits forts de l'octet source suivant) et ainsi de suite...
  • une fois cet index généré, il est utilisé pour récupérer le caractère correspondant dans la table de décryptage (que l'on trouve à partir de $60BE).
  • le caractère retourné subit un ORA avec une valeur attribut (par défaut $80) qui fixe les bits 7-6-5 du caractère. On peut ainsi avoir un affichage clignotant, inverse ou normal.
  • pour modifier cet attribut, il faut que l'index retourné soit à zéro (qui sert ainsi de marqueur). L'algo récupère alors l'index suivant et l'utilise pour modifier en conséquence la valeur attribut. Si on veut être puriste, on devrait même dire que ce ne sont plus vraiment des index dans ce cas en fait...
  • La caractère "généré" est sauvé dans le buffer destination ($6500+). L'affichage ligne à ligne se fera à partir de ce buffer dans la routine à partir de $6109.

 

À noter que Deckard utilise ici encore l'astuce de l'opcode BIT vue plus haut pour éviter de surcharger son code avec des JMP et des sauts lourdingues. On remarquera aussi le soin tout particulier apporté en fin de routine pour écraser les routines et pour effacer les buffers source et destination utilisés. Afin de laisser le moins de traces possibles je suppose en cas d'interruption du programme par un ctrl+reset à l'arrache...

Voilà c'est fini pour cette première partie. Il reste maintenant à digérer tout ça pour se lancer concrètement dans la résolution du défi, c'est à dire aborder la partie programmation des routines qui nous permettront de recrypter le texte signé ! Et ensuite replacer le tout au bon endroit sur le disque original !

À suivre...