如何在64位寄存器中隔离字节和字数组元素

问题描述 投票:0回答:1

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

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

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

我该怎么办?

assembly x86-64 sign-extension zero-extension
1个回答
0
投票

如果要将值视为带符号,则需要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等类似。

合并到RAX的低字节或字中是x86可以完成的,但RISC无法做到的奇怪的CISC。

您可以安全地

读取

8位和16位寄存器(并且您需要存储一个字),但是除非有充分的理由并且理解可能的性能影响,否则通常避免写部分寄存器。 C0])。例如您已经将cmp + lbu之前的完整目标异或为零。
© www.soinside.com 2019 - 2024. All rights reserved.