MOVing between two memory addresses

42,742

Solution 1

Your suspicion is correct, you can't move from memory to memory.

Any general-purpose register will do. Remember to PUSH the register if you are not sure what's inside it and to restore it back once done.

Solution 2

That's correct, x86 machine code can't encode an instruction with two explicit memory operands (arbitrary addresses specified in [])

Whats the recommended register

Any register you don't need to save/restore.

In all the mainstream 32-bit and 64-bit calling conventions, EAX, ECX, and EDX are call-clobbered, so AL, CL, and DL are good choices. For a byte or word copy, you typically want a movzx load into a 32-bit register, then an 8-bit or 16-bit store. This avoids a false dependency on the old value of the register. Only use a narrow 16 or 8-bit mov load if you actively want to merge into the low bits of another value. x86's movzx is the analogue of instructions like ARM ldrb.

    movzx   ecx,  byte [rdi]       ; load CL, zero-extending into RCX
    mov    [rdi+10], cl

In 64-bit mode, SIL, DIL, r8b, r9b and so on are also fine choices, but require a REX prefix in the machine code for the store so there's a minor code-size reason to avoid them.

Generally avoid writing AH, BH, CH, or DH for performance reasons, unless you've read and understood the following links and any false dependencies or partial-register merging stalls aren't going to be a problem or happen at all in your code.


(or should I use the stack instead)?

First of all, you can't push a single byte at all, so there's no way you could do a byte load / byte store from the stack. For a word, dword, or qword (depending on CPU mode), you could push [src] / pop [dst], but that's a lot slower than copying via a register. It introduces an extra store/reload store-forwarding latency before the data can be read from the final destination, and takes more uops.

Unless somewhere on the stack is the desired destination and you can't optimize that local variable into a register, in which case push [src] is just fine to copy it there and allocate stack space for it.

See https://agner.org/optimize/ and other x86 performance links in the x86 tag wiki

Solution 3

It's really simple in 16 bit, just do the following:

     push     di
     push     si
     push     cx
     mov      cx,(number of bytes to move)
     lea      di,(destination address)
     lea      si,(source address)
     rep      movsb
     pop      cx
     pop      si
     pop      di

Note: the pushes & pops are neceessary if you need to save the contents of the registers.

Solution 4

It is technically possible to move from memory to memory.

Try using MOVS (move string), and setting [E]SI and [E]DI, depending on whether you want to transfer byte(s), word(s), etc.

    mov si, t_cur    ; Load SI with address of 't_cur'
    mov di, t_last   ; Load DI with address of 't_last'
    movsb            ; Move byte from [SI] to [DI]

    ; Some dummy data
    t_cur    db 0x9a ; DB tells NASM that we want to declare a byte
    t_last   db 0x7f ; (See above)

Note however that this is less efficient than executing MOV twice, but it does execute the copy in a single instruction.

Here's how MOVS should be used, and how it works: https://www.felixcloutier.com/x86/movs:movsb:movsw:movsd:movsq

The instruction MOVS is almost never used on its own, and is for the most part used in conjunction with a REP prefix.

Modern CPUs have fairly efficient implementations of rep movs that is close to the speed of a loop using AVX vector load/store instructions.

    ; - Assuming that 't_src' and 't_dst' are valid pointers
    mov esi, t_src  ; Load ESI with the address of 't_src'
    mov edi, t_dst  ; Load EDI with the address of 't_dst'
    mov ecx, 48     ; Load [ER]CX with the count (let's say 48 dwords =   blocks)
    rep movsd       ; Repeat copying until ECX == 0

Logically the copy happens in 48 copies of 4-byte dword chunks, but really modern CPUs (fast strings / ERMSB) will use 16 or 32-byte chunks for efficiency.

This manual explains how REP should be used, and how it works: https://www.felixcloutier.com/x86/rep:repe:repz:repne:repnz

Solution 5

There's also a MOVS command from moving data from memory to memory:

MOV SI, OFFSET variable1
MOV DI, OFFSET variable2
MOVS
Share:
42,742
Justin
Author by

Justin

html-query-plan - Display SQL Server Execution Plans in HTML

Updated on August 19, 2020

Comments

  • Justin
    Justin over 3 years

    I'm trying to learn assembly (so bear with me) and I'm getting a compile error on this line:

    mov byte [t_last], [t_cur]
    

    The error is

    error: invalid combination of opcode and operands
    

    I suspect that the cause of this error is simply that its not possible for a mov instruction to move between two memory addresses, but half an hour of googling and I haven't been able to confirm this - is this the case?

    Also, assuming I'm right that means I need to use a register as an intermediate point for copying memory:

    mov cl, [t_cur]
    mov [t_last], cl
    

    Whats the recommended register to use (or should I use the stack instead)?