Assembly 8086 -...

19
Sistemi - Classe Terza robertomana.it Cenni di Assembler pag 1 Assembly 8086 - Introduzione Rev. Digitale 1.0 del 01/09/2016 E‟ un linguaggio mnemonico in corrispondenza 1 : 1 con le istruzioni binarie riconosciute dalla CPU 8086. Cioè codifica con un nome mnemonico tutte le possibili istruzioni binarie riconosciute dalla CPU. Assemblatore Un particolare programma, detto assemblatore (assembler), traduce il file testuale assembly in linguaggio macchina, producendo un file binario che contiene il codice binario corrispondente al programma tradotto. L‟assemblatore, oltre al file OBJ, produce in genere anche un interessante file LST contenente l‟informazione binaria espressa in formato esadecimale con a fianco l‟istruzione testuale assembly. Linker Molto spesso una applicazione è costituita da più file sorgenti, ognuno dei quali contiene specifiche procedure, scritte spesso da persone diverse all‟interno di un team di lavoro. Uno di questi file può far riferimento a funzioni o simboli definiti all‟interno di un altro file. L‟assemblatore, che traduce ogni singolo file in binario, deve prevedere un meccanismo che consenta di gestire queste situazioni senza generare errori. A tal fine l‟assembly prevede l‟utilizzo di pseudoistruzioni che avvisano l‟assemblatore su simboli e procedure che si trovano in altri file. In corrispondenza di queste pseudoistruzioni, l‟assemblatore crea delle „note‟ all‟interno del file binario, che dunque non è più un file eseguibile, ma un file detto file oggetto, sempre binario, ma contenente delle note che verranno interpretato da un programma detto linker , che provvede ad integrare i vari file oggetto in un unico eseguibile risolvendo tutte le varie situazioni lasciate in sospeso dall‟assemblatore. Loader In realtà il codice macchina generato dal linker è un codice rilocabile , cioè che potrà essere caricato in memoria centrale a partire da un qualunque indirizzo. Ciò è possibile mediante l‟utilizzo dei Segment Register il cui contenuto viene assegnato dal loader durante il caricamento in memoria dell‟applicazione. Il loader è un componente del sistema operativo che viene automaticamente richiamato nel momento in cui l‟utente digita sulla riga di comando il nome di un file eseguibile (o fa doppio click sulla sua icona). Il loader provvede a trasferire il codice dell‟eseguibile all‟interno della memoria centrale, provvedendo ad assegnare un valore ai vari segmenti definiti dal linker. Caratteristiche di MASM 6.0 (Macro Assembler) Ogni riga deve essere terminata con un INVIO (ASCII 10). Uno statement può occupare più righe. La precedente deve terminare con \. La seguente deve iniziare con & E‟ case unsensitive: si può scrivere indifferentemente in minuscolo o maiuscolo Normalmente si usa il minuscolo, riservando il maiuscolo per PSEUDOISTRUZIONI e COSTANTI I commenti sono introdotti dal punto virgola Gli identificatori hanno lunghezza massima 31 chr (senza spazi) e devono iniziare con una lettera. Sono ammessi i seguenti 4 caratteri speciali _ ? @ $ Principali Direttive Assembly (Pseudoistruzioni) Non corrispondono ad una precisa istruzione binaria di codice, ma sono direttive che consentono di : Definire dimensioni e tipologia dei vari segmenti (l‟unico ad essere dimensionato automaticamente è il Code Segment). Queste informazioni vengono salvate dal linker all‟interno dell‟header del programma exe secondo un preciso formato che è quello utilizzato dal loader del SO per caricare il programmi. Assegnare un nome alle celle di memoria del Data Segment. In questo modo quando il programma dovrà accedere ad una cella di memoria potrà accedere molto più comodamente attraverso il nome piuttosto che attraverso l‟indirizzo. Assegnare un nome (etichetta) a particolari righe di codice, in modo che quando il programma dovrà eseguire un salto a quella riga potrà utilizzare l‟etichetta anziché l‟indirizzo.

Transcript of Assembly 8086 -...

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 1

Assembly 8086 - Introduzione Rev. Digitale 1.0 del 01/09/2016

E‟ un linguaggio mnemonico in corrispondenza 1 : 1 con le istruzioni binarie riconosciute dalla CPU 8086. Cioè

codifica con un nome mnemonico tutte le possibili istruzioni binarie riconosciute dalla CPU.

Assemblatore

Un particolare programma, detto assemblatore (assembler), traduce il file testuale assembly in linguaggio macchina,

producendo un file binario che contiene il codice binario corrispondente al programma tradotto. L‟assemblatore, oltre

al file OBJ, produce in genere anche un interessante file LST contenente l‟informazione binaria espressa in formato

esadecimale con a fianco l‟istruzione testuale assembly.

Linker

Molto spesso una applicazione è costituita da più file sorgenti, ognuno dei quali contiene specifiche procedure, scritte

spesso da persone diverse all‟interno di un team di lavoro. Uno di questi file può far riferimento a funzioni o simboli

definiti all‟interno di un altro file. L‟assemblatore, che traduce ogni singolo file in binario, deve prevedere un

meccanismo che consenta di gestire queste situazioni senza generare errori. A tal fine l‟assembly prevede l‟utilizzo di

pseudoistruzioni che avvisano l‟assemblatore su simboli e procedure che si trovano in altri file. In corrispondenza di

queste pseudoistruzioni, l‟assemblatore crea delle „note‟ all‟interno del file binario, che dunque non è più un file

eseguibile, ma un file detto file oggetto, sempre binario, ma contenente delle note che verranno interpretato da un

programma detto linker, che provvede ad integrare i vari file oggetto in un unico eseguibile risolvendo tutte le varie

situazioni lasciate in sospeso dall‟assemblatore.

Loader

In realtà il codice macchina generato dal linker è un codice rilocabile, cioè che potrà essere caricato in memoria

centrale a partire da un qualunque indirizzo. Ciò è possibile mediante l‟utilizzo dei Segment Register il cui contenuto

viene assegnato dal loader durante il caricamento in memoria dell‟applicazione. Il loader è un componente del

sistema operativo che viene automaticamente richiamato nel momento in cui l‟utente digita sulla riga di comando il

nome di un file eseguibile (o fa doppio click sulla sua icona). Il loader provvede a trasferire il codice dell‟eseguibile

