我在Delphi中乱搞ASM。根据我的理解,EAX持有Result。在下面,我必须将RET放在最后,否则Result不正确(如果输入为0则正确)。我做错了什么,或者我应该说,我对此无法理解?
function MSb(const Val: Integer): Integer;
label
Go;
asm
CMP EAX, 0
JNZ Go
MOV EAX, -1
RET
Go:
BSR EBX, EAX
MOV EAX, EBX
RET
end;
如果我说以下内容:
MOV Result, EBX
然后我得到以下编译:
MOV [EPB-$04], EBX
MOV EAX, [EPB-$04]
但是,我上面的代码有以下后记:
MOV EAX, EDX
RET
答案 0 :(得分:3)
至少在没有启用优化的情况下,您的功能具有前导码和后置码功能。看看吧:
Project46.dpr.13: asm 0041A1F4 55 push ebp 0041A1F5 8BEC mov ebp,esp 0041A1F7 83C4F8 add esp,-$08 0041A1FA 8945F8 mov [ebp-$08],eax Project46.dpr.14: CMP EAX, 0 0041A1FD 83F800 cmp eax,$00 Project46.dpr.15: JNZ Go 0041A200 7506 jnz $0041a208 Project46.dpr.16: MOV EAX, -1 0041A202 B8FFFFFFFF mov eax,$ffffffff Project46.dpr.17: RET 0041A207 C3 ret Project46.dpr.19: BSR EBX, EAX 0041A208 0FBDD8 bsr ebx,eax Project46.dpr.20: MOV EAX, EBX 0041A20B 89D8 mov eax,ebx Project46.dpr.21: RET 0041A20D C3 ret Project46.dpr.22: end; 0041A20E 8B45FC mov eax,[ebp-$04] 0041A211 59 pop ecx 0041A212 59 pop ecx 0041A213 5D pop ebp 0041A214 C3 ret
因此pre-amble设置堆栈帧。它会保存ebp
寄存器,并修改ebp
和esp
寄存器。还要注意后同步。您需要执行该代码以恢复已保存的寄存器。
处理此问题的常用方法是跳转到函数的末尾而不是使用ret
。所以写下你的代码:
function MSb(const Val: Integer): Integer;
asm
CMP EAX, 0
JNZ @@go
MOV EAX, -1
JMP @@exit
@@go:
BSR EBX, EAX
MOV EAX, EBX
@@exit:
end;
这样可以确保执行后同步码。你应该养成以这种方式编写代码的习惯,以确保执行任何前导码。
现在,除此之外我怀疑你在问题中提到的问题实际上涉及与使用Pascal标签而不是asm标签有关的编译器错误。好吧,也许这是一个编译器错误,但也许使用Pascal标签是一个错误。请考虑以下程序:
{$APPTYPE CONSOLE}
function MSb(const Val: Integer): Integer;
asm
CMP EAX, 0
JNZ @@Go
MOV EAX, -1
JMP @@exit
@@Go:
BSR EBX, EAX
MOV EAX, EBX
@@exit:
end;
function MSb2(const Val: Integer): Integer;
label
Go;
asm
CMP EAX, 0
JNZ Go
MOV EAX, -1
RET
Go:
BSR EBX, EAX
MOV EAX, EBX
end;
begin
Writeln(Msb(0));
Writeln(Msb(1));
Writeln(Msb2(0));
Writeln(Msb2(1));
Readln;
end.
使用优化编译时的输出是:
-1 0 -1 4
那么,那个奇怪的4
呢。好吧,让我们看看Msb2
的汇编代码,它基本上就是您的代码:
004059E8 83F800 cmp eax,$00 004059EB 7506 jnz $004059f3 004059ED B8FFFFFFFF mov eax,$ffffffff 004059F2 C3 ret 004059F3 0FBDD8 bsr ebx,eax 004059F6 89D8 mov eax,ebx 004059F8 8BC2 mov eax,edx 004059FA C3 ret
为什么实际上是edx
的值,这是一个尚未赋值的易失性寄存器,在函数返回之前被移入eax
。这是您要报告的问题。我的猜测是使用Pascal标签会使汇编程序混乱。坚持使用asm标签。
以下是Msb
的汇编代码:
004059D4 83F800 cmp eax,$00 004059D7 7506 jnz $004059df 004059D9 B8FFFFFFFF mov eax,$ffffffff 004059DE C3 ret 004059DF 0FBDD8 bsr ebx,eax 004059E2 89D8 mov eax,ebx 004059E4 C3 ret
那更像是它!请注意编译器如何知道此处没有可检测项,并用jmp @@exit
替换ret
。
答案 1 :(得分:1)
我做错了什么,或者我应该说,我不明白的是什么 此?
基本上清楚地说明了前导和后导,但在使用汇编代码时也要小心。根据调用约定,参数以不同方式传递。如果删除Pascal
,label
并使用汇编程序标签(RET
),则代码将在@@
(具有讽刺意味的过时)调用约定下正常运行。最好始终指示汇编代码的调用约定,因为Result
可能确实引用EAX
或 local 变量来保存它。使用EAX
(默认)调用约定时,Result
会直接映射到Register
。