ELEMANIA
Z80 - Lo stack e la chiamata
Lo stack nella chiamata a sottoprogrammi

Nella lezione precedente abbiamo visto che l'istruzione CALL deve salvare da qualche parte l'indirizzo di ritorno dal sottoprogramma in modo che tale indirizzo possa essere usato dall'istruzione RET. L'area in cui avviene il salvataggio e il recupero dell'indirizzo è lo stack.

In pratica l'istruzione CALL esegue un push automatico del contenuto del program counter (il registro/contatore che contiene sempre l'indirizzo della successiva istruzione da eseguire) sullo stack. Viceversa la RET esegue una pop dalla cima dello stack al program counter:

Tale meccanismo di salvataggio e di recupero è assolutamente automatico e trasparente per il programmatore, il quale deve solo occuparsi di allocare un'area di memoria per lo stack (inizializzando opportunamente il valore dello stack pointer).

L'utilità di questo meccanismo di salvataggio e ripristino risulta evidente se si considera il caso, tutt'altro che infrequente, di un sottoprogramma che al proprio interno ne chiama un altro. Consideriamo l'esempio seguente:

  LD SP,0000h  ; inizializza il fondo dello stack a 0000h
  LD B, 2 ; carica in B il numero 2
  LD C, 5  ; carica in C il numero 5
  CALL MEDIA ; chiama il sottoprogramma di nome MEDIA
  LD B, D  ; carica D in B
  LD C, 7  ; carica in C il numero 7
  CALL MEDIA ; chiama il sottoprogramma di nome MEDIA
  LD E, D   ; carica D in E
  ..... ; il programma prosegue con altre istruzioni
DIVIDI:   SRA A ; divide A per due (scalandolo a destra di uno)
  RET ; ritorna al punto di chiamata (fine del sottoprogramma)
     
MEDIA:  LD A,B ; carica B in A (inizio del sottoprogramma)
  ADD A,C   ; somma A e C e mette il risultato in A
  CALL DIVIDI  ; chiama il sottoprogramma DIVIDI
  LD D, A   ; sposta il risultato della divisione in D
  RET ; ritorna al punto di chiamata (fine del sottoprogramma)

In questo esempio il programma principale chiama il sottoprogramma MEDIA, il quale a sua volta chiama DIVIDI. Affinché il meccanismo delle chiamate e dei ritorni funzioni correttamente occorre che la RET di DIVIDI torni a MEDIA, il quale a sua volta deve tornare al programma principale:

E qui entra in gioco lo stack. Infatti l'indirizzo dell'istruzione LD B,D nel programma principale viene salvato sullo stack al momento di CALL MEDIA. Quando, all'interno di MEDIA, viene chiamata DIVIDI, viene salvato sullo stack l'indirizzo dell'istruzione LD D,A (istruzione del sottoprogramma MEDIA). Quindi la RET di DIVIDI preleva questo indirizzo dallo stack, mentre la seconda RET, quella di MEDIA, preleva dallo stack il primo indirizzo salvato:

Si osservi che, ogni volta che si esegue una CALL, viene salvato un nuovo indirizzo di ritorno sulla cima dello stack, con conseguente crescita dello stack stesso. Non essendo previsto nello Z80 nessun automatismo di controllo del valore assunto dallo stack pointer, se il numero di chiamate annidate (cioè di sottoprogrammi che chiamano altri sottoprogrammi) è troppo elevato, il rischio è che lo stack possa straripare (stack overflow) andando a sovrascrivere altre aree di memoria.

Per evitare problemi, bene sovradimensionare la zona di memoria destinata allo stack, cercando di stimare le proprie necessità in termini di annidamenti ed uso normale delle PUSH e POP. Microprocessori più recenti e più evoluti possiedono dei meccanismi di protezione contro questo tipo di overflow della memoria.

 

precedente - successiva

Sito realizzato in base al template offerto da

http://www.graphixmania.it