assemblyx86addressing

Transformation of based indexed mode into indirect addressing mode (x86 assembly)


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 Based Mode, I'm unable to "transform".

Code:

move %eax, 28(%esp)

I've tried

addl    $28, %esp
movl    (%eax), %esp

This creates a segmentation fault; and I've no idea how else I should write it.

Another example, I've failed to "transform is

compl $4, 28(%esp)

-> into

addl    $28, %esp
cmpl    $4, %esp

However this is working, but it changes my output slightly, so it might not be correct as well.


Solution

  • 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.