将1个字节的立即值添加到2个字节的内存位置

时间:2017-07-13 21:08:59

标签: assembly x86 nasm

this pageadd说明文档说明如下:

enter image description here

请注意我突出显示的两条说明。

我在NASM中尝试了以下代码(符合第一个突出显示的指令):

add WORD [myvar], BYTE 0xA5

但是我收到了以下错误:

  

警告:签名字节值超出界限

我做错了什么?

2 个答案:

答案 0 :(得分:7)

8位立即数操作数(此处由imm8表示)被符号扩展为16(或32)位,以匹配另一个操作数(r/m16r/m32的大小,分别)。

因此,只能表示介于-128和127之间的值,这就是您从汇编程序收到此警告的原因。

对于值0xA5,您需要使用WORD立即(imm16):

add WORD [myvar], WORD 0xA5

(尽管WORD在源操作数上是可选的,因为它是由常量的大小暗示的。)

答案 1 :(得分:2)

我不会重复@ fuz的回答,但我想补充一下:

如果您刚刚让汇编程序通过编写add word [myvar], 0xA5来完成其工作,那么它将选择最小的编码。如果您的立即符合扩展名为imm8的版本,则会使用add r/m16, imm8编码。通常不需要在非内存操作数上使用size-overrides。所有主要的x86汇编程序都优化了立即操作数的大小。有些人(例如NASM)甚至会将mov rax, 1优化为相当但更短的mov eax, 1,以及类似的东西,但其他人(YASM)不会。

但是,您可以强制汇编程序使用比填充/对齐所需更宽的立即数。例如add word [myvar], strict word 1。会使用imm16版本。 (没有strict,它不会阻止汇编程序将其优化为较小的编码。)您还可以add word [rcx + strict dword 0], strict word 1强制[base + disp32]编码用于寻址模式。

如果可能,避免对mov 以外的指令执行16位立即操作数。在许多Intel CPU上,由于an LCP stall,该指令的解码速度很慢。对于具有解码uop缓存的较新CPU,这可能不是问题。但是在较旧的Intel CPU上,这可能会以划痕寄存器为代价运行得更快:

movzx  eax, word [myvar]
add    eax, 0xA5          # add ax, 0xa5 is 1B smaller, but has the same LCP stall.
mov    [myvar], ax

add / sub从左到右,因此较宽的添加的低部分始终与您从狭窄的add获得的部分相同。避免寄存器操作数的LCP停顿通常很便宜(add eax,imm32只需要额外的1B,因为它不需要操作数大小的前缀),但加载和存储是额外的。

这是一个更大的代码大小,因此在没有LCP停顿的CPU上可能会更慢。英特尔Sandybridge系列的前端只有1个uop(可以在单指令版本中微量融合负载+加入),执行单元/调度程序的uop数量相同。 (内存目标指令解码加载,ALU和存储uops。)