Assembleur Z80 sur RC2014 - Contrôle des ports périphériques avec module Digital I/O

Bonjour à tous,

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

Le précédent article traitait des "API Spectrum vc API SCM", aujourd'hui, nous allons manipuler la carte Digital I/O en assembleur déjà abordée dans les billets:

Rappel Digital I/O

Le module Digital I/O que nous allons utilisé exploite l'adresse/port 0x00. Une écriture sur le port permet d'allumer les LEDs correspondantes, une lecture sur le port permet de lire l'état des boutons.

RC2014: Assemblage et test de la carte Digital I/O

Pouvoir allumer facilement des LEDs permet de suivre le fonctionnement d'un programme assembleur... ou tester facilement un périphérique matériel.

Contrôle de la carte Digital I/O en assembleur

Pour contrôler la carte, il suffit de lire ou écrire des octets sur le port du périphérique.

Voici un petit programme assembleur qui allume toutes les LEDs du port (les 8 bits). La mémoire est d'abord remplie avec des NOP avant de commencer l'assemblage.

*fill 8000 8050 00
*a 8000
8000: 00           .     NOP                   > ld a,$FF
8000: 3E FF        >.    LD A,$FF
8002: 00           .     NOP                   > out (0),a
8002: D3 00        ..    OUT ($00),A
8004: 00           .     NOP                   > ret
8004: C9           .     RET
8005: 00           .     NOP                   > .
*

Comme il n'est pas possible d'écrire directement une valeur sur le port de sortie, il faut passer par le registre A.
Puis on transfert le contenu du registre A vers le port de sortie 0x00 avec "out (0),a".

Il est possible d'utiliser la représentation binaire pour fixer l'état des LEDs. En voici un exemple. 

ld a,%10000001 ; light first & last LEDs
out (0),a ; send to peripheral
ret       ; return to SCM

En exécutant le programme avec "g 8000", les LEDs s'allumes.

Lecture de la carte Digital I/O en assembleur

L'instruction "in A,($00)" permet de lire l'état du périphérique/port 0 et de le transférer dans le registre A. 

Le petit script suivant lit l'état des boutons et l'applique immédiatement sur les LEDs.

*
*d 8000
8000: DB 00        ..    IN A,($00)
8002: D3 00        ..    OUT ($00),A
8004: C3 00 80     ...   JP $8000
8007: 00           .     NOP
8008: 00           .     NOP
8009: 00           .     NOP

Semi LARSON Scanner

Le petit script ci-dessous utilise la module Digital I/O pour créer une scanner Larson et l'API $0A pour insérer un délai de 100ms entre deux changements d'états.

Le script termine son exécution si l'un des boutons est pressé sur la carte digital I/O.

Voici une vidéo du fonctionnement de code assemblé.

 


Code assemblé sur Small Computer Monitor

*fill 8000 8100 00
*d 8000
8000: 3E 01        >.    LD A,$01    ; initial value
8002: D3 00        ..    OUT ($00),A ; @Loop. Light LEDs
8004: CB 07        ..    RLC A       ; Rotate Left 8 Bits
8006: F5           .     PUSH AF     ; store A 
8007: 11 F4 01     ...   LD DE,$0064 ; 100ms 
800A: 0E 0A        ..    LD C,$0A    ; delay API
800C: F7           .     RST 30      ; call API
800D: DB 00        ..    IN A,($00)  ; Request input button
800F: B7           .     OR A        ; upd. Zero flag
8010: C2 17 80     ...   JP NZ,$8017 ; jump @End if not pressed
8013: F1           .     POP AF      ; restore A
8014: C3 02 80     ...   JP $8002    ; Jump @loop
8017: F1           .     POP AF      ; @End. Clear stack
8018: C9           .     RET         ; Return to SCM

Quelques explication:

  1. $8000: Le registre A contiendra la valeur qu'on fera passer d'un bit à l'autre. Le bit 0 est placé à 1.
  2. $8002-8004: envoi la donnée sur le port de sortie (allume la 1ière LED) puis effectue un décalage circulaire à gauche avec l'instruction RLC.
  3. $8006: Comme nous allons exécuter un appel d'API (delay) et lire les entrées de la carte digital I/O alors il est fort probable que les registres soient altérés! Il faut donc pousser l'état du registre A sur la Pile pour en sauvegarder l'état.
  4. $800D: lecture de l'état des boutons sur le port 0 dans le registre A. Cette opération n'altere pas les Flags... donc nous ne pouvons savoir si la valeur réceptionnée est égale à zéro ou supérieure à zéro.
  5. $800F:  effectuer une opération mathématique sur le registre A permettrait de mettre les Flags à jour. La seule opération qui ne modifie pas le contenu de A est A = A or A. On exécute donc "OR A" sur l'accumulateur.
  6. $8010: Si la valeur du registre A à un flag Z=1 cela signifie qu'aucun bouton n'est pressé. Si aucun bouton pressé alors on peu continuer l'exécution.... Si un bout est pressé Zero Flag = 0 et on peut terminer le programme. L'instruction JP NZ saute vers la fin d'exécution si Non Zero flag.
  7. $8013-8014: Si ces lignes sont exécutés, c'est qu'aucun bouton n'est pressé. Il faut alors récupérer la valeur A sauvegardée sur la pile/stack. Celle-ci contient déjà la prochaine itération/valeur à afficher sur la carte digital I/O. On peut donc reprendre l'exécution a $8002.
  8. $8017-8018: Si ces lignes sont exécutés c'est que quelqu'un a pressé sur l'un des boutons. Il faut veiller à retirer la valeur poussé sur la Pile car Small Computer Monitor utilise aussi la pile pour son propre fonctionnement.  Si on oublie cette valeur sur la Pile alors le moniteur va perdre les pédales.
    Ensuite, l'instruction RET rend la main à l'appelant (soit SCM)

Voilà,

J'espère que vous avez trouvé cela intéressant.

Bon amusement.





Aucun commentaire