all‟interno della memoria centrale, provvedendo ad assegnare un valore ai vari segmenti definiti dal linker.

Caratteristiche di MASM 6.0 (Macro Assembler)

Ogni riga deve essere terminata con un INVIO (ASCII 10).

Uno statement può occupare più righe. La precedente deve terminare con \. La seguente deve iniziare con &

E‟ case unsensitive: si può scrivere indifferentemente in minuscolo o maiuscolo

Normalmente si usa il minuscolo, riservando il maiuscolo per PSEUDOISTRUZIONI e COSTANTI

I commenti sono introdotti dal punto virgola

Gli identificatori hanno lunghezza massima 31 chr (senza spazi) e devono iniziare con una lettera.

Sono ammessi i seguenti 4 caratteri speciali _ ? @ $

Principali Direttive Assembly (Pseudoistruzioni)

Non corrispondono ad una precisa istruzione binaria di codice, ma sono direttive che consentono di :

Definire dimensioni e tipologia dei vari segmenti (l‟unico ad essere dimensionato automaticamente è il Code

Segment). Queste informazioni vengono salvate dal linker all‟interno dell‟header del programma exe

secondo un preciso formato che è quello utilizzato dal loader del SO per caricare il programmi.

Assegnare un nome alle celle di memoria del Data Segment. In questo modo quando il programma dovrà

accedere ad una cella di memoria potrà accedere molto più comodamente attraverso il nome piuttosto che

attraverso l‟indirizzo.

Assegnare un nome (etichetta) a particolari righe di codice, in modo che quando il programma dovrà eseguire

un salto a quella riga potrà utilizzare l‟etichetta anziché l‟indirizzo.

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 2

Title

La direttiva TITLE consente di assegnare un titolo descrittivo al programma assembly. TITLE *** Calcolo di Area e Perimetro di un Rettangolo ***

Costanti

Subito dopo il titolo si possono definire, mediante la direttiva EQU delle costanti, cioè assegnare una etichetta

identificativa ad un valore, in modo da aumentare la leggibilità del programma.

Le costanti possono essere :

Decimali: 13, 13D

Binarie 00001111B

Esadecimali 72H, 0DH, 0A1H

devono iniziare con un numero. Se iniziano con una lettera occorre premettere uno zero 0A1H

Floating Point 2.345, 715E-3

Stringhe ASCII „CIAO‟, „2‟ ; lunghe uno o più caratteri. Apice Singolo e Apice Doppio sono indifferenti

Esempi: COLUMNS EQU 80

ROWS EQU 25

E‟ possibile anche creare degli alias, cioè simboli che rappresentano con altri nomi simboli già definiti in precedenza.

COLONNE EQU COLUMNS

RIGHE EQU ROWS

SCREEN EQU COLONNE * RIGHE

In alternativa ad EQU per definire delle costanti è anche possibile utilizzare l‟operatore di assegnazione = .

Direttive di Segmento

I programmi Assembly sono costituiti tipicamente da 3 segmenti: CODICE, DATI e STACK.

Per definire un segmento occorre utilizzare la seguente direttiva:

nomeSeg SEGMENT contenuto del segmento

nomeSeg ENDS

Definizione di variabili

All‟interno del Data Segmenti si possono “definire” delle celle di memoria, assegnando loro una etichetta

identificativa ed un valore. L‟etichetta potrà poi essere utilizzata dal programma per accedere alle celle medesime.

Dopo l‟etichetta i due punti di suddivisione dell‟etichetta dalla parte successiva non sono in questo caso accettati.

db definisce un byte

num1 db 60

num2 db ? ; definisce un byte senza assegnare nessun valore iniziale

num3 db 00001110b

Può essere anche utilizzata per memorizzare uno o più caratteri ASCII

var1 db „A‟, „B‟, „C‟ ; definisce tre byte contenenti i codici ASCII dei caratteri indicati

var2 db “ABC” ; equivalente alla precedente

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 3

dw definisce una word (2 bytes)

num1 dw 260

num2 dw ? ; definisce una word senza assegnare nessun valore iniziale

num3 dw AAFFH

Nota: dw non può essere utilizzata per memorizzare una sequenza di caratteri ASCII, ma solo per uno o due caratteri chr dw „A‟ ; viene memorizzato 0 nel byte alto e 65 nel byte basso

chr dw “AB” ; viene memorizzato 65 nel byte alto e 66 nel byte basso

Vettori

Per i nomi dei vettori è consigliato anteporre l‟underscore davanti.

_vect db 1,2,3,4,5,6,7,8,9,0 ;definisce un vettore di 10 bytes inizializzandolo con i valori indicati

_vect dw 1200, 2400, 3600 ;definisce un vettore di 3 word

_vect db 10 dup (0) ;definisce un vettore di 10 bytes tutti con valore 0

_vect dw 10 dup (?) ;definisce un vettore di 10 word non inizializzate

Nota: Per visualizzare il contenuto di una variabile NUM1 su code view utilizzare il comando W NUM1

Per visualizzare invece un vettore _vett lungo 10 su code view utilizzare il comando W _vett L A

Nota: dw può anche essere utilizzata per memorizzare un offset:

lista DB 100 DUP (?) ; 100 celle non inizializzate

listaOffset DW lista ; offset di lista

dd definisce una double word (4 bytes)

num1 dd 125000 ; maggiore di 65000

num2 dd 13.76 ; floating point

dq definisce una quad word (8 bytes). Numeri Double. Non prevista nelle versioni base di Assembly 8086

Nota: dd può anche essere utilizzata per memorizzare un indirizzo completo (segmento + offset) :

lista DB 100 DUP (?) ; 100 celle non inizializzate

listaOffset DD lista ; i due bytes alti contengono il segmento, i due byte bassi contengono l’offset

La direttiva END

Terminata la dichiarazione di tutti i segmenti, la direttiva END termina il modulo di compilazione. Il suo scopo è

quello di poter specificare una etichetta per indicare il punto di partenza del programma (entry point) .

In caso di unico segmento di codice, l‟entry point può essere omesso, nel qual caso il programma partirà dalla 1°

istruzione dell‟unico segmento di codice. Il loader provvede automaticamente a caricare CS e inizializzare PC a 0

In caso di più segmenti di codice, l‟entry point deve essere necessariamente specificato ed il loader caricherà

automaticamente all‟interno di CS l‟indirizzo di partenza del segmento contenente l‟entry point e dentro PC l‟offset

