Assembleur Z80 sur RC2014 - Small Computer Monitor APIs

Bonjour à tous,

Dans la série de vidéos sur le Spectrum s'appuie sur les interfaces (API) disponible sur le Spectrum.

API Spectrum vs API SCM

Le RC2014 n'est pas un Spectrum et ne dispose pas des API Spectrum... mais il dispose de ses API décrite dans la documentation de SCM (voir le billet "Learning the Small Computer Monitor", Google Group, Document PDF).  

Ce billet se penche sur quelques API digne d'intérêt en présentant l'appel dans SCM avec la commande API et puis l'appel depuis du code assembleur. 


API $02 - Sortie caractère

L'API 2 envoi un caractère sur le périphérique de sortie (la console active).

Le caractère est envoyé en paramètre à l'API. L'exemple ci-dessous affiche le caractère W (87 décimal, 0x57) sur la console.

*
*
*
*API 2 $57
W57 0057
*

Une fois l'appel effectué, la lettre W est bien visible avant de recevoir le retour des l'appel.

L'appel d'API en ASSEMBLEUR se fait avec un appel à RST $30 en indiquant le numéro de fonction dans le registre C et le paramètre dans le registre A.

ld a, 'W' ; charger caractère
ld c,$02  ; API a appeler
RST 30    ; Appel d'API

Voici le programme assemblé.

*a 8000
8000: 0E 57        .W    LD C,$57              > ld a,'W'
8000: 3E 57        >W    LD A,$57
8002: 3E 02        >.    LD A,$02              > ld c,$002
8002: 0E 02        ..    LD C,$02
8004: F7           .     RST 30                > rst 30
8004: F7           .     RST 30
8005: C9           .     RET                   > ret
8005: C9           .     RET
8006: 00           .     NOP                   > .
*g 8000
W*

La première instruction peut aussi être ld a,$57 . L'instruction RET permet de rendre la main à SCM.

L'appel de l'API modifie certains registres durant l'exécution. Si votre programme compte sur la valeur de certains registres alors il faut les pousser sur la pile (avec des PUSH) avant l'appel puis récupérer celle-cis après l'appel d'API (avec des POP).

API $01 - Entrée caractère

L'API 1 permet de capturer un caractère sur le périphérique d'entrée/sortie. L'appel d'API attend la réception (saisie) d'un caractère puis indique la valeur de celui-ci par retour d'API.

*api 1
6C 0001
*

L'API indique le caractère $6C (108 en décimal), cela correspond au caractère "l" (L minuscule).

En assembleur, la valeur ASCII est retourné dans le registre A.

ld c,$01 ; input char
rst 30
ret

