28

我正在尝试学习汇编(所以请耐心等待),但在这一行出现编译错误:

mov byte [t_last], [t_cur]

错误是

error: invalid combination of opcode and operands

我怀疑这个错误的原因仅仅是一个 mov 指令不可能在两个内存地址之间移动,但是半小时的谷歌搜索我无法确认 - 是这样吗?

另外,假设我是对的,这意味着我需要使用寄存器作为复制内存的中间点:

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

推荐使用什么寄存器(或者我应该使用堆栈代替)?

4

6 回答 6

35

你的怀疑是正确的,你不能从一个记忆移到另一个记忆。

任何通用寄存器都可以。如果您不确定其中的内容,请记住推入寄存器,并在完成后将其恢复。

于 2009-08-19T10:48:07.387 回答
6

没错,x86 机器代码不能用两个显式内存操作数(在 中指定的任意地址[])对指令进行编码

推荐的寄存器是什么

您不需要保存/恢复的任何寄存器。

在所有主流的 32 位和 64 位调用约定中,EAX、ECX 和 EDX 都是 call-clobbered,因此 AL、CL 和 DL 是不错的选择。对于字节或字副本,您通常需要movzx加载到 32 位寄存器,然后是 8 位或 16 位存储。这避免了对寄存器旧值的错误依赖。mov如果您想要合并到另一个值的低位,请仅使用窄的 16 位或 8 位加载。x86movzx类似于 ARM 等指令ldrb

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

在 64 位模式下,SIL、DIL、r8b、r9b 等也是不错的选择,但需要在存储的机器代码中使用 REX 前缀,因此有一个较小的代码大小理由来避免它们。

通常出于性能原因避免编写 AH、BH、CH 或 DH,除非您已阅读并理解以下链接,并且任何错误的依赖关系或部分寄存器合并停顿不会成为问题或在您的代码中根本不会发生.


(或者我应该使用堆栈代替)?

首先,您根本无法推送单个字节,因此您无法从堆栈中进行字节加载/字节存储。对于 word、dword 或 qword(取决于 CPU 模式),您可以push [src]/ pop [dst],但这比通过寄存器复制要慢得多。在可以从最终目的地读取数据之前,它引入了额外的存储/重新加载存储转发延迟,并且需要更多的微指令。

除非堆栈上的某个地方所需的目标,并且您无法将该局部变量优化到寄存器中,在这种情况下push [src],将它复制到那里并为其分配堆栈空间就可以了。

请参阅https://agner.org/optimize/和x86 标签 wiki中的其他 x86 性能链接

于 2018-11-25T18:42:33.700 回答
5

这在 16 位中非常简单,只需执行以下操作:

     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

注意:如果您需要保存寄存器的内容,则需要推送和弹出。

于 2012-07-10T00:49:39.440 回答
4

从内存移动到内存在技术上是可能的。

尝试使用MOVS(移动字符串),并设置[E]SI[E]DI,具体取决于您是否要传输字节、字等。

    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)

但是请注意,这比执行两次MOV效率低,但它确实在一条指令中执行了副本。

以下是MOVS的使用方法及其工作原理: https ://www.felixcloutier.com/x86/movs:movsb:movsw:movsd:movsq

指令MOVS几乎从不单独使用,并且大部分与REP前缀一起使用。

现代 CPU 具有相当高效的实现rep movs,接近使用 AVX 矢量加载/存储指令的循环速度。

    ; - 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

从逻辑上讲,复制发生在 48 个 4 字节 dword 块的副本中,但真正现代的 CPU(快速字符串/ERMSB)将使用 16 或 32字节块来提高效率。

本手册解释了应如何使用 REP及其工作原理: https ://www.felixcloutier.com/x86/rep:repe:repz:repne:repnz

于 2019-06-16T18:33:01.450 回答
3

还有一个将数据从内存移动到内存的 MOVS 命令:

MOV SI, OFFSET variable1
MOV DI, OFFSET variable2
MOVS
于 2009-08-19T11:01:15.643 回答
-2

只是想和你讨论一下“记忆障碍”。在c代码中

a = b;//Take data from b and puts it in a

将被组装到

mov %eax, b # suppose %eax is used as the temp
mov a, %eax

系统不能保证赋值的原子性。这就是为什么我们需要一个 rmb(读屏障)

于 2013-07-17T07:34:06.897 回答