簡單條件轉移指令
? ? ? ? 根據單個標志位的值(CF, SF,OF,PF,ZF)來確定是否轉移, 如果條件成立,則(EIP) + 位移量 ? EIP,否則什么也不做。
? ? ? ? 注意,這里的EIP在執行本條指令時就已經變成當前指令的下一條指令的地址了,如下例, 0096827D是jnz l1的下一條指令地址,加上機器碼75 07中表示偏移量的07就是l1所在處00968284
mov eax, x
00968270 A1 11 90 9D 00 mov eax,dword ptr [x (09D9011h)] cmp eax, y
00968275 3B 05 15 90 9D 00 cmp eax,dword ptr [y (09D9015h)] jnz l1
0096827B 75 07 jne l1 (0968284h) mov ecx,1
0096827D B9 01 00 00 00 mov ecx,1 jmp l2
00968282 EB 05 jmp l1+5h (0968289h)
l1: mov ecx,0
00968284 B9 00 00 00 00 mov ecx,0
l2:
00968289 ……
JZ / JE?????? ZF=1時,轉移
JNZ / JNE???? ZF=0時,轉移
JS??????????? SF=1時,轉移
JNS?????????? SF=0時,轉移
JO??????????? OF=1時,轉移
JNO?????????? OF=0時,轉移
JC??????????? CF=1時,轉移
JNC?????????? CF=0時,轉移
JP / JPE????? PF=1時,轉移
JNP / JPO???? PF=0時,轉移
無符號條件轉移指令??
JA / JNBE?? 標號?? ( CF=0 且 ZF=0,轉移)
JAE / JNB?? 標號?? ( CF=0 或 ZF=1,轉移)
JB / JNAE?? 標號?? ( CF=1 且 ZF=0,轉移)
JBE / JNA?? 標號?? ( CF=1 或 ZF=1,轉移)
有符號條件轉移指令?
JG / JNLE?? 標號:當 SF=OF 且 ZF=0時,轉移
JGE / JNL?? 標號:當 SF=OF 或者 ZF=1時,轉移
JL / JNGE?? 標號:當 SF≠OF 且 ZF=0時,轉移
JLE / JNG?? 標號:當 SF≠OF 或者 ZF=1時,轉移
?兩種JMP格式
????????間接轉移方式中,除了立即數尋址方式外,其它方式均可以使用。
?功能等價的轉移指令:
1. JMP L1
2. JMP BUF
3. LEA EBX,BUF
? ? JMP DWORD PTR [EBX]
4. MOV EBX,BUF
? ? JMP EBX
指令地址列表?
? ? ? ? ?如果要根據不同的輸入跳轉執行不同的程序片段,如果要JMP來寫會非常麻煩。采用的方法是構造指令地址列表。
? ? ? ? 比如,
????????FUNCTAB? DD? LP1, LP2, LP3
????????JMP? FUNCTAB[EBX*4]
????????(EBX)=0;跳轉到 LP1處
????????(EBX)=1;跳轉到 LP2處
?????????又如,
void arraysubtract_colsfirst( )? {……}
void arraysubtract_rowsfirst( )? {……}
void arraysubtract_onedim ( )? {……}
int main()
{
??? int?? i;
??? void (*funcp[3])() = { arraysubtract_colsfirst ,
??????????????????????????????????? arraysubtract_rowsfirst,
????????????????????????????????? ??arraysubtract_onedim };
??? funcp[i]();?? // i=0,1,2 會執行不同的函數
??? …….
}
這樣也做到了從多分支到無分支的轉化,比如下面例子
6.3.1 多分支向無分支的轉化
例:當x==1時,顯示‘Hello,One’;
??? 當x==2時,顯示‘Two’;
??? 當x==3時,顯示‘Welcome,Three’,……,
??? 即x為不同的值,顯示不同的串。
void myprint() { int x;char msg1[] = "Hello,One";char msg2[] = "Two";char msg3[] = "Welcome, Three";char *p[3] = { msg1,msg2,msg3 };printf("please input 0,1,2 \n");scanf("%d", &x);printf("%s\n", p[x]); }
編譯優化上的利用
編譯層面上可以利用這種轉化實現優化:
比如對下面這個子函數的優化:
???????int absdiff(int x, int y){int result;if (x < y)result = y - x;elseresult = x - y;return result;}
無分支的寫法:
int absdiff(int x, int y)
; _x$ = ecx
; _y$ = edx
push? esi
mov?? esi, ecx
mov?? eax, edx
sub?? esi, edx
sub?? eax, ecx
cmp?? ecx, edx
cmovge eax, esi
pop?? esi
ret?? 0
這樣,先分別計算出x - y 和 y - x, 然后通過cmp、cmovge來實現選擇。
Switch語句?
? ? ? ? ?Switch語句就是采用這種方式,如下例
????????
#include <stdio.h> int main(int argc, char* argv[]) { int x = 3, y = -1, z;char c;c = getch();switch (c) {case '+':case 'a': // 用 字符’a’來表示‘+’z = x + y;break;case '-':case 's': // 用 字符’s’來表示‘-’z = x - y;break;default: z = 0;}printf(" %d %c %d = %d \n", x,c,y,z);return 0; }
可以看出,是通過幾種選擇的值與其中最小值的差作為偏移量從內存中取數,取出的數作為在一個數組取數的下標,取出的數就是跳轉到的地址,實際上就是用指令地址列表來實現的。
與轉移指令功能類似的指令?
? ? ? ? 帶條件的數據傳輸指令
? ? ? ? ? ? ? ? 上一篇詳細寫過。
語句格式:cmov***? r32,r32/m32
功??? 能:在條件“***”成立時,
????????? 傳送數據,即(r32/m32)→r32。
????????? cmov 是Conditional MOVe的縮寫。
要??? 求:
?? ① r32 表示一個32位的寄存器;
?? ② m32位表示一個內存地址;
????? m32對應直接、間接、變址、基址加變址尋址;
????? m32對應的單元的數據類型是雙字,即32位。
????????字節指令?
語句格式:set***? opd
功??? 能:在條件“***”成立時,(opd)??1,否則 (opd)? 0 。
????? opd 一般為 一個字節寄存器
如:
??? cmp?? eax,? ebx
??? setg? cl
??? seta? cl
??? sete? cl
使用單個標志位 設置sete/setz、setc、sets、seto、setp
條件:ZF=1???? CF=1??? SF=1?? OF=1? PF=1?
setne/setnz、setnc、setns、setno、setnp
條件:ZF=0???? CF=0????? SF=0??? OF=0?? PF=0
使用多個標志位組合設置seta、setb、setg、setl
setae、setbe、setge、setle???????