我一直在通过我的Gameboy模拟器运行Blarggs CPU tests,并且 op r,r 测试显示我的ADC指令无法正常工作,但ADD是。我的理解是两者之间的唯一区别是在添加之前将现有的进位标志添加到第二个操作数。因此,我的ADC代码如下:
void Emu::add8To8Carry(BYTE &a, BYTE b) //4 cycles - 1 byte
{
if((Flags >> FLAG_CARRY) & 1)
b++;
FLAGCLEAR_N;
halfCarryAdd8_8(a, b); //generates H flag based on addition
carryAdd8_8(a, b); //generates C flag appropriately
a+=b;
if(a == 0)
FLAGSET_Z;
else
FLAGCLEAR_Z;
}
我将以下内容输入测试ROM:
06 FE 3E 01 88
当进位标志置位时,A的值为 0 (Flags = B0),而 FF (Flags = 00)则不然。就我的理解而言,这应该是如何运作的。但是,它仍然没有通过测试。
根据我的研究,我认为旗帜会以与ADD相同的方式受到影响。从字面上看,我的代码与工作ADD指令的唯一变化是在前两行中添加了标志检查/电位增量,我的测试代码似乎证明是有效的。
我错过了什么吗?也许ADD / ADC之间存在标志状态的特殊性?作为旁注,SUB指令也会通过,但SBC以同样的方式失败。
由于
答案 0 :(得分:4)
问题是b
是一个8位值。如果b
为0xff并且设置了进位,那么将{1}}加1会将其设置为0并且如果添加了b
> = 1,则不会生成进位。您会得到类似的结果如果较低的nybble是0xf,则半进位标志出现问题。
如果在设置进位时调用a
和halfCarryAdd8_8(a, b + 1);
,则可能会修复。但是,我怀疑这些例程也采用字节操作数,因此您可能必须在内部对它们进行更改。也许通过将进位添加为单独的参数,这样您就可以在carryAdd8_8(a, b + 1);
没有溢出的情况下执行tmp = a + b + carry;
。但我只能推测这些功能的来源。
在一个有点相关的说明中,有一种相当简单的方法来检查所有位的结转:
b
这是如何工作的?考虑按位" xor"如果没有进位到该位,则给出每个位的预期结果:0 ^ 0 == 0,1 ^ 0 == 0 ^ 1 == 1和1 ^ 1 == 0.通过xoring {{1}使用int sum = a + b;
int no_carry_sum = a ^ b;
int carry_into = sum ^ no_carry_sum;
int half_carry = carry_into & 0x10;
int carry = carry_info & 0x100;
,我们得到总和与逐位加法不同的位。 sum
只有在进入特定位位置时才会有所不同。因此,几乎没有开销就可以获得半进位和进位。