dell‟Entry Point rispetto all‟inizio del segmento. Esempio: END main

Etichette relative alle istruzioni

Davanti alle istruzioni si può aggiungere una etichetta alfanumerica (con primo carattere una lettera) che rappresenta

l‟indirizzo simbolico dell‟istruzione. I due punti devono separare l‟etichetta dall‟istruzione. Queste etichette sono

utilizzate principalmente per gestire le istruzioni di salto:

RIT: mov AX, 0

. . . . . . . . . . . . .

jmp RIT

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 4

Modello di un programma Assembly

.model small ; un solo segmento di codice ed un solo segmento di dati

.stack ; definisce uno stack segment di nome @stack grande 1024 bytes

; stack ends

.data ; definisce un data segment di nome @data

num1 dw 61

num2 dw 4

ris dw ?

; data ends

.code ; definisce un code segment di nome @code

; CS ed SS sono inizializzati automaticamente ai rispettivi segmenti

; DS non viene caricato in automatico in quanto possono esistere più dichiarazioni di DATA

SEGMENT all‟interno di file differenti. Queste dichiarazioni, nell‟ambito del MODEL

SMALL, verranno poi combinate in un unico segmento in un ordine che solo il linker conosce mov ax, @data

mov ds, ax

mov ax,num1 ; AX <- DS:NUM1

add ax,num2

mov ris, ax

mov ah, 4ch ; ritorno al DOS

int 21h

; code ends

end

Modelli di mappatura dei segmenti : La pseudoistruzione .MODEL

Consente di definire il modello di memoria che dovrà essere utilizzato nella compilazione del file. Modelli possibili:

Code Seg Data Seg Data e Code Combinati TINY Near Near Sì

SMALL Near Near No

MEDIUM Far Near No

COMPACT Near Far No

LARGE Far Far No

Dichiarare i segmenti di codice di tipo NEAR significa che tutti i segmenti di codice appartenenti ai vari moduli

verranno mappati su uno stesso segmento fisico di codice. Ciò implica che tutte le procedure e tutte le etichette

di salto saranno indirizzate come NEAR, cioè indirizzate utilizzando soltanto offset a 16 bit. Dichiarare invece i

segmenti di codice di tipo FAR significa che ogni segmento (eventualmente anche per segmenti appartenenti ad uno

stesso modulo) verrà mappato su un segmento di codice fisico diverso. Ciò implica che tutte le procedure e tutte le

etichette saranno indirizzate come FAR, cioè indirizzate utilizzando segmento + offset.

Dichiarare i data segment come NEAR significa che variabili sono indirizzate come NEAR, cioè soltanto mediante

un offset a 16 bit, mentre FAR significa che saranno tutte indirizzate mediante segmento + offset, cioè posso

indirizzare direttamente una variabile non contenuta nei segmenti attivi: MOV AX, FAR PTR _DATA2:NUM1

Nel modello TINY codice e dati vengono mappati in uno stesso segmento (vecchi file .COM più compatti degli

EXE). Il modello SMALL (utilizzato di default) prevede un unico segmento di codice con dim max 64 K e

l‟accesso soltanto ai dati memorizzati nei due data segment attivi . Per le applicazioni più grandi si utilizza il

modello LARGE che può gestire più segmenti di codice e più segmenti di dati paralleli.

Nei sistemi IA32 (che operano in modalità protetta), si utilizza un nuovo tipo di modello che è il modello FLAT.

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 5

Formato ed avvio di un programma eseguibile

Nel momento in cui il linker provvede a creare il file eseguibile, provvede anche a generare all‟inizio del file stesso

una testata lunga 512 bytes contenente le informazioni necessarie al caricamento del programma in memoria, cioè

sostanzialmente un elenco dei segmenti definiti all‟interno del programma stesso, ciascuno con il proprio indirizzo

virtuale di partenza (partendo da 0) e lunghezza espressa in byte.

Il linker, a richiesta, può generare un file .MAP contenente le informazioni che verranno salvate in testa all‟eseguibile

Start Stop Length Type (Name)

00000H 00003H 00004H DATA @DATA

00010H 0008FH 00080H STACK @STACK

00090H 000BCH 0002DH CODE @CODE

Program entry point at 0009:0000

Il Name è un identificativo mostrato all‟interno del file .MAP. In fase di avvio del programma, il loader provvede ad

allocare in memoria i segmenti necessari, assegnando ai vari segmenti un ben preciso indirizzo fisico.

Inoltre provvede automaticamente a:

inizializzare il registro CS al punto di partenza del segmento di codice contenente l‟Entry Point del programma

inizializzare il registro SS al registro unico di stack dichiarato con l‟opzione di classe „STACK‟

Riservare, al di sopra del segmento di codice, un‟area di 256 bytes (100H) detta PSP (Program Segment

Prefix, cioè Prefisso del Segmento di Programma) utilizzato dal DOS per memorizzare le informazioni

necessarie a ritrasferire il controllo al Sistema Operativo una volta terminata l‟esecuzione del programma.

L‟istruzione finale INT21H (4CH) esegue sostanzialmente un salto all‟indirizzo _CODE – 100H. DS ed ES

vengono entrambi utilizzati per scrivere quest‟area per cui, al termine del caricamento punteranno entrambi a

_CODE – 100H e dovranno essere inizializzati manualmente dal programma andando a scrivere al loro interno

l‟indirizzo effettivo di partenza del Data Segment.

L’istruzione MOVE e le modalità di Indirizzamento

MOV Dest, Source

E‟ l‟istruzione principale del linguaggio Assembly e consente di spostare dati tra una sorgente ed una destinazione,

dove sorgente e destinazione possono essere i registri della CPU o le celle di memoria. Il codice binario è il seguente:

100010dw (in esadecimale 88 o 89 o 8A o 8B) dove:

il bit w indica se deve essere trasferito un byte (w=0) oppure, più frequentemente, una word (w=1)

il bit d indica la direzione del trasferimento. d = 1 significa from MFIELD to REGister e viceversa

Modalità di Indirizzamento

Le modalità fondamentali sono 5 :

(1) Registro – Registro

(2) Indirizzamento diretto

(3) Indirizzamento indiretto

(4) Indirizzamento indiretto con displacement

(5) Indirizzamento immediato (in un registro o in memoria)

