public void PushesPops() { cu = new ControlUnit(mmu, reg); var spStart = --reg.SP; cu.Push(0xfe1b); cu.Push(0x1f2e); Assert.That(cu.POP(), Is.EqualTo(0x1f2e)); Assert.That(cu.POP(), Is.EqualTo(0xfe1b)); }
private void Dispatch(byte op) { switch (op) { //nop; case 0: break; case 0xcb: DispatchCb(); break; //LD(nn),sp case 0x8: mmu.WriteWord(NextWord(), Registers.SP); Cycles += OpTiming.LD_SP; break; //RLCA case 0x07: Registers.A = ControlUnit.RLCA(Registers.A); Cycles += OpTiming.SHIFT; break; //RLA case 0x17: Registers.A = ControlUnit.RLA(Registers.A); Cycles += OpTiming.SHIFT; break; case 0x10: throw new Exception("STOP"); case 0x18: case 0x20: case 0x28: case 0x30: case 0x38: ControlUnit.Jr(op, (sbyte)NextByte()); break; case 0x01: case 0x11: case 0x21: case 0x31: Registers.LoadWordRegisterPair1((op >> 4), NextWord()); Cycles += OpTiming.LD_WORD; break; case 0x09: case 0x19: case 0x29: case 0x39: ControlUnit.Add16(Registers.ReadWordRegisterPair1(op >> 4)); Cycles += OpTiming.ARITHMETIC_LOAD; break; //LD (BC), A case 0x02: case 0x12: case 0x22: case 0x32: WriteByte(Registers.ReadWordRegisterPair3(op >> 4), Registers.A); Cycles += OpTiming.STORE_BYTE; break; case 0xa: case 0x1a: case 0x2a: case 0x3a: Registers.A = Registers.RegisterPair2Indirect(op >> 4); Cycles += OpTiming.STORE_BYTE; break; case 0x03: Registers.BC++; Cycles += OpTiming.INC_WORD_REG; break; case 0x13: Registers.DE++; Cycles += OpTiming.INC_WORD_REG; break; case 0x23: Registers.HL++; Cycles += OpTiming.INC_WORD_REG; break; case 0x33: Registers.SP++; Cycles += OpTiming.INC_WORD_REG; break; case 0x0b: Registers.BC--; Cycles += OpTiming.INC_WORD_REG; break; case 0x1b: Registers.DE--; Cycles += OpTiming.INC_WORD_REG; break; case 0x2b: Registers.HL--; Cycles += OpTiming.INC_WORD_REG; break; case 0x3b: Registers.SP--; Cycles += OpTiming.INC_WORD_REG; break; case 0xe0: //LDH (a8), A which is LD (0xff00+a8), a WriteByte(0xff00 + NextByte(), Registers.A); Cycles += OpTiming.LDH; break; case 0xf0: Registers.A = ReadByte(0xff00 + NextByte()); Cycles += OpTiming.LDH; break; //case var o when (o & 0xa8) == 0xa8: // ControlUnit.XOR(Registers.GetRegById(o & 0x7)); // break; case 0x0e: case 0x1e: case 0x2e: case 0x3e: Registers.SetRegById(op >> 3, NextByte()); Cycles += OpTiming.STORE_BYTE; break; case 0xe2: WriteByte(0xff00 + Registers.C, Registers.A); Cycles += OpTiming.STORE_BYTE; break; case 0xf2: Registers.A = ReadByte(0xff00 + Registers.C); Cycles += OpTiming.STORE_BYTE; break; case 0x0c: case 0x1c: case 0x2c: case 0x3c: ControlUnit.IncReg(op >> 3); Cycles += OpTiming.ARITHMETIC; break; case 0x0d: case 0x2d: case 0x3d: case 0x1d: ControlUnit.DecReg(op >> 3); Cycles += OpTiming.ARITHMETIC; break; case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x77: WriteByte(Registers.HL, Registers.GetRegById(0xf & op)); Cycles += OpTiming.STORE_BYTE; break; case 0xcd: ControlUnit.Call(NextWord()); Cycles += OpTiming.CALL; break; case 0xc9: ControlUnit.RET(); Cycles += OpTiming.RET; break; case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4f: Registers.C = Registers.GetRegById(0x7 & op); Cycles += OpTiming.LD; break; case 0x4e: Registers.C = Registers.GetRegById(0x7 & op); Cycles += OpTiming.STORE_BYTE; break; case var o when(o >= 0x58 && o <= 0x5d) || o == 0x5f: Registers.E = Registers.GetRegById(0x7 & op); Cycles += OpTiming.LD; break; case 0x5e: Registers.E = Registers.GetRegById(0x7 & op); Cycles += OpTiming.STORE_BYTE; break; case var o when(o >= 0x68 && o <= 0x6d) || o == 0x6f: Registers.L = Registers.GetRegById(0x7 & op); Cycles += OpTiming.LD; break; case 0x6e: Registers.L = Registers.GetRegById(0x7 & op); Cycles += OpTiming.STORE_BYTE; break; case var o when(o >= 0x78 && o <= 0x7d) || o == 0x7f: Registers.A = Registers.GetRegById(0x7 & op); Cycles += OpTiming.LD; break; case 0x7e: Registers.A = Registers.GetRegById(0x7 & op); Cycles += OpTiming.STORE_BYTE; break; case 0x06: case 0x16: case 0x26: Registers.SetRegById((op >> 4) << 1, NextByte()); Cycles += OpTiming.STORE_BYTE; break; case 0x36: Registers.SetRegById((op >> 4) << 1, NextByte()); Cycles += OpTiming.LD_WORD; break; case 0xc1: case 0xd1: case 0xe1: case 0xf1: Registers.LoadWordRegisterPair2((op >> 4) & 3, ControlUnit.POP()); Cycles += OpTiming.POP; break; case 0xc5: case 0xd5: case 0xe5: case 0xf5: ControlUnit.Push(Registers.ReadWordRegisterPair2((op >> 4) & 3)); Cycles += OpTiming.PUSH; break; case 0x05: case 0x15: case 0x25: case 0x35: ControlUnit.DecReg((op >> 4) << 1); break; case 0x04: case 0x14: case 0x24: ControlUnit.IncReg((op >> 4) << 1); Cycles += OpTiming.ARITHMETIC; break; case 0x34: ControlUnit.IncReg((op >> 4) << 1); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xfe: ControlUnit.CP(NextByte()); Cycles += OpTiming.ARITHMETIC_LOAD; break; case var o when o >= 0xb8 && o <= 0xbf && o != 0xbe: ControlUnit.CP(Registers.GetRegById(o & 0x7)); Cycles += OpTiming.ARITHMETIC; break; case 0xbe: ControlUnit.CP(Registers.GetRegById(op & 0x7)); Cycles += OpTiming.ARITHMETIC_LOAD; break; case var o when o >= 0xa8 && o <= 0xaf && o != 0xae: ControlUnit.XOR(Registers.GetRegById(o & 0x7)); Cycles += OpTiming.ARITHMETIC; break; case 0xae: ControlUnit.XOR(Registers.GetRegById(op & 0x7)); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xea: WriteByte(NextWord(), Registers.A); Cycles += OpTiming.LDH; break; case 0xfa: Registers.A = mmu.rb(NextWord()); Cycles += OpTiming.LDH; Cycles += OpTiming.ARITHMETIC; break; case 0x76: Cycles += OpTiming.ARITHMETIC; Halt1 = true; if (!IME && (mmu.IE & mmu.IF) > 0) { Halt1 = false; } break; case 0xfb: //Set Interrupt master enable. pendingIME = 2; Cycles += OpTiming.EI; break; case 0xf3: //Disable interrupt; IME = false; Cycles += OpTiming.EI; break; case 0xf6: ControlUnit.ORA(NextByte()); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xd9: //RETI pendingIME = 1; ControlUnit.RET(); Cycles += OpTiming.RET; break; case var o when o >= 0xb0 && o <= 0xb7 && o != 0xb6: ControlUnit.ORA(Registers.GetRegById(o & 0x7)); Cycles += OpTiming.ARITHMETIC; break; case 0xb6: ControlUnit.ORA(Registers.GetRegById(op & 0x7)); Cycles += OpTiming.ARITHMETIC_LOAD; break; //suba case var o when o >= 0x90 && o <= 0x97 && o != 0x96: ControlUnit.SUBA(0x7 & o); Cycles += OpTiming.ARITHMETIC; break; case 0x96: ControlUnit.SUBA(0x7 & op); Cycles += OpTiming.ARITHMETIC_LOAD; break; case var o when o >= 0x98 && o <= 0x9f && o != 0x9e: ControlUnit.SUBC(0x7 & o); Cycles += OpTiming.ARITHMETIC; break; case 0x9e: ControlUnit.SUBC(0x7 & op); Cycles += OpTiming.ARITHMETIC_LOAD; break; case var o when o >= 0x40 && o <= 0x7f && (o & 0x7) != 0x6 && (o & 0xf0) != 0x70: //Catch all cases where load takes 4 cycles. LoadByte(o); Cycles += OpTiming.LD; break; case var o when o >= 0x40 && o <= 0x7f: //catch the rest of the cases which it takes 8. LoadByte(o); Cycles += OpTiming.STORE_BYTE; break; case var o when o >= 0x80 && o <= 0x87 && o != 0x86: ControlUnit.ADDA(Registers.GetRegById(o & 0x7)); Cycles += OpTiming.ARITHMETIC; break; case 0x86: ControlUnit.ADDA(Registers.GetRegById(op & 0x7)); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xc6: ControlUnit.ADDA(NextByte()); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xc3: ControlUnit.JP(NextWord()); Cycles += OpTiming.JP; break; case var o when(o >= 0xa0 && o <= 0xa5) || o == 0xa7: ControlUnit.AND(Registers.GetRegById(o & 7)); Cycles += OpTiming.ARITHMETIC; break; case 0xa6: Cycles += OpTiming.ARITHMETIC_LOAD; ControlUnit.AND(Registers.GetRegById(op & 7)); break; case 0x2f: ControlUnit.CPL(); break; case 0xe6: ControlUnit.AND(NextByte()); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xcf: ControlUnit.RST(0x8); break; case 0xdf: ControlUnit.RST(0x18); break; case 0xef: ControlUnit.RST(0x28); break; case 0xff: ControlUnit.RST(0x38); break; case 0xc7: ControlUnit.RST(0); break; case 0xd7: ControlUnit.RST(0x10); break; case 0xe7: ControlUnit.RST(0x20); break; case 0xf7: ControlUnit.RST(0x30); break; case 0xe9: ControlUnit.JP(Registers.HL); Cycles += OpTiming.ARITHMETIC; break; case 0x37: Registers.Carry = true; Registers.Negative = false; Registers.HalfCarry = false; Cycles += OpTiming.ARITHMETIC; break; case 0x3f: Registers.Carry = !Registers.Carry; Registers.Negative = false; Registers.HalfCarry = false; Cycles += OpTiming.ARITHMETIC; break; case 0xd8: case 0xc8: case 0xc0: case 0xd0: ControlUnit.RETC(op); break; case 0xc4: case 0xd4: case 0xcc: case 0xdc: ControlUnit.CALLCC(op, NextWord()); break; case 0x1f: ControlUnit.RRA(); break; case 0xee: ControlUnit.XOR(NextByte()); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xd6: ControlUnit.SUB(NextByte()); break; case 0xce: ControlUnit.ADDC(NextByte()); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0xde: ControlUnit.SUBCv(NextByte()); break; case var o when(o >= 0x88 && o <= 0x8d) || o == 0x8f: ControlUnit.ADDC(Registers.GetRegById(o & 0x7)); Cycles += OpTiming.ARITHMETIC; break; case 0x8e: ControlUnit.ADDC(Registers.GetRegById(op & 0x7)); Cycles += OpTiming.ARITHMETIC_LOAD; break; case 0x27: ControlUnit.DAA(); break; case 0xf8: ControlUnit.LDSPe8((sbyte)NextByte()); break; case 0xca: case 0xda: case 0xd2: case 0xc2: ControlUnit.JPCC(op, NextWord()); break; case 0xf9: Registers.SP = Registers.HL; Cycles = OpTiming.ARITHMETIC_LOAD; break; case 0xe8: ControlUnit.ADDSP((sbyte)NextByte()); break; case 0x0f: Registers.A = ControlUnit.RRC(Registers.A); Registers.Zero = false; Cycles += OpTiming.SHIFT; break; default: throw new NotImplementedException($"The op code {op:X2} has not been implemented yet."); } }