Ud6: Linguaggio Assembly Lezione Uno: Il linguaggio Assembly e i metodi di indirizzamento (libro di testo pag. 119 - 127) Caratteristiche di un linguaggio Assembly • Basso livello • Diverso per ogni architettura (perché corrispondente al linguaggio macchina...e ogni architettura ha il suo set di istruzioni macchina) • Corrispondenza di 1 a 1 tra istruzioni in linguaggio macchina e istruzioni in linguaggio assembly..... un'istruzione macchina corrisponde esattamente ad un istruzione in assembly e viceversa • Stesso formato istruzioni: ETCHETTA: OPERAZIONE OPERANDI Facilitazioni che l'asssembly mette a disposizione rispetto al linguaggio macchina • L'operazione viene indicata con un codice mnemonico e non binario ( ADD non 1001) • Si possono dare nomi alle variabili e quindi riferirsi a locazioni in memoria attraverso un nome e non solo attraverso un indirizzo • Si possono utilizzare etichette (nomi simbolici) per indicare i punti di arrivo delle istruzioni di salto. Formato istruzione: ETCHETTA: OPERAZIONE OPERANDI Etichetta: Utilizzata come punto di arrivo nelle istruzioni di salto al posto dell'indirizzo binario Operazione: Parola chiave che identifica l'istruzione (ADD → somma, JMP → Salto incodizionato, ….) Operando/i: Il valore o i valori su cui si effettua l'operazione. Al seconda del metodo di indirizzamento utilizzato un operando può indicare: 1. Un valore costante 2. Il nome di una variabile 3. Il riferimento ad un registro 4. Il nome di una variabile che contiene l'indirizzo in memoria METODI DI INDIRIZZAMENTO Indirizzamento immediato L'istruzione contiene già l'operando (fornito come costante). Ovviamente l'eventuale operando destinazione non può mai essere fornito con questo tipo di indirizzamento. I valori costanti possono venir specificati in binario (b), ottale (o), decimale (d) o esadecimale (h). Quando si specifica un valore, quindi, è necessario indicare in che base bisogna considerarlo aggiungendo la lettera corrispondente. Esempi 34d significa 34 in decimale 101b significa 101 i binario ecc. Indirizzamento tramite registro L'istruzione contiene il nome del registro che funge da operando. I registri utilizzabili sono a 8 bit o a 16 bit. Registri a 8 bit: AL, AH, BL, BH, CL, CH, DL, DH Prendendo a coppie i registri a 8 bit si possono formare dei registri a 16 bit AX (AHAL), BX (BHBL), CX (CHCL), DX (DH,DL) Esempio MOV AL,15d Salva nel registro AL al il valore 15. In questa istruzione ci sono due operandi; il primo è passato con indirizzamento tramite registro il secondo con indirizzamento immediato Indirizzamento diretto L'istruzione indica il nome della variabile che funge da operando. In un'istruzione non si possono mai fornire entrambi gli operandi con l'indirizzamento diretto Esempio. Supponiamo che la variabile var1 sia di 8 bit, sia stata dichiarata e che contenga il valore 17d; l'operazione MOV AL, VAR1 ha il primo operando AL specificato per indirizzamento tramite registro ha il secondo operando VAR1 specificato per indirizzamento diretto ha l'effetto di copiare il valore contenuto in var1 (17d) anche nel registro AL Indirizzamento indiretto L'istruzione contiene il nome della variabile che contiene l'indirizzo di memoria in cui si può trovare l'operando. Indirizzamento implicito Alcune operazioni (per esempio la divisione) assumono che un operando si trovi sempre nello stesso registro e quindi non viene specificato Lezione Due: Le Istruzioni Base e la dichiarazione delle variabili (libro di testo pag. 131- 134 e 153-162) Dichiarazione di variabili Nome_variabile DB (valore) Nome_variabile DW )valore) La differenza tra le 2 dichiarazioni è che per la prima, tipo byte, vengono riservati in memoria 1 byte; mentre per la seconda, tipo word, vengono riservati in memoria 2 byte. Il valore, sia nel tipo byte sia word, può essere: costante binaria: Es: 00001011b costante decimale: Es: 123d costante ottale: Es: 47o costante esadecimale: Es: A4Fh senza inizializzarla: Es: ? Istruzione di trasferimento Sintassi: MOV destinazione, sorgente Effetto: Copia i dati o gli indirizzi di 8 o 16 bit dalla sorgente alla destinazione (destinazione = sorgente in un linguaggio ad alto livello) Limitazioni: • destinazione non può essere fornito con indirizzamento immediato • destinazione e sorgente devono avere la stessa dimensione • sorgente e destinazione non devono essere entrambe fornite per indirizzamento diretto ISTRUZIONI ARITMETICHE Addizione Sintassi: ADD destinazione, sorgente Effetto: destinazione=destinazione+sorgente Limitazioni: • Le stesse dell'istruzione MOV Addizione con riporto Sintassi: ADC destinazione, sorgente Effetto: destinazione=destinazione+sorgente+bit di carry Limitazioni: • Le stesse dell'istruzione MOV Sottrazione Sintassi: SUB destinazione, sorgente Effetto: destinazione=destinazione-sorgente Limitazioni: • Le stesse dell'istruzione MOV Sottrazione con riporto Sintassi: SBB destinazione, sorgente Effetto: destinazione=destinazione-sorgente-bit di carry Limitazioni: • Le stesse dell'istruzione MOV Moltiplicazione Sintassi: MUL fattore Effetto: Se fattore ha 8 bit AX = AL * fattore Se fattore ha 16 bit DXAX = AX * fattore Limitazioni: • Fattore non può essere fornito con indirizzamento immediato Divisione Sintassi: DIV dividendo Effetto: Se dividendo ha 8 bit AH = resto di AX/dividendo AL = quoziente di AX/dividendo Se dividendo ha 16 bit DX = resto di DXAX/dividendo AX = quoziente di DXAX/dividendo Limitazioni • Le stesse di Mul Lezione Tre: L'input/output Il linguaggio Assembly non prevede istruzioni per la visualizzazione e l’acquisizione dei caratteri e quindi sarebbero necessari dei programmi in Assembly stesso molto complicati. Per ovviare a tale inconveniente ( e aggiungerei per fortuna!) possiamo utilizzare le funzioni libreria di DOS che sono richiamabili attraverso l’istruzione: INT 21h L’istruzione int 21h permette di richiamare diversi servizi del dos tra cui: l’input e l’output di CARATTERI (servizi 01 e 02) invio di un carattere sulla stampante (servizio 05) Visualizzazione di una stringa (servizio 09) Acquisizione di una stringa (servizio 10) Prima di invocare l’istruzione INT 21h è necessario mettere nel registro AH il numero del servizio che si intende utilizzare Input di un carattere (servizio 01) Il servizio 01 attende un carattere dallo standard input, lo visualizza sul video e copia il codice ASCII del carattere inserito nel registro AL. Per invocare questo servizio è necessario salvare il valore 01d nel registro AH prima di chiamare INT21H. L’acquisizione di un carattere è dunque eseguita dalle seguenti istruzioni assembly Mov ah,01h Int 21h Dopo queste due istruzioni in AL si trova il codice ASCII del carattere inserito che, ovviamente, andrà copiato (con MOV) in qualche altro registro o in qualche variabile prima di eseguire la prossima acquisizione. Output di un carattere (servizio 02) Il servizio 02 stampa su video il carattere il cui codice ASCII è contenuto in AL. Per invocare questo servizio è necessario salvare il valore 02d nel registro AH ed il codice ASCII del carattere che si vuole stampare nel registro AL prima di chiamare INT21H. L’acquisizione di un carattere è dunque eseguita dalle seguenti istruzioni assembly Mov ah,02h Mov AL,codice carattere da stampare Int 21h Dopo queste tre istruzioni viene visualizzato il carattere con codice ASCII contenuto in AL. Esempi: Visualizzazione sullo schermo di un punto di domanda e uno spazio bianco: MOV AL, 63d MOV AH, 02h INT 21h MOV AL, 32d MOV AH, 02h INT 21h Passaggio alla riga successiva con inizio a capo della riga: MOV AL, 10d MOV AH, 02h INT 21h MOV AL, 13d MOV AH, 02h INT 21h Acquisizione di un dato MOV AH, 01h INT 21h MOV Var, AL Input di numeri Purtroppo non esiste un servizio del DOS che consenta di acquisire in input numeri decimali; per realizzare tale scopo è indispensabile convertire i codici ASCII inseriti, che corrispondono alle cifre che compongono il numero, nel numero decimale corrispondente. Il problema è semplice nel caso di numeri ad una cifra in quanto le cifre decimali occupano posizioni adiacenti nella tabella dei codi ASCII ed in particolare: Carattere Codice ASCII 0 1 2 3 4 5 6 7 8 9 48 (0+48) 49 (1+48) 50 (2+48) 51 (3+48) 52 (4+48) 53 (5+48) 54 (6+48) 55 (7+48) 56 (8+48) 57 (9+48) Se, dunque, si vuole convertire il codice ASCII della cifra inserita (contenuta in AL) nel corrispondente numero decimale ad una cifra è sufficiente sottrarre 48 ;Selezionare il servizio di acquisizione carattere mettendo 02 nel registro AH MOV AH,01d ;Invocare l’int 21h Int 21h ;Sottrarre al contenuto di AL 48 per trasformare la codifica ASCII nel numero ad una ;cifra corrispondente SUB AL,48d ;Salvo in Var il numero mov var,AL Risulta ovviamente più complessa l’acquisizione di un numero a due cifre. Il procedimento di acquisizione di un numero a due cifre può essere diviso nelle seguenti fasi Acquisizione del codice ASCII della cifra che costituisce le decine Trasformazione del codice ASCII in cifra sottraendo 48d Moltiplicare per dieci il valore ottenuto. Per far questo devo ◦ Mettere in AL uno dei due fattori (il valorew ottenuto al passo precedente) ◦ Mettere 10 in un registro qualsiasi ◦ Eseguire MUL passando come operando il registro in cui ho messo 10. <il risultato sarà in AX ma poiché sarà minore di 255 AH sarà composto di zeri quindi la parte significativa del risultato è in AL Salvare temporaneamente il valore del numero senza le unità (contenuto in AX come da specifiche moltiplicazione) nella variabile o in un altro registro Acquisizione del codice ASCII della cifra che costituisce le unità Trasformazione del codice ASCII in cifra sottraendo 48d Sommarlo al valore ottenuto precedentemente e salvare il risultato nella variabile Il codice risulta dunque essere ;Leggo il codice ASCII delle decine (passo1) mov ah,01h int 21h ;in AL ho il codice ASCII del carattere delle decine ;sottraggo 48 per ottenere la cifra (passo 2) sub al,48d ;moltiplico le decine per 10 (passo 3) mov ah,00d mov bl, 10d mul bl ; salvo il valore ottenuto (passo 4) mov var,al ;Leggo il codice ASCII delle unità (passo 5) mov ah,01h int 21h ;in AL ho il codice ASCII del carattere delle unità ;sottraggo 48 per ottenere la cifra e salvo il risultato nella variabile unità (passo 6 e 7) sub al,48d add var, al Nel caso di numeri a tre cifre il problema si risolve in modo analogo aggiungendo la lettura delle centinaia (che andranno moltiplicate per cento) e facendo attenzione che potrebbero servire 16 bit per contenere il numero inserito. Output di numeri Purtroppo non esiste un servizio del DOS che consenta di visualizzare numeri decimali; per realizzare tale scopo è indispensabile convertire il numero decimale che si vuole visualizzare nei codici ASCII delle cifre che lo compongono e viceversa. Il problema è semplice nel caso di numeri ad una cifra in quanto le cifre decimali occupano posizioni adiacenti nella tabella dei codi ASCII ed in particolare: Carattere 0 1 2 3 4 5 6 7 8 9 Codice ASCII 48 (0+48) 49 (1+48) 50 (2+48) 51 (3+48) 52 (4+48) 53 (5+48) 54 (6+48) 55 (7+48) 56 (8+48) 57 (9+48) Se, dunque, si vuole stampare il contenuto del numero minore di dieci contenuto nella variabile var bisogna: ;Spostarlo nel registro AL per prepararsi alla stampa mov AL, var ;Aggiungere al contenuto di DL 48 per trasformare il numero nella codifica ASCII ;della cifra corrispondente ADD AL,48d ;Selezionare il servizio di stampa mettendo 02 nel registro AH MOV AH,02d ;Invocare l’int 21h Int 21h Risulta ovviamente più complessa la stampa di un numero a due cifre. Il procedimento di stampa di un numero a due cifra può essere diviso nelle seguenti fasi 1. 2. 3. 4. 5. 6. 7. 8. 9. Divisione del numero da stampare per 10. Questo primo passo richiede a) Spostamento del dividendo (numero da stampare) in AL b) Azzeramento del registro AH in quanto il dividendo di una divisione è tutto AX c) Spostamento del divisore (10) in un registro d) Divisione per il registro in cui si è messo il divisore Salvataggio in qualche registro delle Unità costituite dal resto della divisione e contenute (come da specifiche della divisione) in AH Conversione della cifra delle decine (contenuta in AL) in carattere ASCII sommando 48 Selezionare il servizio di stampa mettendo 02 in AH Invocare int 21h per stampare le decine Spostare le unità precedentemente salvare in AL Conversione della cifra delle unità (contenuta ORA in AL) in carattere ASCII sommando 48 Selezionare il servizio di stampa mettendo 02 in AH Invocare int 21h per stampare le decine Il codice risulta, dunque essere il seguente: ;Metto il numero a due cifre da stampare in al in quanto dovrò dividerlo ;per dieci ed il dividendo va in AX (passo 1a) mov al,ris ;Azzero AH in quanto il dividendo è tutto AX (passo 1b) mov ah,00d ;Metto in BL 10d (divisore) (passo 1c) mov bl, 10d ;divido per 10 (passo 1d) div bl ;adesso in ah ho il resto delle divisione (unità) ed in AL il quoziente (centinaia) ; salvo in qualche registro (DL) le unità mov dl, AH ;Adesso ho decine in AL e unità in DL non devo far altro che stamparli, uno alla volta ;ricordandomi di convertirli nel rispettivo codice ascii sommando 48 ;stampo le decine (passi 3, 4, 5) add al,48d mov ah,02h int 21h ;stampo le unita (passi 6,7,8,9) mov al,dl add al,48d mov ah,02h int 21h Lezione Quattro: Istruzioni di salto e simulazioni costrutti di selezione e iterativo (libro di testo pagina 142-150) Salto incodizionato Sintassi: JMP etchetta Effetto: La prossima istruzione che verrà eseguita non sarà quella che segue ma quella posizionata dopo la scritta etichetta: Confronto Sintassi: CMP op1, op2 Effetto: Setta opportunamente i registri di stato in modo da memorizzare se: • op1>op2 • op1>=op2 • op1=op2 • op1<>op2 • op1<=op2 • op1<op2 Limitazioni: • op1 e op2 devono avere la stessa dimensione • op1 e op2 non devono essere entrambe fornite per indirizzamento diretto Salto codizionato Sintassi: Jxx etichetta dove xx sono una o due lettere che verranno specificate di seguito che indicano in che condizione saltare. L'istruzione di salto condizionato deve essere preceduta da un CMP op1,op2 possibili condizioni: JE → salta se op1 = op2 JNE → salta se op1<> op2 JA → salta se op1>op2 JAE → salta se op1>= op2 JB → salta se op1< op2 JBE → salta se op1<op2 Effetto Se la condizione è rispettata si salta a etichetta altrimenti si esegue l'istruzione seguente Simulazione del costrutto IF in assembly Supponiamo di voler simulare il seguente programma C if(var1>var2){ ist1 ist2 } else{ ist3 ist4 } ist5 ;per prima cosa devo confrontare var1 con var2 ma poiché l'istruzione cmp non ammette due operandi con indirizzamento indiretto devo spostare una delle due (var1) in un registro (per esempio al) MOV AL,var1 CMP AL,var2 ;se la condizione è rispettata (cioè se al>var2) altrimenti devo eseguire ist3 e ist4 JA allora ;se var1 non è maggiore di var2 JA non esegue il salto e prosegue quindi qui devo mettere le istruzioni da eseguire quando la condizione non è rispettata (ist3 e ist4) mentre, successivamente e dopo l'etichetta allora: scrivero le istruzioni ist1 e ist2 IST3 IST4 ;a questo punto devo andare alla fine dell'if altrimenti eseguirei tutte e due le parti (then e else) JMP fine allora: ;qui è il punto in cui salterà la precedente istruzione JA quindi bisogna mettere le istruzioni ist1 e ist2 IST1 IST2 ;a questo punto si mette l'etichetta di fine della struttura fine: IST5 Simulazione del costruttoWHILE in assembly Supponiamo di voler simulare il seguente programma C while(var1>var2){ ist1 ist2 } ist3 ;come prima operazione devo mettere un'etichetta di inizio ciclo ciclo: ;prima di eseguire il corpo devo confrontare var1 con var2 ma poiché l'istruzione cmp non ammette due operandi con indirizzamento indiretto devo spostare una delle due (var1) in un registro (per esempio al) MOV AL,var1 CMP AL,var2 ;se la condizione non è rispettata (cioè se al<=var2) devo uscire dal ciclo quindi salto ad un etichetta che metterò alla fine JBE fine ;se non sono uscito dal ciclo eseguo il suo corpo (ist1 e ist2) e poi salto in modo incodizionato in testa (etichetta ciclo) al fine di ri testare la condizione IST1 IST2 JMP ciclo fine: IST3