Comme promis, second article sur les défis de Force Fix 2 (WoD) avec cette fois la résolution du défi du Speedy Load BASIC, proposé par Astraban.
J'en rappelle le principe : récupérer le code source du programme (en BASIC d'où le nom...) qui affiche une page texte avec le blabla du défi.
Effectivement si on fait LIST, apparait un listing bizarre avec juste les numéros de lignes sans code APPARENT. Un RUN quant à lui lance le programme tout à fait normalement...

Première chose à faire, une fois qu'on a récupéré la main, c'est de sauver le programme sur Disk.
Evidemment cela ne va pas être aussi simple, il n'y a pas de DOS en mémoire donc les commandes SAVE/BSAVE n'existent tout simplement pas.
On va donc devoir booter un DOS 3.3. Seulement avant de faire ça, il va falloir déplacer le programme BASIC qui nous intéresse dans une partie de la mémoire qui ne sera pas écrasée lors du boot du DOS.

OK mais on déplace quoi ?

Facile... On passe sous Monitor par un CALL -151 et on check les adresses en Page 0 contenant les infos que l'on cherche, à savoir :

  • $67 et $68 qui contiennent l'adresse de départ du programme BASIC en mémoire. Ici on obtient (sans surprise) $801
  • $AF et $B0 qui contiennent l'adresse de fin du programme BASIC. On obtient  $0CEB (qui est en fait la première adresse libre APRES le programme).
; On déplace en $2000 (par exemple) notre programme BASIC par un :
* 2000<801.0CEBM
; On peut ensuite booter notre disk DOS 3.3 :
* C600G

On retrouve sous DOS 3.3 avec les commandes qui nous manquaient tout à l'heure.
On va ici sauver deux fois ce qui nous intéresse, une fois sous forme de BINAIRE (vous verrez pourquoi plus tard) avec :

]BSAVE DUMP,A$2000,L$4EA  
; $4EA représente la longueur ($0CEB-$801) du programme BASIC

Et une fois sous forme de programme BASIC directement. Pour ce faire, on va donc remettre celui-ci à sa place légitime (en $801) et refixer les adresses en Page 0.

]CALL -151
* 801<2000.2CEBM  ; on re-déplace ce qui était en $2000-$2CEB.
;  on fixe l'adresse de départ du programme BASIC en $801
; (normalement ces valeurs sont déjà OK à cause du chargement du HELLO du DOS)
* 67 : 01
* 68 : 08
; on fixe l'adresse de fin du programme. Cette fois, il faut bien mettre nos
; valeurs pour remplacer celles du programme HELLO.
* AF : EB
* B0 : 0C
; on sort du Monitor (CTRL+C/Return) et on peut sauver notre programme BASIC :
]SAVE BASIC
 
; On vérifie si le programme marche correctement par un classique :
]RUN  BASIC

Ok ça marche. On fait LIST et on retrouve notre listing bizarre !

Tout ça pour ça ? !

Oui ! Car maintenant, on a isolé le programme à examiner sur disquette. On va donc aller voir ce qui se passe avec un éditeur de secteurs. J'ai utilisé DiskFixer 4.0, en mode Directory pour afficher les secteurs de notre fichier BASIC, notamment le premier :

Nous sommes en présence d'un fichier de type BASIC, la structure sur disque est on ne peut plus simple :

  • les deux premiers octets représente la longueur du programme
  • tout le reste, c'est le programme en lui-même (sous forme de token).

on y trouvera donc dans l'ordre :

  • adresse de la ligne suivante (2 octets)
  • numéro de la ligne actuelle (2 octets)
  • token (code) de l'instruction BASIC
  • les paramètres de l'instruction sous forme de chaîne de caractères
  • éventuellement le séparateur d'instruction BASIC (' :')
  • éventuellement d'autres tokens /param etc.
  • 00 = fin de ligne

DiskFixer nous affiche (première ligne du premier secteur du fichier) :

EA 04 : longueur du programme - $4EA.

A partir de là commence donc réellement le programme BASIC :
2B 08 : adresse mémoire de la prochaine ligne du programme ($82B)
0A 00 : numéro de la ligne (10)

