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