Nous revoilà pour la suite (et la fin) de la résolution du défi de la saga Archives ! Pour ceux qui auraient raté un épisode, le début est par ici !

On commence par une présentation générale de ce que nous devons concrètement faire :

  • récupérer le texte décrypté.
  • le modifier pour y ajouter notre signature (on fera le service minimum).
  • ré-encrypter le texte.
  • récupérer la portion mémoire entre $6000 et $6600 juste avant l’exécution de la routine en $6000.
  • y incorporer à partir de $61A7 notre texte fraîchement ré-encrypté.
  • regrouper entre $7000-$7600 les octets provenant de $6000 et de $6300+ (un sur deux).
  • encoder en 4:4 ce qui est entre $7000 et $7600.
  • sur les 12 pages mémoire obtenues, faire un EOR entre chaque octet et sa position dans la page.
  • écrire à partir de P$22/S$00 les 12 secteurs avec ces 12 pages.

A) Récupération du texte décrypté et de la mémoire entre $6000 et $6600 :

On va faire d'une pierre deux coups et tout récupérer en même temps. Pour cela, on refait exactement le même parcours que décrit dans la première partie. Je le résume rapidement :

  • boot de la disquette modifiée qui nous rend la main au moment de sauter en $8344.
  • on modifie le STA 0200,X par un STA 2000,X ($8356) et on shunte le JMP $0200 ($835E).
  • 8344G
  • on shunte les ROR (qui modifient le RTS) dans la routine en $2000 : $2C en $2014, $2018 et $201B.
  • 2000G
  • on shunte le JSR $6000 en $81EC.
  • Arrivé à ce moment là, il faut booter un DOS (C600G) et on peut ainsi sauver la mémoire entre $6000 et $6600 :
  • ] BSAVE D6000-6600,A$6000,L$600
  • On repasse sous Monitor et on lance manuellement la routine en $6000 de façon à décoder le texte cette fois :
  • 6000G
  • On peut alors sauver le texte décodé qui commence en $6500. Pour la longueur, on sauve $28x$18 = $3C0 octets.
  • ] BSAVE TEXT-DEC,A$6500,L$3C0

 

B) Ajout de la signature au texte décrypté :

On va directement utiliser DiskFixer pour éditer le texte (en éditant le fichier TEXT-DEC sauvé précédemment). On va se baser sur le mot "PERSONNE" pour se positionner correctement sur la bonne ligne. Attention il n’apparaît pas directement, Deckard a utilisé les codes ASCII "CTRL" qui ne s'affichent pas sous DiskFixer. Une fois le texte modifié, on n'oublie pas de le sauver. On n'oublie pas non plus d'utiliser les codes ASCII INVERSE pour notre signature. Cela fait partie de l'énoncé du défi je le rappelle...

C) Cryptage du texte modifié :

Cette fois, on n'y échappera pas, il va falloir faire chauffer Merlin pour coder un petit programme qui fera exactement l'inverse de la routine de décryptage. J'ai d'ailleurs utilisé comme base cette routine. J'ai également utilisé les mêmes adresses Page Zéro comme variables (attention, elles ne correspondent bien évidemment plus aux mêmes données que pour le décodage). Allez encore une petite liste avec cette fois le fonctionnement du programme de codage :

  • on récupère un caractère du texte à afficher.
  • on le teste pour connaître son attribut ($E0/$80/$40/$00).
  • si cet attribut est différent de celui en cours, on envoie un $00 ainsi que le code correspondant à l'attribut ($08/$01/$04/$02) vers la partie routine d'encodage.
  • on supprime les 3 bits forts (7-6-5) du caractère. On obtient donc un nombre compris entre $01 et $3F.
  • ce nombre va servir d'index et on récupère ainsi dans une table la valeur correspondante. Et c'est finalement cette valeur qui va être encodée à son tour.
  • s'il ne reste plus de caractères (donc texte fini par un 00), on envoie le marqueur de fin, c'est à dire un double 00 qu'on encode également.

