Sei sulla pagina 1di 116

UNIVERSITA` DEGLI STUDI DI PISA

FACOLTA` DI INGEGNERIA
DIPARTIMENTO DI INGEGNERIA DELL'INFORMAZIONE

PAOLO CORSINI

IL CALCOLATORE C86/32
struttura e manuale d'uso
PREFAZIONE

In questo testo vengono presentati il processore P86/32 e la


struttura del calcolatore C86/32, basato su tale processore. Viene
altresì presentato un ambiente di sviluppo sotto Windows, con
servizi di editazione e assemblaggio: un opportuno debugger per-
mette il collaudo di programmi utente.
Il processore P86/32 s’ispira liberamente ai processori della
famiglia INTEL x86 a 32 bit, previo “oscuramento” (oggi di moda)
della struttura a segmenti propria della memoria logica accessibile ai
processori di tale famiglia. L'ambiente di sviluppo software si basa
sull'assemblatore GAS (Gnu Assembler) e GDB (Gnu DeBugger) ed è
scaricabile dal sito:
www2.ing.unipi.it/~a080368/Programmazione/materiale.html
Per avere una visione più completa dei processori a 32 bit della
famiglia INTEL e dell’ambiente di sviluppo, è opportuno far
riferimento a dispense integrative a questo testo, scaricabili anch’esse
dal sito di cui sopra.
INDICE

Capitolo I - Modello funzionale del calcolatore C86/32 e set delle


istruzioni del suo processore, p. 1
1 Blocchi funzionali di un calcolatore, p. 1
2 Modello funzionale del calcolatore C86/32, p. 4
2.1 Lo spazio di memoria, p. 4
2.2. Lo spazio di I/O, p. 6
2.3. Il processore P86/32: i registri, p. 6
2.4 Il processore P86/32: supporto alla gestione della pila, p. 8
2.5 Il processore P86/32: condizioni al reset iniziale, p. 9
3 Il processore P86/32: formato delle istruzioni e modalità di
indirizzamento, p. 9
3.1 Le istruzioni operative, p. 9
3.2 Le istruzioni di controllo, p. 15
4 Il processore P86/32: il set delle istruzioni, p. 17
Capitolo II - Il linguaggio assembler GAS, p. 67
1 Generalità, p. 67
2 Struttura di un programma in linguaggio assembler, p. 68
3 Le costanti in un programma assembler, p. 70
4 Il corpo di una sezione dati: le variabili, p. 71
4.1 Variabili di tipo byte, p. 72
4.2 Variabili di tipo word, p. 72
4.3 Variabili di tipo long, p. 73
4.4 Variabili vettoriali di vario tipo non-inizializzate, p. 73
5 Definizione della pila, p. 74
6 Il corpo di una sezione codice, p. 74
6.1 Come riferire le variabili, p. 75
6.2 Come riferire le istruzioni nelle istruzioni di controllo del
flusso, p. 76
7 Misurazioni di sezioni dati o sezioni codice, p. 77
8 Le direttive .DATA e .TEXT 6, p. 78
9 Esempi di programmi in linguaggio assembler, p. 78
Capitolo III - L’ambiente di sviluppo, p. 85
1 Generalità, p. 85
2 Il debugger GDB, p. 86
3 I sottoprogrammi di utilità, p. 88
Appendice, p. 109
Codifica ASCII dei Caratteri, pp. 109-110
MODELLO FUNZIONALE DEL CALCOLATORE C86/32
E SET DELLE ISTRUZIONI DEL SUO PROCESSORE

1 BLOCCHI FUNZIONALI DI UN CALCOLATORE

I calcolatori sono macchine complesse, la cui struttura fisica


(hardware) è costituita da diversi moduli, ognuno dei quali è in grado
di eseguire un insieme specifico di funzioni. Lo schema a blocchi di
un tipico calcolatore è riportato in Fig. 1.

RETE di INTERCONNESSIONE

INTERFACCE
MEMORIA
PROCESSORE
PRINCIPALE
DISPOSITIVI

SOTTOSISTEMA
di
INGRESSO/USCITA

Fig. 1 - Schema a blocchi di un semplice calcolatore.

Il sottosistema di ingresso riceve le informazioni provenienti dal


mondo esterno sotto forma di movimenti di organi meccanici, di
variazioni di tensioni, di suoni, di immagini, ecc. e le trasforma
(codifica) in sequenze di simboli binari o bit (BInary digiT)1. Sono da
considerarsi informazioni di ingresso sia le liste di istruzioni (o

1 In accordo con la più consolidata delle tradizioni, anche in questo testo i bit
saranno indicati con i simboli 0 e 1
2 CAPITOLO I

programmi) che codificano gli algoritmi di elaborazione, sia i dati da


elaborare.
Il sottosistema di uscita restituisce al mondo esterno le informazio-
ni elaborate, operando trasformazioni di sequenze di bit in movi-
menti di organi meccanici, in variazioni di tensione, in suoni, in im-
magini, ecc.
All’interno del sottosistema di ingresso/uscita, le interfacce hanno
lo scopo di gestire i vari dispositivi (in genere elettromeccanici), in
modo da farli apparire al processore come entità più semplici e più
standard. Le più comuni interfacce gestiscono i monitor che,
unitamente alle tastiere e ai mouse, sono i più diffusi dispositivi di
ingresso/uscita; altre tipiche interfacce gestiscono le memorie di massa
(generalmente dischi magnetici), che hanno lo scopo di immagaz-
zinare dati e programmi attualmente non interessati all’esecuzione.
La memoria principale contiene, durante un’elaborazione, sia una
copia del programma sia una parte dei dati da elaborare (altri dati
possono risiedere nel sottosistema interfacce-dispositivi).
Il processore provvede a prelevare dalla memoria le istruzioni
(una alla volta) e a eseguirle (cioè a compiere le operazioni da esse
specificate). La legge di evoluzione nel tempo di un processore è in
linea di principio, piuttosto semplice:
“Prelevare ed eseguire le istruzioni nell’ordine in cui sono
memorizzate nella memoria principale, fino a quando non viene
incontrata un’istruzione che stabilisce esplicitamente un numero d’ordine
diverso da quello sequenziale per la prossima istruzione da prelevare ed
eseguire; a partire da tale istruzione proseguire con le stesse modalità e
così via all’infinito”.
Le istruzioni si dividono pertanto in due grosse classi: istruzioni
operative ed istruzioni di controllo.
Le istruzioni operative specificano operazioni da compiere sui
dati, tipicamente operazioni aritmetiche semplici (addizione, sottra-
zione, ecc.) oppure operazioni logiche elementari (and, or, not, ecc.).
Le istruzioni di controllo specificano un possibile numero
d’ordine diverso da quello sequenziale per la prossima istruzione da
eseguire; diremo anche che le istruzioni di controllo permettono di
alterare il flusso sequenziale di esecuzione.
La legge di evoluzione del processore implica che un calcolatore,
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 3

per poter funzionare correttamente, deve essere posto in una condi-


zione iniziale in cui la memoria principale contenga un primo pro-
gramma (detto bootstrap) ed il processore conosca il numero d’ordine
della prima istruzione di tale programma. Questa condizione, detta
di reset, è soddisfatta progettando il processore in modo che esso
stesso autoinizializzi alcuni registri interni con l’indirizzo di una ben
precisa porzione della memoria principale ed implementando tale
porzione di memoria con circuiti indelebili di tipo ROM, in cui sia
già stato cablato il programma bootstrap.
In questo capitolo vengono presentati un semplice processore,
denotato P86/32, ed un semplice calcolatore, denotato C86/32,
basato su detto processore. Il processore s’ispira ai processori della
famiglia della INTEL a 32 bit, previo ”oscuramento” della struttura a
segmenti della memoria logica. Esso è in grado di elaborare dati su 8
bit (byte), su 16 bit (word), su 32 bit (long) ed in alcuni casi su 64 bit
(quadword); nell’eseguire le istruzioni aritmetiche, il processore
interpreta i dati come numeri naturali oppure come numeri interi: la
base di lavoro è la base 2 e la rappresentazione per i numeri interi è il
complemento a due.
Il livello a cui viene presentato il processore non è direttamente il
linguaggio macchina (che essendo composto da sequenze di bit, risulta
sostanzialmente incomprensibile), ma un linguaggio mnemonico, che
pur essendo in corrispondenza uno ad uno con il linguaggio
macchina, ha il pregio di una maggiore leggibilità.
Sempre per comodità e leggibilità, sia i dati sia gli indirizzi sono
espressi in questo testo in forma compatta, cioè in notazione
esadecimale: a evidenziare ciò, essi iniziano con la classica coppia 0x (*)

(*) Esprimere una sequenza di bit in notazione esadecimale significa raggruppare


i bit 4 a 4 a partire da destra e sostituire ogni quadrupla di bit con uno dei
simboli 0, 1,...,9, A, B, C, D, E, F (detti anche cifre esadecimali), in accordo alla
seguente Tabella
Quadr. Simb. Quadr. Simb. Quadr. Simb. Quadr. Simb.
0000 0 0100 4 1000 8 1100 C
0001 1 0101 5 1001 9 1101 D
0010 2 0110 6 1010 A 1110 E
0011 3 0111 7 1011 B 1111 F
4 CAPITOLO I

2 MODELLO FUNZIONALE DEL CALCOLATORE C86/32

Il calcolatore C86/32 è un calcolatore basato sul processore


P86/32 e simulato sotto Windows. Il suo modello funzionale è illu-
strato in Fig. 2, dove sono evidenziati, oltre ai registri del processore,
anche lo spazio di memoria e lo spazio di ingresso/uscita (o spazio di I/O)
a cui il processore è in grado di accedere.

2.1 LO SPAZIO DI MEMORIA

Lo spazio di memoria è composto da una sequenza lineare e


contigua di locazioni: ogni locazione ha una capacità pari a un byte ed
è individuabile mediante un numero naturale a 32 bit detto indirizzo
(o, in accordo a una terminologia di derivazione Intel, indirizzo).
Il processore può accedere a una singola locazione, a due locazio-
ni consecutive (doppia locazione) o a quattro locazioni consecutive
(quadrupla locazione). Nel primo caso utilizza l’indirizzo della locazio-
ne stessa. Negli altri casi l’indirizzo della locazione di indirizzo più
piccolo (tale indirizzo verrà detto, rispettivamente, indirizzo della
doppia o della quadrupla locazione). Se una doppia o quadrupla
locazione contiene un numero, la locazione di indirizzo più piccolo
contiene gli 8 bit meno significativi del numero e così via fino alla lo-
cazione di indirizzo più grande che contiene gli 8 bit più significativi.
All’interno di una locazione il bit più significativo fra gli 8 che
essa contiene è quello che si trova all’estremità sinistra (bit numero
7), mentre il meno significativo è quello che si trova all’estremità
destra (bit numero 0).
Il processore accede allo spazio di memoria durante la fase di
prelievo delle istruzioni e durante la fase di esecuzione di molte
delle istruzioni operative (per procurarsi i dati o per memorizzare
risultati). Tutte le istruzioni che riferiscono un dato in memoria,
devono quindi specificare (esplicitamente o implicitamente) un
indirizzo completo a 32 bit.
Nel calcolatore C86/32, come in tutti i calcolatori, non tutto lo
spazio di memoria è implementato. La porzione implementata è
supporta tata da memorie RAM, tranne le 64K locazioni di indirizzo
più alto, che sono supportate da circuiti integrati di tipo ROM.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 5

0x00000000
0x00000001
0x00000002
0x00000003

0x0000
0x0001

0xFFFE
0xFFFF
0xFFFFFFFE
0xFFFFFFFF

Spazio di Memoria Spazio di I/O

AX
EAX EIP
AH AL
BX
EBX EF
BH BL
Registri di Stato
CX
ECX
CH CL
DX
EDX
DH DL
BP
EBP

SI
ESI

DI
EDI

SP
ESP
Registri Generali
Processore

Fig. 2 - Modello funzionale del calcolatore C86/32.


6 CAPITOLO I

2.2 LO SPAZIO DI I/O

Lo spazio di spazio di I/O (Input/Output) è uno spazio lineare


costituito da 64 K locazioni o porte. Ogni porta ha una capacità pari a
un byte ed è indirizzabile attraverso un indirizzo a 16 bit.
Il processore accede allo spazio di I/O durante l’esecuzione delle
istruzioni di ingresso (per leggere il contenuto delle porte) e di uscita
(per scrivere un nuovo contenuto nelle porte). Anche in questo caso il
processore è in grado di accedere a coppie o a quadruple di porte
contigue. Lo spazio di I/O è implementato con interfacce connesse a
dei dispositivi esterni.
L’esecuzione di un’istruzione di ingresso consente quindi al pro-
cessore di prelevare uno o più byte che potranno essere, a seconda
dei casi, un nuovo dato fornito dal dispositive o un’informazione
sullo stato dell’interfaccia o del dispositive.
L’esecuzione di un’istruzione di uscita consente invece al proces-
sore di emettere uno o più byte che potranno essere, a seconda dei
casi, un dato per il dispositive o un’informazione di comando per
l’interfaccia o per il dispositive.

2.3 IL PROCESSORE P86/32: I REGISTRI

Il processore P86/32possiede due insiemi di registri: i registri


generali ed i registri di stato. I registri sono individuati, entro il
processore, tramite indirizzi; a livello di linguaggio mnemonico a
ciascuno di essi è assegnato un acronimo, che noi useremo sempre in
questo testo.
Registri Generali. I registri generali sono utilizzati per memoriz-
zare dati oppure per contenere numeri naturali che opportunamente
combinati producono indirizzo di dati.
I registri generali sono 8, vengono denominati EAX, EBX, ECX,
EDX, EBP, ESI, EDI e ESP ed hanno una capacità di 32 bit. Le 8 parti
basse di ciascuno di essi possono essere viste come 8 registri a 16 bit
denominati, rispettivamente, AX, BX, CX, DX, BP, SI, DI e SP. I 4
registri AX, BX, CX e DX possono essere a loro volta divisi, ciascuno,
in una parte alta (H) e in una parte bassa (L) e possono quindi essere
utilizzati come 8 registri da 8 bit, denominati rispettivamente AH,
AL, BH, BL, CH, CL, DH e DL.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 7

Nei processori moderni, la maggior parte delle istruzioni che


usano i registri generali possono riferirne uno qualunque. Nondi-
meno, alcuni registri mantengono – per motivi storici – dei nomi che,
nelle vecchie architetture, identificavano la funzione cui essi erano
esclusivamente o preferenzialmente dedicati. Più precisamente si ha:
• Il registro EAX (oppure AX, AH od AL) è utilizzato da alcune
istruzioni come accumulatore (e con questo nome viene riferito);
• I registri ESI, EDI, EBX e EBP sono spesso utilizzati come registri
puntatore, i registri ESI e EDI come registri indice e i registri EBX e
EBP come registri base;
• Il registro ESP è utilizzato esclusivamente per indirizzare la pila
(vedi il sottoparagrafo 2.4)

Registri di Stato. Questo gruppo di registri comprende il registro


EIP ed il registro EF.
Il registro EIP (Instruction Pointer register) contiene l’indirizzo
della locazione, a partire dalla quale sarà prelevata la prossima istru-
zione da eseguire. Il contenuto del registro EIP, che è fissato al reset
iniziale, viene automaticamente ed opportunamente incrementato
durante l’esecuzione di ogni istruzione in modo che sia assicurato il
flusso sequenziale di esecuzione. Esso può anche essere dina-
micamente cambiato durante l’esecuzione di alcune istruzioni di
controllo, con conseguente trasferimento del flusso di esecuzione da
una porzione a un’altra del programma.
Il registro EF (Extended Flag register) ha 32 elementi detti flag. Noi
ne considereremo solo quattro (vedi la Fig. 3), tutti appartenenti alla
parte bassa di EF, che è detta registro F (Flag register).
15 11 7 6 0

OF SF ZF CF
Fig. 3 - Il registro dei flag F.
La funzione di questi quattro flag è di norma la seguente:
OF (Overflow Flag): quando contiene 1 indica che durante l’esecuzio-
ne dell’ultima istruzione si è avuto un traboccamento: se l’istruzione
operava su numeri interi, questa situazione indica che il risultato
dell’operazione realizzata dall’istruzione non è rappresentabile.
8 CAPITOLO I

