// // Copyright (c) 2012 Ian Cowburn // using System; namespace Noddybox.Emulation.EightBit.Z80 { public partial class Z80Cpu { /// /// Decode and execute an opcode. /// /// The opcode. private void DecodeByte(byte opcode) { // Check for shifted opcodes // switch(shift) { case 0xdd: Decode(opcode, ref IX); break; case 0xfd: Decode(opcode, ref IY); break; default: Decode(opcode, ref HL); break; } } /// /// Decode and execute an opcode. /// /// The opcode. /// The register to use for HL register operations. private void Decode(byte opcode, ref Register16 hl) { ushort addr; byte b; switch(opcode) { case 0x00: // NOP clock.Add(4); break; case 0x01: // LD BC, nnnn clock.Add(10); BC.reg = FetchWord(); break; case 0x02: // LD (BC), A clock.Add(7); memory.Write(BC.reg, A); break; case 0x03: // INC BC clock.Add(6); BC.reg++; break; case 0x04: // INC B clock.Add(4); INC8(ref BC.high); break; case 0x05: // DEC B clock.Add(4); DEC8(ref BC.high); break; case 0x06: // LD B,n clock.Add(7); BC.high = memory.Read(PC++); break; case 0x07: // RLCA clock.Add(4); RLCA(); break; case 0x08: // EX AF, AF' clock.Add(4); Register16 t = AF_; AF_.high = A; AF_.low = (byte)F; A = t.high; F = (Z80Flags)t.low; break; case 0x09: // ADD HL, BC clock.Add(11); ADD16(ref hl.reg, BC.reg); break; case 0x0a: // LD A,(BC) clock.Add(7); A = memory.Read(BC.reg); break; case 0x0b: // DEC BC clock.Add(6); BC.reg--; break; case 0x0c: // INC C clock.Add(4); INC8(ref BC.low); break; case 0x0d: // DEC C clock.Add(4); DEC8(ref BC.low); break; case 0x0e: // LD C,n clock.Add(7); BC.low = memory.Read(PC++); break; case 0x0f: // RRCA clock.Add(4); RRCA(); break; case 0x10: // DJNZ if (--BC.high == 0) { clock.Add(13); JR(); } else { clock.Add(8); PC++; } break; case 0x11: // LD DE, nnnn clock.Add(10); DE.reg = FetchWord(); break; case 0x12: // LD (DE), A clock.Add(7); memory.Write(DE.reg, A); break; case 0x13: // INC DE clock.Add(6); DE.reg++; break; case 0x14: // INC D clock.Add(4); INC8(ref DE.high); break; case 0x15: // DEC D clock.Add(4); DEC8(ref DE.high); break; case 0x16: // LD D,n clock.Add(7); DE.high = memory.Read(PC++); break; case 0x17: // RLA clock.Add(4); RLA(); break; case 0x18: // JR clock.Add(12); JR(); break; case 0x19: // ADD HL, DE clock.Add(11); ADD16(ref hl.reg, DE.reg); break; case 0x1a: // LD A,(DE) clock.Add(7); A = memory.Read(DE.reg); break; case 0x1b: // DEC DE clock.Add(6); DE.reg--; break; case 0x1c: // INC E clock.Add(4); INC8(ref DE.low); break; case 0x1d: // DEC E clock.Add(4); DEC8(ref DE.low); break; case 0x1e: // LD E,n clock.Add(7); DE.low = memory.Read(PC++); break; case 0x1f: // RRA clock.Add(4); RRA(); break; case 0x20: // JR NZ, r JR_COND(Z80Flags.Zero, Z80Flags.None); break; case 0x21: // LD HL, nnnn clock.Add(10); hl.reg = FetchWord(); break; case 0x22: // LD (nnnn), HL clock.Add(16); addr = FetchWord(); memory.Write(addr, hl.low); memory.Write((ushort)(addr + 1), hl.high); break; case 0x23: // INC HL clock.Add(6); hl.reg++; break; case 0x24: // INC H clock.Add(4); INC8(ref hl.high); break; case 0x25: // DEC H clock.Add(4); DEC8(ref hl.high); break; case 0x26: // LD H,n clock.Add(7); hl.high = memory.Read(PC++); break; case 0x27: // DAA clock.Add(4); DAA(); break; case 0x28: // JR Z,d JR_COND(Z80Flags.Zero, Z80Flags.Zero); break; case 0x29: // ADD HL, HL clock.Add(11); ADD16(ref hl.reg, hl.reg); break; case 0x2a: // LD HL,(nnnnn) clock.Add(7); addr = FetchWord(); hl.low = memory.Read(addr); hl.high = memory.Read((ushort)(addr + 1)); break; case 0x2b: // DEC HL clock.Add(6); hl.reg--; break; case 0x2c: // INC L clock.Add(4); INC8(ref hl.low); break; case 0x2d: // DEC L clock.Add(4); DEC8(ref hl.low); break; case 0x2e: // LD L,n clock.Add(7); hl.low = memory.Read(PC++); break; case 0x2f: // CPL clock.Add(4); A ^= 0xff; ClearFlag(Z80Flags.Hidden); SetFlag(Z80Flags.HalfCarry | Z80Flags.Neg | H35table[A]); break; case 0x30: // JR NC, r JR_COND(Z80Flags.Carry, Z80Flags.None); break; case 0x31: // LD SP, nnnn clock.Add(10); SP = FetchWord(); break; case 0x32: // LD (nnnn), A clock.Add(13); addr = FetchWord(); memory.Write(addr, A); break; case 0x33: // INC SP clock.Add(6); SP++; break; case 0x34: // INC (HL) clock.Add(11); addr = (ushort)(hl.reg + Offset()); b = memory.Read(addr); INC8(ref b); memory.Write(addr, b); break; case 0x35: // DEC (HL) clock.Add(11); addr = (ushort)(hl.reg + Offset()); b = memory.Read(addr); DEC8(ref b); memory.Write(addr, b); break; case 0x36: // LD (HL),n clock.Add(10); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, memory.Read(PC++)); break; case 0x37: // SCF clock.Add(4); F = (F & (Z80Flags.Sign | Z80Flags.Zero | Z80Flags.PV)) | Z80Flags.Carry | H35table[A]; break; case 0x38: // JR C,d JR_COND(Z80Flags.Carry, Z80Flags.Carry); break; case 0x39: // ADD HL, SP clock.Add(11); ADD16(ref hl.reg, SP); break; case 0x3a: // LD A,(nnnnn) clock.Add(13); addr = FetchWord(); A = memory.Read(addr); break; case 0x3b: // DEC SP clock.Add(6); SP--; break; case 0x3c: // INC A clock.Add(4); INC8(ref A); break; case 0x3d: // DEC A clock.Add(4); DEC8(ref A); break; case 0x3e: // LD A,n clock.Add(7); A = memory.Read(PC++); break; case 0x3f: // CCF clock.Add(4); if ((F & Z80Flags.Carry) == Z80Flags.Carry) { SetFlag(Z80Flags.HalfCarry); } else { ClearFlag(Z80Flags.HalfCarry); } F ^= Z80Flags.Carry; ClearFlag(Z80Flags.Hidden); SetFlag(H35table[A]); break; case 0x40: // LD B,B clock.Add(4); break; case 0x41: // LD B,C clock.Add(4); BC.high = BC.low; break; case 0x42: // LD B,D clock.Add(4); BC.high = DE.high; break; case 0x43: // LD B,E clock.Add(4); BC.high = DE.low; break; case 0x44: // LD B,H clock.Add(4); BC.high = hl.high; break; case 0x45: // LD B,L clock.Add(4); BC.high = hl.low; break; case 0x46: // LD B,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); BC.high = memory.Read(addr); break; case 0x47: // LD B,A clock.Add(4); BC.high = A; break; case 0x48: // LD C,B clock.Add(4); BC.low = BC.high; break; case 0x49: // LD C,C clock.Add(4); break; case 0x4a: // LD C,D clock.Add(4); BC.low = DE.high; break; case 0x4b: // LD C,E clock.Add(4); BC.low = DE.low; break; case 0x4c: // LD C,H clock.Add(4); BC.low = hl.high; break; case 0x4d: // LD C,L clock.Add(4); BC.low = hl.low; break; case 0x4e: // LD C,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); BC.low = memory.Read(addr); break; case 0x4f: // LD C,A clock.Add(4); BC.low = A; break; case 0x50: // LD D,B clock.Add(4); DE.high = BC.high; break; case 0x51: // LD D,C clock.Add(4); DE.high = BC.low; break; case 0x52: // LD D,D clock.Add(4); break; case 0x53: // LD D,E clock.Add(4); DE.high = DE.low; break; case 0x54: // LD D,H clock.Add(4); DE.high = hl.high; break; case 0x55: // LD D,L clock.Add(4); DE.high = hl.low; break; case 0x56: // LD D,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); DE.high = memory.Read(addr); break; case 0x57: // LD D,A clock.Add(4); DE.high = A; break; case 0x58: // LD E,B clock.Add(4); DE.low = BC.high; break; case 0x59: // LD E,C clock.Add(4); DE.low = BC.low; break; case 0x5a: // LD E,D clock.Add(4); DE.low = DE.high; break; case 0x5b: // LD E,E clock.Add(4); break; case 0x5c: // LD E,H clock.Add(4); DE.low = hl.high; break; case 0x5d: // LD E,L clock.Add(4); DE.low = hl.low; break; case 0x5e: // LD E,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); DE.low = memory.Read(addr); break; case 0x5f: // LD E,A clock.Add(4); DE.low = A; break; case 0x60: // LD H,B clock.Add(4); hl.high = BC.high; break; case 0x61: // LD H,C clock.Add(4); hl.high = BC.low; break; case 0x62: // LD H,D clock.Add(4); hl.high = DE.high; break; case 0x63: // LD H,E clock.Add(4); hl.high = DE.low; break; case 0x64: // LD H,H clock.Add(4); break; case 0x65: // LD H,L clock.Add(4); hl.high = hl.low; break; case 0x66: // LD H,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); HL.high = memory.Read(addr); break; case 0x67: // LD H,A clock.Add(4); hl.high = A; break; case 0x68: // LD L,B clock.Add(4); hl.low = BC.high; break; case 0x69: // LD L,C clock.Add(4); hl.low = BC.low; break; case 0x6a: // LD L,D clock.Add(4); hl.low = DE.high; break; case 0x6b: // LD L,E clock.Add(4); hl.low = DE.low; break; case 0x6c: // LD L,H clock.Add(4); hl.low = hl.high; break; case 0x6d: // LD L,L clock.Add(4); break; case 0x6e: // LD L,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); HL.low = memory.Read(addr); break; case 0x6f: // LD L,A clock.Add(4); hl.low = A; break; case 0x70: // LD (HL),B clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, BC.high); break; case 0x71: // LD (HL),C clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, BC.low); break; case 0x72: // LD (HL),D clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, DE.high); break; case 0x73: // LD (HL),E clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, DE.low); break; case 0x74: // LD (HL),H clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, HL.high); break; case 0x75: // LD (HL),L clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, HL.low); break; case 0x76: // HALT clock.Add(4); PC--; HALT = true; break; case 0x77: // LD (HL),A clock.Add(7); addr = (ushort)(hl.reg + Offset()); memory.Write(addr, A); break; case 0x78: // LD A,B clock.Add(4); A = BC.high; break; case 0x79: // LD A,C clock.Add(4); A = BC.low; break; case 0x7a: // LD A,D clock.Add(4); A = DE.high; break; case 0x7b: // LD A,E clock.Add(4); A = DE.low; break; case 0x7c: // LD A,H clock.Add(4); A = hl.high; break; case 0x7d: // LD A,L clock.Add(4); A = hl.low; break; case 0x7e: // LD A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); HL.low = memory.Read(addr); break; case 0x7f: // LD A,A clock.Add(4); break; case 0x80: // ADD A,B clock.Add(4); ADD8(BC.high); break; case 0x81: // ADD A,C clock.Add(4); ADD8(BC.low); break; case 0x82: // ADD A,D clock.Add(4); ADD8(DE.high); break; case 0x83: // ADD A,E clock.Add(4); ADD8(DE.low); break; case 0x84: // ADD A,H clock.Add(4); ADD8(hl.high); break; case 0x85: // ADD A,L clock.Add(4); ADD8(hl.low); break; case 0x86: // ADD A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); ADD8(memory.Read(addr)); break; case 0x87: // ADD A,A clock.Add(4); ADD8(A); break; case 0x88: // ADC A,B clock.Add(4); ADC8(BC.high); break; case 0x89: // ADC A,C clock.Add(4); ADC8(BC.low); break; case 0x8a: // ADC A,D clock.Add(4); ADC8(DE.high); break; case 0x8b: // ADC A,E clock.Add(4); ADC8(DE.low); break; case 0x8c: // ADC A,H clock.Add(4); ADC8(hl.high); break; case 0x8d: // ADC A,L clock.Add(4); ADC8(hl.low); break; case 0x8e: // ADC A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); ADC8(memory.Read(addr)); break; case 0x8f: // ADC A,A clock.Add(4); ADC8(A); break; case 0x90: // SUB A,B clock.Add(4); SUB8(BC.high); break; case 0x91: // SUB A,C clock.Add(4); SUB8(BC.low); break; case 0x92: // SUB A,D clock.Add(4); SUB8(DE.high); break; case 0x93: // SUB A,E clock.Add(4); SUB8(DE.low); break; case 0x94: // SUB A,H clock.Add(4); SUB8(hl.high); break; case 0x95: // SUB A,L clock.Add(4); SUB8(hl.low); break; case 0x96: // SUB A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); SUB8(memory.Read(addr)); break; case 0x97: // SUB A,A clock.Add(4); SUB8(A); break; case 0x98: // SBC A,B clock.Add(4); SBC8(BC.high); break; case 0x99: // SBC A,C clock.Add(4); SBC8(BC.low); break; case 0x9a: // SBC A,D clock.Add(4); SBC8(DE.high); break; case 0x9b: // SBC A,E clock.Add(4); SBC8(DE.low); break; case 0x9c: // SBC A,H clock.Add(4); SBC8(hl.high); break; case 0x9d: // SBC A,L clock.Add(4); SBC8(hl.low); break; case 0x9e: // SBC A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); SBC8(memory.Read(addr)); break; case 0x9f: // SBC A,A clock.Add(4); SBC8(A); break; case 0xa0: // AND A,B clock.Add(4); AND(BC.high); break; case 0xa1: // AND A,C clock.Add(4); AND(BC.low); break; case 0xa2: // AND A,D clock.Add(4); AND(DE.high); break; case 0xa3: // AND A,E clock.Add(4); AND(DE.low); break; case 0xa4: // AND A,H clock.Add(4); AND(hl.high); break; case 0xa5: // AND A,L clock.Add(4); AND(hl.low); break; case 0xa6: // AND A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); AND(memory.Read(addr)); break; case 0xa7: // AND A,A clock.Add(4); AND(A); break; case 0xa8: // XOR A,B clock.Add(4); XOR(BC.high); break; case 0xa9: // XOR A,C clock.Add(4); XOR(BC.low); break; case 0xaa: // XOR A,D clock.Add(4); XOR(DE.high); break; case 0xab: // XOR A,E clock.Add(4); XOR(DE.low); break; case 0xac: // XOR A,H clock.Add(4); XOR(hl.high); break; case 0xad: // XOR A,L clock.Add(4); XOR(hl.low); break; case 0xae: // XOR A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); XOR(memory.Read(addr)); break; case 0xaf: // XOR A,A clock.Add(4); XOR(A); break; case 0xb0: // OR A,B clock.Add(4); OR(BC.high); break; case 0xb1: // OR A,C clock.Add(4); OR(BC.low); break; case 0xb2: // OR A,D clock.Add(4); OR(DE.high); break; case 0xb3: // OR A,E clock.Add(4); OR(DE.low); break; case 0xb4: // OR A,H clock.Add(4); OR(hl.high); break; case 0xb5: // OR A,L clock.Add(4); OR(hl.low); break; case 0xb6: // OR A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); OR(memory.Read(addr)); break; case 0xb7: // OR A,A clock.Add(4); OR(A); break; case 0xb8: // CP A,B clock.Add(4); CP(BC.high); break; case 0xb9: // CP A,C clock.Add(4); CP(BC.low); break; case 0xba: // CP A,D clock.Add(4); CP(DE.high); break; case 0xbb: // CP A,E clock.Add(4); CP(DE.low); break; case 0xbc: // CP A,H clock.Add(4); CP(hl.high); break; case 0xbd: // CP A,L clock.Add(4); CP(hl.low); break; case 0xbe: // CP A,(HL) clock.Add(7); addr = (ushort)(hl.reg + Offset()); CP(memory.Read(addr)); break; case 0xbf: // CP A,A clock.Add(4); CP(A); break; case 0xc0: // RET NZ RET_COND(Z80Flags.Zero, Z80Flags.None); break; case 0xc1: // POP BC clock.Add(10); BC.reg = POP(); break; case 0xc2: // JP NZ,nnnn JP_COND(Z80Flags.Zero, Z80Flags.None); break; case 0xc3: // JP nnnn clock.Add(10); JP(); break; case 0xc4: // CALL NZ,nnnn CALL_COND(Z80Flags.Zero, Z80Flags.None); break; case 0xc5: // PUSH BC clock.Add(10); PUSH(BC.reg); break; case 0xc6: // ADD A,n clock.Add(7); ADD8(memory.Read(PC++)); break; case 0xc7: // RST 0 RST(0); break; case 0xc8: // RET Z RET_COND(Z80Flags.Zero, Z80Flags.Zero); break; case 0xc9: // RET clock.Add(10); SP = POP(); break; case 0xca: // JP Z,nnnn JP_COND(Z80Flags.Zero, Z80Flags.Zero); break; case 0xcb: // CB shift AddR(1); // Check for IX/IY shift // if (shift != 0) { if (shift == 0xdd) { addr = (ushort)(IX.reg + Offset()); } else { addr = (ushort)(IY.reg + Offset()); } DecodeShiftedCB(memory.Read(PC++), addr); } else { DecodeCB(memory.Read(PC++)); } break; case 0xcc: // CALL Z,nnnn CALL_COND(Z80Flags.Zero, Z80Flags.Zero); break; case 0xcd: // CALL nnnn clock.Add(17); CALL(); break; case 0xce: // ADC A,n clock.Add(7); ADC8(memory.Read(PC++)); break; case 0xcf: // RST 8 RST(8); break; case 0xd0: // RET NC RET_COND(Z80Flags.Carry, Z80Flags.None); break; case 0xd1: // POP DE clock.Add(10); DE.reg = POP(); break; case 0xd2: // JP NC,nnnn JP_COND(Z80Flags.Carry, Z80Flags.None); break; case 0xd3: // OUT(n),A clock.Add(11); addr = memory.Read(PC++); addr |= (ushort)(A << 8); device.Write(addr, A); break; case 0xd4: // CALL NC,nnnn CALL_COND(Z80Flags.Carry, Z80Flags.None); break; case 0xd5: // PUSH DE clock.Add(10); PUSH(DE.reg); break; case 0xd6: // SUB A,n clock.Add(7); SUB8(memory.Read(PC++)); break; case 0xd7: // RST 10 RST(0x10); break; case 0xd8: // RET C RET_COND(Z80Flags.Carry, Z80Flags.Carry); break; case 0xd9: // EXX clock.Add(4); Swap(ref BC, ref BC_); Swap(ref DE, ref DE_); Swap(ref HL, ref HL_); break; case 0xda: // JP C,nnnn JP_COND(Z80Flags.Carry, Z80Flags.Carry); break; case 0xdb: // IN A,(n) clock.Add(11); addr = memory.Read(PC++); addr |= (ushort)(A << 8); A = device.Read(addr); break; case 0xdc: // CALL C,nnnn CALL_COND(Z80Flags.Carry, Z80Flags.Carry); break; case 0xdd: // DD (IX) PREFIX AddR(1); clock.Add(4); shift = opcode; DecodeByte(memory.Read(PC++)); break; case 0xde: // SBC A,n clock.Add(7); SBC8(memory.Read(PC++)); break; case 0xdf: // RST 18 RST(0x18); break; case 0xe0: // RET PO RET_COND(Z80Flags.PV, Z80Flags.None); break; case 0xe1: // POP HL clock.Add(10); hl.reg = POP(); break; case 0xe2: // JP PO,nnnn JP_COND(Z80Flags.PV, Z80Flags.None); break; case 0xe3: // EX (SP),HL clock.Add(19); addr = POP(); PUSH(hl.reg); hl.reg = addr; break; case 0xe4: // CALL PO,nnnn CALL_COND(Z80Flags.PV, Z80Flags.None); break; case 0xe5: // PUSH HL clock.Add(10); PUSH(hl.reg); break; case 0xe6: // AND A,n clock.Add(7); AND(memory.Read(PC++)); break; case 0xe7: // RST 20 RST(0x20); break; case 0xe8: // RET PE RET_COND(Z80Flags.PV, Z80Flags.PV); break; case 0xe9: // JP (HL) clock.Add(4); PC = hl.reg; break; case 0xea: // JP PE,nnnn JP_COND(Z80Flags.PV, Z80Flags.PV); break; case 0xeb: // EX DE,HL clock.Add(4); Swap(ref DE, ref hl); break; case 0xec: // CALL PE,nnnn CALL_COND(Z80Flags.PV, Z80Flags.PV); break; case 0xed: // ED PREFIX AddR(1); DecodeED(memory.Read(PC++)); break; case 0xee: // XOR A,n clock.Add(7); XOR(memory.Read(PC++)); break; case 0xef: // RST 28 RST(0x28); break; case 0xf0: // RET P RET_COND(Z80Flags.Sign, Z80Flags.None); break; case 0xf1: // POP AF clock.Add(10); addr = POP(); A = (byte)(addr >> 8); F = (Z80Flags)(addr & 0xff); break; case 0xf2: // JP P,nnnn JP_COND(Z80Flags.Sign, Z80Flags.None); break; case 0xf3: // DI clock.Add(4); IFF1 = false; IFF2 = false; break; case 0xf4: // CALL P,nnnn CALL_COND(Z80Flags.Sign, Z80Flags.None); break; case 0xf5: // PUSH AF clock.Add(10); PUSH((ushort)((A << 8) | (int)(F))); break; case 0xf6: // OR A,n clock.Add(7); OR(memory.Read(PC++)); break; case 0xf7: // RST 30 RST(0x30); break; case 0xf8: // RET M RET_COND(Z80Flags.Sign, Z80Flags.Sign); break; case 0xf9: // LD SP,HL clock.Add(6); SP = hl.reg; break; case 0xfa: // JP N,nnnn JP_COND(Z80Flags.Sign, Z80Flags.Sign); break; case 0xfb: // EI clock.Add(4); IFF1 = true; IFF2 = true; break; case 0xfc: // CALL M,nnnn CALL_COND(Z80Flags.Sign, Z80Flags.Sign); break; case 0xfd: // DD (IY) PREFIX AddR(1); clock.Add(4); shift = opcode; DecodeByte(memory.Read(PC++)); break; case 0xfe: // CP A,n clock.Add(7); CP(memory.Read(PC++)); break; case 0xff: // RST 38 RST(0x18); break; default: break; } } } }