根据
“Using Assembler in Delphi”,eax将包含自我.但是,eax的内容为0,如图所示.我想知道有什么问题吗?
procedure TForm1.FormCreate(Sender: TObject); var X,Y: Pointer; begin asm mov X,eax mov Y,edx end; ShowMessage(IntToStr(NativeInt(X)) + ' ; ' + IntToStr(NativeInt(Y))); end;
解决方法
当我编译这个代码时,在调试设置下生成的代码就像这样:
begin 005A9414 55 push ebp 005A9415 8BEC mov ebp,esp 005A9417 83C4E4 add esp,-$1c 005A941A 33C9 xor ecx,ecx 005A941C 894DEC mov [ebp-$14],ecx 005A941F 894DE8 mov [ebp-$18],ecx 005A9422 894DE4 mov [ebp-$1c],ecx 005A9425 8955F0 mov [ebp-$10],edx 005A9428 8945F4 mov [ebp-$0c],eax 005A942B 33C0 xor eax,eax 005A942D 55 push ebp 005A942E 6890945A00 push $005a9490 005A9433 64FF30 push dword ptr fs:[eax] 005A9436 648920 mov fs:[eax],esp mov X,eax 005A9439 8945FC mov [ebp-$04],eax mov Y,edx 005A943C 8955F8 mov [ebp-$08],edx
当代码开始执行时,eax确实是自我指针.但编译器已经选择将其保存到ebp- $0c,然后将其清零.这完全取决于编译器.
发布设置下的代码非常相似.编译器仍然选择使eax归零.当然,你不能依赖这个编译器.
begin 005A82A4 55 push ebp 005A82A5 8BEC mov ebp,esp 005A82A7 33C9 xor ecx,ecx 005A82A9 51 push ecx 005A82AA 51 push ecx 005A82AB 51 push ecx 005A82AC 51 push ecx 005A82AD 51 push ecx 005A82AE 33C0 xor eax,eax 005A82B0 55 push ebp 005A82B1 6813835A00 push $005a8313 005A82B6 64FF30 push dword ptr fs:[eax] 005A82B9 648920 mov fs:[eax],eax 005A82BC 8945FC mov [ebp-$04],edx 005A82BF 8955F8 mov [ebp-$08],edx
记住参数传递定义函数开始执行时的寄存器和堆栈的状态.接下来会发生什么,功能如何解码参数是编译器.没有义务保留用于参数传递的寄存器和堆栈.
如果您将asm注入函数的中间,则不能指望像eax这样的易失性寄存器具有特定的值.他们最近将持有编译器发生的任何事情.
如果要在函数执行开始时检查寄存器,则需要使用纯asm函数来确保编译器修改用于参数传递的寄存器:
var X,Y: Pointer; asm mov X,edx // .... do something with X and Y end;
编译器将使其选择非常依赖于其余功能中的代码.对于您的代码,组合字符串传递给ShowMessage的复杂性将导致相当大的前导.考虑这个代码:
type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private i: Integer; function Sum(j: Integer): Integer; end; .... procedure TForm1.FormCreate(Sender: TObject); begin i := 624; Caption := IntToStr(Sum(42)); end; function TForm1.Sum(j: Integer): Integer; var X: Pointer; begin asm mov X,eax end; Result := TForm1(X).i + j; end;
在这种情况下,代码很简单,编译器可以单独离开eax. Sum的优化版本生成代码是:
begin 005A8298 55 push ebp 005A8299 8BEC mov ebp,esp 005A829B 51 push ecx mov X,eax 005A829C 8945FC mov [ebp-$04],eax Result := TForm4(X).i + j; 005A829F 8B45FC mov eax,[ebp-$04] 005A82A2 8B80A0030000 mov eax,[eax+$000003a0] 005A82A8 03C2 add eax,edx end; 005A82AA 59 pop ecx 005A82AB 5D pop ebp 005A82AC C3 ret
完全诚实地,在Pascal函数中作为asm块放置的内联汇编并不是很有用.编写程序集的事情是你需要充分了解寄存器和堆栈的状态.在ABI定义的函数的开始和结束处有很好的定义.
但在函数的中间,该状态完全取决于编译器做出的决定.在其中注入asm块需要您了解编译器所做的决定.这也意味着编译器不能理解你所做的决定.这通常是不切实际的.实际上对于x64编译器,Embarcadero禁止这样的内嵌asm块.我个人从来没有在我的代码中使用内嵌asm块.如果我写的asm我总是写纯粹的asm函数.