Istruzioni matematiche e logiche, salti condizionali & company
(For Totally Newbies)

Data

by Spider

 

febbraio 2002

UIC's Home Page

Published by Quequero

....

Qualche mio eventuale commento sul tutorial :)))

....

....

Home page: http://bigspider.cjb.net
E-mail: spider_xx87@hotmail.com
Server IRC: irc.azzurra.it
Canali:  #crack-it  #asm
Nickname: ^Spider^

....

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 

In questo tutorial vedremo le più comuni istruzioni che servono per fare matematica o calcoli logici, nonché l'uso dei flags e dei salti condizionali... insomma, una bella scorpacciata di assembler for newbies :-)


Istruzioni matematiche e logiche, salti condizionali & company
(For Totally Newbies)
       Written by Spider

Introduzione


I processori Intel dispongono di un esteso set di istruzioni atte a svolgere operazioni matematiche e logiche, e un non meno esteso set di salti condizionali, ovvero di istruzioni che eseguono dei salti in presenza di particolari condizioni all'interno del registro EFLAGS.

Tools usati


-
Consiglio di prendere i manuali Intel (cercateli su http://developer.intel.com) e/o The Art Of Assembly Language: una buona reference è essenziale.
- Qualunque assembler (io uso MASM) se volete provare a fare qualcosa (ovviamente DOVETE farlo :-)).

Essay


Bene, prima di incominciare un breve discorso sui flags.

Quando eseguiamo una qualunque istruzione matematica o logica il processore setta o resetta alcuni dei flags contenuti nel registro EFLAGS. Vediamo intanto uno schema di tutti i flags contenuti in EFLAGS:

|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13  12|11|10|09|08|07|06|05|04|03|02|01|00|
                                |  |  |  |  |  |     |    |    |  |  |  |  |  |     |     |     |
  ID FLAG(ID) ------------------+  |  |  |  |  |     |    |    |  |  |  |  |  |     |     |     |
  Virtual Interrupt Pending -------+  |  |  |  |     |    |    |  |  |  |  |  |     |     |     |
  Virtual Interrupt Flag -------------+  |  |  |     |    |    |  |  |  |  |  |     |     |     |
  Alignment Check -----------------------+  |  |     |    |    |  |  |  |  |  |     |     |     |
  Virtual-8086 Mode ------------------------+  |     |    |    |  |  |  |  |  |     |     |     |
  Resume Flag ---------------------------------+     |    |    |  |  |  |  |  |     |     |     |
  Nested Task ---------------------------------------+    |    |  |  |  |  |  |     |     |     |
  I/O Privilege Level ------------------------------------+    |  |  |  |  |  |     |     |     |
  Overflow Flag -----------------------------------------------+  |  |  |  |  |     |     |     |
  Direction Flag -------------------------------------------------+  |  |  |  |     |     |     |
  Interrupt Enable Flag ---------------------------------------------+  |  |  |     |     |     |
  Trap Flag ------------------------------------------------------------+  |  |     |     |     |
                                                                           |  |     |     |     |
  Sign Flag ---------------------------------------------------------------+  |     |     |     |
  Zero Flag ------------------------------------------------------------------+     |     |     |
  Auxiliary Carry Flag -------------------------------------------------------------+     |     |
  Parity Flag ----------------------------------------------------------------------------+     |
  Carry Flag -----------------------------------------------------------------------------------+

Come vedete dallo schema, i flags si dividono in due gruppi: i System Flags e gli Status Flags. Quelli che ci interessano e che vedremo in dettaglio sono i secondi:

Carry Flag (CF) - Flag di riporto. Viene settato (cioè viene messo uguale ad uno) quando c'è un riporto o un prestito dal bit più significativo di una operazione.
 
Parity Flag (PF) - Flag di parità. Viene settato quando il risultato di un'operazione contiene un numero pari di 1. Viene generalmente usato nei sistemi di trasmissione dati come sistema di controllo.

Adjust Flag o Auxiliary Carry Flag (AF) - Viene settato quando c'è un riporto o un prestito dal terzo bit di una operazione. E' azzerato in caso contrario.

Zero Flag (CF) - Viene settato se il risultato di una operazione è zero, altrimenti viene azzerato.

Sign Flag (CF) - Flag di segno. Viene settato Se dopo un'operazione aritmetica (o logica) il bit più significativo (ovvero quello che rappresenta il segno) è 1. In caso contrario viene azzerato.