L'encodage est bien évidemment l'exact opposé du décodage mais repose (forcément) sur le même principe (en gros, une valeur est codée sur deux demi-octets consécutifs). Pour la construction de la table d'encodage, et bien, on prend l'opposé encore une fois de la table de décodage. Cette fois c'est la valeur même du caractère (après mise à 0 des bits 5-6-7 ) qui sert directement de valeur d'entrée. Et en retour, on obtient donc l'index. Techniquement, ce n'est donc pas une suite de caractères qui sont encodés mais une suite d'index correspondants à la table de décodage...
À noter que deux valeurs (entre $01 et $3F donc) n'avaient pas d'index associé dans la table de décodage. Ce sont les valeurs $1E et $26. Et ce $26, une fois l'attribut ajouté devient $E6 soit le caractère "f" utilisé abondamment dans le texte de présentation du défi !
Ce n'est bien entendu pas un bug mais une dernière subtilité de notre ami Deckard ! Pour régler le problème, il suffit de mettre la même valeur que pour l'entrée $06 !
Explication : $06 = 00000110  et $26 = 00100110. Comme de toute façon l'attribut (ici $E0 = 11100000) sera ajouté (par un ORA voir explication dans la première partie), que ça soit $06 ou $26 le résultat sera le même, à savoir : 11100110 soit $E6 !
Si arrivé à ce stade, vous êtes encore en train de lire et si en plus vous avez à peu près compris le charabia de ce dernier paragraphe, merci de m'écrire pour vous faire connaître, car vous êtes réellement impressionnant !