Insieme all‟istruzione MOV c‟è sempre un secondo byte che indica quale tipo di indirizzamento utilizzare e quali

sono i registri coinvolti. Il secondo byte è strutturato nel modo seguente:

MM RRR FFF dove i tre gruppi di bit sono indicati rispettivamente come MOD REG MFIELD

MOD(2 bit) indicano la modalità di trasferimento da adottare fra le prime 4 modalità precedenti

REG (Register su 3 bit ) indicano il registro coinvolto nel trasferimento (8 registri possibili)

MFIELD (Memory Field su 3 bit) indicano il registro o cella di memoria coinvolti nel trasferimento.

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 6

Il significato di MFIELD dipende dalla modalità di trasferimento adottata.

Il campo REG ha sempre il seguente significato :

REG W=0 W=1

000 AL AX

001 CL CX

010 DL DX

011 BL BX

100 AH SP

101 CH BP

110 DH SI

111 BH DI

(1) Modalità di trasferimento Registro – Registro (MOD = 11 )

In questo caso MFIELD utilizza la stesa codifica di REG, e contiene il codice del secondo registro coinvolto

nell‟operazione. Nella modalità REGISTER – REGISTER il bit d è sempre 1, cioè :

MFIELD rappresenta sempre il registro sorgente, mentre REG rappresenta sempre il registro destinatario.

mov CX, BX ; d = 1, w = 1, REG = 001, MFIELD = 011 8B CB

mov CL, BL ; d = 1, w = 0, REG = 001, MFIELD = 011 8A CB

mov CH, CL ; d = 1, w = 0, REG = 101, MFIELD = 001 8A E9

(2) Modalità di trasferimento diretto da memoria a registro e viceversa (MOD = 00 e MFIELD = 110)

Il campo MFILED assume il valore fisso 110.

Il campo REG indica il registro destinatario (d=1) oppure il registro sorgente (d=0).

num1 db 60

num2 dw 600

mov DH, num1 ; d = 1, w = 0, REG = 110, MFIELD = 110 + offset 2 bytes

mov num1, DH ; d = 0, w = 0, REG = 110, MFIELD = 110 + offset 2 bytes

mov DX, [num2] ; d = 1, w = 1, REG = 010, MFIELD = 110 + offset 2 bytes

mov [num2], DX ; d = 0, w = 1, REG = 010, MFIELD = 110 + offset 2 bytes 89 16

Oltre ai due bytes relativi a codice ed indirizzamento ci sono questa volta anche 2 bytes contenenti l‟offset della

variabile di memoria. Le parentesi quadre intorno al nome della variabile sono opzionali. Generalmente sono omesse.

L‟offset si intende sempre riferito a DS, salvo indicazione esplicita di segment override. mov DH, ES:num1

Le parentesi quadre diventano obbligatorie quando al posto dell‟etichetta si utilizza un offset scritto in modo diretto

(per non confondere l‟offset con un dato immediato).

mov DS:[3], DX ; copia DX nella quarta cella del Data Segment

(3) Modalità di trasferimento indiretto senza displacement (MOD = 00 e MFIELD != 110)

Effettuare un trasferimento indiretto significa non specificare direttamente il nome della cella di memoria da leggere

o scrivere, ma scrivere il suo offset all’interno di un apposito registro (normalmente SI o DI).

. Es MOV AL, [SI] copia dentro AL il contenuto della cella puntata da SI.

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 7

Esempio: Somma dei numeri contenuti in un vettore

.data

0 num1 db ?

1 num2 db ?

2 ris dw ?

4 vect db 10 dup (12, 5, 6, 8, 7, 15, 6, 21, 5, 11)

.code

mov AX @data

MOV DS, AX

MOV SI, 4

MOV AX, 0

RIT: ADD AL, [SI] ; somma AL + la cella puntata da SI rispetto all‟inizio del Data Segment

INC SI

CMP SI, 14

JB RIT

MOV RIS, AX

(4) Modalità di trasferimento indiretto con displacement (MOD = 01 e 10)

MOD 01 indica un displacement a 8 bit, mentre MOD = 10 indica un displacement a 16 bit.

Il displacement sarà memorizzato di seguito all‟istruzione. Viene automaticamente scelto il displacement a 8 bit per

offset inferiori a 255, mentre viene automaticamente scelto un displacement a 16 bit per offset maggiori di 255

Esempio: Riscrittura del codice precedente con utilizzo di un displacement

.data

0 num1 db ?

1 num2 db ?

2 ris dw ?

4 vect db 10 dup (12, 5, 6, 8, 7, 15, 6, 21, 5, 11)

.code

mov AX @data

MOV DS, AX

MOV SI, 0

MOV AX, 0

RIT: ADD AL, vect[SI] ; somma AL + la cella puntata da SI rispetto a VECT

INC SI

CMP SI, 10

JB RIT

MOV RIS, AX

(5a) Trasferimento di un dato immediato in un registro o in memoria

L‟istruzione di trasferimento di un dato immediato è una istruzione MOV con codifica differente rispetto alla

codifica precedente. Trasferire un dati immediato in un registro (o in memoria) significa che il dato è contenuto

all’interno dell’istruzione stessa. Il trasferimento diretto può essere solo TO register o TO memory (non FROM)

MOV AX, 70 MOV NUM, 70 MOV [SI], 70 MOV [SI+5], 70 MOV VECT[SI], 70

Combinazioni non ammesse dall’istruzione MOV

il registro IP non può essere utilizzato né in scrittura né in lettura

il registro CS non può essere destinazione

memoria – memoria. Si deve passare attraverso un registro

segment register – segment register.

Caricamento di un numero immediato in un segment register

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 8

Breve panoramica sulle Istruzioni Assembly

Istruzioni Aritmetiche

ADD op1, op2 op1 op1 + op2

SUB op1, op2 op1 op1 - op2

MUL op2 AX AL * OP 8bit

DX : AX AX * OP 16bit

DIV op2 AX / OP 8bit => AL quoziente, AH resto

DX : AX / OP 16bit => AX quoziente, DX resto

L’istruzione JMP

L‟istruzione JMP esegue un salto incondizionato all‟etichetta indicata:

_RIT : mov AX, 0

. . . . . . . . . .

. . . . . . . . . .

JMP _RIT

L‟etichetta rappresenta un offset a 16 bit (± 32000 bytes rispetto alla posizione corrente) all‟interno del segmento

