ELEMANIA
Z80 - Lo stack e le istruzioni PUSH e POP
Salvataggio in memoria dei registri

Il numero dei registri interni general purpose dello Z80 è piuttosto limitato e il programmatore si trova spesso nella necessità di liberare un registro per poterlo riusare per altri scopi, senza però perderne il contenuto.

Per fare ciò possiamo usare le istruzioni di LD che consentono di salvare e ripristinare l'accumulatore in memoria RAM. Per esempio:

            LD (8000h),A       ; scrive il contenuto di A nell'indirizzo 8000h
            LD A,(8000h)       ; legge il contenuto di 8000h nell'accumulatore A

In modo simile è possibile salvare e successivamente ripristinare gli altri registri a coppie, nel seguente modo:

            LD (8000h),A       ; scrive il contenuto di A nell'indirizzo 8000h
            LD (8001h), BC    ; scrive il contenuto di BC nell'indirizzo 8001h
            LD (8003h), DE    ; scrive il contenuto di DE nell'indirizzo 8003h
            LD (8005h), HL    ; scrive il contenuto di HL nell'indirizzo 8005h

            ....                    ; resto del programma

            LD A,(8000h)       ; ripristina il contenuto di 8000h nell'accumulatore A
            LD BC, (8001h)    ; il contenuto di 8001h in BC
            LD DE, (8003h)    ; il contenuto di 8003h in BC
            LD HL, (8005h)    ; il contenuto di 8001h in BC

Si noti che il valore di A occupa una sola locazione di memoria (indirizzo 8000h), mentre le successive coppie di registri ne occupano 2. Solo per il registro accumulatore A è disponibile una istruzione LD in memoria separata; tutti gli altri registri devono essere caricati a coppie.

Un modo per rendere un po' più amichevole il codice precedenti è usare la direttiva EQU che permette di associare gli indirizzi numerici con nomi mnemonici (creando una specie di pseudo  variabile in assembly) nel modo seguente:

vA	EQU 8000h
vBC	EQU 8001h
vDE	EQU 8003h
vHL	EQU 8005h

        LD (vA),A       ; salva A  
        LD (vBC), BC    ; salva BC
        LD (vDE), DE    ; salva DE
        LD (vHL), HL    ; salva HL
        ....            ; resto del programma

        LD A,(vA)       ; ripristina A 
        LD BC, (vBC)    ; ripristina BC
        LD DE, (vDE)    ; ripristina DE
        LD HL, (vHL)    ; ripristina HL

 

Lo stack

In molti casi sarebbe più comodo non doversi occupare esplicitamente di gestire la memoria RAM e di conoscere gli indirizzi esatti dove vengono salvati i dati (registri). Questo è possibile se il µP dispone di un meccanismo automatico che faccia le nostre veci nel decidere gli opportuni indirizzi di memoria, mentre noi ci occupiamo soltanto dei dati da trasferire.

Lo stack (tradotto talvolta in italiano con pila o catasta) è appunto un'area di memoria privilegiata, dove il microprocessore può salvare momentaneamente e poi recuperare dati di varia natura. Precisiamo subito che quest'area è speciale solo per l'uso che ne facciamo: dal punto di vista hardware non ha niente di strano, anzi, si tratta semplicemente di un'area della normale memoria RAM

All'interno dello Z80 è presente un registro speciale, detto stack pointer (SP, puntatore allo stack), il cui scopo è appunto quello di gestire lo stack. Si presti attenzione a non confondere i termini: lo stack pointer (e la sua logica di gestione) si trovano all'interno del µP, mentre lo stack vero e proprio risiede nella RAM collegata al microprocessore.

La prima operazione che è necessario compiere per poter utilizzare lo stack è inizializzare lo stack pointer con l'indirizzo base dello stack. In pratica il programmatore decide in quale area della RAM desidera memorizzare lo stack e le sue dimensioni e inizializza lo SP con un'istruzione del tipo:

            LD SP,0FFF0h     ; inizializza SP con l'indirizzo 0FFFh (esempio)

Questa istruzione viene generalmente eseguita all'inizio di ogni programma in assembly. La figura seguente mostra il significato di stack e di stack pointer:

Si osservi che lo SP all'inizio punta al fondo dell'area di stack, cioè all'indirizzo più alto nello stack. Le operazioni push e pop eseguite sullo stack fanno crescere l'area di stack "all'indietro" (backwards), cioè, all'aumentare dei dati salvati sullo stack, lo stack pointer viene progressivamente decrementato.

Per queste ragioni di solito l'area riservata allo stack occupa gli indirizzi più alti in RAM, a partire dall'ultimo indirizzo (in modo da riservare il maggior spazio possibile alla crescita dello stack). Anzi, dato che, come vedremo meglio fra poco, la prima locazione dello stack non viene utilizzata, spesso lo stack pointer viene inizializzato col primo indirizzo in memoria 0000h, in modo tale che, al primo decremento, esso punti all'ultima locazione libera (FFFFh).

Push e pop

Come suggerito dal nome stesso, lo stack viene gestito dal µP come una pila o catasta di oggetti (esempio una pila di fogli posati su una scrivania). Sull'area di memoria stack è possibile effettuare solo due operazioni e precisamente:

Le operazione di push e pop gestiscono lo stack secondo un criterio detto First In First Out (FIFO), ovvero: il primo dato a uscire è l'ultimo entrato (quello posizionato in cima allo stack).