SF ((Sign Flag): quando contiene 1 indica che l’ultima istruzione


eseguita ha generato un risultato con il bit più significativo uguale a
1: se l’istruzione operava su numeri interi, questa situazione indica
che il risultato è un numero negativo.
ZF (Zero Flag): quando contiene 1 indica che l’ultima istruzione
eseguita ha generato un risultato con tutti i bit a 0.
CF (Carry Flag): quando contiene 1 indica che durante l’esecuzione
dell’ultima istruzione si è generato un riporto o si è richiesto un
prestito: se l’istruzione operava su numeri naturali, questa situazione
indica che il risultato dell’operazione aritmetica realizzata
dall’istruzione non è rappresentabile.

2.4 IL PROCESSORE P86/32: SUPPORTO ALLA GESTIONE DELLA PILA

Per motivi che risulteranno più chiari in seguito (chiamata e ri-


torno da sottoprogramma, salvataggio e ripristino di contenuti di
registri, ecc.) è molto utile disporre di una pila (o stack), cioè di una
porzione di memoria in cui immettere e prelevare informazioni in
accordo alla disciplina LIFO (Last In First Out): l’informazione che
viene immessa per ultima è quella che viene prelevata per prima.
La pila viene realizzata utilizzando più locazioni contigue di una
porzione di memoria, che vengono riempite a partire da quella di
indirizzo maggiore (locazione di fondo)
Il registro ESP (Stack Pointer register) del processore contiene ad
ogni istante l’indirizzo del top della pila, cioè l’indirizzo della
locazione riempita per ultima. Più precisamente:
1) il registro ESP va inizializzato con l’indirizzo della locazione
immediatamente consecutiva a quella di fondo;
2) l’immissione di un’informazione nella pila (operazione push) va
effettuata decrementando prima l’indirizzo contenuto nel registro
ESP ed utilizzando poi tale indirizzo come indirizzo per un’ope-
razione di scrittura in memoria;
3) il prelievo di un’informazione della pila (operazione pop) va
effettuato utilizzando prima il contenuto del registro ESP come
indirizzo per un’operazione di lettura dalla memoria e poi
incrementando tale indirizzo.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 9

Il processore P86/32 prevede delle istruzioni per meccanizzare le


operazioni di push e di pop. La loro esecuzione permette di trasferi-
sce nella/dalla pila una word o un long (mai un byte), cosicché il de-
cremento o l’incremento dell’indirizzo contenuto in ESP è di 2 o di 4.

2.5 IL PROCESSORE P86/32: CONDIZIONI AL RESET

Al reset il processore inizializza a zero i quattro flag CF, ZF, SF e


OF e a 0xFFFF0000 il contenuto del registro EIP. Questa seconda
inizializzazione implica che l’istruzione che il processore esegue per
prima dopo il reset, è quella memorizzata a partire dalla locazione di
indirizzo 0xFFFF0000. Tale locazione e le successive sono fra quelle
che debbono contenere indelebilmente il programma di bootstrap.

3 IL PROCESSORE P86/32: FORMATO DELLE ISTRUZIONI E MODALITÀ


DI INDIRIZZAMENTO

Il processore P86/32 appartiene alla classe dei processori CISC


(Complex Instruction Set) e possiede un ricco insieme di istruzioni,
ripartibili in istruzioni operative e istruzioni di controllo.
Per brevità di esposizione, da ora in poi diremo che un’istruzione
o un programma compie una certa azione, intendendo che tale
azione è compiuta dal processore durante l’esecuzione di quell’istru-
zione o di quel programma. Parleremo inoltre di “contenuto” di un
registro o di una o più locazioni di memoria, senza necessariamente
esplicitare che cosa questo contenuto rappresenti.

3.1 LE ISTRUZIONI OPERATIVE

Le più comuni istruzioni operative compiono azioni su due dati


(che, da ora in poi, chiameremo operandi) e la loro esecuzione com-
porta la sostituzione di uno di tali operandi con il risultato della
azione effettuata: l’operando che alla fine viene sostituito è detto
operando destinatario, l’altro operando (che rimane inalterato) è detto
operando sorgente. A livello di linguaggio mnemonico, un’istruzione a
due operandi ha un formato a quattro campi del tipo:
OPCODEsize source,destination
Il campo OPCODE, detto il codice operativo dell’istruzione, speci-
fica il tipo di azione che essa effettua.
10 CAPITOLO I

L’appendice size specifica la lunghezza degli operandi e può


essere omessa ogniqualvolta la lunghezza degli operandi è
implicitamente deducibile da altri campi dell’istruzione. Quando
non è omessa, tale appendice è costituita dalla lettera B ad indicare
operandi a 8 bit, dalla lettera W ad indicare operandi a 16 bit o dalla L
ad indicare operandi a 32 bit.
I campi source e destination specificano, in accordo ad alcune
modalità, note come modalità di indirizzamento, gli indirizzi
dell’operando sorgente e dell’operando destinatario. In un solo caso,
noto come indirizzamento immediato per l’operando sorgente, il
campo source specifica l’operando stesso.
Alcune istruzioni a due operandi non prevedono il campo
destination e/o il campo source in quanto l’operando destinatario
e/o l’operando sorgente sono impliciti.
Altre istruzioni compiono azioni su un solo operando e la loro
esecuzione comporta la sostituzione di tale operando con il risultato
dell’azione effettuata: l’operando è detto operando destinatario. A
livello di linguaggio mnemonico, un’istruzione a un operando ha un
formato a tre campi del tipo:
OPCODEsize destination
Di seguito vengono illustrate le varie modalità di indirizzamento
permesse dal processore P86/32. Implicitamente (ed informalmente)
viene anche introdotto il linguaggio mnemonico. Per quanto
concerne il linguaggio macchina, si rimanda ai manuali dei
processori Intel.

3.1.1 INDIRIZZAMENTO DI REGISTRO.

Questa modalità di indirizzamento prevede che un campo


dell’istruzione specifichi l’indirizzo di uno degli 8 registri generali a
32 bit (EAX, EBX, ECX, EDX, EBP, ESI, EDI e ESP), o di uno degli 8
registri generali a 16 bit (AX, BX, CX, DX, SI, DI, SP, BP) o di uno
degli 8 registri generali a 8 bit (AH, BH, CH, DH, AL, BL, CL, DL)
oppure. Nelle istruzioni a due operandi è possibile che sia previsto,
per entrambi gli operandi, l’indirizzamento di registro.
L’indirizzo del registro, scomodo da usarsi, viene sostituito, an-
che a livello dei linguaggi mnemonici, dal nome del registro stesso.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 11

Per cominciare a prendere confidenza con la (pessima) sintassi del


linguaggio assembler che useremo in seguito, premetteremo, nelle
istruzioni, l’assurdo simbolo % al nome di ciascun registro.
I seguenti esempi chiariscono fino in fondo come la modalità di
indirizzamento di registro viene indicata a livello di linguaggio
mnemonico (e, in seguito, di linguaggio assembler):
OPCODE %DI
l’operando è a 16 bit ed è contenuto nel registro DI.
OPCODE %EAX,%EBX
gli operandi sono a 32 bit; l’operando sorgente è contenuto nel registro
EAX e l’operando destinatario nel registro EBX.
OPCODE %AH,%CL
gli operandi sono a 8 bit; l’operando sorgente è contenuto nel registro
AH e l’operando destinatario nel registro CL.

3.1.2 INDIRIZZAMENTO IMMEDIATO.

Questa modalità di indirizzamento prevede che l’operando sia


parte dell’istruzione, nel senso che esso coincida con il campo
source dell’istruzione stessa2. I seguenti esempi chiariscono come
questa modalità di indirizzamento viene indicata a livello di
linguaggio mnemonico (si noti che l’operando è preceduto dal
simbolo $):
OPCODE $0x20,%AL
gli operandi sono a 8 bit; l’operando sorgente (individuato mediante
l’indirizzamento immediato) vale 0x20; l’operando destinatario
(individuato mediante l’indirizzamento di registro) è contenuto in AL.
OPCODE $0x5683A20B,%ECX
gli operandi sono a 32 bit; l’operando sorgente (individuato mediante
l’indirizzamento immediato) vale 0x5683A20B; l’operando destinatario
(individuato mediante l’indirizzamento di registro) è contenuto in ECX.

La modalità di indirizzamento immediato, comunemente usata


quando l’operando è una costante, è permessa esclusivamente per
l’operando sorgente; se fosse permessa per l’operando destinatario,

2 Poiché le istruzioni stanno, all’atto della loro esecuzione, in memoria, anche


l’operando sta in memoria.
12 CAPITOLO I

l’istruzione verrebbe modificata ogni volta che viene eseguita, e que-


sto è contrario a ogni elementare regola di buona programmazione.

3.1.3 INDIRIZZAMENTI DI MEMORIA.

Nella sua forma più generale, l’indirizzo di un operando in


memoria viene ottenuto combinando tre numeri naturali a 32 bit,
detti rispettivamente base, indice, e displacement, ed un fattore di scala,
in accordo alla seguente relazione:
indirizzo =|base + (indice * scala) ± displacement|modulo232
Il fattore di scala è un numero che vale 1, 2, 4 o 8, e moltiplica il
contenuto registro indice (e soltanto quello).
La base e l’indice sono contenuti in due registri generali
qualunque (con l’eccezione di ESP, che non può essere indice). Per
motivi storici, spesso si usa come base uno dei registri EBX o EBP, e
come indice ESI o EDI. Il displacement e il fattore di scala sono parte
dell’istruzione stessa; vale inoltre la regola secondo cui omettere il
fattore di scala, equivale a fissarlo pari a 1.
Questa flessibilità di specificare l’indirizzo presenta vantaggi
nell’indirizzare in memoria operandi organizzati in strutture dati
complesse. Ovviamente, in un’istruzione, le componenti predette
possono essere utilizzate tutte o in parte, con conseguente possibilità
di avere più tipi di indirizzamento. La sintassi generale è la seguente:
OPCODE displacement(base,indice,scala)
A seconda di quali componenti vengano specificate, si hanno tipi
diversi di indirizzamento di memoria.
Indirizzamento diretto: l’indirizzo dell’operando, che coincide con il
campo source e/o con il campo destination dell’istruzione stessa,
è specificato dal solo displacement. Il seguente esempio chiarisce
come questa modalità di indirizzamento, usata in pratica quando
l’operando è uno scalare a 8, 16 o 32 bit, viene indicata a livello di
linguaggio mnemonico (si noti che l’indirizzo dell’operando non è
preceduto da alcun simbolo):
OPCODEW 0x00002001
l’operando è a 16 bit e si trova nella doppia locazione di indirizzo
0x00002001.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 13

Indirizzamento indiretto (detto anche indirizzamento con registro


puntatore): l’indirizzo dell’operando è il contenuto di un registro
generale e nell’istruzione è specificato di quale di questi registri
trattasi. I seguenti esempi chiarisce come questa modalità di indiriz-
zamento viene indicata a livello di linguaggio mnemonico (si noti
come il nome del registro sia racchiuso tra parentesi tonde):
OPCODEL (%EBX)
l’operando è a 32 bit e si trova nella quadrupla locazione il cui
indirizzo è contenuto nel registro puntatore EBX.
OPCODEW (%EBP)
l’operando è a 16 bit e si trova nella doppia locazione il cui indirizzo è
il contenuto del registro puntatore EBP.
La modalità di indirizzamento indiretto, comunemente usata
quando l’operando è la componente di un vettore, implica che
un’apposita istruzione, precedente a quella che lo usa, definisca
opportunamente il contenuto del registro puntatore utilizzato.
Indirizzamento con displacement e registro di modifica (detto anche
indirizzamento con registro indice): sono utilizzati il displacement e il
contenuto di un registro generale (eventualmente moltiplicato per
un fattore di scala), che sommati danno l’indirizzo dell’operando. Il
seguente esempio chiarisce come questa modalità di indirizzamento
viene indicata a livello di linguaggio mnemonico:
OPCODEB 0x002A3A2B(%ESI)
l’operando è a 8 bit e si trova nella locazione il cui indirizzo viene
ottenuto sommando, modulo 232, il numero naturale 0x002A3A2B con
il contenuto del registro ESI.
OPCODEW 0x002A3A2B(,%ESI,2)
# qualora si specifichi il fattore di scala, occorre
# inserire la virgola prima del nome del registro
l’operando è a 16 bit e si trova nella doppia locazione il cui indirizzo
viene ottenuto sommando, modulo 232, il numero naturale
0x002A3A2B con il contenuto del registro ESI moltiplicato per due.
La modalità di indirizzamento con registro indice, viene, al pari della
modalità di indirizzamento con registro puntatore, usata quando
l’operando è la componente di un vettore. Essa implica che
un’apposita istruzione, precedente a quella che lo usa, inizializzi
opportunamente il contenuto del registro indice utilizzato.
14 CAPITOLO I

Altri casi possibili sono:


a) Indirizzamento bimodificato senza displacement: sono utilizzati il
contenuto di un registro base e di un registro indice
(eventualmente moltiplicato per un fattore di scala), che
sommati danno l’indirizzo dell’operando.
b) Indirizzamento bimodificato con displacement: sono utilizzate tutte
e tre le componenti, e cioè il contenuto di un registro indice
(eventualmente moltiplicato per un fattore di scala), il
contenuto di un registro base e il displacement, che sommate
danno l’indirizzo dell’operando (in realtà il displacement può
essere anche sottratto).
I seguenti esempi chiariscono come queste due ulteriori modalità
di indirizzamento sono indicate a livello di linguaggio mnemonico:
OPCODEL (%EBX,%EDI,4)
l’operando è a 32 bit e si trova nella quadrupla locazione il cui
indirizzo si ottiene sommando, modulo 232, la base contenuta in EBX
con il quadruplo dell’indice contenuto in EDI (indirizzamento
bimodificato senza displacement).
OPCODEB 0x002F9000(%EBX,%EDI)
l’operando è a 8 bit e si trova nella locazione il cui indirizzo vale: |base
in EBX + indice in EDI + 0x002F9000|modulo232 (indirizzamento
bimodificato con displacement).
OPCODEB -0x9000(%EBX,%EDI)
l’operando è a 8 bit e si trova nella locazione il cui indirizzo vale: |base
in EBX + indice in EDI – 0x00009000|modulo232 = |base in EBX +
indice in EDI + 0xFFFF7000| modulo232 (indirizzamento bimodi-
ficato con displacement).

3.1.4 INDIRIZZAMENTO DELLE PORTE DI I/O.

L’indirizzamento diretto e quello indiretto tramite registro


puntatore possono essere utilizzati anche per specificare operandi
contenuti nelle porte dello spazio di I/O. Tuttavia, l’indirizzamento
diretto è possibile solo se la porta da riferire ha indirizzo inferiore a
256, mentre l’indirizzamento indiretto è possibile solo utilizzando
DX come registro puntatore.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 15

I seguenti esempi chiariscono come le modalità di indirizzamento


delle porte vengono indicate a livello di linguaggio mnemonico (IN
ed OUT sono i codici operativi delle istruzioni per accedere alle porte
per operazioni, rispettivamente, di lettura e di scrittura):
IN 0x001A,%AL
gli operandi sono a 8 bit; l’operando destinatario è contenuto in AL;
l’operando sorgente si trova nella porta di indirizzo 0x001A.
IN (%DX),%AX,
gli operandi sono a 16 bit; l’operando destinatario è contenuto in AX;
l’operando sorgente si trova nella doppia porta il cui indirizzo è
contenuto in DX.
OUT %AL,0x003A
gli operandi sono a 8 bit; l’operando destinatario si trova nella porta di
indirizzo 0x003A; l’operando sorgente è contenuto in AL
OUT %AL,(%DX)
gli operandi sono a 8 bit; l’operando destinatario si trova nella porta il
cui indirizzo è contenuto in DX; l’operando sorgente è contenuto in AL.

Per motivi incomprensibili, la sintassi ufficiale prevede che


l’indirizzo di una porta di I/O sia preceduto dal simbolo $. Ad
esempio, la prima delle precedenti istruzioni andrebbe scritta come
IN $0x1A,%AL

3.2 LE ISTRUZIONI DI CONTROLLO

Questa categoria di istruzioni include tutte quelle la cui esecu-


zione altera (eventualmente sotto condizione) il flusso sequenziale di
esecuzione. Ognuna di tali istruzioni deve pertanto specificare un
indirizzo di salto, cioè un nuovo indirizzo da immettere,
eventualmente sotto condizione, nel registro EIP.
Le istruzioni di controllo rientrano in almeno uno dei seguenti
formati:
OPCODE %EIP ± displacement
OPCODE *extended_register
OPCODE *memory
OPCODE
16 CAPITOLO I

Nel primo formato (indirizzamento relativo) l’indirizzo di salto è


