Game Boy 中使用的 SM83 CPU 内核几乎肯定有一个 8 位 ALU,这意味着 16 位 ALU 操作实际上是由两个 8 位操作组成的。与普通的 Z80 CPU 一样,它也有一个专用的 16 位递增/递减/加载单元,可以快速处理某些 16 位操作但不能更新标志。基本上:
- 如果更新 flags,一个 16 位操作肯定会涉及到 ALU,所以它实际上使用了两个 8 位 ALU 操作
- 如果未更新标志,并且 16 位操作只是 +1 / -1 / 加载,则使用 16 位增量单元完成
因此,每当您处理标志时,如果您想对操作进行推理,请尝试从 8 位操作(首先是低字节,然后是高字节)的角度来考虑。
- 半进位标志在操作码 0xE8 中的行为如何?
正如另一个答案中所指出的,当第 3 位有进位时设置 H。(当第 7 位有进位时设置 C)。
这是一个有趣的思考练习:如果SP=$FFFF
你执行ADD SP, -1
,你会得到SP=$FFFE
H 和 C 都被设置。你能明白为什么吗?
由于有符号数字的工作方式,在这种情况下,低字节操作基本上只是一个正常的加法。-1
= $FF
,所以它在计算$FF
+ $FF
。
上面的提示↑</p>
- opcode 0xE8 是如何在物理硬件中实现的?
我们还没有在尽可能低的级别上完全理解它,但我知道有两个 8 位操作。使用我的 Game Boy 测试平台系统,我已经确认首先有一个 ALU 操作更新标志(H,C)但不更新 SP,然后是其他一些操作,最后 SP 一次原子更新。这表明ADD SP, e
实际上可能在两个单独的 8 位操作中将结果计算到某个临时寄存器中(例如,真正的 Z80 有一个不可见的 WZ 临时寄存器用于某些 ALU 操作),然后从中加载 SP。
我认为这ADD HL, BC
是一个更有趣的例子......我的测试平台已经确认它首先更新 L 然后 H,并且标志被更新两次。这意味着它实际上执行了类似
ADD L, C
ADC H, B
后一个 8 位操作会更新标志,因此我们永远不会看到ADD L, C
. 但是如果 L 位 3 有进位,半进位标志可能会被临时设置!
- 哪个是正确的,半进位是从第 7 位到第 8 位,还是半进位是从第 11 位到第 12 位(在 16 位指令的情况下)?
这取决于指令,但是如果您从 8 位值的角度考虑,标志总是基于相同的位位置进行更新......无论我们谈论的是 16 位的高字节还是低字节,它都会有所不同价值。第 11 位只是高字节的第 3 位。
ADD SP, e
: H 来自第 3 位,C 来自第 7 位(来自低字节操作的标志)
LD HL, SP+e
: H 来自第 3 位,C 来自第 7 位(来自低字节操作的标志)
ADD HL, rr
: H 来自第 11 位,C 来自第 15 位(来自高字节操作的标志)
INC rr
:无标志更新(由 16 位 inc/dec 单元执行)
DEC rr
:无标志更新(由 16 位 inc/dec 单元执行)