Emulating GameBoy的0xCB 0x19指令

huangapple go评论57阅读模式
英文:

Emulating GameBoy's 0xCB 0x19 Instruction

问题

我正在实现0xCB 0x19指令。使用此测试ROM:https://github.com/retrio/gb-test-roms/blob/master/cpu_instrs/individual/06-ld%20r%2Cr.gb 进行比较。但是遇到了困难,因为根据日志,指令0xCB 0x19会将寄存器C中的值从0x47更改为0x23,而我的实现将其更改为0xA3。

这是我的实现:

    pub fn rr_reg8(&mut self, register_name: RegisterName, register_byte: RegisterByteName) -> u64 {
        // 实现代码
    }

这是否不是旋转进位应该的工作方式?还是日志中存在问题?

英文:

I am writing a gameboy emulator and I am currently implementing 0xCB 0x19 instruction.

I am using this test rom: <https://github.com/retrio/gb-test-roms/blob/master/cpu_instrs/individual/06-ld%20r%2Cr.gb> and comparing my logs against <https://github.com/wheremyfoodat/Gameboy-logs/blob/master/Blargg6LYStubbed.zip> and I am stuck at as according to those logs the instruction 0xCB 0x19 will change the value in register C from 0x47 to 0x23 while my implementation changes it to 0xA3.

Here is my implementations:

    pub fn rr_reg8(&amp;mut self, register_name: RegisterName, register_byte: RegisterByteName) -&gt; u64 {
        self.clear_flag(Flag::HalfCarry);
        self.clear_flag(Flag::Subtraction);

        let shifted_bit = utils::data::check_bit(self.registers[register_name][register_byte], 0);
        if shifted_bit {
            self.set_flag(Flag::Carry);
        } else {
            self.clear_flag(Flag::Carry);
        }

        self.registers[register_name][register_byte] &gt;&gt;= 1;

        if self.registers[register_name][register_byte] == 0 {
            self.set_flag(Flag::Zero);
        } else {
            self.clear_flag(Flag::Zero);
        }

        if self.check_flag(Flag::Carry) {
            utils::data::set_bit(&amp;mut self.registers[register_name][register_byte], 7);
        }

        8
    }

Is this not how rotate through carry should work? Or is there an issue with the logs?

答案1

得分: 1

I couldn't easily find official documentation for the LR35902, but it's mostly Z80 compatible, so I'll assume that they implement this instruction the same.

rr c 是对由 C 寄存器和进位标志组成的 9 位值的旋转。因此,旋转到寄存器 C 的第 7 位的值应该是进位标志的 值。在这里,您将其设置为进位标志的 值,而这是寄存器 C 位 0 的旧值。这对于 rrc c,操作码 0xcb 0x09 是正确的行为,但对于 rr c 不正确。

(rr/rrc 的命名约定与 x86 的相反,其中 rcr al 将是通过进位旋转的 9 位,而 ror al 是 AL 单独的 8 位旋转。参见 Retrocomputing.SE 上的 为什么 Intel 8080 的旋转指令与直觉相反? ,提出了 8080/Z80 rrc 中的 c 可能代表 "circular",而不是 "carry" 的假设。)

如果 C = 0x47 并且进位标志未设置,则 rr c 应导致 C = 0x23 并设置进位标志。rrc c 将导致 C = 0xA3 并设置进位标志(在这种情况下,进位标志的原始值将不会使用)。

因此,您将需要将 Flag::Carry 的原始值存储在临时变量中,并在最后将其分配给寄存器 C 的第 7 位。

另一个错误是您应该根据寄存器 C 的 最终 结果来设置零标志。因此,例如,如果 C = 0x01 并且进位标志已设置,则结果应为 C = 0x80,进位标志设置,零标志清除。您的代码会错误地在这种情况下保留零标志,因为您在设置高位之前测试了 C 的值。

另外,您似乎遗漏了根据寄存器 C 的高位的最终值来设置符号标志的部分。(这意味着新的符号标志将始终等于进位标志的旧值。)您还必须正确设置奇偶标志。除非您的代码的其他地方已经执行了这些标志的设置,或者 LR35902 在这些标志方面有遗漏?

英文:

I couldn't easily find official documentation for the LR35902, but it's mostly Z80 compatible, so I'll assume that they implement this instruction the same.

rr c is effectively a rotation of the 9-bit value formed by the C register together with the carry flag. So the value rotated in to bit 7 of register C should be the old value of the carry flag. Here you are setting it to the new value of the carry flag, which was the old value of register C bit 0. This would be correct behavior for rrc c, opcode 0xcb 0x09, but not for rr c.

(The naming convention of rr/rrc is the opposite of the x86 convention, where rcr al would be the 9-bit rotate through carry, and ror al is the 8-bit rotate of AL alone. See Why are the Intel 8080's rotate instructions called opposite to intuition? on Retrocomputing.SE which puts forward the hypothesis that the c in 8080/Z80 rrc stands for "circular", not "carry".)

If C = 0x47 and the carry flag is clear, then rr c should result in C = 0x23 and the carry flag set. rrc c would result in C = 0xA3 and carry flag set (in that case the original value of the carry flag would be unused).

So you'll want to store the original value of Flag::Carry in a temporary variable, and assign it to bit 7 of register C at the end.

Another bug is that you should set the zero flag based on the final result in register C. So for instance, if C = 0x01 and the carry flag is set, the result should be C = 0x80, carry flag set, and zero flag clear. Your code would improperly leave the zero flag set in this case, since you tested the value of C before setting the high bit.

Also, you seem to have omitted to set the sign flag based on the final value of the high bit of C. (This means the new value of the sign flag will always equal the old value of the carry flag.) You have to set the parity flag properly as well. Unless that's done somewhere else in your code, or the LR35902 left out these flags?

huangapple
  • 本文由 发表于 2023年5月14日 23:35:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76248309.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定