Il m'arrive régulièrement de tomber sur des images disque refusant de se lancer sous émulateur. La plupart du temps, cela est généralement dû à un problème lors de la numérisation, science qui est tout sauf exacte ! Les secteurs défectueux (ou illisibles) et les artefacts de lecture sont de véritables bombes à retardement. Mais quand une erreur est récurrente sur plusieurs versions d'un même soft, cela mérite de plus amples investigations ! Les émulateurs Apple II ne sont pas parfaits et se heurtent - souvent - à certaines astuces de programmation, l'utilisation de certains softswitches alternatifs en étant un bon exemple. Pourtant les JSR $F3D4 (et leur pendant Basic CALL -3116) dont nous allons parler ici sont, eux, parfaitement émulés. Ce qui ne les empêchent pas dans certains cas de faire planter lamentablement le programme les utilisant. À la décharge des émulateurs, il se passe exactement la même chose sur les machines physiques ! 

Mais commençons par le commencement. D'où provient cette adresse $F3D4 ? Si on se réfère à La pratique de l'Apple II volume 3 de Nicole Bréaud-Pouliquen (éditions du P.S.I. - 1985), il n'y a rien d'officiel en $F3D4 ! Confirmé par La Rom de l'Apple II de Marcel Cottini  (éditions Sybex - 1986), beaucoup plus complet à ce niveau. On y découvre que $F3D4 est en plein dans la routine Applesoft RECALL, qui commence en $F3BC et se termine juste avant la routine HGR2 dont le début est en $F3D8. $F3D4 ne semble donc pas être un point d'entrée Applesoft documenté...

Pourtant, si on consulte What's Where in the Apple de William F. Luebbert (éditions Micro Ink - 1982), on trouve référence à $F3D4 comme point d'entrée pour la routine HGR2 ! Cette même info apparaît également dans le tout premier numéro de Apple Orchard (Mars/Avril 1980) détaillant tous les entry points Applesoft, la source étant Apple Computer, Inc. himself ! Et c'est également l'adresse qui est reprise dans la FAQ officielle de Comp.Sys.Apple2.Programmer !

Il n'y a eu que peu d'évolutions de l'Applesoft au cours des années, une fois la version définitive en place - Applesoft II - en 1978 et son intégration en ROM. En fait, il faudra attendre 1984 et la prise en compte du mode 80 colonnes avec la sortie des Apple IIc et IIe Enhanced pour voir apparaître une révision (et, somme toute, légère). Même l'Apple IIgs, en 1986, n'aura droit qu'à un Applesoft très légèrement remanié. Et à chaque nouvelle version, la rétrocompatibilité descendante stricte du Basic imposait la correspondance des points d'entrées officiels.

 

La routine Applesoft HGR 2  :

On imagine aisément que la routine HGR2 ne pouvait pas changer d'emplacement au gré du vent (et des versions). Pour en avoir définitivement le cœur net, le plus simple est bien évidemment d'aller directement regarder dans le code. Pour ce faire, j'ai conjointement utilisé :

  • des Apple II (IIe Enhanced, IIgs, IIc) physiques en ma possession.
  • les différents émulateurs sous Windows (AppleWin, ActiveGS, etc.) permettant ainsi d'avoir accès à un large panel de machines (II, II+, IIe, IIe Enhanced, IIgs).
  • le listing Applesoft qui fait référence à savoir S-C DocuMentor for Applesoft.

 

Résultat des courses : peu importe que l'on soit sur II+, IIe, IIe Enhanced, IIc ou IIgs, la routine HGR2 commence en $F3D8 et son code reste le même. 

