Game Boy:半进位标志和16位指令(尤其是操作码0xE8) [英] Game Boy: Half-carry flag and 16-bit instructions (especially opcode 0xE8)

查看:197
本文介绍了Game Boy:半进位标志和16位指令(尤其是操作码0xE8)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

此处声明为16位指令如果从第7位到第8位发生进位,则设置半进位标志,而在这里,据说半进位标志表示从第11位到第12位的进位.在此Reddit线程在该问题上似乎有些混乱,而且(我听说有缺陷)(解决方案

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

  • 如果更新了标志,则肯定会涉及ALU的16位操作,因此实际上它在后台使用了两个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. 如何在物理硬件中实现操作码0xE8?

我们还没有对它进行最低程度的全面了解,但是我知道有两个8位操作.通过我的Game Boy测试台系统,我已经确认,首先有一个ALU操作会更新标志(H,C),而不是SP,然后再进行其他操作,最后一次一次原子地更新SP.这表明ADD SP, e实际上可能会在两个单独的8位操作中将结果计算到某个临时寄存器中(例如,实际的Z80在某些ALU操作中具有不可见的WZ临时寄存器),然后从中加载SP. >

我认为ADD HL, BC是一个更有趣的示例...在我的测试平台上,我已经确认它先更新L,然后更新H,并且 flags更新两次.这意味着它从字面上执行类似

的操作

ADD L, C
ADC H, B

后面的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单元执行)

Like so many others, I am writing a Game Boy emulator and I have a couple of questions regarding the instruction 0xE8 (ADD SP, n with an 8-bit immediate).

It is claimed here that in 16-bit instructions the half-carry flag is set if a carry occurs from bit 7 to bit 8, whereas here it is said that the half-carry flag indicates carry from bit 11 to bit 12. In this Reddit thread there seems to be a bit of confusion regarding the issue, and the (notoriously flawed, I hear) Game Boy CPU manual doesn't seem to have anything useful to say either.

My questions are the following:

  1. How does the half-carry flag behave in opcode 0xE8?
  2. How is the opcode 0xE8 implemented in the physical hardware?
  3. Which is right, that half-carry occurs from bit 7 to bit 8 or that half-carry occurs from bit 11 to bit 12 (in the case of 16-bit instructions)?

解决方案

The SM83 CPU core used in Game Boy almost certainly has a 8-bit ALU, which means 16-bit ALU operations are actually composed of two 8-bit operations. Like a normal Z80 CPU, it also has a dedicated 16-bit increment/decrement/load unit, which can handle certain 16-bit operations quickly but can't update the flags. Basically:

  • if flags are updated, a 16-bit operation definitely involves the ALU, so it actually uses two 8-bit ALU operations under the hood
  • if flags are not updated, and the 16-bit operation is just +1 / -1 / load, it's done with the 16-bit incrementer unit

So, whenever you're dealing with flags, try to think in terms of 8-bit operations (low byte first, then the high byte) if you want to reason about the operation.

  1. How does the half-carry flag behave in opcode 0xE8?

As pointed out in the other answer, H is set when there's a carry from bit 3. (And C is set when there's a carry from bit 7).

Here's an interesting thought exercise: if SP=$FFFF and you execute ADD SP, -1, you get SP=$FFFE and both H and C are set. Can you understand why?

Due to how signed numbers work, the low byte operation is in this case basically just a normal addition. -1 = $FF, so it's calculating $FF+ $FF.

Hint above ↑

  1. How is the opcode 0xE8 implemented in the physical hardware?

We don't yet have a full understanding of it at the lowest possible level, but I know that there are two 8-bit operations. With my Game Boy testbench system I've confirmed that there's first an ALU operation that updates the flags (H, C) but not SP, then some other operation, and finally SP is updated atomically in one go. This suggests that ADD SP, e might actually calculate the result into some temporary register (for example, a real Z80 has an invisible WZ temporary register for some ALU operations) in two separate 8-bit operations, and then load SP from it.

I think ADD HL, BC is a bit more interesting example...with my testbench I've confirmed that it updates L first and then H, and flags are updated twice. This means that it literally executes something like

ADD L, C
ADC H, B

The latter 8-bit operation updates the flags, so we never see the resulting flags of ADD L, C. But the half-carry flag might be temporarily set if there's a carry from L bit 3!

  1. Which is right, that half-carry occurs from bit 7 to bit 8 or that half-carry occurs from bit 11 to bit 12 (in the case of 16-bit instructions)?

It depends on the instruction, but the flags are always updated based on the same bit positions if you think in terms of 8-bit values...it just varies whether we're talking about the high or low byte of the 16-bit value. Bit 11 is just bit 3 of the high byte.

  • ADD SP, e: H from bit 3, C from bit 7 (flags from low byte op)
  • LD HL, SP+e: H from bit 3, C from bit 7 (flags from low byte op)
  • ADD HL, rr: H from bit 11, C from bit 15 (flags from high byte op)
  • INC rr: no flag updates (executed by the 16-bit inc/dec unit)
  • DEC rr: no flag updates (executed by the 16-bit inc/dec unit)

这篇关于Game Boy:半进位标志和16位指令(尤其是操作码0xE8)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