Zoom Icon

Le Istruzioni per Fare Matematica

From UIC

Le Istruzioni Per Fare Matematica

Contents


Infos
Author: b0nu$
Email: bonus@numerica.it
Website:
Date: 01/01/2001 (dd/mm/yyyy)
Level: No brain needed
Language: Italian Image:Flag_Italian.gif
Comments:



Introduzione

In questo aticolo analizzeremo le istruzioni che l'Assembly mette a disposizione per effettuare calcoli matematici (somme, sottrazioni, etc...) nonché quelle che permettono di manipolare i dati a livello di bit (AND, OR, etc...)


Operazioni Fondamentali

ADD, ADC e INC

La prima operazione matematica che si imapara alle elementari è senza dubbio la somma ed anche io comincio con quella. Le istruzioni che eseguono la somma sono tre:

        ADD < registro|memoria > , < registro|memoria|valore >
        ADC < registro|memoria > , < registro|memoria|valore >
        INC < registro|memoria >

NOTA:
dato che i trasferimenti diretti memoria-memoria non sono possibili i due operandi non possono essere entrambi locazioni di memoria.

Perchè 3 istruzioni?

IstruzioneSignificato
ADD op1,op2effettua la somma tra op1 e op2 e mette il risultato in op1
ADC op1,op2effettua la somma tra op1 e op2, mette il risultato in op1 e se c'è riporto somma 1 al risultato
INC opincrementa di 1 op;
il numero è considerato senza segno quindi non viene modificato il valore del CF in caso di riporto.

Queste operazioni effettuano una somma a prescindere dal fatto che si stia lavorando con numeri con segno o senza segno, sta al programmatore interpretare il valore della somma. Sta anche al programmatore il compito di controllare il risultato in caso di OVERFLOW (quando il valore della somma non sta nei registri) analizzando il valore del bit di overflow nel Flag Register. Vediamo alcuni esempi:

.DATA
dato DB 39 ;1 byte
.CODE
...
...
mov al,26 ;al=26
inc al ;al=27
add al,76 ;al=103

add al,dato ;al=142 oppure al=-114 (se con segno!!)

mov ah,al ;ah=142, al=142
add al,ah ;al=28 con CF=1
...
...

Tramite l'istruizone ADC è possibile effettuare somme che non stanno in un unico registro:

.DATA
dato32 DD 316423
.CODE
...
...
mov ax,43981 ;ax=43981
sub dx,dx ;dx=0
add ax,WORD PTR dato32[0] ;somma ax ai 2 byte meno significativi
;di dato32
adc dx,WORD PTR dato32[2] ;somma ax ai 2 byte più significativi
;di dato32 tenendo conto del riporto
...
...

In questo caso il risultato è contenuto in DX:AX

SUB, SBB, DEC e NEG

Dopo l'addizione non poteva mancare la sottrazione! La sintassi per queste operazioni è la seguente:

        SUB     < registro|memoria > , < registro|memoria|valore >
        SBB     < registro|memoria > , < registro|memoria|valore >
        DEC     < registro|memoria >
        NEG     < registro|memoria >  

Cominciamo dalla SUB che funziona esattamente come la ADD: sottrae dal primo operando il secondo e mette il risultato nel primo. Valgono le stesse osservazioni fatte per la somma quindi se non ve le ricordate andate a rileggerle. Idem per quanto riguarda la DEC che altro non fa che sottrarre 1 all'operando, anche qui il numero è trattato come senza segno. Vi riporto anche qui qualche esempio per capire meglio:

.DATA
dato DB 122
.CODE
...
...
mov al,95 ;al=95
dec al ;al=94
sub al,23 ;al=71

sub al,dato ;al=-51 se con segno oppure al=205 senza segno
mov ah,119
sub al,ah ;al=86 con OF=1

Quando il risultato va sotto lo zero viene settato il SF (sign flag). Per poter effettuare le sottrazioni vere e proprie con tanto di riporto si usa la SBB (subtract with borrow) che funziona esattamente come la SUB solo che in caso di riporto viene sottratto 1 al risultato. Esempio:

.DATA
datoA DD 316423
datoB DD 156739
.CODE
...
...
mov ax,WORD PTR datoA[0]
mov dx,WORD PTR datoA[2] ;ora ho il primo numero in DX:AX
sub ax,WORD PTR datoB[0]
sub dx,WORD PTR datoB[2] ;ho il risultato in DX:AX
...
...

L'ultima istruzione che rientra nella categoria delle sottrazioni è la NEG che cambia il segno dell'operando e che quindi deve essere usata solo per i numeri con segno. Esempio:

...
mov ax,143 ;ax=143 --> 8Fh
neg ax ;ax=-143 --> FF71h
...

NOTA:
Vi ricordo che i numeri negativi sono rappresentati in complemento a 2.

