代码之家  ›  专栏  ›  技术社区  ›  Hals

Z80 DAA实现和Blargg的测试rom问题

  •  2
  • Hals  · 技术社区  · 7 年前

    为了提高对硬件操作和操作系统主题的理解,我决定编写一个简单的Gameboy模拟器。仅仅几天就完成了核心功能的编程,我测试了模拟器,结果发现屏幕上什么也没画出来。在我的仿真器中逐个遍历几百个操作码,并将其与BGB仿真器中的值进行比较后,我意识到问题中的瓷砖和精灵正在加载到内存中,而不是绘制。从这一点上,我认为问题一定出在我的一个或多个操作码实现中,这会导致程序在某些时候表现出错误的行为。因此,我决定使用Blargg的cpu测试ROM( http://gbdev.gg8.se/files/roms/blargg-gb-tests/ )帮我解决这个问题。但是,运行第一个测试rom时,会出现以下错误消息:

    01-special
    
    36E1FE30 
    DAA
    
    Failed #6
    

    03-op sp,hl
    
    03-op sp,hl
    
    03-op sp,hl
    
    03-op sp,hl
    

    https://github.com/qkmaxware/GBemu/blob/master/src/gameboy/cpu/Opcodes.java )或如下所示:

    Op DAA = new Op(0x27, "DAA", map, () -> {
        int a = reg.a();
    
        if(!reg.subtract()){
            if(reg.halfcarry() || (a & 0xF) > 9)
                a += 0x06;
    
            if(reg.carry() || a > 0x9F)
                a += 0x60;
        }else{
            if(reg.halfcarry())
                a = (a - 0x6) & 0xFF;
    
            if(reg.carry())
                a = (a - 0x60) & 0xFF;
        }
    
        reg.a(a);
    
        reg.zero(isZero(a));
        reg.carry((a & 0x100) == 0x100);
        reg.halfcarry(false);
    
        clock.m(1);
        clock.t(4);
    });
    

    其中,reg.a()等调用表示从寄存器a读取,reg.a(值)表示写入寄存器a(掩码为8位或16位,具体取决于寄存器)。类似地,可以使用“reg”对象的零、减法、半进位和进位函数获取或设置/重置标志Z、N、H、C。

    因此,我的问题有三个:我是否错误地实现了DAA操作,使其无法通过Blargg的测试?有人知道我的错误代码是什么意思吗?或者有人知道我如何集中搜索错误的操作吗。

    1 回复  |  直到 7 年前
        1
  •  5
  •   George Phillips    7 年前

    看起来Blargg的测试借鉴了一个名为zexlax的旧Z-80测试程序,该程序采用了将指令测试视为简单数据比较的实用方法。对于 DAA 它运行所有可能的输入组合,并有效地对照预期答案进行检查。但是保留所有答案会使测试代码变得不切实际。相反,它比较了数据的CRC。正如您所经历的那样,这在验证模拟器的正确操作时非常有效,但在指出如何修复它时却毫无用处。

    虽然如果有人保存了正确的输出,以便您可以对照您的实现进行检查,这将是理想的,但您仍然可以通过在已知良好的模拟器上运行测试来做到这一点。或者只是将您的实现与熟悉的模拟器进行比较。

    case 0x27: /*      DAA */
        {
            int tmp = m_A;
    
            if ( ! ( m_F & FLAG_N ) ) {
                if ( ( m_F & FLAG_H ) || ( tmp & 0x0F ) > 9 )
                    tmp += 6;
                if ( ( m_F & FLAG_C ) || tmp > 0x9F )
                    tmp += 0x60;
            } else {
                if ( m_F & FLAG_H ) {
                    tmp -= 6;
                    if ( ! ( m_F & FLAG_C ) )
                        tmp &= 0xFF;
                }
                if ( m_F & FLAG_C )
                        tmp -= 0x60;
            }
            m_F &= ~ ( FLAG_H | FLAG_Z );
            if ( tmp & 0x100 )
                m_F |= FLAG_C;
            m_A = tmp & 0xFF;
            if ( ! m_A )
                m_F |= FLAG_Z;
        }
        break;
    

    欲了解更多信息,请链接至整个源代码:

    https://github.com/mamedev/mame/blob/master/src/devices/cpu/lr35902/opc_main.hxx#L354

    看起来你的代码可能有些不同,但我还没有仔细研究。

    我注意到Blargg的测试包括未记录的标志位3和5。如果这是一个Z-80处理器,它将使没有像Z-80那样设置这些位的模拟器失败,这实际上是可以预测的,只是没有作为任何可以依赖的文件记录。我不知道夏普LR35902是否有类似的问题,但如果是这样的话,MAME完全有可能没有实现这一点。对于一个“真正的”程序来说,这些比特永远不可能有什么不同。