corrente. La codifica binaria dell‟istruzione JMP occupa normalmente 3 byte. L‟assembler può generare

automaticamente una istruzione su 2 soli byte nel momento in cui si accorge che l‟offset può essere memorizzato

all‟interno di un singolo byte (± 127 bytes rispetto alla posizione corrente).

In corrispondenza del jump, il processore provvede automaticamente a caricare all’interno del Program

Counter l’indirizzo dell’etichetta (offset) a cui si richiede di eseguire il salto. Il valore corrente del Program

Counter viene sovrascritto e dunque perso definitivamente.

Nota 1: Salto ad un offset contenuto in un registro

Oltre ad una etichetta diretta, il jump può anche essere eseguito alla cella puntata da un registro indicato nel

successivo byte di indirizzamento così strutturato:

JMP [SI] MOD 100 MFIELD

dove MOD e MFIELD sono gli stessi della MOV (non è consentito MOD = 11 indicante Register Register).

Nota 2: Salto ad una label di un altro segmento

L’indirizzo a cui viene trasferito il controllo può essere nello stesso segmento (jump NEAR) oppure può anche

appartenere ad un segmento diverso (jump FAR), nel qual caso occorrerà definire davanti al punto di salto,mediante

la pseudoistruzione LABEL FAR, una etichetta a 4 byte.

seg1 SEGMENT

ETICHETTA1 LABEL FAR : . . . . . . . . . . . .

seg1 ENDS

seg2 SEGMENT

JMP FAR PTR Seg1:ETICHETTA1

seg2 ENDS

Nel caso di un JUMP FAR, l‟assemblatore provvede a

scrivere, dopo il codice dell‟istruzione jump, sia il

segmento sia l‟offset a cui deve essere eseguito il salto.

L‟indirizzo di segmento verrà caricato dentro CS, mentre

l‟offset verrà caricato nel Program Counter

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 9

L’istruzione CMP

L‟istruzione CMP (compare) supporta all‟incirca tutte le stesse modalità di indirizzamento dell‟istruzione MOV.

CMP esegue la differenza tra il primo operando meno il secondo operando, senza memorizzare alcun risultato,

ma provvedendo soltanto a settare i flag, in particolare

ZF se il risultato della differenza è zero

SF contenente il segno del risultato

CF carry flag

OF overflow flag

Istruzioni di salto condizionato

Le istruzioni di salto condizionato sono di solito utilizzate immediatamente dopo la COMPARE, che esegue un

confronto fra due numeri. A seconda dell‟esito del confronto (valore dei bit di stato) il salto potrà essere eseguito

oppure non eseguito.

Le istruzioni di salto condizionato possono comunque essere utilizzate anche dopo ADD, SUB o altre istruzioni,

sempre sulla base del valore corrente dei bit di stato.

Operandi Unsigned

Se gli operandi del CMP sono numeri unsigned, si possono utilizzare le seguenti istruzioni di salto condizionato, che

utilizzano un offset di salto memorizzato su un solo byte (per cui il salto può avere una distanza massima di ± 127

bytes). Dunque sono istruzioni più veloci ma:

Possono essere utilizzate per confrontare soltanto numeri positivi

Possono eseguire al max un salto pari a ± 127 bytes

JA jump if above. Salta se NUM1 > NUM2.

JAE jump if above or equal

JB jump if below. Salta se NUM1 < NUM2.

JBE jump if below or equal

Nota: Tecnicamente JA esegue il salto se CF e ZF sono entrambi uguali a zero cioè se non c’è prestito nella

sottrazione (cioè se NUM1 > NUM2) e se i due numeri non sono uguali.

JE salta se i due numeri confrontati dalla CMP sono uguali, cioè se il risultato della CMP è zero (ZF == 1).

JNE salta se i due numeri confrontati dalla CMP non sono uguali (ZF == 0)

JZ stessa cosa di JE (cambia soltanto il nome mnemonico)

JNZ stessa cosa di JNE (cambia soltanto il nome mnemonico)

Queste ultime 4 istruzioni possono ovviamente essere utilizzate anche per i numeri signed, ma usano offset di 8 bit

Operandi Signed

Le seguenti istruzioni di salto condizionato sono più generali e possono essere utilizzate in seguito ad un confronto

fra numeri signed. Il salto prevede un offset memorizzato su 16 bit (± 32000 bytes rispetto alla posizione corrente).

JG jump if greater. Salta se NUM1 > NUM2.

JGE jump if greater or equal.

JL jump if less. Salta se NUM1 < NUM2.

JLE jump if less or equal.

Nota: Tecnicamente JG esegue il salto se ZF=0 e se SF = OF, cioè se:

- non c’è overflow ed il segno del risultato è positivo (5, 3 o -3,-5 o 5,-3 )

- c’è overflow ed il segno del risultato è negativo (124, -5 oppure 5, -124 oppure)

Se non c’è overflow ed il risultato è negativo non salta (4, 5 oppure -3, 2)

Se c’è overflow ed il risultato è positivo non salta ( -124, +5; -129 viene codificato come +127).

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 10

Traduzione dell’istruzione IF

L‟istruzione IF, tipica dei linguaggi di alto livello, può essere tradotta in Assembly nel modo seguente:

CMP A, B

JB VERO

operazioni NO

JMP FINEIF

VERO:

operazioni SI

FINEIF:

.................

Se sul ramo NO non c‟è nessuna operazione da eseguire, si può utilizzare il seguente schema semplificato ottenuto

invertendo la condizione del primo jump

CMP A, B

JAE FINEIF

operazioni SI

FINEIF:

.................

Traduzione del ciclo post-condizionale

Il ciclo post-condizionale è il più semplice da utilizzare in Assembly.

Può essere tradotto nel modo seguente:

XOR CX, CX

INIZIO_CICLO:

.................

.................

.................

INC CX

CMP CX, 20 ; numero di cicli da effettuare

JB INIZIO_CICLO

.................

A<B Si No

operazioni SI operazioni NO

FINEIF:

VERO:

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 11

I sottoprogrammi La definizione di ogni procedura deve iniziare con una direttiva PROC e deve terminare con

una direttiva ENDP.

stampa PROC

. . . . . . . . . . . .

. . . . . . . . . . . .

RET

stampa ENDP

stampa è il nome simbolico della procedura, che dovrà essere utilizzato per chiamare la procedura stessa.

