Game Boy:半进位标志和16位指令(尤其是操作码0xE8)

iho*_*nen 8 embedded assembly emulation gameboy

像许多其他人一样,我正在编写一个Game Boy模拟器,并且对0xE8指令(ADD SP, n带有8位立即数)有一些疑问。

据称在这里,在16位指令的半进位标志,如果从第7位发生位8的进位,而设置在这里它是说,半进位标志表示从位11进位位12 这Reddit线程对此问题似乎有点困惑,而且(我听说臭名昭著的缺陷)Game Boy CPU手册似乎也没有什么有用的说法。

我的问题如下:

  1. 半进位标志在操作码0xE8中的表现如何?
  2. 物理硬件中如何实现操作码0xE8?
  3. 正确的是,半进位出现在第7位到第8位之间,或者半进位发生在第11位到第12位之间(对于16位指令而言)?

Mic*_*ael 8

TL;DR:对于ADD SP,n,当从第 3 位到第 4 位发生进位时设置 H 标志。


我决定在真实硬件上进行测试,所以我用 GB-Z80 程序集编写了一个简单的测试 ROM 来测试以下场景:

[SP = $000F]
ADD SP,$01

[SP = $00F0]
ADD SP,$10

[SP = $0FF0]
ADD SP,$10

对于每一种情况下我寄存器的值存储FADD在内存中,我后来显示比特的每个在屏幕上的那些字节的5(H-标志)。

我在 3 个不同的模型(Gameboy Pocket、Gameboy Color 和 Gameboy Advance SP)上运行它,并在所有 3 个设备上得到以下输出:1 0 0. 因此,位 3->4 的进位会导致 H 置位,而 7->8 或 11->12 的进位则不会。


对于ADD HL,rr(where rris BC/DE/HL/SP),它似乎是一个不同的故事。根据我的测试,如果从第 11 位到第 12 位发生进位,则设置 H。

  • 作为一名前专业 GBC/DMG 开发人员,我对您能够回答这个问题感到印象深刻和震惊:)但是,是的,据我所知,位 3 到 4 也是答案,尽管我不记得曾经使用过它。 (4认同)

gek*_*kio 7

Game Boy 中使用的 SM83 CPU 内核几乎可以肯定有一个 8 位 ALU,这意味着 16 位 ALU 操作实际上是由两个 8 位操作组成的。和普通的 Z80 CPU 一样,它也有一个专用的 16 位递增/递减/加载单元,可以快速处理某些 16 位操作但不能更新标志。基本上:

  • 如果更新标志,则 16 位运算肯定会涉及 ALU,因此它实际上在幕后使用了两个 8 位 ALU 运算
  • 如果标志未更新,并且 16 位操作只是 +1 / -1 / 加载,则使用 16 位增量器单元完成

因此,每当您处理标志时,如果您想对操作进行推理,请尝试考虑 8 位操作(先是低字节,然后是高字节)。

  1. 半进位标志在操作码 0xE8 中的表现如何?

正如另一个答案中所指出的,当第 3 位有进位时设置 H。(当第 7 位有进位时设置 C)。

这是一个有趣的思考练习:如果SP=$FFFF并且你执行ADD SP, -1,你会得到SP=$FFFE 并且 H 和 C 都被设置了。你能明白为什么吗?

由于有符号数的工作方式,低字节操作在这种情况下基本上只是一个正常的加法。-1= $FF,所以它正在计算$FF+ $FF

提示以上?

  1. opcode 0xE8 是如何在物理硬件中实现的?

我们还没有在尽可能低的层次上完全了解它,但我知道有两个 8 位操作。使用我的 Game Boy testbench 系统,我已经确认首先有一个 ALU 操作更新标志(H,C)而不是 SP,然后是其他一些操作,最后 SP 被一次性自动更新。这表明ADD SP, e可能实际上在两个单独的 8 位操作中将结果计算到某个临时寄存器(例如,真正的 Z80 具有用于某些 ALU 操作的不可见 WZ 临时寄存器),然后从中加载 SP。

我认为这ADD HL, BC是一个更有趣的例子......通过我的测试平台,我已经确认它首先更新 L 然后更新 H,并且标志更新两次。这意味着它实际上执行类似

ADD L, C
ADC H, B
Run Code Online (Sandbox Code Playgroud)

后一个 8 位操作更新标志,因此我们永远不会看到ADD L, C. 但是如果 L 位 3 有进位,半进位标志可能会暂时设置!

  1. 哪个是正确的,半进位发生在第 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 单元执行)

  • 天哪,已经过去很长时间了...但我曾经研究过 z80 和 dmg CPU 的逆向工程。我会寻找实际的参考,但现在请考虑我的评论BS(或个人启示) (2认同)