Overflow Flag (CF) - Viene settato settato quando il risultato di una operazione è troppo grande o troppo piccolo per essere contenuto nel registro di destinazione; è azzerato altrimenti.

Tra questi, i più comuni sono il Carry Flag, lo Zero Flag e il Sign Flag.


Iniziamo ora ad analizzare le istruzioni matematiche e logiche, partendo dalle più comuni.

Istruzioni logiche

Per istruzione logica si intende qualunque istruzione che lavori sui singoli bit.

Istruzione AND

Sintassi: AND dest, src

Flags modificati:
CF, OF, PF, SF, ZF (AF non definito)

L'istruzione AND esegue un "AND" logico tra dest e src, e salva il risultato in dest. Per chi non sapesse cos'è un AND logico (e anche per le istruzioni logiche che vedremo tra poco), vi rimando al mio tutorial (o anche qualunque altro :-) ) sulla matematica binaria ed esadecimale.

Se ad esempio vogliamo aggiungere ad eax il valore di ebx, scriveremo così:

add eax,ebx



Istruzione OR

Sintassi: OR dest, src

Flags modificati:
CF, OF, PF, SF, ZF (AF non definito)

L'istruzione OR esegue un "OR" logico tra dest e src, e salva il risultato in dest.

Se ad esempio vogliamo eseguire un or tra eax ed ebx, scriveremo così:

or eax,ebx

OR viene molto spesso usato per testare il valore di un registro. Ad esempio, se vogliamo controllare che edx sia zero, basterà fare un:

or edx,edx
jz ........

Così facendo, infatti, non modifichiamo il valore di eax, mentre settiamo i flags a seconda che il registro al sia uguale a zero, un numero negativo, ecc.



Istruzione XOR (eXclusive OR)

Sintassi: XOR dest, src

Flags modificati:
CF, OF, PF, SF, ZF (AF non definito)

L'istruzione XOR esegue un "XOR" logico tra dest e src, e salva il risultato in dest.

Se ad esempio vogliamo eseguire uno xor tra eax ed ebx, scriveremo così:

xor eax,ebx


XOR viene spesso usato per azzerare un registro. Infatti, come dovreste già sapere, un numero XORato con sè stesso restituisce zero. Ad esempio:

xor eax,eax

azzererà eax, mentre "xor ebx,ebx", azzererà ebx.


Un altro motivo per cui si utilizza l'istruzione xor è la sua reversibilità. Infatti una volta XORato il valore A per il valore B, xorando nuovamente il risultato per B si otterrà di nuovo A... ovvero, traducendo in formula:

(A XOR B) XOR B = A

Questa caratteristica lo rende particolarmente adatto per i sistemi di crittazione.

 

Istruzione NOT

Sintassi: NOT dest

Flags modificati:
nessuno

L'istruzione NOT inverte (o complementa) tutti i bits dell'operando dest.

Se ad esempio vogliamo complementare eax, scriveremo così:

not eax


Supponiamo di avere in al il valore binario 01101001b e di eseguire un "not al". Dopo l'esecuzione dell'istruzione avremo (in al) il valore 10010110b.
L'operazione eseguita da NOT è anche detta complemento a 1.

 

Istruzione SHL/SAL (Shift Logical Left/Shift Arithmetical Left)

Sintassi: SHL dest,count
   
        SAL dest,count

Flags modificati:
CF OF PF SF ZF (AF non definito)

L'istruzione SHL (che è sinonimo di SAL) sposta i bits di dest a sinistra di count bit e riempie di 0 i bit alla destra. L'ultimo bit espulso verso sinistra viene messo nel Carry Flag. count può essere 1, CL o un valore immediato di un byte.
Chiariamo meglio con un esempio. Supponiamo di voler "shiftare" a sinistra di 1 bit il registro AL, contenente il valore binario 10010110:

Prima di SHL:          CF=0  10010110
Dopo di SHL:          CF=1  00101100

Come vedete il bit che viene "espulso" verso sinistra va nel Carry Flag, mentre il bit che si viene a creare a destra viene azzerato.

L'istruzione SHL viene spesso utilizzata per eseguire alcune moltiplicazioni, in sostituzione dell'istruzione MUL che è piuttosto lenta. Infatti, uno shift a sinistra di n bytes è equivalente ad una moltiplicazione per 2n. Facciamo un esempio. Dobbiamo moltiplicare eax per 16 (cioè per 24); useremo quindi questo codice:

 SHL eax, 4      ;moltiplica eax per 16