ottenuto aggiungendo o sottraendo, modulo 232, il displacement
all’indirizzo contenuto in EIP. Nel secondo formato l’indirizzo di
salto è contenuto nel registro specificato nell’istruzione. Nel terzo
formato l’indirizzo di salto è contenuto nella quadrupla locazione
individuata dal campo memory (secondo le modalità di indirizza-
mento viste per gli operandi in memoria delle istruzioni operative).
Nel quarto ed ultimo formato l’indirizzo di salto sta nella pila.
Le istruzioni di controllo si dividono in due categorie: istruzioni di
salto (istruzioni con codice operativo JMP, Jcon, ecc.) e istruzioni per la
gestione dei sottoprogrammi (istruzioni con codice operativo CALL e
RET).
Le istruzioni di salto sono utilizzate per effettuare una scelta fra
due flussi sequenziali di esecuzione indipendenti e quindi non
forniscono alcun supporto per rintracciare l’istruzione testualmente
successiva a quella di salto.
Le istruzioni per la gestione dei sottoprogrammi sono utilizzate
per effettuare un distacco dal flusso sequenziale di esecuzione in atto
(flusso principale), con il vincolo che il nuovo flusso di esecuzione
deve tornare in seguito a reinnestarsi nel flusso principale, a partire
dall’istruzione successiva (istruzione di rientro) a quella che ha provo-
cato il distacco. Le istruzioni per la gestione dei sottoprogrammi
debbono quindi prevedere meccanismi per la manipolazione dell’in-
dirizzo dell’istruzione di rientro. Più precisamente, le istruzioni con
codice operativo CALL salvano nella pila l’indirizzo dell’istruzione di
rientro, mentre quelle con codice operativo RET rintracciano nella
pila l’indirizzo dell’istruzione di rientro e lo utilizzano come indi-
rizzo di salto.
Il tutto permette di predisporre pacchetti di istruzioni, noti come
sottoprogrammi, che prevedono, come ultima istruzione, l’istruzione
RET e che vengono a godere della seguente proprietà: una volta che
un sottoprogramma è stato chiamato, cioè una volta che la prima
delle sue istruzioni è stata messa in esecuzione tramite un’istruzione
CALL appartenente al programma principale, il sottoprogramma stesso
espleta prima le azioni previste dalle varie istruzioni che lo costitui-
scono e poi ritorna, cioè rimette in esecuzione, grazie all’istruzione
RET finale, l’istruzione di rientro del programma principale.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 17

I sottoprogrammi possono essere utilizzati più volte nel corso


dell’esecuzione del programma principale per portare avanti uno
stesso tipo di elaborazione su operandi diversi o per suddividere
logicamente un programma complesso in un programma principale
ed in tanti sottoprogrammi, uno per ogni tipo di elaborazione con-
cettualmente indipendente.
Le convenzioni da rispettare affinché un sottoprogramma operi
correttamente sono note come passaggio dei parametri.
Un esempio di un passaggio di parametri è il seguente: il
programma principale deve lasciare gli operandi da elaborare nei
registri AX e BX ed il sottoprogramma lascerà a sua volta il risultato
dell’elaborazione nel registro DX.
Grazie all’uso della pila per la memorizzazione e il prelievo del-
l’indirizzo dell’istruzione di rientro, è possibile la nidificazione di
sottoprogrammi, cioè è possibile che un sottoprogramma ne chiami
un altro e cosi via (compatibilmente con le dimensioni della pila).
È previsto un meccanismo alternativo mediante il quale può
essere messo in esecuzione un sottoprogramma: tale meccanismo è
noto come meccanismo di interruzione (software, interna od esterna)
ed i sottoprogrammi sono identificati mediante un numero d’ordine
che va da 0 a 255. Per una trattazione completa del meccanismo
dell’interruzione si rimanda, ad esempio, al testo “ P. Corsini: Dalle
Porte AND OR NOT al Sistema Calcolatore, Edizioni ETS, Pisa”.

4 IL PROCESSORE P86/32: IL SET DELLE ISTRUZIONI

Il processore P86/32 è dotato di un set di istruzioni molto ampio


e significativo. Di seguito per ogni istruzione sono riportati il nome,
il formato con il codice operativo, la descrizione delle azioni che il
processore compie durante la sua esecuzione, l’elenco dei flag di cui
viene modificato il contenuto, le possibili localizzazioni per gli ope-
randi.
18 CAPITOLO I

MOVE

FORMATO: MOV source, destination

AZIONE: Sostituisce l’operando destinatario con una copia dell’ope-


rando sorgente.

FLAG di cui viene modificato il contenuto: Nessuno.

Operandi Esempi
Memoria, Registro Generale MOV 0x00002000,%EDX
Registro Generale, Memoria MOV %CL,0x12AB1024
Registro Generale, Registro Generale MOV %AX,%DX
Immediato, Memoria MOVB $0x5B,(%EDI)
Immediato, Registro Generale MOV $0x54A3,%AX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 19

LEA

FORMATO: LEA source, destination

AZIONE: Calcola l’indirizzo dell’operando sorgente e sostituisce


l’operando destinatario con tale indirizzo. Questa è la sola istruzione
che non opera sull’operando sorgente, ma sul suo indirizzo.

FLAG di cui viene modificato il contenuto: Nessuno.

Operandi Esempi
Memoria, Registro Generale a 32 bit LEA (,%ESI,2),%EDX
20 CAPITOLO I

PUSH

FORMATO: PUSH source

AZIONE: Salva nella pila corrente una copia dell’operando sorgente


(che deve essere a 16 o a 32 bit). Più in dettaglio, compie le seguenti
azioni: i) decrementa l’indirizzo contenuto nel registro ESP di due o
di quattro; ii) memorizza una copia dell’operando sorgente nella
doppia o quadrupla locazione il cui indirizzo è contenuto in ESP.

FLAG di cui viene modificato il contenuto: Nessuno.

Operandi Esempi
Memoria PUSHW 0x3214200A
Immediato PUSHL $0x4871A000
Registro Generale PUSH %BX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 21

PUSHF

FORMATO: PUSHF

AZIONE: Salva nella pila corrente una copia del contenuto del
registro dei flag F.

FLAG di cui viene modificato il contenuto: Nessuno.


22 CAPITOLO I

PUSHA

FORMATO: PUSHA

AZIONE: Salva nella pila corrente una copia del contenuto degli 8
registri generali a 16 bit, rispettando il seguente ordine: AX, CX, DX,
BX, SP, BP, SI, DI.

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 23

PUSHAD

FORMATO: PUSHAD

AZIONE: Salva nella pila corrente una copia del contenuto degli 8
registri generali a 32 bit, rispettando il seguente ordine: EAX, ECX,
EDX, EBX, ESP, EBP, ESI, EDI.

FLAG di cui viene modificato il contenuto: Nessuno.


24 CAPITOLO I

POP

FORMATO: POP destination

AZIONE: Rimuove dalla pila corrente una word o un long e la


sostituisce all’operando destinatario. Più in dettaglio, compie le
seguenti azioni: i) sostituisce all’operando destinatario una copia del
contenuto della doppia o della quadrupla locazione il cui indirizzo è
contenuto in ESP, ii) incrementa di due o di quattro l’indirizzo
contenuto in ESP, rimuovendo in tal modo dalla pila la word o il
long copiato.

FLAG di cui viene modificato il contenuto: Nessuno.

Operandi Esempi
Memoria POPW 0x02AB2000
Registro Generale POP %BX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 25

POPF

FORMATO: POPF

AZIONE: Rimuove dalla pila corrente una word e con i bit n. 11, 7, 6
e 0 di essa, rinnova il contenuto dei corrispondenti flag del registro
F.

FLAG di cui viene modificato il contenuto: Tutti.


26 CAPITOLO I

POPA

FORMATO: POPA

AZIONE: Rimuove dalla pila 8 word, scarta la quarta word estratta e


con le 7 word rimanenti rinnova il contenuto di 7 degli 8 registri
generali a 16 bit, rispettando il seguente ordine: DI, SI, BP, BX, DX,
CX, AX. Il registro il cui contenuto non viene modificato è il registro
SP

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 27

POPAD

FORMATO: POPAD

AZIONE: Rimuove dalla pila 8 long, scarta il quarto long estratto e


con i rimanenti 7 rinnova il contenuto di 7 degli 8 registri generali a
32 bit, rispettando il seguente ordine: EDI, ESI, EBP, EBX, EDX, ECX,
EAX. Il registro il cui contenuto non viene modificato è il registro
ESP

FLAG di cui viene modificato il contenuto: Nessuno.


28 CAPITOLO I

EXCHANGE

FORMATO: XCHG source, destination

AZIONE: Sostituisce all’operando destinatario una copia dell’ope-


rando sorgente e viceversa.

FLAG di cui viene modificato il contenuto: Nessuno.

Operandi Esempi
Memoria, Registro Generale XCHG 0x00002000,%DX
Registro Generale, Memoria XCHG %AL,0x000A2003
Registro Generale, Registro Generale XCHG %EAX,%EDX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 29

INPUT

FORMATO: IN indirizzo, %AL


IN indirizzo, %AX
IN indirizzo, %EAX
IN (%DX),%AL
IN (%DX),%AX
IN (%DX),%EAX

AZIONE: Sostituisce il contenuto del registro destinatario (AL, AX,


EAX) con il contenuto di un adeguato numero di porte consecutive.
L’indirizzo della prima (ed eventualmente unica porta) è specificato
direttamente nell’istruzione (primi tre formati) o è contenuto nel
registro DX (ultimi tre formati); i primi tre formati possono essere
utilizzati solo per individuare porte con indirizzo inferiore a 256.

FLAG di cui viene modificato il contenuto: Nessuno.


30 CAPITOLO I

OUTPUT

FORMATO: OUT %AL, indirizzo


OUT %AX, indirizzo
OUT %EAX, indirizzo
OUT %AL,(%DX)
OUT %AX,(%DX)
OUT %EAX,(%DX)

AZIONE: Copia il contenuto del registro sorgente (AL, AX, EAX) in


un adeguato numero di porte consecutive. L’indirizzo della prima
(ed eventualmente unica porta) è specificato direttamente
nell’istruzione (primi tre formati) o è contenuto nel registro DX
(ultimi tre formati); i primi tre formati possono essere utilizzati solo
per individuare porte con indirizzo inferiore a 256.

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 31

ADD

FORMATO: ADD source, destination

AZIONE: Modifica l’operando destinatario sommandovi l’operando


sorgente (il risultato dell’operazione è consistente sia se gli operandi
sono interpretati come numeri naturali sia se gli operandi sono in-
terpretati come numeri interi); mette a 1 il contenuto del flag CF se,
interpretando gli operandi come numeri naturali, si è verificato un
riporto; mette a 1 il contenuto del flag OF se, interpretando gli ope-
randi come numeri interi, si è verificato un traboccamento.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale ADD 0x00002000,%EDX
Registro Generale, Memoria ADD %CL,0x12AB1024
Registro Generale, Registro Generale ADD %AX,%DX
Immediato, Memoria ADDB $0x5B,(%EDI)
Immediato, Registro Generale ADD $0x54A3,%AX
32 CAPITOLO I

ADD WITH CARRY

FORMATO: ADC source, destination

AZIONE: Modifica l’operando destinatario sommandovi sia l’ope-


rando sorgente sia il contenuto del flag CF (il risultato dell’opera-
zione è consistente sia se gli operandi sono interpretati come numeri
naturali sia se gli operandi sono interpretati come numeri interi);
mette a 1 il contenuto del flag CF se, interpretando gli operandi
come numeri naturali, si è verificato un riporto; mette a 1 il conte-
nuto del flag OF se, interpretando gli operandi come numeri interi, si
è verificato un traboccamento. Poiché l’istruzione ADC prevede la
gestione del riporto che può essere stato generato durante l’esecu-
zione di una precedente istruzione ADD o ADC, essa può essere
usata per predisporre pacchetti di istruzioni per la somma di ope-
randi multi_word.

Ad esempio, la somma di due operandi a 64 bit (16 cifre esadecimali)


può essere effettuata utilizzando un’istruzione ADD per elaborare i
32 bit meno significativi ed un’istruzione ADC per elaborare i 32 bit
più significativi.
ADC ADD
56A9C2D4 67A43B5F +
44B9A5A4 A6B4C55A =
9B636879 0E5900B9

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale ADC 0x00002000,%EDX
Registro Generale, Memoria ADC %CL,0x12AB1024
Registro Generale, Registro Generale ADC %AX,%DX
Immediato, Memoria ADCB $0x5B,(%EDI)
Immediato, Registro Generale ADC $0x54A3,%AX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 33

INCREMENT

FORMATO: INC destination

AZIONE: Equivale all’istruzione ADD $1,destination con la sola


differenza che il contenuto del flag CF non viene modificato.

FLAG di cui viene modificato il contenuto: OF, SF e ZF.

Operandi Esempi
Memoria INCB (%ESI)
Registro Generale INC %CX
34 CAPITOLO I

SUBTRACT

FORMATO: SUB source, destination

AZIONE: Modifica l’operando destinatario sottraendovi l’operando


sorgente (il risultato dell’operazione è consistente sia se gli operandi
sono interpretati come numeri naturali sia se gli operandi sono in-
terpretati come numeri interi); mette a 1 il contenuto del flag CF se,
interpretando gli operandi come numeri naturali, si è verificato un
prestito; mette a 1 il contenuto del flag OF se, interpretando gli ope-
randi come numeri interi, si è verificato un traboccamento.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale SUB 0x00002000,%EDX
Registro Generale, Memoria SUB %CL,0x12AB1024
Registro Generale, Registro Generale SUB %AX,%DX
Immediato, Memoria SUBB $0x5B,(%EDI)
Immediato, Registro Generale SUB $0x54A3,%AX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 35

SUBTRACT WITH BORROW

FORMATO: SBB source, destination

AZIONE: Modifica l’operando destinatario sottraendovi sia l’ope-


rando sorgente sia il contenuto del flag CF (il risultato dell’opera-
zione è consistente sia se gli operandi sono interpretati come numeri
naturali sia se gli operandi sono interpretati come numeri interi);
mette a 1 il contenuto del flag CF se, interpretando gli operandi
come numeri naturali, è stato richiesto un prestito; mette a 1 il con-
tenuto del flag OF se, interpretando gli operandi come numeri interi,
si è verificato un traboccamento. Poiché l’istruzione SBB prevede la
gestione del prestito che può essere stato richiesto durante
l’esecuzione di una precedente istruzione SUB o SBB, essa può essere
usata per predisporre pacchetti di istruzioni per la sottrazione di
operandi multi_word.

Ad esempio, la somma di due operandi a 64 bit (16 cifre esadecimali)


può essere effettuata utilizzando una SUB per elaborare i 32 bit meno
significativi ed una SBB per elaborare i 32 bit più significativi.
SBB SUB
9B636879 0E5900B9 -
56A9C2D4 67A43B5F =
44B9A5A4 A6B4C55A

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale SBB 0x00002000,%EDX
Registro Generale, Memoria SBB %CL,0x12AB1024
Registro Generale, Registro Generale SBB %AX,%DX
Immediato, Memoria SBBW $0x255B,(%EDI)
Immediato, Registro Generale SBB $0x54A3,%AX
36 CAPITOLO I

DECREMENT

FORMATO: DEC destination

AZIONE: Equivale all’istruzione SUB $1,destination con la sola


differenza che il contenuto del flag CF non viene modificato.

FLAG di cui viene modificato il contenuto: OF, SF e ZF.

Operandi Esempi
Memoria DECB (%EDI)
Registro Generale DEC %CX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 37

NEGATE

FORMATO: NEG destination

AZIONE: Interpreta l’operando destinatario come un numero intero


e lo sostituisce con il suo opposto. Qualora l’operazione non sia pos-
sibile, mette a 1 il contenuto del flag OF. Mette a 1 il contenuto del
flag CF eccetto quando l’operando é zero: in tal caso lo mette a 0.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria NEGB (%EDI)
Registro Generale NEG %CX
38 CAPITOLO I

COMPARE

FORMATO: CMP source, destination

AZIONE: Verifica se l’operando destinatario è maggiore, uguale o


minore dell’operando sorgente, sia interpretando gli operandi come
numeri naturali che come numeri interi. Aggiorna poi il contenuto
dei flag tenendo conto del risultato della verifica (i due operandi
rimangono inalterati). L’esatto algoritmo di aggiornamento del
contenuto dei flag è noioso da descriversi: l’aggiornamento è
comunque consistente con l’interpretazione che del contenuto dei
flag sarà data dall’istruzione di salto condizionato (vedi avanti), che
in un programma sensato segue sempre l’istruzione CMP.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale CMP 0x00002000,%EDX
Registro Generale, Memoria CMP %CL,0x12AB1024
Registro Generale, Registro Generale CMP %AX,%DX
Immediato, Memoria CMPB $0x5B,(%EDI)
Immediato, Registro Generale CMP $0x45AB54A3,%EAX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 39