Le procedure possono essere scritte prima del main, nel qual caso occorre dichiarare esplicitamente un entry point di

inizio del main, oppure dopo le istruzioni di ritorno al sistema operativo in modo da essere fuori dalla sequenza di

esecuzione principale.

Nota: Dopo PROC si può indicare NEAR / FAR

- NEAR indica che la procedura può essere chiamata solo all'interno del segmento in cui è stata definita (default)

- FAR indica che la procedura può essere chiamata da qualsiasi segmento.

Chiamata ad una procedura mediante l’istruzione CALL

L‟istruzione CALL trasferisce il controllo dal programma chiamante alla procedura chiamata. A differenza

dell‟istruzione JUMP in cui il valore corrente del Program Counter va perso in quanto soprascritto dal nuovo valore,

nel caso delle CALL il valore corrente del Program Counter viene automaticamente salvato all‟interno dello stack

prima di essere soprascritto in modo che, terminata la procedura, la CPU possa riesumare il vecchio valore del

Program Counter dallo stack e riprendere l‟esecuzione esattamente dal punto in cui si era fermata.

In sintesi, in corrispondenza della CALL, vengono automaticamente eseguite le seguenti operazioni:

il valore corrente del Program Counter viene salvato in cima allo stack (operazione di PUSH)

all’interno di IP viene caricato l’indirizzo (offset) del sottoprogramma a cui eseguire il salto

lo Stack Pointer SP viene decrementato di 2

Nota: Se la procedura è stata dichiarata FAR all’interno di un altro segmento, la chiamata dovrà essere eseguita

utilizzando la direttiva FAR PTR

call FAR PTR stampa

In tal caso in cima allo stack, ancor prima del Program Counter, viene salvato anche il Code Segment, e SP viene

ulteriormente decrementato di 2.

L’istruzione RET

L‟istruzione RET consente il ritorno dalla procedura chiamata al programma chiamante.

In corrispondenza dell‟istruzione RET la CPU provvede automaticamente a:

leggere dallo stack (mediante una operazione detta POP) l'indirizzo di ritorno salvato dalla CALL

ricaricare l’indirizzo di ritorno all’interno del Program Counter

incrementare di 2 il valore dello Stack Pointer

Nota: Se la procedura era di tipo FAR, oltre al Program Counter verrà ripristinato anche il Code Segment.

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 12

Salvataggio dei registri

E‟ abbastanza probabile che la procedura chiamata debba fare uso dei registri della CPU, registri all‟interno del quale

probabilmente il chiamante stava memorizzando i dati della propria elaborazione. La procedura chiamata, prima di

iniziare la propria elaborazione deve quindi sempre provvedere a salvare nello stack (mediante apposite istruzioni

PUSH) il contenuto di tutti i registri della CPU che dovrà modificare nel corso della propria esecuzione.

push SI

push DI

In corrispondenza di ogni PUSH lo stack pointer verrà automaticamente decrementato di 2.

Prima di eseguire la RET, la procedura chiamata dovrà preoccuparsi di riesumare dallo stack i valori dei registri

precedentemente salvati, in ordine inverso rispetto all‟ordine con cui erano stati salvati (LIFO).

pop DI

pop SI

Secondo le convenzioni utilizzate dai compilatori C non è richiesto il salvataggio dei registri dati (AX, BX, CX,

DX). Cioè al termine della procedura per questi registri non è garantita la conservazione dei valori che avevano prima

della chiamata. Tutti gli altri registri utilizzati devono invece essere salvati.

Variabili globali e variabili locali

int A = 5;

int B;

main() {

int C = 15;

ind D;

. . . . . . . . . . . . . . . . .

}

Le variabili globali A e B sono visibili ed utilizzabili da tutti (main e sottoprogrammi).

Esse vengono allocate all‟interno del Data Segment.

.data

A dw 5;

B dw ?

; data ends

Le variabili locali C e D sono visibili ed utilizzabili soltanto all‟interno della procedura in cui vengono dichiarati

(cioè se sono dichiarati nel main sono visibili ed utilizzabili soltanto dal main).

Esse vengono allocate all‟interno dello stack segment, cioè vengono tradotte del compilatore nel modo seguente::

mov ax, 15

push ax

La seconda (int D) viene tradotta semplicemente mediante una push AX con AX non inizializzato.

Passaggio dei parametri ad una procedura

Spesso una procedura ha la necessità di utilizzare variabili definite all‟interno del main, variabili che pertanto devono

essere passate come parametri alle procedura. Esistono in questo caso tre diverse possibilità:

1) Copiare i valori in una variabile globale del Data Segment, in modo che sia visibile da tutti. Soluzione poco

portabile perché rende la procedura dipendente dalle variabili esterne (procedura non rientrante). Se le variabili

esterne non vengono create correttamente dal chiamante, la procedura fallisce.

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 13

2) Passare i valori nei registri. Anche questa strada non è ottimale dato il numero limitato dei registri e la loro

dimensione fissa. Una variabile a 4 byte può essere passata su due registri, ma diventa difficile passare più

variabili di questo tipo. Inoltre dai linguaggi di alto livello (C, VB, Pascal) non si può accedere direttamente ai

registri di CPU

3) Passare i valori salvandoli all’interno dello stack.. I compilatori ANSI C passano i parametri attraverso lo

stack. Il chiamante prima di eseguire la chiamata copia i parametri all‟interno dello stack, quindi esegue la

chiamata (salvando automaticamente all‟interno dello stack anche il valore del program counter).

La procedura chiamata dovrà andare a leggersi i parametri all‟interno dello stack, facendo attenzione all‟ordine.

Al ritorno dalla procedura, il main deve provvedere a rimuovere i parametri dallo stack, incrementando lo stack

pointer di un valore pari al totale in byte dei parametri passati.

Esempio

Vediamo come viene tradotta in Assemby la seguente chiamata C ad una funzione somma che esegue la somma di tre

numeri interi int a, int b, int c, ciascuno grande 2 bytes

somma (a, b, c);

push c ; SP <- SP - 2

push b ; SP <- SP - 2

push a ; SP <- SP - 2

call _somma „chiamata alla funzione somma

add sp, 6 „rimozione dei parametri dallo stack

L‟istruzione finale ADD SP, 6 ripulisce lo stack sostituendo 3 inutili POP (al main i parametri ormai non servono più)

Procedura chiamata