Ovviamente questo metodo non può essere utilizzato nel caso di numeri molto grandi: infatti i bit più significativi (cioè quelli più a sinistra) vengono persi, fornendo così risultati errati.


Istruzione SHR e SAR (Shift Logical Right/Shift Arithmetical Right)

Sintassi: SHR dest,count
   
        SAR dest,count

Flags modificati:
CF OF PF SF ZF (AF non definito)

Le istruzioni SHR e SHR spostano i bits di dest a destra di count bit. L'ultimo bit espulso verso sinistra viene messo nel Carry Flag. count può essere 1, CL o un valore immediato di un byte. La differenza fra le due istruzioni consiste nel fatto che la prima (SHR) riempie di zeri i bits che si vengono a formare alla sinistra, mentre la seconda (SAR) riempie i bits che si vengono a creare alla sinistra con il bit del segno, ovvero con 1 per i numeri negativi e 0 per quelli positivi.
Chiariamo meglio con degli esempi:

Prima di SHR:          CF=0  AL=10010101
Dopo di SHR:          CF=1  AL=01001010

 

Prima di SAR:          CF=0  AL=10010101
Dopo di SAR:          CF=1  AL=11001010


SHR e SAR vengono talvolta utilizzate per eseguire divisioni, rispettivamente Unsigned e Signed, in sostituzione delle istruzioni DIVe IDIV che sono relativamente lente. Infatti, uno shift a destra di n bytes è equivalente ad una divisione per 2n. Facciamo un esempio: dobbiamo dividere eax per 8 (cioè per 23); useremo quindi questo codice:

 SHR eax, 3      ;divide eax per 8   (UNSIGNED)
 SAR eax, 3      ;divide eax per 8   (SIGNED)


Questo metodo non può tuttavia essere utilizzato qualora noi volessimo salvare il resto della divisione.

Istruzioni ROR e ROL (ROtate Right/ROtate Left)

Sintassi: ROR dest,count
   
        ROL dest,count

Flags modificati:
CF OF

Le istruzioni ROR e ROL ruotano i bits di dest a destra (ROR) o a sinistra (ROL) di count bit. L'ultimo bit ruotato sinistra viene messo nel Carry Flag. count può essere 1, CL o un valore immediato di un byte.
Chiariamo come al solito con degli esempi:

Prima di ROR:          CF=0  10010101
Dopo di ROR:          CF=1  11001010

 

Prima di ROL:          CF=0  10010101
Dopo di ROL:          CF=1  00101011


ROL e ROR sono istruzioni complementari, ovvero sono una l'inverso dell'altra. Ad esempio, un "ROL eax,2" seguito da un "ROR eax,2" riporta il registro eax allo stato iniziale (mentre i flags vengono modificati). La reversibilità di queste istruzioni le rende molto adatte ai sistemi di crittazione



Istruzioni aritmetiche

Istruzione NEG

Sintassi: NEG dest

Flags modificati:
CF, OF, PF, SF, ZF, AF

L'istruzione NEG esegue il complemento a 2 dell'operando dest.

Esempio:

neg ecx


Il complemento a 2, in parole povere, serve a calcolare l'opposto di un numero. Se ad esempio in EAX abbiamo il valore -2, dopo un neg avremo 2. Viceversa se in EAX abbiamo il valore -3, dopo un neg avremo il valore 3.
In un linguaggio ad alto livello (come ad esempio il C), "neg eax" è equivalente a qualcosa come:

eax = -eax

Non bisogna confondere l'istruzione NEG con l'istruzione NOT. Infatti supponendo di avere in EAX il valore -1, dopo un NEG avremo 1, mentre con un NOT otterremo 0.

Istruzione ADD

Sintassi: ADD dest,src
            
ADD dest,imm

Flags modificati:
CF, OF, PF, SF, ZF, AF

L'istruzione ADD esegue la somma tra i due operandi, salvando il risultato in dest.

Esempio:

add eax,ebx
add eax,015h

Una caratteristica molto importante dell'istruzione ADD è quella di settare il Carry Flag quando c'è un riporto... chiariamo con un esempio: supponiamo di avere in al il valore binario 10010110 e in bl il valore 11000001. Eseguiamo la somma:

 10010110+
 11000001=