MULTIPLY

FORMATO: MUL source

AZIONE: Considera l’operando sorgente come un moltiplicando e


l’operando destinatario (implicito) come un moltiplicatore ed effet-
tua l’operazione di moltiplicazione, interpretando gli operandi come
numeri naturali. Più in dettaglio, compie le azioni che seguono.
i) Se l’operando sorgente (moltiplicando) è:
1) a 8 bit, allora considera il contenuto di AL come il moltiplicatore
implicito; effettua l’operazione di moltiplicazione generando un
prodotto a 16 bit (in modo che non possa mai verificarsi tra-
boccamento) e mette tale prodotto in AX.
2) a 16 bit, allora considera il contenuto di AX come il moltiplicatore
implicito; effettua l’operazione di moltiplicazione generando un
prodotto a 32 bit (in modo che non possa mai verificarsi tra-
boccamento) e mette tale prodotto in DX_AX.
3) a 32 bit, considera il contenuto di EAX come il moltiplicatore
implicito; effettua l’operazione di moltiplicazione generando un
prodotto a 64 bit (in modo che non possa mai verificarsi traboc-
camento) e mette tale prodotto in EDX_EAX.
ii) Se da un’analisi della metà più significativa del prodotto (in
AH, DX, EDX a seconda dei casi) deduce che il risultato stesso
non è esprimibile su un numero di bit pari a quello del molti-
plicando e del moltiplicatore, allora mette a 1 il contenuto dei
flag CF e OF.

FLAG di cui viene modificato il contenuto: CF, OF, SF (non


attendibile) e ZF (non attendibile).

Operandi Esempi
Memoria MULB (%ESI)
Registro Generale MUL %ECX
40 CAPITOLO I

INTEGER MULTIPLY

FORMATO: IMUL source

AZIONE: Considera l’operando sorgente come un moltiplicando e


l’operando destinatario (implicito) come un moltiplicatore ed effet-
tua l’operazione di moltiplicazione, interpretando gli operandi come
numeri interi. Più in dettaglio, compie le azioni che seguono.
i) Se l’operando sorgente (moltiplicando) è:
1) a 8 bit, allora considera il contenuto di AL come il moltiplicatore
implicito; effettua l’operazione di moltiplicazione generando un
prodotto a 16 bit (in modo che non possa mai verificarsi tra-
boccamento) e mette tale prodotto in AX.
2) a 16 bit, allora considera il contenuto di AX come il moltiplicatore
implicito; effettua l’operazione di moltiplicazione generando un
prodotto a 32 bit (in modo che non possa mai verificarsi tra-
boccamento) e mette tale prodotto in DX_AX.
3) a 32 bit, considera il contenuto di EAX come il moltiplicatore
implicito; effettua l’operazione di moltiplicazione generando un
prodotto a 64 bit (in modo che non possa mai verificarsi traboc-
camento) e mette tale prodotto in EDX_EAX.
ii) Se da un’analisi della metà più significativa del prodotto (in
AH, DX, EDX a seconda dei casi) deduce che il risultato stesso
non è rappresentabile su un numero di bit pari a quello del
moltiplicando e del moltiplicatore, allora mette a 1 il contenuto
dei flag CF e OF.

FLAG di cui viene modificato il contenuto: CF, OF, SF (non


attendibile) e ZF (non attendibile).

Operandi Esempi
Memoria IMULB (%ESI)
Registro Generale IMUL %ECX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 41

DIVIDE

FORMATO: DIV source

AZIONE: Considera l’operando sorgente come un divisore e l’ope-


rando destinatario (implicito) come un dividendo ed effettua l’ope-
razione di divisione, interpretando gli operandi come numeri natu-
rali. Più in dettaglio, compie le azioni che seguono.
i) Se l’operando sorgente (divisore) è:
1) a 8 bit, allora considera il contenuto di AX come il dividendo
implicito; effettua l’operazione di divisione producendo un quo-
ziente ed un resto a 8 bit e li mette, rispettivamente, in AL e in
AH.
2) a 16 bit, allora considera il contenuto di DX_AX come il dividendo
implicito; effettua l’operazione di divisione producendo un
quoziente ed un resto a 16 bit e li mette, rispettivamente, in AX e
in DX.
3) a 32 bit, allora considera il contenuto di EDX_EAX come il divi-
dendo implicito; effettua l’operazione di divisione producendo un
quoziente ed un resto a 32 bit e li mette, rispettivamente, in EAX e
in EDX.
ii) Se nell’effettuare l’operazione di divisione riconosce che il quo-
ziente non è esprimibile su un numero di bit pari a quello su cui
è espresso il divisore, allora genera un’interruzione interna in
conseguenza della quale viene messo in esecuzione un op-
portuno sottoprogramma, noto come sottoprogramma n. 0. Il
contenuto dei flag e dei registri destinati a contenere il quo-
ziente ed il resto è, in tal caso, non significativo.

FLAG di cui viene modificato il contenuto: Tutti, ma in modo non


attendibile.

Operandi Esempi
Memoria DIVB (%ESI)
Registro Generale DIV %ECX
42 CAPITOLO I

INTEGER DIVIDE

FORMATO: IDIV source

AZIONE: Considera l’operando sorgente come un divisore e l’ope-


rando destinatario (implicito) come un dividendo ed effettua l’ope-
razione di divisione, interpretando gli operandi come numeri interi.
Più in dettaglio, compie le azioni che seguono.
i) Se l’operando sorgente (divisore) è:
1) a 8 bit, allora considera il contenuto di AX come il dividendo
implicito; effettua l’operazione di divisione producendo un quo-
ziente ed un resto a 8 bit e li mette, rispettivamente, in AL e in
AH.
2) a 16 bit, allora considera il contenuto di DX_AX come il divi-
dendo implicito; effettua l’operazione di divisione producendo
un quoziente ed un resto a 16 bit e li mette, rispettivamente, in
AX e in DX.
3) a 32 bit, allora considera il contenuto di EDX_EAX come il divi-
dendo implicito; effettua l’operazione di divisione producendo
un quoziente ed un resto a 32 bit e li mette, rispettivamente, in
EAX e in EDX.
ii) Se nell’effettuare l’operazione di divisione riconosce che il quo-
ziente non è esprimibile su un numero di bit pari a quello su cui
è espresso il divisore, allora genera un’interruzione interna in
conseguenza della quale viene messo in esecuzione un op-
portuno sottoprogramma, noto come sottoprogramma n. 0. Il
contenuto dei flag e dei registri destinati a contenere il quo-
ziente ed il resto è, in tal caso, non significativo.

FLAG di cui viene modificato il contenuto: Tutti, ma in modo non


attendibile.

Operandi Esempi
Memoria IDIVB (%ESI)
Registro Generale IDIV %ECX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 43

NOT

FORMATO: NOT destination

AZIONE: Modifica l’operando destinatario applicando a ciascuno


dei suoi bit l’operazione logica not.

FLAG di cui viene modificato il contenuto: Nessuno.

Operandi Esempi
Memoria NOTL (%ESI)
Registro Generale NOT %CX
44 CAPITOLO I

AND

FORMATO: AND source, destination

AZIONE: Sostituisce ciascun bit dell’operando destinatario con il ri-


sultato dell’operazione logica and tra il bit stesso ed il corrispondente
bit dell’operando sorgente; mette a 0 il contenuto dei flag CF ed OF.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale AND 0x00002000,%EDX
Registro Generale, Memoria AND %CL,0x12AB1024
Registro Generale, Registro Generale AND %AX,%DX
Immediato, Memoria ANDB $x5B,(%EDI)
Immediato, Registro Generale AND $0x45AB54A3,%EAX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 45

OR

FORMATO: OR source, destination


AZIONE: Sostituisce ciascun bit dell’operando destinatario con il ri-
sultato dell’operazione logica or tra il bit stesso ed il corrispondente
bit dell’operando sorgente; mette a 0 il contenuto dei flag CF ed OF.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale OR 0x00002000,%EDX
Registro Generale, Memoria OR %CL,0x12AB1024
Registro Generale, Registro Generale OR %AX,%DX
Immediato, Memoria ORB $0x5B,(%EDI)
Immediato, Registro Generale OR $0x45AB54A3,%EAX
46 CAPITOLO I

XOR

FORMATO: XOR source, destination


AZIONE: Sostituisce ciascun bit dell’operando destinatario con il ri-
sultato dell’operazione logica xor tra il bit stesso ed il corrispondente
bit dell’operando sorgente; mette a 0 il contenuto dei flag CF ed OF.

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Memoria, Registro Generale XOR 0x00002000,%EDX
Registro Generale, Memoria XOR %CL,0x12AB1024
Registro Generale, Registro Generale XOR %AX,%DX
Immediato, Memoria XORB $0x5B,(%EDI)
Immediato, Registro Generale XOR $0x45AB54A3,%EAX
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 47

CONVERT BYTE TO WORD

FORMATO: CBW

AZIONE: Interpreta il contenuto di AL come un numero intero a 8


bit, rappresenta tale numero su 16 bit e quindi lo memorizza in AX.
L’istruzione AND $0x00FF,%AX compie un’operazione simile, nel-
l’ambito dell’aritmetica dei numeri naturali.

FLAG di cui viene modificato il contenuto: Nessuno.


48 CAPITOLO I

CONVERT WORD TO DOUBLEWORD

FORMATO: CWD

AZIONE: Interpreta il contenuto di AX come un numero intero a 16


bit, rappresenta tale numero su 32 bit e quindi lo memorizza in
DX_AX. L’istruzione AND $0x0000,%DX compie un’operazione
simile, nell’ambito dell’aritmetica dei numeri naturali.

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 49

CONVERT DOUBLEWORD TO QUADWORD

FORMATO: CDQ

AZIONE: Interpreta il contenuto di EAX come un numero intero a 32


bit, rappresenta tale numero su 64 bit e quindi lo memorizza in
EDX_EAX. L’istruzione AND $0x00000000,%EDX compie un’ope-
razione simile, nell’ambito dell’aritmetica dei numeri naturali.

FLAG di cui viene modificato il contenuto: Nessuno.


50 CAPITOLO I

CONVERT WORD TO DOUBLEWORD in EAX

FORMATO: CWDE

AZIONE: Interpreta il contenuto di AX come un numero intero a 16


bit, rappresenta tale numero su 32 bit e quindi lo memorizza in EAX.
L’istruzione AND $0x0000FFFF,%EAX compie un’operazione simile,
nell’ambito dell’aritmetica dei numeri naturali.

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 51

SHIFT LOGICAL LEFT

FORMATO: SHL source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a destra, con-
siderando il bit più significativo dell’operando destinatario imme-
diatamente a destra del bit contenuto in CF e sostituendo con 0 il bit
meno significativo dell’operando destinatario. Pone infine a 0 il con-
tenuto del flag OF. Il numero naturale n non deve superare 31;
ometterlo equivale a fissarlo pari a 1.

Guardando all’operando destinatario come a un numero naturale,


questa istruzione sostituisce tale numero con il suo prodotto per 2 n:
il risultato è attendibile con certezza solo nel caso n=1 e contenuto
finale di CF a 0.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0
CF

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Immediato, Registro Generale SHL $1,%EAX
Immediato, Memoria SHLB $7,0x00002000
Registro CL, Registro Generale SHL %CL,%BX
Registro CL, Memoria SHLL %CL,(%EDI)
52 CAPITOLO I

SHIFT ARITHMETIC LEFT

FORMATO: SAL source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a destra, con-
siderando il bit più significativo dell’operando destinatario imme-
diatamente a destra del bit contenuto in CF e sostituendo con 0 il bit
meno significativo dell’operando destinatario. Qualora il bit più si-
gnificativo dell’operando destinatario abbia cambiato anche una sola
volta il suo valore, mette a 1 il contenuto del flag OF. Il numero na-
turale n non deve superare 31; ometterlo equivale a fissarlo pari a 1.

Guardando all’operando destinatario come a un numero intero,


questa istruzione sostituisce tale numero con il suo prodotto per 2n:
il risultato è attendibile con certezza solo se il contenuto OF è a 0.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0
CF

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Immediato, Registro Generale SAL $1,%EAX
Immediato, Memoria SALB $7,0x00002000
Registro CL, Registro Generale SAL %CL,%BX
Registro CL, Memoria SALL %CL,(%EDI)
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 53

SHIFT LOGICAL RIGHT

FORMATO: SHR source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a sinistra,
considerando il bit meno significativo dell’operando destinatario
immediatamente a sinistra del bit contenuto in CF e sostituendo con
0 il bit più significativo dell’operando destinatario. Pone infine a 0 il
contenuto del flag OF. Il numero naturale n non deve superare 31;
ometterlo equivale a fissarlo pari a 1.

Guardando all’operando destinatario come a un numero naturale,


questa istruzione divide tale numero per 2n e lo sostituisce con il
quoziente (approssimato per difetto).

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
0
CF

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Immediato, Registro Generale SHR $1,%EAX
Immediato, Memoria SHRB $7,0x00002000
Registro CL, Registro Generale SHR %CL,%BX
Registro CL, Memoria SHRL %CL,(%EDI)
54 CAPITOLO I

SHIFT ARITHMETIC RIGHT

FORMATO: SAR source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a sinistra,
considerando il bit meno significativo dell’operando destinatario
immediatamente a sinistra del bit contenuto in CF e sostituendo con
se stesso il bit più significativo dell’operando destinatario. Pone in-
fine a 0 il contenuto del flag OF. Il numero naturale n non deve su-
perare 31; ometterlo equivale a fissarlo pari a 1.

Guardando all’operando destinatario come a un numero intero,


questa istruzione divide tale numero per 2n e lo sostituisce con il
quoziente (approssimato per difetto).

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

CF

FLAG di cui viene modificato il contenuto: Tutti.

Operandi Esempi
Immediato, Registro Generale SAR $1,%EAX
Immediato, Memoria SARB $7,0x00002000
Registro CL, Registro Generale SAR %CL,%BX
Registro CL, Memoria SARL %CL,(%EDI)
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 55

ROTATE LEFT

FORMATO: ROL source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a destra, con-
siderando il bit più significativo dell’operando destinatario imme-
diatamente a destra sia del bit contenuto in CF che del bit meno si-
gnificativo dell’operando destinatario stesso. Il numero naturale n
non deve superare 31; ometterlo equivale a fissarlo pari a 1.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

CF

FLAG di cui viene modificato il contenuto: CF.

Operandi Esempi
Immediato, Registro Generale ROL $1,%EAX
Immediato, Memoria ROLB $7,0x00002000
Registro CL, Registro Generale ROL %CL,%BX
Registro CL, Memoria ROLL %CL,(%EDI)
56 CAPITOLO I

ROTATE RIGHT

FORMATO: ROR source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a sinistra,
considerando il bit meno significativo dell’operando destinatario
immediatamente a sinistra sia del bit contenuto in CF che del bit più
significativo dell’operando destinatario. Il numero naturale n non
deve superare 31; ometterlo equivale a fissarlo pari a 1.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

CF

FLAG di cui viene modificato il contenuto: CF.

Operandi Esempi
Immediato, Registro Generale ROR $1,%EAX
Immediato, Memoria RORB $7,0x00002000
Registro CL, Registro Generale ROR %CL,%BX
Registro CL, Memoria RORL %CL,(%EDI)
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 57

ROTATE THROUGH CARRY LEFT

FORMATO: RCL source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a destra, con-
siderando il bit contenuto in CF immediatamente a destra del bit
meno significativo ed immediatamente a sinistra del bit più signifi-
cativo dell’operando destinatario. Il numero naturale n non deve
superare 31; ometterlo equivale a fissarlo pari a 1.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

CF

FLAG di cui viene modificato il contenuto: CF

Operandi Esempi
Immediato, Registro Generale RCL $1,%EAX
Immediato, Memoria RCLB $7,0x00002000
Registro CL, Registro Generale RCL %CL,%BX
Registro CL, Memoria RCLL %CL,(%EDI)
58 CAPITOLO I

ROTATE THROUGH CARRY RIGHT

FORMATO: RCR source, destination

AZIONE: Interpreta l’operando sorgente come un numero naturale n


e, per n volte, sostituisce il bit contenuto in CF e ciascun bit dell’ope-
rando destinatario con il bit che gli è immediatamente a sinistra,
considerando il bit contenuto in CF immediatamente a sinistra del
bit più significativo ed immediatamente a destra del bit meno signi-
ficativo dell’operando destinatario; ometterlo equivale a fissarlo pari
a 1.

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

CF

FLAG di cui viene modificato il contenuto: CF

