Istruzioni principali e funzioni
In questa lezione vengono descritte le principali istruzioni assembly e viene mostrato come creare e richiamare una funzione. Affronteremo le istruzioni per inizializzare nuove variabili, effettuare dei confronti tra registri e capiremo meglio come scrivere il nostro prmo programma.
Istruzioni assembly
Di seguito sono riportate e descritte alcune istruzioni assembly; nel nostro corso saranno quelle più utilizzate. Il set completo di istruzioni dei processori della famiglia di processori Intel x86 è facilmente reperibile in rete.
Inizializzazione
Move
mov src, dst
Consente l’inizializzazione di un registro o di un’area di memoria. Accetta alcuni suffissi (come l,w, e b) per indicare la dimensione dell’operando src.
Load Effective Address
lea src, dst
Trasferisce l’indirizzo di memoria dell’operando src nell’operando dst.
Confronti e salti
Compare
cmp op1, op2
Esegue la sottrazione op2 – op1, scarta il risultato ed in base ad esso imposta le flag del registro EFLAGS. Di solito l’istruzione cmp è seguita da istruzioni di salto condizionato che operano in base alle flag impostate da cmp.
Jump if above
ja etichetta
Salta all’istruzione associata a etichetta se l’istruzione cmp op1, op2 ha verificato che op2 è maggiore di op1.
Jump if below
jb etichetta
Salta all’istruzione associata a etichetta se l’istruzione cmp op1, op2 ha verificato che op1 è maggiore di op2.
Jump if equal
je etichetta
Salta all’istruzione associata a etichetta se l’istruzione cmp op1, op2 ha verificato che op1 è uguale a op2.
Jump if not X
jnX etichetta
Salta all’istruzione associata a etichetta se l’istruzione cmp op1, op2 ha verificato che op1 non è X di op2. Il carattere X deve essere sostituito da uno tra a (above), b (below), e (equal), ae (above-equal), be (below-equal).
Jump if CX is zero
jcxz etichetta
Salta all’istruzione associata a etichetta se il registro CX contiene il valore 0.
Jump
jmp etichetta
Salta incondizionatamente alla istruzione associata a etichetta.
Test
test op1, op2 Test.
Eseguito l’AND bit a bit tra gli operandi, scarta il risultato e in base ad esso impostando le flag del registro EFLAGS. A differenza dell’istruzione AND, non memorizza il risultato in modo da poter fare più test in sequenza. L’istruzione test %ax, %ax imposta lo Zero Flag a 1 se e solo se AX è zero.
Loop
loop lbl
Quando il processore incontra questa istruzione, per prima cosa decrementa ECX di 1 e successivamente controlla se ECX è zero; se ECX non è zero allora salta all’istruzione indicata dall’etichetta lbl. Solo ECX si può utilizzare per questa e di conseguenza non è possibile innestare cicli uno dentro l’altro.
Aritmetiche
Increment
inc op
Incrementa di 1 il valore memorizzato in op (op può essere un registro o una locazione di memoria).
Decrement
dec op
Decrementa di 1 il valore memorizzato in op (op può essere un registro o una locazione di memoria)
Somma
add src, dst
Somma a dst il valore di src e memorizza il risultato in dst.
Sottrazione
sub src, dst
Sottrae a dst il valore di src e memorizza il risultato in dst.
Divisione senza segno
div op
Esegue la divisione senza segno. Se l’operando op è un byte il registro AX viene diviso per l’operando, il quoziente viene memorizzato in AL, e il resto in AH. Se l’operando è una word, il valore ottenuto concatenando il contenuto di DX e AX viene diviso per l’operando, i 16 bit più significativi del dividendo devono essere memorizzati nel registro DX, il quoziente viene memorizzato nel registro AX e il resto in DX. La divisione in doppia precisione utilizza EAX ed EDX.
Moltiplicazione senza segno
mul op
Esegue la moltiplicazione senza segno. Se l’operando op è un byte il registro AL viene moltiplicato per l’operando e il risultato viene memorizzato in AH. Se l’operando è una word il contenuto del registro AX viene moltiplicato per l’operando e il risultato viene memorizzato nella coppia di registri DX:AX (DX conterrà i 16 bit più significativi del risultato). La moltiplicazione in doppia precisione utilizza EAX ed EDX.
Logiche
OR esclusivo XOR
xor src, dst
Calcola l’OR esclusivo bit a bit dei due operandi e lo memorizza nell’operando dst.
OR logico
or src, dst
Calcola l’OR bit a bit dei due operandi e memorizza il risultato nell’operando dst.
AND logico
and src, dst
Calcola l’AND bit a bit dei due operandi e lo memorizza nell’operando dst.
NOT logico
not op
Inverte ogni singolo bit dell’operando op.
Gestione dello stack
Push
push src
Memorizza nello stack il valore contenuto nell’operando src.
Pop
pop dest
Estrae il contenuto della locazione di memoria che si trova in cima allo stack e lo memorizza nell’operando dest.
Chiamata a funzione
Call
call op
Memorizza nello stack il valore di ritorno, ovvero l’indirizzo dell’istruzione successiva alla call e trasferisce l’esecuzione alla locazione di memoria referenziata da op (op di solito è un etichetta associata ad una funzione).
Return
ret
Estrae dallo stack il valore di ritorno corrispondente all’indirizzo dell’istruzione da cui riprendere l’esecuzione dopo una chiamata all’istruzione call.
Etichette
Nel linguaggio Assembly non esiste il costrutto IF ... THEN ... ELSE
e quindi le istruzioni di salto servono per far saltare l’esecuzione del programma ad una certa istruzione in funzione del valore di una condizione.
Le uniche condizioni che si possono valutare sono <,=,> tra due valori numerici e la presenza di zero nel registro ECX. In particolare, la valutazione di una condizione di <,=,> consiste di due istruzioni: la prima sottrae tra loro i due valori numerici e imposta i bit SF e ZF del registro EFLAGS, la seconda effettua il salto in base al valore di tali flags.
Le etichette sono essenziali per le istruzioni di salto in quanto indicano a quale punto della sequenza di istruzioni bisogna saltare. Occorre inserire prima dell’istruzione a cui si vuole saltare un nome simbolico seguito dal carattere “:”.
In Assembly non esistono istruzioni ad alto livello per realizzare i cicli come FOR …, WHILE …; essi si devono costruire manualmente a partire dalle istruzioni di salto condizionato. Se si vuole eseguire un ciclo per un certo numero di volte occorre utilizzare ECX come contatore.
Le funzioni
Una funzione in assembly si definisce mediante il seguente costrutto:
nome_funzione: .type @function
istruzione 1
istruzione 2
....
....
istruzione n
ret
La funzione può essere richiamata utilizzando l’istruzione call nome_funzione
.
Gli eventuali parametri della funzione devono essere caricati nei registri della CPU o nello stack. All’interno del corpo della funzione i parametri possono essere recuperati accedendo ai registri o allo stack impostati prima della chiamata alla funzione.