----------
101010111

Come vedete il risultato è un numero a 9 bits, ma la CPU deve mettere il risultato in AL che è di 8 bits... come risolve il problema? Semplice: il risultato viene troncato a 8 bit, e avremo quindi in AL il valore binario 01010111; il nono bit, tuttavia, non viene perso, ma viene messo nel CF (Carry Flag); ciò si rivelerà molto utile nelle somme tra numeri di lunghezza maggiore della lunghezza di un registro, come vedremo in seguito.

Istruzione SUB

Sintassi: SUB dest,src
             SUB dest,imm

Flags modificati:
CF, OF, PF, SF, ZF, AF

L'istruzione SUB esegue la differenza tra i due operandi, salvando il risultato in dest.

Esempio:

sub ecx,edx
sub ebx,01234567h

Analogamente all'istruzione ADD, l'istruzione SUB setta il CF quanto vi è un prestito dal bit più significativo.
Supponiamo di avere in al 01110110 e in bl 10000110, e di eseguire un
SUB al,bl:

01110110-
10000110=
---------
11110000

Come ben vedete il processore si trova a dover eseguire la sottrazione tra 0 e 1. Poiché deve fornire un risultato, 'si fa prestare' un bit, e setta il CF per segnalare questa situazione. Ciò risulterà utile nelle sottrazioni tra numeri troppo grandi per 'entrare' nei registri a 32 bit. Vedremo meglio tra poco.

Istruzione ADC (Add With Carry)

Sintassi: ADC dest,src
      
        ADC dest,imm

Flags modificati:
CF, OF, PF, SF, ZF, AF

L'istruzione ADC esegue la somma tra i due operandi, e aggiunge 1 se il Carry Flag è settato. Il risultato va in dest.

Esempio:

adc eax,ebx

L'istruzione ADC è spesso usata per le addizioni multi-byte, multi-word o multi-dword. Infatti supponendo di dover sommare il numero contenuto in ECX:EBX  al numero contenuto in EDX:EAX, non possiamo limitarci ad una doppia somma, cioè così:

add eax,ebx
add edx,ecx          ;SBAGLIATO!!!!

Questa sequenza di istruzioni è del tutto sbagliata, e in molti casi fornirà un risultato errato. Il motivo è semplice: nella somma dobbiamo tenere conto dell'eventuale riporto tra la dword bassa e la dword alta degli operandi. Tale riporto (e per questo vi rimando all'istruzione ADD) è contenuto nel CF, pertanto alla seconda istruzione dobbiamo sostituire ADC:

add eax,ebx
adc edx,ecx          ;OK!

Ricordate che bisogna SEMPRE partire dalla parte bassa degli operandi. Non avremmo potuto fare il contrario, ovvero prima ADC e poi ADD.
In questo esempio eseguiamo la somma tra 2 numeri a 64 bit. Ovviamente non c'è un limite, avremmo potuto eseguire la somma anche tra numeri di diverse centinaia di bit.

Istruzione SBB (Subtract With Borrow)

Sintassi: SBB dest,src
                SBB dest,imm

Flags modificati:
CF, OF, PF, SF, ZF, AF

L'istruzione SBB esegue la differenza tra i due operandi, e decrementa dest di 1 se il Carry Flag è settato. Il risultato va in dest.

Esempio:

sbb ecx,edx

Analogamente all'istruzione ADC, l'istruzione SBB viene generalmente usata per sottrazioni multi-byte, multi-word o multi-dword. Se ad esempio dobbiamo eseguire la sottrazione tra edx:eax ed ecx:ebx, non possiamo limitarci ad una semplice sottrazione tra eax ed ebx, e tra edx ed ecx. Dobbiamo infatti tenere conto dell'eventuale (e molto probabile) prestito tra la parte alta e la parte bassa degli operandi. Pertanto il codice corretto sarà il seguente:

sub eax,ebx
sbb edx,ecx


Istruzioni INC e DEC (INCrement/DECrement)

Sintassi: INC dest
          DEC dest

Flags modificati:
OF, PF, SF, ZF, AF

L'istruzione INC incrementa dest di 1; l'istruzione DEC decrementa dest di 1. Il Carry Flag non viene modificato, mentre tutti gli altri flags vengono settati in base al risultato.

Esempi:

INC ebx               ;incrementa il registro ebx
INC dl                ;incrementa il registro dl
INC byte ptr[ebx+eax] ;incrementa il byte a [ebx+eax]

DEC ebx               ;decrementa il registro ebx
DEC dl                ;decrementa il registro dl
DEC byte ptr[ebx+eax] ;decrementa il byte a [ebx+eax]


Queste due istruzioni vengono spesso utilizzate all'interno di cicli, frequentemente utilizzando come dest il registro ecx:

ciclo: add byte ptr[eax+ecx],dl  ;aggiunge al byte a [eax+ecx] il contenuto di DL
      
inc ecx                   ;incrementa ecx
       cmp ecx,10                ;confronta ecx con 10
       jb ciclo                  ;se è minore ripete il ciclo

Oppure:

       mov ecx,10
ciclo: add byte ptr[eax+ecx],dl  ;aggiunge al byte a [eax] il contenuto di DL

       inc eax                   ;incrementa eax
      
dec ecx                   ;decrementa ecx
       jnz ciclo                 ;se è diverso da zero ripete il ciclo


Istruzione MUL

Sintassi: MUL src

Flags modificati:
CF, OF (PF, SF, ZF, AF non definiti)

L'istruzione MUL esegue la moltiplicazione senza segno tra il registro accumulatore (AL a 8 bit, AX a 16 bit, EAX a 32 bit) e il registro src. Se src è un registro a 16 bit il risultato viene salvato in DX:AX; se src è a 32 bit il risultato va in EDX:EAX.

Esempio:

MUL ebx     ;moltiplica il contenuto di eax per ebx e salva il risultato in edx:eax


L'istruzione MUL è però molto lenta, e quindi, quando possibile, è consigliabile sostituirla con un'istruzione o una sequenza di istruzioni equivalenti. Vedremo meglio ciò quando analizzeremo le istruzioni di shift.

 

Istruzione DIV

Sintassi: DIV src

Flags modificati:
(CF, OF, PF, SF, ZF, AF non definiti)

L'istruzione DIV esegue la divisione senza segno tra il registro accumulatore e il registro src. Se src è un operando di 1 byte, DIV effettua la divisione tra AX e src e salva il risultato in AL e il resto in AH; se src è un operando di 2 bytes (1 word), DIV divide DX:AX per src e salva il risultato in DX:AX e il resto in DX; se src è un operando di 4 bytes (1 dword), DIV divide EDX:EAX per src e salva il risultato in EDX:EAX e il resto in EDX.

Esempio:

DIV ecx      ;divide edx:eax per ecx e salva il risultato in edx:eax e il resto in edx

Spero vi sia sorto un dubbio: come fa l'istruzione DIV a salvare il risultato in EDX:EAX e contemporaneamente il resto in EDX? Beh... Non può farlo :-)  Se infatti il quoziente è troppo grande per poter essere salvato in eax, l'istruzione DIV provocherà un errore di divisione (Divide Error), e lo stesso avviene quando src contiene 0, dato che, come saprete (o se non lo sapete ve lo dico io :-)), non si può dividere un numero per 0.
Per evitare il problema dell'overflow di divisione è pertanto buona norma azzerare il registro edx prima della divisione; in questo modo saremo sicuri che il risultato sia abbastanza piccolo da entrare in eax. Esempio:

XOR edx,edx  ;azzera edx 
DIV ecx      ;divide eax per ecx e salva il risultato in eax e il resto in edx


Istruzione IMUL

Sintassi:  IMUL src

 
       IMUL dest,src
         IMUL dest,imm

         IMUL dest,src,imm

Flags modificati:
CF, OF (PF, SF, ZF, AF non definiti)

L'istruzione IMUL esegue la moltiplicazione con segno tra due operandi. Questa istruzione può trovarsi in tre forme diverse: ad 1 operando, a 2 operandi o a 3 operandi.
Nella forma ad 1 operando il valore di src viene moltiplicato per il valore del registro accumulatore (AL, AX o EAX), e il risultato viene salvato in AH:AL, AX:DX o EAX:EDX, a seconda della larghezza dell'operando.
Nella forma a 2 operandi l'istruzione IMUL effettua il prodotto tra il primo operando (dest) e il secondo operando (src o imm). Il risultato viene salvato in dest.
Nella forma a 3 operandi, infine, si effettua la moltiplicazione tra il registro indicato in dest, l'operando src, e l'operando imm. Il risultato va in dest.

