我可以说这是一个非常简单的问题,但我还没有弄清楚。基本上,我只希望能够将一个元素作为数组,并使用寄存器从中添加和减去一些数字,然后将结果放入我的结果变量中。
segment .data
a dw 4, 234, -212
b db 112, -78, 50
result dq 0
segment .text
global main
main:
mov rax, [a]
我知道解决方案与偏移量和索引有关,但是我不知道应该如何只将一个数组元素放入寄存器。
我该怎么办?
如果要将值视为带符号,则需要movsx
。假设NASM语法:
movsx
((MASM或GNU .intel_syntax将使用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
而不是word ptr
,只需将word
添加到内存操作数的大小说明符中。)
ptr
可以是类似于1
或[a + rsi*2]
的寄存器,因此您可以轻松地遍历数组。 [b + rsi]
我写了Referencing the contents of a memory location. (x86 addressing modes)而不是2来表示它的索引1(第二个数组元素),按元素大小缩放。汇编器将计算常量表达式,并仅使用与1*2
相同的(相对RIP)寻址方式,但偏移量不同。
[如果您需要它以与位置无关的代码工作(在其中您不能将符号的32位绝对地址用于[a]
寻址模式),请先进行[disp32 + register]
(相对RIP的LEA),然后执行lea rdi, [a]
。
如果要零扩展,请使用[rsi + rsi*2]
movzx
如果知道全部结果的高位始终为零,请使用EAX(32位操作数大小),但最后要使用。 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
此代码对应于C,例如
The advantages of using 32bit registers/instructions in x86-64
其中,您可以查看编译器输出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?
}
,并查看这些指令和寻址模式。
请注意,on the Godbolt compiler explorer会加载一个字节并将其[[merge放入RAX的低字节。
您通常不想要这个; mov al, [b + 1]
是现代x86中加载字节的常规方法。现代的x86 CPU将x86解码为类似于RISC的内部uops,以进行寄存器重命名和乱序执行。 movzx
避免了对完整寄存器的旧值的任何错误依赖。与ARM movzx
,MIPS ldrb
等类似。
您可以安全地
读取
8位和16位寄存器(并且您需要存储一个字),但是除非有充分的理由并且理解可能的性能影响,否则通常避免写部分寄存器。 C0])。例如您已经将cmp +lbu
之前的完整目标异或为零。