Bref, le plus simple c'est de passer directement au code source que j'ai essayé de commenter le plus possible :

    1          ORG   $2000
    2          LST   OFF
    3
    4 SOURCE   EQU   $08
    5 DEST     EQU   $06
    6 VALEUR   EQU   $EC
    7 INDSOUR  EQU   $EE        ; INDEX DE LA SOURCE
    8 INDDEST  EQU   $EF        ; INDEX DE LA DESTINATION
    9 COMENCO  EQU   $ED        ; COMPTEUR ENCODAGE
   10 ATTRIBP  EQU   $1B        , ATTRIBUT PRECEDENT
   11 ATTRIB   EQU   $1A        ; ATTRIBUT DU CARACTERE
   12 CARACT   EQU   $1C        ; CARACTERE COURANT
   13
   14          LDA   #$00
   15          STA   COMENCO    ; COMPTEUR ENCODAGE
   16          STA   SOURCE
   17          LDA   #$A7
   18          STA   DEST
   19          LDA   #$61       ;
   20          STA   DEST+1     ; DESTINATION
   21          LDA   #$65       ; SOURCE
   22          STA   SOURCE+1
   23          LDA   #$80
   24          STA   ATTRIBP    ; ATTRIBUT TEXTE PRECEDENT
   25
   26
   27          LDY   #$00
   28          STY   INDSOUR    ; INDEX SOURCE
   29          STY   INDDEST    ; INDEX DESTINATION
   30
   31 BOUCLEPR LDY   INDSOUR    ; ON RECUPERE L'INDEX SOURCE
   32          LDA   (SOURCE),Y ; LECTURE CARACTERE A ENCODE
   33          BEQ   FIN        ; SI C'EST 00 ON A FINIT !
   34          STA   CARACT     ; ON LE SAUVE
   35          INY              ; ON PREPARE LE SUIVANT
   36          BNE   SUITE
   37          INC   SOURCE+1
   38 SUITE    STY   INDSOUR    ; ON SAUVE INDEX SOURCE
   39
   40          AND   #$E0       ; ON TEST LES BITS 5-6-7
   41          CMP   #$E0       ; 111000000 ?
   42          BNE   SUITE1
   43          LDA   #$08       ; SI OUI ON FIXE L'ATTRIBUT
   44          STA   ATTRIB     ;
   45          JMP   GO
   46
   47 SUITE1   LDA   CARACT
   48          AND   #$80       ; ON TEST LE BIT 7
   49          CMP   #$80       ; 10000000 ?
   50          BNE   SUITE2
   51          LDA   #$01       ; SI OUI ATTRIBUT = 1
   52          STA   ATTRIB
   53          JMP   GO
   54
   55 SUITE2   LDA   CARACT
   56          AND   #$40       ; BIT 6
   57          CMP   #$40       ; 01000000
   58          BNE   SUITE3
   59          LDA   #$04       ; ATTRIBUT = 4
   60          STA   ATTRIB
   61          JMP   GO
   62
   63 SUITE3   LDA   #$02       ; BIT 5-6-7 A 0 ?
   64          STA   ATTRIB     ; ATTRIBUT = 2
   65
   66 GO       CMP   ATTRIBP    ; EST-CE LE MEME ATTRIBUT
   67          BEQ   SUITE4     ; SI OUI ON NE CHANGE RIEN
   68          LDA   #$00       ; SI NON ON ENCODE LE CHGT
   69          JSR   ENCODE     ; UN 00 POUR AMORCER
   70          LDA   ATTRIB
   71          STA   ATTRIBP
   72          JSR   ENCODE     ; + L'ATTRIBUT CODE
   73
   74 SUITE4   LDA   CARACT     ; ON RECUPERE LE CARACTERE
   75          AND   #$3F       ; 00111111 - ON GARDE LES 6 BITS
   76          TAX              ; ON TRANSFORME EN INDEX DE TABLE
   77          LDA   TABLE,X    ; ON PREND LA VALEUR ASSOCIE
   78          JSR   ENCODE     ; ON ENCODE LA VALEUR !
   79          JMP   BOUCLEPR   ; ON BOUCLE !
   80
   81 FIN      LDA   #$00       ; SI C'EST LA FIN
   82          JSR   ENCODE     ; ON ENCODE
   83          LDA   #$00       ; DEUX ZERO
   84          JSR   ENCODE     ; DE SUITE
   85          RTS              ; ET ON QUITTE !
   86
   87 ENCODE   LDY   INDDEST    ; ON RECUPERE L'INDEX POUR LA DEST
   88          STA   VALEUR     ; ON SAUVE LA VALEUR A ENCODE
   89          LDA   COMENCO    ; COMPTEUR ENCODAGE
   90          BEQ   TYPE0      ; 0 ?
   91          CMP   #$01       ; 1 ?
   92          BEQ   TYPE1
   93          CMP   #$02       ; 2 ?
   94          BEQ   TYPE2
   95
   96 TYPE3    LDA   VALEUR     ; OU TYPE 3
   97          CLC
   98          ADC   (DEST),Y   ; ON AJOUTE
   99          STA   (DEST),Y   ; DIRECTEMENT
  100          INY              ; DEST + 1
  101          BNE   S1
  102          INC   DEST+1
  103 S1       LDA   #$00       ; REMISE A ZERO COMPTEUR
  104          STA   COMENCO
  105          STY   INDDEST
  106          RTS
  107
  108 TYPE0    LDA   VALEUR     ; TYPE 0
  109          ASL              ; ON NE GARDE
  110          ASL              ; QUE LES 6 BITS FAIBLES
  111          STA   (DEST),Y   ; ON SAUVE
  112          INC   COMENCO    ; TYPE ENCODAGE + 1
  113          STY   INDDEST
  114          RTS
  115
  116 TYPE1    LDA   VALEUR     ; TYPE 1
  117          AND   #$30       ; ON NE GARDE QUE LES BITS 4-5
  118          LSR              ; ON LES DECALLE
  119          LSR              ;
  120          LSR              ;
  121          LSR              ; JUSQU'A OBTENIR 000000XX
  122          CLC
  123          ADC   (DEST),Y   ; ON LES AJOUTE
  124          STA   (DEST),Y   ; A LA DESTINATION
  125          INY              ; DEST + 1
  126          BNE   S2
  127          INC   DEST+1
  128 S2       LDA   VALEUR     ; ON REPREND LA VALEUR
  129          AND   #$0F       ; ON NE GARDE QUE LES BITS 0-3
  130          ASL              ; ON DECALLE POUR OBTENIR
  131          ASL
  132          ASL
  133          ASL              ; XXXX0000
  134          STA   (DEST),Y   ; ON SAUVE
  135          INC   COMENCO    ; TYPE + 1
  136          STY   INDDEST
  137          RTS
  138
  139 TYPE2    LDA   VALEUR     ; TYPE 2
  140          AND   #$3C       ; ON OBTIENT : 00XXXX00
  141          LSR              ; DECALLAGE
  142          LSR              ; POUR OBTENIR 0000XXXX
  143          CLC
  144          ADC   (DEST),Y   ; ON AJOUTE A DEST
  145          STA   (DEST),Y
  146          INY              ; DEST + 1
  147          BNE   S3
  148          INC   DEST+1
  149 S3       LDA   VALEUR     ; ON REPREND LA VALEUR
  150          AND   #$03       ; ON NE GARDE QUE BIT 0-1
  151          ASL              ; OUI JE SAIS J'AURAIS
  152          ASL              ; PU DECALLER DANS L'AUTRE
  153          ASL              ; SENS MAIS C'EST PLUS CLAIR
  154          ASL              ; JE TROUVE !
  155          ASL              ; ON OBTIENT DONC
  156          ASL              ; XX000000
  157          STA   (DEST),Y   ; ET ON SAUVE !
  158          INC   COMENCO    ; TYPE + 1
  159          STY   INDDEST
  160          RTS
  161
  162 TABLE    HEX   00,0C,07,3A,2F,13,24,10,06,0F,27,0A,29,23
  163          HEX   30,31,01,15,17,3C,04,09,3E,03,35,21,18,0B
  164          HEX   28,32,38,26,38,14,20,1D,33,22,24,0E,3D,2A
  165          HEX   2B,08,3B,34,2D,36,19,1E,05,1C,39,02,2E,2C
  166          HEX   25,11,12,1A,1B,37,0D,16

