From d088e204769fe87dade63a474edee7ac16dd8d05 Mon Sep 17 00:00:00 2001 From: henridd Date: Sat, 4 Nov 2023 10:28:25 +0100 Subject: [PATCH] Implement saving and loading of states --- ProjectDMG/DMG/CPU.cs | 1309 +++++++++-------- ProjectDMG/DMG/GamePak/IGamePak.cs | 7 +- ProjectDMG/DMG/GamePak/MBC0.cs | 14 +- ProjectDMG/DMG/GamePak/MBC1.cs | 13 +- ProjectDMG/DMG/GamePak/MBC2.cs | 12 +- ProjectDMG/DMG/GamePak/MBC3.cs | 48 +- ProjectDMG/DMG/GamePak/MBC5.cs | 12 +- ProjectDMG/DMG/MMU.cs | 195 +-- ProjectDMG/DMG/PPU.cs | 27 +- .../DMG/State/DataStructures/CPUSavedState.cs | 39 + .../GamePak/GamePakSavedState.cs | 10 + .../DataStructures/GamePak/MBC3SavedState.cs | 33 + .../DMG/State/DataStructures/MMUSavedState.cs | 37 + .../DMG/State/DataStructures/PPUSavedState.cs | 14 + .../DataStructures/ProjectDMGSavedState.cs | 12 + .../DMG/State/DataStructures/SavedState.cs | 27 + .../State/DataStructures/TimerSavedState.cs | 13 + ProjectDMG/DMG/State/SaveStateManager.cs | 49 + ProjectDMG/DMG/State/SaveStateSerializer.cs | 24 + ProjectDMG/DMG/TIMER.cs | 45 +- ProjectDMG/GUI/Form.cs | 56 +- ProjectDMG/PluginLoader.cs | 41 + ProjectDMG/ProjectDMG.cs | 141 +- ProjectDMG/ProjectDMG.csproj | 1 + ProjectDMG/Utils/DirectBitmap.cs | 7 +- README.md | 2 + 26 files changed, 1418 insertions(+), 770 deletions(-) create mode 100644 ProjectDMG/DMG/State/DataStructures/CPUSavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/GamePak/GamePakSavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/GamePak/MBC3SavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/MMUSavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/PPUSavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/ProjectDMGSavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/SavedState.cs create mode 100644 ProjectDMG/DMG/State/DataStructures/TimerSavedState.cs create mode 100644 ProjectDMG/DMG/State/SaveStateManager.cs create mode 100644 ProjectDMG/DMG/State/SaveStateSerializer.cs create mode 100644 ProjectDMG/PluginLoader.cs diff --git a/ProjectDMG/DMG/CPU.cs b/ProjectDMG/DMG/CPU.cs index 16d4ebb..00c7f52 100644 --- a/ProjectDMG/DMG/CPU.cs +++ b/ProjectDMG/DMG/CPU.cs @@ -1,9 +1,12 @@ -using System; +using ProjectDMG.DMG.State.DataStructures; +using System; using static ProjectDMG.Utils.BitOps; -namespace ProjectDMG { +namespace ProjectDMG +{ - class CPU { // Sharp LR35902 CPU + class CPU + { // Sharp LR35902 CPU private MMU mmu; private ushort PC; @@ -27,34 +30,66 @@ class CPU { // Sharp LR35902 CPU private bool HALT_BUG; private int cycles; - public CPU(MMU mmu) { + internal CPU(MMU mmu) : this(mmu, null) { } + + internal CPU(MMU mmu, CPUSavedState savedState) + { + if (savedState != null) + { + SetValuesFromState(savedState); + } + else + { + AF = 0x01B0; + BC = 0x0013; + DE = 0x00D8; + HL = 0x014d; + SP = 0xFFFE; + PC = 0x100; + } + this.mmu = mmu; - AF = 0x01B0; - BC = 0x0013; - DE = 0x00D8; - HL = 0x014d; - SP = 0xFFFE; - PC = 0x100; } - public int Exe() { + private void SetValuesFromState(CPUSavedState savedState) + { + A = savedState.A; + B = savedState.B; + C = savedState.C; + D = savedState.D; + E = savedState.E; + F = savedState.F; + H = savedState.H; + L = savedState.L; + IME = savedState.IME; + PC = savedState.PC; + SP = savedState.SP; + IMEEnabler = savedState.IMEEnabler; + HALTED = savedState.HALTED; + HALT_BUG = savedState.HALT_BUG; + cycles = savedState.cycles; + } + public int Exe() + { byte opcode = mmu.readByte(PC++); - if (HALT_BUG) { + if (HALT_BUG) + { PC--; HALT_BUG = false; } //debug(opcode); cycles = 0; - switch (opcode) { - case 0x00: break; //NOP 1 4 ---- - case 0x01: BC = mmu.readWord(PC); PC += 2; break; //LD BC,D16 3 12 ---- - case 0x02: mmu.writeByte(BC, A); break; //LD (BC),A 1 8 ---- - case 0x03: BC += 1; break; //INC BC 1 8 ---- - case 0x04: B = INC(B); break; //INC B 1 4 Z0H- - case 0x05: B = DEC(B); break; //DEC B 1 4 Z1H- - case 0x06: B = mmu.readByte(PC); PC += 1; break; //LD B,D8 2 8 ---- + switch (opcode) + { + case 0x00: break; //NOP 1 4 ---- + case 0x01: BC = mmu.readWord(PC); PC += 2; break; //LD BC,D16 3 12 ---- + case 0x02: mmu.writeByte(BC, A); break; //LD (BC),A 1 8 ---- + case 0x03: BC += 1; break; //INC BC 1 8 ---- + case 0x04: B = INC(B); break; //INC B 1 4 Z0H- + case 0x05: B = DEC(B); break; //DEC B 1 4 Z1H- + case 0x06: B = mmu.readByte(PC); PC += 1; break; //LD B,D8 2 8 ---- case 0x07: //RLCA 1 4 000C F = 0; @@ -63,12 +98,12 @@ class CPU { // Sharp LR35902 CPU break; case 0x08: mmu.writeWord(mmu.readWord(PC), SP); PC += 2; break; //LD (A16),SP 3 20 ---- - case 0x09: DAD(BC); break; //ADD HL,BC 1 8 -0HC - case 0x0A: A = mmu.readByte(BC); break; //LD A,(BC) 1 8 ---- - case 0x0B: BC -= 1; break; //DEC BC 1 8 ---- - case 0x0C: C = INC(C); break; //INC C 1 8 Z0H- - case 0x0D: C = DEC(C); break; //DEC C 1 8 Z1H- - case 0x0E: C = mmu.readByte(PC); PC += 1; break; //LD C,D8 2 8 ---- + case 0x09: DAD(BC); break; //ADD HL,BC 1 8 -0HC + case 0x0A: A = mmu.readByte(BC); break; //LD A,(BC) 1 8 ---- + case 0x0B: BC -= 1; break; //DEC BC 1 8 ---- + case 0x0C: C = INC(C); break; //INC C 1 8 Z0H- + case 0x0D: C = DEC(C); break; //DEC C 1 8 Z1H- + case 0x0E: C = mmu.readByte(PC); PC += 1; break; //LD C,D8 2 8 ---- case 0x0F: //RRCA 1 4 000C F = 0; @@ -76,13 +111,13 @@ class CPU { // Sharp LR35902 CPU A = (byte)((A >> 1) | (A << 7)); break; - case 0x10: STOP(); break; //STOP 2 4 ---- - case 0x11: DE = mmu.readWord(PC); PC += 2; break; //LD DE,D16 3 12 ---- - case 0x12: mmu.writeByte(DE, A); break; //LD (DE),A 1 8 ---- - case 0x13: DE += 1; break; //INC DE 1 8 ---- - case 0x14: D = INC(D); break; //INC D 1 8 Z0H- - case 0x15: D = DEC(D); break; //DEC D 1 8 Z1H- - case 0x16: D = mmu.readByte(PC); PC += 1; break; //LD D,D8 2 8 ---- + case 0x10: STOP(); break; //STOP 2 4 ---- + case 0x11: DE = mmu.readWord(PC); PC += 2; break; //LD DE,D16 3 12 ---- + case 0x12: mmu.writeByte(DE, A); break; //LD (DE),A 1 8 ---- + case 0x13: DE += 1; break; //INC DE 1 8 ---- + case 0x14: D = INC(D); break; //INC D 1 8 Z0H- + case 0x15: D = DEC(D); break; //DEC D 1 8 Z1H- + case 0x16: D = mmu.readByte(PC); PC += 1; break; //LD D,D8 2 8 ---- case 0x17://RLA 1 4 000C bool prevC = FlagC; @@ -91,13 +126,13 @@ class CPU { // Sharp LR35902 CPU A = (byte)((A << 1) | (prevC ? 1 : 0)); break; - case 0x18: JR(true); break; //JR R8 2 12 ---- - case 0x19: DAD(DE); break; //ADD HL,DE 1 8 -0HC - case 0x1A: A = mmu.readByte(DE); break; //LD A,(DE) 1 8 ---- - case 0x1B: DE -= 1; break; //INC DE 1 8 ---- - case 0x1C: E = INC(E); break; //INC E 1 8 Z0H- - case 0x1D: E = DEC(E); break; //DEC E 1 8 Z1H- - case 0x1E: E = mmu.readByte(PC); PC += 1; break; //LD E,D8 2 8 ---- + case 0x18: JR(true); break; //JR R8 2 12 ---- + case 0x19: DAD(DE); break; //ADD HL,DE 1 8 -0HC + case 0x1A: A = mmu.readByte(DE); break; //LD A,(DE) 1 8 ---- + case 0x1B: DE -= 1; break; //INC DE 1 8 ---- + case 0x1C: E = INC(E); break; //INC E 1 8 Z0H- + case 0x1D: E = DEC(E); break; //DEC E 1 8 Z1H- + case 0x1E: E = mmu.readByte(PC); PC += 1; break; //LD E,D8 2 8 ---- case 0x1F://RRA 1 4 000C bool preC = FlagC; @@ -106,19 +141,22 @@ class CPU { // Sharp LR35902 CPU A = (byte)((A >> 1) | (preC ? 0x80 : 0)); break; - case 0x20: JR(!FlagZ); break; //JR NZ R8 2 12/8 ---- - case 0x21: HL = mmu.readWord(PC); PC += 2; break; //LD HL,D16 3 12 ---- - case 0x22: mmu.writeByte(HL++, A); break; //LD (HL+),A 1 8 ---- - case 0x23: HL += 1; break; //INC HL 1 8 ---- - case 0x24: H = INC(H); break; //INC H 1 8 Z0H- - case 0x25: H = DEC(H); break; //DEC H 1 8 Z1H- - case 0x26: H = mmu.readByte(PC); PC += 1; ; break; //LD H,D8 2 8 ---- + case 0x20: JR(!FlagZ); break; //JR NZ R8 2 12/8 ---- + case 0x21: HL = mmu.readWord(PC); PC += 2; break; //LD HL,D16 3 12 ---- + case 0x22: mmu.writeByte(HL++, A); break; //LD (HL+),A 1 8 ---- + case 0x23: HL += 1; break; //INC HL 1 8 ---- + case 0x24: H = INC(H); break; //INC H 1 8 Z0H- + case 0x25: H = DEC(H); break; //DEC H 1 8 Z1H- + case 0x26: H = mmu.readByte(PC); PC += 1; ; break; //LD H,D8 2 8 ---- case 0x27: //DAA 1 4 Z-0C - if (FlagN) { // sub + if (FlagN) + { // sub if (FlagC) { A -= 0x60; } if (FlagH) { A -= 0x6; } - } else { // add + } + else + { // add if (FlagC || (A > 0x99)) { A += 0x60; FlagC = true; } if (FlagH || (A & 0xF) > 0x9) { A += 0x6; } } @@ -126,565 +164,571 @@ class CPU { // Sharp LR35902 CPU FlagH = false; break; - case 0x28: JR(FlagZ); break; //JR Z R8 2 12/8 ---- - case 0x29: DAD(HL); break; //ADD HL,HL 1 8 -0HC - case 0x2A: A = mmu.readByte(HL++); break; //LD A (HL+) 1 8 ---- - case 0x2B: HL -= 1; break; //DEC HL 1 4 ---- - case 0x2C: L = INC(L); break; //INC L 1 4 Z0H- - case 0x2D: L = DEC(L); break; //DEC L 1 4 Z1H- - case 0x2E: L = mmu.readByte(PC); PC += 1; ; break; //LD L,D8 2 8 ---- - case 0x2F: A = (byte)~A; FlagN = true; FlagH = true; break; //CPL 1 4 -11- - - case 0x30: JR(!FlagC); break; //JR NC R8 2 12/8 ---- - case 0x31: SP = mmu.readWord(PC); PC += 2; ; break; //LD SP,D16 3 12 ---- - case 0x32: mmu.writeByte(HL--, A); break; //LD (HL-),A 1 8 ---- - case 0x33: SP += 1; break; //INC SP 1 8 ---- - case 0x34: mmu.writeByte(HL, INC(mmu.readByte(HL))); break; //INC (HL) 1 12 Z0H- - case 0x35: mmu.writeByte(HL, DEC(mmu.readByte(HL))); break; //DEC (HL) 1 12 Z1H- - case 0x36: mmu.writeByte(HL, mmu.readByte(PC)); PC += 1; break; //LD (HL),D8 2 12 ---- - case 0x37: FlagC = true; FlagN = false; FlagH = false; break; //SCF 1 4 -001 - - case 0x38: JR(FlagC); break; //JR C R8 2 12/8 ---- - case 0x39: DAD(SP); break; //ADD HL,SP 1 8 -0HC - case 0x3A: A = mmu.readByte(HL--); break; //LD A (HL-) 1 8 ---- - case 0x3B: SP -= 1; break; //DEC SP 1 8 ---- - case 0x3C: A = INC(A); break; //INC A 1 4 Z0H- - case 0x3D: A = DEC(A); break; //DEC (HL) 1 4 Z1H- - case 0x3E: A = mmu.readByte(PC); PC += 1; break; //LD A,D8 2 8 ---- - case 0x3F: FlagC = !FlagC; FlagN = false; FlagH = false; break; //CCF 1 4 -00C + case 0x28: JR(FlagZ); break; //JR Z R8 2 12/8 ---- + case 0x29: DAD(HL); break; //ADD HL,HL 1 8 -0HC + case 0x2A: A = mmu.readByte(HL++); break; //LD A (HL+) 1 8 ---- + case 0x2B: HL -= 1; break; //DEC HL 1 4 ---- + case 0x2C: L = INC(L); break; //INC L 1 4 Z0H- + case 0x2D: L = DEC(L); break; //DEC L 1 4 Z1H- + case 0x2E: L = mmu.readByte(PC); PC += 1; ; break; //LD L,D8 2 8 ---- + case 0x2F: A = (byte)~A; FlagN = true; FlagH = true; break; //CPL 1 4 -11- + + case 0x30: JR(!FlagC); break; //JR NC R8 2 12/8 ---- + case 0x31: SP = mmu.readWord(PC); PC += 2; ; break; //LD SP,D16 3 12 ---- + case 0x32: mmu.writeByte(HL--, A); break; //LD (HL-),A 1 8 ---- + case 0x33: SP += 1; break; //INC SP 1 8 ---- + case 0x34: mmu.writeByte(HL, INC(mmu.readByte(HL))); break; //INC (HL) 1 12 Z0H- + case 0x35: mmu.writeByte(HL, DEC(mmu.readByte(HL))); break; //DEC (HL) 1 12 Z1H- + case 0x36: mmu.writeByte(HL, mmu.readByte(PC)); PC += 1; break; //LD (HL),D8 2 12 ---- + case 0x37: FlagC = true; FlagN = false; FlagH = false; break; //SCF 1 4 -001 + + case 0x38: JR(FlagC); break; //JR C R8 2 12/8 ---- + case 0x39: DAD(SP); break; //ADD HL,SP 1 8 -0HC + case 0x3A: A = mmu.readByte(HL--); break; //LD A (HL-) 1 8 ---- + case 0x3B: SP -= 1; break; //DEC SP 1 8 ---- + case 0x3C: A = INC(A); break; //INC A 1 4 Z0H- + case 0x3D: A = DEC(A); break; //DEC (HL) 1 4 Z1H- + case 0x3E: A = mmu.readByte(PC); PC += 1; break; //LD A,D8 2 8 ---- + case 0x3F: FlagC = !FlagC; FlagN = false; FlagH = false; break; //CCF 1 4 -00C case 0x40: /*B = B;*/ break; //LD B,B 1 4 ---- - case 0x41: B = C; break; //LD B,C 1 4 ---- - case 0x42: B = D; break; //LD B,D 1 4 ---- - case 0x43: B = E; break; //LD B,E 1 4 ---- - case 0x44: B = H; break; //LD B,H 1 4 ---- - case 0x45: B = L; break; //LD B,L 1 4 ---- - case 0x46: B = mmu.readByte(HL); break; //LD B,(HL) 1 8 ---- - case 0x47: B = A; break; //LD B,A 1 4 ---- - - case 0x48: C = B; break; //LD C,B 1 4 ---- + case 0x41: B = C; break; //LD B,C 1 4 ---- + case 0x42: B = D; break; //LD B,D 1 4 ---- + case 0x43: B = E; break; //LD B,E 1 4 ---- + case 0x44: B = H; break; //LD B,H 1 4 ---- + case 0x45: B = L; break; //LD B,L 1 4 ---- + case 0x46: B = mmu.readByte(HL); break; //LD B,(HL) 1 8 ---- + case 0x47: B = A; break; //LD B,A 1 4 ---- + + case 0x48: C = B; break; //LD C,B 1 4 ---- case 0x49: /*C = C;*/ break; //LD C,C 1 4 ---- - case 0x4A: C = D; break; //LD C,D 1 4 ---- - case 0x4B: C = E; break; //LD C,E 1 4 ---- - case 0x4C: C = H; break; //LD C,H 1 4 ---- - case 0x4D: C = L; break; //LD C,L 1 4 ---- - case 0x4E: C = mmu.readByte(HL); break; //LD C,(HL) 1 8 ---- - case 0x4F: C = A; break; //LD C,A 1 4 ---- - - case 0x50: D = B; break; //LD D,B 1 4 ---- - case 0x51: D = C; break; //LD D,C 1 4 ---- + case 0x4A: C = D; break; //LD C,D 1 4 ---- + case 0x4B: C = E; break; //LD C,E 1 4 ---- + case 0x4C: C = H; break; //LD C,H 1 4 ---- + case 0x4D: C = L; break; //LD C,L 1 4 ---- + case 0x4E: C = mmu.readByte(HL); break; //LD C,(HL) 1 8 ---- + case 0x4F: C = A; break; //LD C,A 1 4 ---- + + case 0x50: D = B; break; //LD D,B 1 4 ---- + case 0x51: D = C; break; //LD D,C 1 4 ---- case 0x52: /*D = D;*/ break; //LD D,D 1 4 ---- - case 0x53: D = E; break; //LD D,E 1 4 ---- - case 0x54: D = H; break; //LD D,H 1 4 ---- - case 0x55: D = L; break; //LD D,L 1 4 ---- - case 0x56: D = mmu.readByte(HL); break; //LD D,(HL) 1 8 ---- - case 0x57: D = A; break; //LD D,A 1 4 ---- - - case 0x58: E = B; break; //LD E,B 1 4 ---- - case 0x59: E = C; break; //LD E,C 1 4 ---- - case 0x5A: E = D; break; //LD E,D 1 4 ---- + case 0x53: D = E; break; //LD D,E 1 4 ---- + case 0x54: D = H; break; //LD D,H 1 4 ---- + case 0x55: D = L; break; //LD D,L 1 4 ---- + case 0x56: D = mmu.readByte(HL); break; //LD D,(HL) 1 8 ---- + case 0x57: D = A; break; //LD D,A 1 4 ---- + + case 0x58: E = B; break; //LD E,B 1 4 ---- + case 0x59: E = C; break; //LD E,C 1 4 ---- + case 0x5A: E = D; break; //LD E,D 1 4 ---- case 0x5B: /*E = E;*/ break; //LD E,E 1 4 ---- - case 0x5C: E = H; break; //LD E,H 1 4 ---- - case 0x5D: E = L; break; //LD E,L 1 4 ---- - case 0x5E: E = mmu.readByte(HL); break; //LD E,(HL) 1 8 ---- - case 0x5F: E = A; break; //LD E,A 1 4 ---- - - case 0x60: H = B; break; //LD H,B 1 4 ---- - case 0x61: H = C; break; //LD H,C 1 4 ---- - case 0x62: H = D; break; //LD H,D 1 4 ---- - case 0x63: H = E; break; //LD H,E 1 4 ---- + case 0x5C: E = H; break; //LD E,H 1 4 ---- + case 0x5D: E = L; break; //LD E,L 1 4 ---- + case 0x5E: E = mmu.readByte(HL); break; //LD E,(HL) 1 8 ---- + case 0x5F: E = A; break; //LD E,A 1 4 ---- + + case 0x60: H = B; break; //LD H,B 1 4 ---- + case 0x61: H = C; break; //LD H,C 1 4 ---- + case 0x62: H = D; break; //LD H,D 1 4 ---- + case 0x63: H = E; break; //LD H,E 1 4 ---- case 0x64: /*H = H;*/ break; //LD H,H 1 4 ---- - case 0x65: H = L; break; //LD H,L 1 4 ---- - case 0x66: H = mmu.readByte(HL); break; //LD H,(HL) 1 8 ---- - case 0x67: H = A; break; //LD H,A 1 4 ---- - - case 0x68: L = B; break; //LD L,B 1 4 ---- - case 0x69: L = C; break; //LD L,C 1 4 ---- - case 0x6A: L = D; break; //LD L,D 1 4 ---- - case 0x6B: L = E; break; //LD L,E 1 4 ---- - case 0x6C: L = H; break; //LD L,H 1 4 ---- + case 0x65: H = L; break; //LD H,L 1 4 ---- + case 0x66: H = mmu.readByte(HL); break; //LD H,(HL) 1 8 ---- + case 0x67: H = A; break; //LD H,A 1 4 ---- + + case 0x68: L = B; break; //LD L,B 1 4 ---- + case 0x69: L = C; break; //LD L,C 1 4 ---- + case 0x6A: L = D; break; //LD L,D 1 4 ---- + case 0x6B: L = E; break; //LD L,E 1 4 ---- + case 0x6C: L = H; break; //LD L,H 1 4 ---- case 0x6D: /*L = L;*/ break; //LD L,L 1 4 ---- - case 0x6E: L = mmu.readByte(HL); break; //LD L,(HL) 1 8 ---- - case 0x6F: L = A; break; //LD L,A 1 4 ---- - - case 0x70: mmu.writeByte(HL, B); break; //LD (HL),B 1 8 ---- - case 0x71: mmu.writeByte(HL, C); break; //LD (HL),C 1 8 ---- - case 0x72: mmu.writeByte(HL, D); break; //LD (HL),D 1 8 ---- - case 0x73: mmu.writeByte(HL, E); break; //LD (HL),E 1 8 ---- - case 0x74: mmu.writeByte(HL, H); break; //LD (HL),H 1 8 ---- - case 0x75: mmu.writeByte(HL, L); break; //LD (HL),L 1 8 ---- - case 0x76: HALT(); break; //HLT 1 4 ---- - case 0x77: mmu.writeByte(HL, A); break; //LD (HL),A 1 8 ---- - - case 0x78: A = B; break; //LD A,B 1 4 ---- - case 0x79: A = C; break; //LD A,C 1 4 ---- - case 0x7A: A = D; break; //LD A,D 1 4 ---- - case 0x7B: A = E; break; //LD A,E 1 4 ---- - case 0x7C: A = H; break; //LD A,H 1 4 ---- - case 0x7D: A = L; break; //LD A,L 1 4 ---- - case 0x7E: A = mmu.readByte(HL); break; //LD A,(HL) 1 8 ---- + case 0x6E: L = mmu.readByte(HL); break; //LD L,(HL) 1 8 ---- + case 0x6F: L = A; break; //LD L,A 1 4 ---- + + case 0x70: mmu.writeByte(HL, B); break; //LD (HL),B 1 8 ---- + case 0x71: mmu.writeByte(HL, C); break; //LD (HL),C 1 8 ---- + case 0x72: mmu.writeByte(HL, D); break; //LD (HL),D 1 8 ---- + case 0x73: mmu.writeByte(HL, E); break; //LD (HL),E 1 8 ---- + case 0x74: mmu.writeByte(HL, H); break; //LD (HL),H 1 8 ---- + case 0x75: mmu.writeByte(HL, L); break; //LD (HL),L 1 8 ---- + case 0x76: HALT(); break; //HLT 1 4 ---- + case 0x77: mmu.writeByte(HL, A); break; //LD (HL),A 1 8 ---- + + case 0x78: A = B; break; //LD A,B 1 4 ---- + case 0x79: A = C; break; //LD A,C 1 4 ---- + case 0x7A: A = D; break; //LD A,D 1 4 ---- + case 0x7B: A = E; break; //LD A,E 1 4 ---- + case 0x7C: A = H; break; //LD A,H 1 4 ---- + case 0x7D: A = L; break; //LD A,L 1 4 ---- + case 0x7E: A = mmu.readByte(HL); break; //LD A,(HL) 1 8 ---- case 0x7F: /*A = A;*/ break; //LD A,A 1 4 ---- - case 0x80: ADD(B); break; //ADD B 1 4 Z0HC - case 0x81: ADD(C); break; //ADD C 1 4 Z0HC - case 0x82: ADD(D); break; //ADD D 1 4 Z0HC - case 0x83: ADD(E); break; //ADD E 1 4 Z0HC - case 0x84: ADD(H); break; //ADD H 1 4 Z0HC - case 0x85: ADD(L); break; //ADD L 1 4 Z0HC + case 0x80: ADD(B); break; //ADD B 1 4 Z0HC + case 0x81: ADD(C); break; //ADD C 1 4 Z0HC + case 0x82: ADD(D); break; //ADD D 1 4 Z0HC + case 0x83: ADD(E); break; //ADD E 1 4 Z0HC + case 0x84: ADD(H); break; //ADD H 1 4 Z0HC + case 0x85: ADD(L); break; //ADD L 1 4 Z0HC case 0x86: ADD(mmu.readByte(HL)); break; //ADD M 1 8 Z0HC - case 0x87: ADD(A); break; //ADD A 1 4 Z0HC - - case 0x88: ADC(B); break; //ADC B 1 4 Z0HC - case 0x89: ADC(C); break; //ADC C 1 4 Z0HC - case 0x8A: ADC(D); break; //ADC D 1 4 Z0HC - case 0x8B: ADC(E); break; //ADC E 1 4 Z0HC - case 0x8C: ADC(H); break; //ADC H 1 4 Z0HC - case 0x8D: ADC(L); break; //ADC L 1 4 Z0HC + case 0x87: ADD(A); break; //ADD A 1 4 Z0HC + + case 0x88: ADC(B); break; //ADC B 1 4 Z0HC + case 0x89: ADC(C); break; //ADC C 1 4 Z0HC + case 0x8A: ADC(D); break; //ADC D 1 4 Z0HC + case 0x8B: ADC(E); break; //ADC E 1 4 Z0HC + case 0x8C: ADC(H); break; //ADC H 1 4 Z0HC + case 0x8D: ADC(L); break; //ADC L 1 4 Z0HC case 0x8E: ADC(mmu.readByte(HL)); break; //ADC M 1 8 Z0HC - case 0x8F: ADC(A); break; //ADC A 1 4 Z0HC - - case 0x90: SUB(B); break; //SUB B 1 4 Z1HC - case 0x91: SUB(C); break; //SUB C 1 4 Z1HC - case 0x92: SUB(D); break; //SUB D 1 4 Z1HC - case 0x93: SUB(E); break; //SUB E 1 4 Z1HC - case 0x94: SUB(H); break; //SUB H 1 4 Z1HC - case 0x95: SUB(L); break; //SUB L 1 4 Z1HC + case 0x8F: ADC(A); break; //ADC A 1 4 Z0HC + + case 0x90: SUB(B); break; //SUB B 1 4 Z1HC + case 0x91: SUB(C); break; //SUB C 1 4 Z1HC + case 0x92: SUB(D); break; //SUB D 1 4 Z1HC + case 0x93: SUB(E); break; //SUB E 1 4 Z1HC + case 0x94: SUB(H); break; //SUB H 1 4 Z1HC + case 0x95: SUB(L); break; //SUB L 1 4 Z1HC case 0x96: SUB(mmu.readByte(HL)); break; //SUB M 1 8 Z1HC - case 0x97: SUB(A); break; //SUB A 1 4 Z1HC - - case 0x98: SBC(B); break; //SBC B 1 4 Z1HC - case 0x99: SBC(C); break; //SBC C 1 4 Z1HC - case 0x9A: SBC(D); break; //SBC D 1 4 Z1HC - case 0x9B: SBC(E); break; //SBC E 1 4 Z1HC - case 0x9C: SBC(H); break; //SBC H 1 4 Z1HC - case 0x9D: SBC(L); break; //SBC L 1 4 Z1HC + case 0x97: SUB(A); break; //SUB A 1 4 Z1HC + + case 0x98: SBC(B); break; //SBC B 1 4 Z1HC + case 0x99: SBC(C); break; //SBC C 1 4 Z1HC + case 0x9A: SBC(D); break; //SBC D 1 4 Z1HC + case 0x9B: SBC(E); break; //SBC E 1 4 Z1HC + case 0x9C: SBC(H); break; //SBC H 1 4 Z1HC + case 0x9D: SBC(L); break; //SBC L 1 4 Z1HC case 0x9E: SBC(mmu.readByte(HL)); break; //SBC M 1 8 Z1HC - case 0x9F: SBC(A); break; //SBC A 1 4 Z1HC - - case 0xA0: AND(B); break; //AND B 1 4 Z010 - case 0xA1: AND(C); break; //AND C 1 4 Z010 - case 0xA2: AND(D); break; //AND D 1 4 Z010 - case 0xA3: AND(E); break; //AND E 1 4 Z010 - case 0xA4: AND(H); break; //AND H 1 4 Z010 - case 0xA5: AND(L); break; //AND L 1 4 Z010 + case 0x9F: SBC(A); break; //SBC A 1 4 Z1HC + + case 0xA0: AND(B); break; //AND B 1 4 Z010 + case 0xA1: AND(C); break; //AND C 1 4 Z010 + case 0xA2: AND(D); break; //AND D 1 4 Z010 + case 0xA3: AND(E); break; //AND E 1 4 Z010 + case 0xA4: AND(H); break; //AND H 1 4 Z010 + case 0xA5: AND(L); break; //AND L 1 4 Z010 case 0xA6: AND(mmu.readByte(HL)); break; //AND M 1 8 Z010 - case 0xA7: AND(A); break; //AND A 1 4 Z010 - - case 0xA8: XOR(B); break; //XOR B 1 4 Z000 - case 0xA9: XOR(C); break; //XOR C 1 4 Z000 - case 0xAA: XOR(D); break; //XOR D 1 4 Z000 - case 0xAB: XOR(E); break; //XOR E 1 4 Z000 - case 0xAC: XOR(H); break; //XOR H 1 4 Z000 - case 0xAD: XOR(L); break; //XOR L 1 4 Z000 + case 0xA7: AND(A); break; //AND A 1 4 Z010 + + case 0xA8: XOR(B); break; //XOR B 1 4 Z000 + case 0xA9: XOR(C); break; //XOR C 1 4 Z000 + case 0xAA: XOR(D); break; //XOR D 1 4 Z000 + case 0xAB: XOR(E); break; //XOR E 1 4 Z000 + case 0xAC: XOR(H); break; //XOR H 1 4 Z000 + case 0xAD: XOR(L); break; //XOR L 1 4 Z000 case 0xAE: XOR(mmu.readByte(HL)); break; //XOR M 1 8 Z000 - case 0xAF: XOR(A); break; //XOR A 1 4 Z000 - - case 0xB0: OR(B); break; //OR B 1 4 Z000 - case 0xB1: OR(C); break; //OR C 1 4 Z000 - case 0xB2: OR(D); break; //OR D 1 4 Z000 - case 0xB3: OR(E); break; //OR E 1 4 Z000 - case 0xB4: OR(H); break; //OR H 1 4 Z000 - case 0xB5: OR(L); break; //OR L 1 4 Z000 - case 0xB6: OR(mmu.readByte(HL)); break; //OR M 1 8 Z000 - case 0xB7: OR(A); break; //OR A 1 4 Z000 - - case 0xB8: CP(B); break; //CP B 1 4 Z1HC - case 0xB9: CP(C); break; //CP C 1 4 Z1HC - case 0xBA: CP(D); break; //CP D 1 4 Z1HC - case 0xBB: CP(E); break; //CP E 1 4 Z1HC - case 0xBC: CP(H); break; //CP H 1 4 Z1HC - case 0xBD: CP(L); break; //CP L 1 4 Z1HC - case 0xBE: CP(mmu.readByte(HL)); break; //CP M 1 8 Z1HC - case 0xBF: CP(A); break; //CP A 1 4 Z1HC - - case 0xC0: RETURN(!FlagZ); break; //RET NZ 1 20/8 ---- - case 0xC1: BC = POP(); break; //POP BC 1 12 ---- - case 0xC2: JUMP(!FlagZ); break; //JP NZ,A16 3 16/12 ---- - case 0xC3: JUMP(true); break; //JP A16 3 16 ---- - case 0xC4: CALL(!FlagZ); break; //CALL NZ A16 3 24/12 ---- - case 0xC5: PUSH(BC); break; //PUSH BC 1 16 ---- - case 0xC6: ADD(mmu.readByte(PC)); PC += 1; break; //ADD A,D8 2 8 Z0HC - case 0xC7: RST(0x0); break; //RST 0 1 16 ---- - - case 0xC8: RETURN(FlagZ); break; //RET Z 1 20/8 ---- - case 0xC9: RETURN(true); break; //RET 1 16 ---- - case 0xCA: JUMP(FlagZ); break; //JP Z,A16 3 16/12 ---- - case 0xCB: PREFIX_CB(mmu.readByte(PC++)); break; //PREFIX CB OPCODE TABLE - case 0xCC: CALL(FlagZ); break; //CALL Z,A16 3 24/12 ---- - case 0xCD: CALL(true); break; //CALL A16 3 24 ---- - case 0xCE: ADC(mmu.readByte(PC)); PC += 1; break; //ADC A,D8 2 8 ---- - case 0xCF: RST(0x8); break; //RST 1 08 1 16 ---- - - case 0xD0: RETURN(!FlagC); break; //RET NC 1 20/8 ---- - case 0xD1: DE = POP(); break; //POP DE 1 12 ---- - case 0xD2: JUMP(!FlagC); break; //JP NC,A16 3 16/12 ---- + case 0xAF: XOR(A); break; //XOR A 1 4 Z000 + + case 0xB0: OR(B); break; //OR B 1 4 Z000 + case 0xB1: OR(C); break; //OR C 1 4 Z000 + case 0xB2: OR(D); break; //OR D 1 4 Z000 + case 0xB3: OR(E); break; //OR E 1 4 Z000 + case 0xB4: OR(H); break; //OR H 1 4 Z000 + case 0xB5: OR(L); break; //OR L 1 4 Z000 + case 0xB6: OR(mmu.readByte(HL)); break; //OR M 1 8 Z000 + case 0xB7: OR(A); break; //OR A 1 4 Z000 + + case 0xB8: CP(B); break; //CP B 1 4 Z1HC + case 0xB9: CP(C); break; //CP C 1 4 Z1HC + case 0xBA: CP(D); break; //CP D 1 4 Z1HC + case 0xBB: CP(E); break; //CP E 1 4 Z1HC + case 0xBC: CP(H); break; //CP H 1 4 Z1HC + case 0xBD: CP(L); break; //CP L 1 4 Z1HC + case 0xBE: CP(mmu.readByte(HL)); break; //CP M 1 8 Z1HC + case 0xBF: CP(A); break; //CP A 1 4 Z1HC + + case 0xC0: RETURN(!FlagZ); break; //RET NZ 1 20/8 ---- + case 0xC1: BC = POP(); break; //POP BC 1 12 ---- + case 0xC2: JUMP(!FlagZ); break; //JP NZ,A16 3 16/12 ---- + case 0xC3: JUMP(true); break; //JP A16 3 16 ---- + case 0xC4: CALL(!FlagZ); break; //CALL NZ A16 3 24/12 ---- + case 0xC5: PUSH(BC); break; //PUSH BC 1 16 ---- + case 0xC6: ADD(mmu.readByte(PC)); PC += 1; break; //ADD A,D8 2 8 Z0HC + case 0xC7: RST(0x0); break; //RST 0 1 16 ---- + + case 0xC8: RETURN(FlagZ); break; //RET Z 1 20/8 ---- + case 0xC9: RETURN(true); break; //RET 1 16 ---- + case 0xCA: JUMP(FlagZ); break; //JP Z,A16 3 16/12 ---- + case 0xCB: PREFIX_CB(mmu.readByte(PC++)); break; //PREFIX CB OPCODE TABLE + case 0xCC: CALL(FlagZ); break; //CALL Z,A16 3 24/12 ---- + case 0xCD: CALL(true); break; //CALL A16 3 24 ---- + case 0xCE: ADC(mmu.readByte(PC)); PC += 1; break; //ADC A,D8 2 8 ---- + case 0xCF: RST(0x8); break; //RST 1 08 1 16 ---- + + case 0xD0: RETURN(!FlagC); break; //RET NC 1 20/8 ---- + case 0xD1: DE = POP(); break; //POP DE 1 12 ---- + case 0xD2: JUMP(!FlagC); break; //JP NC,A16 3 16/12 ---- //case 0xD3: break; //Illegal Opcode - case 0xD4: CALL(!FlagC); break; //CALL NC,A16 3 24/12 ---- - case 0xD5: PUSH(DE); break; //PUSH DE 1 16 ---- - case 0xD6: SUB(mmu.readByte(PC)); PC += 1; break; //SUB D8 2 8 ---- - case 0xD7: RST(0x10); break; //RST 2 10 1 16 ---- - - case 0xD8: RETURN(FlagC); break; //RET C 1 20/8 ---- - case 0xD9: RETURN(true); IME = true; break; //RETI 1 16 ---- - case 0xDA: JUMP(FlagC); break; //JP C,A16 3 16/12 ---- + case 0xD4: CALL(!FlagC); break; //CALL NC,A16 3 24/12 ---- + case 0xD5: PUSH(DE); break; //PUSH DE 1 16 ---- + case 0xD6: SUB(mmu.readByte(PC)); PC += 1; break; //SUB D8 2 8 ---- + case 0xD7: RST(0x10); break; //RST 2 10 1 16 ---- + + case 0xD8: RETURN(FlagC); break; //RET C 1 20/8 ---- + case 0xD9: RETURN(true); IME = true; break; //RETI 1 16 ---- + case 0xDA: JUMP(FlagC); break; //JP C,A16 3 16/12 ---- //case 0xDB: break; //Illegal Opcode - case 0xDC: CALL(FlagC); break; //Call C,A16 3 24/12 ---- + case 0xDC: CALL(FlagC); break; //Call C,A16 3 24/12 ---- //case 0xDD: break; //Illegal Opcode - case 0xDE: SBC(mmu.readByte(PC)); PC += 1; break; //SBC A,A8 2 8 Z1HC - case 0xDF: RST(0x18); break; //RST 3 18 1 16 ---- + case 0xDE: SBC(mmu.readByte(PC)); PC += 1; break; //SBC A,A8 2 8 Z1HC + case 0xDF: RST(0x18); break; //RST 3 18 1 16 ---- - case 0xE0: mmu.writeByte((ushort)(0xFF00 + mmu.readByte(PC)), A); PC += 1; break; //LDH (A8),A 2 12 ---- - case 0xE1: HL = POP(); break; //POP HL 1 12 ---- - case 0xE2: mmu.writeByte((ushort)(0xFF00 + C), A); break; //LD (C),A 1 8 ---- + case 0xE0: mmu.writeByte((ushort)(0xFF00 + mmu.readByte(PC)), A); PC += 1; break; //LDH (A8),A 2 12 ---- + case 0xE1: HL = POP(); break; //POP HL 1 12 ---- + case 0xE2: mmu.writeByte((ushort)(0xFF00 + C), A); break; //LD (C),A 1 8 ---- //case 0xE3: break; //Illegal Opcode //case 0xE4: break; //Illegal Opcode - case 0xE5: PUSH(HL); break; //PUSH HL 1 16 ---- - case 0xE6: AND(mmu.readByte(PC)); PC += 1; break; //AND D8 2 8 Z010 - case 0xE7: RST(0x20); break; //RST 4 20 1 16 ---- + case 0xE5: PUSH(HL); break; //PUSH HL 1 16 ---- + case 0xE6: AND(mmu.readByte(PC)); PC += 1; break; //AND D8 2 8 Z010 + case 0xE7: RST(0x20); break; //RST 4 20 1 16 ---- - case 0xE8: SP = DADr8(SP); break; //ADD SP,R8 2 16 00HC - case 0xE9: PC = HL; break; //JP (HL) 1 4 ---- - case 0xEA: mmu.writeByte(mmu.readWord(PC), A); PC += 2; break; //LD (A16),A 3 16 ---- + case 0xE8: SP = DADr8(SP); break; //ADD SP,R8 2 16 00HC + case 0xE9: PC = HL; break; //JP (HL) 1 4 ---- + case 0xEA: mmu.writeByte(mmu.readWord(PC), A); PC += 2; break; //LD (A16),A 3 16 ---- //case 0xEB: break; //Illegal Opcode //case 0xEC: break; //Illegal Opcode //case 0xED: break; //Illegal Opcode - case 0xEE: XOR(mmu.readByte(PC)); PC += 1; break; //XOR D8 2 8 Z000 - case 0xEF: RST(0x28); break; //RST 5 28 1 16 ---- + case 0xEE: XOR(mmu.readByte(PC)); PC += 1; break; //XOR D8 2 8 Z000 + case 0xEF: RST(0x28); break; //RST 5 28 1 16 ---- - case 0xF0: A = mmu.readByte((ushort)(0xFF00 + mmu.readByte(PC))); PC += 1; break; //LDH A,(A8) 2 12 ---- - case 0xF1: AF = POP(); break; //POP AF 1 12 ZNHC - case 0xF2: A = mmu.readByte((ushort)(0xFF00 + C)); break; //LD A,(C) 1 8 ---- - case 0xF3: IME = false; break; //DI 1 4 ---- + case 0xF0: A = mmu.readByte((ushort)(0xFF00 + mmu.readByte(PC))); PC += 1; break; //LDH A,(A8) 2 12 ---- + case 0xF1: AF = POP(); break; //POP AF 1 12 ZNHC + case 0xF2: A = mmu.readByte((ushort)(0xFF00 + C)); break; //LD A,(C) 1 8 ---- + case 0xF3: IME = false; break; //DI 1 4 ---- //case 0xF4: break; //Illegal Opcode - case 0xF5: PUSH(AF); break; //PUSH AF 1 16 ---- - case 0xF6: OR(mmu.readByte(PC)); PC += 1; break; //OR D8 2 8 Z000 - case 0xF7: RST(0x30); break; //RST 6 30 1 16 ---- - - case 0xF8: HL = DADr8(SP); break; //LD HL,SP+R8 2 12 00HC - case 0xF9: SP = HL; break; //LD SP,HL 1 8 ---- - case 0xFA: A = mmu.readByte(mmu.readWord(PC)); PC += 2; break; //LD A,(A16) 3 16 ---- - case 0xFB: IMEEnabler = true; break; //IE 1 4 ---- + case 0xF5: PUSH(AF); break; //PUSH AF 1 16 ---- + case 0xF6: OR(mmu.readByte(PC)); PC += 1; break; //OR D8 2 8 Z000 + case 0xF7: RST(0x30); break; //RST 6 30 1 16 ---- + + case 0xF8: HL = DADr8(SP); break; //LD HL,SP+R8 2 12 00HC + case 0xF9: SP = HL; break; //LD SP,HL 1 8 ---- + case 0xFA: A = mmu.readByte(mmu.readWord(PC)); PC += 2; break; //LD A,(A16) 3 16 ---- + case 0xFB: IMEEnabler = true; break; //IE 1 4 ---- //case 0xFC: break; //Illegal Opcode //case 0xFD: break; //Illegal Opcode - case 0xFE: CP(mmu.readByte(PC)); PC += 1; break; //CP D8 2 8 Z1HC - case 0xFF: RST(0x38); break; //RST 7 38 1 16 ---- + case 0xFE: CP(mmu.readByte(PC)); PC += 1; break; //CP D8 2 8 Z1HC + case 0xFF: RST(0x38); break; //RST 7 38 1 16 ---- - default: warnUnsupportedOpcode(opcode); break; + default: warnUnsupportedOpcode(opcode); break; } cycles += Cycles.Value[opcode]; return cycles; } - private void PREFIX_CB(byte opcode) { - switch (opcode) { - case 0x00: B = RLC(B); break; //RLC B 2 8 Z00C - case 0x01: C = RLC(C); break; //RLC C 2 8 Z00C - case 0x02: D = RLC(D); break; //RLC D 2 8 Z00C - case 0x03: E = RLC(E); break; //RLC E 2 8 Z00C - case 0x04: H = RLC(H); break; //RLC H 2 8 Z00C - case 0x05: L = RLC(L); break; //RLC L 2 8 Z00C - case 0x06: mmu.writeByte(HL, RLC(mmu.readByte(HL))); break; //RLC (HL) 2 8 Z00C - case 0x07: A = RLC(A); break; //RLC B 2 8 Z00C - - case 0x08: B = RRC(B); break; //RRC B 2 8 Z00C - case 0x09: C = RRC(C); break; //RRC C 2 8 Z00C - case 0x0A: D = RRC(D); break; //RRC D 2 8 Z00C - case 0x0B: E = RRC(E); break; //RRC E 2 8 Z00C - case 0x0C: H = RRC(H); break; //RRC H 2 8 Z00C - case 0x0D: L = RRC(L); break; //RRC L 2 8 Z00C - case 0x0E: mmu.writeByte(HL, RRC(mmu.readByte(HL))); break; //RRC (HL) 2 8 Z00C - case 0x0F: A = RRC(A); break; //RRC B 2 8 Z00C - - case 0x10: B = RL(B); break; //RL B 2 8 Z00C - case 0x11: C = RL(C); break; //RL C 2 8 Z00C - case 0x12: D = RL(D); break; //RL D 2 8 Z00C - case 0x13: E = RL(E); break; //RL E 2 8 Z00C - case 0x14: H = RL(H); break; //RL H 2 8 Z00C - case 0x15: L = RL(L); break; //RL L 2 8 Z00C - case 0x16: mmu.writeByte(HL, RL(mmu.readByte(HL))); break; //RL (HL) 2 8 Z00C - case 0x17: A = RL(A); break; //RL B 2 8 Z00C - - case 0x18: B = RR(B); break; //RR B 2 8 Z00C - case 0x19: C = RR(C); break; //RR C 2 8 Z00C - case 0x1A: D = RR(D); break; //RR D 2 8 Z00C - case 0x1B: E = RR(E); break; //RR E 2 8 Z00C - case 0x1C: H = RR(H); break; //RR H 2 8 Z00C - case 0x1D: L = RR(L); break; //RR L 2 8 Z00C - case 0x1E: mmu.writeByte(HL, RR(mmu.readByte(HL))); break; //RR (HL) 2 8 Z00C - case 0x1F: A = RR(A); break; //RR B 2 8 Z00C - - case 0x20: B = SLA(B); break; //SLA B 2 8 Z00C - case 0x21: C = SLA(C); break; //SLA C 2 8 Z00C - case 0x22: D = SLA(D); break; //SLA D 2 8 Z00C - case 0x23: E = SLA(E); break; //SLA E 2 8 Z00C - case 0x24: H = SLA(H); break; //SLA H 2 8 Z00C - case 0x25: L = SLA(L); break; //SLA L 2 8 Z00C - case 0x26: mmu.writeByte(HL, SLA(mmu.readByte(HL))); break; //SLA (HL) 2 8 Z00C - case 0x27: A = SLA(A); break; //SLA B 2 8 Z00C - - case 0x28: B = SRA(B); break; //SRA B 2 8 Z00C - case 0x29: C = SRA(C); break; //SRA C 2 8 Z00C - case 0x2A: D = SRA(D); break; //SRA D 2 8 Z00C - case 0x2B: E = SRA(E); break; //SRA E 2 8 Z00C - case 0x2C: H = SRA(H); break; //SRA H 2 8 Z00C - case 0x2D: L = SRA(L); break; //SRA L 2 8 Z00C - case 0x2E: mmu.writeByte(HL, SRA(mmu.readByte(HL))); break; //SRA (HL) 2 8 Z00C - case 0x2F: A = SRA(A); break; //SRA B 2 8 Z00C - - case 0x30: B = SWAP(B); break; //SWAP B 2 8 Z00C - case 0x31: C = SWAP(C); break; //SWAP C 2 8 Z00C - case 0x32: D = SWAP(D); break; //SWAP D 2 8 Z00C - case 0x33: E = SWAP(E); break; //SWAP E 2 8 Z00C - case 0x34: H = SWAP(H); break; //SWAP H 2 8 Z00C - case 0x35: L = SWAP(L); break; //SWAP L 2 8 Z00C - case 0x36: mmu.writeByte(HL, SWAP(mmu.readByte(HL))); break; //SWAP (HL) 2 8 Z00C - case 0x37: A = SWAP(A); break; //SWAP B 2 8 Z00C - - case 0x38: B = SRL(B); break; //SRL B 2 8 Z000 - case 0x39: C = SRL(C); break; //SRL C 2 8 Z000 - case 0x3A: D = SRL(D); break; //SRL D 2 8 Z000 - case 0x3B: E = SRL(E); break; //SRL E 2 8 Z000 - case 0x3C: H = SRL(H); break; //SRL H 2 8 Z000 - case 0x3D: L = SRL(L); break; //SRL L 2 8 Z000 - case 0x3E: mmu.writeByte(HL, SRL(mmu.readByte(HL))); break; //SRL (HL) 2 8 Z000 - case 0x3F: A = SRL(A); break; //SRL B 2 8 Z000 - - case 0x40: BIT(0x1, B); break; //BIT B 2 8 Z01- - case 0x41: BIT(0x1, C); break; //BIT C 2 8 Z01- - case 0x42: BIT(0x1, D); break; //BIT D 2 8 Z01- - case 0x43: BIT(0x1, E); break; //BIT E 2 8 Z01- - case 0x44: BIT(0x1, H); break; //BIT H 2 8 Z01- - case 0x45: BIT(0x1, L); break; //BIT L 2 8 Z01- - case 0x46: BIT(0x1, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x47: BIT(0x1, A); break; //BIT B 2 8 Z01- - - case 0x48: BIT(0x2, B); break; //BIT B 2 8 Z01- - case 0x49: BIT(0x2, C); break; //BIT C 2 8 Z01- - case 0x4A: BIT(0x2, D); break; //BIT D 2 8 Z01- - case 0x4B: BIT(0x2, E); break; //BIT E 2 8 Z01- - case 0x4C: BIT(0x2, H); break; //BIT H 2 8 Z01- - case 0x4D: BIT(0x2, L); break; //BIT L 2 8 Z01- - case 0x4E: BIT(0x2, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x4F: BIT(0x2, A); break; //BIT B 2 8 Z01- - - case 0x50: BIT(0x4, B); break; //BIT B 2 8 Z01- - case 0x51: BIT(0x4, C); break; //BIT C 2 8 Z01- - case 0x52: BIT(0x4, D); break; //BIT D 2 8 Z01- - case 0x53: BIT(0x4, E); break; //BIT E 2 8 Z01- - case 0x54: BIT(0x4, H); break; //BIT H 2 8 Z01- - case 0x55: BIT(0x4, L); break; //BIT L 2 8 Z01- - case 0x56: BIT(0x4, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x57: BIT(0x4, A); break; //BIT B 2 8 Z01- - - case 0x58: BIT(0x8, B); break; //BIT B 2 8 Z01- - case 0x59: BIT(0x8, C); break; //BIT C 2 8 Z01- - case 0x5A: BIT(0x8, D); break; //BIT D 2 8 Z01- - case 0x5B: BIT(0x8, E); break; //BIT E 2 8 Z01- - case 0x5C: BIT(0x8, H); break; //BIT H 2 8 Z01- - case 0x5D: BIT(0x8, L); break; //BIT L 2 8 Z01- - case 0x5E: BIT(0x8, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x5F: BIT(0x8, A); break; //BIT B 2 8 Z01- - - case 0x60: BIT(0x10, B); break; //BIT B 2 8 Z01- - case 0x61: BIT(0x10, C); break; //BIT C 2 8 Z01- - case 0x62: BIT(0x10, D); break; //BIT D 2 8 Z01- - case 0x63: BIT(0x10, E); break; //BIT E 2 8 Z01- - case 0x64: BIT(0x10, H); break; //BIT H 2 8 Z01- - case 0x65: BIT(0x10, L); break; //BIT L 2 8 Z01- - case 0x66: BIT(0x10, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x67: BIT(0x10, A); break; //BIT B 2 8 Z01- - - case 0x68: BIT(0x20, B); break; //BIT B 2 8 Z01- - case 0x69: BIT(0x20, C); break; //BIT C 2 8 Z01- - case 0x6A: BIT(0x20, D); break; //BIT D 2 8 Z01- - case 0x6B: BIT(0x20, E); break; //BIT E 2 8 Z01- - case 0x6C: BIT(0x20, H); break; //BIT H 2 8 Z01- - case 0x6D: BIT(0x20, L); break; //BIT L 2 8 Z01- - case 0x6E: BIT(0x20, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x6F: BIT(0x20, A); break; //BIT B 2 8 Z01- - - case 0x70: BIT(0x40, B); break; //BIT B 2 8 Z01- - case 0x71: BIT(0x40, C); break; //BIT C 2 8 Z01- - case 0x72: BIT(0x40, D); break; //BIT D 2 8 Z01- - case 0x73: BIT(0x40, E); break; //BIT E 2 8 Z01- - case 0x74: BIT(0x40, H); break; //BIT H 2 8 Z01- - case 0x75: BIT(0x40, L); break; //BIT L 2 8 Z01- - case 0x76: BIT(0x40, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x77: BIT(0x40, A); break; //BIT B 2 8 Z01- - - case 0x78: BIT(0x80, B); break; //BIT B 2 8 Z01- - case 0x79: BIT(0x80, C); break; //BIT C 2 8 Z01- - case 0x7A: BIT(0x80, D); break; //BIT D 2 8 Z01- - case 0x7B: BIT(0x80, E); break; //BIT E 2 8 Z01- - case 0x7C: BIT(0x80, H); break; //BIT H 2 8 Z01- - case 0x7D: BIT(0x80, L); break; //BIT L 2 8 Z01- - case 0x7E: BIT(0x80, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- - case 0x7F: BIT(0x80, A); break; //BIT B 2 8 Z01- - - case 0x80: B = RES(0x1, B); break; //RES B 2 8 ---- - case 0x81: C = RES(0x1, C); break; //RES C 2 8 ---- - case 0x82: D = RES(0x1, D); break; //RES D 2 8 ---- - case 0x83: E = RES(0x1, E); break; //RES E 2 8 ---- - case 0x84: H = RES(0x1, H); break; //RES H 2 8 ---- - case 0x85: L = RES(0x1, L); break; //RES L 2 8 ---- + private void PREFIX_CB(byte opcode) + { + switch (opcode) + { + case 0x00: B = RLC(B); break; //RLC B 2 8 Z00C + case 0x01: C = RLC(C); break; //RLC C 2 8 Z00C + case 0x02: D = RLC(D); break; //RLC D 2 8 Z00C + case 0x03: E = RLC(E); break; //RLC E 2 8 Z00C + case 0x04: H = RLC(H); break; //RLC H 2 8 Z00C + case 0x05: L = RLC(L); break; //RLC L 2 8 Z00C + case 0x06: mmu.writeByte(HL, RLC(mmu.readByte(HL))); break; //RLC (HL) 2 8 Z00C + case 0x07: A = RLC(A); break; //RLC B 2 8 Z00C + + case 0x08: B = RRC(B); break; //RRC B 2 8 Z00C + case 0x09: C = RRC(C); break; //RRC C 2 8 Z00C + case 0x0A: D = RRC(D); break; //RRC D 2 8 Z00C + case 0x0B: E = RRC(E); break; //RRC E 2 8 Z00C + case 0x0C: H = RRC(H); break; //RRC H 2 8 Z00C + case 0x0D: L = RRC(L); break; //RRC L 2 8 Z00C + case 0x0E: mmu.writeByte(HL, RRC(mmu.readByte(HL))); break; //RRC (HL) 2 8 Z00C + case 0x0F: A = RRC(A); break; //RRC B 2 8 Z00C + + case 0x10: B = RL(B); break; //RL B 2 8 Z00C + case 0x11: C = RL(C); break; //RL C 2 8 Z00C + case 0x12: D = RL(D); break; //RL D 2 8 Z00C + case 0x13: E = RL(E); break; //RL E 2 8 Z00C + case 0x14: H = RL(H); break; //RL H 2 8 Z00C + case 0x15: L = RL(L); break; //RL L 2 8 Z00C + case 0x16: mmu.writeByte(HL, RL(mmu.readByte(HL))); break; //RL (HL) 2 8 Z00C + case 0x17: A = RL(A); break; //RL B 2 8 Z00C + + case 0x18: B = RR(B); break; //RR B 2 8 Z00C + case 0x19: C = RR(C); break; //RR C 2 8 Z00C + case 0x1A: D = RR(D); break; //RR D 2 8 Z00C + case 0x1B: E = RR(E); break; //RR E 2 8 Z00C + case 0x1C: H = RR(H); break; //RR H 2 8 Z00C + case 0x1D: L = RR(L); break; //RR L 2 8 Z00C + case 0x1E: mmu.writeByte(HL, RR(mmu.readByte(HL))); break; //RR (HL) 2 8 Z00C + case 0x1F: A = RR(A); break; //RR B 2 8 Z00C + + case 0x20: B = SLA(B); break; //SLA B 2 8 Z00C + case 0x21: C = SLA(C); break; //SLA C 2 8 Z00C + case 0x22: D = SLA(D); break; //SLA D 2 8 Z00C + case 0x23: E = SLA(E); break; //SLA E 2 8 Z00C + case 0x24: H = SLA(H); break; //SLA H 2 8 Z00C + case 0x25: L = SLA(L); break; //SLA L 2 8 Z00C + case 0x26: mmu.writeByte(HL, SLA(mmu.readByte(HL))); break; //SLA (HL) 2 8 Z00C + case 0x27: A = SLA(A); break; //SLA B 2 8 Z00C + + case 0x28: B = SRA(B); break; //SRA B 2 8 Z00C + case 0x29: C = SRA(C); break; //SRA C 2 8 Z00C + case 0x2A: D = SRA(D); break; //SRA D 2 8 Z00C + case 0x2B: E = SRA(E); break; //SRA E 2 8 Z00C + case 0x2C: H = SRA(H); break; //SRA H 2 8 Z00C + case 0x2D: L = SRA(L); break; //SRA L 2 8 Z00C + case 0x2E: mmu.writeByte(HL, SRA(mmu.readByte(HL))); break; //SRA (HL) 2 8 Z00C + case 0x2F: A = SRA(A); break; //SRA B 2 8 Z00C + + case 0x30: B = SWAP(B); break; //SWAP B 2 8 Z00C + case 0x31: C = SWAP(C); break; //SWAP C 2 8 Z00C + case 0x32: D = SWAP(D); break; //SWAP D 2 8 Z00C + case 0x33: E = SWAP(E); break; //SWAP E 2 8 Z00C + case 0x34: H = SWAP(H); break; //SWAP H 2 8 Z00C + case 0x35: L = SWAP(L); break; //SWAP L 2 8 Z00C + case 0x36: mmu.writeByte(HL, SWAP(mmu.readByte(HL))); break; //SWAP (HL) 2 8 Z00C + case 0x37: A = SWAP(A); break; //SWAP B 2 8 Z00C + + case 0x38: B = SRL(B); break; //SRL B 2 8 Z000 + case 0x39: C = SRL(C); break; //SRL C 2 8 Z000 + case 0x3A: D = SRL(D); break; //SRL D 2 8 Z000 + case 0x3B: E = SRL(E); break; //SRL E 2 8 Z000 + case 0x3C: H = SRL(H); break; //SRL H 2 8 Z000 + case 0x3D: L = SRL(L); break; //SRL L 2 8 Z000 + case 0x3E: mmu.writeByte(HL, SRL(mmu.readByte(HL))); break; //SRL (HL) 2 8 Z000 + case 0x3F: A = SRL(A); break; //SRL B 2 8 Z000 + + case 0x40: BIT(0x1, B); break; //BIT B 2 8 Z01- + case 0x41: BIT(0x1, C); break; //BIT C 2 8 Z01- + case 0x42: BIT(0x1, D); break; //BIT D 2 8 Z01- + case 0x43: BIT(0x1, E); break; //BIT E 2 8 Z01- + case 0x44: BIT(0x1, H); break; //BIT H 2 8 Z01- + case 0x45: BIT(0x1, L); break; //BIT L 2 8 Z01- + case 0x46: BIT(0x1, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x47: BIT(0x1, A); break; //BIT B 2 8 Z01- + + case 0x48: BIT(0x2, B); break; //BIT B 2 8 Z01- + case 0x49: BIT(0x2, C); break; //BIT C 2 8 Z01- + case 0x4A: BIT(0x2, D); break; //BIT D 2 8 Z01- + case 0x4B: BIT(0x2, E); break; //BIT E 2 8 Z01- + case 0x4C: BIT(0x2, H); break; //BIT H 2 8 Z01- + case 0x4D: BIT(0x2, L); break; //BIT L 2 8 Z01- + case 0x4E: BIT(0x2, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x4F: BIT(0x2, A); break; //BIT B 2 8 Z01- + + case 0x50: BIT(0x4, B); break; //BIT B 2 8 Z01- + case 0x51: BIT(0x4, C); break; //BIT C 2 8 Z01- + case 0x52: BIT(0x4, D); break; //BIT D 2 8 Z01- + case 0x53: BIT(0x4, E); break; //BIT E 2 8 Z01- + case 0x54: BIT(0x4, H); break; //BIT H 2 8 Z01- + case 0x55: BIT(0x4, L); break; //BIT L 2 8 Z01- + case 0x56: BIT(0x4, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x57: BIT(0x4, A); break; //BIT B 2 8 Z01- + + case 0x58: BIT(0x8, B); break; //BIT B 2 8 Z01- + case 0x59: BIT(0x8, C); break; //BIT C 2 8 Z01- + case 0x5A: BIT(0x8, D); break; //BIT D 2 8 Z01- + case 0x5B: BIT(0x8, E); break; //BIT E 2 8 Z01- + case 0x5C: BIT(0x8, H); break; //BIT H 2 8 Z01- + case 0x5D: BIT(0x8, L); break; //BIT L 2 8 Z01- + case 0x5E: BIT(0x8, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x5F: BIT(0x8, A); break; //BIT B 2 8 Z01- + + case 0x60: BIT(0x10, B); break; //BIT B 2 8 Z01- + case 0x61: BIT(0x10, C); break; //BIT C 2 8 Z01- + case 0x62: BIT(0x10, D); break; //BIT D 2 8 Z01- + case 0x63: BIT(0x10, E); break; //BIT E 2 8 Z01- + case 0x64: BIT(0x10, H); break; //BIT H 2 8 Z01- + case 0x65: BIT(0x10, L); break; //BIT L 2 8 Z01- + case 0x66: BIT(0x10, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x67: BIT(0x10, A); break; //BIT B 2 8 Z01- + + case 0x68: BIT(0x20, B); break; //BIT B 2 8 Z01- + case 0x69: BIT(0x20, C); break; //BIT C 2 8 Z01- + case 0x6A: BIT(0x20, D); break; //BIT D 2 8 Z01- + case 0x6B: BIT(0x20, E); break; //BIT E 2 8 Z01- + case 0x6C: BIT(0x20, H); break; //BIT H 2 8 Z01- + case 0x6D: BIT(0x20, L); break; //BIT L 2 8 Z01- + case 0x6E: BIT(0x20, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x6F: BIT(0x20, A); break; //BIT B 2 8 Z01- + + case 0x70: BIT(0x40, B); break; //BIT B 2 8 Z01- + case 0x71: BIT(0x40, C); break; //BIT C 2 8 Z01- + case 0x72: BIT(0x40, D); break; //BIT D 2 8 Z01- + case 0x73: BIT(0x40, E); break; //BIT E 2 8 Z01- + case 0x74: BIT(0x40, H); break; //BIT H 2 8 Z01- + case 0x75: BIT(0x40, L); break; //BIT L 2 8 Z01- + case 0x76: BIT(0x40, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x77: BIT(0x40, A); break; //BIT B 2 8 Z01- + + case 0x78: BIT(0x80, B); break; //BIT B 2 8 Z01- + case 0x79: BIT(0x80, C); break; //BIT C 2 8 Z01- + case 0x7A: BIT(0x80, D); break; //BIT D 2 8 Z01- + case 0x7B: BIT(0x80, E); break; //BIT E 2 8 Z01- + case 0x7C: BIT(0x80, H); break; //BIT H 2 8 Z01- + case 0x7D: BIT(0x80, L); break; //BIT L 2 8 Z01- + case 0x7E: BIT(0x80, mmu.readByte(HL)); break; //BIT (HL) 2 8 Z01- + case 0x7F: BIT(0x80, A); break; //BIT B 2 8 Z01- + + case 0x80: B = RES(0x1, B); break; //RES B 2 8 ---- + case 0x81: C = RES(0x1, C); break; //RES C 2 8 ---- + case 0x82: D = RES(0x1, D); break; //RES D 2 8 ---- + case 0x83: E = RES(0x1, E); break; //RES E 2 8 ---- + case 0x84: H = RES(0x1, H); break; //RES H 2 8 ---- + case 0x85: L = RES(0x1, L); break; //RES L 2 8 ---- case 0x86: mmu.writeByte(HL, RES(0x1, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0x87: A = RES(0x1, A); break; //RES B 2 8 ---- - - case 0x88: B = RES(0x2, B); break; //RES B 2 8 ---- - case 0x89: C = RES(0x2, C); break; //RES C 2 8 ---- - case 0x8A: D = RES(0x2, D); break; //RES D 2 8 ---- - case 0x8B: E = RES(0x2, E); break; //RES E 2 8 ---- - case 0x8C: H = RES(0x2, H); break; //RES H 2 8 ---- - case 0x8D: L = RES(0x2, L); break; //RES L 2 8 ---- + case 0x87: A = RES(0x1, A); break; //RES B 2 8 ---- + + case 0x88: B = RES(0x2, B); break; //RES B 2 8 ---- + case 0x89: C = RES(0x2, C); break; //RES C 2 8 ---- + case 0x8A: D = RES(0x2, D); break; //RES D 2 8 ---- + case 0x8B: E = RES(0x2, E); break; //RES E 2 8 ---- + case 0x8C: H = RES(0x2, H); break; //RES H 2 8 ---- + case 0x8D: L = RES(0x2, L); break; //RES L 2 8 ---- case 0x8E: mmu.writeByte(HL, RES(0x2, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0x8F: A = RES(0x2, A); break; //RES B 2 8 ---- - - case 0x90: B = RES(0x4, B); break; //RES B 2 8 ---- - case 0x91: C = RES(0x4, C); break; //RES C 2 8 ---- - case 0x92: D = RES(0x4, D); break; //RES D 2 8 ---- - case 0x93: E = RES(0x4, E); break; //RES E 2 8 ---- - case 0x94: H = RES(0x4, H); break; //RES H 2 8 ---- - case 0x95: L = RES(0x4, L); break; //RES L 2 8 ---- + case 0x8F: A = RES(0x2, A); break; //RES B 2 8 ---- + + case 0x90: B = RES(0x4, B); break; //RES B 2 8 ---- + case 0x91: C = RES(0x4, C); break; //RES C 2 8 ---- + case 0x92: D = RES(0x4, D); break; //RES D 2 8 ---- + case 0x93: E = RES(0x4, E); break; //RES E 2 8 ---- + case 0x94: H = RES(0x4, H); break; //RES H 2 8 ---- + case 0x95: L = RES(0x4, L); break; //RES L 2 8 ---- case 0x96: mmu.writeByte(HL, RES(0x4, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0x97: A = RES(0x4, A); break; //RES B 2 8 ---- - - case 0x98: B = RES(0x8, B); break; //RES B 2 8 ---- - case 0x99: C = RES(0x8, C); break; //RES C 2 8 ---- - case 0x9A: D = RES(0x8, D); break; //RES D 2 8 ---- - case 0x9B: E = RES(0x8, E); break; //RES E 2 8 ---- - case 0x9C: H = RES(0x8, H); break; //RES H 2 8 ---- - case 0x9D: L = RES(0x8, L); break; //RES L 2 8 ---- + case 0x97: A = RES(0x4, A); break; //RES B 2 8 ---- + + case 0x98: B = RES(0x8, B); break; //RES B 2 8 ---- + case 0x99: C = RES(0x8, C); break; //RES C 2 8 ---- + case 0x9A: D = RES(0x8, D); break; //RES D 2 8 ---- + case 0x9B: E = RES(0x8, E); break; //RES E 2 8 ---- + case 0x9C: H = RES(0x8, H); break; //RES H 2 8 ---- + case 0x9D: L = RES(0x8, L); break; //RES L 2 8 ---- case 0x9E: mmu.writeByte(HL, RES(0x8, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0x9F: A = RES(0x8, A); break; //RES B 2 8 ---- - - case 0xA0: B = RES(0x10, B); break; //RES B 2 8 ---- - case 0xA1: C = RES(0x10, C); break; //RES C 2 8 ---- - case 0xA2: D = RES(0x10, D); break; //RES D 2 8 ---- - case 0xA3: E = RES(0x10, E); break; //RES E 2 8 ---- - case 0xA4: H = RES(0x10, H); break; //RES H 2 8 ---- - case 0xA5: L = RES(0x10, L); break; //RES L 2 8 ---- + case 0x9F: A = RES(0x8, A); break; //RES B 2 8 ---- + + case 0xA0: B = RES(0x10, B); break; //RES B 2 8 ---- + case 0xA1: C = RES(0x10, C); break; //RES C 2 8 ---- + case 0xA2: D = RES(0x10, D); break; //RES D 2 8 ---- + case 0xA3: E = RES(0x10, E); break; //RES E 2 8 ---- + case 0xA4: H = RES(0x10, H); break; //RES H 2 8 ---- + case 0xA5: L = RES(0x10, L); break; //RES L 2 8 ---- case 0xA6: mmu.writeByte(HL, RES(0x10, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0xA7: A = RES(0x10, A); break; //RES B 2 8 ---- - - case 0xA8: B = RES(0x20, B); break; //RES B 2 8 ---- - case 0xA9: C = RES(0x20, C); break; //RES C 2 8 ---- - case 0xAA: D = RES(0x20, D); break; //RES D 2 8 ---- - case 0xAB: E = RES(0x20, E); break; //RES E 2 8 ---- - case 0xAC: H = RES(0x20, H); break; //RES H 2 8 ---- - case 0xAD: L = RES(0x20, L); break; //RES L 2 8 ---- + case 0xA7: A = RES(0x10, A); break; //RES B 2 8 ---- + + case 0xA8: B = RES(0x20, B); break; //RES B 2 8 ---- + case 0xA9: C = RES(0x20, C); break; //RES C 2 8 ---- + case 0xAA: D = RES(0x20, D); break; //RES D 2 8 ---- + case 0xAB: E = RES(0x20, E); break; //RES E 2 8 ---- + case 0xAC: H = RES(0x20, H); break; //RES H 2 8 ---- + case 0xAD: L = RES(0x20, L); break; //RES L 2 8 ---- case 0xAE: mmu.writeByte(HL, RES(0x20, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0xAF: A = RES(0x20, A); break; //RES B 2 8 ---- - - case 0xB0: B = RES(0x40, B); break; //RES B 2 8 ---- - case 0xB1: C = RES(0x40, C); break; //RES C 2 8 ---- - case 0xB2: D = RES(0x40, D); break; //RES D 2 8 ---- - case 0xB3: E = RES(0x40, E); break; //RES E 2 8 ---- - case 0xB4: H = RES(0x40, H); break; //RES H 2 8 ---- - case 0xB5: L = RES(0x40, L); break; //RES L 2 8 ---- + case 0xAF: A = RES(0x20, A); break; //RES B 2 8 ---- + + case 0xB0: B = RES(0x40, B); break; //RES B 2 8 ---- + case 0xB1: C = RES(0x40, C); break; //RES C 2 8 ---- + case 0xB2: D = RES(0x40, D); break; //RES D 2 8 ---- + case 0xB3: E = RES(0x40, E); break; //RES E 2 8 ---- + case 0xB4: H = RES(0x40, H); break; //RES H 2 8 ---- + case 0xB5: L = RES(0x40, L); break; //RES L 2 8 ---- case 0xB6: mmu.writeByte(HL, RES(0x40, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0xB7: A = RES(0x40, A); break; //RES B 2 8 ---- - - case 0xB8: B = RES(0x80, B); break; //RES B 2 8 ---- - case 0xB9: C = RES(0x80, C); break; //RES C 2 8 ---- - case 0xBA: D = RES(0x80, D); break; //RES D 2 8 ---- - case 0xBB: E = RES(0x80, E); break; //RES E 2 8 ---- - case 0xBC: H = RES(0x80, H); break; //RES H 2 8 ---- - case 0xBD: L = RES(0x80, L); break; //RES L 2 8 ---- + case 0xB7: A = RES(0x40, A); break; //RES B 2 8 ---- + + case 0xB8: B = RES(0x80, B); break; //RES B 2 8 ---- + case 0xB9: C = RES(0x80, C); break; //RES C 2 8 ---- + case 0xBA: D = RES(0x80, D); break; //RES D 2 8 ---- + case 0xBB: E = RES(0x80, E); break; //RES E 2 8 ---- + case 0xBC: H = RES(0x80, H); break; //RES H 2 8 ---- + case 0xBD: L = RES(0x80, L); break; //RES L 2 8 ---- case 0xBE: mmu.writeByte(HL, RES(0x80, mmu.readByte(HL))); break; //RES (HL) 2 8 ---- - case 0xBF: A = RES(0x80, A); break; //RES B 2 8 ---- - - case 0xC0: B = SET(0x1, B); break; //SET B 2 8 ---- - case 0xC1: C = SET(0x1, C); break; //SET C 2 8 ---- - case 0xC2: D = SET(0x1, D); break; //SET D 2 8 ---- - case 0xC3: E = SET(0x1, E); break; //SET E 2 8 ---- - case 0xC4: H = SET(0x1, H); break; //SET H 2 8 ---- - case 0xC5: L = SET(0x1, L); break; //SET L 2 8 ---- + case 0xBF: A = RES(0x80, A); break; //RES B 2 8 ---- + + case 0xC0: B = SET(0x1, B); break; //SET B 2 8 ---- + case 0xC1: C = SET(0x1, C); break; //SET C 2 8 ---- + case 0xC2: D = SET(0x1, D); break; //SET D 2 8 ---- + case 0xC3: E = SET(0x1, E); break; //SET E 2 8 ---- + case 0xC4: H = SET(0x1, H); break; //SET H 2 8 ---- + case 0xC5: L = SET(0x1, L); break; //SET L 2 8 ---- case 0xC6: mmu.writeByte(HL, SET(0x1, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xC7: A = SET(0x1, A); break; //SET B 2 8 ---- - - case 0xC8: B = SET(0x2, B); break; //SET B 2 8 ---- - case 0xC9: C = SET(0x2, C); break; //SET C 2 8 ---- - case 0xCA: D = SET(0x2, D); break; //SET D 2 8 ---- - case 0xCB: E = SET(0x2, E); break; //SET E 2 8 ---- - case 0xCC: H = SET(0x2, H); break; //SET H 2 8 ---- - case 0xCD: L = SET(0x2, L); break; //SET L 2 8 ---- + case 0xC7: A = SET(0x1, A); break; //SET B 2 8 ---- + + case 0xC8: B = SET(0x2, B); break; //SET B 2 8 ---- + case 0xC9: C = SET(0x2, C); break; //SET C 2 8 ---- + case 0xCA: D = SET(0x2, D); break; //SET D 2 8 ---- + case 0xCB: E = SET(0x2, E); break; //SET E 2 8 ---- + case 0xCC: H = SET(0x2, H); break; //SET H 2 8 ---- + case 0xCD: L = SET(0x2, L); break; //SET L 2 8 ---- case 0xCE: mmu.writeByte(HL, SET(0x2, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xCF: A = SET(0x2, A); break; //SET B 2 8 ---- - - case 0xD0: B = SET(0x4, B); break; //SET B 2 8 ---- - case 0xD1: C = SET(0x4, C); break; //SET C 2 8 ---- - case 0xD2: D = SET(0x4, D); break; //SET D 2 8 ---- - case 0xD3: E = SET(0x4, E); break; //SET E 2 8 ---- - case 0xD4: H = SET(0x4, H); break; //SET H 2 8 ---- - case 0xD5: L = SET(0x4, L); break; //SET L 2 8 ---- + case 0xCF: A = SET(0x2, A); break; //SET B 2 8 ---- + + case 0xD0: B = SET(0x4, B); break; //SET B 2 8 ---- + case 0xD1: C = SET(0x4, C); break; //SET C 2 8 ---- + case 0xD2: D = SET(0x4, D); break; //SET D 2 8 ---- + case 0xD3: E = SET(0x4, E); break; //SET E 2 8 ---- + case 0xD4: H = SET(0x4, H); break; //SET H 2 8 ---- + case 0xD5: L = SET(0x4, L); break; //SET L 2 8 ---- case 0xD6: mmu.writeByte(HL, SET(0x4, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xD7: A = SET(0x4, A); break; //SET B 2 8 ---- - - case 0xD8: B = SET(0x8, B); break; //SET B 2 8 ---- - case 0xD9: C = SET(0x8, C); break; //SET C 2 8 ---- - case 0xDA: D = SET(0x8, D); break; //SET D 2 8 ---- - case 0xDB: E = SET(0x8, E); break; //SET E 2 8 ---- - case 0xDC: H = SET(0x8, H); break; //SET H 2 8 ---- - case 0xDD: L = SET(0x8, L); break; //SET L 2 8 ---- + case 0xD7: A = SET(0x4, A); break; //SET B 2 8 ---- + + case 0xD8: B = SET(0x8, B); break; //SET B 2 8 ---- + case 0xD9: C = SET(0x8, C); break; //SET C 2 8 ---- + case 0xDA: D = SET(0x8, D); break; //SET D 2 8 ---- + case 0xDB: E = SET(0x8, E); break; //SET E 2 8 ---- + case 0xDC: H = SET(0x8, H); break; //SET H 2 8 ---- + case 0xDD: L = SET(0x8, L); break; //SET L 2 8 ---- case 0xDE: mmu.writeByte(HL, SET(0x8, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xDF: A = SET(0x8, A); break; //SET B 2 8 ---- - - case 0xE0: B = SET(0x10, B); break; //SET B 2 8 ---- - case 0xE1: C = SET(0x10, C); break; //SET C 2 8 ---- - case 0xE2: D = SET(0x10, D); break; //SET D 2 8 ---- - case 0xE3: E = SET(0x10, E); break; //SET E 2 8 ---- - case 0xE4: H = SET(0x10, H); break; //SET H 2 8 ---- - case 0xE5: L = SET(0x10, L); break; //SET L 2 8 ---- + case 0xDF: A = SET(0x8, A); break; //SET B 2 8 ---- + + case 0xE0: B = SET(0x10, B); break; //SET B 2 8 ---- + case 0xE1: C = SET(0x10, C); break; //SET C 2 8 ---- + case 0xE2: D = SET(0x10, D); break; //SET D 2 8 ---- + case 0xE3: E = SET(0x10, E); break; //SET E 2 8 ---- + case 0xE4: H = SET(0x10, H); break; //SET H 2 8 ---- + case 0xE5: L = SET(0x10, L); break; //SET L 2 8 ---- case 0xE6: mmu.writeByte(HL, SET(0x10, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xE7: A = SET(0x10, A); break; //SET B 2 8 ---- - - case 0xE8: B = SET(0x20, B); break; //SET B 2 8 ---- - case 0xE9: C = SET(0x20, C); break; //SET C 2 8 ---- - case 0xEA: D = SET(0x20, D); break; //SET D 2 8 ---- - case 0xEB: E = SET(0x20, E); break; //SET E 2 8 ---- - case 0xEC: H = SET(0x20, H); break; //SET H 2 8 ---- - case 0xED: L = SET(0x20, L); break; //SET L 2 8 ---- + case 0xE7: A = SET(0x10, A); break; //SET B 2 8 ---- + + case 0xE8: B = SET(0x20, B); break; //SET B 2 8 ---- + case 0xE9: C = SET(0x20, C); break; //SET C 2 8 ---- + case 0xEA: D = SET(0x20, D); break; //SET D 2 8 ---- + case 0xEB: E = SET(0x20, E); break; //SET E 2 8 ---- + case 0xEC: H = SET(0x20, H); break; //SET H 2 8 ---- + case 0xED: L = SET(0x20, L); break; //SET L 2 8 ---- case 0xEE: mmu.writeByte(HL, SET(0x20, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xEF: A = SET(0x20, A); break; //SET B 2 8 ---- - - case 0xF0: B = SET(0x40, B); break; //SET B 2 8 ---- - case 0xF1: C = SET(0x40, C); break; //SET C 2 8 ---- - case 0xF2: D = SET(0x40, D); break; //SET D 2 8 ---- - case 0xF3: E = SET(0x40, E); break; //SET E 2 8 ---- - case 0xF4: H = SET(0x40, H); break; //SET H 2 8 ---- - case 0xF5: L = SET(0x40, L); break; //SET L 2 8 ---- + case 0xEF: A = SET(0x20, A); break; //SET B 2 8 ---- + + case 0xF0: B = SET(0x40, B); break; //SET B 2 8 ---- + case 0xF1: C = SET(0x40, C); break; //SET C 2 8 ---- + case 0xF2: D = SET(0x40, D); break; //SET D 2 8 ---- + case 0xF3: E = SET(0x40, E); break; //SET E 2 8 ---- + case 0xF4: H = SET(0x40, H); break; //SET H 2 8 ---- + case 0xF5: L = SET(0x40, L); break; //SET L 2 8 ---- case 0xF6: mmu.writeByte(HL, SET(0x40, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xF7: A = SET(0x40, A); break; //SET B 2 8 ---- - - case 0xF8: B = SET(0x80, B); break; //SET B 2 8 ---- - case 0xF9: C = SET(0x80, C); break; //SET C 2 8 ---- - case 0xFA: D = SET(0x80, D); break; //SET D 2 8 ---- - case 0xFB: E = SET(0x80, E); break; //SET E 2 8 ---- - case 0xFC: H = SET(0x80, H); break; //SET H 2 8 ---- - case 0xFD: L = SET(0x80, L); break; //SET L 2 8 ---- + case 0xF7: A = SET(0x40, A); break; //SET B 2 8 ---- + + case 0xF8: B = SET(0x80, B); break; //SET B 2 8 ---- + case 0xF9: C = SET(0x80, C); break; //SET C 2 8 ---- + case 0xFA: D = SET(0x80, D); break; //SET D 2 8 ---- + case 0xFB: E = SET(0x80, E); break; //SET E 2 8 ---- + case 0xFC: H = SET(0x80, H); break; //SET H 2 8 ---- + case 0xFD: L = SET(0x80, L); break; //SET L 2 8 ---- case 0xFE: mmu.writeByte(HL, SET(0x80, mmu.readByte(HL))); break; //SET (HL) 2 8 ---- - case 0xFF: A = SET(0x80, A); break; //SET B 2 8 ---- + case 0xFF: A = SET(0x80, A); break; //SET B 2 8 ---- default: warnUnsupportedOpcode(opcode); break; } cycles += Cycles.CBValue[opcode]; } - private byte SET(byte b, byte reg) {//---- + private byte SET(byte b, byte reg) + {//---- return (byte)(reg | b); } - private byte RES(int b, byte reg) {//---- + private byte RES(int b, byte reg) + {//---- return (byte)(reg & ~b); } - private void BIT(byte b, byte reg) {//Z01- + private void BIT(byte b, byte reg) + {//Z01- FlagZ = (reg & b) == 0; FlagN = false; FlagH = true; } - private byte SRL(byte b) {//Z00C + private byte SRL(byte b) + {//Z00C byte result = (byte)(b >> 1); SetFlagZ(result); FlagN = false; @@ -693,7 +737,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte SWAP(byte b) {//Z000 + private byte SWAP(byte b) + {//Z000 byte result = (byte)((b & 0xF0) >> 4 | (b & 0x0F) << 4); SetFlagZ(result); FlagN = false; @@ -702,8 +747,9 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte SRA(byte b) {//Z00C - byte result = (byte)((b >> 1) | ( b & 0x80)); + private byte SRA(byte b) + {//Z00C + byte result = (byte)((b >> 1) | (b & 0x80)); SetFlagZ(result); FlagN = false; FlagH = false; @@ -711,7 +757,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte SLA(byte b) {//Z00C + private byte SLA(byte b) + {//Z00C byte result = (byte)(b << 1); SetFlagZ(result); FlagN = false; @@ -720,7 +767,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte RR(byte b) {//Z00C + private byte RR(byte b) + {//Z00C bool prevC = FlagC; byte result = (byte)((b >> 1) | (prevC ? 0x80 : 0)); SetFlagZ(result); @@ -730,7 +778,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte RL(byte b) {//Z00C + private byte RL(byte b) + {//Z00C bool prevC = FlagC; byte result = (byte)((b << 1) | (prevC ? 1 : 0)); SetFlagZ(result); @@ -740,7 +789,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte RRC(byte b) {//Z00C + private byte RRC(byte b) + {//Z00C byte result = (byte)((b >> 1) | (b << 7)); SetFlagZ(result); FlagN = false; @@ -749,7 +799,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private byte RLC(byte b) {//Z00C + private byte RLC(byte b) + {//Z00C byte result = (byte)((b << 1) | (b >> 7)); SetFlagZ(result); FlagN = false; @@ -758,7 +809,8 @@ class CPU { // Sharp LR35902 CPU return result; } - private ushort DADr8(ushort w) {//00HC | warning r8 is signed! + private ushort DADr8(ushort w) + {//00HC | warning r8 is signed! byte b = mmu.readByte(PC++); FlagZ = false; FlagN = false; @@ -767,23 +819,29 @@ class CPU { // Sharp LR35902 CPU return (ushort)(w + (sbyte)b); } - private void JR(bool flag) { - if (flag) { + private void JR(bool flag) + { + if (flag) + { sbyte sb = (sbyte)mmu.readByte(PC); PC = (ushort)(PC + sb); PC += 1; //<---- //TODO WHAT? cycles += Cycles.JUMP_RELATIVE_TRUE; - } else { + } + else + { PC += 1; cycles += Cycles.JUMP_RELATIVE_FALSE; } } - private void STOP() { + private void STOP() + { throw new NotImplementedException(); } - private byte INC(byte b) { //Z0H- + private byte INC(byte b) + { //Z0H- int result = b + 1; SetFlagZ(result); FlagN = false; @@ -791,7 +849,8 @@ class CPU { // Sharp LR35902 CPU return (byte)result; } - private byte DEC(byte b) { //Z1H- + private byte DEC(byte b) + { //Z1H- int result = b - 1; SetFlagZ(result); FlagN = true; @@ -799,7 +858,8 @@ class CPU { // Sharp LR35902 CPU return (byte)result; } - private void ADD(byte b) { //Z0HC + private void ADD(byte b) + { //Z0HC int result = A + b; SetFlagZ(result); FlagN = false; @@ -808,7 +868,8 @@ class CPU { // Sharp LR35902 CPU A = (byte)result; } - private void ADC(byte b) { //Z0HC + private void ADC(byte b) + { //Z0HC int carry = FlagC ? 1 : 0; int result = A + b + carry; SetFlagZ(result); @@ -820,7 +881,8 @@ class CPU { // Sharp LR35902 CPU A = (byte)result; } - private void SUB(byte b) {//Z1HC + private void SUB(byte b) + {//Z1HC int result = A - b; SetFlagZ(result); FlagN = true; @@ -829,7 +891,8 @@ class CPU { // Sharp LR35902 CPU A = (byte)result; } - private void SBC(byte b) {//Z1HC + private void SBC(byte b) + {//Z1HC int carry = FlagC ? 1 : 0; int result = A - b - carry; SetFlagZ(result); @@ -841,7 +904,8 @@ class CPU { // Sharp LR35902 CPU A = (byte)result; } - private void AND(byte b) {//Z010 + private void AND(byte b) + {//Z010 byte result = (byte)(A & b); SetFlagZ(result); FlagN = false; @@ -850,7 +914,8 @@ class CPU { // Sharp LR35902 CPU A = result; } - private void XOR(byte b) {//Z000 + private void XOR(byte b) + {//Z000 byte result = (byte)(A ^ b); SetFlagZ(result); FlagN = false; @@ -859,7 +924,8 @@ class CPU { // Sharp LR35902 CPU A = result; } - private void OR(byte b) {//Z000 + private void OR(byte b) + {//Z000 byte result = (byte)(A | b); SetFlagZ(result); FlagN = false; @@ -868,7 +934,8 @@ class CPU { // Sharp LR35902 CPU A = result; } - private void CP(byte b) {//Z1HC + private void CP(byte b) + {//Z1HC int result = A - b; SetFlagZ(result); FlagN = true; @@ -876,7 +943,8 @@ class CPU { // Sharp LR35902 CPU SetFlagC(result); } - private void DAD(ushort w) { //-0HC + private void DAD(ushort w) + { //-0HC int result = HL + w; FlagN = false; SetFlagH(HL, w); //Special Flag H with word @@ -884,64 +952,86 @@ class CPU { // Sharp LR35902 CPU HL = (ushort)result; } - private void RETURN(bool flag) { - if (flag) { + private void RETURN(bool flag) + { + if (flag) + { PC = POP(); cycles += Cycles.RETURN_TRUE; - } else { + } + else + { cycles += Cycles.RETURN_FALSE; } } - private void CALL(bool flag) { - if (flag) { + private void CALL(bool flag) + { + if (flag) + { PUSH((ushort)(PC + 2)); PC = mmu.readWord(PC); cycles += Cycles.CALL_TRUE; - } else { + } + else + { PC += 2; cycles += Cycles.CALL_FALSE; } } - private void JUMP(bool flag) { - if (flag) { + private void JUMP(bool flag) + { + if (flag) + { PC = mmu.readWord(PC); cycles += Cycles.JUMP_TRUE; - } else { + } + else + { PC += 2; cycles += Cycles.JUMP_FALSE; } } - private void RST(byte b) { + private void RST(byte b) + { PUSH(PC); PC = b; } - private void HALT() { - if (!IME) { - if ((mmu.IE & mmu.IF & 0x1F) == 0) { + private void HALT() + { + if (!IME) + { + if ((mmu.IE & mmu.IF & 0x1F) == 0) + { HALTED = true; PC--; - } else { + } + else + { HALT_BUG = true; } } } - public void UpdateIME() { + public void UpdateIME() + { IME |= IMEEnabler; IMEEnabler = false; } - public void ExecuteInterrupt(int b) { - if (HALTED) { + public void ExecuteInterrupt(int b) + { + if (HALTED) + { PC++; HALTED = false; } - if (IME) { + if (IME) + { PUSH(PC); PC = (ushort)(0x40 + (8 * b)); IME = false; @@ -949,12 +1039,14 @@ class CPU { // Sharp LR35902 CPU } } - private void PUSH(ushort w) {// (SP - 1) < -PC.hi; (SP - 2) < -PC.lo + private void PUSH(ushort w) + {// (SP - 1) < -PC.hi; (SP - 2) < -PC.lo SP -= 2; mmu.writeWord(SP, w); } - private ushort POP() { + private ushort POP() + { ushort ret = mmu.readWord(SP); SP += 2; //byte l = mmu.readByte(++SP); @@ -966,41 +1058,50 @@ class CPU { // Sharp LR35902 CPU return ret; } - private void SetFlagZ(int b) { + private void SetFlagZ(int b) + { FlagZ = (byte)b == 0; } - private void SetFlagC(int i) { + private void SetFlagC(int i) + { FlagC = (i >> 8) != 0; } - private void SetFlagH(byte b1, byte b2) { + private void SetFlagH(byte b1, byte b2) + { FlagH = ((b1 & 0xF) + (b2 & 0xF)) > 0xF; } - private void SetFlagH(ushort w1, ushort w2) { + private void SetFlagH(ushort w1, ushort w2) + { FlagH = ((w1 & 0xFFF) + (w2 & 0xFFF)) > 0xFFF; } - private void SetFlagHCarry(byte b1, byte b2) { + private void SetFlagHCarry(byte b1, byte b2) + { FlagH = ((b1 & 0xF) + (b2 & 0xF)) >= 0xF; } - private void SetFlagHSub(byte b1, byte b2) { + private void SetFlagHSub(byte b1, byte b2) + { FlagH = (b1 & 0xF) < (b2 & 0xF); } - private void SetFlagHSubCarry(byte b1, byte b2) { + private void SetFlagHSubCarry(byte b1, byte b2) + { int carry = FlagC ? 1 : 0; FlagH = (b1 & 0xF) < ((b2 & 0xF) + carry); } - private void warnUnsupportedOpcode(byte opcode) { + private void warnUnsupportedOpcode(byte opcode) + { Console.WriteLine((PC - 1).ToString("x4") + " Unsupported operation " + opcode.ToString("x2")); } private int dev; - private void debug(byte opcode) { + private void debug(byte opcode) + { dev += cycles; if (dev >= 23440108 /*&& PC == 0x35A*/) //0x100 23440108 Console.WriteLine("Cycle " + dev + " PC " + (PC - 1).ToString("x4") + " Stack: " + SP.ToString("x4") + " AF: " + A.ToString("x2") + "" + F.ToString("x2") @@ -1008,6 +1109,26 @@ class CPU { // Sharp LR35902 CPU + " op " + opcode.ToString("x2") + " D16 " + mmu.readWord(PC).ToString("x4") + " LY: " + mmu.LY.ToString("x2")); } - + internal CPUSavedState CreateSaveState() + { + return new CPUSavedState() + { + A = A, + B = B, + C = C, + D = D, + E = E, + F = F, + H = H, + L = L, + IME = IME, + PC = PC, + SP = SP, + IMEEnabler = IMEEnabler, + HALTED = HALTED, + HALT_BUG = HALT_BUG, + cycles = cycles, + }; + } } } \ No newline at end of file diff --git a/ProjectDMG/DMG/GamePak/IGamePak.cs b/ProjectDMG/DMG/GamePak/IGamePak.cs index 98e505e..f2436a2 100644 --- a/ProjectDMG/DMG/GamePak/IGamePak.cs +++ b/ProjectDMG/DMG/GamePak/IGamePak.cs @@ -1,10 +1,13 @@ -namespace ProjectDMG.DMG.GamePak { +using ProjectDMG.DMG.State.DataStructures.GamePak; + +namespace ProjectDMG.DMG.GamePak { interface IGamePak { byte ReadLoROM(ushort addr); byte ReadHiROM(ushort addr); void WriteROM(ushort addr, byte value); byte ReadERAM(ushort addr); void WriteERAM(ushort addr, byte value); - void Init(byte[] ROM); + void Init(byte[] ROM, GamePakSavedState savedState); + GamePakSavedState GetSavedState(); } } diff --git a/ProjectDMG/DMG/GamePak/MBC0.cs b/ProjectDMG/DMG/GamePak/MBC0.cs index e084747..a2f90f0 100644 --- a/ProjectDMG/DMG/GamePak/MBC0.cs +++ b/ProjectDMG/DMG/GamePak/MBC0.cs @@ -1,4 +1,6 @@ -namespace ProjectDMG.DMG.GamePak { +using ProjectDMG.DMG.State.DataStructures.GamePak; + +namespace ProjectDMG.DMG.GamePak { class MBC0 : IGamePak { private byte[] ROM; @@ -26,5 +28,15 @@ class MBC0 : IGamePak { public void WriteROM(ushort addr, byte value) { //MBC0 should ignore writes } + + public GamePakSavedState GetSavedState() + { + throw new System.NotImplementedException(); + } + + public void Init(byte[] ROM, GamePakSavedState savedState) + { + throw new System.NotImplementedException(); + } } } diff --git a/ProjectDMG/DMG/GamePak/MBC1.cs b/ProjectDMG/DMG/GamePak/MBC1.cs index 0f07525..3e2525c 100644 --- a/ProjectDMG/DMG/GamePak/MBC1.cs +++ b/ProjectDMG/DMG/GamePak/MBC1.cs @@ -1,4 +1,5 @@ -using System; +using ProjectDMG.DMG.State.DataStructures.GamePak; +using System; namespace ProjectDMG.DMG.GamePak { class MBC1 : IGamePak { @@ -64,5 +65,15 @@ class MBC1 : IGamePak { break; } } + + public GamePakSavedState GetSavedState() + { + throw new NotImplementedException(); + } + + public void Init(byte[] ROM, GamePakSavedState savedState) + { + throw new NotImplementedException(); + } } } diff --git a/ProjectDMG/DMG/GamePak/MBC2.cs b/ProjectDMG/DMG/GamePak/MBC2.cs index b2edada..c338779 100644 --- a/ProjectDMG/DMG/GamePak/MBC2.cs +++ b/ProjectDMG/DMG/GamePak/MBC2.cs @@ -1,4 +1,5 @@ -using System; +using ProjectDMG.DMG.State.DataStructures.GamePak; +using System; namespace ProjectDMG.DMG.GamePak { class MBC2 : IGamePak { @@ -46,5 +47,14 @@ class MBC2 : IGamePak { } } + public GamePakSavedState GetSavedState() + { + throw new NotImplementedException(); + } + + public void Init(byte[] ROM, GamePakSavedState savedState) + { + throw new NotImplementedException(); + } } } diff --git a/ProjectDMG/DMG/GamePak/MBC3.cs b/ProjectDMG/DMG/GamePak/MBC3.cs index 6d4291b..27cc569 100644 --- a/ProjectDMG/DMG/GamePak/MBC3.cs +++ b/ProjectDMG/DMG/GamePak/MBC3.cs @@ -1,12 +1,13 @@ -using System; +using ProjectDMG.DMG.State.DataStructures.GamePak; +using System; namespace ProjectDMG.DMG.GamePak { class MBC3 : IGamePak { private byte[] ROM; - private byte[] ERAM = new byte[0x8000]; //MBC1 MAX ERAM on 4 banks + private byte[] ERAM; //MBC1 MAX ERAM on 4 banks private bool ERAM_ENABLED; - private int ROM_BANK = 1; //default as 0 is 0x0000 - 0x3FFF fixed + private int ROM_BANK; private int RAM_BANK; private const int ROM_OFFSET = 0x4000; private const int ERAM_OFFSET = 0x2000; @@ -20,8 +21,29 @@ class MBC3 : IGamePak { private byte RTC_6; //Bit 6 Halt(0=Active, 1=Stop Timer) private byte RTC_7; //Bit 7 Day Counter Carry Bit(1=Counter Overflow) - public void Init(byte[] ROM) { + public void Init(byte[] ROM, GamePakSavedState savedState) { this.ROM = ROM; + + if(savedState is MBC3SavedState mBC3SavedState) + { + ERAM = mBC3SavedState.ERAM; + ERAM_ENABLED = mBC3SavedState.ERAM_ENABLED; + ROM_BANK = mBC3SavedState.ROM_BANK; + RAM_BANK = mBC3SavedState.RAM_BANK; + RTC_0 = mBC3SavedState.RTC_0; + RTC_6 = mBC3SavedState.RTC_6; + RTC_7 = mBC3SavedState.RTC_7; + RTC_DH = mBC3SavedState.RTC_DH; + RTC_DL = mBC3SavedState.RTC_DL; + RTC_H = mBC3SavedState.RTC_H; + RTC_M = mBC3SavedState.RTC_M; + RTC_S = mBC3SavedState.RTC_S; + } + else + { + ERAM = new byte[0x8000]; + ROM_BANK = 1; //default as 0 is 0x0000 - 0x3FFF fixed + } } public byte ReadERAM(ushort addr) { @@ -109,5 +131,23 @@ class MBC3 : IGamePak { } } + public GamePakSavedState GetSavedState() + { + return new MBC3SavedState() + { + ERAM = ERAM, + ERAM_ENABLED = ERAM_ENABLED, + ROM_BANK = ROM_BANK, + RAM_BANK = RAM_BANK, + RTC_S = RTC_S, + RTC_M = RTC_M, + RTC_H = RTC_H, + RTC_DL = RTC_DL, + RTC_DH = RTC_DH, + RTC_0 = RTC_0, + RTC_6 = RTC_6, + RTC_7 = RTC_7 + }; + } } } diff --git a/ProjectDMG/DMG/GamePak/MBC5.cs b/ProjectDMG/DMG/GamePak/MBC5.cs index 471e3a9..2fce0d8 100644 --- a/ProjectDMG/DMG/GamePak/MBC5.cs +++ b/ProjectDMG/DMG/GamePak/MBC5.cs @@ -1,4 +1,5 @@ -using System; +using ProjectDMG.DMG.State.DataStructures.GamePak; +using System; namespace ProjectDMG.DMG.GamePak { class MBC5 : IGamePak { @@ -55,5 +56,14 @@ class MBC5 : IGamePak { } } + public GamePakSavedState GetSavedState() + { + throw new NotImplementedException(); + } + + public void Init(byte[] ROM, GamePakSavedState savedState) + { + throw new NotImplementedException(); + } } } diff --git a/ProjectDMG/DMG/MMU.cs b/ProjectDMG/DMG/MMU.cs index cef1096..230c252 100644 --- a/ProjectDMG/DMG/MMU.cs +++ b/ProjectDMG/DMG/MMU.cs @@ -1,23 +1,27 @@ using ProjectDMG.Api; using ProjectDMG.DMG.GamePak; +using ProjectDMG.DMG.State.DataStructures; +using ProjectDMG.DMG.State.DataStructures.GamePak; using System; using System.IO; using System.Runtime.CompilerServices; using static ProjectDMG.Utils.BitOps; -namespace ProjectDMG { - public class MMU { +namespace ProjectDMG +{ + public class MMU + { //GamePak private IGamePak gamePak; //DMG Memory Map - private byte[] VRAM = new byte[0x2000]; - private byte[] WRAM0 = new byte[0x1000]; - private byte[] WRAM1 = new byte[0x1000]; - private byte[] OAM = new byte[0xA0]; - private byte[] IO = new byte[0x80]; - private byte[] HRAM = new byte[0x80]; + private byte[] VRAM; + private byte[] WRAM0; + private byte[] WRAM1; + private byte[] OAM; + private byte[] IO; + private byte[] HRAM; private readonly IMemoryWatcher _memoryWatcher; //Timer IO Regs @@ -56,36 +60,64 @@ public class MMU { public byte JOYP { get { return IO[0x00]; } set { IO[0x00] = value; } }//FF00 - JOYP - public MMU(IMemoryWatcher memoryWatcher) { - //FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch - //HardCoded to FF to identify DMG as 00 is GBC - IO[0x4D] = 0xFF; + internal MMU(IMemoryWatcher memoryWatcher, MMUSavedState savedState = null) + { + if(savedState != null) + { + SetValuesFromState(savedState); + } + else + { + VRAM = new byte[0x2000]; + WRAM0 = new byte[0x1000]; + WRAM1 = new byte[0x1000]; + OAM = new byte[0xA0]; + IO = new byte[0x80]; + HRAM = new byte[0x80]; + + //FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch + //HardCoded to FF to identify DMG as 00 is GBC + IO[0x4D] = 0xFF; + + IO[0x10] = 0x80; + IO[0x11] = 0xBF; + IO[0x12] = 0xF3; + IO[0x14] = 0xBF; + IO[0x16] = 0x3F; + IO[0x19] = 0xBF; + IO[0x1A] = 0x7F; + IO[0x1B] = 0xFF; + IO[0x1C] = 0x9F; + IO[0x1E] = 0xBF; + IO[0x20] = 0xFF; + IO[0x23] = 0xBF; + IO[0x24] = 0x77; + IO[0x25] = 0xF3; + IO[0x26] = 0xF1; + IO[0x40] = 0x91; + IO[0x47] = 0xFC; + IO[0x48] = 0xFF; + IO[0x49] = 0xFF; + } - IO[0x10] = 0x80; - IO[0x11] = 0xBF; - IO[0x12] = 0xF3; - IO[0x14] = 0xBF; - IO[0x16] = 0x3F; - IO[0x19] = 0xBF; - IO[0x1A] = 0x7F; - IO[0x1B] = 0xFF; - IO[0x1C] = 0x9F; - IO[0x1E] = 0xBF; - IO[0x20] = 0xFF; - IO[0x23] = 0xBF; - IO[0x24] = 0x77; - IO[0x25] = 0xF3; - IO[0x26] = 0xF1; - IO[0x40] = 0x91; - IO[0x47] = 0xFC; - IO[0x48] = 0xFF; - IO[0x49] = 0xFF; _memoryWatcher = memoryWatcher; } - public byte readByte(ushort addr) { - switch (addr) { // General Memory Map 64KB + private void SetValuesFromState(MMUSavedState savedState) + { + VRAM = savedState.VRAM; + WRAM0 = savedState.WRAM0; + WRAM1 = savedState.WRAM1; + OAM = savedState.OAM; + IO = savedState.IO; + HRAM = savedState.HRAM; + } + + public byte readByte(ushort addr) + { + switch (addr) + { // General Memory Map 64KB case ushort _ when addr <= 0x3FFF: //0000-3FFF 16KB ROM Bank 00 (in cartridge, private at bank 00) return gamePak.ReadLoROM(addr); case ushort _ when addr <= 0x7FFF: // 4000-7FFF 16KB ROM Bank 01..NN(in cartridge, switchable bank number) @@ -113,46 +145,12 @@ public class MMU { default: return 0xFF; } - - //tests to simplify reads... somehow they are slower :\ - //ushort add = (ushort)(addr >> 12); - //switch (add) { - // case 0x0: - // case 0x1: - // case 0x2: - // case 0x3:return gamePak.ReadLoROM(addr); - // case 0x4: - // case 0x5: - // case 0x6: - // case 0x7: return gamePak.ReadHiROM(addr); - // case 0x8: - // case 0x9: return VRAM[addr & 0x1FFF]; - // case 0xA: - // case 0xB: return gamePak.ReadERAM(addr); - // case 0xC: return WRAM0[addr & 0xFFF]; - // case 0xD: return WRAM1[addr & 0xFFF]; - // case 0xE: return WRAM0[addr & 0xFFF]; - // case 0xF: - // switch (addr) { - // case ushort _ when addr <= 0xFDFF: // E000-FDFF Same as 0xC000-DDFF(ECHO) - // return WRAM1[addr & 0xFFF]; - // case ushort _ when addr <= 0xFE9F: // FE00-FE9F Sprite Attribute Table(OAM) - // return OAM[addr - 0xFE00]; - // case ushort _ when addr <= 0xFEFF: // FEA0-FEFF Not Usable 0 - // return 0x00; - // case ushort _ when addr <= 0xFF7F: // FF00-FF7F IO Ports - // return IO[addr & 0x7F]; - // case ushort _ when addr <= 0xFFFF: // FF80-FFFE High RAM(HRAM) - // return HRAM[addr & 0x7F]; - // default: - // return 0xFF; - // } - // default: return 0xFF; - //} } - public void writeByte(ushort addr, byte b) { - switch (addr) { // General Memory Map 64KB + public void writeByte(ushort addr, byte b) + { + switch (addr) + { // General Memory Map 64KB case ushort _ when addr <= 0x7FFF: //0000-3FFF 16KB ROM Bank 00 (in cartridge, private at bank 00) 4000-7FFF 16KB ROM Bank 01..NN(in cartridge, switchable bank number) gamePak.WriteROM(addr, b); break; @@ -181,7 +179,8 @@ public class MMU { //Console.WriteLine("Warning: Tried to write to NOT USABLE space"); break; case ushort _ when addr <= 0xFF7F: // FF00-FF7F IO Ports - switch (addr) { + switch (addr) + { case 0xFF0F: b |= 0xE0; break; // IF returns 1 on first 3 unused bits case 0xFF04: //DIV on write = 0 case 0xFF44: b = 0; break; //LY on write = 0 @@ -202,60 +201,84 @@ public class MMU { _memoryWatcher.OnMemoryUpdatedAsync(addr, b); } - public ushort readWord(ushort addr) { + public ushort readWord(ushort addr) + { return (ushort)(readByte((ushort)(addr + 1)) << 8 | readByte(addr)); } - public void writeWord(ushort addr, ushort w) { + public void writeWord(ushort addr, ushort w) + { writeByte((ushort)(addr + 1), (byte)(w >> 8)); writeByte(addr, (byte)w); } - public byte readOAM(int addr) { + public byte readOAM(int addr) + { return OAM[addr]; } - public byte readVRAM(int addr) { + public byte readVRAM(int addr) + { return VRAM[addr & 0x1FFF]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void requestInterrupt(byte b) { + public void requestInterrupt(byte b) + { IF = bitSet(b, IF); } - private void DMA(byte b) { + private void DMA(byte b) + { ushort addr = (ushort)(b << 8); - for (byte i = 0; i < OAM.Length; i++) { + for (byte i = 0; i < OAM.Length; i++) + { OAM[i] = readByte((ushort)(addr + i)); } } - public void loadGamePak(String cartName) { + // TODO implement saved state for other gamepaks + internal void loadGamePak(String cartName, GamePakSavedState savedState) + { byte[] rom = File.ReadAllBytes(cartName); - switch (rom[0x147]) { + switch (rom[0x147]) + { case 0x00: gamePak = new MBC0(); break; - case 0x01: case 0x02: case 0x03: + case 0x01: + case 0x02: + case 0x03: gamePak = new MBC1(); break; - case 0x05: case 0x06: + case 0x05: + case 0x06: gamePak = new MBC2(); break; - case 0x0F: case 0x10: case 0x11: case 0x12: case 0x13: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: gamePak = new MBC3(); break; - case 0x19: case 0x1A: case 0x1B: + case 0x19: + case 0x1A: + case 0x1B: gamePak = new MBC5(); break; default: Console.WriteLine("Unsupported MBC: " + rom[0x147].ToString("x2")); break; } - gamePak.Init(rom); + gamePak.Init(rom, savedState); } + internal MMUSavedState CreateSaveState() + => new(VRAM, WRAM0, WRAM1, OAM, IO, HRAM); + + internal GamePakSavedState CreateGamePakSaveState() + => gamePak.GetSavedState(); } } diff --git a/ProjectDMG/DMG/PPU.cs b/ProjectDMG/DMG/PPU.cs index 128487a..2ed8541 100644 --- a/ProjectDMG/DMG/PPU.cs +++ b/ProjectDMG/DMG/PPU.cs @@ -1,8 +1,10 @@ -using System.Runtime.CompilerServices; +using ProjectDMG.DMG.State.DataStructures; +using System; +using System.Runtime.CompilerServices; using static ProjectDMG.Utils.BitOps; namespace ProjectDMG { - public class PPU { + public class PPU : IDisposable { private const int SCREEN_WIDTH = 160; private const int SCREEN_HEIGHT = 144; @@ -22,9 +24,14 @@ public class PPU { private Form window; - public PPU(Form window) { + internal PPU(Form window, PPUSavedState savedState) { this.window = window; - bmp = new DirectBitmap(); + if(savedState != null) + { + scanlineCounter = savedState.scanlineCounter; + } + + bmp = new DirectBitmap(savedState?.Bits); window.pictureBox.Image = bmp.Bitmap; } @@ -289,5 +296,17 @@ public class PPU { //Bit 5 - Window Display Enable (0=Off, 1=On) return isBit(5, LCDC) && WY <= LY; } + + internal PPUSavedState CreateSaveState() + => new() + { + Bits = bmp.Bits, + scanlineCounter = scanlineCounter + }; + + public void Dispose() + { + bmp.Dispose(); + } } } \ No newline at end of file diff --git a/ProjectDMG/DMG/State/DataStructures/CPUSavedState.cs b/ProjectDMG/DMG/State/DataStructures/CPUSavedState.cs new file mode 100644 index 0000000..48cacec --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/CPUSavedState.cs @@ -0,0 +1,39 @@ +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures +{ + [ProtoContract] + public class CPUSavedState + { + [ProtoMember(1)] + public byte A { get; set; } + [ProtoMember(2)] + public byte B { get; set; } + [ProtoMember(3)] + public byte C { get; set; } + [ProtoMember(4)] + public byte D { get; set; } + [ProtoMember(5)] + public byte E { get; set; } + [ProtoMember(6)] + public byte F { get; set; } + [ProtoMember(7)] + public byte H { get; set; } + [ProtoMember(8)] + public byte L { get; set; } + [ProtoMember(9)] + public ushort PC { get; set; } + [ProtoMember(10)] + public ushort SP { get; set; } + [ProtoMember(11)] + public bool IME { get; set; } + [ProtoMember(12)] + public bool IMEEnabler { get; set; } + [ProtoMember(13)] + public bool HALTED { get; set; } + [ProtoMember(14)] + public bool HALT_BUG { get; set; } + [ProtoMember(15)] + public int cycles { get; set; } + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/GamePak/GamePakSavedState.cs b/ProjectDMG/DMG/State/DataStructures/GamePak/GamePakSavedState.cs new file mode 100644 index 0000000..19d850f --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/GamePak/GamePakSavedState.cs @@ -0,0 +1,10 @@ +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures.GamePak +{ + [ProtoContract()] + [ProtoInclude(100, typeof(MBC3SavedState))] + internal abstract class GamePakSavedState + { + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/GamePak/MBC3SavedState.cs b/ProjectDMG/DMG/State/DataStructures/GamePak/MBC3SavedState.cs new file mode 100644 index 0000000..59c9aa8 --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/GamePak/MBC3SavedState.cs @@ -0,0 +1,33 @@ +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures.GamePak +{ + [ProtoContract] + internal class MBC3SavedState : GamePakSavedState + { + [ProtoMember(2)] + public byte[] ERAM { get; set; } + [ProtoMember(3)] + public bool ERAM_ENABLED { get; set; } + [ProtoMember(4)] + public int ROM_BANK { get; set; } + [ProtoMember(5)] + public int RAM_BANK { get; set; } + [ProtoMember(6)] + public byte RTC_S { get; set; } + [ProtoMember(7)] + public byte RTC_M { get; set; } + [ProtoMember(8)] + public byte RTC_H { get; set; } + [ProtoMember(9)] + public byte RTC_DL { get; set; } + [ProtoMember(10)] + public byte RTC_DH { get; set; } + [ProtoMember(11)] + public byte RTC_0 { get; set; } + [ProtoMember(12)] + public byte RTC_6 { get; set; } + [ProtoMember(13)] + public byte RTC_7 { get; set; } + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/MMUSavedState.cs b/ProjectDMG/DMG/State/DataStructures/MMUSavedState.cs new file mode 100644 index 0000000..a48969d --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/MMUSavedState.cs @@ -0,0 +1,37 @@ +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures +{ + [ProtoContract] + internal class MMUSavedState + { + [ProtoMember(1)] + public byte[] VRAM { get; set; } + [ProtoMember(2)] + public byte[] WRAM0 { get; set; } + [ProtoMember(3)] + public byte[] WRAM1 { get; set; } + [ProtoMember(4)] + public byte[] OAM { get; set; } + [ProtoMember(5)] + public byte[] IO { get; set; } + [ProtoMember(6)] + public byte[] HRAM { get; set; } + + // Used by the serializer + public MMUSavedState() + { + + } + + public MMUSavedState(byte[] vram, byte[] wram0, byte[] wram1, byte[] oam, byte[] io, byte[] hram) + { + VRAM = vram; + WRAM0 = wram0; + WRAM1 = wram1; + OAM = oam; + IO = io; + HRAM = hram; + } + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/PPUSavedState.cs b/ProjectDMG/DMG/State/DataStructures/PPUSavedState.cs new file mode 100644 index 0000000..0cac8bc --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/PPUSavedState.cs @@ -0,0 +1,14 @@ +using ProtoBuf; +using System; + +namespace ProjectDMG.DMG.State.DataStructures +{ + [ProtoContract] + internal class PPUSavedState + { + [ProtoMember(1)] + public int scanlineCounter { get; set; } + [ProtoMember(2)] + public Int32[] Bits { get; set; } + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/ProjectDMGSavedState.cs b/ProjectDMG/DMG/State/DataStructures/ProjectDMGSavedState.cs new file mode 100644 index 0000000..e199f24 --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/ProjectDMGSavedState.cs @@ -0,0 +1,12 @@ +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures +{ + [ProtoContract] + internal class ProjectDMGSavedState + { + [ProtoMember(1)] + public int cyclesThisUpdate { get; set; } + + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/SavedState.cs b/ProjectDMG/DMG/State/DataStructures/SavedState.cs new file mode 100644 index 0000000..4374ec3 --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/SavedState.cs @@ -0,0 +1,27 @@ +using ProjectDMG.DMG.State.DataStructures.GamePak; +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures +{ + [ProtoContract] + internal class SavedState + { + [ProtoMember(1)] + internal MMUSavedState MMUSavedState { get; set; } + [ProtoMember(2)] + internal CPUSavedState CPUSavedState { get; set; } + [ProtoMember(3)] + internal PPUSavedState PPUSavedState { get; set; } + [ProtoMember(4)] + internal GamePakSavedState GamePakSavedState { get; set; } + [ProtoMember(5)] + public TimerSavedState TimerSavedState { get; set; } + [ProtoMember(6)] + public ProjectDMGSavedState ProjectDMGSavedState { get; set; } + + public SavedState() + { + + } + } +} diff --git a/ProjectDMG/DMG/State/DataStructures/TimerSavedState.cs b/ProjectDMG/DMG/State/DataStructures/TimerSavedState.cs new file mode 100644 index 0000000..d160d8f --- /dev/null +++ b/ProjectDMG/DMG/State/DataStructures/TimerSavedState.cs @@ -0,0 +1,13 @@ +using ProtoBuf; + +namespace ProjectDMG.DMG.State.DataStructures +{ + [ProtoContract] + internal class TimerSavedState + { + [ProtoMember(1)] + public int divCounter{get;set;} + [ProtoMember(2)] + public int timerCounter { get; set; } + } +} diff --git a/ProjectDMG/DMG/State/SaveStateManager.cs b/ProjectDMG/DMG/State/SaveStateManager.cs new file mode 100644 index 0000000..327486b --- /dev/null +++ b/ProjectDMG/DMG/State/SaveStateManager.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; +using ProjectDMG.DMG.State.DataStructures; + +namespace ProjectDMG.DMG.State +{ + internal class SaveStateManager + { + public SaveStateManager() + { + if (!Directory.Exists(GetDefaultPath())) + { + Directory.CreateDirectory(GetDefaultPath()); + } + } + + internal void GenerateSaveState(MMU mmu, CPU cpu, PPU ppu,TIMER timer, ProjectDMGSavedState projectDMGSavedState, string fileName) + { + var filePath = GetPath(fileName); + var mmuState = mmu.CreateSaveState(); + var cpuState = cpu.CreateSaveState(); + var ppuState = ppu.CreateSaveState(); + var gamePakState = mmu.CreateGamePakSaveState(); + var timerState = timer.CreateSaveState(); + var state = new SavedState() + { + CPUSavedState = cpuState, + MMUSavedState = mmuState, + PPUSavedState = ppuState, + GamePakSavedState = gamePakState, + TimerSavedState = timerState, + ProjectDMGSavedState = projectDMGSavedState, + }; + SaveStateSerializer.Serialize(filePath, state); + } + + internal SavedState LoadSavedState(string fileName) + { + var filePath = GetPath(fileName); + return SaveStateSerializer.Deserialize(filePath); + } + + private string GetDefaultPath() + => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SaveStates"); + + private string GetPath(string fileName) + => Path.Combine(GetDefaultPath(), fileName + ".st"); + } +} diff --git a/ProjectDMG/DMG/State/SaveStateSerializer.cs b/ProjectDMG/DMG/State/SaveStateSerializer.cs new file mode 100644 index 0000000..de3c7a6 --- /dev/null +++ b/ProjectDMG/DMG/State/SaveStateSerializer.cs @@ -0,0 +1,24 @@ +using System.IO; +using ProtoBuf; + +namespace ProjectDMG.DMG.State +{ + internal static class SaveStateSerializer + { + public static void Serialize(string filePath, T obj) where T : class + { + using (FileStream fs = new FileStream(filePath, FileMode.Create)) + { + Serializer.Serialize(fs, obj); + } + } + + public static T Deserialize(string filePath) where T : class + { + using (FileStream fs = new FileStream(filePath, FileMode.Open)) + { + return Serializer.Deserialize(fs); + } + } + } +} diff --git a/ProjectDMG/DMG/TIMER.cs b/ProjectDMG/DMG/TIMER.cs index 88bf843..1c8d074 100644 --- a/ProjectDMG/DMG/TIMER.cs +++ b/ProjectDMG/DMG/TIMER.cs @@ -1,7 +1,10 @@ -using System; +using ProjectDMG.DMG.State.DataStructures; +using System; -namespace ProjectDMG { - public class TIMER { +namespace ProjectDMG +{ + public class TIMER + { private const int DMG_DIV_FREQ = 256; //16384Hz private const int CGB_DIV_FREQ = DMG_DIV_FREQ * 2; //32768Hz @@ -15,32 +18,54 @@ public class TIMER { private int divCounter; private int timerCounter; - public void update(int cycles, MMU mmu) { + internal TIMER(TimerSavedState savedState) + { + if(savedState != null) + { + timerCounter = savedState.timerCounter; + divCounter = savedState.divCounter; + } + } + + public void update(int cycles, MMU mmu) + { handleDivider(cycles, mmu); handleTimer(cycles, mmu); } - private void handleDivider(int cycles, MMU mmu) { + private void handleDivider(int cycles, MMU mmu) + { divCounter += cycles; - while (divCounter >= DMG_DIV_FREQ) { + while (divCounter >= DMG_DIV_FREQ) + { mmu.DIV++; divCounter -= DMG_DIV_FREQ; } } - private void handleTimer(int cycles, MMU mmu) { - if (mmu.TAC_ENABLED) { + private void handleTimer(int cycles, MMU mmu) + { + if (mmu.TAC_ENABLED) + { timerCounter += cycles; - while (timerCounter >= TAC_FREQ[mmu.TAC_FREQ]) { + while (timerCounter >= TAC_FREQ[mmu.TAC_FREQ]) + { mmu.TIMA++; timerCounter -= TAC_FREQ[mmu.TAC_FREQ]; } - if (mmu.TIMA == 0xFF) { + if (mmu.TIMA == 0xFF) + { mmu.requestInterrupt(TIMER_INTERRUPT); mmu.TIMA = mmu.TMA; } } } + internal TimerSavedState CreateSaveState() + => new() + { + timerCounter = timerCounter, + divCounter = divCounter, + }; } } \ No newline at end of file diff --git a/ProjectDMG/GUI/Form.cs b/ProjectDMG/GUI/Form.cs index e9a1949..b1d92df 100644 --- a/ProjectDMG/GUI/Form.cs +++ b/ProjectDMG/GUI/Form.cs @@ -1,10 +1,12 @@ using System; +using System.Threading; using System.Windows.Forms; namespace ProjectDMG { public partial class Form : System.Windows.Forms.Form { ProjectDMG dmg; + private string romPath = "G:\\Desenvolvimento\\CSharp\\ProjectDMG\\Roms\\PokemonRed.gb"; public Form() { InitializeComponent(); @@ -12,13 +14,65 @@ public partial class Form : System.Windows.Forms.Form { private void Form_Load(object sender, EventArgs e) { dmg = new ProjectDMG(this); - dmg.POWER_ON("G:\\Desenvolvimento\\CSharp\\ProjectDMG\\Roms\\PokemonRed.gb"); + dmg.POWER_ON(romPath); } private void Key_Down(object sender, KeyEventArgs e) { + if(e.Control) + { + if(e.Shift) + { + HandleCtrlShiftCommand(e); + return; + } + + HandleCtrlCommand(e); + return; + } + if (dmg.power_switch) dmg.joypad.handleKeyDown(e); } + private void HandleCtrlCommand(KeyEventArgs e) + { + switch(e.KeyCode) + { + case Keys.D1: + case Keys.D2: + case Keys.D3: + SaveState(e.KeyCode.ToString()); + break; + } + } + + private void HandleCtrlShiftCommand(KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.D1: + case Keys.D2: + case Keys.D3: + LoadState(e.KeyCode.ToString()); + break; + } + } + + private void LoadState(string fileName) + { + dmg.POWER_OFF(); + var state = dmg.LoadSavedState(fileName); + + while (dmg.IsRunning) + Thread.Sleep(100); + + dmg.POWER_ON(romPath, state); + } + + private void SaveState(string fileName) + { + dmg.GenerateSaveState(fileName); + } + private void Key_Up(object sender, KeyEventArgs e) { if (dmg.power_switch) dmg.joypad.handleKeyUp(e); } diff --git a/ProjectDMG/PluginLoader.cs b/ProjectDMG/PluginLoader.cs new file mode 100644 index 0000000..d5158ce --- /dev/null +++ b/ProjectDMG/PluginLoader.cs @@ -0,0 +1,41 @@ +using ProjectDMG.Api; +using System; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace ProjectDMG +{ + internal static class PluginLoader + { + internal static void Load() + { + var pluginInterface = typeof(ProjectDMGPlugin); + var solutionFolder = Environment.CurrentDirectory.Substring(0, Environment.CurrentDirectory.IndexOf("ProjectDMG\\bin")); + var pluginsFolder = Path.Combine(solutionFolder, "PluginsDlls"); + + if (Directory.Exists(pluginsFolder)) + { + var dllFiles = Directory.GetFiles(pluginsFolder, "*.dll"); + + foreach (string dllFile in dllFiles) + { + try + { + var assembly = Assembly.LoadFrom(dllFile); + foreach (var pluginType in assembly.GetTypes().Where(t => pluginInterface.IsAssignableFrom(t) && !t.IsAbstract)) + ((ProjectDMGPlugin)Activator.CreateInstance(pluginType)).Run(); + } + catch (Exception ex) + { + Console.WriteLine($"Error loading assembly {Path.GetFileName(dllFile)}: {ex.Message}"); + } + } + } + else + { + Console.WriteLine("Plugins folder not found."); + } + } + } +} diff --git a/ProjectDMG/ProjectDMG.cs b/ProjectDMG/ProjectDMG.cs index e6b72e4..cc804ff 100644 --- a/ProjectDMG/ProjectDMG.cs +++ b/ProjectDMG/ProjectDMG.cs @@ -1,4 +1,6 @@ using ProjectDMG.Api; +using ProjectDMG.DMG.State; +using ProjectDMG.DMG.State.DataStructures; using ProjectDMG.Utils; using System; using System.Collections.Generic; @@ -6,14 +8,18 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; -namespace ProjectDMG { - public class ProjectDMG { +namespace ProjectDMG +{ + public class ProjectDMG + { Form window; - public ProjectDMG(Form window) { + public ProjectDMG(Form window) + { this.window = window; } @@ -22,81 +28,70 @@ public class ProjectDMG { private PPU ppu; private TIMER timer; public JOYPAD joypad; + private SaveStateManager saveStateManager; public bool power_switch; + private int cpuCycles; + private int cyclesThisUpdate; + private object saveLock = new object(); - public void POWER_ON(string cartName) { - mmu = new MMU(MemoryWatcherProvider.GetInstance()); - cpu = new CPU(mmu); - ppu = new PPU(window); - timer = new TIMER(); - joypad = new JOYPAD(); + public bool IsRunning { get; private set; } - mmu.loadGamePak(cartName); + internal void POWER_ON(string cartName) + => POWER_ON(cartName, null); - LoadPlugins(); + internal void POWER_ON(string cartName, SavedState state) + { + mmu = new MMU(MemoryWatcherProvider.GetInstance(), state?.MMUSavedState); + cpu = new CPU(mmu, state?.CPUSavedState); + ppu = new PPU(window, state?.PPUSavedState); + timer = new TIMER(state?.TimerSavedState); + joypad = new JOYPAD(); + saveStateManager = new SaveStateManager(); - power_switch = true; + mmu.loadGamePak(cartName, state?.GamePakSavedState); - Task t = Task.Factory.StartNew(EXECUTE, TaskCreationOptions.LongRunning); - } + PluginLoader.Load(); - private void LoadPlugins() - { - var pluginInterface = typeof(ProjectDMGPlugin); - var solutionFolder = Environment.CurrentDirectory.Substring(0, Environment.CurrentDirectory.IndexOf("ProjectDMG\\bin")); - var pluginsFolder = Path.Combine(solutionFolder, "PluginsDlls"); + power_switch = true; - if (Directory.Exists(pluginsFolder)) + if (state != null) { - var dllFiles = Directory.GetFiles(pluginsFolder, "*.dll"); - - foreach (string dllFile in dllFiles) - { - try - { - var assembly = Assembly.LoadFrom(dllFile); - foreach (var pluginType in assembly.GetTypes().Where(t => pluginInterface.IsAssignableFrom(t) && !t.IsAbstract)) - ((ProjectDMGPlugin)Activator.CreateInstance(pluginType)).Run(); - } - catch (Exception ex) - { - Console.WriteLine($"Error loading assembly {Path.GetFileName(dllFile)}: {ex.Message}"); - } - } - } - else - { - Console.WriteLine("Plugins folder not found."); + cyclesThisUpdate = state.ProjectDMGSavedState.cyclesThisUpdate; } + + Task t = Task.Factory.StartNew(EXECUTE, TaskCreationOptions.LongRunning); } - public void POWER_OFF() { + public void POWER_OFF() + { power_switch = false; } int fpsCounter; - public void EXECUTE() { + public void EXECUTE() + { // Main Loop Work in progress long start = nanoTime(); - long elapsed = 0; - int cpuCycles = 0; - int cyclesThisUpdate = 0; var timerCounter = new Stopwatch(); timerCounter.Start(); + IsRunning = true; - while (power_switch) { - if (timerCounter.ElapsedMilliseconds > 1000) { - //window.Text = "ProjectDMG | FPS: " + fpsCounter; - timerCounter.Restart(); - fpsCounter = 0; - } + while (power_switch) + { + lock (saveLock) + { + if (timerCounter.ElapsedMilliseconds > 1000) + { + //window.Text = "ProjectDMG | FPS: " + fpsCounter; + timerCounter.Restart(); + fpsCounter = 0; + } - //if ((elapsed - start) >= 16740000) { //nanoseconds per frame - // start += 16740000; - while (cyclesThisUpdate < Constants.CYCLES_PER_UPDATE) { + while (cyclesThisUpdate < Constants.CYCLES_PER_UPDATE) + { cpuCycles = cpu.Exe(); cyclesThisUpdate += cpuCycles; @@ -107,20 +102,22 @@ private void LoadPlugins() } fpsCounter++; cyclesThisUpdate -= Constants.CYCLES_PER_UPDATE; - //} - - //elapsed = nanoTime(); - //if ((elapsed - start) < 15000000) { - // Thread.Sleep(1); - //} + } } + + ppu.Dispose(); + + IsRunning = false; } - private void handleInterrupts() { + private void handleInterrupts() + { byte IE = mmu.IE; byte IF = mmu.IF; - for (int i = 0; i < 5; i++) { - if ((((IE & IF) >> i) & 0x1) == 1) { + for (int i = 0; i < 5; i++) + { + if ((((IE & IF) >> i) & 0x1) == 1) + { cpu.ExecuteInterrupt(i); } } @@ -128,12 +125,30 @@ private void LoadPlugins() cpu.UpdateIME(); } - private static long nanoTime() { + private static long nanoTime() + { long nano = 10000L * Stopwatch.GetTimestamp(); nano /= TimeSpan.TicksPerMillisecond; nano *= 100L; return nano; } + internal void GenerateSaveState(string fileName) + { + lock (saveLock) + { + var dmgSaveState = new ProjectDMGSavedState() + { + cyclesThisUpdate = cyclesThisUpdate, + }; + + saveStateManager.GenerateSaveState(mmu, cpu, ppu, timer, dmgSaveState, fileName); + } + } + + internal SavedState LoadSavedState(string fileName) + { + return saveStateManager.LoadSavedState(fileName); + } } } diff --git a/ProjectDMG/ProjectDMG.csproj b/ProjectDMG/ProjectDMG.csproj index aa813bc..6a91c04 100644 --- a/ProjectDMG/ProjectDMG.csproj +++ b/ProjectDMG/ProjectDMG.csproj @@ -19,6 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/ProjectDMG/Utils/DirectBitmap.cs b/ProjectDMG/Utils/DirectBitmap.cs index 344eb39..1c303c9 100644 --- a/ProjectDMG/Utils/DirectBitmap.cs +++ b/ProjectDMG/Utils/DirectBitmap.cs @@ -14,8 +14,11 @@ public class DirectBitmap : IDisposable { protected GCHandle BitsHandle { get; private set; } - public DirectBitmap() { - Bits = new Int32[Width * Height]; + public DirectBitmap() : this(null) { } + + public DirectBitmap(Int32[] bits) + { + Bits = bits ?? new Int32[Width * Height]; BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned); Bitmap = new Bitmap(Width, Height, Width * 4, PixelFormat.Format32bppRgb, BitsHandle.AddrOfPinnedObject()); } diff --git a/README.md b/README.md index e1a6650..2ea526b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ Once power on, Input is mapped as: * B: **X** or **K** * Start: **V** or **Enter** * Select: **C** or **Space** +* Save state: **CTRL+1-3** +* Load state: **CTRL+Shift+1-3** ## Screenshots