L'article précédent présentait un accrochage sur un hook d'interruption. Dans cet article, je vais regarder du côté des hooks de commandes de périphériques.
Ces hooks sont initialisés différements des 10 premiers, selon le code qui suit :
ld a,$c3
ld (inst_lpen),a
ld (inst_disk),a
ld (inst_modem),a
ld hl,no_device
ld ($47f2),hl
ld ($47f5),hl
ld ($47f8),hl
L'instruction placée au début de chaque vecteur de redirection est un JP
et l'adresse par défaut est celle d'une routine indiquant que le périphérique n'est pas géré.
Il s'agit d'une extension de commandes mise à disposition par le VG5000µ, permettant de traiter les commandes LPEN
, MODEM
et DISK
. Il est d'ailleurs amusant de voir que dans le manuel d'utilisation, ces trois commandes sont mentionnées dans le rappel des instructions reconnues, mais qu'elles ne sont pas décrites.
Dans la table des instructions, le décodage de ces tokens envoie directement sur chacun des trois vecteurs, sans traitement particulier. C'est donc du code de décodage de paramètres qu'il faut écrire si l'on veut traiter ces instructions.
Mise en place de la routine
Le code est similaire à celui de la mise en place de la routine sur l'interruption. Seule l'adresse du hook change.
defc dskhk = $47f4 ; Adresse du hook
org $7A00 ; Spécification de l'adresse mémoire d'implémentation
push AF ; Sauvegarde des registres sur la pile
push HL
ld A,$C3 ; Mise en place de la routine sur le HOOK
ld (dskhk),A ; Il y a normalement déjà un 'JP' ici, mais on s'en assure
ld HL,cmd_routine
ld (dskhk+1),HL ; l'adresse de la routine
pop HL ; Restauration des registres depuis la pile
pop AF
ret ; Retour au programme appelant
cmd_routine:
ret
Passons donc rapidement sur la partie la plus intéressante : le traitement de l'instruction.
Traitement de l'instruction
Lorsque l'interpréteur appelle la routine associée à une instruction, plusieurs choses sont mises en place. Les principales sont :
HL
pointe vers la chaîne en train d'être interprétée, sur le prochain caractère à lire,- Le flag "Z" est à 1 si l'on est à la fin de la chaîne,
- L'adresse de retour sur la pile est positionnée pour traiter l'instruction suivante.
Le contrat en sortie est :
HL
doit pointer sur le caractère après le dernier consommé par l'instruction,- S'il s'agit d'une fonction, son résultat doit être dans l'accumulateur flottant (numérique, ou pointeur de chaîne).
Pour s'amuser avec le paramètre, je vais écrire le décodage d'un paramètre de type chaîne, qui sera ensuite affiché à l'écran, suivi par un message.
defc type_eq_str = $2851
defc read_expr = $2861
defc out_str = $36aa
defc out_str1 = $36ad
cmd_routine:
push HL ; Sauvegarde du pointeur d'exécution
call read_expr ; Lecture de l'expression suivant la commande
ex (sp), hl ; Remplacement de la valeur du pointeur d'exécution
call type_eq_str ; Vérification du type du paramètre
call out_str1 ; Affichage de la chaîne
ld hl, answer
call out_str ; Affichage du message de la commande
pop HL ; Récupération du pointeur d'exécution
ret
answer:
defm " <-- PARAM", $00
La structure est assez simple, car les routines nécessaires sont toutes disponibles dans la ROM.
Tout d'abord, on sauve le pointeur vers la chaîne interprétée. HL
est actuellement sur le début du paramètre de DISK
(en tout cas, ce qui suit).
L'appel à read_expr
se charge de lire une expression valide (ou échouer avec une erreur). À la sortie de la routine, HL
est positionné à la fin de ce qui a été consommé par l'expression.
Ce pointeur est celui qu'il faudra conserver pour l’interpréteur. Du coup, on échange la valeur précédente de HL
actuellement sur la pile avec la nouvelle valeur. Hop, le tour est joué.
L'appel à type_eq_str
vérifie si le type de l'expression est bien une chaîne, et affiche un message d'erreur sinon (et dans ce cas l'interprétation est arrêtée immédiatement).
Si c'est bien une chaîne, l'appel à out_str1
affiche le résultat de l'expression de type chaîne de caractères qui vient d'être évaluée.
L'appel à out_str
qui suit affiche la chaîne terminée par un 0
définie dans le programme.
Et voilà, voir le résultat à la fin de l'article.
BASIC
Voici un programme BASIC pour charger et lancer la routine.
10 CLEAR 50,&"79FF"
20 S=&"7A00"
30 READ A$
40 IF A$="FIN" THEN END
50 A$="&"+CHR$(34)+A$+CHR$(34):A=VAL(A$)
60 POKE S,A
70 S=S+1
80 GOTO 30
300 DATA F5,E5,3E,C3,32,F4,47,21,10,7A,22,F5,47,E1,F1,C9,E5,CD,61,28
310 DATA E3,CD,51,28,CD,AD,36,21,23,7A,CD,AA,36,E1,C9,20,3C,2D,2D,20
320 DATA 50,41,52,41,4D,0
1000 DATA FIN
RUN
CALL &"7A00"
Pour éviter d'avoir à tout rentrer au clavier, voici le fichier .k7. À charger avec CLOAD
, suivi d'un RUN
et du CALL &"7A00"
.