I'm messing around with ASM in Delphi. From my understanding, EAX holds Result. In the following, I have to put RET at the end, otherwise Result is not correct (it is correct if the input is 0). What am I doing wrong, or should I say, what don't I understand about this?
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;
If I say the following:
MOV Result, EBX
Then I get the following compilation:
MOV [EPB-$04], EBX
MOV EAX, [EPB-$04]
However, my code above has the following postscript:
MOV EAX, EDX
RET
At least without optimization enabled, your function has a pre-amble and a post-amble. Have a look at it:
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
So the pre-amble sets up the stack frame. It saves away the ebp
register, and modifies both the ebp
and esp
registers. Notice also the post-amble. You do need to execute that code to restore the saved registers.
The usual way to deal with this is to jump to the end of the function instead of using ret
. So write your code like this:
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;
This way you ensure that the post-amble is executed. You really should get into the habit of writing the code this way to ensure that any pre-amble is executed.
Now, beyond that I suspect that the problem you mention in the question actually relates to a compiler bug relating to your use of a Pascal label rather than an asm label. Well, perhaps it is a compiler bug, but perhaps it is just a mistake to use a Pascal label. Consider the following program:
{$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.
The output when compiled with optimization is:
-1 0 -1 4
So, what about that rather odd 4
. Well, let's look at the assembled code for Msb2
, which is essentially your code:
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
Why on earth is the value of edx
, a volatile register whose value has not been assigned, being moved into eax
just before the function returns. This is the problem that you are reporting. My guess is that the use of a Pascal label is confusing the assembler. Stick to asm labels.
Here is the assembled code for 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
That's more like it! Notice how the compiler knows that there is no post-able here, and replaces the jmp @@exit
with a straight ret
.