Esempi:

IMUL bl             ;moltiplica al per bl e salva il risultato in ax (ah:al)
IMUL ecx            ;moltiplica eax per ecx e salva il risultato in edx:eax

IMUL ecx,edx        ;moltiplica ecx per edx e salva il risultato in edx.
IMUL ebx,50h        ;moltiplica ebx per 50h e salva il risultato in ebx.

IMUL ebx,eax,15h    ;moltiplica ebx per eax e poi per 15h. Il risultato va in ebx.


Istruzione IDIV

Sintassi: IDIV src

Flags modificati:
(CF, OF, PF, SF, ZF, AF non definiti)

L'istruzione IDIV esegue la divisione con segno tra il registro accumulatore e src. Se src è un valore di un byte, la divisione viene effettuata tra AX (AH:AL) e src e il risultato va in AL e il resto in AH. Se src è un valore di una word, la divisione viene effettuata tra DX:AX e src e il risultato va in AX e il resto in DX. Se src è un valore di una dword, la divisione viene effettuata tra EDX:EAX e src; il risultato va in EAX e il resto in EDX.
Anche l'istruzione IDIV, come l'istruzione DIV, provoca un Divide Error (#DE) quando il risultato è troppo grande per poter entrare nell'operando di destinazione; è quindi prudente azzerare il registro EDX per evitare imprevisti. 

Esempi:

XOR edx,edx  ;azzera edx
DIV ebx      ;Divide edx:eax per ebx. Il risultato va in eax e il resto in edx.

Istruzioni di confronto

Sono 2 le funzioni di confronto più comuni: CMP e TEST. Analizziamole in dettaglio.

Istruzione CMP (CoMPare)

Sintassi: CMP dest,src

Flags modificati:
CF, OF, PF, SF, ZF, AF

L'istruzione CMP esegue la sottrazione tra dest e src, ma non salva il risultato. I flags vengono però settati a seconda del risultato. In questo modo si possono eseguire confronti senza modificare il valore di src e dest;
generalmente l'istruzione CMP è seguita da un jump condizionale (Jcc).

Esempi:

CMP eax,15          ;confronta eax con 15
JE  quindici        ;salta all'etichetta "quindici" se eax è uguale a 15

CMP eax,15          ;confronta eax con 15
JB  menodiquindici  ;salta all'etichetta "menoquindici" se eax è minore di 15

CMP eax,15          ;confronta eax con 15
JA  piudiquindici   ;salta all'etichetta "piudiquindici" se eax è maggiore di 15

CMP ebx,ecx         ;confronta ebx con ecx
JNE diversi         ;salta all'etichetta "diversi" se ebx ed ecx non sono uguali.

CMP esi,edi         ;confronta esi con edi
JB minore           ;salta a "minore" se esi è minore di edi
JE uguale           ;salta a "uguale" se sono uguali
maggiore:           ;se è arrivato qui vuol dire che esi è maggiore di edi :)
...


Istruzione TEST

Sintassi: TEST dest,src

Flags modificati:
CF, OF, PF, SF, ZF (AF non definito)

L'istruzione TEST esegue un AND logico tra dest e src, ma non salva il risultato; i flags vengono però settati a seconda del risultato. In questo modo si possono eseguire confronti logici senza modificare il valore di src e dest. Molto di frequente l'istruzione TEST è seguita da un jump condizionale (Jcc).
L'istruzione TEST risulta molto utile quando noi vogliamo controllare lo stato di uno o più bit all'interno di un registro o una locazione di memoria.

Esempi:

TEST eax,1       ;controlla il primo bit (ovvero quello meno significativo) di eax
JZ nonsettato    ;salta all'etichetta "nonsettato" se quel bit è azzerato

TEST al,0000100b  ;controlla il terzultimo bit di al
JNZ settato      ;salta se è uguale ad 1


Salti condizionali (Jcc)

L'architettura Intel mette a disposizione del programmatore assembler una serie di istruzioni, chiamate salti condizionali, che vengono raggruppate con il nome Jcc. Queste istruzioni controllano lo stato dei flags, e, qualora la condizione espressa da cc risultasse vera, eseguono un salto, altrimenti passano all'istruzione successiva. Vediamo quali sono i salti condizionali e quali flags valutano:

