The unimplementable DAA instruction

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
The unimplementable DAA instruction
by on (#96734)
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:
         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:
   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:
A = 0x00
F = 0x80 // Z flag set
execute DAA


Produces the following:
Code:
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.

by on (#96751)
This is from my (C#) GB emu, originally based on code posted by Blarrg and passes his GB cpu tests.

Code:
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;
}

by on (#96848)
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
by on (#99181)
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! :)