Calcolatori elettronici/Le procedure
Una procedura è una porzione di codice richiamabile tramite il suo nome in qualsiasi punto del programma. Il salto alla procedura supporta il ritorno al chiamante. Migliora la leggibilità del codice e risparmia memoria.
Struttura
[modifica | modifica sorgente]Una procedura si definisce in questo modo:
<etichetta> PROC <tipo>
- ...
<etichetta> ENDP
La procedura può essere richiamata con l'istruzione CALL <etichetta>
. In realtà nell'istruzione CALL
l'assemblatore converte il simbolo <etichetta>
nell'indirizzo della prima istruzione della procedura. Alla chiamata, l'indirizzo del chiamante viene salvato dall'IP allo stack.
L'istruzione RET
, priva di parametri, permette di ritornare alla procedura chiamante.
Località
[modifica | modifica sorgente]Una procedura di tipo NEAR
(predefinito) può essere richiamata solo all'interno dello stesso segmento di codice, e viene richiamata da una CALL
su 3 byte tramite l'offset della sua prima istruzione. Una procedura di tipo FAR
può essere chiamata da procedure appartenenti a segmenti di codice diversi; la CALL
su 5 byte prima di caricare l'offset della procedura deve modificare il registro CS, effettuando il push nello stack del vecchio valore di CS.
Anche l'istruzione RET
si distingue per i tipi NEAR
e FAR
, effettuando la pop dallo stack di uno o due word.
Memorizzazione di variabili locali
[modifica | modifica sorgente]La procedura può memorizzare temporaneamente nello stack le variabili dichiarate in modo locale al suo interno.
Backup del contenuto dei registri
[modifica | modifica sorgente]Le procedure vengono richiamate tramite l'istruzione CALL
, che salta alla prima istruzione della procedura. Al termine della procedura, l'istruzione RET
deve poter ritornare al chiamante → l'istruzione CALL
, prima di sovrascrivere il PC con il nuovo indirizzo, deve quindi temporaneamente salvare (push) nello stack il PC del chiamante, che verrà letto (pop) dall'istruzione RET
. La struttura LIFO garantisce che l'ordine dei PC letti da una serie di istruzioni RET
appartenenti a procedure annidate sia corretto, cioè dall'ultima procedura chiamata fino al primo chiamante.
I valori dei registri possono essere salvati prima della prima istruzione e ripristinati dopo l'ultima istruzione, in modo che la procedura non "sporchi" i registri.
Passaggio di parametri
[modifica | modifica sorgente]I linguaggi assembler non hanno alcun meccanismo apposito per il passaggio dei parametri.
Prima della chiamata della procedura il chiamante può salvare nello stack i parametri, che verranno letti durante l'esecuzione della procedura. Il problema è che l'istruzione CALL
li coprirebbe con il valore dell'IP. Il registro BP serve per salvare temporaneamente il valore dell'SP subito dopo la chiamata come prima istruzione della procedura (lo stack si riempie a partire dal fondo), in modo che il primo parametro sia accessibile all'indirizzo [BP+2]
. Un semplice ripristino dell'SP al valore originario segue il ritorno al chiamante. I parametri di ritorno possono essere salvati dalla procedura e letti dal chiamante in una posizione destinata appositamente nello stack tramite una istruzione SUB
dell'SP che precede le push dei parametri. Non c'è limite al numero di parametri, e i parametri rimangono in memoria solo per il tempo di esecuzione della procedura. Questa soluzione non può essere applicata a procedure annidate, perché il registro BP può contenere un solo valore di SP; per ovviare a questo problema, si può salvare il contenuto di BP a sua volta nello stack subito prima di sovrascriverlo con il valore di SP, e quindi ripristinarlo con una pop subito prima della istruzione RET
, avendo l'accortezza di accedere al primo parametro all'indirizzo [BP+4]
.
Altri modi
[modifica | modifica sorgente]Variabili globali
[modifica | modifica sorgente]La procedura può usare direttamente delle variabili globali, che sono visibili a tutte le procedure compresa quella chiamante.
- Svantaggio
- non è una soluzione flessibile
Registri
[modifica | modifica sorgente]Lo scambio di informazioni tra la procedura e il chiamante avviene tramite i registri.
I registri che non sono restituiti al programma chiamante possono essere usati internamente alla procedura, salvando temporaneamente nello stack i loro vecchi valori.
- Svantaggio
- il numero di registri è limitato