技术交流,DH讲解.
正式之前,我们看看寄存器和CPU的标志位:
OD中的截图,下方的CPAZSTDO就是标志位.
Delphi的FPU窗口,右边一列就是标志位.
为什么要给大家看标志位呢?因为ASM中的跳转语句都是由相应的标志位控制的.
而标志位又是由于以下的情况影响的:
1.运算结果为0,Zero Flag(ZF)被设定
2.运算结果太大或者太小,超容了,Carry Flag(CF)被设定
3.Sign Flag(SF)是运算结果的最高位发生变化的时候,结果为正SF=1,为负SF=0
4.指令无效时(al=+127, al+1爆 , al = -128 , al-1爆),Over Flag会被设定
5.运算造成结果的地位中1的个数是偶数个的时候Parity Flag会被设定
指令讲解:
具体指令之前,先说一下:reg代表寄存器,mem代表内存上的数(变量),imm代表立即数(常量)
AND,二元操作符,位与
合法形式:
1
2
3
4
5
|
AND reg , reg
AND reg , mem
AND reg , imm
AND mem , reg
AND mem , imm
|
标志位:总是清除OF和CF,根据结果来设置SF,ZF,PF.
来看个例子吧:
1
2
3
4
5
6
7
8
9
10
11
12
|
function TestAnd(a,b: Integer ): Integer ;
asm and eax,edx
end ;
procedure TForm4 . FormCreate(Sender: TObject);
var a: Integer ;
begin a:= $F ; //00001111
ShowMessage(IntToStr(TestAnd(a, 1 )));
end ;
|
运算前
运算后
来个有用的函数,字符转换成大写:
1
2
3
4
|
function UpcaseChar(a: AnsiChar ): AnsiChar ;
asm and al, $DF
end ;
|
OR,二元操作符,位或
合法形式:
1
2
3
4
5
|
OR reg , reg
OR reg , mem
OR reg , imm
OR mem , reg
OR mem , imm
|
标志位:和AND一样.
例子将0~9的整数变成对应的Char形式:
1
2
3
4
|
function _0_9ToChar(n: Byte ): AnsiChar ;
asm or al, $30
end ;
|
运算前
运算后
XOR,二元操作符,位异或(相同为0,不同为1)
合法形式:同AND
标志位:同AND
我们有时候需要将某个寄存器清零,我们有2种方式:
1
2
3
4
5
|
procedure TestXor;
asm xor eax,eax
mov eax, 0
end ;
|
反汇编看:
第一个指令:$31C0只用了2个字节.第二个指令就用了5个字节.
NOT,一元操作符,位非(1变0,0变1)
合法形式:
1
2
|
NOT reg
NOT mem
|
标志位:不影响任何标志位
TEST,二元操作符,和AND的一样,但是不改变2个操作数的值,只影响标志位.
合法形式:同AND
标志位:同AND
我们经常需要判断一个string变量是否为nil
1
2
3
4
5
|
function TestTest( const s: string ): Integer ;
asm test eax,eax //if s=nil then
jz @@nExit
end ;
|
CMP,二元操作符,比较2个数大小
合法形式:同SUB指令
标志位:
CF ZF
目的<來源 | 0 | 1
目的>來源 | 0 | 0
目的=來源 | 1 | 0
带正负号的数比较:
目的<來源 | SF≠OF
目的>來源 | SF=OF
目的=來源 | ZF=1
常用的设置个别标志位的方法:
1 ZF
1
2
|
and al, 0 //ZF=1,任何数和0进行与
or al, 1 //ZF=0,任何数和1进行或
|
2 SF
1
2
|
or al, $80 //SF=1,最高位与1进行或
and al, $7F //SF=0,最高位与0进行与
|
3 CF
1
2
|
stc //CF=1
clc //CF=0
|
4 OF
1
2
3
|
mov al, $7F
inc al //OF=1
or al, 0 //OF=0
|
跳转指令
一般跳转根据:1 标志位 2 参与运算的2个数
第一组:N代表Not,J就是Jump,然后其他字母对应相应的标志位
JZ | 若為零則跳 | ZF=1
JNZ | 若為不零則跳 | ZF=0
JC | 若進位則跳 | CF=1
JNC | 若不進位則跳 | CF=0
JO | 若溢位則跳 | OF=1
JNO | 若不溢位則跳 | OF=0
JS | 若負號則跳 | SF=1
JNS | 若非負號則跳 | SF=0
JP | 同位(偶)則跳 | PF=1
JNP | 非同位(奇)則跳 | PF=0
第二组:根据参与运算的2个数的值
JE | 相等則跳(leftOp=rightOp)
JNE | 不相等則跳(leftOp≠rightOp)
JCXZ | 若 CX = 0 則跳
JECXZ | 若 ECX = 0 則跳
无符号2个数比较时候:
JA | 較大則跳 大于
JNBE | 不是較小或相等則跳(=JA) 大于
JAE | 較大或相等則跳 大于等于
JNB | 不是較小則跳(=JAE)大于等于
JB | 較小則跳 小于
JNAE | 不是較大或相等則跳(=JB)小于
JBE | 較小或相等則跳 小于等于
JNA | 不是較大則跳(=JBE)小于等于
有符号2个数比较时候:
JG | 較大則跳
JNLE | 非較小或相等則跳(=JG)
JGE | 較大或相等則跳
JNL | 不是較小則跳(=JGE)
JL | 較小則跳
JNGE | 非較大或相等則跳(=JL)
JLE | 較小或相等則跳
JNG | 不是較大則跳(=JLE) | 不是較大則跳(=JBE)
好我们来看下怎么写一个求2个数中较小的一个数的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function IntegerMin(a,b: Integer ): Integer ;
asm cmp eax,edx
jg @@nBmin
ret
@@nBmin: mov eax,edx
end ;
function CardinalMin(a,b: Cardinal ): Cardinal ;
asm cmp eax,edx
ja @@nBmin
ret
@@nBmin: mov eax,edx
end ;
|
关键处的跳转指令不一样的,因为数据类型不一样.
最后来看一下循环指令
LOOPE和LOOPZ,一元操作符,满足条件就跳转,条件是ZF=1 and ecx>0
合法形式:LOOPE/LOOPZ 标签
LOOPNE和LOOPNZ,一元操作符,条件ZF=0 AND ECX>0
合法形式:和LOOPE
我们用LoopE来在一个字符串中查找一个字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function FindChar(a: AnsiChar ; const s: AnsiString ): Integer ;
asm test edx,edx //s=nil
jz @@nNotFound
mov ecx,[edx - 4 ]
//length(s)=0
jz @@nNotFound
push edx
dec edx
@@nLoop: inc edx
cmp al, Byte ptr [edx]
loopne @@nLoop
pop edx
test ecx,ecx
jz @@nNotFound
mov eax,[edx - 4 ]
sub eax,ecx
dec eax
ret
@@nNotFound: xor eax,eax
end ;
|
好了今天能说的就这么多了.特别感谢http://evotalk.net..都是中国人哈哈.
http://www.cnblogs.com/huangjacky/archive/2010/01/20/1652495.html