; routine Applesoft HGR2 (PAGE 2)
F3D8-   2C 55 C0    BIT   $C055  ; TXTPAGE2 (page 2)
F3DB-   2C 52 C0    BIT   $C052  ; MIXCLR (affiche tout l'écran gfx)
F3DE-   A9 40       LDA   #$40 	 ; $40 (page 2) pour HPAG
F3E0-   D0 08       BNE   $F3EA  ; saut (systématique)
 
; routine Applesoft HGR  (PAGE 1) 
F3E2-   A9 20       LDA   #$20   ; $20 (page 1) pour HPAG
F3E4-   2C 54 C0    BIT   $C054  ; TXTPAGE1 (page 1)
F3E7-   2C 53 C0    BIT   $C053  ; MIXCLR (4 pages de texte !)
 
F3EA-   85 E6       STA   $E6    ; HPAG (numéro de page - $20 si 1, $40 si 2)
F3EC-   AD 57 C0    LDA   $C057  ; HIRES (haute résolution 280x192)
F3EF-   AD 50 C0    LDA   $C050  ; TXTCLR (mode graphique)
 
F3F2-   A9 00       LDA   #$00   ; début routine HCLR (efface page courante HGR)
F3F4-   85 1C       STA   $1C    ; début routine BKGND
F3F6-   A5 E6       LDA   $E6
F3F8-   85 1B       STA   $1B
F3FA-   A0 00       LDY   #$00
F3FC-   84 1A       STY   $1A
F3FE-   A5 1C       LDA   $1C
F400-   91 1A       STA   ($1A),Y
F402-   20 7E F4    JSR   $F47E
F405-   C8          INY   
F406-   D0 F6       BNE   $F3FE
F408-   E6 1B       INC   $1B
F40A-   A5 1B       LDA   $1B
F40C-   29 1F       AND   #$1F
F40E-   D0 EE       BNE   $F3FE
F410-   60          RTS

On peut donc en conclure que toutes les documentations référençant HGR2 en $F3D4 se plantent ! Alors erreur de frappe sur une vieille documentation Apple ? Résidu des toutes premières versions de l'Applesoft ? Le mystère reste entier...
À noter que l'Apple II originel et son Basic Integer est bien entendu hors concours ici.

Plusieurs choses sont à remarquer dans le code ci-dessus :

  • les instructions HGR et HGR2 partagent du code commun, l'une continuant à la suite de l'autre.
  • HGR   = Page 1 + Mode Graphique + Haute Résolution + 4 lignes de texte + Effacement Page 1
  • HGR2 = Page 2 + Mode Graphique + Haute Résolution + Full Screen + Effacement Page 2

On retrouve bien évidemment le comportement normal des commandes HGR et HGR2 utilisées depuis un programme Basic ou directement depuis le prompt Applesoft.

 

Et $F3D4 dans tout ça ?

Que se passe-t'il concrètement lorsqu'un programme rencontre un JSR $F3D4 ou un CALL -3116 ?

Et bien, le résultat sera différent suivant la machine sur lequel le code sera exécuté. 

 Sous II+, IIe et IIe Enhanced :

Voici ce qu'affiche le Monitor sous ces différentes machines :

F3D4 : F7       : ???
F3D5 : 4C FD FE : JMP $FEFD ; JMP vers routine READ

Un opcode (F7a priori inconnu suivi d'un JMP vers la routine READ (lecture depuis l'interface cassette). Ceci confirme bien d'ailleurs que $F3D4 se situe bien en plein dans la routine RECALL (rappel en mémoire centrale d'un tableau sauvegardé au préalable sur cassette par l'instruction STORE).

Ce qui se passe réellement, nous pouvons le visualiser grâce au mode debugger d'AppleWin : 

Capture d'écran du mode debugger avec AppleWin en mode Apple II+ ou IIe. On remarque immédiatement qu'en $F3D4, une instruction est visible mais ne fait surement pas partie des documentées ! Car nous entrons ici dans le monde merveilleux des opcodes illégaux. Sur 6502, les opcodes non officiels sont tout simplement exécutés logiquement par le processeur (logique, dans le sens de "correspondant à son câblage interne"). Ce qui peut conduire à l'exécution d'instructions un poil farfelues... L'opcode F7 correspond donc à un INC-SBC (INS) : on incrémente l'argument (ici en page zéro, indexée par x) et on soustrait le résultat du registre A ! Bien évidemment, l'instruction en elle-même ne sert à rien ici, ce qu'il faut par contre savoir c'est que pour le 6502, F7 est considéré comme un opcode sur 2 bytes puisqu'il prend un argument ! Ce qui entraîne ensuite un décalage dans tout le code qui suit.

Reprise du code précédent :

F3D4 : F7 4C    : INS $4C,X    ; garbage (mais $4C+X modifié !)
F3D6 : FD FE 2C : SBC $2CFE,X  ; garbage
F3D9 : 55 C0    : EOR $C0,X    ; garbage
F3DB : 2C 52 C0 : BIT $C052    ; MIXCLR
F3DE : A9 40    : LDA   #$40   ; $40 (page 2) pour HPAG
F3E0 : D0 08    : BNE   $F3EA  ; saut (systématique)
... -> suite routine HGR

Une fois les instructions en $F3D4, $F3D6 et $F3D9 exécutées, il y a resynchronisation avec le code  de la routine HGR2 en $F3DB. Par contre, BIT $C055 (normalement en $F3D8) a été shunté. On ne passe donc plus en page 2.

Il est à noter qu'en page zéro, la mémoire $4C+X est incrémentée. X dépendant du code d'avant l'appel vers $3DF4, n'importe quelle adresse de la page zéro est susceptible d'être touchée. C'est ce qu'on pourrait appeler un dommage collatéral. C'est bien évidemment non voulu et résulte juste de l'interprétation de l'opcode F7 par le 6502.
Plus important encore, la variable HPAG ($E6) est positionnée à $40 (page 2) alors que la page courante (qui peut très bien être 1) est toujours active. La routine en $F3F2, exécutée à la suite, censée effacer la page graphique "courante", utilise HPAG pour déterminer celle-ci. C'est donc systématiquement la page 2 qui sera effacée, même si la page courante est la 1.

En résumé, sur Apple II+ et IIe, un JSR $F3D4 (ou un CALL -3116) équivaut à : 

  • Page Courante + Mode Graphique + Haute Résolution + Full Screen + Effacement Page 2

 

Nouvelle capture d'écran du mode debugger d'AppleWin, cette fois en mode Apple IIe Enhanced et son 65C02 ! On remarque que l'opcode F7 est ici considéré comme un NOP ! Sur 65C02 en effet, tous les opcodes non utilisés (ou illégaux ou non documentés, peu importe le nom) sont systématiquement considérés comme des NOP. C'est une des features du processeur. Seul le nombre de bytes de l'instruction et son nombre de cycles varient. On pourra donc obtenir des NOP sur 1 ou 2 bytes. Pour F7, c'est 1 byte. D'où le NOP uniquement sur l'adresse $F3D4.
Attention, sur Rockwell 65C02 et WDC 65C02, l'opcode F7 existe ! AppleWin, lui n'émule qu'un 65C02 classique (comme sur IIe Enhanced), donc F7 = NOP !

Reprise du code précédent :

F3D4 : F7       : NOP 
F3D5 : 4C FD FE : JMP $FEFD (routine READ)
 
FEFD : 8D 07 C0 : STA $C007 (SETINTCXROMC007)
FF00 : 4C D1 C5 : JSR $C5D1 (XREAD)
-> plantage !

Le pseudo NOP en $F3D4 nous amène donc directement au JMP $FEFD (routine READ) qui elle-même nous conduit en $C5D1 entraînant un joli plantage ! Nous venons de découvrir pourquoi l'appel à la routine $F3D4 (ou CALL -3116) pouvait planter sur certains Apple II (et sous émulateur). Ceci ne concerne que les IIe Enhanced ! Et paradoxalement, c'est parce que l'opcode F7 est traité correctement par le processeur que l'on arrive à ce résultat.

En résumé, sur Apple IIe Enhanced, un appel à $F3D4 entraîne :

  • un plantage !

 

 Sur Apple IIc (Rom 00, 03 et 04) :

Listing Monitor :

F3D4 : 1F	: ???         ; NOP 1 byte/1 cycle
F3D5 : C0 0A  	: CPY #$0A    ; garbage 
F3D7 : 60     	: RTS         ; sortie !
F3D8 : 2C 55 C0	: BIT $C055
F3DB : 2C 52 C0 : BIT $C052
F3DE : ...

L'opcode inutilisé 1F est une nouvelle fois traité comme un NOP d'un byte par le 65C02. Le CPY en $F3D5 ne changeant rien, on se retrouve ensuite en $F3D7 et le RTS nous ramène vers l'appelant.
Sur Apple IIc, un appel à la routine $F3D4 ne fait donc strictement rien ! On évite le plantage, ce qui n'est déjà pas si mal...
Contrairement aux Apple II+, IIe et IIe Enhanced, $F3D4 ne se situe plus dans la routine RECALL. En effet, n'ayant plus de port cassette, sur Apple IIc, toutes les routines dédiées ont été supprimées et l'espace ainsi libéré a été utilisé pour d'autres routines. Ce qui explique pourquoi en $F3D4, on ne retrouve plus l'opcode F7 vu précédemment mais 1F. La routine HGR2 en $F3D8 est bien évidemment présente et identique.

En conclusion, sur Apple IIc, un appel à $F3D4 :

  • ne fait strictement rien.

 

 Sur Apple IIgs (Rom 01 et 03) :

Listing Monitor :

FF/F3D4 : 1F C0 0A 60 : ORA 600AC0,X
FF/F3D8 : 2C 55 C0    : BIT C055
FF/F3DB : 2C 52 C0    : BIT C052
FF/F3DE : A9 40       : LDA #40
...

On retrouve les mêmes opcodes que sur IIc mais traités différemment. En effet, le IIgs et son 65SC816 reconnaît l'opcode 1F comme un ORA (sur 4 bytes). Après exécution, on se retrouve directement au début de la routine HGR2 en $F3D8. Aucun plantage donc, par contre, contrairement aux Apple II+ et IIe, on se retrouve systématiquement page 2 et non plus page courante.

En résumé, sur Apple IIgs, un appel à $F3D4 équivaut à l'exécution de la routine HGR2 soit :

  • Page 2 + Mode Graphique + Haute Résolution + Full Screen + Effacement Page 2

 

Le Fix : 

Maintenant que nous avons tout décortiqué, il va bien falloir trouver un moyen de fixer tout ça.  Non seulement $F3D4 n'est pas l'adresse réelle de HGR2, mais son exécution ne fera pas exactement la même chose. Il ne faut donc pas simplement remplacer $F3D4 par $F3D8 sous peine d'obtenir un résultat différent de celui voulu par le programmeur.

Nous voulons également obtenir le même résultat peu importe la machine, du II+ au IIgs. Le plus évident, c'est de faire en sorte que l'on se retrouve en $F3DB directement. Car, c'est finalement là que nous atterrissons sur II+ et IIe. Sur IIgs, cela aura l'avantage de nous laisser dans la page courante (et pas forcément la 2). Et sur IIc, il y aura changement de mode graphique (alors qu'il ne se passait rien). En fait, $F3DB est l'adresse de la première instruction réellement voulue par le programmeur lorsqu'il utilisait par erreur $F3D4.

En remplaçant les JSR $F3D4 par des JSR $F3DB (et les CALL -3116 par des CALL -3109), nous nous retrouverons avec ce code-ci, exécuté, quelque soit la machine  :

F3DB-   2C 52 C0    BIT   $C052  ; MIXCLR (tout l'écran gfx)
F3DE-   A9 40       LDA   #$40 	 ; $40 pour HPAG
F3E0-   D0 08       BNE   $F3EA  ; saut
...
F3EA-   85 E6       STA   $E6    ; HPAG ($40 -> effacement page 2)
F3EC-   AD 57 C0    LDA   $C057  ; HIRES (haute résolution)
F3EF-   AD 50 C0    LDA   $C050  ; TXTCLR (mode graphique)
...
  •  $F3DB = Page Courante + Mode Graphique + Haute Résolution + Full Screen + Effacement Page 2

 

Concrètement : 

• Pour remplacer les JSR $F3D4 : faire une recherche sur 20 D4 F3. Et remplacer par 20 DB F3.

• Pour les CALL -3116 : en Token Applesoft, CALL -3116 correspond à 8C (CALL) C9 (-) 33 (3) 31 (1) 31 (1) 36 (6).
Faire une recherche sur 8CC933313136. Et remplacer par 8CC933313039.

Pour modifier des images disque, il est recommandé d'utiliser un éditeur Hexa et d'agir directement sur l'image. C'est bien plus rapide. Pour les disquettes physiques, un éditeur de secteur style DiskFixer fera le job au coup par coup ! 

Attention, tous les JSR $F3D4 ne sont pas sources de problèmes. Dans le cas de programmes utilisant la Hi-Ram ($D000-$FFFF) en Mémoire Auxiliaire, un appel vers $F3D4 peut être tout à fait légitime.  

 

Pour aller plus loin, références et sources :  

http://bbc.nvg.org/doc/6502OpList.txt : liste des opcodes 6502 y compris ceux non documentés.

http://www.pagetable.com/?p=39 : l'explication du pourquoi du comment les opcodes illégaux sur 6502 existent et font ce qu'ils font...

http://www.6502.org/tutorials/65c02opcodes.html : liste des opcodes 65C02. J'attire tout particulièrement votre attention sur la partie Unused opcodes (guaranteed NOPs).

http://www.txbobsc.com/scsc/scdocumentor/ : code source Applesoft (attention, ne contient que l'Applesoft II de base, pas les modifications IIc et IIgs).

Apple Orchard numéro 1 : avec la référence à HGR2 en $F3D4.

http://www.faqs.org/faqs/apple2/programmerfaq/part1/ : même chose pour la FAQ officielle de C.S.A.P. sur Usenet.

Open-Apple numéro 5 : contient une analyse des nouveautés Applesoft de l'Apple IIc. Parle notamment de la routine en $F3CB qui remplace la routine RECALL.

 

Galeries :

 

 

Un des jeux récurrent posant problème sous IIe Enhanced à cause de l'appel à $F3D4 est Robot Battle. On retrouve ce vieux jeu régulièrement sur les compiles "Catalog" de l'époque. Peu importe la version, c'est souvent le même crack et donc la même source.

 

 

Autre grande victime, Lock'n'Chase. Là encore, c'est un jeu que l'on retrouve souvent sur des vieilles compilations. La version en cause semble être celle du Disk Jockey et ses dérivées (comme ici le joli défacement d'un certain Dick Jokey - on appréciera le trait d'esprit à sa juste valeur).

 

 

 

Plus près de nous, la version The Softman/Solex Crack Band du Casse. Le Call -3116 dans le HELLO modifié par les crackers fait planter lamentablement le boot sur IIe Enhanced.

 

 

 

 

On retrouve également un CALL -3116 sur la disquette officielle Calvados ! Le bug n’apparaît que lors de la démonstration. Sans surprise, c'est le programme en Basic DEMONSTRATION qui est en cause...

Robotron - Frogger - QBert (Jim Dos)

 

 

 

La compilation avec Robotron, Frogger et Q*Bert, proposée par Jim Dos, souffre aussi du JSR $F3D4 ! Cette fois, c'est la présentation qui est en cause et pas les jeux en eux-même !

 

Minit Man (XCB)

 

 

 

Même chose pour le Minit Man signé Mandrake pour le Xanadu Crack Band