15.4.5 Assembly

Il linguaggio di programmazione di più basso livello (linguaggio macchina a parte) è il linguaggio assembly. Esso non è altro che poco più di una forma mnemonica del linguaggio macchina.

Si tratta di un linguaggio di programmazione per sua natura non strutturato che però permette di avere sotto controllo tutto il dettaglio possibile della macchina: i registri della CPU, gli indirizzi di memoria, gli interrupt, ... Per questo tale linguaggio è dipendente dalla piattaforma hardware utilizzata. Esso è utilizzato per la scrittura di programmi non molto estesi ma molto specializzati, garantendo le massime prestazioni di velocità di esecuzione.

La CPU è in grado di eseguire un numero più o meno limitato di istruzioni, dipendentemente dal fatto se essa è di tipo CISC (Complex Instruction Set Computer) o RISC (Reduced Instruction Set Computer). Le CPU di tipo RISC si basano su un insieme minimo di istruzioni ma possono essere eseguite con una velocità elevatissima, rispetto alle istruzioni eseguibili da una CPU CISC, il cui insieme di base ne raccoglie molte di più.13

Il funzionamento di una CPU si basa sui circuiti presenti al suo interno. Da un punto di vista logico si possono suddividere in (v. fig. 15.12)


pict
Figura 15.12: Schematizzazione della struttura interna di una CPU.

Il linguaggio assembly per processori Intel X386 può essere scritto secondo due sintassi: quella Intel e quella AT&T. Le due sintassi differiscono nella specificazione degli argomenti all’interno delle singole istruzioni ma il linguaggio rimane sostanzialmente lo stesso.

Una delle istruzioni più utilizzate nel linguaggio assembly è mov che permette di assegnare un valore ad un registro o ad una locazione di memoria.

L’istruzione che permette il controllo delle condizioni è cmp. Questa non fa altro che aggiornare il registro dei flag in accordo con il risultato della sottrazione tra gli argomenti della funzione. Quindi di seguito a tale istruzione, generalmente è presente una istruzione di salto condizionato jz (o je) che indica di saltare ad una determinata locazione di memoria se il bit zero nel registro dei flag risulta impostato, jnz (o jne) che indica di saltare ad una determinata locazione di memoria se il bit zero nel registro dei flag non risulta impostato, ...

Esiste un’istruzione particolare per far eseguire al sistema delle particolari routine dette routine di interrupt. Queste sono definite in particolari strutture di memoria che generalmente fanno parte del kernel. L’istruzione in questione è int. Ad esempio, si pensi di voler visualizzare una scritta sullo schermo: per far ciò in assembly è necessario ricorrere a particolari routine del kernel che permettono l’accesso all’hardware che consente la visualizzazione della scritta.

... esempi ...

[da completare ...]

15.4.5.1 Lo stack

La logica interna della CPU utilizza un mecanismo di memorizzazione delle informazioni nella memoria centrale di tipo pila15. È per questo motivo che una parte della memoria utilizzata dai processi è detta stack16 e viene gestita appunto come una pila.

Il linguaggio assembly mette a disposizione del programmatore apposite istruzioni che permettono la gestione della memorizzazione dei dati nello stack. L’istruzione push inserisce un’informazione nello stack e l’istruzione pop preleva un’informazione dallo stack, secondo il meccanismo LIFO. Tutto questo è possibile grazie allo stack pointer (che contiene sempre l’indirizzo della locazione di memoria nella quale sarà inserito il prossimo dato nello stack) ed al frame pointer (che contiene l’indirizzo della locazione di memoria alla quale inizia la porzione di stack utilizzabile dal contesto corrente).

Lo stack viene utilizzato anche per memorizzare il contesto di esecuzione prima di chiamare una subroutine (o funzione) per mezzo dell’istruzione call. Questa salva il registro dei flag, l’istruction pointer, lo stack pointer ed il frame pointer nello stack, quindi memorizza lo stack pointer nel frame pointer e nell’instruction pointer l’indirizzo a cui inizia la subroutine. La prossima istruzione che verrà eseguita sarà così la prima istruzione della subroutine. Quando, dalla subroutine, sarà eseguita l’istruzione ret verranno ripristinati il registro dei flag, lo stack pointer, il frame pointer e l’instruction pointer con i valori precedentemente salvati nello stack. In questo modo saranno eseguite le istruzioni successive alla call relativa alla subroutine, quindi il flusso del programma procederà dal punto in cui era stato effettuata la chiamata alla subroutine.

[da completare ...]