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
- Mes premiers programmes assembleur
Sur la carte Z80. Sélection du programme d'assemblage, Makefile, transfert sur EEprom. - Les sources assembleurs (GitHub MCHobby)
kicad-public-projects/HASKEL-Z80/Z80-ASM/test/ - Schéma des cartes Z80 (GitHub MCHobby)
kicad-public-projects/HASKEL-Z80/
Écrire un commentaire