Default.asm .MODEL small ; specifica il modello di memoria
.STACK ; riserva spazio per lo stack (default = 1 Kbyte)
.DATA ; crea un segmento di dati (di tipo NEAR)
;-------------------------------------------------------------------
; definizione variabili
;-------------------------------------------------------------------
.CODE ; definisce un segmento di codice
.STARTUP ; genera le istruzioni di interfaccia a MS-DOS
;-------------------------------------------------------------------
; definizione codice main
;-------------------------------------------------------------------
.EXIT ; genera le istruzioni di ritorno a MS-DOS
;-------------------------------------------------------------------
; definizione codice procedure
;-------------------------------------------------------------------
END ; fine del modulo da assemblare
Formato istruzioni nel codice sorgente
label: mnemonico operando, operando ;commento
Variabili
Identificano in modo simbolico una zona di memoria contenente dati.
All’atto della definizione della variabile, si definiscono
- il nome
- la dimensione
- il tipo di dato contenuto (eventualmente)
- il valore di inizializzazione (eventualmente)
Pseduo-istruzioni per la Definizione di Variabili
simbolo direttiva espressione
direttive: BYTE (DB), WORD (DW), DWORD (DD), QWORD (DQ)
valore: valore numerico, stringa tra apici, il carattere ?, il costrutto num DUP (val) che repplica num volte il valore val
Costanti
Pseduo-istruzioni per la Definizione di Costantisimbolo EQU espressione
simbolo = espressione
Definiscono costanti simboliche durante l’assemblaggio. Le costanti definite con = possono essere cambiate di valore nel corso del programma, a differenza di quelle definite con EQU.
Modi di indirizzamento
1) Register Addressing
Nell'istr è specificato il nome del registro da utilizzare come operando.
2) Immediate Addressing
Nell'istr stessa è specificato come operando un valore immediato
3) Direct Addressing
Nell'istr è contenuto l'
identificatore di una variabile, corrispondente all'effective address della parola di memoria da utilizzare come operando. Alla variabile può essere sommato o sottratto un
dispacement
4) Register Indirect
L'effettive address di una variabile è contenuto in uno dei registri: Base Register (BX), Index Register (DI oppure SI), Base Pointer (BP). Nell'istruzione viene quindi indicato non il valore della variabile ma l'indirizzo in cui si trova: il reindirizzamento avviene attraverso la notazione:
[BX
]5) Base Relative
6) Direct Indexed
7) Base Indexed
Operatori
Operatori per il calcolo degli attributi di una variabile
OFFSET
OFFSET variabile
Restituisce il il valore dell'offset di una variabile. (in alternativa alla funzione LEA). E' utilizzabile solo su operandi indirizzati direttamente attraverso un nome di variabile e nn ad operandi indirizzati indirettamente.
TYPE
TYPE variabile
Restituisce il numero di byte dell'operando.
LENGTH
LENGTH variabile
Restituisce in numero di unità allocate per l'operando. Ha senso solo per variabili allocate con l'operando DUP
SIZE
SIZE variabile
Restituisce lo spazio di memoria utilizzato dall'operando. (SIZE = LENGTH * TYPE)
SEG
SEG variabile
Restituisce l'adr di inizio del segmento a cui appartiene l'operando variabile.
Operatori che modificano il tipo di una variabile
PTR
tipo PTR variabile
Forza l'assemblatore a modificare per l'istruzione corrente il
tipo del dato avente come identificatore
nome.
Operatori aritmetici, logici e relazionali
MOV
MOV < registro|memoria > , < registro|memoria|valore imm. >
I dati vengono letti dall’operando sorgente e memorizzati nell’operando destinazione.
XCHG
XCHG < registro|memoria > , < registro|memoria >
Esegue lo scambio tra due registri o tra un registro ed una locazione di memoria.
Gli operandi devono essere dello stesso tipo
LEA
LDS
LES
PUSH e POP
PUSH < registro|memoria >
POP < registro|memoria >
PUSH: trasferisce una word dall'operando sorgente all'elemento dello stack indirizzato da SP che viene incrementato di 2.
POP: trasferisce una word dall'elemento dello stack indirizzato da SP all'operando destinazione e decrementa SP di 2.
PUSHA
POPA
Eseguono le operazioni di push e pop di tutti i registri
general purpose (AX, BX, CX, DX, SP, BP, SI, DI)
NB: il valore del registro SP caricato nello stack è pari al valore che tale registro assume prima del caricamento del primo registro.
PUSHF
POPF
Permettono di salvare e di ripristinare i 16 bit della parola di stato (PSW)
SAHF LAHF
SAHF
LAHF
Permettono di accedere al valore dei flag memorizzandoli e prelevandoli dal registro AH.
LAHF: trasferisce i valori dei flag SP, ZF, AF, e CF nel registro AH
SAHF: trasferisce i valori di alcuni bit del registro AH nei flag SF, ZF, AF e CF.
ADD
ADD < registro|memoria > , < registro|memoria|valore imm. >
Esegue un’addizione tra l’operando dest e l’operando sorg e scrive il risultato nell’operando dest; l’operando sorg rimane immutato.
Modifica il valore di tutti i flag (AF, PF, CF, SF, OF, ZF).
Gli operandi devono essere dello stesso tipo.
ADC
ADC < registro|memoria > , < registro|memoria|valore imm. >
Somma al contenuto dell’operando dest il contenuto dell’operando sorg ed il valore del flag CF.
NB: Somma tra numeri interi su 32 bit
Per eseguire le operazioni aritmetiche di somma tra numeri di tipo doubleword occorre sommare coppie di word, cominciando da quella meno significativa: si sommano le due word meno significative utilizzando l’istruzione ADD, quindi si sommano le due word più significative utilizzando l’istruzione ADC.
SUB
SUB < registro|memoria > , < registro|memoria|valore imm. >
Esegue una sottrazione tra l’operando dest e l’operando sorg e scrive il risultato nell’operando dest; l’operando sorg rimane immutato.
Modifica il valore di tutti i flag (AF, PF, CF, SF, OF, ZF).
Gli operandi devono essere dello stesso tipo.
SBB
SBB < registro|memoria > , < registro|memoria|valore imm. >
Esegue la sottrazione tra l’operando dest e l’operando sorg; il valore del flag CF viene sottratto al risultato ed il valore ottenuto viene copiato nell’operando dest; l’operando sorg rimane immutato.
INC e DEC
INC < registro|memoria >
DEC < registro|memoria >
L’istruzione INC incrementa operando di un’unità e copia il risultato in operando stesso.
L’istruzione DEC decrementa operando di un’unità e copia il risultato in operando stesso.
Le due istruzioni aggiornano tutti i flag di stato tranne il flag CF.
CBW
CBW
Converte un byte nella word equivalente. (Caricare byte in AL, conversione a word in AX)
Utile quando si vuole eseguire un’operazione di addizione o sottrazione tra un numero memorizzato in un byte ed un numero memorizzato in una word.
CWD
CWD
Converte una word nella double word equivalente. (Caricare word in AX, conversione in double word in DX:AX)
Utile quando si vuole eseguire un'operazione di divisione tra due numeri a 16 bit.
NEG
NEG < registro|memoria >
Cambia il segno di operando, che si assume rappresentato in complemento a 2.
L’istruzione NEG aggiorna lo stato di tutti i flag di stato.
MUL e IMUL
MUL < registro|memoria >
IMUL < registro|memoria >
Permettono di eseguire l’operazione di moltiplicazione tra numeri interi senza segno (MUL) e con segno (IMUL). I fattori della moltiplicazione devono essere dello stesso tipo.
L'operando specificato nella MUL (o nella IMUL) verra moltiplicato per
il numero che è contenuto nell'accumulatore (AX per i 16bit, AL per gli
8bit o EAX per i 32bit).
Il risultato dipende dagli operandi:
1byte * AL risultato in AX (CF=0 e OF=0 se e solo se AH=0)
2byte * AX risultato in DX:AX (CF=0 e OF=0 se e solo se DX=0)
4byte * EAX risultato in EDX:EAX (CF=0 e OF=0 se e solo se EDX=0)
DIV e IDIV
DIV < registro|memoria >
IDIV < registro|memoria >
Permettono di eseguire l'operazione di divisione tra numeri interi senza segno (DIV) e con segno (IDIV).
Divisione 16bit / 8bit: dividendo in AX, divisore specificato nell'operando. Risultato divisione in AL, resto in AH. NB il dividendo viene perso!
Divisione 32bit / 16bit: dividendo in DX:AX, divisore specificato nell'operando (no reg AX o DX). Risultato in AX e resto in DX.
Divisione 16bit / 16bit: si deve convertire con CWD il dividendo espandendolo a 32bit.
Istruzioni di salto
JMP
JMP < registro|memoria >
Salto incondizionato all'istruzione specificata in destinazione (
label)
CMP
CMP < registro|memoria > , < registro|memoria >
Esegue il confronto tra l'operando destinazione e l'operando sorgente. (esegue la sottrazione tra l'operando destinazione e l'operando sorgente, senza restituire il risultato). Aggiorna opportunamente tutti i flag di stato.
JXXX
JXXX < registro|memoria >
XXX è un suffisso ke specifica le condizioni dei flag
Confronto tra numeri con segno
| Confronto tra numeri senza segno |
|
JL
| JB
| dest < sorg
|
JLE
| JA
| dest <= sorg |
JG
| JBE
| dest > sorg |
JGE
| JAE
| dest >= sorg |
JE
| JE
| dest == sorg |
JNE
| JNE
| dest <> sorg
|
JZ
| ZF = 1
|
JNZ
| ZF = 0
|
JS
| SF = 1
|
JNS
| SF = 0
|
JO
| OF = 1
|
JNO
| OF = 0
|
JC
| CF = 1
|
JNC
| CF = 0
|
JP
| PF = 1
|
JNP
| PF = 0
|
Procedure
Il massimo livello di annidamento delle procedura è limitato dalle dimensioni dello stack.
Definizione di procedure
La definizione di una procedura comincia e termina con una direttiva. Vanno dichiarate alla fine del programma
< nome_procedura > PROC < NEAR | FAR > ; se il tipo viene omesso viene assunto NEAR di default
; corpo della procedura
< nome_procedura > ENDP
Una procedura è di tipo NEAR se è richiamabile solo all'interno dello stesso segmento di codice, mentre una procedura di tipo FAR può essere chiamata da procedure appartenenti a segmenti di codice diversi.
NB occorre fare in modo che ogni procedura esegua come prima operazione il salvataggio nello stack di tutti i registri ke vengono da essa modificati. E prevederne un ripristino al termine della procedura. Essendo lo stack una coda di tipo LIFO l'ordine delle istr POP deve essere l'inverso di quello delle istr PUSH.
Gestione di una procedura
Chiamata di una procedura
CALL < nome_procedura >
Trasferisce il controlle del flusso del programma alla procedura
nome_procedura.
L'istr CALL salva nello stack il valore del registro IP (eventualmente anche di CS per le procedure di tipo FAR) per poter riprendere l'esecuzione della procedura chiamante con l'istr successiva a quella di chiamata terminata la procedura. Pone quindi in IP l’indirizzo specificato da
nome_procedura, che rappresenta l’adr della prima istr della procedura. Salta all’indirizzo specificato da IP
Ritorno di una procedura
RET < pop_value > ; pop_value optional
Permette di restituire il controllo alla procedura chiamante, una volta che la procedura chiamata ha terminato l'esecuzione. Sempre presente al termine di una procedura.
L'operando
pop_value è opzionale e corrisponde ad un valore immediato; esso permette di eseguire l'operazione di liberazione dello stack al momento del ritorno alla procedura chiamata di un numero di byte pari a
pop_value.
L'istr RET recupera dallo stack il valore dell’indirizzo di ritorna e lo copia in IP (assumento che l'adr di ritorno sia in cima allo stack). Estrazione dallo stack di
pop_value byte. Salta quindi al'adr specificato da IP.
Passaggio di parametri
La procedura chiamante può passare in entrata alla procedura chiamata alcuni valori, detti parametri. La procedura chiamata può passare in uscita alcuni parametri alla procedura chiamante.
Esistono diverse modalità di passaggio di parametri:
- Tramite registri (Veloce, Limitato)
- Tramite stack (Più lento (accesso alla memoria), Più generale)
I possibili metodi con cui i parametri vengono trasferiti alle procedure si caratterizzano in due categorie fondamentali:
- Trasferimento per valore (
by value): la procedura chiamante passa a quella chiamata una copia del valore del parametro. La procedura chiamante non vede le modifiche effettuate sul parametro
dalla procedura chiamata, in quanto le modifiche compiute da questa avvengono esclusivamene sulla copia.
- Trasferimento per riferimento (
by reference): la procedura chiamante passa alla procedura chiamata l’indirizzo di memoria del parametro: ogni modifica del parametro effettuata dalla procedura chiamata si ripercuote sul valore del parametro per la procedura chiamante.