iho*_*nen 8 embedded assembly emulation gameboy
像许多其他人一样,我正在编写一个Game Boy模拟器,并且对0xE8指令(ADD SP, n带有8位立即数)有一些疑问。
据称在这里,在16位指令的半进位标志,如果从第7位发生位8的进位,而设置在这里它是说,半进位标志表示从位11进位位12 这Reddit线程对此问题似乎有点困惑,而且(我听说臭名昭著的缺陷)Game Boy CPU手册似乎也没有什么有用的说法。
我的问题如下:
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
对于每一种情况下我寄存器的值存储F后ADD在内存中,我后来显示比特的每个在屏幕上的那些字节的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。
Game Boy 中使用的 SM83 CPU 内核几乎可以肯定有一个 8 位 ALU,这意味着 16 位 ALU 操作实际上是由两个 8 位操作组成的。和普通的 Z80 CPU 一样,它也有一个专用的 16 位递增/递减/加载单元,可以快速处理某些 16 位操作但不能更新标志。基本上:
因此,每当您处理标志时,如果您想对操作进行推理,请尝试考虑 8 位操作(先是低字节,然后是高字节)。
- 半进位标志在操作码 0xE8 中的表现如何?
正如另一个答案中所指出的,当第 3 位有进位时设置 H。(当第 7 位有进位时设置 C)。
这是一个有趣的思考练习:如果SP=$FFFF并且你执行ADD SP, -1,你会得到SP=$FFFE 并且 H 和 C 都被设置了。你能明白为什么吗?
由于有符号数的工作方式,低字节操作在这种情况下基本上只是一个正常的加法。
-1=$FF,所以它正在计算$FF+$FF。
提示以上?
- 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 有进位,半进位标志可能会暂时设置!
- 哪个是正确的,半进位发生在第 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 单元执行)