I\'m corrently working on changing examples from complex indirect addresssing mode into simple indirect addressing mode pieces. However, I\'ve come across an example from the B
Disclaimer: I am no fan of the AT&T syntax so if the following seems to describe exactly the opposite of what you wanted to do, I messed it up and you need to switch the arguments around (to, from my point of view, the opposite of what the instructions are meant to do, which is then the opposite of what I meant it to but appears to do the other thing ... wait, I think I twisted myself in a corner here).
(Editor's note: yes, this was backwards, e.g. loading instead of storing, and at least one backwards operand other than that. Fixed now.)
mov %eax, 28(%esp) # store EAX to memory at ESP+28
You need a scratch register to calculate the store address in. If you were loading, you could calculate in the destination register, but for a store we need the original data and the address. This is one reason addressing modes are so convenient.
mov %esp, %ecx # copy ESP to ECX
add $28, %ecx # modify the tmp copy, not the stack pointer
mov %eax, (%ecx) # store EAX into memory dword pointed to by ECX
As @nrz commented, modifying ESP itself will likely make your function crash later when it tries to return by popping its return address off the stack with ret
.
Similarly, a small-steps variant of
cmpl $4, 28(%esp)
is
mov %esp, %eax
add $28, %eax
cmpl $4, (%eax) # compare immediate with dword pointed to by EAX
or, if that last line is too complicated (because also indirect),
mov (%eax), %eax
cmp $4, %eax # dword operand size (cmpl) implied by the register
Note that this instruction sequence alters %eax
whereas the original single instruction does not. There is a reason for that: indirect pointer operations are provided because they do not need intermediate registers. (And because they save instructions, increasing the amount of work you can get done per instruction and per byte of code.)
In older CPUs such as the Z80, one indeed had to do indirect addressing by "manually" loading a register as base, adding the offset, then loading the target value.
Example 1:
This:
addl $28, %esp
movl (%eax), %esp
doesn't do the same thing as move %eax, 28(%esp)
. What you're looking for is something like:
addl $28, %esp
movl %eax, (%esp)
But note that your version modifies the value of esp
, while the original instruction does not.
Example 2:
Again, this:
addl $28, %esp
cmpl $4, %esp
doesn't do the same thing as cmpl $4, 28(%esp)
. The original instruction compares the 32-bit value in memory at address esp+28
with the value 4. Your version compares the value esp+28
with the value 4 (i.e. there's no memory access).