Quelques petites notes encore à propos de ce code source.
- j'ai utilisé les mêmes adresses de destination (donc texte codé vers $61A7) et de source (texte avant codage en $6500) que celles utilisées par la routine de décodage, afin de faciliter les tests. Vous trouverez d'ailleurs sur la disquette de travail en fin d'article, la routine de décodage de Deckard issue du défi dans laquelle a été virée toute la partie écrasement/nettoyage du code). Exécutable en $6109, ce fichier DECODAGE1 (qui se charge en $6000) permet ainsi de tester immédiatement si le texte a été correctement encrypté.
- évidemment, il ne fallait pas espérer la même optimisation de mon code que celle rencontrée dans le défi. Ici le but est d'avoir quelque chose de fonctionnel avec chaque étape bien détaillée.
- pour bien comprendre l'encryptage, il faut regarder en parallèle ce qui se passe dans la partie décryptage (voir article partie I). Le point fondamental à comprendre c'est que la plupart des valeurs sont encodées à cheval sur deux octets. De même les attributs des caractères (inverse/clignotant/normal) sont intégrés et encodés avec les valeurs.

Utilisation de tout cela (on fera référence ici aux fichiers présents sur la disquette de travail fournie plus bas) :

  • on charge notre DUMP mémoire de la partie $6000-$6600 (fichier D6000-6600).
  • on charge notre programme CODAGE1 (issu du source précédent) exécutable en $2000.
  • on charge notre TEXTE modifié avec notre superbe signature (fichier TEXT-DEC) en $6500.
  • on exécute le codage ($2000G sous Monitor).
  • on nettoie par des 00 ce qui est entre $6500 et $6600 (à savoir une partie de notre texte décodé). On va utiliser une petite ligne de commande sous Monitor : *6500 : 00 N 6501<6500.6600M. Cette partie n'est pas obligatoire mais c'est plus propre...
  • À cet instant, on a donc entre $6000 et $6600, le programme de décryptage du défi et notre texte codé. On sauve l'espace mémoire (il sera utilisé par la partie suivante) :
  • ] BSAVE PHASE1,A$6000,L$600

