Programmazione
Assembly 80x86
Intel 8086
•L'Intel 8086 è un microprocessore a 16 bit
progettato dalla Intel nel 1978, che diede origine
all'architettura x86. È basato sull'8080 e
sull'8085 (è compatibile con l'assembly
dell'8080), con un insieme di registri simili, ma a
16 bit.
Architettura X86
•Con x86 si intende l’architettura di microprocessori inizialmente sviluppata
dall’azienda Intel negli anni ’70, e che è ancora oggi predominante sul
mercato mondiale. Altre importanti aziende producono calcolatori basati su
questa tecnologia, i cui diritti sono stati a suo tempo venduti, ad esempio
AMD.
•Il nome x86 deriva dal primo microprocessore della serie, Intel 8086 (1976),
a cui sono seguite numerose versioni via via più potenti che hanno mantenuto
il suffisso nel nome: 8088 (1979), 80186 (1980), 80286 (1982), 80386
(1986), 80486 (1989).
•Sono da considerarsi macchine x86 anche i modelli successivi all’80486, che
hanno dovuto rinunciare al nome ‘numerico’ per l’impossibilità di
brevettarlo: Pentium (o P5, 1993), Pentium Pro (o P6, 1995), Pentium II
(1997), Pentium III (1999), Pentium 4 (o P7, 2000), Pentium M (2003),
Pentium D (2005), Core 2 Duo (o P8, 2006).
•Tutti i processori di questa famiglia sono tutti retrocompatibili e, in
particolare, tutti ancora in grado di eseguire le istruzioni originali dell’ISA
primitiva del progenitore, l’Intel 8086, benché esso fosse una macchina a 16
bit, mentre le ultime citate sono tutte a 32 bit e, in parte, a 64 bit.
L’evoluzione: Core i7 Extreme
•Core i7 Extreme è il nome commerciale di una serie di
microprocessori x86 di nona generazione sviluppati da Intel e
presentati il 17 novembre 2008.
•Le CPU Core i7 Extreme, insieme alle controparti di fascia medio alta
Core i7, sono state le prime incarnazioni della nuova architettura
Nehalem, successiva alla Intel Core Microarchitecture, e che andrà
progressivamente a sostituire in tutti i settori di mercato,
prendendo gradualmente il posto dei Core 2 Duo, Core 2 Quad e Core
2 Extreme.
•Come ormai abitudine da parte di Intel, i processori "Extreme"
vengono proposti per la fascia più alta del mercato desktop (e in un
secondo tempo anche mobile), e oltre ad avere valori di clock più
elevati, vengono anche accompagnati dalla presenza del moltiplicatore
sbloccato sia verso il basso che verso l'alto in modo da semplificare
le operazioni di overclock tipiche di questa fascia di utenti.
La Famiglia X86:Tabella riassuntiva
Architettura 8086
•L'Intel 8086, è un microprocessore a 16 bit
(ampiezza dei registri e del Dbus) con 20 linee
sull’Abus per un totale di 1Mbyte di spazio di
indirizzamento fisico (220 = 1048756 celle).
•L'unità di interfaccia con il bus o Unità di
Controllo è denominata BIU (Bus Interface Unit),
e passa le istruzioni all'ALU (detta EU da
Execution Unit.
Architettura 8086
Execution Unit
(EU)
AH
AL
BH
BL
CH
CL
DH
DL
Address bus (20 bits)
General purpose
register
SP
Segment
register
BP
SI
DI
ALU Data bus
(16 bits)
ALU
Flag register

