Vogliamo adesso fare un esempio dei problemi che si incontrano quando si vuole gestire il passaggio di parametri e il valore di ritorno di una subroutine in assembly.
A tale scopo consideriamo la seguente funzione in linguaggio C per il calcolo della somma di tutti gli interi fino a un valore num dato. Per esempio con num=4 la funzione calcola la somma di 1+2+3+4=10
Questa semplice funzione fa uso di una delle tecniche di programmazione più avanzate e anche più difficili da comprendere per i principianti: la ricorsività.
In informatica viene detto ricorsivo un algoritmo espresso in termini di se stesso, ovvero in cui l'esecuzione dell'algoritmo comporta l'esecuzione di numerose copie (o istanze) dell'algoritmo stesso.
Si consideri l'esempio della funzione precedente. Se il valore di num è diverso da zero, la funzione chiama se stessa. Tuttavia il valore passato a questa seconda istanza della funzione non è il valore originale di num, ma il valore decrementato di una unità (num-1).
In questo modo prima o poi (dopo un certo numero di chiamate ricorsive) il valore di num diventa zero e dunque la ripetizione dell'algoritmo non diventa infinita.
Questa esecuzione ricorsiva è possibile in C in quanto ogni istanza della funzione num ha il proprio spazio di variabili locali che non condivide con nessuno (in questo caso lo spazio consiste nella variabile num). Infatti, sebbene tutte queste variabili num abbiano lo stesso nome, non si tratta affatto della stessa variabile. Pertanto ogni variabile num locale a un'istanza della funzione può assumere valori diversi rispetto alle altre.
Questa creazione di uno spazio privato per le variabili delle funzioni può essere realizzata solo facendo uso dello stack, come si vedrà provando a scrivere il codice assembly per la nostra funzione.
La codifica in assembly di algoritmi ricorsivi presenta la necessità di implementare il salvataggio e il ripristino dallo stack dello spazio delle variabili locali a ogni istanza della subroutine.
Si consideri l'esempio seguente, in cui la funzione ricorsiva sum precedente è tradotta in assembly Z80:
main: LD SP,0 ; inizializza lo stack pointer
IN A,(0) ; legge il valore iniziale di num PUSH AF ; salva il valore sullo stack CALL sum ; chiama la funzione ricorsiva POP AF ; recupera dallo stack il valore del risultato OUT (0),A ; visualizza il risultato HALT
sum: POP DE ; estrae l'indirizzo di ritorno dallo stack POP AF ; estrae dallo stack il parametro (valore di num) PUSH DE ; ripristina l'indirizzo di ritorno sullo stack PUSH BC ; salva lo spazio delle variabili locali del chiamante
CP 0 ; se A è zero (cioè se num è zero) JP Z,fine LD B,A ; copia in B il valore di A (num) DEC A ; decrementa il valore PUSH AF ; salva sullo stack CALL sum ; chiamata ricorsiva POP AF ; recupera il risultato ADD A,B ; somma num col risultato della chiamata fine: POP BC ; ripristina le variabili del chiamante POP DE ; estrae l'indirizzo di ritorno PUSH AF ; mette sullo stack il risultato PUSH DE ; ripristina l'indirizzo di ritorno RET
Si osservi che, al momento della chiamata della subroutine ricorsiva, il valore su cui bisogna fare il calcolo (num, contenuto in A) viene salvato sullo stack. In questo modo però tale valore si trova nello stack al di sotto del valore dell'indirizzo di ritorno della chiamata (salvato in automatico al momento della CALL).
Per questa ragione all'inizio della subroutine occorre prima estrarre il valore di tale indirizzo dallo stack (per poter recuperare il valore sottostante di num) e quindi rimetterlo sullo stack stesso:
sum: POP DE ; estrae l'indirizzo di ritorno dallo stack POP AF ; estrae dallo stack il parametro (valore di num) PUSH DE ; ripristina l'indirizzo di ritorno sullo stack PUSH BC ; salva lo spazio delle variabili locali del chiamante
L'ultima istruzione PUSH BC serve invece per salvare sullo stack lo spazio delle variabili (registri) usati dal chiamante. Infatti tutte le istanze della subroutine usano lo stesso registro B per effettuare la somma, ma ogni copia del registro deve poter conservare un differente valore.
Sito realizzato in base al
template offerto da
http://www.graphixmania.it