D) Et on emballe le tout...

On touche (presque) au but (si si) ! Une fois le texte ré-encrypté et placé en $61A7, il ne reste plus qu'à faire faire au tout (routine de décryptage + texte encrypté) le chemin inverse de celui qui l'a emmené là !

Première étape : on regroupe ce qui est en $6000+/$6300+ vers $7000 un octet sur deux. Un source vaut mieux que de longs discours donc hop :

    1          ORG   $2000
    2          LST   OFF
    3
    4 SOURCE1  EQU   $06
    5 SOURCE2  EQU   $08
    6 DESTI    EQU   $18
    7 TEMP     EQU   $1A
    8
    9          LDA   #$00
   10          STA   SOURCE1
   11          STA   SOURCE2
   12          STA   DESTI
   13          LDA   #$60
   14          STA   SOURCE1+1
   15          LDA   #$63
   16          STA   SOURCE2+1
   17          LDA   #$70
   18          STA   DESTI+1
   19
   20
   21          LDX   #$00
   22
   23 B1       LDY   #$00
   24
   25 B2       LDA   (SOURCE1),Y  ; $6000+
   26          STY   TEMP
   27          LDY   #$00
   28          STA   (DESTI),Y  ; $7000+
   29          INC   DESTI
   30          BNE   S1
   31          INC   DESTI+1
   32 S1       LDY   TEMP
   33          LDA   (SOURCE2),Y  ; $6300+
   34          LDY   #$00
   35          STA   (DESTI),Y  ; $7001+
   36          INC   DESTI
   37          BNE   S2
   38          INC   DESTI+1
   39 S2       LDY   TEMP
   40          INY
   41          BNE   B2
   42          INX
   43          CPX   #$06
   44          BEQ   FIN
   45          INC   SOURCE1+1
   46          INC   SOURCE2+1
   47          JMP   B1
   48
   49 FIN      RTS

Pas besoin de commentaires particuliers sur ce code je pense, c'est vrai que c'est très moche mais ça a au moins le mérite d'être plutôt limpide au niveau compréhension. Ceci correspond au programme GATHERING sur la disquette de travail. On charge donc PHASE1 et on lance ce code en $2000. On n'a plus qu'à sauver ce qui a été écrit entre $7000 et $7600 (dans PHASE2 par exemple).

