Delphi 嵌入汇编

2018-10-30

Delphi 与汇编入门

// 作者 万一

http://www.cnblogs.com/del/category/121079.html

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, 8); // EPrivilege with message Privileged instruction
Memo1.Lines.Add(format('%d', [i]));
end;

procedure Proc(w, x, y, z:integer);
var a, b, c, d, e, f, g: Integer;
begin
asm
mov a, eax
mov b, ebx
mov c, ecx
mov d, edx
end;
Form1.Memo1.Lines.Add(format('%d %d %d %d',[a, b, c, d]));
end;

function Proc2(w, x, y, z: Integer): Integer;
var a, b, c, d, e, f, g: Integer;
begin
asm
mov a, eax
mov b, ebx
mov c, ecx
mov d, edx
end;
//Form1.Memo1.Lines.Add(format('%d %d %d %d',[a, b, c, d])); // 如果加上此句注释则返回值为 0 ,不加此句则为 11
end;

procedure TForm1.Button3Click(Sender: TObject);
var i: Integer;
begin
Proc(11, 22, 33, 44);
// 前三个参数通过寄存器传递, 其他参数存于栈.
// EAX 先接受第一个参数再接受返回值, EBX ECX 接受后面两个参数.
// 0 0 33 22
i := Proc2(11, 22, 33, 44);
Memo1.Lines.Add(format('%d', [i])); // 11
end;

procedure TForm1.Button4Click(Sender: TObject);
var i: Integer; // 4 字节、32 位
w: Word; // 2 字节、16 位
b1, b2: Byte; // 1 字节、 4 位
begin
{
EAX 32 位
--------------------------------
| | AX 16 位 |
----------------
|AH 8 位|AL 8 位|
}
i := maxint;
w := 0;
b1 := 0;
b2 := 0;
asm
mov eax, i // EAX 寄存器 返回类型 Integer、LongWord、AnsiString、Pointer、Class
mov w, ax // AX 寄存器 返回类型 SmallInt、Word
mov b1, ah
mov b2, al // AL 寄存器 返回类型 Char、Byte
end;
Form1.Memo1.Lines.Add(Format('w=%d b1=%d b2=%d',[w, b1, b2]));
// w=65535; b1=255; b2=255 给 ecx 赋值后, cx ch cl 都有值了!

i := 8;
w := 0;
b1 := 0;
b2 := 0;
asm
mov ebx, i
mov w, bx
mov b1, bh
mov b2, bl
end;
Form1.Memo1.Lines.Add(Format('w=%d b1=%d b2=%d',[w, b1, b2]));
// w=8 b1=0 b2=8

i := 60000;
w := 0;
b1 := 0;
b2 := 0;
asm
mov edx, i
mov w, dx
mov b1, dh
mov b2, dl
end;
Form1.Memo1.Lines.Add(Format('w=%d b1=%d b2=%d',[w, b1, b2]));
// w=60000 b1=234 b2=96
end;

function AsmFun(x: Integer): Integer;
asm
add eax, eax // 相当于 Result := x * 2;
// eax 可以获取第一个参数, 同时又是函数的返回值, 所以可以如此简洁!}
end;

procedure TForm1.Button5Click(Sender: TObject);
var i: Integer;
begin
i := AsmFun(8);
Memo1.Lines.Add(format('%d', [i])); // 16
end;

function Fun(x: Integer): Integer;
asm
mov ecx, &x // 在汇编中访问 Delphi 的变量 x 可以使用 & 操作符
dec ecx // 汇编中的 dec 是减 1 指令, 和 Delphi 是一样的}
mov @Result, ecx // 在本例中去掉 @ 也可以, 暂时不知怎么回事
end;

procedure TForm1.Button6Click(Sender: TObject);
var i: Integer;
begin
i := Fun(9);
Memo1.Lines.Add(format('%d', [i])); // 8
end;

