Okay, implemented every instruction on the Z80. Have a few more questions, of course.
RETI, RETN
I understand that RETI is a specialized version of RET that somehow signals to external hardware that it occurred. For the sake of the Master System and/or Mega Drive, do I need to do any kind of special handling for this, or can I just treat it like RET?
And on that note, RETN is just RET + IFF1=IFF2, right?
ED 77, ED 7F
Are these instructions NOP (as per z80-documented.pdf), or LD I,I and LD R,R (as per
http://www.z80.info/z80oplist.txt) ? This matters as there's (presumably) an extra cycle penalty on the latter.
LD (I,A;A,I;A,R;R,A)
The manual states the T-cycles for these are T(4,5). I really would have expected this one to be T(4,4).
Is there really an extra cycle here?
Also, the manual says that "LD A,I"; "LD A,R"; set flags. I could understand that adding an extra cycle penalty. But "LD I,A"; "LD R,A" do not set flags and still (supposedly) have the extra cycle penalty?
DAA
These algorithms are always brutally difficult to get correct. I remember VBA using a lookup table for the Game Boy DAA instruction,
and still getting the wrong results! (the table itself had bad values in it.)
Wasn't able to use my LR35902 implementation (known to be correct per blargg), because it's missing a lot of the flag values and said CPU doesn't have the N flag that affects the computation.
So my implementation was based off
CZ80 ... is this algorithm known to be correct or incorrect?
Specifically, this is what I came up with in adapting said code:
Code: Select all
auto Z80::instructionDAA() -> void {
uint8 lo = A.bits(0,3);
uint8 hi = A.bits(4,7);
uint8 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;
}
if(NF == 0) A += diff;
if(NF == 1) A -= diff;
CF = CF || (lo <= 9 ? hi >= 10 : hi >= 9);
PF = parity(A);
XF = A.bit(3);
HF = NF ? (HF && lo <= 5) : (lo >= 10);
YF = A.bit(5);
ZF = A == 0;
SF = A.bit(7);
}
If that works, it's a very clever way to implement it. But it's highly unusual looking to me.
16-bit arithmetic
I'd rather be a bit lazy and reuse the existing 8-bit ADD/SUB functions if possible.
99% sure this is fine, but just to be certain, it's okay if I implement these like so, correct?
Code: Select all
auto Z80::instructionADC_hl_rr(uint16& x) -> void {
wait(4);
auto lo = ADD(HL >> 0, x >> 0, CF);
wait(3);
auto hi = ADD(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
ZF = HL == 0;
}
auto Z80::instructionADD_hl_rr(uint16& x) -> void {
wait(4);
auto lo = ADD(HL >> 0, x >> 0);
wait(3);
auto hi = ADD(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
ZF = HL == 0;
}
auto Z80::instructionSBC_hl_rr(uint16& x) -> void {
wait(4);
auto lo = SUB(HL >> 0, x >> 0, CF);
wait(3);
auto hi = SUB(HL >> 8, x >> 8, CF);
HL = hi << 8 | lo << 0;
ZF = HL == 0;
}
RLD / RRD
It was easy enough to see what these were doing via CZ80, but ... what the hell are these useful for? >_>
Testing
Last question, are there any good stress-testing modules for a Z80 core that don't require a working Master System VDP core? If I have to go my usual route (compare my trace logs to Mednafen's), I can do that, but I'd rather save time on the inevitable weeks of debugging a new CPU core.