Alors comme l'assembleur n'affiche pas le contenu des registres pendant son exécution, il faudra placer un breakpoint (point d'arrêt) sur la ligne $8003, ce qui permettra d'inspecter la valeur du registre A avant qu'il ne soit modifié.

*a 8000
8000: 00           .     NOP                   > ld c,$01
8000: 0E 01        ..    LD C,$01
8002: 00           .     NOP                   > rst 30
8002: F7           .     RST 30
8003: 00           .     NOP                   > ret
8003: C9           .     RET
8004: 00           .     NOP                   > .
*b 8003
Breakpoint set
*g 8000
Breakpoint
PC:8003 AF:7438 BC:2501 DE:6DA4 HL:0325 IX:EB4A IY:EA39 Flags:---H----
*

Le breakpoint est placé avec la commande "b 8003". Quand le programme est démarré avec "g 8000", il attend la pression d'une touche (entrée sur la console) puis le breakpoint s'active sur l'instruction RET (juste avant son exécution).
Presser la touche Entrée pour exécuter la ligne suivante.

L'activation du breakpoint affiche l'état des registres. Le registre A contient la valeur $72, ce qui correspond à la lettre "r".

API $07 - Retour à la ligne

L'API 7 permet d'envoyer une séquence de retour à la ligne avec les caractères CR/LF ($0A/$0D). Cela évite de faire deux appels d'API consécutifs.
C'est une fonction bien pratique.

API $06 - Sortie d'une ligne

L'API 6 permet de sortir une chaîne de caractère complète sur la sortie de la console. La chaîne doit être terminée par un NULL (codé 0x00). 

Cette API reçoit l'adresse de la chaîne de caractère en RAM  ou ROM. Cette adresse est communiquée par le registre DE.

Il faut donc avoir une telle chaîne de caractère définie quelque-part, ce que nous allons faire à l'adresse $8500 avec "e 8500". La zone aura au préalable été remplie avec des 0x00 et la commande "m 8500" permet de vérifier le contenu de la mémoire.

*fill 8500 8600 00
*e 8500
8500: 00           .     NOP                   > "RC2014 is really AWESOME!
8519: 00           .     NOP                   > 00
851A: 00           .     NOP                   > .
*m 8500
8500:  52 43 32 30 31 34 20 69  73 20 72 65 61 6C 6C 79  RC2014 is really
8510:  20 41 57 45 53 4F 4D 45  21 00 00 00 00 00 00 00   AWESOME!.......
8520:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
8530:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
8540:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
8550:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
8560:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
8570:  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................

Maintenant que l'information est en place dans la mémoire, il est possible de d'appeler l'API.

*
**api 6 8500
RC2014 is really AWESOME!00 851A
*

Le 00 851A présenté sont les retours de l'API. $851A est par ailleurs l'adresse du dernier caractère de la chaîne (avant le $00).

Appel en assembleur

Pour appeler cette API, il faut initialiser le registre DE avec l'adresse de la chaîne de caractères.

*a 8000
8000: 00           .     NOP                   > ld de,$8500
8000: 11 00 85     ...   LD DE,$8500
8003: 00           .     NOP                   > ld c,$6
8003: 0E 06        ..    LD C,$06
8005: 00           .     NOP                   > rst 30
8005: F7           .     RST 30
8006: 00           .     NOP                   > ret
8006: C9           .     RET
8007: 00           .     NOP                   > .
*g 8000
RC2014 is really AWESOME!*
*

Après l'affichage de la chaîne, SCM présente immédiatement son invite de commande  "*" sans retour à la ligne puisqu'il n'y en a pas dans la chaîne de caractères.

API $04 - Entrée une ligne

L'API 4 permet de saisir une ligne sur le périphérique d'entrée jusqu'à la longueur indiquée lors de l'appel (ou jusqu'au retour clavier). La chaîne de caractère est stockée dans l'emplacement mémoire prévu à cet effet.

La chaîne de caractère est terminée par un 0x00 (NULL) mais ne contiendra pas le retour clavier ($0D). L'emplacement du caractère 0x00 doit être prévu dans la mémoire pour y être également stocké. 

La chaîne de caractères stockée à l'adresse indiquée (par le registre DE) et la longueur de la chaîne encodée est retournée dans le registre A (longueur excepté 0x00).

Dans l'exemple ci-dessous, nous allons stocker une chaîne de caractères de 25 caractères (donc un emplacement de 26 caractères pour le 0x00) à l'emplacement $8500.
Pour rendre la saisie évidente nous allons pré-initialiser la mémoire $8500 à $8600 avec des 0xFF.

*fill 8500 8600 FF
*m 8500
8500:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8510:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8520:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8530:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8540:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8550:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8560:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8570:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
*

L'intérêt de cet API concerne surtout les programmes que l'on pourrait écrire en assembleur à partir de $8000.

ld de,$8500 ; storage buffer
ld a,$1a ; len 25 + null
ld c,$04 ; Input line API
rst $30  ; call API
ret      ; return SCM

Voici l'assemblage du code:

8000: 00           .     NOP                   > ld de,8500
8000: 11 00 85     ...   LD DE,$8500
8003: 00           .     NOP                   > ld a, $1a
8003: 3E 25        >%    LD A,$25
8005: 00           .     NOP                   > ld c,4
8005: 0E 04        ..    LD C,$04
8007: 00           .     NOP                   > rst 30
8007: F7           .     RST 30
8008: 00           .     NOP                   > ret
8008: C9           .     RET
8009: 00           .     NOP                   > .
*
*

En exécutant le code, il est possible de saisir jusque 25 caractères que l'on peut ensuite visualiser en mémoire à l'adresse $8500.

*g 8000
Dodo teste son RC2014 en
*m 8500
8500:  44 6F 64 6F 20 74 65 73  74 65 20 73 6F 6E 20 52  Dodo teste son R
8510:  43 32 30 31 34 20 65 6E  20 00 FF FF FF FF FF FF  C2014 en .......
8520:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8530:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8540:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8550:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8560:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................
8570:  FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................

Il est effectivement possible de saisir jusqu'à 25 caractères (même si le dernier est un espace 0x20). La chaîne de caractères se termine bien par un null (0x00).

API $0A - Delay (ms)

L'API $0A permet de créer une pause en x milliseconde. Ce délai est calculé sur base d'une horloge CPU cadencée 7.37Mhz. Si la fréquence d'horloge est différente alors il faudra appliquer une règle de trois pour adapter le délai d'attente souhaité.

voici quelques instructions assembleurs.

ld de,+2000 ; 2000ms (decimal)
ld c, $0A   ; delay API
rst 30
ret         ; return to SCM

Ensuite

Ce ne sont là que quelque-unes des API les plus utiles pour tester des routines assembleurs avec Small Computer Monitor (SCM).

Voyez les autres APIs décritent dans la documentation de SCM (voir le billet "Learning the Small Computer Monitor", Google Group, Document PDF).

Aucun commentaire