JA     Jump if above (CF=0 and ZF=0)
JAE    Jump if above or equal (CF=0)
JB     Jump if below (CF=1)
JBE    Jump if below or equal (CF=1 or ZF=1)
JC     Jump if carry (CF=1)
JE     Jump if equal (ZF=1)
JG     Jump if greater (signed) (ZF=0 and SF=OF)
JGE    Jump if greater or equal (signed) (SF=OF)
JL     Jump if less (signed) (SF<>OF)
JLE    Jump if less or equal (signed) (ZF=1 or SF<>OF)
JNA    Jump if not above (CF=1 or ZF=1)
JNAE   Jump if not above or equal (CF=1)
JNB    Jump if not below (CF=0)
JNBE   Jump if not below or equal (CF=0 and ZF=0)
JNC    Jump if not carry (CF=0)
JNE    Jump if not equal (ZF=0)
JNG    Jump if not greater (signed) (ZF=1 or SF<>OF)
JNGE   Jump if not greater or equal (signed) (SF<>OF)
JNL    Jump if not less (signed) (SF=OF)
JNLE   Jump if not less or equal (signed) (ZF=0 and SF=OF)
JNO    Jump if not overflow (OF=0)
JNP    Jump if not parity (PF=0)
JNS    Jump if not sign (SF=0)
JNZ    Jump if not zero (ZF=0)
JO     Jump if overflow (OF=1)
JP     Jump if parity (PF=1)
JPE    Jump if parity even (PF=1)
JPO    Jump if parity odd (PF=0)
JS     Jump if sign (SF=1)
JZ     Jump if zero (ZF=1)


Come potete vedere dalla tabella, molte fra queste istruzioni controllano gli stessi flags. Non si tratta di inutili ridondanze, ma di diversi modi di chiamare la stessa istruzione. Ad esempio l'istruzione JBE e l'istruzione JNA hanno per il processore lo stesso significato, mentre nei nostri sorgenti potremo usarle per migliorare la leggibilità del codice.

Bisogna poi fare la differenza, in molti casi, tra signed e unsigned. Le istruzioni JB (Jump if Below) e JL (Jump if Less) NON sono equivalenti: la prima è infatti utilizzata per confronti o operazioni senza segno, mentre la seconda è d'obbligo per operazioni signed. Lo stesso vale ad esempio per JA (Jump if Above) e JG (Jump if Greater).

Vediamo un po' di esempi:

cmp eax,ebx    ;confronta eax con edx
je uguali      ;salta se sono uguali


cmp ecx,edx    ;confronta ecx con edx
jb less        ;salta se ecx è minore di edx (unsigned!)


cmp ecx,edx    ;confronta ecx con edx
jl less        ;salta se ecx è minore di edx (signed!)


sub eax,ebx    ;sottrae ebx ad eax
js negativo    ;salta se il risultato è negativo


sub eax,ebx    ;sottrae ebx ad eax
js negativo    ;salta se il risultato è negativo


dec ecx        ;decrementa ecx
jz ciclo       ;salta se è uguale a zero


dec ecx        ;decrementa ecx
jz ciclo       ;salta se è uguale a zero
(In quest'ultimo caso è più opportuno usare l'istruzione LOOP)

test eax,eax   ;testa eax (eseguendo un AND logico)
jz zero        ;salta se è zero


Potremmo continuare per molto ancora... gli esempi sono praticamente infiniti :)  Non vi preoccupate per il gran numero di salti condizionali che esistono... imparerete ad usarli soltanto in un modo: usandoli! Quindi... happy coding!!




Accanto ai Jcc esiste un'altro tipo di salto condizionale, che va visto separatamente in quanto non controlla lo stato dei flags, ma piuttosto lo stato del registro ECX (o CX se siamo a 16 bit). Questa istruzione è JECXZ (o JCXZ), ovvero Jump if ECX flag is Zero. Questa istruzione è particolarmente utile quando bisogna eseguire un ciclo (tramite loop) e si vuole evitare di dover eseguire il ciclo 232 volte. Usando JECXZ il ciclo viene saltato:

      jecxz fineciclo ;se ecx è 0, salta il ciclo
ciclo:
      add eax,ecx     ;aggiunge ecx ad eax
      loop ciclo      ;ripete il ciclo
fineciclo:
    . . .




Ciauz e alla prossima!

                                Spider

Note finali


Saluti a tutti gli amici dell'UIC, ringz3r0 (mirror), Itassembly, #crack-it e #asm.



Home

</BODY>