Triceraprog
La programmation depuis le Crétacé

VG5000µ, ajouter des instructions au BASIC ? ()

Il y a quelques temps, un message sur le forum Config.cfg demandait s'il était simple ou même possible d'ajouter des instructions supplémentaires à la ROM d'un VG5000µ. C'est une question que je me posais aussi, avec dans l'idée d'ajouter des instructions graphiques utilisant l'implémentation des articles précédents].

J'ai donc continué à étudier la ROM (que je commence à bien connaître maintenant) à la recherche d'une méthode. Et on va voir que ça n'est pas gagné.

Le parseur

Lorsque la touche RET du clavier est appuyée, il se passe plusieurs choses. Tout d'abord, un caractère NUL (valeur 0) est placé dans le buffer d'entrée à l'emplacement du dernier caractère qui n'est pas un espace (adresses $3c4a à $3c56). Le RET à la fin de cette fonction ramène hors de la boucle principal du traitement interactif.

Après un traitement de la protection de programmes qui n'est pas le sujet ici, une fonction cherche si la ligne commence par un numéro de ligne puis débute la transcription de la ligne vers une ligne « tokenisée ». La procédure de tokénisation, en bref, va repérer toutes les instructions, fonctions et opérations connues et les transformer en « tokens » : un octet dont le bit de poids fort est à 1.

Un token permet de prendre moins de place en mémoire et facilite le travail de l'évaluateur.

La procédure saute bien entendu les chaînes de caractères en repérant les paires de guillemets.

Je laisse de côté le traitement de caractères à significations particulières (le ? qui remplace PRINT comme dans la plupart des BASIC par exemple) pour arriver à la recherche principale. Cette routine, qui commence globalement en $23a5, va comparer le contenu des caractères à décoder (en les passant en majuscules si nécessaire) avec une liste de noms et symboles dans une table située en $23a7.

Premier problème, cette liste est figée. Si aucune correspondance n'est trouvée, la routine ne dispose pas de hook pour passer la main à une éventuelle liste gérée par l'utilisateur. Les caractères sans correspondance sont laissés tels quels dans le buffer. Si plus tard, à l'évaluation, ces caractères n'ont pas de sens (ne désignent pas une variable par exemple), une erreur sera émise.

On pourrait alors imaginer se servir d'un autre hook, celui qui est appelé à chaque affichage de caractère ($47e2) ou celui qui est appelé lors d'un retour à la ligne (\$47e5), afin d'ajouter un parsing à la main. Mais pour cela, il faudrait choisir des tokens libres pour les nouvelles instructions ; ce qui amène à étudier les tokens.

Les tokens

Voici la liste des tokens avec leurs valeurs.

