Istruzioni matematiche e logiche, salti condizionali & company | ||
Data |
by Spider |
|
febbraio 2002 |
Published by Quequero | |
.... |
Qualche mio eventuale commento sul tutorial :))) |
.... |
.... |
|
.... |
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 :-)
Introduzione |
Tools usati |
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.