在网上转来的,文章对学习用DELPHI有很大的帮助的。
主选课本是清华大学王爽老师的《汇编语言》. 推荐 王爽老师的汇编网
--------------------------------------------------------------------------------
汇编语言之前是机器语言.
机器语言是机器指令的集合, 机器指令是一系列二进制数字, 计算机将之转换为一系列高低电平, 而实现运算.
在 PC 机上运行机器指令的是 CPU; 不同的 CPU 有不同的指令, 所以某种汇编语言也只是针对某系列的 CPU.
王爽老师举了一个用机器语言输出 "welcome to masm" 的例子:
00011110
101110000000000000000000
01010000
101110001100011000001111
1000111011011000
1011010000000110
1011000000000000
1011011100000111
101110010000000000000000
1011011000011000
1011001001001111
1100110100010000
1011010000000010
1011011100000000
1011011000000000
1011001000000000
1100110100010000
1011010000001001
10001101000101100010101000000000
1100110100100001
1011010000001010
10001101000101100011000100000000
1100110100100001
1011010000000110
1011000000010100
1011011100011001
1011010100001011
1011000100010011
1011011000001101
1011001000111100
1100110100010000
1101010000000010
1101011100000000
1101000000001100
1101001000010100
1100110100010000
1011010000001001
10001101000101100000000000000000
1100110100100001
11001011
我怀着对计算机先人的无比崇敬, 把它给抄下来, 也不知对也不对.
--------------------------------------------------------------------------------
后来有了汇编, 譬如用:
mov ax,bx {在 Delphi 中相当于 ax := bx}
代替机器指令:
1000100111011000
这样更接近人类的思维; 但最终还是要有编译器把 mov ax,bx 翻译回 1000100111011000 才能被计算机接受.
初学 Delphi 嵌入汇编[2] - 汇编语言关键字
汇编语言不区分大小写.
关键字 用途
AH
AL
AND
AX
BH
BL
BP
BX
BYTE
CH
CL
CS
CX
DH
DI
DL
DS
DWORD
DX
EAX
EBP
EBX
ECX
EDI
EDX
EIP
ES
ESI
ESP
FS
GS
HIGH
LOW
MOD
NOT
OFFSET
OR
PTR
QWORD
SHL
SHR
SI
SP
SS
ST
TBYTE
TYPE
WORD
XOR
非常遗憾, 现在我不能给每个关键字做一个哪怕是非常简单的解释, 以后会了再补吧.
初学 Delphi 嵌入汇编[3] - 第一个 Delphi 与汇编的例子
譬如: ADD AX,BX; 这相当于 Delphi 中的 AX := AX + BX;
另外提前来个列表 - Delphi 可以用汇编管理以下寄存器:
32 位寄存器: EAX EBX ECX EDX ESP EBP ESI EDI
16 位寄存器: AX BX CX DX SP BP SI DI
8 位寄存器 : AL BL CL DL AH BH CH DH
16 位段寄存器: CS DS SS ES 以及协处理器寄存器堆栈: ST
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//使用汇编的函数
function add(x,y: Integer): Integer;
var
count: Integer;
begin
asm
MOV EAX,x {把 x 值放入寄存器 EAX}
MOV ECX,y {把 y 值放入寄存器 ECX}
ADD EAX,ECX {把 EAX + ECX 的值放入 EAX}
MOV count,EAX {把 EAX 的值给变量 count}
end;
Result := count; {返回值}
{asm 中每个语句换行即可分句, 无须 ; 在这里加上也没有问题}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := add(2,4);
ShowMessage(IntToStr(i)); {6}
end;
补充嵌入汇编的注释及分句:
1、注释同 Delphi
2、可以用分号 ; 分句
3、可以用换行分句
4、甚至可以用注释分句
如果改变了其他寄存器, 在过程和函数结束前要给恢复.
记得前面学习过 Delphi 的过程和函数默认的调用约定是 Register , 前三个参数通过寄存器传递, 其他参数存与栈.
它所指的三个寄存器就应该是 EAX ECX EDX 了.
看资料介绍应该是: EAX 先接受第一个参数再接受返回值, ECX EDX 接受后面两个参数.
但我不能进行完整的测试.
--------------------------------------------------------------------------------
//测试三个自由寄存器: EAX ECX EDX
procedure Proc(x,y,z: Integer);
var
a,b: Integer;
begin
asm
MOV a,ECX
MOV b,EDX
end;
ShowMessage(Format('%d,%d',[a,b]));
{EAX 我测试不了}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
Proc(11,22,33); {显示: 33,22}
{看来寄存器 ECX 储存的是第三个参数; EDX 储存第二个参数}
end;
procedure Proc(x,y: Integer);
var
a,b: Integer;
begin
asm
MOV a,ECX
MOV b,EDX
end;
ShowMessage(IntToStr(a) + #44 + IntToStr(b));
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
Proc(11,22); {显示: 0,22}
{看来是 EDX 一直接受第二个值; 那么 ECX 肯定接受第三个值了}
end;
procedure Proc(str1,str2: string);
var
s1,s2: string;
begin
asm
mov ecx, &str1 {}
mov edx, &str2
mov &s1, ecx {}
mov &s2, edx
end;
ShowMessage(s1 + s2);
end;
{在没有歧义的情况下, 操作符 & 是可以省略的, 譬如上面的例子就可以省略}
//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
Proc('我是', '万一'); {显示: 我是万一}
end;
--------------------------------------------------------------------------------
//在什么情况下不能省略呢? 例如:
procedure TForm1.Button2Click(Sender: TObject);
var
ecx: Integer; {这个变量和其中一个寄存器重名了}
begin
ecx := 99;
asm
mov ecx, &ecx {现在 ecx 是寄存器; &ecx 是变量}
add ecx, 1
mov &ecx, ecx
end;
ShowMessage(IntToStr(ecx)); {100}
end;
--------------------------------------------------------------------------------
{现在也得知: 其实这之前的例子, 只要是在汇编中使用的本地变量都可以加 & }
var
a: Integer;
const
n = $10;
begin
asm
mov ecx, 10 {使用十进制常数}
mov a, ecx
end;
ShowMessage(IntToStr(a)); {10}
asm
mov ecx, $10 {使用十六进制常数}
mov a, ecx
end;
ShowMessage(IntToStr(a)); {16}
asm
mov ecx, 10H {可以加 H 或 h 表示十六进制}
mov a, ecx
end;
ShowMessage(IntToStr(a)); {16}
asm
mov ecx, n {使用预定义常量}
mov a, ecx
end;
ShowMessage(IntToStr(a)); {16}
end;
现在我们在 32 位的系统下工作, 当然主要使用的是 32 位寄存器; 那它和 8 位、16 位的寄存器又有什么关系呢?
从网上找到一个简洁明了的图片:
EAX 是 32 位的, 也就是 4 个字节大小; 它的低两位就是 AX;
AX 是 16 位的, 又分 2 个字节; 它的高字节是 AH、低字节是 AL;
AH 与 AL 是 8 位的.
这样就兼容了以前的 16 位与 8 位.
同理,
EBX 就包含着 BX BH BL;
ECX 就包含着 CX CH CL;
EDX 就包含着 DX DH DL;
好啊, 一下子认识了那么多寄存器!
按照这个道理, 如果给 EAX 赋了值, 那么 AX AH AL 也就都有了值;
如果给 AL 赋了值, 那么 AX EAX 也就有了值.
//测试1
var
i: integer;
{4 字节、32 位}
w: word;
{2 字节、16 位}
b1,b2: byte;
{1 字节、 4 位}
begin
i := maxint;
w := 0;
b1 := 0;
b2 := 0;
asm
mov ecx, i
mov w, cx
mov b1, ch
mov b2, cl
end
;
ShowMessage(Format(
'w=%d; b1=%d; b2=%d'
,[w,b1,b2]));
{结果显示: w=65535; b1=255; b2=255
果然没错, 给 ecx 赋值后, cx ch cl 都有值了!
}
end
;
//测试2
var
i: integer;
{4 字节、32 位}
w: word;
{2 字节、16 位}
b: byte;
{1 字节、 4 位}
begin
b := 255;
i := 0;
w := 0;
asm
mov cl, b
mov w, cx
mov i, ecx
end;
ShowMessage(Format(
'i=%d; w=%d',[i,w]));
{结果显示: i=255; w=255
这好像是没有问题的, 我用 al ax eax 三个寄存器测试也是如此;
但用 dl dx edx 测试, 就会有意外的结果, 奇怪呀!
}
end;
function Fun(x: Integer): Integer;
asm
mov eax, x
inc eax
end;
{
汇编中的 inc 指令和 Delphi 中的 inc 是一样的;
本例也同时证明 eax 寄存器确实保存着函数的返回值.
}
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := 8;
i := Fun(i);
ShowMessage(IntToStr(i)); {9}
end;
| 返回类型 | 寄存位置 |
| Char、Byte | AL 寄存器 |
| SmallInt、Word | AX 寄存器 |
| Integer、LongWord、AnsiString、Pointer、Class | EAX 寄存器 |
| Real48 | EAX 寄存器中是栈内返回值的指针 |
| Int64 | EDX、EAX 寄存器对 |
| Single、Double、Extended、Comp | 栈首寄存器 ST(0) |
| 短字符串或变体类型 | 在@Result指向的临时位置中返回 |
function DelphiFun(x: Integer): Integer;
begin
Result := x * 2;
end;
//汇编函数, 和上面的函数是同样的功能
function AsmFun(x: Integer): Integer;
asm
add eax, eax {eax 可以获取第一个参数, 同时又是函数的返回值, 所以可以如此简洁!}
end;
//测试 DelphiFun
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := 34;
i := DelphiFun(i);
ShowMessage(IntToStr(i)); {68}
end;
//测试 AsmFun
procedure TForm1.Button2Click(Sender: TObject);
var
i: Integer;
begin
i := 34;
i := AsmFun(i);
ShowMessage(IntToStr(i)); {68}
end;
做本例时, 我同时做了一个测试:
循环执行 1,000,000 次以内, 基本没有区别; 循环执行 10,000,000 次时, 才有 10 几毫米的差距.
这说明 Delphi 本身速度就足够快了!
function Fun(x: Integer): Integer;
asm
mov ecx, &x
dec ecx {汇编中的 dec 是减 1 指令, 和 Delphi 是一样的}
mov @Result, ecx {在本例中去掉 @ 也可以, 暂时不知怎么回事}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := 100;
i := Fun(i);
ShowMessage(IntToStr(i)); {99}
end;
function Fun(var x,y: Integer): Integer;
asm
mov eax, x {现在 eax 中只是 x 的地址}
mov eax, [eax] { [eax] 是取值, 是不是类似于 P^ ? }
mov edx, y
add eax, [edx]
//mov @Result, eax {在这里, 这句可有可无}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
a,b: Integer;
begin
a := 1;
b := 8;
a := Fun(a,b);
ShowMessage(IntToStr(a)); {9}
end;
function Fun: Integer;
const
a = 11;
b = 5;
asm mov eax, a-b
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := Fun;
ShowMessage(IntToStr(i)); {6}
end;
//变量不可以, 方法中的参数也都属于变量
function Fun: Integer;
var
x,y: Integer;
asm
mov x, 11
mov y, 5
//mov eax, x-y {不能这样使用}
mov eax, x
sub eax, y {sub 是减, 就像 add 是加一样}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);var
i: Integer;
begin
i := Fun;
ShowMessage(IntToStr(i)); {6}
end;
初学 Delphi 嵌入汇编[15] - 需要保护的寄存器
那么剩下的 EBX ESP EBP ESI EDI 五个寄存器就是应该保护的!
所谓保护, 并不是不可以使用, 而是在使用前先把其中的值寄存在另外一个地方, 用完后再恢复其值.
如果不这样做, 有可能会发生意想不到的错误.
举例:
//使用应该保护的 ebx 寄存器
function Fun(x: Integer): Integer;
asm
push ebx {push 是入栈指令, 栈就是系统自动分配的内存}
mov ebx, x
inc ebx
mov @Result, ebx
pop ebx {pop 是出栈指令, 也就是恢复 ebx 寄存器原来的值}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
i := Fun(8);
ShowMessage(IntToStr(i)); {9}
end;
生活中有 7进制(星期)、60进制(小时)、10进制(算术)等等.
计算机要用到 10进制、16进制、2进制和 8进制.
8进制用得少了, 但也有, 譬如 Unix 服务器上的文件属性.
2进制是计算机容易识别的; 10进制是人容易识别的, 据说 10进制源自于人有十个手指.
计算机为什么会使用 8进制和 16进制呢? 因为: 23=8; 24=16; 这样比较容易和 2进制换算.
从这里也能看出, 2进制和 10进制的换算相对复杂些.
下面例子中说明了进制的表示方法, 七个函数都会返回整数 255:
//十进制
function Fun0: Integer;
asm
mov eax, 255
end;
//十进制数后面也可以加个 D(大小写无关)
function Fun1: Integer;
asm
mov eax, 255D
end;
//二进制后面加 B(大小写无关)
function Fun2: Integer;
asm
mov eax, 11111111B
end;
//八进制后面加 O(大小写无关)
function Fun3: Integer;
asm
mov eax, 377O
end;
//十六进制前面加 $
function Fun4: Integer;
asm
mov eax, $FF
end;
//十六进制也可以是后面加 H(大小写无关)
function Fun5: Integer;
asm
mov eax, 0FFH {使用这种方法, 数字的首位不能是字母, 不然会被认为成标识符}
end;
//非汇编代码的 Delphi 只支持用 $ 表示十六进制
function Fun6: Integer;
begin
Result := $FF;
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(Fun0)); {255}
ShowMessage(IntToStr(Fun1)); {255}
ShowMessage(IntToStr(Fun2)); {255}
ShowMessage(IntToStr(Fun3)); {255}
ShowMessage(IntToStr(Fun4)); {255}
ShowMessage(IntToStr(Fun5)); {255}
ShowMessage(IntToStr(Fun6)); {255}
end;
OR : 逻辑或指令
XOR: 逻辑异或指令
NOT: 逻辑非指令
这和 Delphi 的逻辑命令完全一致.
//逻辑非 Not:
{
not 1 = 0;
not 0 = 1;
}
var
ByteNum: Byte;
begin
//赋值 11111111B (255) 取反:
asm
mov al, 11111111B {eax 包含 ax; ax 包含 al; al 是 eax 的低八位}
not al {给 11111111 取反会得到 00000000}
mov ByteNum, al {把寄存器 al 中的值给变量 ByteNum}
end;
ShowMessage(IntToStr(ByteNum)); {0}
//赋值 00000001B (1) 取反:
asm
mov al, 00000001B
not al {给 00000001 取反会得到 11111110}
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {254}
//赋值 10000000B (128) 取反:
asm
mov al, 00000001B
not al {给 10000000 取反会得到 01111111}
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {127}
end;
--------------------------------------------------------------------------------
//逻辑或 Or:
{
1 or 0 = 1;
0 or 1 = 1;
1 or 1 = 1;
0 or 0 = 0;
}
var
ByteNum: Byte;
begin
asm
mov al, 10101010B {170}
mov cl, 01010101B {85}
or al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {255}
end;
--------------------------------------------------------------------------------
//逻辑与 And:
{
1 and 1 = 1;
1 and 0 = 0;
0 and 1 = 0;
0 and 0 = 0;
}
var
ByteNum: Byte;
begin
//例1
asm
mov al, 11111111B {255}
mov cl, 11111111B {255}
and al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {255}
//例2
asm
mov al, 00000000B {0}
mov cl, 00000000B {0}
and al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {0}
//例3
asm
mov al, 11111111B {255}
mov cl, 00000000B {0}
and al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {0}
//例4
asm
mov al, 10101010B {170}
mov cl, 01010101B {85}
and al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {0}
end;
--------------------------------------------------------------------------------
//逻辑异或 Xor:
{
1 Xor 0 = 1;
0 Xor 1 = 1;
1 Xor 1 = 0;
0 Xor 0 = 0;
}
var
ByteNum: Byte;
begin
//例1
asm
mov al, 11111111B {255}
mov cl, 11111111B {255}
xor al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {0}
//例2
asm
mov al, 11111111B {255}
mov cl, 00000000B {0}
xor al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {255}
//例3
asm
mov al, 10101010B {170}
mov cl, 01010101B {85}
xor al, cl
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {255}
end;
var
ByteNum: Byte;
begin
//右移 shr
asm
mov al, 10000000B {128}
shr al, 1 {shr 10000000 一次会得到 01000000}
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {64; shr 相当于 ÷2}
//左移 shl
asm
mov al, 00000001B {1}
shl al, 1 {shl 一次会得到 00000010}
shl al, 1 {shl 两次会得到 00000100}
mov ByteNum, al
end;
ShowMessage(IntToStr(ByteNum)); {4; shl 相当于 ×2}
end;
B: Byte;
W: Word;
C: Cardinal;
begin
{Byte 是1字节(8位)无符号整型, 其最大值是 111111112}
asm
mov B, 11111111B;
end;
ShowMessage(IntToStr(B)); {255}
{Word 是2字节(16位)无符号整型, 其最大值是 11111111 111111112}
asm
mov W, 1111111111111111B;
end;
ShowMessage(IntToStr(W)); {65535}
{Cardinal 是4字节(32位)无符号整型, 其最大值是 11111111 11111111 11111111 111111112}
asm
mov C, 11111111111111111111111111111111B;
end;
ShowMessage(IntToStr(C)); {4294967295}
{它们的最小值都是 0}
end;
I: Integer;
begin
//Integer 类型是4字节(32位)有符号整数, 最高位是符号位, 如果是正数, 符号位是 0、负数的符号位是1
//所以 Integer 的最大值是: 01111111 11111111 11111111 111111112
asm
mov I, 01111111111111111111111111111111B;
end;
ShowMessage(IntToStr(I)); {2147483647}
//有符号整数的负数等于相同正数的反码 + 1; Integer 最大值是:
//01111111 11111111 11111111 111111112; 其反码是:
//10000000 00000000 00000000 000000002; 反码 + 1 以后是:
//10000000 00000000 00000000 000000012
asm
mov I, 10000000000000000000000000000001B;
end;
ShowMessage(IntToStr(I)); {-2147483647}
//那 Integer 的最小值是多少呢?
//应该是: 10000000 00000000 00000000 000000002
asm
mov I, 10000000000000000000000000000000B;
end;
ShowMessage(IntToStr(I)); {-2147483648}
//11111111 11111111 11111111 111111112 是?
asm
mov I, 11111111111111111111111111111111B;
end;
ShowMessage(IntToStr(I)); {-1}
//Integer 类型的 0 在内存中是: 00000000 00000000 00000000 000000002
asm
mov I, 00000000000000000000000000000000B;
end;
ShowMessage(IntToStr(I)); {0}
//Integer 类型的 10010 的二进制是: 00000000 00000000 00000000 011001002
asm
mov I, 00000000000000000000000001100100B;
end;
ShowMessage(IntToStr(I)); {100}
//算 Integer 类型的 -10010:
//00000000 00000000 00000000 01100100 的反码是:
//11111111 11111111 11111111 10011011 ; 反码 + 1 以后是:
//11111111 11111111 11111111 10011100
asm
mov I, 11111111111111111111111110011100B;
end;
ShowMessage(IntToStr(I)); {-100}
end;
Longint、Longword、Shortint、Smallint、Int64
其中 Longint 相当于 Integer; Longword 相当于 Cardinal. 这样还有三种类型:
Shortint、Smallint、Int64
--------------------------------------------------------------------------------
//Shortint 是1字节(8位)有符号整数
var
I: Shortint;
begin
//其最大值是: 011111112
asm
mov I, 01111111B
end;
ShowMessage(IntToStr(I)); {127}
//其最小值是: 100000002
asm
mov I, 10000000B
end;
ShowMessage(IntToStr(I)); {-128}
end;
--------------------------------------------------------------------------------
//Shortint 是2字节(16位)有符号整数
var
I: Smallint;
begin
//其最大值是: 01111111 111111112
asm
mov I, 0111111111111111B
end;
ShowMessage(IntToStr(I)); {32767}
//其最小值是: 10000000 000000002
asm
mov I, 1000000000000000B
end;
ShowMessage(IntToStr(I)); {-32768}
end;
--------------------------------------------------------------------------------
//Int64 是8字节(64位)的, 暂时的汇编知识, 我还测试不了它.
Type
TMyRec = record
i: Integer;
c: Char;
end;
//在汇编中使用记录的过程
procedure GetRec(Rec: TMyRec);
asm
mov eax.TMyRec.i, 100 {eax 会接受第一个参数}
mov eax.TMyRec.c, 'M'
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
myRec: TMyRec;
begin
GetRec(myRec);
ShowMessage(Format('%d, %s',[myRec.i, myRec.c])); {100, M}
end;
function Fun(x,n: Integer): Integer;
asm
//mov eax, x {因为 eax 会先获取第一个参数, 这句可以省略}
mov ecx, n {ecx 是个计数寄存器, 会记录循环的次数, 没循环一次 ecx 的值就会减1}
@Lable1: add eax, eax {内部标签必须使用 @ 前缀; 也可以使用 Delphi 的标签}
loop @Lable1 {循环到标签执行}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(Fun(2,10))); {2048}
end;
| 类型 | 助记符 | 助记符简写 | 所占字节数 | 数值范围 |
| 字节 | BYTE | DB | 1 | 0..255 |
| 字 | WORD | DW | 2 | 0..65535 |
| 双字 | DWORD | DD | 4 | 0..4294967295 |
| 远字 | FWORD | DF | 6 | |
| 四字 | QWORD | DQ | 8 | |
| 十字节 | TBYTE | DT | 10 | |
| 有符号字节 | SBYTE | 1 | -128..127 | |
| 有符号字 | SWORD | 1 | -32768..32767 | |
| 有符号双字 | SDWORD | 1 | -2147483648..2147483647 |
书上抄的, 还没有测试 Delphi 支持情况.
function DelphiFun(x,y: Integer): Integer;
begin
Result := x + y;
end;
//汇编函数
function AsmFun: Integer;
asm
mov eax, 1 {eax 对应函数的第一个参数, 这里给第一个参数赋值为 1}
mov edx, 2 {edx 对应函数的第二个参数, 这里给第二个参数赋值为 2}
call DelphiFun {call 是调用命令; 返回值在 eax}
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
var
num: Integer;
begin
num := AsmFun;
ShowMessage(IntToStr(num)); {3}
end;
function Fun1(C: Char): Char;
asm
sub C, 32
//sub C, 'a'-'A' {竟也可以这样写}
end;
//大写字母转小写字母
function Fun2(C: Char): Char;
asm
add C, 32
end;
//测试
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(Fun1('b')); {B}
ShowMessage(Fun2('B')); {b}
end;
var
x,y: Integer;
begin
x := 1; y := 9;
asm
mov eax, x
mov ecx, y
xchg eax, ecx {xchg 的参数必须至少一个是寄存器, 不能有 xchg x,y 类似的操作}
mov x, eax
mov y, ecx
end;
ShowMessage(Format('x=%d, y=%d',[x,y])); {x=9, y=1}
end;
{Fun1 需要读取常数 0, 最慢}
function Fun1: Integer;
asm
mov eax, 0
end;
{Fun2 与 Fun3 只是操作 CPU 的寄存器, 比 Fun1 快}
function Fun2: Integer;
asm
sub eax, eax
end;
{Fun3 最快}
function Fun3: Integer;
asm
xor eax, eax
end;
//速度测试
procedure TForm1.Button1Click(Sender: TObject);
var
t: Cardinal;
i: Integer;
begin
t := GetTickCount;
for i := 0 to 100000000 do Fun1;
t := GetTickCount - t;
ShowMessage(IntToStr(t)); {均: 600 多}
t := GetTickCount;
for i := 0 to 100000000 do Fun2;
t := GetTickCount - t;
ShowMessage(IntToStr(t)); {均: 500 多}
t := GetTickCount;
for i := 0 to 100000000 do Fun3;
t := GetTickCount - t;
ShowMessage(IntToStr(t)); {均: 400 多}
end;
初学 Delphi 嵌入汇编[29] - 寄存器所能接受的数值范围譬如 EAX AX AH AL 四个储存器, 真实存在的其实只有一个 EAX, AX AH AL 不过是不同的访问方式.
11111111 11111111 11111111 11111111 : EAX 11111111 11111111 11111111 11111111 : AX 11111111 11111111 11111111 11111111 : AH 11111111 11111111 11111111 11111111 : AL //譬如 AX 是一个16位2字节储存器, 它能接受的最大整数是 65535 {下面函数会返回 65535} function Fun: Integer; asm mov ax, 65535 {给 AX 赋值就是给 EAX 赋值} end; {下面函数会出错} function Fun: Integer; asm mov ax, 65536 {超出了 AX 的容量} end; //同样给 AL AH 赋值不能超过 255 {下面函数会返回 255} function Fun: Integer; asm mov al, 255 end; {下面函数会返回 65280} function Fun: Integer; asm mov ah, 255 end; {为什么不是 255? 因为给 AH 赋值 255 后, EAX 中的值是 00000000 00000000 11111111 000000002} //上面演示的是通过立即数(常量)赋值, 通过变量也是如此; 但在其他运算中会不会溢出是 CPU 之前不会知道的, 譬如: {下面的函数不会出错, 但返回的是 0 } function Fun: Integer; asm mov eax, 4294967295 {这是 eax 所能接受的最大整数} add eax, 1 {再 +1 就放不下了} end; {因为结果会是: 1 00000000 00000000 00000000 000000002; EAX 只能放下32位, 前面的一位就被忽略了.} //如果 AX 溢出, EAX 会不会接着? {下面的函数也会返回 0 , 看来不会进位到 EAX; AX 虽然是 EAX 的一部分, 但使用时也是相对独立的} function Fun: Integer; asm mov ax, 65535 add ax, 1 end; //AH AL 也是如此 {返回 0} function Fun: Integer; asm mov al, 255 add al, 1 end; //对于负数呢? {下面函数不会有问题, 返回 -1 } function Fun: Integer; asm mov eax, -1 end; {它们能接受的最小负整数分别是: } function Fun: Integer; asm mov al, -256 mov ah, -256 mov ax, -63356 mov eax, -4294967296 end; {再小于这个数字就会报错! } //但它们的返回值缺不能所愿, 譬如: {下面函数会返回 0 } function Fun: Integer; asm mov eax, -4294967296 end; {这个好理解, 函数的返回值是 Integer; Integer 的最小值是 -2147483648; 怎么可能放得下 -4294967296? 又被忽略了.} //其他情况也大概如此, 现在给 AX 一个绝对放得下的一个值: -1 {竟然返回一个正数: 65535, 为什么?} function Fun: Integer; asm mov ax, -1 end; { 因为 -1 在 AX 中被存为: 11111111 111111112; 这样 EAX 的值是: 00000000 00000000 11111111 111111112; EAX 的最高位只有是 1 才有可能是个负数, 现在 EAX 中的值就是: 65535 } //下面两个函数也是同样的道理: {返回 255} function Fun: Integer; asm mov al, -1 end; {返回 65280} function Fun: Integer; asm mov ah, -1 end; //其他允许单独访问低16位和低8位的32位寄存器, 情况肯定也是如此. |
|---|
| 类型 | 名称 | 二进制码 | 寄存器说明 |
| 多功能寄存器 | AL | 0 | 累加寄存器低八位 |
| AH | 100 | 累加寄存器低八位 | |
| AX | 0 | 16 位累加寄存器 | |
| EAX | 0 | 32 位累加寄存器 | |
| BL | 11 | 基址寄存器低八位 | |
| BH | 111 | 基址寄存器低八位 | |
| BX | 11 | 16 位基址寄存器 | |
| EBX | 11 | 32 位基址寄存器 | |
| CL | 1 | 计数寄存器低八位 | |
| CH | 101 | 计数寄存器低八位 | |
| CX | 1 | 16 位计数寄存器 | |
| ECX | 1 | 32 位计数寄存器 | |
| DL | 10 | 数据寄存器低八位 | |
| DH | 110 | 数据寄存器低八位 | |
| DX | 10 | 16 位数据寄存器 | |
| EDX | 10 | 32 位数据寄存器 | |
| 指针寄存器 | SP | 100 | 16 位堆栈指针寄存器 |
| ESP | 100 | 32 位堆栈指针寄存器 | |
| BP | 101 | 16位基址指针寄存器 | |
| EBP | 101 | 32 位基址指针寄存器 | |
| 变址寄存器 | DI | 111 | 16 位目标变址寄存器 |
| EDI | 111 | 32位目标变址寄存器 | |
| SI | 110 | 16 位源变址寄存器 | |
| ESI | 110 | 32位源变址寄存器 | |
| 专用寄存器 | IP | * | 16 位指令指针寄存器 |
| EIP | * | 32 位指令指针寄存器 | |
| FLAGS | * | 16 位标志寄存器 | |
| EFLAGS | * | 32位标志寄存器 | |
| 段寄存器 | CS | 1 | 代码段寄存器 |
| DS | 11 | 数据段寄存器 | |
| ES | 0 | 附加段寄存器 | |
| SS | 10 | 堆栈段寄存器 | |
| FS | 100 | 标志段寄存器 | |
| GS | 101 | 全局段寄存器 | |
| 控制寄存器 | CR0 | 0 | 控制寄存器零 |
| CR1* | 1 | 控制寄存器一 | |
| CR2 | 10 | 控制寄存器二 | |
| CR3 | 11 | 控制寄存器三 | |
| CR4 | 100 | 控制寄存器四 | |
| CR5* | 101 | 控制寄存器五 | |
| CR6* | 110 | 控制寄存器六 | |
| CR7* | 111 | 控制寄存器七 | |
| 调试寄存器 | DR0 | 0 | 调试寄存器零 |
| DR1 | 1 | 调试寄存器一 | |
| DR2 | 10 | 调试寄存器二 | |
| DR3 | 11 | 调试寄存器三 | |
| DR4* | 100 | 调试寄存器四 | |
| DR5* | 101 | 调试寄存器五 | |
| DR6 | 110 | 调试寄存器六 | |
| DR7 | 111 | 调试寄存器七 | |
| 任务寄存器 | TR0 | 0 | 任务寄存器零 |
| TR1 | 1 | 任务寄存器一 | |
| TR2 | 10 | 任务寄存器二 | |
| TR3 | 11 | 任务寄存器三 | |
| TR4 | 100 | 任务寄存器四 | |
| TR5 | 101 | 任务寄存器五 | |
| TR6 | 110 | 任务寄存器六 | |
| TR7 | 111 | 任务寄存器七 | |
| 浮点寄存器 | ST0 | 0 | 浮点寄存器零 |
| ST1 | 1 | 浮点寄存器一 | |
| ST2 | 10 | 浮点寄存器二 | |
| ST3 | 11 | 浮点寄存器三 | |
| ST4 | 100 | 浮点寄存器四 | |
| ST5 | 101 | 浮点寄存器五 | |
| ST6 | 110 | 浮点寄存器六 | |
| ST7 | 111 | 浮点寄存器七 | |
| 多媒体寄存器 | MM0 | 0 | 媒体寄存器零 |
| MM1 | 1 | 媒体寄存器一 | |
| MM2 | 10 | 媒体寄存器二 | |
| MM3 | 11 | 媒体寄存器三 | |
| MM4 | 100 | 媒体寄存器四 | |
| MM5 | 101 | 媒体寄存器五 | |
| MM6 | 110 | 媒体寄存器六 | |
| MM7 | 111 | 媒体寄存器七 | |
| 单指令流、多数据流寄存器 | XMM0 | 0 | 单指令流、多数据流寄存器零 |
| XMM1 | 1 | 单指令流、多数据流寄存器一 | |
| XMM2 | 10 | 单指令流、多数据流寄存器二 | |
| XMM3 | 11 | 单指令流、多数据流寄存器三 | |
| XMM4 | 100 | 单指令流、多数据流寄存器四 | |
| XMM5 | 101 | 单指令流、多数据流寄存器五 | |
| XMM6 | 110 | 单指令流、多数据流寄存器六 | |
| XMM7 | 111 | 单指令流、多数据流寄存器七 | |
| 注: 英文名称有星号"*"的表示作为保留域, 实际并没有使用, 二进制码有星号"*"表示无需二进制数表示 | |||



#1