La procedura chiamata (somma) deve salvare nello stack il contenuto dei registri che andrà ad utilizzare e quindi

deve leggere i parametri che il main gli ha passato (parametri che sono “sepolti” in fondo allo stack).

Poiché non è possibile utilizzare SP come registro di indirizzamento indiretto dello stack, occorre copiare il valore

di SP all’interno di BP e poi utilizzare BP per indirizzare lo stack in modo indiretto. Prima di fare la copia occorre

salvare il vecchio valore di BP.

_somma proc

push bp

mov bp,sp

mov ax, [bp+4] ; a

mov bx, [bp+6] ; b

mov cx, [bp+8] ; c

add ax, bx ;a+b

add ax, cx ;a+b+c

pop bp

ret cella 1024

_somma endp

Restituzione dei valori

Una funzione di solito restituisce un risultato al main. Secondo le convenzioni del compilatore C :

I valori su 8 bit vengono convenzionalmente restituiti in AL

I valori su 16 bit vengono convenzionalmente restituiti in AX

I valori su 32 bit vengono convenzionalmente restituiti in DX : AX

Nell‟esempio precedente la somma finale viene messa in AX dove il main potrà andarla a leggere.

SP BP

SP + 2 Program Counter

SP + 4 a

SP + 6 b

SP + 8 c

EOS

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 14

Procedure di Lettura e di Visualizzazione

INT 21H

AH = 1 Legge un carattere ASCII e lo memorizza in AL

AH = 2 Manda a video il carattere contenuto in DL

AH = 9 Manda a video una stringa terminata dal $

AH = 4c Ritorno al Sistema Operativo

Visualizzazione di un numero a 16 bit passato nello stack ed estratto in AX

buffer db 10 dup (?)

visualizza proc

push bx

push cx

push dx

push di

push bp

; non è possibile utilizzare sp con un displacement

mov bp, sp

mov ax, [bp+12]

mov bl, 10

mov di, 0

inizio1:

; AX / BL => AL quoziente, AH resto

div bl

add ah, 48

mov buffer[di], ah

mov ah, 0

inc di

cmp al, 0

jne inizio1

inizio2:

dec di

mov dl, buffer[di]

mov ah, 2

int 21h

cmp di, 0

jne inizio2

call acapo

pop bp

pop di

pop dx

pop cx

pop bx

mov ax, 0 ; procedura terminata correttamente ok

ret

visualizza endp

AX=numero da visualizzare

BL=10;

DI=0;

INIZIO1:

AL=AX/10;

AH=AX%10;

AH=AH+48;

BUFFER[DI]=AH;

AH=0;

DI++;

if(AL>0) goto INIZIO1;

INIZIO2:

DI--;

DL=BUFFER[DI];

printf(DL);

if(DI>0) goto INIZIO2;

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 15

Lettura da tastiera di un numero a 8 bit e restituzione in AL

leggi proc

push bx

push cx

push dx

xor dx, dx

lettura:

mov ah, 1

int 21h

cmp al,13

je fine

mov cl, al

sub cl, 48

mov al, 10

mul dl ; ax = al * dl

mov dl, al

add dl, cl

jmp lettura

fine:

mov al, dl ; prima di terminare occorre copiare il risultato in AX

pop dx

pop cx

pop bx

ret

leggi endp

acapo proc

push ax

push dx

mov ah, 2

mov dl, 13

int 21h

mov dl, 10

int 21h

pop dx

pop ax

ret

acapo endp

Esempio di Main: Area e Perimetro di un rettangolo

.data

msg1 db “Inserire valore della base $”

msg2 db “Inserire valore della altezza $”

msg3 db “Area = $”

msg4 db “Perimetro = $”

buffer db 10 dup (?)

DX=0;

LETTURA:

AL = getchar( );

if(AL==‟\n‟) goto FINE;

CL = AL - 48

DL = DL * 10 +CL

goto LETTURA;

FINE:

…………..………

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 16

base db ?

altezza db ?

area dw ?

perimetro dw ?

.code

mov ax, @data

mov ds, ax

lea dx, msg1 ; inserire valore della base

mov ah, 9

int 21h

call leggi

call acapo

mov base, al

lea dx, msg2 ; inserire valore della altezza

mov ah, 9

int 21h

call leggi

call acapo

mov altezza, al

mov al, base ; calcolo del perimetro

mov ah, 0

mov bl, altezza

mov bh, 0

add ax, bx

add ax, ax

mov perimetro, ax

mov al, base ; calcolo dell’area

mov ah, 0

mov bl, altezza

mul bl

mov area, ax

lea dx, msg3 ; visualizzazione dell’area

mov ah, 9

int 21h

push area

call visualizza

add sp, 2

lea dx, msg4 ; visualizzazione del perimetro

mov ah, 9

int 21h

push perimetro

call visualizza

add sp, 2

mov ah, 4ch ;ritorno al DOS

int 21h

seguono le procedure precedenti

end

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 17

Direttive di Segmento

nomeSeg SEGMENT [allineamento] [rango] ['classe'] contenuto del segmento

nomeSeg ENDS

E‟ ammessa la creazione di più segmenti con lo stesso nome (soprattutto in programmi articolati in più moduli),

segmenti che saranno riuniti dall‟Assemblatore o dal linker in un unico segmento. In tal caso deve però essere

specificata l‟opzione RANGO.

Dopo SEGMENT è possibile specificare le seguenti opzioni:

[allineamento] indica l‟allineamento del segmento rispetto al primo byte di memoria libera. Può assumere i valori

byte, word, dword, para (16 bytes), page (256 bytes). Il default è para. Gli allineamenti inferiori a para sono

ottenuti assegnando a PC un offset iniziale maggiore di zero.

[„classe’] Il Terzo Parametro „classe‟ consente di raggruppare i segmenti in classi. Può assumere i valori ‘Data’

‘Code’ ‘Stack’. Definisce un pratica il tipo di segmento che si sta definendo. L‟eventuale fusione dei diversi

segmenti dello stesso tipo verrà definita sulla base dell‟opzione rango. Segmenti con lo stesso nome non sono

combinati se il loro campo class assume valori diversi.

[rango]Il Seconda Parametro denominato RANGO indica all‟assemblatore come combinare tra loro i segmenti

aventi lo stesso nome e appartenenti alla stessa classe. Può assumere i valori:

