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.
envoyé le 12-01-2012 à 0 h 09 min
J’ai le vague souvenir d’avoir fait autrement. Pour rendre le programme encore utilisable, il suffit de remplacer le « garbage » par des « : » avec un éditeur de secteurs, c’est à dire mettre à la place le séparateur d’instructions BASIC. Un programme BASIC avec des « :::::: » fonctionne toujours!
Puis une fois le source listable, j’ai viré les « : » en trop pour n’en garder qu’un.
J’avais été le 1er à relever le défi mais j’avais refusé le prix, estimant que ça avait été trop simple
JM
envoyé le 12-01-2012 à 9 h 40 min
Je n’y avais même pas pensé au « : ». Excellent !
Comme quoi, parfois, on se complique la vie pour rien.