CommandesSupportOpérateursFonctions
128:END178:TAB(185:+195:SGN
129:FOR179:TO186:-196:INT
130:NEXT180:FN187:*197:ABS
131:DATA181:SPC(188:/198:USR
132:INPUT182:THEN189:^199:FRE
133:DIM183:NOT190:AND200:LPOS
134:READ184:STEP191:OR201:POS
135:LET192:>202:SQR
136:GOTO193:=203:RND
137:RUN194:<204:LOG
138:IF205:EXP
139:RESTORE206:COS
140:GOSUB207:SIN
141:RETURN208:TAN
142:REM209:ATN
143:STOP210:PEEK
144:ON211:LEN
145:LPRINT212:STR$
146:DEF213:VAL
147:POKE214:ASC
148:PRINT215:STICKX
149:CONT216:STICKY
150:LIST217:ACTION
151:LLIST218:KEY
152:CLEAR219:LPEN
153:RENUM220:CHR$
154:AUTO221:LEFT$
155:LOAD222:RIGHT$
156:SAVE223:MID$
157:CLOAD
158:CSAVE
159:CALL
160:INIT
161:SOUND
162:PLAY
163:TX
164:GR
165:SCREEN
166:DISPLAY
167:STORE
168:SCROLL
169:PAGE
170:DELIM
171:SETE
172:ET
173:EG
174:CURSOR
175:DISK
176:MODEM
177:NEW

On peut remarquer que cette liste comporte plusieurs parties. La première, de 128 à 177, contient des instructions, c'est-à-dire des commandes impératives, sans valeur de retour.

De 178 à 184, il s'agit de commandes de support, qui ne peuvent être trouvées qu'en conjonction de commandes maîtresses. Par exemple THEN avec IF ; TO et STEP avec FOR,...

De 185 à 194, il s'agit d'opérateurs logiques et arithmétiques. Enfin, à partir de 195 jusqu'à la fin, 223, il s'agit de fonctions, qui retournent des valeurs, et qui apparaîtrons donc, au côté des opérateurs, dans des expressions.

On commence à sentir que s'il fallait ajouter des mot-clés, en fonction de leur nature, il faudrait les placer dans le bon groupe. Sauf que cette liste est compacte. On peut imaginer ajouter des fonctions après MID$, mais ajouter une instruction ou un opérateur décalerait toute la table, ce qui poserait un problème de compatibilité au moins avec les programmes enregistrés (les programmes BASIC enregistrés le sont sous forme tokenisée).

L'évaluateur

C'est donc vers l'évaluateur qu'il faut se tourner et se demander comment sont traitées les lignes en BASIC. Et c'est là que vient se planter le dernier clou dans le cercueil de l'ajout de commandes utilisateurs... du moins sans modifier la ROM.

Tout d'abord, examinons le décodage des tokens en $250f.

             sub      a,$80
             jp       c,inst_let           ; Comme tous les tokens sont supérieurs ou égaux
                                           ; à $80, si le caractère est inférieur, c'est le début du nom d'une variable.
                                           ; C'est un LET implicite.

             cp       a,$32
             jp       nc,stx_err_prt       ; Les 50 ($32) premiers tokens seulement sont des instructions
                                           ; Si le token est après, alors c'est une Erreur de syntaxe.

Voilà... la ROM contient en dur les bornes des instructions pouvant être décodées.

Et ce n'est pas fini. Lorsque l'on regarde l'évaluation d'expression en $28d8 :

             xor      a,a
             ld       (valtyp),a           ; Type numérique par défaut
             rst      chget
             jp       z,missing_op         ; Cas où le caractère est NUL
             jp       c,str_to_num         ; Cas où le caractère est un chiffre.
             cp       a,$26
             jp       z,str_hex_dec        ; Saut si sur le point de parser un nombre en hexa (caractère '&')
             call     a_to_z_2
             jr       nc,str_to_var        ; Saut si la valeur est entre A et Z
             cp       a,$b9                ; Token pour '+'
             jr       z,parse_value
             cp       a,$2e                ; Caractère '.'
             jp       z,str_to_num
             cp       a,$ba                ; Token pour '-'
             jr       z,str_to_min
             cp       a,$22                ; Caractère '"'
             jp       z,str_to_str
             cp       a,$b7                ; Token pour 'NOT'
             jp       z,str_to_not
             cp       a,$b4                ; Token pour 'FN'
             jp       z,str_to_fn
             sub      a,$c3                ; Token pour 'SGN', la première des fonctions
             jr       nc,str_to_func

Afin de déterminer le type de valeur à décoder, un certain nombre de tokens est là aussi en dur.

Et donc ?

Et donc tel quel, la ROM ne fourni pas de mécanisme d’extension pour écrire de nouvelles instructions. D'autre part, même s'il reste une trentaine de tokens libres à la fin de la liste, les constantes dans le parseur et dans l'évaluateur exigent que les tailles des groupes du tableau soient respectés.

Reste la possibilité de modifier la ROM pour ajouter les nouvelles instructions, ce qui n'est pas très compliqué, mais qui nécessitera de la conversion au chargement pour être compatible avec des programmes en BASIC enregistrés sur K7.