MUL e IMUL

La terza operazione che vediamo è la moltiplicazione, senza segno (MUL) e con segno (IMUL). La loro sintassi è la seguente:

        MUL     < registro|memoria >
        IMUL    < registro|memoria >

Vi chiederete dove sta il secondo fattore. Bene l'operando specificato nella MUL (o nella IMUL) verra moltiplicato per il numero che è contenuto nell'accumulatore (AX per i 16bit, AL per gli 8bit o EAX per i 32bit). E il risultato? Quello dipende dagli operandi:

IstruzioneSignificato
1byte * ALrisultato in AX (CF=0 e OF=0 se e solo se AH=0)
2byte * AXrisultato in DX:AX (CF=0 e OF=0 se e solo se DX=0)
4byte * EAXrisultato in EDX:EAX (CF=0 e OF=0 se e solo se EDX=0)

Anche qui vediamo alcuni esempi:

.DATA
dato DW -30000
.CODE
...
...
mov al,23 ;al=23
mov bl,24 ;bl=24
mul bl ;ax=552 (CF=1 e OF=1)

mov ax,50
imul dato ;DX:AX=-1500000 (CF=1 e OF=1)

Da questo esempio si capisce anche come funziona la IMUL.
Per processori 80186 e superiori la IMUL prevede anche le seguenti sintassi:

        IMUL    < registro > , < valore >
        IMUL    < registro > , < memoria > , < valore >

In questo caso il risultato viene messo nel registro specificato e nel caso non ci stia viene settato CF e OF. Quando specifico 3 parametri il primo è la destinazione e gli altri due sono i fattori del prodotto. Esempi:

...
imul dx,455 ;moltiplica dx*455
...
imul ax,[bx],6 ;moltiplica il valore puntato da bx*6 e
;mette il risultato in ax


DIV e IDIV

Anche per la divisione esistono 2 istruzioni una per i numeri senza segno (DIV) e una per quelli col segno (IDIV). Entrambe ritornano il quoziente e il resto, e la loro sintassi è:

        DIV     < registro|memoria >
        IDIV    < registro|memoria >