Operandi Esempi
Immediato, Registro Generale RCR $1,%EAX
Immediato, Memoria RCRB $7,0x00002000
Registro CL, Registro Generale RCR %CL,%BX
Registro CL, Memoria RCRL %CL,(%EDI)
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 59

JUMP

FORMATO: JMP %EIP ± displacement


JMP *extended_register
JMP * memory

AZIONE: Calcola, in accordo alle specifiche descritte nel sottopar-


grafo 3.2, un indirizzo di salto e lo immette nel registro EIP.

FLAG di cui viene modificato il contenuto: Nessuno.


60 CAPITOLO I

JUMP if CONDITION MET

FORMATO: Jcon %EIP ± displacement

AZIONE: Esamina il contenuto dei flag (o il contenuto di CX o di


ECX in due casi molto particolari). Se da questo esame risulta che la
condizione con è soddisfatta, allora si comporta come l’istruzione
JMP %EIP ± displacement; in caso contrario termina senza
compiere alcuna azione.

FLAG di cui viene modificato il contenuto: Nessuno.

Di seguito sono riassunti alcuni dei codici operativi delle più


significative istruzioni di salto condizionato e, per ciascuno di essi, è
spiegato brevemente il significato della pertinente condizione con.

JE (Jump if Equal) la condizione è soddisfatta se ZF contiene 1; il verificarsi di


questa condizione dopo l’esecuzione di un’istruzione CMP indica che
l’operando destinatario era uguale all’operando sorgente.
JNE (Jump if Not Equal) la condizione è soddisfatta se ZF contiene 0; il verifi-
carsi di questa condizione dopo un’istruzione CMP indica che l’operando
destinatario non era uguale all’operando sorgente.
JA (Jump if Above) la condizione è soddisfatta se CF contiene 0 e ZF contiene
0; il verificarsi di questa condizione dopo l’esecuzione di una istruzione
CMP indica che l’operando destinatario era maggiore dell’operando sor-
gente, essendo gli operandi interpretati come numeri naturali.
JAE Jump if Above or Equal) la condizione è soddisfatta se CF contiene 0; il
verificarsi di questa condizione dopo l’esecuzione di un’istruzione CMP
indica che l’operando destinatario era maggiore o uguale rispetto all’ope-
rando sorgente, essendo gli operandi interpretati come numeri naturali.
JB (Jump if Below) la condizione è soddisfatta se CF contiene 1; il verificarsi di
questa condizione dopo l’esecuzione di un’istruzione CMP indica che
l’operando destinatario era minore dell’operando sorgente, essendo gli
operandi interpretati come numeri naturali.
JBE (Jump if Below or Equal) la condizione è soddisfatta se CF contiene 1 o ZF
contiene 1; il verificarsi di questa condizione dopo l’esecuzione di
un’istruzione CMP indica che l’operando destinatario era minore od
uguale rispetto all’operando sorgente, essendo gli operandi interpretati
come numeri naturali.
MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 61

JG (Jump if Greater) la condizione è soddisfatta se ZF contiene 0 e se il conte-


nuto di SF è uguale a quello di OF; il verificarsi di questa condizione dopo
l’esecuzione di un’istruzione CMP indica che l’operando destinatario era
maggiore dell’operando sorgente, essendo gli operandi interpretati come
numeri interi.
JGE (Jump if Greater or Equal) la condizione è soddisfatta se il contenuto di SF
è uguale a quello di OF; il verificarsi di questa condizione dopo
l’esecuzione di un’istruzione CMP indica che l’operando destinatario
maggiore o uguale rispetto all’operando sorgente, essendo gli operandi
interpretati come numeri interi.
JL (Jump if Less) la condizione è soddisfatta se il contenuto di SF è diverso da
quello di OF; il verificarsi di questa condizione dopo l’esecuzione di
un’istruzione CMP indica che l’operando destinatario era minore del-
l’operando sorgente, essendo gli operandi interpretati come numeri interi.
JLE (Jump if Less or Equal) la condizione è soddisfatta se ZF contiene 1 oppure
se il contenuto di SF è diverso da quello di OF; il verificarsi di questa
condizione dopo l’esecuzione di un’istruzione CMP indica che l’operando
destinatario era minore o uguale rispetto all’operando sorgente, essendo gli
operandi interpretati come numeri interi.
JZ (Jump if Zero) la condizione è soddisfatta se ZF contiene 1; il verificarsi di
questa condizione indica che il risultato dell’istruzione precedente è stato
zero.
JNZ (Jump if Not Zero) la condizione è soddisfatta se ZF contiene 0; il verificarsi
di questa condizione indica che il risultato dell’istruzione precedente è
stato diverso da zero.
JC (Jump if Carry) la condizione è soddisfatta se CF contiene 1.
JNC (Jump if No Carry) la condizione è soddisfatta se CF contiene 0.
JO (Jump if Overflow) la condizione è soddisfatta se OF contiene 1.
JNO (Jump if No Overflow) la condizione è soddisfatta se OF contiene 0.
JS (Jump if Sign) la condizione è soddisfatta se SF contiene 1.
JNS (Jump if No Sign) la condizione è soddisfatta se SF contiene 0.
JCXZ (Jump if CX is 0) la condizione è soddisfatta se il registro CX contiene 0.
JECXZ (Jump if ECX is 0) la condizione è soddisfatta se il registro ECX contiene 0.
62 CAPITOLO I

CALL

FORMATO: CALL %EIP ± $displacement


CALL *extended_register
CALL * memory

AZIONE: Effettua la chiamata di un sottoprogramma. Più precisa-


mente, salva nella pila corrente il contenuto del registro EIP e poi
modifica il contenuto di tale registro in modo del tutto simile a come
fa l’istruzione JMP.

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 63

RETURN

FORMATO: RET

AZIONE: Effettua il ritorno da un sottoprogramma. Più precisamen-


te, rimuove un long dalla pila e con esso rinnova il contenuto di EIP.

FLAG di cui viene modificato il contenuto: Nessuno.


64 CAPITOLO I

NO OPERATION

FORMATO: NOP

AZIONE: Termina senza compiere alcuna azione.

FLAG di cui viene modificato il contenuto: Nessuno.


MODELLO DEL CALCOLATORE E SET DI ISTRUZIONI DEL PROCESSORE 65

HALT

FORMATO: HLT

AZIONE: Provoca la cessazione di ogni attività del processore.


IL LINGUAGGIO ASSEMBLER GAS

1 GENERALITÀ

Il linguaggio assembler rappresenta un’estensione del linguaggio


mnemonico, nel senso che permette a un utente di scrivere un pro-
gramma in modo completo, sia per quanto concerne le parti conte-
nenti le istruzioni (sezioni codice) che per quanto concerne le parti
contenenti gli operandi e la pila (sezioni dati e stack). Il linguaggio
consente di assegnare nomi simbolici alle locazioni di memoria e di
riferirle tramite questi nomi, senza che sia necessaria la conoscenza
degli indirizzi numerici. Il linguaggio permette inoltre di definire
costanti, di assegnare loro un nome simbolico e di riferirle tramite
quel nome. Per poter utilizzare il linguaggio assembler occorre
disporre di un apposito traduttore (assemblatore) che provveda a
trasformare programmi scritti in linguaggio assembler nei corrispon-
denti programmi in linguaggio macchina.
Un programma in linguaggio assembler è costituito da una serie
di statement, che si possono suddividere in istruzioni assembler,
dichiarazioni di variabili e direttive. Le istruzioni assembler consentono
di specificare istruzioni macchina in forma simbolica e servono quin-
di sia a definire il contenuto delle locazioni di memoria che costitui-
scono le sezioni codice sia ad assegnare a tali locazioni nomi simbo-
lici; le dichiarazioni di variabili consentono sia di definire il conte-
nuto delle locazioni di memoria che costituiscono le sezioni dati e la
sezione pila, sia di assegnare a tali locazioni nomi simbolici; le
direttive forniscono all’assemblatore informazioni utili per effettuare
la traduzione da linguaggio assembler a linguaggio macchina.
Il linguaggio assembler cui faremo riferimento in questo testo è
noto come GAS (GnuASsembler) e viene presentato con lo scopo di
fornire uno strumento di programmazione che consenta al lettore di
impadronirsi degli aspetti architetturali di un calcolatore. Viene
pertanto fornita una versione del linguaggio comprendente solo
quelle parti che più si avvicinano al linguaggio macchina o a quello
mnemonico precedentemente introdotto.
68 CAPITOLO II

L’assemblatore, incluso in un ambiente di sviluppo che sarà


illustrato nel capitolo successivo, è installato in una modalità che lo
rende “assemblatore assoluto”, cioè capace di accettare un unico file
contenente il programma in linguaggio assembler (file sorgente o di
tipo .s) e generare un file contenente il programma in linguaggio
macchina, già pronto per essere eseguito (file eseguibile o di tipo .exe).
Non sono pertanto definite le direttive per l’assemblaggio separato
di più moduli contenuti in file sorgenti distinti ed il successivo
collegamento dei moduli tradotti.
In quanto segue supporremo che un programma, inizialmente
scritto da un utente in linguaggio assembler e poi tradotto in lin-
guaggio macchina ed incluso in un file eseguibile dall’assemblatore,
sia destinato ad essere caricato nella memoria del calcolatore C86/32
e ad essere eseguito dal processore P86/32, gestito dal sistema
operativo Windows. Il programma utente è pertanto assimilabile a
un “sottoprogramma” del sistema operativo e deve tornare ad esso
una volta concluso il proprio lavoro.
In questa ottica, organizzeremo i programmi utente come
costituiti da: i) un “sottoprogramma principale” visibile a livello di
Windows; ii) altri eventuali sottoprogrammi chiamabili dall’interno
del sottoprogramma principale o comunque annidati fra di loro, ma
non visibili a livello di sistema operativo.

2 STRUTTURA DI UN PROGRAMMA IN LINGUAGGIO ASSEMBLER

Uno statement in linguaggio assembler è composto, al più, dai