function Fun2(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.Button7Click(Sender: TObject);
var a, b: Integer;
begin
a := 1;
b := 8;
a := Fun2(a, b);
Memo1.Lines.Add(IntToStr(a)); {9}
end;

// 常量可以在汇编语句中运算
function Fun3: Integer;
const
a = 11;
b = 5;
asm
mov eax, a - b
end;

//变量不可以, 方法中的参数也都属于变量

function Fun4: 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.Button8Click(Sender: TObject);
var i: Integer;
begin
i := Fun3;
Memo1.Lines.Add(format('%d', [i])); // 6
i := Fun4;
Memo1.Lines.Add(format('%d', [i])); // 6
end;

// 使用应该保护的 ebx 寄存器
function Fun5(x: Integer): Integer;
{
前面提到 32 位的寄存器有: EAX EBX ECX EDX ESP EBP ESI EDI;
其中, EAX ECX EDX 是自由使用的.
那么剩下的 EBX ESP EBP ESI EDI 五个寄存器就是应该保护的!
所谓保护, 并不是不可以使用,
而是在使用前先把其中的值寄存在另外一个地方, 用完后再恢复其值.
如果不这样做, 有可能会发生意想不到的错误.
}
asm
push ebx // push 是入栈指令, 栈就是系统自动分配的内存
mov ebx, x
inc ebx
mov @Result, ebx
pop ebx // pop 是出栈指令, 也就是恢复 ebx 寄存器原来的值
end;

procedure TForm1.Button9Click(Sender: TObject);
var i: Integer;
begin
i := Fun5(8);
Memo1.Lines.Add(format('%d', [i])); // 9
end;

//十进制
function Fun6: Integer;
asm
mov eax, 255
end;

//十进制数后面也可以加个 D(大小写无关)
function Fun7: Integer;
asm
mov eax, 255D
end;

//二进制后面加 B(大小写无关)
function Fun8: Integer;
asm
mov eax, 11111111B
end;

//八进制后面加 O(大小写无关)
function Fun9: Integer;
asm
mov eax, 377O
end;

//十六进制前面加 $
function Fun10: Integer;
asm
mov eax, $FF
end;

//十六进制也可以是后面加 H(大小写无关)
function Fun11: Integer;
asm
mov eax, 0FFH {使用这种方法, 数字的首位不能是字母, 不然会被认为成标识符}
end;

//非汇编代码的 Delphi 只支持用 $ 表示十六进制
function Fun12: Integer;
begin
Result := $FF;
end;

procedure TForm1.Button10Click(Sender: TObject);
begin
with Memo1.Lines do
begin
Add(format('%d', [Fun6])); // 255
Add(format('%d', [Fun7])); // 255
Add(format('%d', [Fun8])); // 255
Add(format('%d', [Fun9])); // 255
Add(format('%d', [Fun10])); // 255
Add(format('%d', [Fun11])); // 255
Add(format('%d', [Fun12])); // 255
end;
end;

procedure TForm1.Button11Click(Sender: TObject);
var ByteNum: Byte;
begin
{ 逻辑非 Not:
not 1 = 0;
not 0 = 1;
}
//赋值 11111111B (255) 取反:
asm
mov al, 11111111B {eax 包含 ax; ax 包含 al; al 是 eax 的低八位}
not al {给 11111111 取反会得到 00000000}
mov ByteNum, al {把寄存器 al 中的值给变量 ByteNum}
end;
Memo1.Lines.Add(IntToStr(ByteNum)); {0}

{ 逻辑或 Or:
1 or 0 = 1;
0 or 1 = 1;
1 or 1 = 1; // 逻辑异或 1 Xor 1 = 0;
0 or 0 = 0;
}
asm
mov al, 10101010B {170}
mov cl, 01010101B {85}
or al, cl
mov ByteNum, al
end;
Memo1.Lines.Add(IntToStr(ByteNum)); {255}

{ 逻辑与 And:
1 and 1 = 1;
1 and 0 = 0;
0 and 1 = 0;
0 and 0 = 0;
}
asm
mov al, 11111111B {255}
mov cl, 11111111B {255}
and al, cl
mov ByteNum, al
end;
Memo1.Lines.Add(IntToStr(ByteNum)); {255}

{ 逻辑异或 Xor:
1 Xor 0 = 1;
0 Xor 1 = 1;
1 Xor 1 = 0; // 常用来置 0 , 与逻辑或不同: 1 or 1 = 1;
0 Xor 0 = 0;
}

asm
mov al, 11111111B {255}
mov cl, 11111111B {255}
xor al, cl
mov ByteNum, al
end;
Memo1.Lines.Add(IntToStr(ByteNum)); {0}
end;

procedure TForm1.Button12Click(Sender: TObject);
var ByteNum: Byte;
begin
//右移 shr
asm
mov al, 10000000B {128}
shr al, 1 {shr 10000000 一次会得到 01000000}
mov ByteNum, al
end;
Memo1.Lines.Add(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;
Memo1.Lines.Add(IntToStr(ByteNum)); {4; shl 相当于 ×2}
end;

procedure TForm1.Button13Click(Sender: TObject);
var B: Byte;
W: Word;
C: Cardinal;
begin
{Byte 是1字节(8位)无符号整型, 其最大值是 111111112}
asm
mov B, 11111111B;
end;
Memo1.Lines.Add(IntToStr(B)); {255}

{Word 是2字节(16位)无符号整型, 其最大值是 11111111 111111112}
asm
mov W, 1111111111111111B;
end;
Memo1.Lines.Add(IntToStr(W)); {65535}

{Cardinal 是4字节(32位)无符号整型, 其最大值是 11111111 11111111 11111111 111111112}
asm
mov C, 11111111111111111111111111111111B;
end;
Memo1.Lines.Add(IntToStr(C)); {4294967295}

{它们的最小值都是 0}
end;

procedure TForm1.Button14Click(Sender: TObject);
var I: Integer;
begin
// Integer 类型是4字节(32位)有符号整数, 最高位是符号位, 如果是正数, 符号位是 0、负数的符号位是1
// 所以 Integer 的最大值是: 01111111 11111111 11111111 111111112
asm
mov I, 01111111111111111111111111111111B;
end;
Memo1.Lines.Add(IntToStr(I)); {2147483647}

//有符号整数的负数等于相同正数的反码 + 1; Integer 最大值是:
//01111111 11111111 11111111 111111112; 其反码是:
//10000000 00000000 00000000 000000002; 反码 + 1 以后是:
//10000000 00000000 00000000 000000012
asm
mov I, 10000000000000000000000000000001B;
end;
Memo1.Lines.Add(IntToStr(I)); {-2147483647}

//那 Integer 的最小值是多少呢?
//应该是: 10000000 00000000 00000000 000000002
asm
mov I, 10000000000000000000000000000000B;
end;
Memo1.Lines.Add(IntToStr(I)); {-2147483648}

//11111111 11111111 11111111 111111112 是?
asm
mov I, 11111111111111111111111111111111B;
end;
Memo1.Lines.Add(IntToStr(I)); {-1}

//Integer 类型的 0 在内存中是: 00000000 00000000 00000000 000000002
asm
mov I, 00000000000000000000000000000000B;
end;
Memo1.Lines.Add(IntToStr(I)); {0}

//Integer 类型的 10010 的二进制是: 00000000 00000000 00000000 011001002
asm
mov I, 00000000000000000000000001100100B;
end;
Memo1.Lines.Add(IntToStr(I)); {100}

//算 Integer 类型的 -10010:
//00000000 00000000 00000000 01100100 的反码是:
//11111111 11111111 11111111 10011011 ; 反码 + 1 以后是:
//11111111 11111111 11111111 10011100
asm
mov I, 11111111111111111111111110011100B;
end;
Memo1.Lines.Add(IntToStr(I)); {-100}
end;


阅读15