Z80 DAA

Por hap

Paragon (2040)

Imagen del hap

12-02-2007, 14:36

I'm currently creating a Z80 CPU emulator, using several technical documents, including this: http://wikiti.denglend.net/index.php?title=Z80_Instruction_Set

I noticed blueMSX and openMSX fill a big 4KB lookuptable with an unnecessarily complex calculation, like this (from openMSX):

for (int x = 0; x < 0x800; ++x) {
	bool nf = x & 0x400;
	bool hf = x & 0x200;
	bool cf = x & 0x100;
	byte a = x & 0xFF;
	byte hi = a / 16;
	byte lo = a & 15;
	byte diff;
	if (cf) {
		diff = ((lo <= 9) && !hf) ? 0x60 : 0x66;
	} else {
		if (lo >= 10) {
			diff = (hi <= 8) ? 0x06 : 0x66;
		} else {
			if (hi >= 10) {
				diff = hf ? 0x66 : 0x60;
			} else {
				diff = hf ? 0x06 : 0x00;
			}
		}
	}
	byte res_a = nf ? a - diff : a + diff;
	byte res_f = ZSPXYTable[res_a] | (nf ? N_FLAG : 0);
	if (cf || ((lo <= 9) ? (hi >= 10) : (hi >= 9))) {
		res_f |= C_FLAG;
	}
	if (nf ? (hf && (lo <= 5)) : (lo >= 10)) {
		res_f |= H_FLAG;
	}
	DAATable[x] = (res_a << 8) + res_f;
}

and then handle DAA like this:

template <class T> void CPUCore<T>::daa()
{
	int i = R.getA();
	if (R.getF() & C_FLAG) i |= 0x100;
	if (R.getF() & H_FLAG) i |= 0x200;
	if (R.getF() & N_FLAG) i |= 0x400;
	R.AF = DAATable[i];
}


a direct translation from the link above gives exactly the same result, without the need of a DAA table:

u8 r=A;
if NF {
	if (HF||(A&0xf)>9) r-=6;
	if (CF||A>0x99) r-=0x60;
}
else {
	if (HF||(A&0xf)>9) r+=6;
	if (CF||A>0x99) r+=0x60;
}
F=(F&3)|lut_flags[r]|(A>0x99)|((A^r)&0x10);
A=r;

I don't know which one's faster, but if you're going to stick with the DAA table, at least get rid of that big, hard-to-understand calculation :P

Login sesión o register para postear comentarios

Por Kwik

Expert (127)

Imagen del Kwik

12-02-2007, 15:09

I've seen very different implementations on DAA, but none as short as this one. Have you verified it with ZEXALL? Are X-flag and Y flag ("undocumented" ) supported?

Por hap

Paragon (2040)

Imagen del hap

12-02-2007, 15:25

I've verified it by comparing all possible results with that 4KB table, and verified blueMSX DAA opcode by comparing it with my MSX2 (small program: push value, pop af, daa, push af, pop value and write to ram). The X/Y flags are handled by that 256byte lut_flags, which is basically the same as openMSX ZSPXYTable.

My Z80 emulator is far from complete, so it'd be nice if someone double checked this shorter implementation in their own ZEXALL compliant Z80 emulation core.

Por Kwik

Expert (127)

Imagen del Kwik

12-02-2007, 15:34

If the code is correct then it's very cool. I spend several hours on DAA and came up with this (which is a lot bigger):

        reg1 = reg_a;
        if (reg_f & NFLAG) {
            reg2 = reg_f & (CFLAG|NFLAG|HFLAG);
            if ((reg_f&CFLAG)||(reg_a>0x99)) reg1 -= 0x160;
            if ((reg_f&HFLAG)||((reg_a&0x0F)>9)) {
                if ((reg_a&0x0F)>5) reg2 &= ~HFLAG;
                reg1 = (reg1&0xFF00)|((reg1-6)&0xFF);
            }
        } else {
            reg2 = (reg_f&CFLAG) | (((reg_a&0x0F)>9)?HFLAG:0);
            if ((reg2|reg_f)&HFLAG) reg1 += 6;
            if ((reg_f&CFLAG) || ((reg1&0x1F0)>0x90)) reg1 += 0x60;
        }
        reg_a = reg1&255;
	reg_f = flagSZP[reg_a] | reg2 | ((reg1>>8)&CFLAG);

It does pass ZEXALL by the way.

Por FluBBa

Supporter (3)

Imagen del FluBBa

22-05-2007, 23:16

Speaking of which, does anyone know where to find ZEXALL for the MSX?

Por Kwik

Expert (127)

Imagen del Kwik

23-05-2007, 09:39

Por FluBBa

Supporter (3)

Imagen del FluBBa

23-05-2007, 12:37

So which one should I download for the MSX? The YAZE, CPM or Spectrum?

Por NYYRIKKI

Enlighted (5918)

Imagen del NYYRIKKI

23-05-2007, 14:27

CPM

MSX-DOS is CP/M compatible.

Por FluBBa

Supporter (3)

Imagen del FluBBa

23-05-2007, 21:40

Oh, wow. Thanks a lot.
I guess I have some more coding to do then Wink