Le operazioni push e pop corrispondono a due istruzioni omonime dello Z80:

    PUSH HL    ; mette il contenuto di HL (o AF, BC, DE) in cima allo stack
    PUSH IX    ; mette il contenuto di IX (o IY) in cima allo stack
    POP HL     ; preleva il contenuto di HL (o AF, BC, DE) dalla cima dello stack
    POP IX     ; preleva il contenuto di IX (o IY) dalla cima dello stack

Come si può osservare tutte le istruzioni precedenti operano su dati a 16 bit che vengono trasferiti da/verso lo stack suddivisi in due byte, alto e basso, occupando pertanto due locazioni di memoria consecutive. Consideriamo l'esempio che segue:

        LD SP,0FFF0h      ; inizializza SP con l'indirizzo 0FFFh (esempio)
        LD BC, 0001h      ; memorizza il valore 0001h in BC
        LD HL, 0302h      ; memorizza il valore 0005h in HL
        PUSH BC            ; salva il contenuto di BC sullo stack
        PUSH HL            ; salva il contenuto di HL sullo stack
        POP BC             ; preleva la cima dello stack e la salva in BC
        POP HL             ; preleva la cima dello stack e la salva in HL

Al termine dell'esecuzione di questa sequenza di istruzioni i due registri BC e HL si sono scambiati il contenuto: BC contiene 0302h e HL contiene 0001h.

La figura seguente mostra la situazione dello stack e dei registri interni dopo l'esecuzione delle istruzioni PUSH BC e PUSH HL:

Osserviamo in particolare che:

Vediamo ora l'effetto dell'esecuzione delle due istruzioni POP BC e POP HL:

Si osservi che alla fine lo SP contiene lo stesso valore che conteneva inizialmente (FFF0). Tuttavia è bene sapere che lo Z80 non effettua alcun controllo di correttezza sulle operazioni compiute sullo stack e che pertanto è compito del programmatore accertarsi del fatto che il numero di POP sia uguale al numero di PUSH precedentemente eseguite.

Osserviamo inoltre che i valori non vengono fisicamente cancellati dalla memoria RAM dopo essere stati prelevati con un'istruzione POP. Tuttavia essi, trovandosi al di sotto della locazione puntata da SP, risultano in pratica inaccessibili con ulteriori operazioni di POP sullo stack.

Salvataggio e ripristino di tutti i registri interni con lo stack

Al termine della nostra discussione sul funzionamento dello stack, vediamo ora un primo semplice esempio di come l'area di stack potrebbe essere utilizzata per salvare temporaneamente il contenuto di tutti i registri interni dello Z80 e per ripristinarli in seguito. Si consideri il seguente spezzone di programma:

        PUSH AF        ; inizia il salvataggio dei registri nello stack
        PUSH BC
        PUSH DE
        PUSH HL
        PUSH IX
        PUSH IY

        .....          ; il programma prosegue con qualsiasi istruzione che
        .....          ; modifica il contenuto dei registri interni

        POP IY         ; inizia il ripristino dei registri
        POP IX         ; si noti come il ripristino viene fatto al contrario
        POP HL         ; rispetto al salvataggio
        POP DE
        POP BC
        POP AF

Evidentemente il programmatore aveva bisogno di usare tutti i registri interni alla CPU, in un qualche compito "locale", ma senza perderne il contenuto. Dopo l'esecuzione di tutte le POP, la situazione precedente viene completamente ripristinata. Osserviamo che:

a) il programmatore non ha dovuto indirizzare esplicitamente le locazioni di memoria usate;
b) un certo numero di PUSH deve essere sempre seguito da un numero identico di POP (altrimenti lo stack di riempie senza svuotarsi o viceversa);
c) la sequenza di recupero deve essere rovesciata rispetto a quella di salvataggio.

La coppia PUSH/POP è ideale quando è necessario usare un registro senza rischiare di perderne il contenuto; può succedere infatti di avere tutti i registri caricati con valori importanti e, non avendone altri (dato il loro limitato numero...) di non sapere come fare. In questi casi è possibile salvare i registri sullo stack (con l'istruzione PUSH), riusarli liberamente e quindi ripristinare i valori iniziali (con la POP).

Un altra applicazione frequente delle istruzioni PUSH e POP è lo scambio di valori fra i registri, come abbiamo già visto prima.

In realtà il salvataggio e il ripristino dei registri interni dello Z80 può essere effettuato in modo più rapida ed efficiente usando le istruzioni EX e EXX.  La seconda scambia il contenuto delle copie di registri BC, DE e HL con le corrispondenti copie "ombra" (shadow registers) BC', DE' e HL'. Restano esclusi da questo salvataggio l'accumulatore A e il registro dei flag F, per salvare i quali occorre usare l'istruzione EX. Ecco un esempio:

        EX AF, AF'        ; scambia i contenuti di AF e AF'
        EXX                ; scambia i contenuti degli altri registri

        .....                ; il programma prosegue con qualsiasi istruzione che
        .....                ; modifica il contenuto dei registri interni

        EXX                ; queste due istruzioni di scambio
        EX AF,AF'        ; ripristinano i valori precedenti

 

precedente - successiva

Sito realizzato in base al template offerto da

http://www.graphixmania.it