0

我可以说这是一个超级简单的问题,但我还没有弄清楚。基本上,我只想能够将一个元素放入一个数组,并使用寄存器从中添加和减去一些数字,然后将结果放入我的结果变量中。

segment .data
  a      dw  4, 234, -212
  b      db  112, -78, 50
  result dq  0
segment .text       
  global main
main:
  mov   rax, [a]        

我知道解决方案与偏移量和索引有关,但我不明白我应该如何将一个数组元素放入寄存器。

我能做些什么?

4

1 回答 1

0

如果要将值视为已签名,则需要movsx. 假设 NASM 语法:

default rel
; ... declarations and whatever    

    movsx   rax, word [a + 1*2]    ; a is an array of dw = words
    movsx   rcx, byte [b + 1*1]    ; b is an array of db = bytes

    add     rax, rcx
    mov     [result], rax         ; result is a qword

(MASM 或 GNU .intel_syntax 将使用word ptr而不是word,只需添加ptr到内存操作数的大小说明符。)

1可以是一个寄存器,这样您[a + rsi*2][b + rsi]可以轻松地遍历您的数组。 引用内存位置的内容。(x86 寻址模式)

我写1*2的不是 2,而是表示它是索引 1(第二个数组元素),按元素大小缩放。汇编器将对常量表达式求值,并仅使用与它相同的(RIP 相对)寻址模式,[a]但具有不同的偏移量。

如果您需要它在与位置无关的代码中工作(在这种情况下,您不能[disp32 + register]对符号使用具有 32 位绝对地址的寻址模式),lea rdi, [a]请首先使用 (RIP-relative LEA) 并执行[rsi + rsi*2].


如果你想要零扩展,你会使用movzx

    movzx   eax, word [a + 1*2]    ; a is an array of dw = words
    movzx   ecx, byte [b + 1*1]    ; b is an array of db = bytes
    ; word and byte zero-extended into 64-bit registers:
    ; explicitly to 32-bit by MOVZX, and implicitly to 64-bit by writing a 32-bit reg

    ; add     eax, ecx              ; can't overflow 32 bits, still zero-extended to 64
    sub     rax, rcx              ; want the full width 64-bit signed result 
    mov     [result], rax         ; result is a qword

如果您知道完整结果的高位始终为零,则只需使用 EAX(32 位操作数大小),除了末尾。 在 x86-64 中使用 32 位寄存器/指令的优点

这段代码对应C之类的

static  uint16_t a[] = {...};
static  uint8_t b[] = {...};
static  int64_t result;

void foo(){
    int64_t rax = a[1] - (int64_t)b[1];
    result = rax;    // why not just return this like a normal person instead of storing?
}

说到这,您可以查看Godbolt 编译器资源管理器上的编译器输出并查看这些指令和寻址模式。


请注意,这mov al, [b + 1]将加载一个字节并将其合并到 RAX 的低字节中。

你通常不想要这个;movzx是在现代 x86中加载字节的正常方式。现代 x86 CPU 将 x86 解码为类似 RISC 的内部微指令,用于寄存器重命名 + 乱序执行。 movzx避免对完整寄存器的旧值的任何错误依赖。它类似于 ARM ldrb、 MIPSlbu等。

合并到 RAX 的低字节或字是 x86 可以做的奇怪的 CISC 事情,但 RISC 不能。

您可以安全地读取8 位和 16 位寄存器(并且您需要进行字存储),但通常避免写入部分寄存器,除非您有充分的理由,并且您了解可能的性能影响(为什么 GCC 不使用部分注册?)。例如,您已经在 cmp + 之前对整个目的地进行了异或归零setcc al

于 2019-10-30T06:03:17.207 回答