Per effettuare una divisione di un numero a 16bit per uno di 8bit devo mettere il numero da dividere in AX e specificare il divisore (cioè l'altro numero) in un altro registro. Il risultato della divisione sara in AL e il resto in AH ; quindi fate attenzione il dividendo viene perso. Se invece voglio dividere un numero a 32 bit per uno di 16 si deve mettere il dividendo in DX:AX, specificare il divisore in un registro a 16 bit (che non sia AX o DX) ed effettuare la divisione: avremo il risultato in AX e il resto in DX. Per dividere tra loro due numeri a 16 bit si deve prima trasformare il dividendo in un numero a 32 bit tramite l'istruzione CWD (andate a rivederla) se ci interessa il segno oppure nel modo visto nel tutorial precedente se stiamo lavorando con numeri senza segno. Se si cerca di effettuare una divisione per 0 o se il quoziente non sta nei registri viene generata una chiamata all'int 0 ,il programma termina e il controllo torna al DOS. Per evitare questo problema ci sono due modi: o si controlla il valore del divisore prima di effettuare l'istruzione DIV, oppure intercettare l'int 0 e riscriversi una routine di interrupt per gestire l'evento ma questo lo vedremo più avanti!! Vi riporto quelche esempio:

.DATA
data16 DW -2000
data32 DD 500000
.CODE
...
...
mov ax,700 ;ax=700
mov bl,36 ;bl=36
div bl ;al=19 (quoziente), ah=16 (resto)

mov ax,WORD PTR dato32[0]
mov dx,WORD PTR dato32[2] ;carico il dividendo in DX:AX
idiv dato16 ;500000/-2000
;AX=-250 , DX=0
mov ax,WORD PTR dato16
cwd ;lo estendo a 32bit in DX:AX
mov bx,-421
idiv bx ;ax=4 , dx=-316

Ora che abbiamo visto le 4 operazioni fondamentali analizzeremo alcune istruzioni che lavorano con i numeri binari in notazione BCD. Vi chiederete perche.. beh sappiate che lavorare con in numeri in BCD spesso è più facile per fare calcoli. Queste istruzioni non hanno operandi perche lavorano sempre con al.


Operazioni BCD

AAA (Adjust after an Addiction)

Per capire cosa fa vediamo un esempio:

mov ax,9 ;ax=9
mov bx,3 ;bx=3
add al,bl ;al=C
aaa ;al=2 ah=1 CF=1

Come si vede dall'esempio il risulato (in "unpacked" BCD) l'ho in ax: in ah ci sono le decine e in al le unità. Notate che setta anche il Carry a 1. Ricordatevi che una cifra in decimale viene qui codificata con 1byte.

AAS (Adjust After a Subctraction)

mov ax,103h ;ax=13h (in BCD : ah=1 al=3!!!)
mov bx,4 ;bx=4
sub al,bl ;al=FFh (-1)
aas ;al=9 ah=0 CF=1


AAM (Adjust After a Multiplication)

mov ax,903h
mul ah ;moltiplica ah*al = 1Bh
aam ;ah=2 al=7;

Attenzione : questa istruzone si può usare solo per le moltiplicazioni senza segno !!!

AAD (Adjust BEFORE a division)

A differenza delle altre 3 questa va usata prima di una divisione infatti trasforma il numero in BCD in codica binario prima dell'operazione. Dopo la divisione il risultato va risistemato tramite un AAM. Vediamo ad esempio (25/2):

mov ax,205h ;carico il dividendo (25)
mov bl ;carico il divisore (2)
aad ;trasformao il 25 in BCD in 19h binario (ax=19h)
div bl ;quoziente in al=0Ch resto in ah=1
aam ;sistema al in ax=102h (12 in BCD)

Notate che il resto viene perso: se vi serve ricordatevi di slvarlo prima dell'istruzione AAM.

Abbiamo visto come si lavora con numeri a 2 cifre ma se devo usare numeri più grossi? Semplice devo iterare il metodo per ogni cifra del numero !!

Esistono poi altre due istruzioni per lavorare con un altro tipo di numeri BCD cosiddetti "packed" in cui ogni cifra occupa solo 4 bit. A differenza delle altre non cambiano il valore di ah ma lavorano sul registro di flag ogni volta che c'è un riporto.

DAA (Adjust After an Addiction)

mov ax,8833h ;carica 88 e 33 in un unico registro
add al,ah ;0BBh
daa ;CF=1 e AL=21


DAS (Adjust After an Subctraction)

mov ax,3883h ;carica 38 e 83 in BCD
sub al,ah ;04Bh
das ;CF=0 AL=45


Operazioni Logiche

Vediamo ora le istruzioni che il processore ci mette a disposizione per effettuare operazioni logiche. Qui andrò abbastanza veloce, il significato degli operatori l'ho già spiegato perciò mi limiterò a elencare la sintassi delle istruzioni e a fare qualche breve commento.

AND

         < registro|memoria >,< registro|memoria|valore >

Effettua un AND bit a bit degli operandi. L'AND può essere utile per azzerare alcuni bit di un registro tramite una "maschera" in cui i bit da azzerare sono 0 e gli altri sono 1:

mov ax,035h ;00110101
mov bx,0FBh ;11111011
and ax,bx ;00110001 ho azzerato il secondo bit !!


OR

         < registro|memoria >,< registro|memoria|valore >

Effettua l'OR bit a bit. Può essere utile per settare solo alcuni bit di un registr con un'opportuna "maschera" di bit.

XOR

         < registro|memoria >,< registro|memoria|valore >

Effettua l'OR esclusivo bit a bit. È molto usata per azzerare il valore di un registro facendo XOR con se stesso: è molto veloce!!

NOT

         < registro|memoria >

Effettua il NOT logico sui bit dell'operando.


Altre Operazioni

Vediamo infine alcune istruzioni non prettamente matematiche ma che vengono spesso utilizzate per questo scopo.

SHL e SHR

Queste due istruzioni shiftano a sinistra e destra (risp.) l'argomento. Innanzi tutto la sintassi è la seguente:

        SHL  < registro|memoria > , < CL|1 >
        SHR  < registro|memoria > , < CL|1 >

Vediamo cosa vuol dire con esempio:

mov al,01001011 ;al=01001011
shl al,1 ;al=10010110 CF=0

In pratica è successo che tutti i bit sono stati spostati di una posizione verso sx, il bit più significativo è andato nel CF e quello meno significativo è stato messo a zero. Dal punto di vista matematico ho effettuato una moltiplicazione per 2!! Lo stesso discorso simmetrico vale per lo Shift a dx e in questo caso il numero viene diviso per 2. Se sposto di due posizioni il numero verra moltiplicato/diviso per 4, se sposto di 3 per 8,di 4....

ROR e ROL

Queste servono per far ruotare i bit a destra e sinistra (risp.) La loro sintassi è uguale a shl e shr (non sto a riscriverla!). Vediamo subito un esempio:

mov al,01001011 ;al=01001011
ror al,1 ;al=10100101
mov cl,3
rol al,cl ;al=00101101


Note Finali

Bene anche questa puntata è finita. Ora dovreste avere i mezzi per scrivere qualche semplice routine matematica. È importante che abbiate capito come vengono rappresentati i numeri all'interno di un calcolatore quindi andate a rileggervi il primo capitolo sui numeri binari ponendo particolare attenzione ai numeri negativi.


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.