BASM基础实例(二)
{ --------- 加法 --------- }
function TTestFrm.Adder(X, Y: Integer): Integer;
begin
asm
mov EAX, X
add EAX, Y
mov @Result, EAX
end;
end;
procedure TTestFrm.AddBtnClick(Sender: TObject);
begin
ShowMessage(IntToStr(Adder(10, 20)));
end;
{ --------- 减法 --------- }
function TTestFrm.Subtracter(X, Y: Integer): Integer;
begin
asm
mov EAX, X
sub EAX, Y
mov @Result, EAX
end;
end;
procedure TTestFrm.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(Subtracter(20, 16)));
end;
{ --------- 乘法 ---------
Mul指令是 14~18 个周期和 5 个吞吐量
}
function TTestFrm.Multiply(X, Y: Integer): Integer;
begin
asm
mov EAX, X
mul EAX, Y
mov @Result, EAX
end;
end;
procedure TTestFrm.Button3Click(Sender: TObject);
begin
ShowMessage(IntToStr(Multiply(20, 20)));
end;
{ --------- 除法 --------- }
function TTestFrm.Division(X, Y: Integer): Integer;
begin
asm
xor EDX, EDX { 32位除法时,如果被除数达到了双精度时,必须将EDX清零,否则可能引起溢出 }
mov EAX, X { 被除数 }
mov ECX, Y { 除数 }
div ECX
mov @Result, EAX { 相除结果的值数保存在EAX中 }
end;
end;
procedure TTestFrm.Button4Click(Sender: TObject);
begin
ShowMessage(IntToStr(Division(20, 3)));
end;
{ --------- 除法取余 --------- }
function TTestFrm.ArithComp(X, Y: Integer): Integer;
begin
asm
xor EDX, EDX
mov EAX, X
mov ECX, Y
div ECX
mov @Result, EDX { 相除结果的余数保存在EDX中 }
end;
end;
procedure TTestFrm.Button5Click(Sender: TObject);
begin
ShowMessage(IntToStr(ArithComp(20, 3)));
end;
{ --------- 2的阶乘 ---------
shl左移 是 4 个时钟周期和 1 个吞吐量,在可以使用shl指令完成乘法的情况下,
shl效率更高
}
function TTestFrm.Factorial(X, FactX: Integer): Integer;
begin
asm
mov EAX, X
mov ECX, FactX { 初始化计数器的值为:FactX }
@For:
shl EAX, 1 { 左移 }
sub ECX, 1 { 计数器累减 }
cmp ECX, 0 { 判断计数器值是否为:0 }
jge @For { 跳转 }
mov @Result, EAX
end;
end;
procedure TTestFrm.Button2Click(Sender: TObject);
begin
ShowMessage(IntToStr(Factorial(5, 1)));
end;
{ --------- 右移除法 --------- }
function TTestFrm.RightDispl(X, DispX: Integer): Integer;
begin
asm
mov EAX, X
mov ECX, DispX { 初始化计数器的值为:DispX }
@For:
shr EAX, 1 { 右移 }
sub ECX, 1 { 计数器累减 }
cmp ECX, 1 { 判断计数器值是否为:1 }
jge @For { 跳转 }
mov @Result, EAX
end;
end;
procedure TTestFrm.Button6Click(Sender: TObject);
begin
ShowMessage(IntToStr(RightDispl(15, 2)));
end;
{ --------- 无条件跳转 ---------
jmp指令提供了控制转移,但是它不允许进行任何复杂的判断
}
function TTestFrm.UncondJump: Integer;
begin
asm
mov EAX, 10
jmp @Next2
@Next0:
mov EAX, 11
jmp @Next3
@Next1:
mov EAX, 12
jmp @Next0
@Next2:
mov EAX, 13
jmp @Next1
@Next3:
mov @Result, EAX
end;
end;
procedure TTestFrm.Button7Click(Sender: TObject);
begin
ShowMessage(IntToStr(UncondJump));
end;
{ --------- 有条件分支 ---------
一般和 cmp 指令配合使用,条件分支常用的有:
(JG)大于,(JGE、JNGE)大于等于,(JNG)不大于,(JE)等于,(JNE)不等于,(JNL)不小于,
(JL、JLE)小于,(JNLE)小于等于
}
procedure TTestFrm.CondJump(CondX: Integer);
var
reArray: Array[0..4] of String;
begin
reArray[0] := '大于500'; { 在当前的函数栈内,按顺序申请数组空间 }
reArray[1] := '大于等于400'; { 基址指针一般保存在EBP中,每个数组单元占用4个字节,内容保存为指向堆的具体指针 }
reArray[2] := '小于等于300'; { 基于栈是向下增长的,即每压一次栈,栈顶的地址就减少一次 }
reArray[3] := '不小于200'; { 可以得出:reArray[0] = EBP-$18 }
reArray[4] := '小于150'; { reArray[4] = EBP-$08 (按数组单元占用字节数递减) }
{ EBP-$04 的位置则保存了 CondX 的值 }
asm
mov EAX, CondX
cmp EAX, 500
jg @Gt500 { 大于500 }
cmp EAX, 400
jge @Gt400 { 大于等于400 }
cmp EAX, 300
jnle @Gt300 { 小于等于300 }
cmp EAX, 200
jnl @Gt200 { 不小于200 }
cmp EAX, 150
jl @Gt150 { 小于150 }
@Gt500:
mov EAX, [EBP-$18] { 根据参数调用原则,EAX、EDX、ECX分别保存第1,2,3个参数 }
jmp @End { 将指定地址的值传入EAX,再调用Showmessage,相当于调用Showmessage(EAX) }
@Gt400:
mov EAX, [EBP-$14]
jmp @End
@Gt300:
mov EAX, [EBP-$10]
jmp @End
@Gt200:
mov EAX, [EBP-$0c]
jmp @End
@Gt150:
mov EAX, [EBP-$08]
jmp @End
@End:
CALL ShowMessage
end;
end;
procedure TTestFrm.Button9Click(Sender: TObject);
begin
CondJump(120);
end;
{ --------- 循环 --------- }
function TTestFrm.Circulator(X: Integer): Integer;
begin
asm
mov EAX, X { 赋初始值 }
mov ECX, X { 初始化计数器值为X }
@Cyc:
ADD EAX, X { 执行累加操作 }
loop @Cyc { 循环 0-X 次 }
mov @Result, EAX
end;
end;
procedure TTestFrm.Button8Click(Sender: TObject);
begin
ShowMessage(IntToStr(Circulator(4)));
end;
{ --------- 引用对象方法 --------- }
procedure TTestFrm.FunTest(X, Y: Integer);
begin
ShowMessage(Format('X: %d, Y: %d', [X, Y]));
end;
procedure TTestFrm.CallFun(X, Y: Integer);
begin
asm
//mov EAX, Self { 在引用对象方法时,Delphi会默认将Self做为参数传入EAX,此行是否添加不影响结果 }
mov EDX, X { 使用Register调用规则,传递参数的顺序是:Eax, Edx, Ecx }
mov ECX, Y
call FunTest
end;
end;
procedure TTestFrm.Button10Click(Sender: TObject);
begin
CallFun(11, 22);
end;
{ --------- 引用DLL方法 ---------
DLL中开放的接口定义如下:
procedure DllTest(AName, AText: PChar; Var AReMsg: PChar); stdcall;
}
{ 加载DLL的过程 }
procedure TTestFrm.CallDll(AName, AText: PChar; var AReMsg: PChar);
var
iDLLHandle: THandle;
pProc: Pointer;
begin
iDLLHandle := LoadLibrary('Dll_Test.dll');
try
if iDLLHandle >= 32 then
begin
pProc := GetProcAddress(iDLLHandle, PChar('DllTest'));
if pProc <> nil then
DoCallDll(pProc, AName, AText, AReMsg)
end;
except
FreeLibrary(iDLLHandle);
end;
end;
procedure TTestFrm.DoCallDll(AProc: Pointer; AName, AText: PChar; var AReMsg: PChar);
begin
asm { 使用Stdcall调用规则,参数从右至左的顺序传递 }
mov EAX, AReMsg { 外部传入或全局的变量,需要使用MOV,如果是局部变量,必须使用LEA }
push EAX { 对象外方法调用,应该使用入栈的方法传入参数? }
push AText { 未解:为何使用PUSH而不直接MOV寄存器还没有理解清楚?}
push AName
call AProc { 调用指定的方法指针 }
end; { 未解:如果调用的是Function指针,使用 mov @Result, al时,Result总得不到正确的反回结果 }
end;
procedure TTestFrm.Button15Click(Sender: TObject);
var
pReMsg: PChar;
begin
pReMsg := StrAlloc(255);
try
CallDll(PChar('TestName'), PChar('TestText'), pReMsg);
ShowMessage('反馈内容:' + pReMsg);
finally
StrDispose(pReMsg);
end;
end;
{ --------- 字符检索 --------- }
function TTestFrm.AsmPos(APosStr: String; ASubChar: String): Integer;
begin
asm { 根据Register调用规则,Eax保存的是Self, Edx保存的是APosStr, Ecx保存的是ASubChar }
test EDX, EDX { 测试Edx值是否为0,如果为0,则表明传入的值为nil }
jz @Exit { 如果0标志位被置,则跳转到END }
mov EDI, [EDX - 4] { String类型在内存中的结构,前8位为引用计数,前4位为长度,所以EDX-4为APosStr的长度 }
test ECX, ECX { 测试ECX的值是否为空,Ecx中保存ASubChar的值 }
jz @Exit
mov ESI, 0 { 由于常用的计数器寄存器Ecx被占用,使用ESI做为计数器寄存器并初始化 }
mov AL, BYTE [ECX] { 将Ecx中指针指向的字符保存到AL中 }
@Loop:
cmp AL, BYTE [EDX + ESI] { 比较字符 }
jz @Found { 相等则跳出 }
inc ESI { 计数器累加 }
cmp ESI, EDI { 比较当前计数是否等于长度 }
jz @Exit { 如果相等,则跳出 }
jmp @Loop { 循环 }
@Found:
inc ESI { 因为查找的起始值是从0开始,所以反回的位置应该加1 }
mov EAX, ESI { 保存计数器到结果 }
jmp @End
@Exit:
mov EAX, -1 { 没找到反回-1 }
@End:
mov @Result, EAX
end;
end;
procedure TTestFrm.Button11Click(Sender: TObject);
begin
ShowMessage(IntToStr(AsmPos('abcde', 'g')));
end;