Page 1 of 1

The unimplementable DAA instruction

Posted: Thu Jul 12, 2012 3:16 am
by Profetylen
Hi!

I'm making a Gameboy emulator and I've been trying to get the DAA instruciton to pass Blargg's tests for quite some time now.

I've tried to copy the behavior of how gambatte (which passes the tests) handles it.

Gambattes implmentation:

Code: Select all

			case 0x27:
				calcHF(HF1, HF2);
				
				{
					unsigned correction = (CF & 0x100) ? 0x60 : 0x00;
					
					if (HF2 & 0x200)
						correction |= 0x06;
					
					if (!(HF2 &= 0x400)) {
						if ((A & 0x0F) > 0x09)
							correction |= 0x06;
						
						if (A > 0x99)
							correction |= 0x60;
						
						A += correction;
					} else
						A -= correction;
					
					CF = correction << 2 & 0x100;
					ZF = A;
					A &= 0xFF;
				}
				break;
and this is my current code:

Code: Select all

	cycleCounter += 4;
	
	int corr = 0;
	
	corr |= regAF.lo & FLAG_MASK_H? 0x06: 0x00;
	corr |= regAF.lo & FLAG_MASK_C? 0x60: 0x00;
	
	if (regAF.lo & FLAG_MASK_N)
	{
		regAF.hi -= corr;
	} else
	{
		corr |= (regAF.hi & 0x0F) > 0x09? 0x06: 0x00;
		corr |= regAF.hi > 0x99? 0x60: 0x00;
		regAF.hi += corr;
	}
	
	CLEAR_FLAGS(FLAG_MASK_H);
	if (corr & 0x60)
	{
		SET_FLAGS(FLAG_MASK_C);
	}
	if (corr & 0x06)
	{
		//SET_FLAGS(FLAG_MASK_H);
	}
	
	if (regAF.hi == 0)
	{
		SET_FLAGS(FLAG_MASK_Z);
	} else
	{
		CLEAR_FLAGS(FLAG_MASK_Z);
	}
What I find the most confusing is the behavior of the H flag after the DAA instruction. According to the documentation it says "set or reset according to operation", which doesn't really explain how it behaves. Currently I'm always clearing it, because I think that is what gambatte does.

I then started comparing it to VBA-M's DAA table and found that the Z flag doesn't always behave as I thought. For example:

Code: Select all

A = 0x00
F = 0x80 // Z flag set
execute DAA
Produces the following:

Code: Select all

output:
A = 0x00
F = 0x00
Also, sometimes (I haven't figured out when), the H flag is set.

I couldn't test whether VBA-M is accurate or not because it fails the test before the DAA test is run.

Posted: Thu Jul 12, 2012 12:46 pm
by DParrott
This is from my (C#) GB emu, originally based on code posted by Blarrg and passes his GB cpu tests.

Code: Select all

private void Daa()
{
    int a = _regs.A;

    if (!_regs.F.HasFlag(Flags.N))
    {
        if (_regs.F.HasFlag(Flags.H) || (a & 0xF) > 9)
            a += 0x06;

        if (_regs.F.HasFlag(Flags.C) || a > 0x9F)
            a += 0x60;
    }
    else
    {
        if (_regs.F.HasFlag(Flags.H))
            a = (a - 6) & 0xFF;

        if (_regs.F.HasFlag(Flags.C))
            a -= 0x60;
    }

    _regs.F &= ~(Flags.H | Flags.Z);

    if ((a & 0x100) == 0x100)
        _regs.F |= Flags.C;

    a &= 0xFF;

    if (a == 0)
        _regs.F |= Flags.Z;

    _regs.A = (byte)a;
}

Posted: Mon Jul 16, 2012 10:39 am
by Profetylen
Thank you!

I have now fed all possible values of A and F into your DAA function and compared it with my function, and they now produce the exact same result, so I guess there is some other instruction that messes this up... Time for massive debugging!NI!NI!NG

Thank you once again!! :)

Re: The unimplementable DAA instruction

Posted: Fri Sep 07, 2012 5:06 pm
by Profetylen
Now it passes the DAA test, it was due to the crc-checks didn't work because of a faulty RRCA instruction. Thank you once again for the help! :)