Seconde étape : on applique à PHASE2 le codage de type 4:4 et dans la foulée le EOR (reportez-vous à la partie I de l'article pour savoir de quoi on parle hein...). Pas besoin de détailler le EOR, tout le monde connaît. Quant au codage/décodage type 4:4, on le rencontre dans n'importe quelle routine de lecture bas niveau (standard), car c'est le codage utilisé par les infos VOLUME/PISTE/SECTEUR/CHECKSUM dans le champ adresse de chaque secteur. C'est donc un codage courant dans le monde Apple II que l'on repère vite. Le principe est simple : on code les bits pairs d'un côté et les impairs de l'autre. Chaque octet est donc réparti sur deux octets. Pas optimal certes mais ultra rapide et facile à mettre en œuvre, on ne va donc pas s'en plaindre !
Voici le code source (avec en commentaires, ce qui se passe au niveau des bits lors de ce fameux codage) :

    1          ORG   $2000
    2          LST   OFF
    3
    4 SOURCE   EQU   $06
    5 DESTI1   EQU   $08
    6 DESTI2   EQU   $18
    7 TEMP     EQU   $1A
    8
    9          LDA   #$00
   10          STA   SOURCE
   11          STA   DESTI1
   12          STA   DESTI2
   13          LDA   #$70
   14          STA   SOURCE+1
   15          LDA   #$40
   16          STA   DESTI1+1
   17          LDA   #$46
   18          STA   DESTI2+1
   19
   20 **** CODAGE TYPE 4:4 ****
   21
   22          LDX   #$00
   23 B1       LDY   #$00
   24
   25 B2       LDA   (SOURCE),Y  ; A = XYXYXYXY. On commence par les impairs X
   26          LSR               ; après décalage droite A = 0XYXYXYX
   27          ORA   #$AA        ; $AA = 10101010 (sert à "encapsuler" les bits!)
   28          STA   (DESTI1),Y  ; après le ORA, A=1X1X1X1X
   29          LDA   (SOURCE),Y  ; A = XYXYXYXY (on s'occupe des bits pairs Y)
   30          ORA   #$AA        ; $AA = 10101010
   31          STA   (DESTI2),Y  ; après le ORA, A = 1Y1Y1Y1Y
   32
   33          INY
   34          BNE   B2
   35          INC   SOURCE+1
   36          INC   DESTI1+1
   37          INC   DESTI2+1
   38          INX
   39          CPX   #$06
   40          BNE   B1
   41
   42 **** EOR ****
   43
   44          LDA   #$40
   45          STA   DESTI1+1
   46
   47          LDX   #$00
   48
   49 B4       LDY   #$00
   50 B3       STY   TEMP ; Y = index de l'octet dans la page mémoire courante
   51          LDA   (DESTI1),Y
   52          EOR   TEMP ; EOR de l'octet avec sa position dans la page
   53          STA   (DESTI1),Y
   54          INY
   55          BNE   B3
   56          INX
   57          CPX   #$0C
   58          BEQ   FIN
   59          INC   DESTI1+1
   60          JMP   B4
   61
   62 FIN      RTS

Le résultat de ce source, c'est le programme QUATQUAT que l'on applique sur PHASE2 en mémoire (toujours par un $2000G). On obtient alors l'équivalent de 12 secteurs (on avait 6 pages mémoire qui ont été "doublées" par le 4:4) à partir de l'adresse $4000. Il ne reste plus alors qu'à écrire ces secteurs directement sur une copie propre d'Archives 10 à partir de la piste $22, secteur $00 à l'aide du programme ACCÈS DIRECT par exemple (ordre croissant).

Et voilà c'est fini... Incroyable non ? ! Il ne reste plus qu'à rebooter le tout, d'appuyer sur ESC et de contempler le  joli graffiti (qui doit normalement s'afficher, enfin j'espère...). Tant d'efforts pour ça !

E) Le mot de la (vraie) fin

On ne va tout de même pas se quitter comme ça après tout ce temps passé ensemble, voici donc quelques remarques supplémentaires (dans une ultime liste) :

  • oui effectivement Deckard avait raison de prévenir, c'était bien chiant ! Je me suis bien pris la tête sur certaines parties...
  • on ne peut que lui rendre hommage pour la qualité de son code et le soin apporté au défi.
  • pourquoi spécifiquement devoir écrire en INVERSE alors ? Vu que le cryptage oblige à recoder tout le texte, même ce qu'on ne modifie pas, écrire en inverse ou en normal ne rend pas spécialement le défi plus compliqué. Ou plutôt devrais-je dire, même si on avait écrit en normal, cela n'aurait pas été plus simple pour autant...
  • Deckard a écrit une routine complète de lecture bas niveau en y incorporant un EOR. Seulement, ce EOR n'arrivant qu'après la phase de Post-Nibblelisation, il ne pose pas vraiment de problème pour le (re)cryptage. Et surtout pas besoin de routine d'écriture spécifique (ouf).
  • That's All Folks...

 

Télécharger l'image .dsk de la face 1 de Archives 10 avec le défi accompli !

Retrouvez sur la disquette de travail les sources et les binaires des programmes de cryptage. Ainsi que les différents fichiers auxquels il est fait allusion dans l'article.