在 2 的补码世界中,整数的取反可以通过取 1 的补码(所有位反转)并加 1 来获得。例如,在 8 位世界中:
A: 0x00000002 ; my number
~A: 0xFFFFFFFD ; 1's complement of my number
-A: 0xFFFFFFFE ; 2's complement of my number (negative A)
要减去A-B,当然可以加上负数A+(-B):
NOT B ; invert each bit in the 8-bit value, B
ADD B, 1 ; add 1, giving the 2's complement negated B
ADD A, B
当然,我必须先修改B(否定它)才能添加它。如果我想让B 保持原样怎么办?
PUSH B ; save B
NOT B ; invert each bit in the 8-bit value, B
INC A ; add 1, giving the 2's complement negated B
ADD A, B
POP B ; restore B
或者
NOT B ; invert each bit in the 8-bit value, B
INC A ; add 1, giving the 2's complement negated B
ADD A, B
NOT B ; restore B
这样就行了。但是有一个SUB 指令不是更容易吗?
SUB A, B
如果您正在编写汇编语言来进行大量算术运算,您更喜欢哪种方法?而且,在第一种情况下,我使用了INC A 指令。我可以在没有INC 的情况下逃脱,而只需使用ADD A, 1。但是,在许多微处理器上,ADD A, 1 要求我从指令内存中获取更多内容来执行,以便获得即时的1 值。因此,提供了INC,因为这样的操作很常见。
当微处理器设计人员确定要使用的指令集时,他们会考虑最常用的操作类型。减法很常见,所以SUB 指令非常方便。因此,它几乎存在于您能找到的任何指令集中。指令集中还有其他指令存在的原因不太明显。例如,x86 具有 XLAT 指令,以及所有“字符串”指令,LODS、STOS 等。当我可以使用 MOV 和 @987654342 完成所有这些工作时,它们为什么存在@, ETC?因为有人认为这些操作足够普遍,值得拥有一条指令。
因此,SUB 指令背后的目的,与 CPU 实现的许多其他指令一样,是提供一种更快(执行时间)和更简单的方式来执行最常在软件中执行的操作,同时兼顾以下事实:可以执行多少指令的实际限制。