Jusqu'ici tout va bien. On devrait donc trouver ensuite le token de l'instruction basic, ses paramètres et l'annonce de fin de ligne. Or on voit :
00 5A 5A 5A 5A B2 20 20 20 etc.
Pas de token ici, directement un 00 suivi de 4 5A. Et après seulement on trouve notre token ($B2 = REM) et ses paramètres (chaîne de caractère "   BRAVO T'AS PRESQUE GAGNE  ! !" .

On a donc 00 5A 5A 5A 5A en "trop".

Si on va à la position $2C sous DiskFixer (qui contient ce qui sera en $82B en mémoire, n'oubliez pas que le programme se charge en $801, là nous avons la structure du fichier sur disk y compris les deux octets pour la taille, à partir de la position $00 ), on retrouve le même cirque :

38 08 : adresse mémoire de la prochaine ligne ($838)
14 00 : numéro de ligne BASIC (20)
00 5A 5A 5A 5A : encore du garbage !
89 : token = TEXT
3A : ascii de " :" (qui sépare deux fonctions BASIC sur une même ligne)
97 : token = HOME
00 : fin de ligne

Et ainsi de suite pour ce qui sera à l'adresse $838 (position sur disk en $39) :

48 08 : adresse mémoire de la prochaine ligne ($848)
1E 00 : numéro de ligne (30)
00 5A 5A 5A 5A : garbage again !
A2 33 : token + param = VTAB 3
3A : séparateur
96 31 32 : token + param = HTAB 12

etc...

Vous l'aurez compris, pour résoudre le défi, il va donc falloir supprimer tout  le garbage du source BASIC.

Le Bon, La Brute et le Truand !

Ici plusieurs solution s'offrent à nous... La plus simple, si on est sous émulateur, c'est celle du Truand :
On ouvre l'image de notre Disk sous CiderPress, on extrait le fichier BASIC en prenant bien soin d'appuyer sur le bouton "Configure to preserve Apple II formats" et on va gentiment éditer le résultat avec un éditeur de fichier binaire. On dégage tous les "00 5A 5A 5A 5A" et on réinjecte le fichier dans l'image ! Ni vu ni connu j't'embrouille ! Défi finished ! A vous la gloire et les femmes ! (N'oubliez pas que ce défi à presque 30 ans donc je vous laisse imaginer la gueule des femmes en question...).

La méthode de la Brute se fait bien évidemment à l'arrache. On charge notre fichier BASIC, on passe sous Monitor, et on Move tout le code après chaque garbage en écrasant le garbage lui-même. Il faut refaire l'opération pour chaque occurrence du 00 5A 5A 5A 5A. C'est long, pénible mais ça doit marcher ! On oublie pas de fixer la fin de fichier BASIC (en $AF/$B0) avant de re-sauver le code propre par un SAVE...

Et il y a la méthode du Bon, (presque) élégante et tout en finesse : écrire un petit programme ASM pour faire le boulot.
Cet petit programme le voilà... J'avoue que j'en ai bavé pour l'écrire car 25 ans sans avoir touché à un assembleur 6502 ça fait drôle. Je n'ai jamais été une brute de programmation de toute façon. Ce n'est sûrement pas super optimisé, c'est moche (on ne parlait pas d'élégance au fait ?), c'est pas bien commenté  mais ça reste compréhensible. Et puis une matinée à pleurer du sang pour l'écrire, vous allez me faire le plaisir de le regarder !

Son utilisation :

  • on charge le Dump que l'on a sauvé précédemment (j'avais prévenu !) :  BLOAD DUMP,A$2000 ($2000 obligatoirement).
  • on exécute le programme ASM (BRUN ASM) qui va normalement faire le nettoyage.
  • on passe sous Monitor pour fixer les fameux $AF/$B0. On y met respectivement $19 et $0C (nouvelle adresse de fin de code BASIC après nettoyage). Oui j'aurai pu inclure ça dans le code ASM, je sais...
  • on peut alors sauver le programme BASIC par un classique SAVE TRALALA
  • on vérifie que ça marche...

Le code d'ASM :

6000-   A9 00       LDA   #$00
6002-   85 F9       STA   $F9
6004-   A9 20       LDA   #$20 ; entrée en $2000
6006-   85 FA       STA   $FA
6008-   A9 01       LDA   #$01
600A-   85 FB       STA   $FB
600C-   A9 08       LDA   #$08 ; destination (BASIC) $801
600E-   85 FC       STA   $FC
6010-   A0 00       LDY   #$00
6012-   B1 F9       LDA   ($F9),Y ; on lit l'octet de la source
6014-   C9 00       CMP   #$00 ; on check si c'est 00
6016-   AA          TAX
6017-   F0 23       BEQ   $603C ; si oui on va checker la suite
6019-   8A          TXA
601A-   A0 00       LDY   #$00
601C-   91 FB       STA   ($FB),Y ; on sauve l'octet vers la partie BASIC
601E-   E6 FB       INC   $FB     ; et on passe à l'octet suivant
6020-   D0 02       BNE   $6024
6022-   E6 FC       INC   $FC
6024-   E6 F9       INC   $F9
6026-   F0 0F       BEQ   $6037
6028-   A5 F9       LDA   $F9
602A-   C9 EA       CMP   #$EA  ; est-ce qu'on est arrivé
602C-   D0 E2       BNE   $6010
602E-   A5 FA       LDA   $FA
6030-   C9 24       CMP   #$24  ; à la fin du DUMP
6032-   D0 DC       BNE   $6010 ; non bah on continue alors
6034-   4C 52 60    JMP   $6052 ; oui -> fin
6037-   E6 FA       INC   $FA
6039-   4C 10 60    JMP   $6010
603C-   A0 01       LDY   #$01 ; octet + 1
603E-   B1 F9       LDA   ($F9),Y ; on check si on a $5A
6040-   C9 5A       CMP   #$5A    ; pour l'octet suivant
6042-   D0 D5       BNE   $6019   ; si non, ce n'est pas du garbage
6044-   18          CLC           ; si oui, on shunte les 4 prochains octets
6045-   A9 04       LDA   #$04    ; pourquoi seulement 4 ?
6047-   65 F9       ADC   $F9     ; car
6049-   85 F9       STA   $F9
604B-   90 02       BCC   $604F   ; comme on
604D-   E6 FA       INC   $FA
604F-   4C 24 60    JMP   $6024  ; retourne en pleine incrémentation (donc +1)
6052-   60          RTS

Le code ne teste que la séquence 00 5A (et non pas 00 5A 5A 5A 5A) pour le garbage car on a jamais 00 5A hors garbage donc pas la peine de tester plus.
Vous pouvez télécharger la disquette contenant le programme BASIC lisible, le programme ASM et son source ainsi que le fichier DUMP du programme Basic déplacé en $2000.