问题描述
二进制值是否默认以 2 的补码形式存储?
我试验了代码 mov al,-1
并看到 EAX = 000000FF
这是默认的还是我们可以指定使用补码或其他格式
解决方法
x86 硬件使用 2 的补码有符号整数,例如有关 movsx
符号扩展、imul
/idiv
带符号乘法/除法和 add
等的 FLAGS 设置(特别是 OF)等指令,包括 sub
/ cmp
和分支条件,如 jle
小于等于。并且没有任何补码数学指令(除了 not
,一个补码否定与 neg
二进制补码否定,也就是二进制 0 - x
。)
另请参见 Understanding Carry vs. Overflow conditions/flags,它准确描述了 2 的补码溢出 (OF) 与进位 (CF) 对加法的作用。
在将源代码中的负数编码为机器代码时,汇编器总是1使用 2 的补码。 机器代码中的 mov
-immediate 只是将位模式复制到登记册;在 CPU 看到之前,所有“解释”都已经完成。 (mov reg,sign_extended_narrow_immediate
的唯一情况是 x86-64 mov r/m64,imm32
仅在 64 位模式下。)还要注意 mov al,-1
不影响 EAX 的高位。如果您看到 0x000000FF
,那是因为 EAX 的高位字节碰巧已经为零。
脚注 1:您当然可以编写一个非常奇怪的 x86 汇编程序并执行其他操作。不过,不太可能有人愿意使用它,因为这意味着 add eax,-2
不会将 EAX 的值减少 2。现有 主流汇编程序使用与硬件,硬件为2的补码硬连线,不可切换。
old_timer 指出一些汇编器(例如用于简单微控制器的简单汇编器)甚至可能根本不支持负常量的语法,在这种情况下,您总是必须手动将常量编码为十六进制或其他形式。诸如 0xFF
或 $FF
或任何语法之类的东西。
如果您想使用 1 的补码位模式,请手动将它们编码为十六进制。例如mov al,0FDh
(~2
) 而不是 mov al,0FEh
或 -2
。
当然,您必须使用多条指令来实现 1 的补码数学。 add
进行二元加法,与 2 的补码有符号加法相同,但不是与 1 的补码相同的运算。 (这是计算机使用 2 的补码的一个主要原因:+/- 与无符号运算相同,乘法的低半部分也是如此。)
请注意,x86 机器代码具有某些形式的指令,例如 add r/m32,sign_extended_imm8
,它在解码中涉及 2 的补码扩展。即高 24 位是第 7 位的副本,复制立即数的最高位以填充寄存器。许多 1 的补码值与此兼容,例如add eax,0FFFFFFFDh
可以编码为 imm8,汇编程序会为您完成。