PUBLIC. Tutti i segmenti PUBLIC con lo stesso nome e la stessa classe vengono fusi in un unico segmento.

Valore normalmente utilizzato per Data e Codice

STACK. Come PUBLIC, però questa volta, in aggiunta, l‟indirizzo assoluto del segmento unificato dichiarato

„STACK‟ viene automaticamente caricato allo start up all‟interno del registro SS:SP. Se non si specifica il rango

„STACK‟ il caricamento dovrà essere eseguito manualmente.

PRIVATE. [default]. Ogni segmento viene allocato separatamente dagli altri.

COMMON Sovrappone i segmenti con lo stesso nome. Eventuali variabili con lo stesso nome vengono fuse in

una unica variabile. Abbastanza pericoloso.

AT B800:0000 localizza il segmento a partire da un ben preciso indirizzo assoluto. Utile per accedere

direttamente ad una precisa area di sistema (ad esempio B800:0000 è la RAM Video). In tal caso non è possibile

definire né allineamento né classe.

La direttiva ASSUME

Le direttive ASSUME possono essere inserite ovunque all‟interno del programma, ed ogni direttiva maschera la

direttiva precedente. Obbligatorie. Se omesse errore di sintassi.

assume cs:_code, ds:_data, ss:_stack, es:_extraData

Associa un registro di segmento al segmento avente il nome indicato. Viene utilizzata dall'assemblatore per

determinare il registro di segmento che dovrà essere utilizzato dal RUN TIME per accedere alle variabili (ed

etichette) di un certo segmento. Non provvede però al caricamento dell'indirizzo del segmento nel registro

relativo, che dovrà pertanto essere eseguito manualmente via codice

Ad esempio scrivendo ds: _data, quando il RUN TIME dovrà accedere ad una variabile del _data segment, utilizzerà

DS come indirizzo di partenza del segmento. Quando dovrà accedere ad una variabile del _code segment utilizzerà

CS e così via. Se i registri di segmento non stati caricati adeguatamente, ovviamente si avrà un malfunzionamento.

Operatori Aritmetici sulle costanti

+, - , *, / , MOD

Gli operatori relazionali (== != < <= > >=) non sono definiti in Assembler, ma fanno parte dell‟istruzione JUMP

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 18

Pseudoistruzioni aggiuntive relative alle variabili

OFFSET

L‟operatore OFFSET restituisce il valore dell'offset di una variabile. Può essere usato in alternativa all‟istruzione

LEA. Ad esempio le due seguenti istruzioni sono equivalenti:

MOV AX, OFFSET VAR

LEA AX, VAR

L‟operatore OFFSET può essere applicato solo ad operandi indirizzati direttamente attraverso un nome di variabile e

non a operandi indirizzati indirettamente. Ad esempio

MOV AX, OFFSET VAR[SI] ; Errore !!

Si possono utilizzare le seguenti istruzioni: MOV AX, OFFSET VAR

ADD AX, SI

oppure: LEA AX, VAR[SI]

LENGTH

Restituisce il numero di elementi di una variabile dichiarata mediante DUP (variabile vettoriale).

Per una variabile scalare restituisce 1.

NUM DW 5 DUP (?)

MOV AX, LENGTH NUM ; 5

TYPE

Restituisce il numero di byte occupati da una variabile scalare o da una singola variabile vettoriale.

Nel caso dell‟esempio precedente si può scrivere

MOV BX, TYPE EXP ; 2

SIZE

Restituisce lo spazio di memoria complessivamente occupato dalla variabile (in pratica SIZE = LENGHT * TYPE).

MOV CX, SIZE EXP ; 10

SEG

L‟operatore SEG restituisce l‟indirizzo di inizio del segmento a cui la variabile appartiene.

MOV AX, SEG STR

MOV DS, AX

PTR

L‟operatore PTR (sintassi : TIPO PTR nomeVariabile) forza l‟assemblatore a modificare per l'istruzione

corrente il tipo della variabile, eseguendo in pratica un TYPE CAST (conversione del tipo).

TOT DW 35

MOV BH, BYTE PTR TOT

MOV CH, BYTE PTR TOT+1

COPPIA DB 2 DUP (?)

MOV AX, WORD PTR COPPIA

Sistemi - Classe Terza robertomana.it

Cenni di Assembler

pag 19

Public e Extern

Utilizzabili per variabili e procedure. Gestiscono l‟accesso a variabili / procedure definite all‟interno di altri moduli.

PUBLIC rende la variabile / procedura visibile ed utilizzabile su ogni file del progetto

PUBLIC MAX_ETH DD 1500

EXTRN Consente al file corrente di accedere ad una variabile / procedura dichiarata altrove.

EXTRN MAX_ETH : DWORD

EXTRN miaProc : NEAR / FAR

Per ogni simbolo dichiarato EXTRN, deve corrispondere una dichiarazione PUBLIC in qualche altro modulo.

Etichette tipizzate

La pseudoistruzione LABEL, utilizzata davanti ad una pseudoistruzione di definizione di un dato, consente di creare

una nuova etichetta tipizzata che fa riferimento alla stessa cella di memoria relativa alla variabile che segue, ma

utilizzando un tipo di riferimento differente. Ad esempio :

RamVideoByte LABEL BYTE

RamVideo DW 0b800H

L‟etichetta RamVideoByte consente di indirizzare la cella di memoria RamVideo come Byte anziché come Word

Gestione delle etichette da parte del Compilatore

Queste etichette servono al compilatore per crearsi una mappa dei simboli (salvata dal compilatore all‟interno del file

.LST). Ogni volta che in compilazione incontra uno di questi simboli, va nella mappa a vedere a quale indirizzo

corrisponde ed in quale segmento si trova, producendo codice macchina opportuno.

Modello completo di un programma Assembly

_data segment para public ‘data’

num1 dw 61

num2 dw 4

ris dw ?

_data ends

_stack segment para stack ‘stack’

db 128 dup (?)

eos label word ; INUTILE !

_stack ends

_code segment para public ‘code’

assume cs:_code, ds:_data, ss:_stack

main: mov ax, _stack

mov ss, ax

lea ax, eos

mov sp, ax ;queste 4 istruzioni sono eseguite in automatico dalla direttiva

mov ax, _data

mov ds, ax

mov ax,num1 ; AX <- DS:NUM1

add ax,num2

mov ris, ax

mov ah, 4ch ; ritorno al DOS

int 21h

_code ends

end main