Programmazione Assembly 80x86 -...
Transcript of Programmazione Assembly 80x86 -...
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.
AH AL
BH BL
CH CL
DH DL
SP
BP
SI
DI
ALU
Flag register
Execution Unit (EU)
EU control
CS
DS
SS
ES ALU Data bus (16 bits)
Address bus (20 bits)
Instruction Queue
Bus control
External bus
IP
Data bus (16 bits)
Bus Interface Unit (BIU)
General purpose register
Segment register
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.
Architettura 8086
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.
Registri
Visione stratificata
Hardware (X86, RAM e periferche)
BIOS
Sistema Operativo
Applicazioni Assembly
Livelli superiori
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
Syntax check
Traduzione in Linguaggio macchina
file sorgenti Assembler
Linker OBJ file
OBJ file
librerie
File eseguibile
EXE file
ASM file
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.
.MODEL SMALL
.STACK
.CODE
.STARTUP
MOV AX,40H ;set AX to 0040H
D1: MOV DS,AX ;load data segment with 0040H
MOV SI,17H ;load SI with 0017H
AND BYTE PTR [SI],0DFH ;clear NUM-LOCK bit
.EXIT
END
Commenti
Direttive all’assemblatore
Istruzioni
Direttive all’assemblatore
Label - Etichette
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
• 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
DATA SEGMENT PARA 'DATA‘
ORG 7000H
POINTS DB 16 DUP(?)
SUM DB ?
DATA ENDS
CODE SEGMENT PARA 'CODE‘
ASSUME CS:CODE, DS:DATA
ORG 8000H
TOTAL: MOV AX,7000H
MOV DS,AX
MOV AL,0
•••••••••
CODE ENDS
END TOTAL
0000 DATA SEGMENT PARA 'DATA’
ORG 7000H
7000 0010 [00] POINTS DB 16 DUP(?)
7010 00 SUM DB ?
7011 DATA ENDS
0000 CODE 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
•••••••••
File Sorgente List File
Direttive per l’Assemblatore
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: MOV AX, BX
CMP AX, 12h
JZ EQUAL
INT 21h
RET
EQUAL: 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 - MOV AX, [indirizzo]
•PUSH AX - 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 ; ERRORE !!!
Si può sostituire con:
MOV AH, VAL2
ADD VAL1, AH
Logiche
•And, Or, Xor, Not, Test
•AND AX, BX
•OR AX, BX
•TEST AX, 01100100b
Le istruzioni INC e DEC e NEG
INC operando DEC operando NEG 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
Indirizzo di ritorno
Parametro n
. . .
Parametro 1
Registro BP
Area locale di dati
Registri salvati
. . .
BP
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] ; primo parametro
MOV CX, [BP+6] ; secondo parametro
SHL AX, CL
POP BP
RET
_power2 ENDP
END
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 ;1 byte con valore 0
var2 DB 41h ;1 byte con valore 65
var3 DB ‘A’ ;idem
var4 DB ? ;1 byte non inizializzato
var5 DB 10 DUP(0) ;10 byte inizializzati a 0
var6 DB “ciao” ;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 ;numero di asterischi da stampare car DB 'x'
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.
.MODEL small
.STACK
.DATA
NUMA DD ?
NUMB DD ?
NUMC DD ?
...
.CODE
...
MOV AX, WORD PTR NUMA ; somma tra le 2 word
ADD AX, WORD PTR NUMB ; meno significative
MOV WORD PTR NUMC, AX
MOV AX, WORD PTR NUMA+2 ; somma tra le due word
ADC AX, WORD PTR NUMB+2 ; più significative + CF
MOV WORD PTR NUMC+2, AX
...
Esempio:Somma tra numeri su 32 bit
.DATA
NUMA DQ ?
NUMB DQ ?
NUMC DQ ?
.CODE
CLC ; azzeramento del flag CF
LEA SI, WORD PTR NUMA
LEA DI, WORD PTR NUMB
LEA BX, WORD PTR NUMC
MOV CX, 4
ciclo: MOV AX, [SI]
ADC AX, [DI] ; [DI] + [SI] + CF
MOV [BX], AX
INC SI
INC SI
INC DI
INC DI
INC BX
INC BX
LOOP ciclo
...
Somma tra numeri su 64 bit
Differenza tra numeri su 64 bit .DATA
NUMA DQ ?
NUMB DQ ?
NUMC DQ ?
.CODE
CLC ; azzeramento del flag CF
LEA SI, WORD PTR NUMA
LEA DI, WORD PTR NUMB
LEA BX, WORD PTR NUMC
MOV CX, 4 ; 4 iterazioni
ciclo: MOV AX, [SI]
SBB AX, [DI] ; [SI] - [DI]- CF
MOV [BX], AX
INC SI
INC SI
INC DI
INC DI
INC BX
INC BX
LOOP 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 EQU 295
.386
.MODEL small
.STACK
.DATA
F_COST DW ?
IT_COST DW ?
ERR_MSG 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
ok: MOV IT_COST, AX ; No
esci: ...
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
PUSH BP
MOV BP,SP
PUSH CX
PUSH DI
MOV DI,[BP+4]
MOV CX,[BP+6]
MOV AX,[DI]
CICLO: ADD DI,2
CMP AX,[DI]
JL FINE
MOV AX,[DI]
FINE: LOOP CICLO
POP DI
POP CX
POP BP
RET
_findmin ENDP
END
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 EQU 10
.MODEL small
.STACK
.DATA
VETT DW LUNG DUP (?)
COUNT DB ? ; numero di positivi
AVG DB ?
...
.CODE
...
MOV CX, LUNG
MOV SI, 0
MOV BX, 0 ; somma totale
MOV COUNT, 0
ciclo: 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]
cont: 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
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
...
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
lab: 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.
Si realizzi un programma che esegua la conversione in codifica Gray di 100 numeri binari compresi tra 0 e 15.
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 PROC NEAR
PUSH BX ; salva n
CMP BX, 1 ; if (n == 1)
JE return
DEC BX
CALL FACT ; fact(n-1) --> ax
INC BX ;
MUL BX ; ax*n = fact(n-1)*n
JMP fine
return: MOV AX, 1 ; return(1)
XOR DX, DX
fine: POP BX ; ripristina n
RET
FACT ENDP
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
BX Call1
Return Addr Call 2
BX Call2
Return Addr Call 3
BX Call3
…
BP
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 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
lab2: 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: 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
error: MOV AH, 9
; carattere diverso da 0, 1 e X
LEA DX, ERR_MESS
INT 21H
fine: POP SI
POP DX
POP AX
RET
SPLIT 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 si,[bp+6] ; indirizzo dati_input
mov cx,[bp+4] ; int n, numero rilevamenti
xor ax,ax ; inizializzazione contatore temperature
mov 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 ; contatore byte
min dw 0FFFFh ; valore minimo spazio libero
offset_min dw 0 ; valore indirizzo minimo spazio libero
ultimo dw 0 ; spostamento ultimo blocco
dim_blocco dw 0 ; dimensione ultimo blocco
sostituito db 0 ; flag true se il blocco e’ stato sostituito
.code
_compatta proc
push bp
mov bp,sp
push si
push di
mov di,[bp+4] ; primo parametro: *tabella
mov bx,[bp+6] ; secondo parametro: n
Soluzione Assembler (cont)
dec bx ; calcolo spostamento per l'ultimo blocco
shl bx,2 ; 4*(n-1)
mov ultimo,bx
add bx,2 ; spostamento per ultimo elemento
mov ax,[di+bx] ; ultimo elemento
sub bx,2 ; spostamento per penultimo elemento
sub ax,[di+bx] ; calcolo dimensione blocco
mov dim_blocco,ax ;salvo il valore nella variabile app
mov cx,[bp+6] ; n
blocco:
mov ax,[di+2] ; secondo elemento blocco
sub ax,[di] ; calcolo byte blocco
add byte_tot,ax ; sommo il valore nella variabile app
cmp cx,1 ; ultimo elemento no calcolo spazio libero
je end_blocco
add di,2 ; secondo elemento blocco
mov bx,[di+2] ; primo elemento blocco successivo
sub bx,[di] ; 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] ;secondo elemento blocco precedente
push [di+2] ;salvo nello stack secondo e
push [di] ;primo elemento del blocco da
sostituire
mov [di],ax ;sostituisco primo elemento
add ax,dim_blocco ;calcolo secondo elemento
mov [di+2],ax ;sostituisco secondo elemento
mov sostituito,1 ;il blocco e' stato sostituito
jmp scorri
sposta:
pop ax ;primo elemento blocco
pop bx ;secondo elemento blocco
push [di+2] ;salvo nello stack secondo e
push [di] ;primo elem. del blocco da sostituire
mov [di],ax ;primo elemento blocco da scorrere
mov [di+2],bx ;secondo elemento blocco da scorrere
jmp scorri
Soluzione Assembler (cont)
ult_blocco:
pop ax ;primo elemento blocco
pop bx ;secondo elemento blocco
mov [di],ax ;primo elemento blocco da scorrere
mov [di+2],bx ;secondo elemento blocco da scorrere
jmp fine
scorri: ;spiazzamento blocco successivo
add di,4
loop inserimento
fine:
mov ax,byte_tot ;valore di ritorno
pop di
pop si
pop bp
ret
_compatta endp
end