Retro-ingénierie d'une carte Z80 : partie 7 - Stack Pointer

Bonjour à tous,

Il y a de nombreux mois, j'ai récupéré une ancienne centrale téléphonique analogique à base de Z80.  Les derniers articles partie 5 et partie 6 faisaient état des tests avec les PIOs permettant d'ajouter des entrées/sorties sur un processeur Z80.

Les choses devenant intéressantes il est aussi temps de se pencher sur la gestion de la pile (le Stack en anglais) et réaliser un feu de passage à niveau en faisant clignoter en alternance les LEDs D6 et D7.

Source: exemples Z80-ASM

La pile / Stack

La stack , comme son nom le suggère, empile l'adresse courante avant d'initier un appel vers une sous-routine.
Dans son fonctionnement, la pile (stack) fonctionne selon le principe "dernier entré = premier sorti".


Cela permet de programmer un saut (call) vers une sous-routine. 

  • Juste avant le saut, call empile (push) l'adresse de départ sur la pile (stack) puis effectue le saut vers la sous-routine. 
  • La sous-routine s'exécute.
  • En fin d'exécution de la sous-routine, l'instruction ret permet de revenir au point de départ. ret extrait l'adresse de retour (pop)  de la stack puis fait le saut en arrière vers l'adresse de départ.
  • Le programme peut alors poursuivre son exécution.


Le pile (stack) peut aussi être utilisé pour stocker des données complémentaires comme l'état des registres  (mais ce n'est pas le but du présent article).

  • La pile est stockée à la fin de l'espace mémoire (également nommé memory top).
  • Le stack pointer (Pointeur de pile, registre SP) contient l'adresse du dernier élément ajouté sur la pile (Stack).
  • La pile fonctionne de façon inversée et décrémente la position du pointeur avant chaque ajout.
  • Lorsqu'un élément est retiré de la pile (stack), le pointeur de pile est incrémenté (en direction de la fin de mémoire)
  • La pile (stack) stocke des octets (8 bits à la fois). Par conséquent pour ajouter une adresse (16 bits), le pointeur de pile (stack pointer) est incrémenté de deux octets.

 

Exemple assembleur

Le programme assembleur 05_stack.asm démontre l'usage d'une sous-routine  "_delay" permettant d'insérer un temps de pause (en effectuant des opérations de soustraction) sinon il serait impossible de voir les LEDs clignoter.

L'analyse de la CPU-Board (CPU-BOARD/docs) à permis d'apprendre que les 2 Kios de mémoire SRAM est accessible entre les adresses 0x2000 et 0x27FF.


Comme dans les articles précédents, io.asm est utilisé pour définir des constantes.
include 'io.asm'

RAM_BASE: equ 0x2000 ; voir table d'adresse CPU-BOARD
RAM_END:  equ 0x27FF

org     0x0000               ; Point d'entrée du Reset a froid
RST0:                        ; aussi appelé Reset 0

    ld sp,RAM_END+1          ; initialisation "stack pointer"

    ld a, $00                ; Eteindre toutes les LED de RCIO
    out (RCIO_OUTPUT), a


START:
    ld a, 0x80           ; Allumer LED D7 (éteindre les autres)
    out (RCIO_OUTPUT),a  ; 

    call _delay

    ld a, 0x40           ; Allumer LED D6 (éteindre les autres)
    out (RCIO_OUTPUT),a  ; 

    call _delay
    jp START             ; recommencer la boucle

Et voici les détails de la fonction _delay qui insère une pause de ~500ms à chaque appel.

_delay:
    ld hl, 0x8000 ; valeur initiale = temps de pause
dloop:
    dec hl        ; décompter de 1
    ld a, h       ; Vérifier que tous les 16 bits sont a 0
    or l          ;   en faisant un OR de poids fort et poids faible
    jp nz, dloop  ; Si Zero --> Ligne suivante
                  ; Si PAS Zero --> saut à dloop
    
    ret           ; retour vers l'appelant

Voila, c'était un exercice très intéressant.

Ensuite

Il reste encore tellement à découvrir.
Les prochaines fois, nous nous attarderons sur:

  • Les conventions d'appels (qui utilisent aussi la pile)
  • La manipulation de la RAM
  • Le CTC/Timer (dispositif permettant de compter et gérer des actions planifiées)
  • L'ajout d'un SIO (UART)

Ressources

 

Aucun commentaire