seguenti 4 campi:
nome KEY_WORD specifica degli operandi #commento
Il campo nome, che è spesso opzionale, è costituito da un identifi-
catore scelto dal programmatore. Esso è formato da lettere, da cifre e
dai caratteri speciali _ , @ e $ e deve iniziare con una lettera o con
uno dei caratteri speciali _ e $ . Il campo KEY_WORD è costituito da
una o più parole chiave del linguaggio e caratterizza lo statement. Il
campo specifica degli operandi ha una struttura che dipende
da quanto specificato nel campo KEY_WORD. Il campo commento è
opzionale e, se presente, inizia con il carattere cancelletto (#).
IL LINGUAGGIO ASSEMBLER GAS 69

Per esemplificare, consideriamo lo schema di programma


riportato in Tabella 1: esso comprende la pila, una sezione dati con
M variabili e una sezione codice con il sottoprogramma principale e
K ulteriori sottoprogrammi. Gli identificatori scelti dal program-
matore sono scritti con lettere minuscole mentre le parole chiave del
linguaggio sono scritte con lettere maiuscole.
La direttiva .GLOBAL _main rende visibile al sistema operativo
l’etichetta della prima istruzione del sottoprogramma principale.
Tale etichetta deve essere obbligatoriamente _main, per cui potrebbe
apparire inutile dichiarala in un’apposita direttiva, ma così non è !

.GLOBAL _main

mystack: corpo della pila

variable1: ...
...
...
variableM: ...

_main: corpo del "sottoprogramma principale"


RET

sub1: corpo di un sottoprogramma


RET
...
...

subK: corpo di un sottoprogramma


RET

Tabella 1 - Struttura di un semplice programma assembler.

Gli identificatori mystack, variable1, …, variableM sono i nomi sim-


bolici della prima delle locazioni da cui iniziano le porzioni di me-
moria contenenti la pila, la prima variabile, … la M-esima variabile;
gli identificatori _main, sub1, sub2,..., subK sono i nomi simbolici della
prima delle locazioni da cui iniziano i vari sottoprogrammi costi-
tuenti globalmente la sezione codice. Ad ogni nome corrisponde, a
livello di linguaggio macchina, l’indirizzo numerico della pertinente
locazione. Pertanto mystack, variable1, …, variableM , _main, sub1,
sub2,..., subK sono a tutti gli effetti dei numeri naturali a 32 bit.
70 CAPITOLO II

3 LE COSTANTI IN UN PROGRAMMA ASSEMBLER

Negli statement possono essere utilizzate costanti, sia per speci-


ficare operandi nelle istruzioni che per definire valori iniziali nelle
dichiarazioni di variabili. A livello di linguaggio assembler le costanti
si dividono in numeri e stringhe.
Un numero può essere naturale o intero. Un numero naturale è
esprimibile come una sequenza di cifre in una data base, preceduta
da 0b se la base è due, 0x se la base è sedici e da nessun simbolo,ma
con prima cifra iniziale diversa da 0, se la base è dieci. Esempi di
numeri naturali sono 0b10010 (binario), 0x2A246 (esadecimale), 246
(decimale). Un numero intero viene espresso in una delle precedenti
basi, facendo precedere la sequenza di cifre dal segno del numero.
Esempi di numeri interi sono –0x2A e +35.
I numeri vengono convertiti dall’assemblatore in base due su 8,
16, 32 o 64 bit, a seconda del contesto in cui sono scritti; i numeri
interi vengono rappresentati in complemento a due.
Una stringa è una sequenza di uno o più caratteri racchiusi tra
virgolette: esempi di stringhe sono "messaggio", "Questa e` una
stringa". Le stringhe sono convertite dall’assemblatore in sequenze
di byte, sostituendo a ogni carattere la corrispondente codifica ASCII
estesa a 8 bit con l’aggiunta di un bit 0 in testa (vedi l’Appendice).
Una stringa con un solo carattere è esprimibile anche racchiudendo il
carattere tra apici. Esempi di stringhe monocarattere sono '5' , 'A'
, ';'. A questo proposito è bene notare che, ad esempio, le costanti
'5', 0x35, 53 e 0b00110101 vengono tutte convertite
dall’assemblatore negli 8 bit 00110101.
Alle costanti può essere assegnato un nome simbolico mediante
la direttiva .set. Tale nome può essere poi usato nel programma in
sostituzione della costante, con minore possibilità di errore e
maggiore leggibilità. Ad esempio lo statement:
.set max_lines, 246
assegna il nome simbolico max_lines alla costante 246.
Una direttiva .set può essere inserita in un qualunque punto del
programma, con il solo vincolo che il nome assegnato dalla direttiva
alla costante non può essere utilizzato a monte della direttiva stessa.
IL LINGUAGGIO ASSEMBLER GAS 71

4 IL CORPO DI UNA SEZIONE DATI: LE VARIABILI

Nel linguaggio assembler, il corpo di una sezione dati è costituito


da una sequenza di dichiarazioni di variabili.
Ogni variabile è caratterizzata da un tipo e può avere una sola
componente di un certo tipo (variabile scalare) o più componenti dello
stesso tipo (variabile vettoriale). Sono previste variabili di vario tipo,
fra cui le variabili di tipo byte, word e long che sono caratterizzate dal
fatto che il valore di ogni componente è costituito, rispettivamente, da
1, 2 o 4 byte.
Ad ogni variabile sono associate, a livello di linguaggio
macchina, una o più locazioni contigue di memoria, nel senso che tali
locazioni sono destinate a contenere il valore delle componenti (o
dell’unica componente) della variabile. Alle variabili dichiarate in
sequenza in un programma sono associati gruppi di locazioni
adiacenti, secondo l’ordine di dichiarazione.
Gli statement per dichiarare variabili dei tre precedenti tipi,
hanno la forma:
nome .BYTE espressione, ...
nome .WORD espressione, ...
nome .LONG espressione, ...

Il nome di una variabile è opzionale, corrisponde all’indirizzo


della prima delle locazioni destinate a contenere i valori delle
componenti della variabile stessa e come tale può essere usato in
tutte le istruzioni.
Il campo espressione, obbligatorio, stabilisce il numero delle
componenti della variabile ed il loro valore iniziale.
Un’espressione è in genere una costante, ma sono anche possibili
espressioni complesse costituite da più espressioni legate da
operatori quali + , - , * (moltiplicazione), ecc.
Le componenti di una variabile vettoriale possono essere viste a
loro volta come variabili scalari indipendenti aventi un loro nome: se
ad esempio vett è il nome di una variabile vettoriale, allora il nome
della i-esima componente è vett + i*type_size, dove type_size
è il numero delle locazioni di memoria destinate a ogni componente
e vale quindi 1, 2 o 4 a seconda che vett sia di tipo byte, word o long.
72 CAPITOLO II

4.1 VARIABILI DI TIPO BYTE

Per illustrare le dichiarazioni di variabili di tipo byte,


consideriamo il seguente esempio:
alphab: .BYTE 0x2A # Variabile a 1 componente
betab: .BYTE 'm','s' # Variabile a 2 componenti
deltab: .BYTE 'm',0x0A,0x0D # Variabile a 3 componenti

La prima dichiarazione definisce una variabile scalare, il cui


valore iniziale è il numero naturale 0x2A (ovvero gli 8 bit 00101010).
La seconda dichiarazione definisce una variabile vettoriale a due
componenti, i cui valori iniziali sono, rispettivamente, la codifica
ASCII a 8 bit del carattere m e la codifica ASCII a 8 bit del carattere s
(ovvero l’espressione binaria dei numeri naturali 0x6D e 0x73).
La terza dichiarazione definisce una variabile vettoriale a tre
componenti, i cui valori iniziali sono, rispettivamente, la codifica
ASCII del carattere m, il numero naturale 0x0A ed il numero naturale
0x0DH (questi due ultimi numeri sono spesso utilizzati perché la loro
espressione binaria costituisce, rispettivamente, la codifica ASCII
dell’avanzamento linea e la codifica ASCII del ritorno carrello).
Variabili di fatto di tipo byte, ma atte ad essere inizializzate con
codifiche ASCII, possono essere dichiara tramite la direttiva .ASCII
come illustrato di seguito:
mess: .ASCII "messaggio" # Variabile a 9 componenti
Tale dichiarazione definisce una variabile vettoriale a 9 com-
ponenti, i cui valori iniziali sono, rispettivamente, la codifiche ASCII
(a 8 bit) dei caratteri m, e, s, …, o.

4.2 VARIABILI DI TIPO WORD

Per illustrare le dichiarazioni di variabili di tipo word, conside-


riamo il seguente esempio:
alphaw: .WORD 0x6BA9,35 # Variabile a 2 componenti
La dichiarazione definisce una variabile vettoriale a due
componenti, il cui valore iniziale è, rispettivamente, il numero
naturale 0x6BA9 (ovvero i 16 bit 0110101110101001) ed il numero
naturale 35 (ovvero i 16 bit 0000000000100011).
IL LINGUAGGIO ASSEMBLER GAS 73

4.3 VARIABILI DI TIPO LONG

Per illustrare le dichiarazioni di variabili di tipo long,


consideriamo i seguente esempi:
alphad: .LONG 0xAB124591 # Variabile a 1 componente
betad: .LONG alphad # Variabile a 1 componente

La prima dichiarazione definisce una variabile scalare, il cui


valore iniziale è il numero naturale 0xAB124591.
La seconda dichiarazione definisce una variabile scalare, il cui
valore iniziale è l’indirizzo della variabile alphad.

4.4 VARIABILI VETTORIALI DI VARIO TIPO NON-INIZIALIZZATE

Nello scrivere un programma, è spesso necessario poter


dichiarare variabili vettoriali con un elevato numero di componenti,
di cui non interessa il valore iniziale, ma il cui valore è destinato ad
essere definito man mano che il programma è in esecuzione (cioè a
run time).
A tale scopo, il linguaggio assembler prevede la direttiva .FILL,
che ha la forma:
nome_variabile: .FILL numero_componenti,tipo
Se il campo tipo vale 1, la variabile vettoriale è di tipo byte; se
vale 2 è di tipo word, se vale 4 è di tipo long, ecc.
I seguenti esempi ne chiariscono l’uso:
vett1: .FILL 12,2
vett2: .FILL 512,1

La prima dichiarazione definisce una variabile vettoriale a 12


componenti di tipo word.
La seconda dichiarazione definisce una variabile vettoriale a 512
componenti di tipo byte.
Volendo, si può assegnare un valore iniziale identico per tutte le
componenti. Per fare ciò occorre aggiungere nella dichiarazione un
campo che contenga tale valore. Ad esempio:
vett3: .FILL 12,2,0xAB23
definisce una variabile vettoriale a 12 componenti di tipo word e
tutte con valore iniziale 0xAB23.
74 CAPITOLO II

5 DEFINIZIONE DELLA PILA

Il corpo della pila è costituito da una variabile vettoriale, che


inizialmente costituirà la parte vuota della pila e poi, a run time, sia
la parte vuota sia la parte piena. Tale variabile è pertanto espri-
mibile, ad esempio, come:
mystack: .FILL N,4
con N numero naturale, scelto dal programmatore.
Ne deriva che l’indirizzo da caricare inizialmente nel registro
stack pointer ESP, è l’indirizzo della prima locazione successiva a
quelle destinate a contenere la variabile mystack .
Tale indirizzo è pertanto il numero naturale a 32 bit esprimibile a
livello di linguaggio assembler come (mystack + N*4) e a cui noi,
tramite la direttiva .set assegneremo il nome simbolico
initial_esp:
.set initial_esp,(mystack + N*4)
Il numero naturale N deve essere scelto dal programmatore in
relazione alle sue esigenze e l’istruzione per inizializzare il registro
ESP diviene:
MOV $initial_esp,%ESP
Poiché l’ambiente di sviluppo automatizza sia la definizione di
una pila per i programmi utente sia l’inizializzazione del registro
ESP, di questo problema non ci occuperemo più quando scriveremo
in nostri programmi.

6 IL CORPO DI UNA SEZIONE CODICE

Il corpo di una sezione codice è costituito da qualche direttiva e


dalle istruzioni dei vari sottoprogrammi.
Ad ogni istruzione viene associato un numero di locazioni di
memoria sufficienti a contenere la sua codifica in linguaggio
macchina; ad istruzioni consecutive nel programma, sono associate
locazioni consecutive nella memoria.
Il campo KEY_WORD delle istruzioni coincide con il campo OPCODE
introdotto nel linguaggio mnemonico riportato nel Capitolo 1. Gli
operandi (costanti o variabili) possono essere indicati utilizzando i
loro nomi simbolici.
IL LINGUAGGIO ASSEMBLER GAS 75

6.1 COME RIFERIRE LE VARIABILI

L’accesso alle variabili richiede, nel caso più generale, l’utilizzo


di un indirizzamento bimodificato con displacement. Nei casi
comuni, in cui si gestiscano variabili scalari e vettoriali, è sufficiente
l’uso dell’indirizzamento diretto per riferire le variabile scalari e
dell’indirizzamento indiretto e dell’indirizzamento con displacement
e registro di modifica per riferire le varie componenti delle variabili
vettoriali.
In ogni caso, l’assemblatore considera il nome di una variabile
come l’indirizzo della variabile stessa.
Per evidenziare alcuni dei casi più comuni una sezione dati in cui
sono dichiarate due variabili scalari di nome alphab e alphad e una
variabile vettoriale di nome vettw.
alphab: .BYTE 0x25
alphad: .LONG 0x12345678
vettw: .FILL 50,2
Le variabili scalari vengono quindi riferite, utilizzando l’indiriz-
zamento diretto, come chiarito nei seguenti esempi:
# Indirizzamento diretto di alphab
OPCODEB alphab
# Indirizzamento diretto di alphad
OPCODEL alphad
L’inserzione della lettera B come appendice del codice operativo
dell’istruzione che opera su alphab e della lettera L come appendice
del codice operativo dell’istruzione che opera su alphad è necessario
in quanto l’assemblatore, purtroppo (e stupidamente), non deriva
un’informazione equivalente a quella fornita da tali lettere
dall’esame del tipo delle variabili.
Le componenti di una variabile vettoriale vengono di solito
riferite utilizzando l’indirizzamento con displacement e registro
(indice o base) di modifica e l’indirizzamento con registro puntatore;
meno utile l’indirizzamento diretto.
Consideriamo per esempio la quarta componente della variabile
vettw definita poco sopra. Tale componente ha indirizzo (simbolico)
vettw+6 (6 deriva da i * type_size con i pari a 3 e type_size pari a 2).
A livello di linguaggio assembler, essa può essere riferita nei
seguenti tre modi:
76 CAPITOLO II

# Indirizzamento con registro indice ESI


MOV $0,%ESI #Inizializzazione del registro indice
Porzione di programma che incrementa
di 3 il contenuto del registro ESI
OPCODEW vettw(,%ESI,2)
# Indirizzamento con registro puntatore EBX
LEA vettw,%EBX #Inizializzazione del registro puntatore
Porzione di programma che incrementa
di 6 il contenuto del registro EBX
OPCODEW (%EBX)
# Indirizzamento diretto (ha poco senso usarlo)
OPCODEW vettw + 6

6.2 COME RIFERIRE LE ISTRUZIONI NELLE ISTRUZIONI DI CONTROLLO


DEL FLUSSO

Nel linguaggio assembler, a ogni istruzione può essere assegnato


un nome simbolico, detto etichetta, che corrisponde all’indirizzo
dell’istruzione, cioè all’indirizzo della prima delle locazioni di memo-
ria occupate dalla codifica in linguaggio macchina dell’istruzione.
L’etichetta di un’istruzione trova la sua maggiore utilità nel fatto
che essa può essere utilizzata per riferire quell’istruzione nelle
istruzioni di salto.
Per assegnare un’etichetta a un’istruzione occorre premettere
l’etichetta stessa al codice operativo dell’istruzione ed usare i due
punti come separatore. Ad esempio, scrivendo:
label1: MOV %AX,%DX
si assegna l’etichetta label1 all’istruzione MOV %AX,%DX.
Il seguente programma riassume in forma sintetica l’intera casi-
stica sopra descritta, relativamente alle istruzioni JMP e Jcon.
# Sezione dati
alphal: .FILL 1,4

# Sezione codice
_main: ...
...
cycle: istruzione
...
IL LINGUAGGIO ASSEMBLER GAS 77

# Forme alternative di salto a cycle


# Indirizzo di salto del tipo EIP ± displacement
JE cycle
# Indirizzo di salto in un registro
LEA cycle,%EBX
JMP *%EBX
# Indirizzo di salto in memoria
MOVL $cycle,alphal
JMP *alphal
...
Si noti come nel formato in cui è riferita l’etichetta dell’istruzione
a cui saltare (in pratica il formato più usato), l’assemblatore liberi il
programmatore dal complicato calcolo del displacement.
Considerazioni del tutto simili valgono per la chiamata dei
sottoprogrammi, come chiarito dal seguente esempio:
# Sezione dati
alphal: .FILL 1,4
# Sezione codice
...
...
# Forme alternative di chiamata a mysub
CALL mysub
LEA mysub,%EBX
CALL *%EBX
MOVL $mysub,alphal
CALL alphal
...
...
mysub: ...
...
...
RET

7 MISURAZIONI DI SEZIONI DATI O SEZIONI CODICE

I numero di locazioni occupate da una sezione dati o codice o da


parte di essa, può essere facilmente calcolato inserendo delle
etichette delimitatrici, come chiarito nel seguente esempio:
78 CAPITOLO II

start_label:
porzione di dati o di codice da misurare
end_label:
Il nome simbolico del numero di locazioni occupate è pertanto
definibile tramite la direttiva .set, come segue:
.set numero_locazioni_occupate, end_label - start_label

8 LE DIRETTVE .DATA E .TEXT

Non è strettamente necessario, ma è buona regola far precedere le


sezioni dati dalla direttiva .DATA e le sezioni codice dalla direttiva
.TEXT. L’uso di tali direttive rende più leggibile un programma e
permette all’ambiente di sviluppo di attivare meccanismi di prote-
zione. Non essendo comunque il loro uso strettamente necessario,
nel seguito del testo non ricorreremo ad esse.

9 ESEMPI DI PROGRAMMI IN LINGUAGGIO ASSEMBLER

Per illustrare l’uso del linguaggio assembler GAS, riportiamo


alcuni semplici programmi in cui è previsto l’uso di un sotto-
programma di ingresso (inbyte) e di un sottoprogramma di uscita
(outbyte), che vengono per il momento illustrati solo a livello funzio-
nale. Sulla loro struttura torneremo nel Cap. 3.
Il sottoprogramma inbyte preleva un byte da un dispositivo ester-
no (tramite un’interfaccia di ingresso) e lo trasferisce nel registro AL.
Il sottoprogramma outbyte invia il byte contenuto nel registro AL
a un dispositivo esterno (tramite un’interfaccia di uscita).
Per comodità si pensi di disporre di un file di nome utility avente
la struttura riportata sotto e contenente i sottoprogrammi inbyte e
outbyte e la direttiva .GLOBAL _main.

File utility
inbyte: ...
...
RET
outbyte:...
...
RET
.GLOBAL _main
IL LINGUAGGIO ASSEMBLER GAS 79

Se il file utility viene incluso alla fine del file contenente il


programma utente, questo può avere la seguente comoda struttura:
# Sezione dati
...
...
# Sottoprogramma principale
_main: ...
...
...
RET
# Sottoprogrammi dell’utente
mysott1: ...
...
RET
...
...
mysottK: ...
...
RET
# Direttiva per includere il file utility
.INCLUDE "utility"

Conteggio del numero di bit a 1


Il seguente programma preleva un byte da un dispositivo esterno
di ingresso, lo elabora costruendo un byte che, interpretato come
espressione in base due di un numero naturale, indica quanti bit 1
sono presenti nel byte iniziale ed invia infine il byte risultato al
dispositivo esterno di uscita. Diremo pertanto che il programma
preleva un byte, conta quanti bit 1 sono presenti in tale byte ed
emette il numero naturale risultato del conteggio.
Il byte da esaminare viene trasferito nel registro DL e il numero
dei bit 1 presenti in esso viene costruito nel registro AL.
Il conteggio dei bit a 1 viene effettuato in più passi, ciascuno dei
quali consiste nel traslare il contenuto del registro DL di una
posizione verso sinistra ed incrementare o lasciare inalterato il
numero contenuto in AL, a secondo che CF venga a contenere 1 o 0
(istruzione ADC $0,%AL).
Il conteggio viene considerato concluso quando il contenuto del
registro DL diventa 0.
80 CAPITOLO II

# Sottoprogramma principale
# Prelievo del byte e suo trasferimento in DL
_main: CALL inbyte
MOV %AL,%DL
# Elaborazione del byte e costruzione
# del risultato in AL
MOV $0,%AL
cycle: CMP $0,%DL
JE send
SHL $1,%DL
ADC $0,%AL
JMP cycle
# Emissione del risultato
send: CALL outbyte
RET
.INCLUDE "utility"

Somma di più numeri naturali a 8 bit


Il seguente programma preleva dal dispositivo esterno di ingres-
so dei byte e li elabora ottenendo come risultato un byte che emette
tramite il dispositivo esterno di uscita. Poiché l’elaborazione viene
fatta interpretando tutti i byte come espressioni in base due di
numeri naturali, diremo che il programma preleva più numeri
naturali a 8 bit (cioè minori od eguali a duecentocinquantacinque), li
elabora ed emette il numero naturale a 8 bit che ottiene come
risultato. Più in dettaglio: il programma preleva dapprima un
numero naturale a 8 bit N e poi N numeri naturali a 8 bit. Somma
modulo 28 gli N numeri naturali ed invia al dispositivo esterno di
uscita il numero naturale a 8 bit, risultato dell’elaborazione.
Nel programma sono dichiarate due variabili di tipo byte che
fungono da “variabili di appoggio”. Alla prima variabile, che ha no-
me numcomp ed è scalare, viene assegnato, come valore, il numero N
dei byte da elaborare. La seconda variabile, di nome vett, è una varia-
bile vettoriale a 255 componenti. All’i-esima componente di tale va-
riabile viene assegnato, come valore, l’i-esimo dei numeri da elabo-
rare (solo N delle 255 componenti vengono ovviamente utilizzate).
Nel programma, il riferimento alle componenti di vett viene
effettuato due volte, ciascuna volta compiendo tanti passi per quanti
sono i byte da elaborare.
IL LINGUAGGIO ASSEMBLER GAS 81

La prima volta il riferimento è effettuato utilizzando l’indirizza-


mento con registro puntatore EDI: il contenuto di tale registro viene
inizializzato con l’offset della prima componente di vett ed
aggiornato ad ogni passo sommandovi uno (le componenti di vett
sono di tipo byte).
La seconda volta, il riferimento è effettuato utilizzando l’indiriz-
zamento con displacement e registro indice ESI: il contenuto di tale
registro viene inizializzato a zero ed aggiornato ad ogni passo
sommandovi uno.
In entrambi i casi il numero N dei byte da elaborare è trasferito
inizialmente nel registro CL, dopo di che, ad ogni passo, il contenuto
del registro CL viene decrementato di uno. Il riferimento alle
componenti di vett è pertanto considerato completo quando il
contenuto di CL è divenuto 0.
# Sezione dati
numcomp: .FILL 1,1
vett: .FILL 255,1
# Sottoprogramma principale
# Prelievo del numero dei byte e sua assegnazione, come
# valore, alla variabile numcomp
_main: CALL inbyte
MOV %AL,numcomp
# Prelievo dei byte e loro assegnazione, come valore,
# alle componenti della variabile vett. Individuazione
# delle componenti tramite indirizzamento con registro
# puntatore EDI
MOV numcomp,%CL
LEA vett,%EDI
read: CALL inbyte
MOV %AL,(%EDI)
INC %EDI
DEC %CL
JNZ read
# Elaborazione dei byte con costruzione del risultato
# nel registro AL. Individuazione delle componenti di
# vett tramite indirizzamento con registro indice ESI
MOV numcomp,%CL
MOV $0,%ESI
MOV $0,%AL
cycle: ADD vett(%ESI),%AL
INC %ESI
DEC %CL
JNZ cycle
82 CAPITOLO II

# Emissione del risultato dell’elaborazione


CALL outbyte
RET
# Direttiva per includere il file utility
.INCLUDE "utility"

Somma di più numeri naturali tramite sottoprogramma:


Il seguente programma è una generalizzazione del precedente.
Esso somma, in tempi successivi, gli elementi di due insiemi di
numeri naturali a 8 bit assegnati come valori alle componenti di due
distinte variabili vettoriali vett1 e vett2.
Le operazioni di somma degli elementi di ciascun insieme, sono
effettuate chiamando un sottoprogramma sommav. Il passaggio dei
parametri da programma chiamante a sottoprogramma e viceversa
avviene in accordo alle seguenti regole:
1) il programma chiamante lascia nel registro CL il numero dei nu-
meri naturali da sommare e nel registro EBX l’indirizzo della
variabile vettoriale alle cui componenti sono stati assegnati, come
valori, i numeri naturali stessi;
2) il sottoprogramma chiamato lascia il risultato della somma nel
registro AL.
# Sezione dati
numcomp1: .FILL 1,1
vett1: .FILL 255,1
numcomp2: .FILL 1,1
vett2: .FILL 255,1
# Sottoprogramma principale
# Elaborazione del primo insieme
# Prelievo del numero dei byte del primo insieme
_main: CALL inbyte
MOV %AL,numcomp1
# Prelievo dei byte del primo insieme
MOV numcomp1,%CL
LEA vett1,%EDI
read1: CALL inbyte
MOV %AL,(%EDI)
INC %EDI
DEC %CL
JNZ read1
IL LINGUAGGIO ASSEMBLER GAS 83

