There are two parts to your code which have just been jammed together in your example. Hopefully that's just a formatting mistake. Otherwise the code makes no sense.
First, the part that calls the function. This assumedly would be a snippet from some larger program that does something with the result and eventually exits.
val dw 4
push val
call square_me
push EIP
The problems here are:
push EIP
isn't a valid instruction. You can't push the EIP register, and even if you could, why would you want to?
- For this kind of calling format, it's the callers responsibility to clean up the stack so you need to follow that call with something like
add ESP,4
to clean up the val that you pushed onto the stack.
Then for the function itself:
push EBP
mov EBP, ESP
That bit is good - you're setting up the stack frame. But what is the purpose of this next line?
push val
First you're accessing the global variable val rather than the parameter passed to the function. If you want to access parameters by name, I believe you can set that up as part of the PROC
directive in MASM, otherwise you need to use [ebp+8]
to get to the first parameter on the stack.
Regardless of how you access it, though, there is no need to save the value on the stack here. It's already in memory - you're not going to lose it.
As for the multiply, this isn't a valid instruction:
mul val, val
You can't multiply two memory references together - you can only multiply something with the EAX register. So you need to load your value into eax first. Something like this:
mov EAX,[EBP+8]
mul EAX,EAX
At this point the result will be in EAX (and EDX - but we don't care about the high word), which is what you want for the function to return the answer. But then you are doing this:
mov eax, val
That's just going to overwrite the multiplication result that you just calculated.
pop val
This pop assumedly goes with the initial push val
, and can likewise be removed.
mov ESP, EBP
pop EBP
Those are fine - that just cleans up stack frame. But then you don't need this:
leave
The leave
instruction is essentially identical to mov esp,ebp / pop ebp
. You don't need both of them.
In short, your function should look something more like this:
push EBP
mov EBP, ESP
mov EAX, [EBP + 8]
mul EAX, EAX
mov ESP, EBP
pop EBP
ret
As for the answer given in the document, that wasn't great either. There is no reason for setting EDX to zero. As ady has already said, the mul
instruction is going to override EDX anyway.
I could understand maybe setting it to zero after the mul
instruction, because the result is technically incorrect given that you are using an unsigned multiplication on signed integers. It just doesn't matter because you are going to be discarding the high word (EDX) anyway. In my opinion, it would have been clearer to use imul
instead.
I also don't see why they felt the need to move the parameter into both EBX and EAX when they could have just multiplied EAX with itself. Perhaps there is some performance reason for doing that, but I doubt it.
Regarding the use of [EBP+8]
to access the function parameter, you need to understand what your stack looks like inside the function.
When calling the function, you first push val, then the call pushes its return address onto the stack, and then inside the function you push EBP. So at that point, your stack would look like this:
val // push val
return address // call square_me
STACK TOP: EBP // push ebp
Now when you do mov EBP,ESP
you will have set EBP to point to the top of the stack. So at [EBP+0]
you have the saved copy of EBP, at [EBP+4]
you have the return address of the caller, and at [EBP+8]
you have the val parameter that was passed in the call.