Explains除非我们在将立即数添加到存储在内存地址的值时指定大小运算符(例如 byte 或 dword),否则 NASM 将返回错误消息。
section .data ; Section containing initialized data
memory_address: db "PIPPACHIP"
section .text ; Section containing code
global _start ; Linker needs this to find the entry point!
_start:
23 mov ebx, memory_address
24 add [ebx], 32
...................................................... ......
24: error: operation size not specified.
公平就是公平。
我很好奇为什么会这样。因为以下两段代码将产生相同的结果。
添加字节[ebx], 32
或
添加双字 [ebx], 32
那么这有什么区别呢? (除了对于为什么在这种情况下使用双字没有多大意义之外)。仅仅是因为“NASM 这么说”吗?或者我在这里缺少一些逻辑?
如果汇编器可以从寄存器名称中破译操作数大小,例如 add [ebx], eax
可以工作,为什么不对立即数执行相同的操作,即继续计算预先立即值的大小。
什么要求意味着在将立即值添加到内存地址处的值时需要指定大小运算符?
NASM 版本 2.11.08 架构x86
最佳答案
出于多种原因,您使用的操作数大小确实很重要,并且使用整数值隐含的大小会很奇怪且不直观/不明显。当存在歧义时出现 NASM 错误是一个更好的设计,因为两个操作数都不是寄存器。
<小时/>As the two following segments of code will yield the same result:
add byte [ebx], 32 add dword [ebx], 32
它们只会产生相同的结果,因为 'P' + 32
不会进入下一个字节。
根据结果设置标志。如果第 4 个字节设置了高位,则 SF
将设置为双字版本。
回复:关于 CF 如何工作的评论:
添加的进位始终为 0
或 1
。即两个 N
位整数之和始终适合 (N+1)
位整数,其中额外位为 CF
。将 add eax, ebx
视为在 CF:EAX
中生成结果,其中每个位可以是 0 或 1,具体取决于输入操作数。
此外,如果 ebx
指向页面中的最后一个字节,则 dword [ebx]
可能会出现段错误(如果下一页未映射),但是 byte [ebx]
不会。
这也会影响性能:字节的读取-修改-写入无法存储转发到双字加载,并且双字读取-修改-写入会访问所有 4 个字节。 (如果另一个线程在该线程存储旧值之前刚刚修改了其他字节之一,则正确性。)
<小时/>出于这些以及其他各种原因,NASM 汇编到输出文件中的指令的操作码是 add r/m32, imm8
还是 add r/m8 的操作码很重要,imm8
。
这是一件好事,它迫使你明确你的意思,而不是有某种默认。基于立即数的大小也会令人困惑,特别是在使用 ASCII_casebit equ 0x20
常量时。您不希望在更改常量时更改指令的操作数大小。
关于assembly - 为什么在将立即值添加到内存地址处的值时需要消除歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47445362/