# Preparazione dei parametri per il sottoprogramma


MOV numcomp1,%CL
LEA vett1,%EBX
# Chiamata del sottoprogramma
CALL sommav
# Emissione del risultato
CALL outbyte
# Elaborazione del secondo insieme
# Prelievo del numero dei byte del secondo insieme
CALL inbyte
MOV %AL,numcomp2
# Prelievo dei byte del secondo insieme
MOV numcomp2,%CL
LEA vett2,%EDI
read2: CALL inbyte
MOV %AL,(%EDI)
INC %EDI
DEC %CL
JNZ read2
# Preparazione dei parametri per il sottoprogramma
MOV numcomp2,%CL
LEA vett2,%EBX
# Chiamata del sottoprogramma
CALL sommav
# Emissione del risultato
CALL outbyte
RET
# Sottoprogramma per effettuare la somma
sommav: PUSH %EBX
PUSH %CX
MOV $0,%AL
cycle: ADD (%EBX),%AL
INC %EBX
DEC %CL
JNZ cycle
POP %CX
POP %EBX
RET

# Direttiva per includere il file utility


.INCLUDE "utility"
L’AMBIENTE DI SVILUPPO

1 GENERALITÀ

L’ambiente di sviluppo che abbiamo predisposto e che è


compatibile con il calcolatore C86/32, si basa sull’ambiente GNU e
gira, in un calcolatore host, sotto tutte le versioni del sistema operativo
Windows. In particolare l’ambiente include l’assemblatore per il
linguaggio assembler GAS (GnuASsembler) e il debugger GDB
(GnuDeBugger). I file eseguibili che l’ambiente produce possono
essere lanciati con successo sotto il sistema operativo Windows, a
meno che non si voglia, maliziosamente (in quanto non necessario),
inserire nei programmi istruzioni per accedere allo spazio di I/O o
all’intero spazio di memoria.
L’ambiente di sviluppo può essere scaricato dal sito
www2.ing.unipi.it/~a080368/Programmazione/materiale.ht,
dove è inglobato in un file autoscoppattante di nome amb_GAS.exe
da espandersi in una directory C:/GAS. In tale directory è presente
(fra molti altri) un file di nome installa.bat che va lanciato e che crea
la directory C:/WORK. Questa directory contiene, nella fase iniziale,
le icone degli alias assemble&link e debug e le icone di alcuni file
sorgente con delle demo.
Lavorando nella directory C:/WORK è pertanto possibile: i)
editare e modificare un file sorgente utilizzando gli editor standard
di Windows; ii) produrre un file eseguibile1 spostando l’icona del file
sorgente2 sull’icona dell’alias assemble&link; iii) mettere in esecuzione
il file eseguibile nel calcolatore simulato C82/32,o direttamente
(questo è possibile cliccando sulla icona del file) o attivando il
debugger GDB (questo è possibile spostando l’icona del file
sull’icona dell’alias debug).

1 A meno che non siano stati o commessi errori di sintassi nel predisporre il file
sorgente
2 Occorre comunque evidenziare che non è data la possibilità di assemblare
separatamente più file sorgenti e collegarli insieme in un secondo momento
per ottenere un unico file eseguibile finale.
86 CAPITOLO III

L’assemblatore produce, oltre al file eseguibile, anche file di


nome listato.txt in cui, accanto al programma in linguaggio
assembler, è riportato il programma in linguaggio mnemonico, con
in più una serie di informazioni sugli indirizzi delle istruzioni e degli
operandi. Nel caso si siano commessi errori di sintassi il file listato.txt
evidenzia, in modo molto leggibile, dove tali errori sono stati
commessi.

2 IL DEBUGGER GDB

Il debugger permette di collaudare a run time un programma,


mettendo a disposizione una serie di comandi. Di seguito sono
riportati, in forma estremamente semplificata, i più comuni comandi
impartibili al debugger.
Comando list source
Sintassi: l
Azione: Mostra uno spezzone del file sorgente.

Comando insert breakpoint


Sintassi: b etichetta
Azione: Inserisce un breakpoint all’istruzione individuata dall’eti-
chetta indicata nel comando (che pertanto non viene eseguita) e vi
associa un numero d’ordine.

Comando info breakpoints


Sintassi: info b
Azione: Mostra i breakpoint inseriti; per ogni breakpoint viene
indicato il numero d’ordine ed il numero di linea che ha l’istruzione
corrispondente nel file listato.

Comando delete breakpoint


Sintassi: del numero
Azione: Rimuove il breakpoint avente il numero d’ordine indicato
nel comando. L’omissione del parametro numero comporta la
rimozione di tutti i breakpoint.
L'AMBIENTE DI SVILUPPO 87

Comando run
Sintassi: r
Azione: Mette in esecuzione il programma.
Comando step
Sintassi: s enne
Azione: Mette in esecuzione le enne istruzioni successive a quella
eseguita per ultima. Omettere enne equivale a specificarlo pari a 1.
Comando next
Sintassi: n
Azione: Da usarsi esclusivamente se la prossima istruzione da esegui-
re è l’istruzione CALL. Mette in esecuzione l’intero sottoprogramma.
Comando continue
Sintassi: c
Azione: Mette in esecuzione il programma a partire dall’istruzione
successiva a quella eseguita per ultima.
Comando info registers
Sintassi: info reg
Azione: Mostra il contenuto dei registri.
Comando print register
Sintassi: p $registro
Azione: Mostra il contenuto del registro indicato. Il nome de registro
va scritto con lettere minuscole e non va fatto precedere dal carattere %.
Comando examine memory
Sintassi: x /numerotipo variabile
Azione: Visualizza il valore della variabile riferita. Il parametro
numero indica quante componenti della variabile si intendono esami-
nare; il parametro tipo indica il tipo della variabile (nessun spazio
va inserito tra i due parametri). Le indicazioni di tipo, diverse da
quelle del linguaggio assembler, sono: b per il tipo byte, h per il tipo
word e w per il tipo long. Omettere il parametro numero equivale a
specificarlo come 1; omettere il parametro tipo equivale a specificar-
lo come w. Non si possono tuttavia omettere entrambi i parametri.
88 CAPITOLO III

Comando quit
Sintassi: quit
Azione: Provoca la terminazione del debugger ed il ritorno al
sistema operativo Windows.

Un manuale più completo del debugger GDB è scaricabile da:


www2.ing.unipi.it/~a080368/Programmazione/materiale.html
Il manuale originale è invece scaricabile da:
www.ece.gatech.edu/academic/courses/fall2004/ece3090/handouts
/gdb-reference.pdf

Il debugger GDB non è comodissimo da usare. Una modalità


d’uso, che non dà molti problemi, prevede che: i) sia inserito un
breakpoint sull’istruzione di etichetta _main; ii) sia successivamente
impartito il comando r. In tal modo il programma è pronto ad essere
collaudato usando tutti gli altri comandi del debugger prece-
dentemente illustrati. Per comodità, l’ambiente di sviluppo predi-
sposto nella directory GAS prevede che questi due comandi iniziali
siano automaticamente impartiti all’attivazione del debugger stesso.
Oltre all’uso del debugger, un diffuso metodo di collaudo dei
programmi consiste nella strumentazione del codice, cioè nell’inser-
zione nel file sorgente delle istruzioni che inviino sul monitor
opportuni messaggi o il valore di opportune variabili.

3 I SOTTOPROGRAMMI DI UTILITÀ

Nel Capitolo 2 si è fatto riferimento a un file utility da includere


nel programma sorgente e che ha la seguente struttura:

File utility
inbyte: ...
...
...
RET
outbyte: ...
...
...
RET
.GLOBAL _main
L'AMBIENTE DI SVILUPPO 89

Con tali ipotesi, il sottoprogramma principale deve iniziare con la


prima istruzione utile, la cui etichetta deve essere _main. Poiché il no-
stro ambiente di sviluppo prevede che in sede di debugging sia auto-
maticamente inserito un breakpoint sull’istruzione di etichetta _main,
si consiglia che tale istruzione sia l’istruzione NOP (l’esperienza dimo-
stra che questa raccomandazione, è molto utile).
Un file utility avente le caratteristiche di cui sopra, ma arricchito
con una serie di sottoprogrammi, è presente nella directory C:/GAS.
Esso va quindi incluso, alla fine del file sorgente che contiene il
programma, tramite la direttiva:
.INCLUDE "C:/GAS/utility"

I vari sottoprogrammi di utilità sono ovviamente chiamabili


mediante istruzioni del tipo:
CALL nome_sottoprogramma

Di seguito sono descritti alcuni dei sottoprogrammi presenti nel


file utility e, per ciascuno di essi, sono specificati:
 il nome,
 le azioni che esso compie,
 i parametri che il programma principale deve passargli,
 i parametri che esso ritorna indietro al programma principale,
 eventuali condizioni di mal funzionamento.
90 CAPITOLO III

INGRESSO DI UNA CODIFICA ASCII

Nome: input

Azione
Attende che dalla tastiera arrivi, tramite l’interfaccia di ingresso, la
codifica ASCII di un carattere. Quando ciò avviene immette tale co-
difica nel registro AL.

Parametri da passare al sottoprogramma


Nessuno

Parametri ritornati al programma chiamante


AL <= codifica ASCII ricevuta dall’interfaccia
L'AMBIENTE DI SVILUPPO 91

EMISSIONE DI UNA CODIFICA ASCII

Nome: output

Azione
Invia al monitor, tramite l’interfaccia di uscita, la codifica ASCII di
un carattere.

Parametri da passare al sottoprogramma


AL <= codifica ASCII da emettere tramite l’interfaccia

Parametri ritornati al programma chiamante


Nessuno

Note
Se AL non contiene una codifica ASCII, il simbolo che eventualmente
compare sul monitor è privo di significato.
92 CAPITOLO III

SPOSTAMENTO DEL CURSORE SU UNA NUOVA LINEA

Nome: newline

Azione
Invia al monitor prima il byte 0x0D (codifica ASCII del carattere
Ritorno Carrello) e poi il byte 0x0A (codifica ASCII del carattere
Avanzamento Linea), provocando lo spostamento del cursore dalla
posizione attuale al margine sinistro della linea successiva.

Parametri da passare al sottoprogramma


Nessuno

Parametri ritornati al programma chiamante


Nessuno

Struttura
Utilizza il sottoprogramma output.
newline: PUSH %AX
MOV $0x0D,%AL
CALL output
MOV $0x0A,%AL
CALL output
POP %AX
RET
L'AMBIENTE DI SVILUPPO 93

EMISSIONE DI UN MESSAGIO DI CONTROLLO

Nome: pauseN

Azione
Invia al monitor il messaggio:

Checkpoint number N. Press any key to continue

ed attende che dalla tastiera arrivi, tramite l’interfaccia di ingresso, la


codifica ASCII di un qualunque carattere. Quando ciò avviene torna
al programma chiamante.

Parametri da passare al sottoprogramma


Nessuno
Parametri ritornati al programma chiamante
Nessuno

Note
Il sottoprogramma pause0 ha anche, come nome alternativo, pause.
94 CAPITOLO III

INGRESSO DI UNA LINEA con eco

Nome: inline_eco

Azione
Preleva le codifiche ASCII man mano che arrivano dalla tastiera, le
rinvia (eco) al monitor e le immette in una serie di locazioni conse-
cutive di memoria (buffer). Termina quando riceve il byte 0x0D
(codifica ASCII del carattere Ritorno Carrello), immettendo nel
buffer prima il byte 0x0A (codifica ASCII del carattere
Avanzamento Linea) e poi il byte 0x0D stesso. Il byte 0x08 (codifica
ASCII del carattere Backspace) viene interpretato dal sottopro-
gramma come l’ordine di eliminare dal buffer l’ultima codifica
ASCII che vi è stata immessa.

Parametri da passare al sottoprogramma


EBX <= indirizzo della prima locazione del buffer
CX <= numero delle locazioni costituenti il buffer

Parametri ritornati al programma chiamante


buffer <= codifiche ASCII ricevute dall’interfaccia

Note
Se il numero delle codifiche ASCII ricevute prima di ricevere il byte
0x0D è maggiore del numero specificato in CX diminuito di 2, alcune
codifiche vengono perse.
L'AMBIENTE DI SVILUPPO 95

EMISSIONE DI UNA LINEA

Nome: outline

Azione
Invia al monitor le codifiche ASCII contenute in una serie di
locazioni consecutive di memoria (buffer), fino a che non trova ed
emette il byte 0x0D (codifica ASCII del carattere Ritorno Carrello).

Parametri da passare al sottoprogramma


buffer <= codifiche ASCII da emettere
EBX <= indirizzo della prima locazione del buffer

Parametri ritornati al programma chiamante


Nessuno

Note
Se nel buffer non viene trovata la codifica ASCII del carattere
Ritorno Carrello, non viene segnalato alcun errore, ma il sottopro-
gramma entra in un ciclo in cui emette all’infinito il contenuto di
tutte le locazioni della memoria.
96 CAPITOLO III

EMISSIONE DI UN MESSAGGIO

Nome: outmess

Azione
Invia al monitor le codifiche ASCII contenute in una serie di
locazioni consecutive di memoria (buffer).

Parametri da passare al sottoprogramma


buffer <= codifiche ASCII da emettere
EBX <= indirizzo della prima locazione del buffer
CX <= numero delle codifiche ASCII da emettere

Parametri ritornati al programma chiamante


Nessuno

Note
Se il numero delle codifiche ASCII da emettere è maggiore del
numero delle locazioni del buffer non viene segnalato alcun errore,
ma il sottoprogramma emette anche byte casuali.
L'AMBIENTE DI SVILUPPO 97

INGRESSO con eco DELLA CODIFICA ASCII


DI UNA CIFRA DECIMALE

Nome: inDA1_eco

Azione
Attende che dalla tastiera arrivi la codifica ASCII di una cifra
decimale (cioè appartenente all’insieme 0, 1, 2, 3, 4, 5, 6, 7, 8, 9), e
quando ciò avviene immette tale codifica nel registro AL e la rinvia
(eco) al monitor.

Parametri da passare al sottoprogramma


Nessuno

Parametri ritornati al programma chiamante


AL <= codifica ASCII di una cifra decimale