EU
control
CS
DS
Data bus
(16 bits)
SS
ES
IP
Instruction Queue
Bus
control
External bus
Bus Interface Unit (BIU)
Architettura 8086
•L'Intel 8086 possiede 14 registri da 16 bit, di cui quattro registri per
uso generico (AX, BX, CX, DX), a cui si può accedere anche come se
fossero otto registri a 8 bit (AH e AL, BH e BL, CH e CL, DH e DL), due
registri indice per indirizzare in memoria (SI, DI) e due registri dedicati
alla gestione dello stack (BP e SP).
•A questi si aggiungono altri quattro registri detti di segmento (CS, ES,
DS e ES), dedicati specificatamente all’indirizzamento della Memoria.
Completano il set di registri l’Instruction Pointer IP e il registro PSW
(Program Status Word), denominato Flag register.
•Lo spazio degli indirizzi di I/O si avvale di un indirizzamento a 16 bit, per
un totale di 64KByte (216 = 65536) registri di Input/Output disponibili.
Completa la sezione di I/O un set di 8 linee di interruzione hardware (poi
ampliato a 16) e un canale DMA per dispositivi di I/O con ampio traffico.
•La frequenza originale del clock di CPU valeva 4,77 MHz.
Registri
Registri
• 4 registri di uso generale, pur utilizzati frequentemente come registri di
memorizzazione temporanea (a 16 o a 8 bit), sono dedicati a precisi compiti e sono
coinvolti implicitamente in numerose istruzioni.
– AX, o registro Accumulatore, è predisposto per le istruzioni aritmetiche (somme, sottrazioni, moltiplicazioni e
divisioni).
– BX, o registro Base, è l’unico dei registri di uso generale che può specificare un indirizzo di memoria
– CX, o registro Contatore, è utilizzato implicitamente nelle istruzioni di conteggio dei cicli
– DX, o registro di I/O consente di indirizzare le porte di I/O. Usato anche in moltiplicazioni e divisioni.
• 2 registri indice sono usati nelle istruzioni per manipolare array di caratteri (stringhe).
• SI, o registro Indice Sorgente, specifica l’indirizzo da cui leggere l’array
• DI, o registro Indice Destinazione, specifica l’indirizzo in cui scrivere l’array.
• 2 registri dedicati allo stack sono in grado di indirizzare in memoria, anche se non
liberamente.
– BP, o Base Pointer, contiene l’indirizzo di partenza della pila di stack, per poter gestire il passaggio dei parametri delle procedure
– SP, o Stack pointer, contiene sempre l’indirizzo di memoria dell’ultimo elemento sullo stack.
• IP e Flag, sono registri non modificabili esplicitamente.
– IP, o Instruction Pointer, contiene la parte meno significativa dell’indirizzo della prossima istruzione da eseguire. Il programmatore
non lo modifica mai.
– Flag, o registro dei Flags, è l’unico registro intepretato a singolo bit, ove ogni bit ha un significato differente e concorre a
descrivere lo stato attuale del Processore dopo l’esecuzione dell’ultima istruzione.
Visione stratificata
Livelli superiori
Applicazioni Assembly
Sistema Operativo
BIOS
Hardware (X86, RAM e periferche)
Il BIOS
•Il BIOS (Basic Input Output System) è uno
strato di software utilizzato per
standardizzare l'accesso ai periferici
•Fornisce un insieme di procedure standard di
interfaccia
•Permette la gestione a basso livello di:
–video, tastiera, mouse, stampante,…
Linguaggio Macchina
•Insieme di istruzioni eseguibili dalla CPU
•Dipende dalla CPU:
–cablata al suo interno, ogni istruzione genera una
sequenza di segnali di controllo
•Linguaggio di basso livello
–si può accedere direttamente alle funzionalità di base
del calcolatore
•Complesso da utilizzare:
–ogni istruzione esegue un'operazione semplicissima
–esistono librerie con procedure generali
•Gli altri linguaggi vengono "convertiti" in sequenze di
istruzioni in linguaggio macchina
Linguaggio Macchina
•Il Linguaggio Macchina è estremamente efficiente
•I programmi sono:
–più veloci
–più corti
–ma più complessi
•La scrittura è molto artificiosa:
–istruzioni formate da stringhe di 1 e 0: quindi è
necessario un linguaggio simbolico (Assembly) che
operi a un livello di astrazione più alto
–per referenziare le locazioni di memoria è
necessario avere delle etichette
–necessario commentare ogni istruzione
Dal sorgente all’eseguibile
file sorgenti Assembler
ASM
file
Syntax check
Traduzione in
Linguaggio
macchina
librerie
OBJ
file
OBJ
file
Linker
EXE
file
File
eseguibile
Definizione di Assembler
•Un assembler (assemblatore in italiano) è un
software che trasforma le istruzioni mnemoniche
dell'assembly in linguaggio macchina.
•Si tratta dunque di un compilatore per un particolare
linguaggio assembly.
•Il termine assembler deriva dal fatto che le
istruzioni vengono convertite e montate una accanto
all'altra come se fossero in fila.
•Ci sono molti tipi di linguaggi assembly e di
conseguenza diversi assemblatori: esistono gli
assembler per programmare i microchip, per creare
programmi sul Personal Computer, per telefoni
cellulari, ecc. Questo perché un assemblatore
produce codice assembly per una specifica famiglia di
processori (intel 8086, 80386, Motorola 68000,
ecc.).
Le fasi dell’assemblatore
Determinazione
degli indirizzi
dei simboli
Generazione
codice
Prima passata
•Il programma viene analizzato e il Location
Counter viene aggiornato di volta in volta in
base alla lunghezza di ogni singola istruzione
(utilizzando una tabella con i codici delle
istruzioni e la loro lunghezza).
•Viene generata la Tabella dei simboli e delle
costanti. (ad ogni simbolo o costante viene
associato il relativo indirizzo)
Seconda passata
•Utilizzando le tabelle costruite al passo
precedente viene generato il programma
oggetto
Caricatore (linker)
•Il linking (letteralmente "collegamento”) è il
procedimento di integrazione dei vari moduli
a cui un programma fa riferimento (i quali
possono essere sottoprogrammi o librerie),
per creare una singola unità eseguibile.
•Il linker (o link editor) è un programma che
effettua il collegamento tra il programma
oggetto, cioè la traduzione del codice
sorgente in linguaggio macchina, e le librerie
del linguaggio necessarie per l'esecuzione del
programma.
Caricatore (loader)
•Viene individuata una zona di memoria in cui
caricare il programma.
•Il programma viene caricato in questa zona di
memoria e vengono risolti gli indirizzamenti
•A questo punto il programma può essere
esguito
Esempio di Pogramma Assembly
;NUMOFF.ASM: Turn NUM-LOCK indicator off.
Commenti
.MODEL SMALL
.STACK
Direttive all’assemblatore
.CODE
.STARTUP
MOV
AX,40H
;set AX to 0040H
D1: MOV
DS,AX
;load data segment with 0040H
MOV
SI,17H
AND
BYTE PTR [SI],0DFH ;clear NUM-LOCK bit
.EXIT
END
Label - Etichette
Istruzioni
;load SI with 0017H
Direttive all’assemblatore
Commenti
•Servono a rendere il programma più
comprensibile al programmatore e a chi lo
analizzerà in futuro
•Vengono ignorati dalla macchina
•Devono essere utili ed esplicativi
Direttive per l’Assemblatore
•
SEGMENT directive
•
ENDS directive
•
END directive
•
ORG directive
•
DB: Define Byte; DW, ….
•
ASSUME directive
Specificano I registri di segmento che saranno
utilzzati per calcolare gli indirizzi effettivi per tutte le
etichette e le variabili devinite all’interno di un
determinato segmento o gruppo
Direttive per l’Assemblatore
File Sorgente
List File
DATA SEGMENT PARA 'DATA‘
ORG
ORG
DB
16 DUP(?)
?
DATA ENDS
CODE
DATA SEGMENT PARA 'DATA’
7000H
POINTS DB
SUM
0000
SEGMENT PARA 'CODE‘
7000 0010 [00]
POINTS DB
7010
SUM
00
TOTAL:
8000H
MOV
AX,7000H
MOV
DS,AX
MOV
AL,0
•••••••••
CODE ENDS
END TOTAL
DB
7011
DATA ENDS
0000
CODE
ASSUME CS:CODE, DS:DATA
ORG
7000H
16 DUP(?)
?
SEGMENT PARA 'CODE'
ASSUME CS:CODE, DS:DATA
ORG
8000H
8000 B8 7000 TOTAL: MOV
AX,7000H
8003 8E D8
MOV
DS,AX
8005 B0 00
MOV
AL,0
•••••••••
Istruzioni Macchina
•Composte da:
–etichette (per eliminare riferimenti ad
indirizzi fisici, facilitano le modifiche)
–codici operativi (non può mai mancare)
–operandi (0, 1 o 2)
START:
EQUAL:
MOV
AX, BX
CMP
AX, 12h
JZ EQUAL
INT 21h
RET
MOV
BL, 82h
Descrizione istruzioni
•Il costruttore fornisce delle tabelle che
descrivono l'esatto comportamento delle
istruzioni
–operazione effettuata
–side effects
–tempo di esecuzione
–codifica
Codifica delle istruzioni
•Problema:
–rappresentare l'instruction set con
opportune stringhe di bit
–particolare riguardo anche alle prestazioni
•Soluzione:
–codifiche con lunghezza variabile
–codifica di Huffman
Tipi di istruzioni
(Intel x86)
•Trasferimento dati
•Aritmetiche e logiche
•Manipolazione di bit
•Manipolazione di stringhe
•Trasferimento di controllo
•Manipolazione di interruzioni
•Controllo del processore
Trasferimento dati
•Servono per trasferire dati tra:
–registri
–memoria
–unità esterne
•MOV AX, BX •PUSH AX
-
MOV AX, [indirizzo]
POP BX
Aritmetiche
•Somme, sottrazioni, confronti, (moltiplicazioni,
divisioni)
•Side effect sui flag (AF, PF, CF, SF, OF, ZF).
•ADD AX, BX
•ADC AX, BX
•MUL BX
(macchine "a 0, 1, 2 indirizzi")
•CMP AX, BX
Restrizioni sulle istruzioni
aritmetiche
–Gli operandi devono essere dello stesso tipo (o entrambi byte o
entrambi word).
–L’operando destinazione può essere un registro, oppure una
locazione di memoria.
–L’operando sorgente può essere un registro, una locazione di
memoria, oppure un valore immediato.
–Non è lecito eseguire l’istruzione tra due locazioni di memoria.
ADD
VAL1, VAL2
Si può sostituire con:
MOV
ADD
AH, VAL2
VAL1, AH
; ERRORE !!!
Logiche
•And, Or, Xor, Not, Test
•AND AX, BX
•OR AX, BX
•TEST AX, 01100100b
Le istruzioni INC e DEC e NEG
INC
DEC
NEG
operando
operando
operando
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.
L’istruzione NEG cambia il segno di operando, che si assume
rappresentato in complemento a 2.
L’operando puè essere un registro oppure il contenuto di una
locazione di memoria.
Manipolazione di bit
•Traslazioni e Rotazioni delle configurazioni
binarie
•Traslazioni: SHL, SHR (shift sinistro o destro)
•Traslazioni aritmetiche: SAL, SAR
•Rotazioni: ROL, ROR (rotazione sinistra o
destra)
•Rotazioni con carry: RCL, RCR
Manipolazione di stringhe
•Spostamento, confronto, ricerca,…
•Utilizzano due registri puntatori e un
registro contatore
•MOVS [ethchetta], [etichetta]
•CMPS [ethchetta], [etichetta]
•SCAS [ethchetta], [etichetta]
Trasferimento del controllo
•Salti condizionati, incondizionati, chiamate e
ritorni da procedure
•JZ [etichetta]: salta se zero (zero flag set)
•JC [etichetta]: salta se flag Carry è settato
–diverse combinazioni
•JMP [etichetta]: salto incondizionato
•CALL [procedura]: chiamata di procedura
•RET: ritorno da procedura
Istruzioni di salto su base flag
Salto su base esito comparazione
•Numeri con segno:
•Numeri senza segno
Istruzione LOOP
Manipolazione delle interruzioni
•Concetto di Interrupt
•Interrupt hardware e software
•La Interrupt Service Routine (ISR) è
paragonabile ad una procedura ma:
–è attivabile via hardware
–non specifica l'indirizzo della procedura,
che è quindi modificabile
–durante l'esecuzione disabilita le
interruzioni
Controllo del processore
•Servono a modificare il comportamento della
CPU
•Modificano i flag di controllo: CLC, STC, CMC
(agiscono sul flag C), …
•Sincronizzazione: NOP, HLT, ESC, LOCK, …
Procedure assembly chiamabili da
programmi esterni
•Al fine di poter linkare una procedura
Assembler con un programma chiamante C
occorre che ci sia compatibilità tra i
segmenti usati.
•È necessario utilizzare lo stesso modello di
memoria sia per il modulo C che per il
modulo Assembler: la procedura Assembler
va dichiarata NEAR per modelli tiny, small
e compact, mentre va dichiarata FAR per
modelli medium, large o huge.
Dichiarazione della procedura
•Il nome della procedura Assembler deve
essere reso pubblico tramite una
dichiarazione PUBLIC, così come il nome
di ogni altra variabile che si vuole
rendere accessibile dall’esterno.
•I nomi di tutte le variabili e procedure
definite esternamente al modulo
Assembler e da esso utilizzate vanno
dichiarate esterne attraverso la
direttiva EXTRN.
Convenzione per i nomi
•Il compilatore altera il nome degli identificatori
prima di memorizzarli nel file oggetto.
•Tutti i nomi delle entità comuni ai moduli C ed a
quello Assembly devono tener conto del fatto
che il compilatore C premette sempre, nella
costruzione della symbol table, un carattere ‘_’.
•Il nome della procedura Assembly deve iniziare
con tale carattere, così come quello di tutte le
variabili pubbliche utilizzabili dal modulo C.
Convenzioni per i nomi
(segue)
•Utilizzando l’opzione di linguaggio nella direttiva
.MODEL, l’assemblatore aggiunge il carattere _
davanti a tutti gli identificatori del modulo
Assembly.
•Il nome della procedura chiamata e tutte le
variabili globali definite nel modulo Assembler
devono essere dichiarate come extern all’interno
della procedura C.
•È compito del programma chiamante C svuotare lo
stack dello spazio destinato ai parametri di
ingresso. Tale operazione è effettuata dal
compilatore C in maniera automatica.
Compatibilità del tipo di dato
•Il linguaggio C presenta una molteplicità di
tipi di dato, mentre il linguaggio Assembly
presenta un numero ristretto di possibili
tipi di dato:
C
MASM
char
BYTE
short, int
WORD
long, float
DWORD
double
QWORD
long double
TBYTE
Compatibilità del tipo di dato
(segue)
•I puntatori in C specificano indirizzi di variabili
o di funzioni. In base al modello di memoria
utilizzato un puntatore occupa una word
(puntatore di tipo NEAR) oppure una doubleword
(puntatore di tipo FAR).
modello
punt. a funzione
punt. a dato
tiny
WORD
WORD
small
WORD
WORD
medium
DWORD
WORD
compact
WORD
DWORD
large
DWORD
DWORD
huge
DWORD
DWORD
Procedure:Convenzione
su parametri in ingresso
I parametri sono passati alle procedure
mettendoli nello stack in ordine inverso
rispetto a quello in cui appaiono nella
chiamata.
Ai parametri si può fare accesso attraverso il
registro BP. Le prime istruzioni da eseguire
all’interno della procedura Assembler sono le
seguenti:
PUSH
BP
MOV
BP, SP
Variabili locali
All’interno della procedura può essere allocato
spazio per eventuali variabili locali, così come
accade nei linguaggi di alto livello.
Per fare questo è necessario riservare un’area
dello stack utilizzabile per la memorizzazione di
variabili locali.
Tale operazione può essere fatta o con un
numero opportuno di istruzioni PUSH, oppure
decrementando il contenuto di SP attraverso
un’istruzione SUB.
Salvataggio dei registri
•Il compilatore C tipicamente richiede che
eventuali procedure chiamate da un
programma C non modifichino i valori
contenuti nei registri SI, DI, SS, DS e BP.
•Nel caso in cui tali registri debbano essere
utilizzati, devono essere opportunamente
salvati nello stack e poi ripristinati al
termine.
Frame
Parametro n
...
Parametro 1
BP
Indirizzo di ritorno
Registro BP
Area locale di dati
Registri salvati
SS
...
SP
Convenzioni sui parametri di uscita
Il parametro eventualmente ritornato dalla
procedura Assembler è atteso dal chiamante
nel registro accumulatore.
Se il tipo del dato di ritorno è un char il
parametro è passato attraverso il registro AL;
se il tipo è un int od un indirizzo di tipo NEAR il
registro utilizzato è AX; se il tipo è un long od
un indirizzo di tipo FAR il parametro di ritorno
è copiato nella coppia di registri DX, AX.
Uscita dalla procedura
Le operazioni da effettuare a conclusione della
procedura sono:
–ripristinare i valori dei registri
eventualmente salvati all’inizio;
–liberare l’area locale di dati incrementando
opportunamente il contenuto del registro
SP;
–eseguire l’istruzione RET.
Esercizio
•Calcolo di un’espressione aritmetica.
•Si vuole scrivere una procedura
Assembler di nome power2 richiamabile
da un programma scritto in linguaggio C
per il calcolo dell’espressione X*2Y.
•Alla procedura power2 vengono passati i
due parametri interi X e Y; la funzione
restituisce nel registro AX il risultato
dell’espressione. Si supponga che il
programma chiamante sia compilato
usando il modello di memoria small.
Programma C chiamante
#include <stdio.h>
extern int power2 (int factor, int
power);
void main()
{
printf(”3 volte 2 elevato 5=%d\n”,
power2(3,5));
}
Procedura Assembler
PUBLIC _power2
.MODEL small
.CODE
_power2 PROC
PUSH BP
MOV BP, SP
MOV AX, [BP+4]
MOV CX, [BP+6]
SHL AX, CL
POP BP
RET
_power2 ENDP
END
; primo parametro
; secondo parametro
Esercizio
•Si vuole eseguire una procedura Assembler
di nome invert richiamabile da un
programma scritto in linguaggio C per
l’inversione del contenuto di una stringa: al
termine dell’esecuzione, gli elementi del
vettore devono essere memorizzati
nell’ordine inverso rispetto a quello iniziale.
Programma C chiamante
#include <stdio.h>
extern char *invert (char * str);
void main()
{
char *s;
s = strdup(”Salve Mondo !”);
printf(”%s\n”, invert(s));
}
Procedura Assembler
PUBLIC _invert
.MODEL small
.CODE
_invert PROC
PUSH BP
MOV BP, SP
PUSH SI
PUSH DI
MOV AX, DS
MOV ES, AX
MOV DI, WORD PTR [BP+4]
MOV SI, DI
XOR AX, AX
; ax = 0
MOV CX, 0FFFFH
REPNE SCASB ; cerca ‘\0’
SUB DI, 2
NOT CX ; cx =
strlen+1
DEC CX
SHR CX, 1
ciclo: MOV AH, [SI]
XCHG AH, [DI]
MOV [SI], AH
INC SI
DEC DI
LOOP
ciclo
MOV
AX, WORD PTR [BP+4]
POP
DI
POP
SI
POP
BP
RET
_invert ENDP
END
Accesso alla memoria
•Il metodo di indirizzamento definisce il
meccanismo per ottenere i dati:
–in registri
–nell'istruzione stessa
–in memoria
–su una porta di I/O
Modi di indirizzamento
•Immediato:
–l'operando compare direttamente
nell'istruzione come costante
–è utilizzato solo per operandi 'sorgente’
Modi di indirizzamento
•Assoluto:
–Nell'istruzione compare l'indirizzo
effettivo (fisico) di memoria dove si trova
l'operando
–MOV AX, [3923:2314]
Modi di indirizzamento
•Relativo:
–l'indirizzo di memoria è specificato
relativamente al contenuto del PC
–vantaggio per programmi rilocabili
–MOV AX, [PC+102]
Modi di indirizzamento
Modi di indirizzamento
•Diretto:
–l'operando è contenuto in un registro
–nell'istruzione è specificato
l'identificativo del registro
–MOV AX, BX
Modi di indirizzamento
•Indiretto con registro:
–l'operando è in una cella di memoria il cui
indirizzo è contenuto in un registro
–nell'istruzione è specificato
l'identificativo del registro
–MOV AX, [BX]
Modi di indirizzamento
•Con autodecremento/incremento:
–analogo all'indiretto con registro, ma il
contenuto del registro viene decrementato
di una quantità pari alla dimensione in bytes
dell'operando
–predecremento e postdecremento
–preincremento e postincremento
Modi di indirizzamento
•Indiretto con autoincremento:
–l'operando è in memoria; il suo indirizzo è in
un'altra posizione della memoria puntata dal
contenuto di un registro.
–nell'istruzione è contenuto l'identificativo del
registro
–dopo l'uso, il contenuto del registro è
incrementato di una quantità pari alla
dimensione in bytes di un indirizzo di memoria
Modi di indirizzamento
•Con spiazzamento:
–nell'istruzione sono specificati un dato in
complemento a 2 e l'identificatore di un
registro
–il dato viene sommato al contenuto del
registro per ottenere l'indirizzo
dell'operando
–MOV AX, [BX+9382]
Modi di indirizzamento
•Indiretto con spiazzamento:
–come il precedente, ma l'indirizzo ottenuto
della somma punta ad una posizione di
memoria dove è contenuto l'indirizzo
dell'operando
Modi di indirizzamento
•Con registri indice:
–utilizza due registri: uno contiene un
indirizzo base e l'altro un numero da
moltiplicare per la dimensione
dell'operando e da sommare alla base per
ottenere la locazione dell'operando.
–Utile per l'accesso a vettori
Modi di indirizzamento
•Con lo stack pointer:
–SP punta alla sommità dello stack;
–le istruzioni PUSH e POP permettono di
inserire e prelevare elementi dallo stack
–Passaggio di parametri
Modi di indirizzamento
•Implicito:
–alcune istruzioni non prevedono di
specificare esplicitamente alcuni dei loro
operandi
–DIV BL
–Macchine "a 0 e 1 indirizzi"
Dichiarazione di variabili
Una variabile indica una locazione di memoria.
In assembler troviamo due tipi di variabili: BYTE e WORD.
Dichiarazione:
•<nome> DB <valore>
•<nome> DW <valore>
Dove:
•DB significa Define Byte.
•DW significa Define Word.
•<nome>
–è l’identificatore della variabile che è formato da lettere o cifre e
deve iniziare con una lettera.
–è possibile dichiarare variabili senza specificare il nome
•<valore>
–è un valore numerico
–il simbolo "?" significa che la variabile non è inizializzata
Esempi
var1 DB 0
var2 DB 41h
var3 DB ‘A’
var4 DB ?
var5 DB 10 DUP(0)
var6 DB “ciao”
;1 byte con valore 0
;1 byte con valore 65
;idem
;1 byte non inizializzato
;10 byte inizializzati a 0
;4 byte con i codici ascii …
L’area di memoria che contiene i dati NON deve essere eseguita
come codice:
– definire i dati dopo l’ultima istruzione
– mettere una istruzione di salto per non “eseguire i dati”
Nel modello COM area Dati e area Codice sono nello stesso
segmento (CS = DS)
Esempio 1
;Esempio di utilizzo variabili
;Visulizza n_cicli asterischi
;dove n_cicli e' una variabile
org 100h
mov ch,n_cicli ;inizializzazione contatore
mov dl,'*'
mov ah,2
STAMPA:
int 21h
;visualizzo il carattere
dec ch
;decremento contatore
jnz STAMPA
;torna a stampare se il contatore non è 0
FINE:
int 20h
ret
n_cicli DB 5
;numero di asterischi da stampare
Esempio 2
;Esempio di utilizzo variabili
;Visulizza n_cicli volte il carattere car
;dove n_cicli e' una variabile e car e' un'altra variabile
org 100h
mov ch,n_cicli ;inizializzazione contatore
mov dl,car
mov ah,2
STAMPA:
int 21h
;visualizzo il carattere
dec ch
;decremento contatore
jnz STAMPA
;torna a stampare se il contatore non è 0
FINE:
int 20h
ret
n_cicli DB 5
car DB 'x'
;numero di asterischi da stampare
Esempio 3
;Esempio di utilizzo variabili
;Visulizza n_cicli asterischi
;dove n_cicli e' una variabile
org 100h
jmp INIZIO
n_cicli DB 5
;numero di asterischi da stampare
INIZIO:
mov ch,n_cicli ;inizializzazione contatore
mov dl,'*'
mov ah,2
STAMPA:
int 21h
;visualizzo il carattere
dec ch
;decremento contatore
jnz STAMPA
;torna a stampare se il contatore non è 0
FINE:
int 20h
ret
Variabili e locazioni
L’assemblatore converte automaticamente le variabili
con il loro indirizzo (offset dell’indirizzo)
Nei file .COM i registri di segmento CS e DS (Data
Segment) sono inizializzati allo stesso valore.
L’assembler non è case sensitive (non distingue fra
lettere maiuscole e minuscole per i nomi delle
variabili)
Costanti
Se i valori non devono essere modificati dopo
la dichiarazione è possibile utilizzare le
costanti.
Dichiarazione:
<nome> EQU <valore>
L’uso è analogo a quello delle variabili.
Indirizzo di una variabile
L’istruzione LEA permette di caricare in una
variabile l’indirizzo di una variabile.
Sintassi:
LEA <registro>,<variabile>
Esempio:
lea dx, messaggio
…
messaggio db "Saluti$”
L’istruzione equivale a:
MOV <registro>,offset <variabile>
Visualizzazione di una stringa
E’ possibile definire una stringa in memoria e visualizzarla con
una sola chiamata a una servizio.
L’ultimo carattere deve essere $ (terminatore)
Servizio 9 dell’interrupt 21
Esempio
lea dx, messaggio ; carica in dx l'indirizzo di
messaggio
mov ah, 9
; stampa tutto fino al carattere $
int 21h
FINE:
int 20h
ret
messaggio db "Saluti$"
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.
•Le operazioni da eseguire sono:
–si sommano le due word meno
significative utilizzando l’istruzione
ADD
–si sommano le due word più
significative utilizzando l’istruzione
ADC.
Esempio:Somma tra numeri su 32 bit
.MODEL
small
.STACK
.DATA
NUMA DD
?
NUMB DD
?
NUMC DD
?
...
.CODE
...
MOV
AX, WORD
ADD
AX, WORD
MOV
WORD PTR
MOV
AX, WORD
ADC
AX, WORD
MOV
WORD PTR
...
PTR NUMA ; somma tra le 2 word
PTR NUMB ; meno significative
NUMC, AX
PTR NUMA+2 ; somma tra le due word
PTR NUMB+2
; più significative + CF
NUMC+2, AX
Somma tra numeri su 64 bit
.DATA
NUMA
NUMB
NUMC
ciclo:
DQ
DQ
DQ
.CODE
CLC
LEA
LEA
LEA
MOV
MOV
ADC
MOV
INC
INC
INC
INC
INC
INC
LOOP
...
?
?
?
; azzeramento del flag CF
SI, WORD PTR NUMA
DI, WORD PTR NUMB
BX, WORD PTR NUMC
CX, 4
AX, [SI]
AX, [DI] ; [DI] + [SI] + CF
[BX], AX
SI
SI
DI
DI
BX
BX
ciclo
Differenza tra numeri su 64 bit
.DATA
NUMA DQ
NUMB DQ
NUMC DQ
.CODE
CLC
LEA
LEA
LEA
MOV
ciclo: MOV
SBB
MOV
INC
INC
INC
INC
INC
INC
LOOP
...
?
?
?
; azzeramento del flag CF
SI, WORD PTR NUMA
DI, WORD PTR NUMB
BX, WORD PTR NUMC
CX, 4 ; 4 iterazioni
AX, [SI]
AX, [DI]
; [SI] - [DI]- CF
[BX], AX
SI
SI
DI
DI
BX
BX
ciclo
Esempio: Calcolo modulo di un vettore
Specifiche:
Si realizzi un programma che calcoli il modulo del
contenuto di tutte le celle di un vettore di interi.
main()
{
int i, vett[10];
...
for (i=0 ; i < 10 ; i++)
if (vett[i] < 0)
vett[i] *= -1;
...
}
Soluzione Assembler
LUNG
EQU
10
.MODEL small
.STACK
.DATA
VETT DW
LUNG DUP (?)
...
.CODE
...
MOV
SI, 0
MOV
CX, LUNG
ciclo: CMP
VETT[SI], 0
; elemento < 0 ?
JNL
cont
; No: va a continua
NEG
VETT[SI]
; Sì: calcola il modulo
cont: ADD
SI, 2
; scansione del vettore
LOOP ciclo
...
Esempio: Calcolo del quadrato
.MODEL
small
.STACK
.DATA
NUM DW
?
RES DD
?
...
.CODE
...
MOV WORD PTR RES+2, 0
MOV AX, NUM
; AX = NUM
MUL AX
; DX,AX = NUM * NUM
MOV WORD PTR RES, AX
JNC esce
; word alta = 0 ?
MOV WORD PTR RES+2, DX
esce: ...
Conversione di valuta
Specifiche:
Si realizzi un frammento di programma che converte il
costo di un prodotto da franchi francesi in lire italiane.
#define FFRANCO 295
main()
{
int f_costo, it_costo;
...
it_costo = f_costo*FFRANCO;
...
}
Soluzione Assembler
FFRANCO
F_COST
IT_COST
ERR_MSG
ok:
MOV
esci:
EQU
295
.386
.MODEL
small
.STACK
.DATA
DW
?
DW
?
DB
"Overflow nella moltiplicazione",0DH,0AH,"$"
.CODE
...
MOV
AX, F_COST
IMUL AX, FFRANCO
; AX = AX * 297
JNC
ok
; CF = 1 ?
LEA
DX, ERR_MSG
; Si: messaggio di errore
MOV
AH, 09H
INT
21H
JMP
esci
IT_COST, AX
; No
...
Inversione di un vettore di interi
.MODEL SMALL
.STACK
.DATA
VETT DW 0,1,2,3,4,5,6,7,8,9
.CODE
.STARTUP
MOV CX,5
LEA SI,VETT
LEA DI,VETT
ADD DI,18
CICLO:
MOV AX,[SI]
MOV BX,[DI]
MOV [SI],BX
MOV [DI],AX
ADD SI,2
SUB DI,2
LOOP CICLO
MOV AH,4CH ; Termina programma con retcode (come int 20H)
INT 21H
END
Ricerca del minimo
Specifiche
Si realizzi una funzione richiamabile da un
programma esterno che determini il minimo in un
vettore di interi la cui lunghezza e passata come
secondo parametro.
main()
{
int vett[10];
printf(“MIN=%d\n”, findmin(vett, 10));
}
Soluzione Assembler
PUBLIC _findmin
.MODEL SMALL
.CODE
_findmin PROC
CICLO:
FINE:
_findmin ENDP
END
PUSH BP
MOV BP,SP
PUSH CX
PUSH DI
MOV DI,[BP+4]
MOV CX,[BP+6]
MOV AX,[DI]
ADD DI,2
CMP AX,[DI]
JL FINE
MOV AX,[DI]
LOOP CICLO
POP DI
POP CX
POP BP
RET
Ricerca max e min in un vettore
.MODEL SMALL
.DATA
VETT DW -4,45,19,-67,9,37,198,-455,64,29
MAX DW (?)
MIN DW (?)
.CODE
.STARTUP
LEA SI,VETT
ADD SI, 2h
MOV CX,9h
MOV AX,[VETT]
MOV MAX,AX
MOV MIN,AX
CICLO:
MOV BX,[SI]
CMP BX,MAX
JG SCAMBIA_MAX
CMP BX,MIN
JL SCAMBIA_MIN
CONTINUA:
ADD SI,2h
LOOP CICLO
MOV AX,4Ch
INT 21h
SCAMBIA_MAX:MOV MAX,BX
JMP CONTINUA
SCAMBIA_MIN:MOV MIN,BX
JMP CONTINUA
END
Somma degli elementi in un vettore
LUNG
EQU 10
.MODEL SMALL
.DATA
VETT DW 1,2,3,4,5,6,7,8,9,0
SOMMA DW (?)
.CODE
.STARTUP
MOV AX,LUNG
LEA BX,VETT
SUB SP,2
;CREO UNO SPAZIO
;DA RIEMPIRE
;CON IL RISULTATO
;DELLA SOMMA
PUSH AX
PUSH BX
CALL SOM_VET
ADD SP,4
POP SOMMA
MOV AL,BYTE PTR SOMMA ;STAMPO
COME VALORE DI RITORNO LA SOMMA
MOV AH,04CH
INT 021H
SOM_VET PROC
PUSH BP
MOV BP,SP
PUSH BX
PUSH CX
PUSH AX
;SALVA REGISTRI
MOV CX,[BP+6]
MOV BX,[BP+4]
XOR AX,AX
CICLO: ADD AX,[BX]
ADD BX,2
LOOP CICLO
MOV [BP+8],AX ; RITORNA IL
; RISULTATO SULLO
POP AX
; STACK
POP CX
POP BX
POP BP
RET
SOM_VET ENDP
END
Media di un insieme di numeri
Specifiche
Si realizzi un frammento di codice che calcoli il
valor medio dei numeri positivi memorizzati in un
vettore di numeri interi con segno.
main()
{
int i, count=0, somma=0, avg, vett[10];
...
for (i=0 ; i<10 ; i++)
if (vett[i] > 0)
{
count++;
somma += vett[i];
}
avg = somma/count;
...
}
Soluzione Assembler
LUNG
VETT
COUNT
AVG
ciclo:
cont:
EQU
10
.MODEL small
.STACK
.DATA
DW
LUNG DUP (?)
DB
?
; numero di positivi
DB
?
...
.CODE
...
MOV
CX, LUNG
MOV
SI, 0
MOV
BX, 0
; somma totale
MOV
COUNT, 0
CMP
VETT[SI], 0
; VETT[] > 0 ?
JNG
cont
; No: va a continua
INC
COUNT
; Sì: incrementa il
; contatore
ADD
BX, VETT[SI]
; BX = BX + VETT[SI]
ADD
SI, 2
; scansione del vettore
LOOP ciclo
MOV
AX, BX
; copia in AX del
; dividendo
DIV COUNT
; BX / COUNT
MOV
AVG, AL
; copia in AVG del
; quoziente
...
Calcola la distanza di Hamming
PUBLIC _hamming
.MODEL small
.CODE
_hamming
PROC
PUSH BP
MOV BP, SP
MOV DX, [BP+4] ; primo parametro
MOV BX, [BP+6] ; secondo parametro
XOR DX, BX
XOR AX, AX
MOV CX, 16
inizio:
ROL DX, 1
JNC zero
INC
AX
zero:
LOOP inizio
POP BP
RET
_hamming
ENDP
END
Implementazione strutture “case”
switch(var)
{
case '1':codice_1;
break;
case '2':codice_2;
break;
case '3':codice_3;
break;
}
.DATA
TAB
DW lab_1
DW lab_2
DW lab_3
...
.CODE
...
DEC VAR ; decremento il valore di VAR
MOV BX, VAR ; BX assume un valore
; compreso tra 0 e 2
SHL BX, 1
; BX = BX * 2
JMP TAB[BX] ; salto ad indirizzi
; contenuti in TABELLA
; a seconda del valore di BX
Istruzioni di shift
Istruzioni di rotazione
Calcola l’area di un triangolo
.MODEL small
.STACK
.DATA
BASE DW ?
ALT DW ?
AREA DW ?
.CODE
...
MOV AX, BASE
MUL ALT ; DX,AX = BASE * ALTEZZA
SHR DX, 1 ; carico CF con il bit 0 di DX
RCR AX, 1 ; divisione per 2 e copia di CF nel bit 15
CMP DX, 0 ; DX != 0 ?
JNE err ; Sì: overflow
MOV AREA, AX
...
err: ... ; istruzioni di gestione dell’overflow
Testa o Croce?
.MODEL small
.STACK
.DATA
TESTA DB "TESTA",0Dh,0Ah,"$"
CROCE DB "CROCE",0DH,0AH,"$"
.CODE
...
MOV AH, 2CH
INT 21H ; in DX il timer di sistema
TEST DH, 1 ; bit 0 = 0 ?
JNZ lab_t ; No: va a lab_t
LEA DX, CROCE ; Sì: in DX l’offset di CODA
JMP video
lab_t: LEA DX, TESTA ; in DX l’offset di TESTA
video: MOV AH, 09H
INT 21H ; visualizza su video
Istruzioni complesse: XLAT
•Durante l’esecuzione, il processore esegue la somma del contenuto
dei registri AL e BX, trasferendo in AL il dato avente come offset il
risultato di tale somma.
•L’istruzione XLAT si usa nell’accesso a look-up table.
•BX deve contenere l'indirizzo di partenza della tabella e AL l'offset
al suo interno.
• Al termine AL contiene il byte puntato nella tabella.
•I dati memorizzati nella tabella devono essere di tipo byte
TAB DB 30H,31H,32H,33H,34H ;01234
DB 35H,36H,37H,38H,39H ;56789
DB 41H,42H,43H,44H,45H,46H ;ABCDEF
MOV AL, 10
MOV BX, OFFSET TAB
XLAT
L’istruzione copia il valore della locazione di memoria avente offset TAB+10 nel registro AL.
Codifica Gray
Si realizzi un programma che esegua la conversione in
codifica Gray di 100 numeri binari compresi tra 0 e 15.
LUNG EQU 100
.MODEL small
.STACK
.DATA
; tabella di conversione da
; numero decimale a codice Gray
TAB DB 00000000B, 00000001B,
00000011B, 00000010B
00000110B, 00000111B,
00000101B, 00000100B
00001100B, 00001101B,
00001111B, 00001110B
00001010B, 00001011B,
00001001B, 00001000B
NUM DB LUNG DUP (?)
GRAY DB LUNG DUP (?)
.CODE
...
lab:
LEA SI, NUM ; copia dell’offset di NUM
LEA DI, GRAY ; copia offset di GRAY
MOV CX, LUNG
LEA BX, TAB ; copia dell’offset di TAB
MOV AL, [SI] ; copia di NUM in AL
XLAT ; conversione
MOV [DI], AL ; copia di AL in GRAY
INC SI ; scansione di NUM
INC DI ; scansione di GRAY
LOOP lab
•XLAT:
•BX deve contenere l'indirizzo di partenza della tabella
e AL l'offset al suo interno. Al termine AL contiene il
byte puntato nella tabella.
Memorizzazione Array e Matrici
Indirizzamento array e matrici
La rappresentazione in memoria è carico del
programmatore.
Le modalità preferibili di indirizzamento:
•Per indirizzare array: Direct Indexed
•Per indirizzare matrici: Base Indexed
Direct Indexed Addressing
L’Effective Addressing dell’operando è
calcolato sommando il valore di un offset
contenuto in una variabile al contenuto di un
displacement contenuto in uno degli Index
Register (SI o DI).
Formato
<variable> [SI]
<variable> [DI]
Esempio
MOV AX, TABLE[DI]
indirizzamento Direct Indexed
Indirizzamento base-indexed
L’Effective Address dell’operando è calcolato come
somma dei seguenti termini:
• contenuto di uno dei Base Register (BX o BP)
• contenuto di uno degli Index Register (SI o DI)
• un campo opzionale displacement che può essere
un identificatore di variabile oppure una costante
numerica.
Formato
<variable> [BX] + [SI]
Esempio
MOV AX, TAB[BX][DI]
Indirizzamento base-indexed
Esempio: vettori di caratteri
Calcolo del numero di lettere minuscole in una
stringa rappresentata da un vettore di
caratteri.
#define LUNG 20
main()
{
int i;
char vett[LUNG], minuscole = 0;
for (i=0 ; i< LUNG ; i++)
if ((vett[i] >= 'a') && (vett[i] <= 'z'))
minuscole++;
}
Calcolo del numero di lettere
minuscole
car_a EQU ‘a’
car_z EQU ‘z’
...
.DATA
VETT DB "Nel mezzo del cammin di nostra... "
LUNG EQU $-VETT
MINUSCOLE DW ?
.CODE
...
MOV SI, 0
MOV AX, 0 ; contatore di minuscole
MOV CX, LUNG
ciclo: CMP VETT[SI], car_a ; VETT[SI] >= 'a' ?
JB salta ; No: va a salta
CMP VETT[SI], car_z ; Sì: VETT[SI] <= 'z' ?
JA salta ; No: va a salta
INC AX ; Sì: carattere minuscolo
salta: INC SI
LOOP ciclo
MOV MINUSCOLE, AX
...
Esempio: Copia di una riga in
una matrice di dati
Specifiche:
Date due matrici SORG e DEST di dimensione
4x5, si deve copiare la quarta riga da SORG a
DEST..
main()
{
int i;
int sorg[4][5], dest[4][5];
...
for (i=0 ; i < 5 ; i++)
dest[3][i] = sorg[3][i];
...
}
Soluzione Assembler
RIGHE EQU 4
COLONNE EQU 5
.MODEL small
.STACK
.DATA
SORG DW RIGHE*COLONNE DUP (?) ; matrice sorgente
DEST DW RIGHE*COLONNE DUP (?) ; matrice destinazione
.CODE
MOV BX, COLONNE*3*2 ; caricamento in BX del primo
; elemento della quarta riga
MOV SI, 0 ; inizializzazione del registro SI
MOV CX, 5 ; in CX del numero di colonne
ciclo: MOV AX, SORG[BX][SI]
MOV DEST[BX][SI], AX
ADD SI, 2 ; scansione dell’indice
LOOP ciclo ; fine? No => va a ciclo
... ; Sì
La ricorsione
•L’Assembler permette la ricorsione, che deve
però essere gestita dal programmatore
stesso.
•Nulla vieta che una procedura richiami se
stessa: in tal caso l’indirizzo di ritorno messo
nello stack è quello della procedura stessa e
nello stack si accumuleranno tanti di questi
indirizzi quante sono le chiamate ricorsive.
Esempio: Fattoriale
Si realizzi una procedura di nome FACT che
legge un numero nel registro BX e ne calcola il
fattoriale, scrivendo il risultato nel registro AX.
La versione C della stessa procedura è:
int fact ( int x)
{
if( x == 1)
return( 1);
return( x * fact( x-1));
}
Soluzione Assembler ricorsiva
FACT
return:
fine:
FACT
PROC NEAR
PUSH BX
;
CMP BX, 1 ;
JE
return
DEC BX
CALL FACT ;
INC BX
;
MUL BX
;
JMP fine
MOV AX, 1 ;
XOR DX, DX
POP BX
;
RET
ENDP
salva n
if (n == 1)
fact(n-1) --> ax
ax*n = fact(n-1)*n
return(1)
ripristina n
Chiamate annidate
•L’uso dello stack permette l’esecuzione di procedure annidate.
•Il massimo livello di annidamento permesso è limitato dalle dimensioni
dello stack.
Stack Frame Ricorsione
Return Addr Call 1
BP
BX Call1
Return Addr Call 2
BX Call2
Return Addr Call 3
BX Call3
…
SP
Esempio: Split & Substitute
Si vuole scrivere un programma in grado di espandere (splitting) stringhe
di bit contenenti 0, 1 e X, producendo tutte le possibili stringhe ottenibili
da quella data, tramite la sostituzione di ciascuna X con un 1 o uno 0.
Esempio
0x11x0 → 001100, 001110, 011100, 011110
void split(void)
{
if (curr_index==len)
{
printf("%s\n", obuff);
return;
}
else
switch (ibuff[curr_index])
{
case '0':
obuff[curr_index++] = '0';
split();
break;
case '1':
obuff[curr_index++] = '1';
split();
break;
case 'X':
obuff[curr_index++] = '0';
split();
obuff[curr_index-1] = '1';
split();
break;
}
return;
}
Soluzione Assembler ricorsiva
LF EQU 10
CR EQU 13
DIM EQU 30 ; dimensione massima della
; stringa da espandere
OBUFF DB DIM DUP ('0')
IBUFF DB DIM DUP ('0')
LEN DW 0
ERR_MESSDB 'Carattere non ammesso$‘
SPLIT
lab2:
PROC
PUSH AX
PUSH DX
PUSH SI
CMP BX, LEN ; stringa vuota ?
JNE ancora
MOV CX, LEN ; Sì: visualizza
MOV AH, 2
XOR SI, SI
MOV DL, OBUFF[SI]
INT 21H
INC SI
LOOP lab2
MOV DL, CR
INT 21H
MOV DL, LF
INT 21H
JMP fine
ancora: MOV DL, IBUFF[BX] ; No,
;considera il primo carattere
CMP DL, '0'
JNE not_z
MOV OBUFF[BX], '0' ; '0'
INC BX
CALL SPLIT
DEC BX
JMP fine
not_z: CMP DL, '1' ; '1'
JNE not_one
MOV OBUFF[BX], '1'
INC BX
CALL SPLIT
DEC BX
JMP fine
Soluzione Assembler ricorsiva (cont)
not_one:
error:
fine:
SPLIT
CMP DL, 'X' ; 'X‘
JNE error
MOV OBUFF[BX], '0'
; trasforma la X in 0
INC BX
CALL SPLIT
DEC BX
MOV OBUFF[BX], '1'
; trasforma la X in 1
INC BX
CALL SPLIT
DEC BX
JMP fine
MOV AH, 9
; carattere diverso da 0, 1 e X
LEA DX, ERR_MESS
INT 21H
POP SI
POP DX
POP AX
RET
ENDP
;Programma Principale
.CODE
.STARTUP
MOV CX, DIM
; lettura stringa di input
MOV SI, 0
MOV AH, 1
lab1:
INT 21H
MOV IBUFF[SI], AL
INC SI
CMP AL, CR
JE done:
LOOP lab1
sone:
DEC SI
MOV LEN, SI
XOR BX, BX
CALL SPLIT
.EXIT
Conversione da Ascii a Binario
.MODEL small
.STACK
.DATA
ASCIIDB ?
NUM DB ?
...
.CODE
...
MOV AL, ASCII
AND AL, 0FH ; mascheramento dei
; 4 bit alti
MOV NUM, AL
…
Conversione da Binario a Ascii
.MODEL small
.STACK
.DATA
ASCIIDB ?
NUM DB ?
...
.CODE
...
MOV AL, NUM
OR AL, 30H ; mascheramento dei
; 4 bit alti
MOV ASCII, AL
…
Conversione da Binario a Ascii
RESTI DB 8 DUP(?)
DIECI DW 10
PUBLIC BIN2ASC
BIN2ASC PROC
;In ingresso: AX: numero da convertire BX: stringa risultato
MOV SI,0
MOV CX,0
CICLO1:
MOV DX,0 ;
DIV DIECI ;AX= quoziente(AX/10); DX= resto(AX/10)
ADD DL,’0’ ;Resto trasformato in ASCII
MOV RESTI[SI],DL
INC SI
INC CX
CMP AX,0 ;Quoziente = 0?
JNE CICLO1
DEC SI
CICLO2:
MOV AL,RESTI[SI] ;Prende
MOV [BX],AL ;-e deposita
DEC SI
INC BX
LOOP CICLO2
MOV AL,’$’ ;Aggiunta ’$’ a fine stringa
MOV [BX],AL
RET
BIN2ASC ENDP
Conversione da Ascii a Binario
ASC2BIN PROC FAR
;In ingresso: BX: stringa da convertire, CX: lunghezza
;In uscita CL=0 OK: stringa numerica corretta; AX: risultato della conv.
; CL= 1 Errore: stringa in ingresso non numerica CL= 2 Errore: trabocco
MOV SI,BX ;i=0
XOR DX,DX ;z=0
CICLO: MOV AX,10
MOV BL,[SI] ;prende il prossimo
CMP BL,’0’ ;Test
JL NAN ;-per
CMP BL,’9’ ;--carattere
JG NAN ;---numerico
SUB BL,’0’ ;da carattere a numero
MOV BH,0 ;BX= numero
MUL DX ;z= z*10
CMP DX,0 ;Test di
JNE OFLOW ;-overflow
ADD AX,BX ;z= z+num(STR[i]
JC OFLOW
MOV DX,AX ;z in DX
INC SI
LOOP CICLO
RET ;CL=0; AX=numero
NAN:
MOV CL,1 ;Not A Number
RET
OFLOW: MOV CL,2 ;Overflow
RET
ASC2BIN ENDP
Lettura dati da sensori
scrivere una procedura richiamabile da linguaggio C che esegua un’operazione
di elaborazione di dati provenienti da centraline metereologiche.
La stazione metereologica riceve dati da 4 siti la temperatura rilevata. La
procedura deve elaborare i dati letti calcolando la media delle temperature
per ogni sito. I dati di input sono memorizzati in una stringa che contiene
per ogni rilevamento una coppia di dati:
•codice identificativo del sito (da 0 a 3)
•temperatura rilevata.
Il prototipo della procedura è il seguente:
int stazione (int n, int *dati_input, int *dati_output);
dove n rappresenta il numero complessivo di rilevamenti, dati_input
rappresenta l’indirizzo di inizio dell’area di memoria contenente i dati da
elaborare (si tenga conto che la dimensione di questo vettore è pari a 2*n
elementi, in quanto ogni rilevamento richiede la memorizzazione di 2 dati) e
dati_output rappresenta l’indirizzo di inizio dell’area di memoria allocata per
memorizzare la media delle temperature dei 4 siti.
La procedura deve restituire la temperatura massima rilevata.
Chiamata in C
extern int stazione (int n, int *dati_input, int *dati_output);
void main()
{
int n = 6;
int dati_input[18] = {1,0,1,-1,3,3,0,1,2,4,0,1,2,6,3,3,1,-2};
int dati_output[4];
int max=stazione(n, dati_input, dati_output);
printf("Stringa di input: ");
for (int i=0; i<18; i++)
printf("%d",dati_input[i]);
printf("\nStringa di output: ");
for (int i=0; i<4; i++)
printf("%d",dati_output[i]);
printf("\nTemperatura massima rilevata: %d",max);
}
Soluzione Assembler
public _stazione
.model small
.stack
.data
cont_ril dw 0 ; inizializzazione contatore rilevamenti
max
dw 0 ; valore di appoggio per massimo
.code
_stazione proc
push bp
mov bp,sp
push si
push di
push bx
push dx
xor bx,bx
; inizializzazione contatore stazioni
mov di,[bp+8]
; indirizzo dati_output
Soluzione Assembler (cont)
stazioni:
mov
mov
xor
mov
si,[bp+6]
; indirizzo dati_input
cx,[bp+4]
; int n, numero rilevamenti
ax,ax
; inizializzazione contatore temperature
cont_ril,0 ; inizializzazione contatore rilevamenti
rilevamenti:
cmp bx,[si] ; controllo se stazione corrente
jne continua
; no --> salta rilevazione
add ax,[si+2]
; si --> prendi la temperatura e somma a
quelle precedenti
inc cont_ril
; incremento contatore rilevamenti per
stazione
push ax
; salvo ax nello stack
mov ax,[si+2]
; metto in ax la temperatura corrente
cmp ax,max ; ax > max?
jl continua_pop ; no --> continua
mov max,ax ; si --> max = ax
Soluzione Assembler (cont)
continua_pop:
pop ax
; ripristino il valore di ax
continua:
add si,4
; si muove alla coppia successiva
loop rilevamenti
cwd
; inizializzazione per divisione tra word
idiv cont_ril
; esegue la media
mov [di],ax ; salva il risultato nel vettore di output
add di,2
; sposta il puntatore del vettore di output
inc bx
; incremento stazione
cmp bx,4
; controllo se stazioni esaurite
jb stazioni ; no --> continua a scorrere
mov ax,max ; valore di ritorno
pop dx
pop bx
pop di
pop si
pop bp
ret
_stazione endp
end
Compatta Memoria
La gestione della memoria principale da parte di un sistema operativo avviene
utilizzando una tabella che ad ogni istante elenca i blocchi occupati della
memoria: per ognuno degli n blocchi occupati la tabella contiene una coppia di
valori interi
•indirizzo_di_partenza,
•indirizzo_di_fine.
Si assuma che le informazioni sui blocchi siano ordinate per indirizzi
crescenti.
Si scriva una procedura Assembler richiamabile da linguaggio C che permetta
di compattare la memoria, spostando (se possibile) l’ultimo blocco tra quelli
memorizzati nella tabella in uno spazio libero (quello di dimensioni minime,
tra quelli in grado di contenere il blocco) tra i blocchi precedenti.
La procedura ha il seguente prototipo
int compatta( int *tabella, int n);
La procedura ritorna il numero di byte occupati in memoria.
Esempio
•10 - 20
•70 - 90
•130 - 200
•1000 - 1500
•2000 – 2030
Dopo un passo di compattamento dell’ultimo blocco diventa
•10 - 20
•70 - 90
•90 - 120
•130 - 200
•1000 - 1500
Chiamata in C
#include <stdio.h>
extern int compatta (int *tabella, int n);
void main()
{
int n = 5;
int tabella[10] = {10,20,70,90,130,200,1000,1500,2000,2030};
printf("Tabella input: \n");
for (int i=0; i<10; i++)
printf("%d\n",tabella[i]);
int byte_totali=compatta(tabella,n);
printf("\nTabella output: ");
for (int i=0; i<4; i++)
printf("%d",tabella[i]);
printf("\nNumero di byte occupati: %d",byte_totali);
}
Soluzione Assembler
public _compatta
.model small
.stack
.data
byte_tot
dw 0
min
dw 0FFFFh
offset_min dw 0
;
ultimo
dw 0
dim_blocco dw 0
;
sostituito db 0
;
.code
_compatta proc
push bp
mov bp,sp
push si
push di
mov di,[bp+4]
mov bx,[bp+6]
; contatore byte
; valore minimo spazio libero
valore indirizzo minimo spazio libero
; spostamento ultimo blocco
dimensione ultimo blocco
flag true se il blocco e’ stato sostituito
; primo parametro: *tabella
; secondo parametro: n
Soluzione Assembler (cont)
dec
shl
mov
add
mov
sub
sub
mov
mov
bx
; calcolo spostamento per l'ultimo blocco
bx,2
; 4*(n-1)
ultimo,bx
bx,2
; spostamento per ultimo elemento
ax,[di+bx] ; ultimo elemento
bx,2
; spostamento per penultimo elemento
ax,[di+bx] ; calcolo dimensione blocco
dim_blocco,ax ;salvo il valore nella variabile app
cx,[bp+6]
; n
blocco:
mov ax,[di+2]
sub ax,[di]
add byte_tot,ax
cmp cx,1
je end_blocco
add di,2
mov bx,[di+2]
sub bx,[di]
;
;
;
;
secondo elemento blocco
calcolo byte blocco
sommo il valore nella variabile app
ultimo elemento no calcolo spazio libero
; secondo elemento blocco
; primo elemento blocco successivo
; spazio libero tra i 2 blocchi adiacenti
Soluzione Assembler (cont)
add di,2
; primo elemento blocco successivo
cmp dim_blocco,bx; spazio libero >= dim. ultimo blocco?
ja continua
; no --> continua
cmp min,bx
; spazio libero < min?
jbe continua
; no --> continua
mov min,bx
; nuovo minimo
mov offset_min,di; offset primo elem. blocco da sostituire
continua: loop blocco
end_blocco:
cmp offset_min,0; nessuno spazio libero sufficiente
je fine
mov di,[bp+4]
; reset blocco iniziale
mov cx,[bp+6]
; inizializzazione cont. a n
inserimento:
cmp cx,1
;ultimo blocco?
je ult_blocco
;si --> ult_blocco
cmp sostituito,1
;il blocco e' stato sostituito?
je sposta
;si --> sposta
cmp offset_min,di
;offset blocco da sostituire?
jne scorri
;no --> scorri
Soluzione Assembler (cont)
mov ax,[di-2]
push [di+2]
push [di]
sostituire
mov [di],ax
add ax,dim_blocco
mov [di+2],ax
mov sostituito,1
jmp scorri
sposta:
pop ax
pop bx
push [di+2]
push [di]
mov [di],ax
mov [di+2],bx
jmp scorri
;secondo elemento blocco precedente
;salvo nello stack secondo e
;primo elemento del blocco da
;sostituisco primo elemento
;calcolo secondo elemento
;sostituisco secondo elemento
;il blocco e' stato sostituito
;primo elemento blocco
;secondo elemento blocco
;salvo nello stack secondo e
;primo elem. del blocco da sostituire
;primo elemento blocco da scorrere
;secondo elemento blocco da scorrere
Soluzione Assembler (cont)
ult_blocco:
pop ax
pop bx
mov [di],ax
mov [di+2],bx
jmp fine
scorri:
add di,4
loop inserimento
;primo elemento blocco
;secondo elemento blocco
;primo elemento blocco da scorrere
;secondo elemento blocco da scorrere
;spiazzamento blocco successivo
fine:
mov ax,byte_tot
pop di
pop si
pop bp
ret
_compatta endp
end
;valore di ritorno