Struttura
Utilizza i sottoprogrammi input ed output e tratta le codifiche
ASCII come se fossero numeri naturali a 8 bit. Accetta pertanto le
codifiche ASCII che stanno nel range da 0b00110000 (codifica ASCII
di 0) a 0b00111001 (codifica ASCII di 9).

inDA1_eco: CALL input


CMP $48,%AL # '0'=48=0x30=0b00110000
JB inDA1_eco
CMP $57,%AL # '9'=57=0x39=0b00111001
JA inDA1_eco
CALL output
RET
98 CAPITOLO III

INGRESSO con eco DELLA CODIFICA ASCII


DI UNA CIFRA ESADECIMALE

Nome: inHA1_eco

Azione
Attende che dalla tastiera arrivi la codifica ASCII di una cifra
esadecimale (cioè appartenente all’insieme 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A,
B, C, D, E, F). Quando ciò avviene immette tale codifica nel registro
AL e la rinvia (eco) al monitor. Utilizza i sottoprogrammi input ed
output.

Parametri da passare al sottoprogramma


Nessuno

Parametri ritornati al programma chiamante


AL <= codifica ASCII di una cifra esadecimale

Struttura
Utilizza i sottoprogrammi input ed output e tratta le codifiche
ASCII come se fossero numeri naturali a 8 bit. Accetta le codifiche
ASCII che stanno nel range da 48 (codifica ASCII di 0) a 57 (codifica
ASCII di 9) e nel range da 65 (codifica ASCII di A) a 70 (codifica
ASCII di F).
inHA1_eco: CALL input
CMP $48,%AL # '0'=48=0x30=0b00110000
JB inHA1_eco
CMP $70,%AL # 'F'=70=0x46=0b01000110
JA inHA1_eco
CMP $57,%AL # '9'=57=0x39=0b00111001
JBE eco
CMP $65,%AL # 'A'=65=0x41=0b01000001
JB inHA1_eco
eco: CALL output
RET
L'AMBIENTE DI SVILUPPO 99

CONVERSIONE DA BINARIO A ESADECIMALE


(da 4 bit alla codifica ASCII di una cifra esadecimale)

Nome: B4HA1

Azione
Trasforma i 4 bit meno significativi del byte contenuto in AL nella
codifica ASCII della cifra esadecimale che li esprime in forma
compatta (vedi la Tabella a pag. 3 e la Tabella in Appendice).

Parametri da passare al sottoprogramma


AL <= x x x x . . . .

4 bit da elaborare

Parametri ritornati al programma chiamante


AL <= codifica ASCII della cifra esadecimale

Struttura
Dopo aver azzerato i 4 bit più significativi di AL e guardando ai vari
byte come a numeri naturali, deve compiere le seguenti
trasformazioni:
da 0b00000000 a 0b00110000 cioè, in decimale, da 0 a 48
...
da 0b00001001 a 0b00111001 cioè, in decimale, da 9 a 57
e
da 0b00001010 a 0b01000001 cioè, in decimale, da 10 a 65
...
da 0b00001111 a 0b01000110 cioè, in decimale, da 15 a 70
Compie pertanto la prima trasformazione sommando 48 e la
seconda sommando 55.

B4HA1: AND $0x0F,%AL


CMP $9,%AL
JA add_55
add_48: ADD $48,%AL
RET
add_55: ADD $55,%AL
RET
100 CAPITOLO III

CONVERSIONE DA BINARIO A ESADECIMALE


(da 8 bit alle codifiche ASCII di 2 cifre esadecimali)

Nome: B8HA2

Azione
Trasforma gli 8 bit contenuti in AL nelle codifiche ASCII delle 2 cifre
esadecimali che esprimono gli 8 bit in forma compatta (vedi la
Tabella a pag. 3 e la Tabella in Appendice).

Parametri da passare al sottoprogramma


AL <= byte da elaborare

Parametri ritornati al programma chiamante


AH <= codifica ASCII della cifra esadecimale più significativa
AL <= codifica ASCII della cifra esadecimale meno significativa

Struttura
Utilizza il sottoprogramma B4HA1.
B8HA2: PUSH %CX
# Salvataggio del contenuto di AL in CH
MOV %AL,%CH
# Elaborazione dei 4 bit più significativi
SHR $4,%AL
CALL B4HA1
MOV %AL,%AH
# Elaborazione dei 4 bit meno significativi
MOV %CH,%AL
CALL B4HA1
POP %CX
RET
L'AMBIENTE DI SVILUPPO 101

EMISSIONE DI UN BYTE
(da 8 bit alle codifiche ASCII di 2 cifre esadecimali
con emissione delle codifiche ASCII, ovvero B8HA2out)

Nome: outbyte

Azione
Trasforma gli 8 bit contenuti in AL nelle codifiche ASCII delle 2 cifre
esadecimali che esprimono gli 8 bit in forma compatta (vedi la
Tabella a pag. 3 del Capitolo 1 e la Tabella in Appendice) ed invia al
monitor le 2 codifiche ASCII.

Parametri da passare al sottoprogramma


AL <= byte da elaborare

Parametri ritornati al programma chiamante


Nessuno

Struttura
Il sottoprogramma utilizza i sottoprogrammi B8HA2 e output.

outbyte: PUSH %AX


# AH,AL <= due codifiche ASCII
CALL B8HA2
# Invio al monitor delle due codifiche ASCII
PUSH %AX
MOV %AH,%AL
CALL output
POP %AX
CALL output
POP %AX
RET
102 CAPITOLO III

CONVERSIONE DA ESADECIMALE A BINARIO


(dalla codifica ASCII di una cifra esadecimale a 4 bit)

Nome: HA1B4

Azione
Trasforma la codifica ASCII di una cifra esadecimale nei 4 bit che
essa esprime in forma compatta (vedi la Tabella a pag. 3 del Capitolo
1 e la Tabella in Appendice) e deposita i 4 bit così ottenuti nella parte
bassa di AL

Parametri da passare al sottoprogramma


AL <= codifica ASCII della cifra esadecimale

Parametri ritornati al programma chiamante


AL <= 0 0 0 0 . . . .

quadrupla di bit espressa


dalla cifra esadecimale

Note
Se AL non contiene la codifica ASCII di una cifra esadecimale il
risultato è inattendibile.

Struttura
Compie le trasformazioni inverse rispetto al sottoprogramma
B4HA1.
HA1B4: CMP $57,%AL
JA sub_55
sub_48: SUB $48,%AL
RET
sub_55: SUB $55,%AL
RET
L'AMBIENTE DI SVILUPPO 103

CONVERSIONE DA ESADECIMALE A BINARIO


(dalle codifiche ASCII di 2 cifre esadecimali a un byte)

Nome: HA2B8

Azione
Trasforma le codifiche ASCII di 2 cifre esadecimali negli 8 bit che
esse esprimono in forma compatta (vedi la Tabella a pag. 3 del Capi-
tolo 1 e la Tabella in Appendice) e deposita il byte così ottenuto in
AL.

Parametri da passare al sottoprogramma


AH <= codifica ASCII della cifra esadecimale più significativa
AL <= codifica ASCII della cifra esadecimale meno significativa

Parametri ritornati al programma chiamante


AH <= byte casuale
AL <= byte ricavato dalle elaborazione delle 2 codifiche ASCII

Note
Se AH e AL non contengono codifiche ASCII di cifre esadecimali il
risultato è inattendibile.

Struttura
Utilizza il sottoprogramma HA1B4.
HA2B8: PUSH %CX
# Salvataggio del contenuto di AL in CH
MOV %AL,%CH
# Elaborazione della codifica della cifra più significativa
MOV %AH,%AL
CALL HA1B4
MOV %AL,%AH
ROL $4,%AH
# Elaborazione della codifica della cifra meno significativa
MOV %CH,%AL
CALL HA1B4
OR %AH,%AL
POP %CX
RET
104 CAPITOLO III

INGRESSO con eco DI UN BYTE


(prelievo con eco delle codifiche ASCII di 2 cifre esadecimali e
costruzione del byte corrispondente, ovvero inHA2B8_eco)

Nome: inbyte

Azione
Attende che dalla tastiera arrivino le codifiche ASCII di 2 cifre
esadecimali. Quando ciò avviene trasforma le 2 codifiche ASCII negli
8 bit che le due cifre esprimono in forma compatta (vedi la Tabella a
pag. 3 del Capitolo 1 e la Tabella in Appendice) e deposita il byte
così ottenuto in AL. Oltre a ciò fa l’eco sul monitor delle codifiche
ASCII delle 2 cifre esadecimali prelevate.

Parametri da passare al sottoprogramma


Nessuno

Parametri ritornati al programma chiamante


AL <= byte ottenuto dall’elaborazione delle 2 codifiche ASCII
prelevate

Struttura
Il sottoprogramma utilizza i sottoprogrammi inHA1_eco e HA2B8.
inbyte: PUSH %BX
MOV %AH,%BH
# Prelievo con eco delle due codifiche ASCII
CALL inHA1_eco
MOV %AL,%AH
CALL inHA1_eco
# Conversione in binario con risultato in AL
CALL HA2B8
MOV %BH,%AH
POP %BX
RET
L'AMBIENTE DI SVILUPPO 105

CONVERSIONE DA BINARIO A DECIMALE


con emissione delle codifiche ASCII delle cifre decimali

Nome: B16DAN_out

Azione
Interpreta il contenuto di AX come un numero naturale binario, lo
esprime in base dieci su più cifre e poi invia al monitor le codifiche
ASCII delle cifre stesse.

Parametri da passare al sottoprogramma


AX <= numero binario da esprimere in base dieci

Parametri ritornati al programma chiamante


Nessuno

Struttura
Detto A il numero da esprimere in base dieci e dn-1, dn-2, ..., d0
le cifre (incognite) che lo esprimono in tale base, allora:
A = ((dn-1 · dieci + dn-2) · dieci + ... d1) · dieci + d0
Posto:
A[0] = A e A[1] = (dn-1 · dieci + dn-2) · dieci + ... d1
si ha
A[0] = A[1] · dieci + d0
da cui:
d0 = A[0] modulo dieci , A[1] = A[0]/dieci
Iterando, si ricava il seguente algoritmo:
di = A[i] modulo dieci , A[i+1] = A[i]/dieci
per i = 0, 1, ..., m-1 e A[0] = A

che, se si esprime dieci in base due e si opera in base due, permette


di ricavare le varie cifre decimali, espresse esse stesse in base due.
106 CAPITOLO III

B16DAN_out: PUSH %AX


PUSH %CX
PUSH %DX
PUSH %SI
# Calcolo in %CL del numero di cifre decimali
MOV $5,%CL
CMP $9999,%AX
JA prosegui
DEC %CL
CMP $999,%AX
JA prosegui
DEC %CL
CMP $99,%AX
JA prosegui
DEC %CL
CMP $9,%AX
JA prosegui
DEC %CL
# Appoggio del contenuto di CL in CH per successivo utilizzo
prosegui: MOV %CL,%CH
# Immissione in SI del divisore dieci espresso in base due
MOV $0x000A,%SI

# Calcolo della cifra di espressa in base due


ciclo: MOV $0x0000,%DX # Immissione in DX_AX del
# dividendo esteso su 32 bit
DIV %SI # In AX va A[i]/dieci e in DX
# (e quindi in DL) va A[i]
# modulo dieci
# Calcolo e salvataggio nella pila della codifica ASCII
# della cifra
ADD $0x30,%DL
PUSH %DX
# Controllo del ciclo
DEC %CL
JNZ ciclo
# Emissione della codifica ASCII della cifre
emetti: POP %AX
CALL output
DEC %CH
JNZ emetti
POP %SI
POP %DX
POP %CX
POP %AX
RET
L'AMBIENTE DI SVILUPPO 107

CONVERSIONE DA DECIMALE A BINARIO


con ingresso delle codifiche ASCII delle cifre decimali

Nome: inDANB16_eco

Azione
Attende che dalla tastiera arrivino più codifiche ASCII di cifre
decimali. Quando ciò avviene interpreta le cifre come l’espressione
in base dieci di un numero naturale, esprime tale numero in base due
su 16 bit e lo deposita in AX. Oltre a ciò, fa l’eco sul monitor delle
codifiche ASCII delle cifre decimali prelevate.

Parametri da passare al sottoprogramma


CX <= numero delle cifre decimali da gestire (al più 5)

Parametri ritornati al programma chiamante


AX <= numero naturale espresso in base due

Note
Se il numero decimale supera 65535 non sono segnalati errori, ma il
risultato della conversione è inattendibile.

Struttura
Detto A il numero da esprimere in base due e dn-1, dn-2, ..., d0
le cifre (note) che lo esprimono in base dieci, allora:
A = ((0 · dieci + dn-1) · dieci + dn-2) · dieci ... + d0

Posto
A[0] = 0
A[1] = 0 · dieci + dn-1
= A[0] · dieci + dn-1
A[2] = (0 · dieci + dn-1) · dieci + dn-2
= A[1] · dieci + dn-2
...
108 CAPITOLO III

A[m] = ((0 · dieci + dn-1) · dieci + dn-2) · dieci ... + d0


= A[m-1] · dieci + d0
si ha A = A[m]. Pertanto, l’algoritmo:
A[i] = A[i-1] · dieci + dn-i per i = 1,..., n e A[0] = 0
se portato avanti esprimendo dieci e le varie cifre decimali in base
due ed operando in base due, fornisce A = A[n] in base due.

inDANB16_eco: CMP $0,%CX


JZ fine
PUSH %CX
PUSH %DX
PUSH %SI
# Immissione in SI di dieci espresso in base due
MOV $0x000A,%SI
# Immissione in A di A[0] = 0
MOV $0x0000,%AX
# Calcolo in DX_AX e quindi in AX di (A[i-1] * dieci)
ciclo: MUL %SI
# Prelievo della codifica ASCII della cifra decimale dn-i e
# immissione in DX dell’espressione binaria, su 16 bit, di
# tale cifra
PUSH %AX
CALL inDA1_eco
MOV %AL,%DL
POP %AX
AND $0x000F,%DX
# Calcolo in AX di A[i] = (A[i-1] * dieci) + dn-i
ADD %DX,%AX
# Controllo del ciclo
DEC %CX
JNZ ciclo
POP %SI
POP %DX
POP %CX
RET
APPENDICE 109

CODIFICHE ASCII DEI CARATTERI


viste come stringhe di 8 bit

0000 0001 0010 0011 0100 0101 0110 0111

NUL DLE SP 0 @ P ` p 0000

SOH DC1 ! 1 A Q a q 0001

STX DC2 " 2 B R b r 0010

ETX DC3 # 3 C S c s 0011

EQT DC4 $ 4 D T d t 0100

ENQ NAK % 5 E U e u 0101

ACK SYN & 6 F V f v 0110

BEL ETB ' 7 G W g w 0111

BS CAN ( 8 H X h x 1000

HT EM ) 9 I Y i y 1001

LF SUB * : J Z j z 1010

VF ESC + ; K [ k { 1011

FF FS , < L \ l | 1100

CR GS - = M ] m } 1101

SO RS . > N ^ n ~ 1110

SI US / ? O _ o DEL 1111
110 APPENDICE

CODIFICHE ASCII DEI CARATTERI


viste come numeri naturali in base dieci

Simbolo Codifica Simbolo Codifica Simbolo Codifica Simbolo Codifica


NUL 0 SPACE 32 @ 64 ` 96
SOH 1 ! 33 A 65 a 97
STX 2 " 34 B 66 b 98
ETX 3 # 35 C 67 c 99
EOT 4 $ 36 D 68 d 100
ENQ 5 % 37 E 69 e 101
ACK 6 & 38 F 70 f 102
BEL 7 ' 39 G 71 g 103
BS 8 ( 40 H 72 h 104
HT 9 ) 41 I 73 i 105
LF 10 * 42 J 74 j 106
VT 11 + 43 K 75 k 107
FF 12 , 44 L 76 l 108
CR 13 - 45 M 77 m 109
SO 14 . 46 N 78 n 110
SI 15 / 47 O 79 o 111
DLE 16 0 48 P 80 p 112
DC1 17 1 49 Q 81 q 113
DC2 18 2 50 R 82 r 114
DC3 19 3 51 S 83 s 115
DC4 20 4 52 T 84 t 116
NAK 21 5 53 U 85 u 117
SYN 22 6 54 V 86 v 118
ETB 23 7 55 W 87 w 119
CAN 24 8 56 X 88 x 120
EM 25 9 57 Y 89 y 121
SUB 26 : 58 Z 90 z 122
ESC 27 ; 59 [ 91 { 123
FS 28 < 60 \ 92 | 124
GS 29 = 61 ] 93 } 125
RS 30 > 62 ^ 94 ~ 126
US 31 ? 63 _ 95 DEL 127