diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index 42e9a75a89..b592f1696b 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -112,11 +112,12 @@ namespace BizHawk.Client.Common case "SNES": return SystemInfo.SNES; case "GB": + /* if ((Emulator as IGameboyCommon).IsCGBMode()) { return SystemInfo.GBC; } - + */ return SystemInfo.GB; case "A26": return SystemInfo.Atari2600; diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index d20e74a845..99e355081e 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -13,6 +13,7 @@ using BizHawk.Emulation.Cores.Computers.AppleII; using BizHawk.Emulation.Cores.Computers.Commodore64; using BizHawk.Emulation.Cores.Consoles.Sega.gpgx; using BizHawk.Emulation.Cores.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; using BizHawk.Emulation.Cores.Nintendo.SNES; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.Saturn; @@ -935,10 +936,36 @@ namespace BizHawk.Client.Common break; case "GB": - case "GBC": if (!Global.Config.GB_AsSGB) { - core = CoreInventory.Instance["GB", "Gambatte"]; + if (Global.Config.GB_UseGBHawk) + { + core = CoreInventory.Instance["GB", "GBHawk"]; + } + else + { + core = CoreInventory.Instance["GB", "Gambatte"]; + } + } + else + { + if (Global.Config.SGB_UseBsnes) + { + game.System = "SNES"; + game.AddOption("SGB"); + var snes = new LibsnesCore(game, rom.FileData, null, nextComm, GetCoreSettings(), GetCoreSyncSettings()); + nextEmulator = snes; + } + else + { + core = CoreInventory.Instance["SGB", "SameBoy"]; + } + } + break; + case "GBC": + if (!Global.Config.GB_AsSGB) + { + core = CoreInventory.Instance["GBC", "Gambatte"]; } else { diff --git a/BizHawk.Client.Common/config/Config.cs b/BizHawk.Client.Common/config/Config.cs index bcdeb39420..4dea2406f2 100644 --- a/BizHawk.Client.Common/config/Config.cs +++ b/BizHawk.Client.Common/config/Config.cs @@ -557,6 +557,7 @@ namespace BizHawk.Client.Common public bool SNES_InSnes9x = true; public bool GBA_UsemGBA = true; public bool SGB_UseBsnes = false; + public bool GB_UseGBHawk = false; public bool CoreForcingViaGameDB = true; public string LibretroCore; } diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 9d6774638e..0e250d1e01 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -194,9 +194,12 @@ this.MgbaCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SGBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); - this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SgbSameBoyMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGambatteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGBHawkMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); this.allowGameDBCoreOverridesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); @@ -1810,7 +1813,8 @@ this.CoreSNESSubMenu, this.GbaCoreSubMenu, this.SGBCoreSubmenu, - this.GBInSGBMenuItem, + this.GBCoreSubmenu, + this.GBInSGBMenuItem, this.toolStripMenuItem16, this.allowGameDBCoreOverridesToolStripMenuItem, this.toolStripSeparator8, @@ -1908,10 +1912,20 @@ this.SGBCoreSubmenu.Size = new System.Drawing.Size(239, 22); this.SGBCoreSubmenu.Text = "SGB"; this.SGBCoreSubmenu.DropDownOpened += new System.EventHandler(this.SGBCoreSubmenu_DropDownOpened); - // - // SgbBsnesMenuItem - // - this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; + // + // GBCoreSubmenu + // + this.GBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.GBGambatteMenuItem, + this.GBGBHawkMenuItem}); + this.GBCoreSubmenu.Name = "GBCoreSubmenu"; + this.GBCoreSubmenu.Size = new System.Drawing.Size(239, 22); + this.GBCoreSubmenu.Text = "GB"; + this.GBCoreSubmenu.DropDownOpened += new System.EventHandler(this.GBCoreSubmenu_DropDownOpened); + // + // SgbBsnesMenuItem + // + this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; this.SgbBsnesMenuItem.Size = new System.Drawing.Size(152, 22); this.SgbBsnesMenuItem.Text = "BSNES"; this.SgbBsnesMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); @@ -1922,10 +1936,24 @@ this.SgbSameBoyMenuItem.Size = new System.Drawing.Size(152, 22); this.SgbSameBoyMenuItem.Text = "SameBoy"; this.SgbSameBoyMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); - // - // GBInSGBMenuItem - // - this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; + // + // GBGambatteMenuItem + // + this.GBGambatteMenuItem.Name = "GBGambatteMenuItem"; + this.GBGambatteMenuItem.Size = new System.Drawing.Size(152, 22); + this.GBGambatteMenuItem.Text = "Gambatte"; + this.GBGambatteMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBGBHawkMenuItem + // + this.GBGBHawkMenuItem.Name = "GBGBHawkMenuItem"; + this.GBGBHawkMenuItem.Size = new System.Drawing.Size(152, 22); + this.GBGBHawkMenuItem.Text = "GBHawk"; + this.GBGBHawkMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBInSGBMenuItem + // + this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; this.GBInSGBMenuItem.Size = new System.Drawing.Size(239, 22); this.GBInSGBMenuItem.Text = "GB in SGB"; this.GBInSGBMenuItem.Click += new System.EventHandler(this.GbInSgbMenuItem_Click); @@ -4407,6 +4435,9 @@ private System.Windows.Forms.ToolStripMenuItem SGBCoreSubmenu; private System.Windows.Forms.ToolStripMenuItem SgbBsnesMenuItem; private System.Windows.Forms.ToolStripMenuItem SgbSameBoyMenuItem; + private System.Windows.Forms.ToolStripMenuItem GBCoreSubmenu; + private System.Windows.Forms.ToolStripMenuItem GBGambatteMenuItem; + private System.Windows.Forms.ToolStripMenuItem GBGBHawkMenuItem; private System.Windows.Forms.ToolStripMenuItem pCFXToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem3; private System.Windows.Forms.ToolStripMenuItem SMSControllerToolStripMenuItem; diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index fd1d42f241..026adb32be 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1244,6 +1244,12 @@ namespace BizHawk.Client.EmuHawk SgbSameBoyMenuItem.Checked = !Global.Config.SGB_UseBsnes; } + private void GBCoreSubmenu_DropDownOpened(object sender, EventArgs e) + { + GBGambatteMenuItem.Checked = !Global.Config.GB_UseGBHawk; + GBGBHawkMenuItem.Checked = Global.Config.GB_UseGBHawk; + } + private void SgbCorePick_Click(object sender, EventArgs e) { Global.Config.SGB_UseBsnes ^= true; @@ -1254,6 +1260,16 @@ namespace BizHawk.Client.EmuHawk } } + private void GBCorePick_Click(object sender, EventArgs e) + { + Global.Config.GB_UseGBHawk ^= true; + // TODO: only flag if one of these cores + if (!Emulator.IsNull()) + { + FlagNeedsReboot(); + } + } + private void GbInSgbMenuItem_Click(object sender, EventArgs e) { Global.Config.GB_AsSGB ^= true; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 0acda8f08e..05baf2d9d0 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -581,6 +581,35 @@ VBANext.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + N64.cs @@ -752,7 +781,7 @@ - + @@ -1161,6 +1190,14 @@ + + + + + + + + @@ -1186,12 +1223,6 @@ - - - - - - @@ -1322,4 +1353,4 @@ --> - + \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs new file mode 100644 index 0000000000..921ee5f19f --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs @@ -0,0 +1,552 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + private int totalExecutedCycles; + public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + + private int EI_pending; + private bool interrupts_enabled; + + // variables for executing instructions + public int instr_pntr = 0; + public ushort[] cur_instr; + public int opcode; + public bool CB_prefix; + public bool halted; + public bool stopped; + public bool jammed; + public int LY; + + public void FetchInstruction(byte opcode) + { + if (!CB_prefix) + { + switch (opcode) + { + case 0x00: NOP_(); break; // NOP + case 0x01: LD_IND_16(C, B, PCl, PCh); break; // LD BC, nn + case 0x02: LD_8_IND(C, B, A); break; // LD (BC), A + case 0x03: INC_16(C, B); break; // INC BC + case 0x04: INT_OP(INC8, B); break; // INC B + case 0x05: INT_OP(DEC8, B); break; // DEC B + case 0x06: LD_IND_8_INC(B, PCl, PCh); break; // LD B, n + case 0x07: INT_OP(RLC, Aim); break; // RLCA + case 0x08: LD_R_IM(SPl, SPh, PCl, PCh); break; // LD (imm), SP + case 0x09: ADD_16(L, H, C, B); break; // ADD HL, BC + case 0x0A: REG_OP_IND(TR, A, C, B); break; // LD A, (BC) + case 0x0B: DEC_16(C, B); break; // DEC BC + case 0x0C: INT_OP(INC8, C); break; // INC C + case 0x0D: INT_OP(DEC8, C); break; // DEC C + case 0x0E: LD_IND_8_INC(C, PCl, PCh); break; // LD C, n + case 0x0F: INT_OP(RRC, Aim); break; // RRCA + case 0x10: STOP_(); break; // STOP + case 0x11: LD_IND_16(E, D, PCl, PCh); break; // LD DE, nn + case 0x12: LD_8_IND(E, D, A); break; // LD (DE), A + case 0x13: INC_16(E, D); break; // INC DE + case 0x14: INT_OP(INC8, D); break; // INC D + case 0x15: INT_OP(DEC8, D); break; // DEC D + case 0x16: LD_IND_8_INC(D, PCl, PCh); break; // LD D, n + case 0x17: INT_OP(RL, Aim); break; // RLA + case 0x18: JR_COND(true); break; // JR, r8 + case 0x19: ADD_16(L, H, E, D); break; // ADD HL, DE + case 0x1A: REG_OP_IND(TR, A, E, D); break; // LD A, (DE) + case 0x1B: DEC_16(E, D); break; // DEC DE + case 0x1C: INT_OP(INC8, E); break; // INC E + case 0x1D: INT_OP(DEC8, E); break; // DEC E + case 0x1E: LD_IND_8_INC(E, PCl, PCh); break; // LD E, n + case 0x1F: INT_OP(RR, Aim); break; // RRA + case 0x20: JR_COND(!FlagZ); break; // JR NZ, r8 + case 0x21: LD_IND_16(L, H, PCl, PCh); break; // LD HL, nn + case 0x22: LD_8_IND_INC(L, H, A); break; // LD (HL+), A + case 0x23: INC_16(L, H); break; // INC HL + case 0x24: INT_OP(INC8, H); break; // INC H + case 0x25: INT_OP(DEC8, H); break; // DEC H + case 0x26: LD_IND_8_INC(H, PCl, PCh); break; // LD H, n + case 0x27: INT_OP(DA, A); break; // DAA + case 0x28: JR_COND(FlagZ); break; // JR Z, r8 + case 0x29: ADD_16(L, H, L, H); break; // ADD HL, HL + case 0x2A: LD_IND_8_INC(A, L, H); break; // LD A, (HL+) + case 0x2B: DEC_16(L, H); break; // DEC HL + case 0x2C: INT_OP(INC8, L); break; // INC L + case 0x2D: INT_OP(DEC8, L); break; // DEC L + case 0x2E: LD_IND_8_INC(L, PCl, PCh); break; // LD L, n + case 0x2F: INT_OP(CPL, A); break; // CPL + case 0x30: JR_COND(!FlagC); break; // JR NC, r8 + case 0x31: LD_IND_16(SPl, SPh, PCl, PCh); break; // LD SP, nn + case 0x32: LD_8_IND_DEC(L, H, A); break; // LD (HL-), A + case 0x33: INC_16(SPl, SPh); break; // INC SP + case 0x34: INC_8_IND(L, H); break; // INC (HL) + case 0x35: DEC_8_IND(L, H); break; // DEC (HL) + case 0x36: LD_8_IND_IND(L, H, PCl, PCh); break; // LD (HL), n + case 0x37: INT_OP(SCF, A); break; // SCF + case 0x38: JR_COND(FlagC); break; // JR C, r8 + case 0x39: ADD_16(L, H, SPl, SPh); break; // ADD HL, SP + case 0x3A: LD_IND_8_DEC(A, L, H); break; // LD A, (HL-) + case 0x3B: DEC_16(SPl, SPh); break; // DEC SP + case 0x3C: INT_OP(INC8, A); break; // INC A + case 0x3D: INT_OP(DEC8, A); break; // DEC A + case 0x3E: LD_IND_8_INC(A, PCl, PCh); break; // LD A, n + case 0x3F: INT_OP(CCF, A); break; // CCF + case 0x40: REG_OP(TR, B, B); break; // LD B, B + case 0x41: REG_OP(TR, B, C); break; // LD B, C + case 0x42: REG_OP(TR, B, D); break; // LD B, D + case 0x43: REG_OP(TR, B, E); break; // LD B, E + case 0x44: REG_OP(TR, B, H); break; // LD B, H + case 0x45: REG_OP(TR, B, L); break; // LD B, L + case 0x46: REG_OP_IND(TR, B, L, H); break; // LD B, (HL) + case 0x47: REG_OP(TR, B, A); break; // LD B, A + case 0x48: REG_OP(TR, C, B); break; // LD C, B + case 0x49: REG_OP(TR, C, C); break; // LD C, C + case 0x4A: REG_OP(TR, C, D); break; // LD C, D + case 0x4B: REG_OP(TR, C, E); break; // LD C, E + case 0x4C: REG_OP(TR, C, H); break; // LD C, H + case 0x4D: REG_OP(TR, C, L); break; // LD C, L + case 0x4E: REG_OP_IND(TR, C, L, H); break; // LD C, (HL) + case 0x4F: REG_OP(TR, C, A); break; // LD C, A + case 0x50: REG_OP(TR, D, B); break; // LD D, B + case 0x51: REG_OP(TR, D, C); break; // LD D, C + case 0x52: REG_OP(TR, D, D); break; // LD D, D + case 0x53: REG_OP(TR, D, E); break; // LD D, E + case 0x54: REG_OP(TR, D, H); break; // LD D, H + case 0x55: REG_OP(TR, D, L); break; // LD D, L + case 0x56: REG_OP_IND(TR, D, L, H); break; // LD D, (HL) + case 0x57: REG_OP(TR, D, A); break; // LD D, A + case 0x58: REG_OP(TR, E, B); break; // LD E, B + case 0x59: REG_OP(TR, E, C); break; // LD E, C + case 0x5A: REG_OP(TR, E, D); break; // LD E, D + case 0x5B: REG_OP(TR, E, E); break; // LD E, E + case 0x5C: REG_OP(TR, E, H); break; // LD E, H + case 0x5D: REG_OP(TR, E, L); break; // LD E, L + case 0x5E: REG_OP_IND(TR, E, L, H); break; // LD E, (HL) + case 0x5F: REG_OP(TR, E, A); break; // LD E, A + case 0x60: REG_OP(TR, H, B); break; // LD H, B + case 0x61: REG_OP(TR, H, C); break; // LD H, C + case 0x62: REG_OP(TR, H, D); break; // LD H, D + case 0x63: REG_OP(TR, H, E); break; // LD H, E + case 0x64: REG_OP(TR, H, H); break; // LD H, H + case 0x65: REG_OP(TR, H, L); break; // LD H, L + case 0x66: REG_OP_IND(TR, H, L, H); break; // LD H, (HL) + case 0x67: REG_OP(TR, H, A); break; // LD H, A + case 0x68: REG_OP(TR, L, B); break; // LD L, B + case 0x69: REG_OP(TR, L, C); break; // LD L, C + case 0x6A: REG_OP(TR, L, D); break; // LD L, D + case 0x6B: REG_OP(TR, L, E); break; // LD L, E + case 0x6C: REG_OP(TR, L, H); break; // LD L, H + case 0x6D: REG_OP(TR, L, L); break; // LD L, L + case 0x6E: REG_OP_IND(TR, L, L, H); break; // LD L, (HL) + case 0x6F: REG_OP(TR, L, A); break; // LD L, A + case 0x70: LD_8_IND(L, H, B); break; // LD (HL), B + case 0x71: LD_8_IND(L, H, C); break; // LD (HL), C + case 0x72: LD_8_IND(L, H, D); break; // LD (HL), D + case 0x73: LD_8_IND(L, H, E); break; // LD (HL), E + case 0x74: LD_8_IND(L, H, H); break; // LD (HL), H + case 0x75: LD_8_IND(L, H, L); break; // LD (HL), L + case 0x76: HALT_(); break; // HALT + case 0x77: LD_8_IND(L, H, A); break; // LD (HL), A + case 0x78: REG_OP(TR, A, B); break; // LD A, B + case 0x79: REG_OP(TR, A, C); break; // LD A, C + case 0x7A: REG_OP(TR, A, D); break; // LD A, D + case 0x7B: REG_OP(TR, A, E); break; // LD A, E + case 0x7C: REG_OP(TR, A, H); break; // LD A, H + case 0x7D: REG_OP(TR, A, L); break; // LD A, L + case 0x7E: REG_OP_IND(TR, A, L, H); break; // LD A, (HL) + case 0x7F: REG_OP(TR, A, A); break; // LD A, A + case 0x80: REG_OP(ADD8, A, B); break; // ADD A, B + case 0x81: REG_OP(ADD8, A, C); break; // ADD A, C + case 0x82: REG_OP(ADD8, A, D); break; // ADD A, D + case 0x83: REG_OP(ADD8, A, E); break; // ADD A, E + case 0x84: REG_OP(ADD8, A, H); break; // ADD A, H + case 0x85: REG_OP(ADD8, A, L); break; // ADD A, L + case 0x86: REG_OP_IND(ADD8, A, L, H); break; // ADD A, (HL) + case 0x87: REG_OP(ADD8, A, A); break; // ADD A, A + case 0x88: REG_OP(ADC8, A, B); break; // ADC A, B + case 0x89: REG_OP(ADC8, A, C); break; // ADC A, C + case 0x8A: REG_OP(ADC8, A, D); break; // ADC A, D + case 0x8B: REG_OP(ADC8, A, E); break; // ADC A, E + case 0x8C: REG_OP(ADC8, A, H); break; // ADC A, H + case 0x8D: REG_OP(ADC8, A, L); break; // ADC A, L + case 0x8E: REG_OP_IND(ADC8, A, L, H); break; // ADC A, (HL) + case 0x8F: REG_OP(ADC8, A, A); break; // ADC A, A + case 0x90: REG_OP(SUB8, A, B); break; // SUB A, B + case 0x91: REG_OP(SUB8, A, C); break; // SUB A, C + case 0x92: REG_OP(SUB8, A, D); break; // SUB A, D + case 0x93: REG_OP(SUB8, A, E); break; // SUB A, E + case 0x94: REG_OP(SUB8, A, H); break; // SUB A, H + case 0x95: REG_OP(SUB8, A, L); break; // SUB A, L + case 0x96: REG_OP_IND(SUB8, A, L, H); break; // SUB A, (HL) + case 0x97: REG_OP(SUB8, A, A); break; // SUB A, A + case 0x98: REG_OP(SBC8, A, B); break; // SBC A, B + case 0x99: REG_OP(SBC8, A, C); break; // SBC A, C + case 0x9A: REG_OP(SBC8, A, D); break; // SBC A, D + case 0x9B: REG_OP(SBC8, A, E); break; // SBC A, E + case 0x9C: REG_OP(SBC8, A, H); break; // SBC A, H + case 0x9D: REG_OP(SBC8, A, L); break; // SBC A, L + case 0x9E: REG_OP_IND(SBC8, A, L, H); break; // SBC A, (HL) + case 0x9F: REG_OP(SBC8, A, A); break; // SBC A, A + case 0xA0: REG_OP(AND8, A, B); break; // AND A, B + case 0xA1: REG_OP(AND8, A, C); break; // AND A, C + case 0xA2: REG_OP(AND8, A, D); break; // AND A, D + case 0xA3: REG_OP(AND8, A, E); break; // AND A, E + case 0xA4: REG_OP(AND8, A, H); break; // AND A, H + case 0xA5: REG_OP(AND8, A, L); break; // AND A, L + case 0xA6: REG_OP_IND(AND8, A, L, H); break; // AND A, (HL) + case 0xA7: REG_OP(AND8, A, A); break; // AND A, A + case 0xA8: REG_OP(XOR8, A, B); break; // XOR A, B + case 0xA9: REG_OP(XOR8, A, C); break; // XOR A, C + case 0xAA: REG_OP(XOR8, A, D); break; // XOR A, D + case 0xAB: REG_OP(XOR8, A, E); break; // XOR A, E + case 0xAC: REG_OP(XOR8, A, H); break; // XOR A, H + case 0xAD: REG_OP(XOR8, A, L); break; // XOR A, L + case 0xAE: REG_OP_IND(XOR8, A, L, H); break; // XOR A, (HL) + case 0xAF: REG_OP(XOR8, A, A); break; // XOR A, A + case 0xB0: REG_OP(OR8, A, B); break; // OR A, B + case 0xB1: REG_OP(OR8, A, C); break; // OR A, C + case 0xB2: REG_OP(OR8, A, D); break; // OR A, D + case 0xB3: REG_OP(OR8, A, E); break; // OR A, E + case 0xB4: REG_OP(OR8, A, H); break; // OR A, H + case 0xB5: REG_OP(OR8, A, L); break; // OR A, L + case 0xB6: REG_OP_IND(OR8, A, L, H); break; // OR A, (HL) + case 0xB7: REG_OP(OR8, A, A); break; // OR A, A + case 0xB8: REG_OP(CP8, A, B); break; // CP A, B + case 0xB9: REG_OP(CP8, A, C); break; // CP A, C + case 0xBA: REG_OP(CP8, A, D); break; // CP A, D + case 0xBB: REG_OP(CP8, A, E); break; // CP A, E + case 0xBC: REG_OP(CP8, A, H); break; // CP A, H + case 0xBD: REG_OP(CP8, A, L); break; // CP A, L + case 0xBE: REG_OP_IND(CP8, A, L, H); break; // CP A, (HL) + case 0xBF: REG_OP(CP8, A, A); break; // CP A, A + case 0xC0: RET_COND(!FlagZ); break; // Ret NZ + case 0xC1: POP_(C, B); break; // POP BC + case 0xC2: JP_COND(!FlagZ); break; // JP NZ + case 0xC3: JP_COND(true); break; // JP + case 0xC4: CALL_COND(!FlagZ); break; // CALL NZ + case 0xC5: PUSH_(C, B); break; // PUSH BC + case 0xC6: REG_OP_IND_INC(ADD8, A, PCl, PCh); break; // ADD A, n + case 0xC7: RST_(0); break; // RST 0 + case 0xC8: RET_COND(FlagZ); break; // RET Z + case 0xC9: RET_(); break; // RET + case 0xCA: JP_COND(FlagZ); break; // JP Z + case 0xCB: PREFIX_(); break; // PREFIX + case 0xCC: CALL_COND(FlagZ); break; // CALL Z + case 0xCD: CALL_COND(true); break; // CALL + case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n + case 0xCF: RST_(0x08); break; // RST 0x08 + case 0xD0: RET_COND(!FlagC); break; // Ret NC + case 0xD1: POP_(E, D); break; // POP DE + case 0xD2: JP_COND(!FlagC); break; // JP NC + case 0xD3: JAM_(); break; // JAM + case 0xD4: CALL_COND(!FlagC); break; // CALL NC + case 0xD5: PUSH_(E, D); break; // PUSH DE + case 0xD6: REG_OP_IND_INC(SUB8, A, PCl, PCh); break; // SUB A, n + case 0xD7: RST_(0x10); break; // RST 0x10 + case 0xD8: RET_COND(FlagC); break; // RET C + case 0xD9: RETI_(); break; // RETI + case 0xDA: JP_COND(FlagC); break; // JP C + case 0xDB: JAM_(); break; // JAM + case 0xDC: CALL_COND(FlagC); break; // CALL C + case 0xDD: JAM_(); break; // JAM + case 0xDE: REG_OP_IND_INC(SBC8, A, PCl, PCh); break; // SBC A, n + case 0xDF: RST_(0x18); break; // RST 0x18 + case 0xE0: LD_FF_IND_8(PCl, PCh, A); break; // LD(n), A + case 0xE1: POP_(L, H); break; // POP HL + case 0xE2: LD_FFC_IND_8(PCl, PCh, A); break; // LD(C), A + case 0xE3: JAM_(); break; // JAM + case 0xE4: JAM_(); break; // JAM + case 0xE5: PUSH_(L, H); break; // PUSH HL + case 0xE6: REG_OP_IND_INC(AND8, A, PCl, PCh); break; // AND A, n + case 0xE7: RST_(0x20); break; // RST 0x20 + case 0xE8: ADD_SP(); break; // ADD SP,n + case 0xE9: JP_HL(); break; // JP (HL) + case 0xEA: LD_FF_IND_16(PCl, PCh, A); break; // LD(nn), A + case 0xEB: JAM_(); break; // JAM + case 0xEC: JAM_(); break; // JAM + case 0xED: JAM_(); break; // JAM + case 0xEE: REG_OP_IND_INC(XOR8, A, PCl, PCh); break; // XOR A, n + case 0xEF: RST_(0x28); break; // RST 0x28 + case 0xF0: LD_8_IND_FF(A, PCl, PCh); break; // A, LD(n) + case 0xF1: POP_(F, A); break; // POP AF + case 0xF2: LD_8_IND_FFC(A, PCl, PCh); break; // A, LD(C) + case 0xF3: DI_(); break; // DI + case 0xF4: JAM_(); break; // JAM + case 0xF5: PUSH_(F, A); break; // PUSH AF + case 0xF6: REG_OP_IND_INC(OR8, A, PCl, PCh); break; // OR A, n + case 0xF7: RST_(0x30); break; // RST 0x30 + case 0xF8: LD_HL_SPn(); break; // LD HL, SP+n + case 0xF9: LD_SP_HL(); break; // LD, SP, HL + case 0xFA: LD_16_IND_FF(A, PCl, PCh); break; // A, LD(nn) + case 0xFB: EI_(); break; // EI + case 0xFC: JAM_(); break; // JAM + case 0xFD: JAM_(); break; // JAM + case 0xFE: REG_OP_IND_INC(CP8, A, PCl, PCh); break; // XOR A, n + case 0xFF: RST_(0x38); break; // RST 0x38 + } + } + else + { + CB_prefix = false; + switch (opcode) + { + case 0x00: INT_OP(RLC, B); break; // RLC B + case 0x01: INT_OP(RLC, C); break; // RLC C + case 0x02: INT_OP(RLC, D); break; // RLC D + case 0x03: INT_OP(RLC, E); break; // RLC E + case 0x04: INT_OP(RLC, H); break; // RLC H + case 0x05: INT_OP(RLC, L); break; // RLC L + case 0x06: INT_OP_IND(RLC, L, H); break; // RLC (HL) + case 0x07: INT_OP(RLC, A); break; // RLC A + case 0x08: INT_OP(RRC, B); break; // RRC B + case 0x09: INT_OP(RRC, C); break; // RRC C + case 0x0A: INT_OP(RRC, D); break; // RRC D + case 0x0B: INT_OP(RRC, E); break; // RRC E + case 0x0C: INT_OP(RRC, H); break; // RRC H + case 0x0D: INT_OP(RRC, L); break; // RRC L + case 0x0E: INT_OP_IND(RRC, L, H); break; // RRC (HL) + case 0x0F: INT_OP(RRC, A); break; // RRC A + case 0x10: INT_OP(RL, B); break; // RL B + case 0x11: INT_OP(RL, C); break; // RL C + case 0x12: INT_OP(RL, D); break; // RL D + case 0x13: INT_OP(RL, E); break; // RL E + case 0x14: INT_OP(RL, H); break; // RL H + case 0x15: INT_OP(RL, L); break; // RL L + case 0x16: INT_OP_IND(RL, L, H); break; // RL (HL) + case 0x17: INT_OP(RL, A); break; // RL A + case 0x18: INT_OP(RR, B); break; // RR B + case 0x19: INT_OP(RR, C); break; // RR C + case 0x1A: INT_OP(RR, D); break; // RR D + case 0x1B: INT_OP(RR, E); break; // RR E + case 0x1C: INT_OP(RR, H); break; // RR H + case 0x1D: INT_OP(RR, L); break; // RR L + case 0x1E: INT_OP_IND(RR, L, H); break; // RR (HL) + case 0x1F: INT_OP(RR, A); break; // RR A + case 0x20: INT_OP(SLA, B); break; // SLA B + case 0x21: INT_OP(SLA, C); break; // SLA C + case 0x22: INT_OP(SLA, D); break; // SLA D + case 0x23: INT_OP(SLA, E); break; // SLA E + case 0x24: INT_OP(SLA, H); break; // SLA H + case 0x25: INT_OP(SLA, L); break; // SLA L + case 0x26: INT_OP_IND(SLA, L, H); break; // SLA (HL) + case 0x27: INT_OP(SLA, A); break; // SLA A + case 0x28: INT_OP(SRA, B); break; // SRA B + case 0x29: INT_OP(SRA, C); break; // SRA C + case 0x2A: INT_OP(SRA, D); break; // SRA D + case 0x2B: INT_OP(SRA, E); break; // SRA E + case 0x2C: INT_OP(SRA, H); break; // SRA H + case 0x2D: INT_OP(SRA, L); break; // SRA L + case 0x2E: INT_OP_IND(SRA, L, H); break; // SRA (HL) + case 0x2F: INT_OP(SRA, A); break; // SRA A + case 0x30: INT_OP(SWAP, B); break; // SWAP B + case 0x31: INT_OP(SWAP, C); break; // SWAP C + case 0x32: INT_OP(SWAP, D); break; // SWAP D + case 0x33: INT_OP(SWAP, E); break; // SWAP E + case 0x34: INT_OP(SWAP, H); break; // SWAP H + case 0x35: INT_OP(SWAP, L); break; // SWAP L + case 0x36: INT_OP_IND(SWAP, L, H); break; // SWAP (HL) + case 0x37: INT_OP(SWAP, A); break; // SWAP A + case 0x38: INT_OP(SRL, B); break; // SRL B + case 0x39: INT_OP(SRL, C); break; // SRL C + case 0x3A: INT_OP(SRL, D); break; // SRL D + case 0x3B: INT_OP(SRL, E); break; // SRL E + case 0x3C: INT_OP(SRL, H); break; // SRL H + case 0x3D: INT_OP(SRL, L); break; // SRL L + case 0x3E: INT_OP_IND(SRL, L, H); break; // SRL (HL) + case 0x3F: INT_OP(SRL, A); break; // SRL A + case 0x40: BIT_OP(BIT, 0, B); break; // BIT 0, B + case 0x41: BIT_OP(BIT, 0, C); break; // BIT 0, C + case 0x42: BIT_OP(BIT, 0, D); break; // BIT 0, D + case 0x43: BIT_OP(BIT, 0, E); break; // BIT 0, E + case 0x44: BIT_OP(BIT, 0, H); break; // BIT 0, H + case 0x45: BIT_OP(BIT, 0, L); break; // BIT 0, L + case 0x46: BIT_TE_IND(BIT, 0, L, H); break; // BIT 0, (HL) + case 0x47: BIT_OP(BIT, 0, A); break; // BIT 0, A + case 0x48: BIT_OP(BIT, 1, B); break; // BIT 1, B + case 0x49: BIT_OP(BIT, 1, C); break; // BIT 1, C + case 0x4A: BIT_OP(BIT, 1, D); break; // BIT 1, D + case 0x4B: BIT_OP(BIT, 1, E); break; // BIT 1, E + case 0x4C: BIT_OP(BIT, 1, H); break; // BIT 1, H + case 0x4D: BIT_OP(BIT, 1, L); break; // BIT 1, L + case 0x4E: BIT_TE_IND(BIT, 1, L, H); break; // BIT 1, (HL) + case 0x4F: BIT_OP(BIT, 1, A); break; // BIT 1, A + case 0x50: BIT_OP(BIT, 2, B); break; // BIT 2, B + case 0x51: BIT_OP(BIT, 2, C); break; // BIT 2, C + case 0x52: BIT_OP(BIT, 2, D); break; // BIT 2, D + case 0x53: BIT_OP(BIT, 2, E); break; // BIT 2, E + case 0x54: BIT_OP(BIT, 2, H); break; // BIT 2, H + case 0x55: BIT_OP(BIT, 2, L); break; // BIT 2, L + case 0x56: BIT_TE_IND(BIT, 2, L, H); break; // BIT 2, (HL) + case 0x57: BIT_OP(BIT, 2, A); break; // BIT 2, A + case 0x58: BIT_OP(BIT, 3, B); break; // BIT 3, B + case 0x59: BIT_OP(BIT, 3, C); break; // BIT 3, C + case 0x5A: BIT_OP(BIT, 3, D); break; // BIT 3, D + case 0x5B: BIT_OP(BIT, 3, E); break; // BIT 3, E + case 0x5C: BIT_OP(BIT, 3, H); break; // BIT 3, H + case 0x5D: BIT_OP(BIT, 3, L); break; // BIT 3, L + case 0x5E: BIT_TE_IND(BIT, 3, L, H); break; // BIT 3, (HL) + case 0x5F: BIT_OP(BIT, 3, A); break; // BIT 3, A + case 0x60: BIT_OP(BIT, 4, B); break; // BIT 4, B + case 0x61: BIT_OP(BIT, 4, C); break; // BIT 4, C + case 0x62: BIT_OP(BIT, 4, D); break; // BIT 4, D + case 0x63: BIT_OP(BIT, 4, E); break; // BIT 4, E + case 0x64: BIT_OP(BIT, 4, H); break; // BIT 4, H + case 0x65: BIT_OP(BIT, 4, L); break; // BIT 4, L + case 0x66: BIT_TE_IND(BIT, 4, L, H); break; // BIT 4, (HL) + case 0x67: BIT_OP(BIT, 4, A); break; // BIT 4, A + case 0x68: BIT_OP(BIT, 5, B); break; // BIT 5, B + case 0x69: BIT_OP(BIT, 5, C); break; // BIT 5, C + case 0x6A: BIT_OP(BIT, 5, D); break; // BIT 5, D + case 0x6B: BIT_OP(BIT, 5, E); break; // BIT 5, E + case 0x6C: BIT_OP(BIT, 5, H); break; // BIT 5, H + case 0x6D: BIT_OP(BIT, 5, L); break; // BIT 5, L + case 0x6E: BIT_TE_IND(BIT, 5, L, H); break; // BIT 5, (HL) + case 0x6F: BIT_OP(BIT, 5, A); break; // BIT 5, A + case 0x70: BIT_OP(BIT, 6, B); break; // BIT 6, B + case 0x71: BIT_OP(BIT, 6, C); break; // BIT 6, C + case 0x72: BIT_OP(BIT, 6, D); break; // BIT 6, D + case 0x73: BIT_OP(BIT, 6, E); break; // BIT 6, E + case 0x74: BIT_OP(BIT, 6, H); break; // BIT 6, H + case 0x75: BIT_OP(BIT, 6, L); break; // BIT 6, L + case 0x76: BIT_TE_IND(BIT, 6, L, H); break; // BIT 6, (HL) + case 0x77: BIT_OP(BIT, 6, A); break; // BIT 6, A + case 0x78: BIT_OP(BIT, 7, B); break; // BIT 7, B + case 0x79: BIT_OP(BIT, 7, C); break; // BIT 7, C + case 0x7A: BIT_OP(BIT, 7, D); break; // BIT 7, D + case 0x7B: BIT_OP(BIT, 7, E); break; // BIT 7, E + case 0x7C: BIT_OP(BIT, 7, H); break; // BIT 7, H + case 0x7D: BIT_OP(BIT, 7, L); break; // BIT 7, L + case 0x7E: BIT_TE_IND(BIT, 7, L, H); break; // BIT 7, (HL) + case 0x7F: BIT_OP(BIT, 7, A); break; // BIT 7, A + case 0x80: BIT_OP(RES, 0, B); break; // RES 0, B + case 0x81: BIT_OP(RES, 0, C); break; // RES 0, C + case 0x82: BIT_OP(RES, 0, D); break; // RES 0, D + case 0x83: BIT_OP(RES, 0, E); break; // RES 0, E + case 0x84: BIT_OP(RES, 0, H); break; // RES 0, H + case 0x85: BIT_OP(RES, 0, L); break; // RES 0, L + case 0x86: BIT_OP_IND(RES, 0, L, H); break; // RES 0, (HL) + case 0x87: BIT_OP(RES, 0, A); break; // RES 0, A + case 0x88: BIT_OP(RES, 1, B); break; // RES 1, B + case 0x89: BIT_OP(RES, 1, C); break; // RES 1, C + case 0x8A: BIT_OP(RES, 1, D); break; // RES 1, D + case 0x8B: BIT_OP(RES, 1, E); break; // RES 1, E + case 0x8C: BIT_OP(RES, 1, H); break; // RES 1, H + case 0x8D: BIT_OP(RES, 1, L); break; // RES 1, L + case 0x8E: BIT_OP_IND(RES, 1, L, H); break; // RES 1, (HL) + case 0x8F: BIT_OP(RES, 1, A); break; // RES 1, A + case 0x90: BIT_OP(RES, 2, B); break; // RES 2, B + case 0x91: BIT_OP(RES, 2, C); break; // RES 2, C + case 0x92: BIT_OP(RES, 2, D); break; // RES 2, D + case 0x93: BIT_OP(RES, 2, E); break; // RES 2, E + case 0x94: BIT_OP(RES, 2, H); break; // RES 2, H + case 0x95: BIT_OP(RES, 2, L); break; // RES 2, L + case 0x96: BIT_OP_IND(RES, 2, L, H); break; // RES 2, (HL) + case 0x97: BIT_OP(RES, 2, A); break; // RES 2, A + case 0x98: BIT_OP(RES, 3, B); break; // RES 3, B + case 0x99: BIT_OP(RES, 3, C); break; // RES 3, C + case 0x9A: BIT_OP(RES, 3, D); break; // RES 3, D + case 0x9B: BIT_OP(RES, 3, E); break; // RES 3, E + case 0x9C: BIT_OP(RES, 3, H); break; // RES 3, H + case 0x9D: BIT_OP(RES, 3, L); break; // RES 3, L + case 0x9E: BIT_OP_IND(RES, 3, L, H); break; // RES 3, (HL) + case 0x9F: BIT_OP(RES, 3, A); break; // RES 3, A + case 0xA0: BIT_OP(RES, 4, B); break; // RES 4, B + case 0xA1: BIT_OP(RES, 4, C); break; // RES 4, C + case 0xA2: BIT_OP(RES, 4, D); break; // RES 4, D + case 0xA3: BIT_OP(RES, 4, E); break; // RES 4, E + case 0xA4: BIT_OP(RES, 4, H); break; // RES 4, H + case 0xA5: BIT_OP(RES, 4, L); break; // RES 4, L + case 0xA6: BIT_OP_IND(RES, 4, L, H); break; // RES 4, (HL) + case 0xA7: BIT_OP(RES, 4, A); break; // RES 4, A + case 0xA8: BIT_OP(RES, 5, B); break; // RES 5, B + case 0xA9: BIT_OP(RES, 5, C); break; // RES 5, C + case 0xAA: BIT_OP(RES, 5, D); break; // RES 5, D + case 0xAB: BIT_OP(RES, 5, E); break; // RES 5, E + case 0xAC: BIT_OP(RES, 5, H); break; // RES 5, H + case 0xAD: BIT_OP(RES, 5, L); break; // RES 5, L + case 0xAE: BIT_OP_IND(RES, 5, L, H); break; // RES 5, (HL) + case 0xAF: BIT_OP(RES, 5, A); break; // RES 5, A + case 0xB0: BIT_OP(RES, 6, B); break; // RES 6, B + case 0xB1: BIT_OP(RES, 6, C); break; // RES 6, C + case 0xB2: BIT_OP(RES, 6, D); break; // RES 6, D + case 0xB3: BIT_OP(RES, 6, E); break; // RES 6, E + case 0xB4: BIT_OP(RES, 6, H); break; // RES 6, H + case 0xB5: BIT_OP(RES, 6, L); break; // RES 6, L + case 0xB6: BIT_OP_IND(RES, 6, L, H); break; // RES 6, (HL) + case 0xB7: BIT_OP(RES, 6, A); break; // RES 6, A + case 0xB8: BIT_OP(RES, 7, B); break; // RES 7, B + case 0xB9: BIT_OP(RES, 7, C); break; // RES 7, C + case 0xBA: BIT_OP(RES, 7, D); break; // RES 7, D + case 0xBB: BIT_OP(RES, 7, E); break; // RES 7, E + case 0xBC: BIT_OP(RES, 7, H); break; // RES 7, H + case 0xBD: BIT_OP(RES, 7, L); break; // RES 7, L + case 0xBE: BIT_OP_IND(RES, 7, L, H); break; // RES 7, (HL) + case 0xBF: BIT_OP(RES, 7, A); break; // RES 7, A + case 0xC0: BIT_OP(SET, 0, B); break; // SET 0, B + case 0xC1: BIT_OP(SET, 0, C); break; // SET 0, C + case 0xC2: BIT_OP(SET, 0, D); break; // SET 0, D + case 0xC3: BIT_OP(SET, 0, E); break; // SET 0, E + case 0xC4: BIT_OP(SET, 0, H); break; // SET 0, H + case 0xC5: BIT_OP(SET, 0, L); break; // SET 0, L + case 0xC6: BIT_OP_IND(SET, 0, L, H); break; // SET 0, (HL) + case 0xC7: BIT_OP(SET, 0, A); break; // SET 0, A + case 0xC8: BIT_OP(SET, 1, B); break; // SET 1, B + case 0xC9: BIT_OP(SET, 1, C); break; // SET 1, C + case 0xCA: BIT_OP(SET, 1, D); break; // SET 1, D + case 0xCB: BIT_OP(SET, 1, E); break; // SET 1, E + case 0xCC: BIT_OP(SET, 1, H); break; // SET 1, H + case 0xCD: BIT_OP(SET, 1, L); break; // SET 1, L + case 0xCE: BIT_OP_IND(SET, 1, L, H); break; // SET 1, (HL) + case 0xCF: BIT_OP(SET, 1, A); break; // SET 1, A + case 0xD0: BIT_OP(SET, 2, B); break; // SET 2, B + case 0xD1: BIT_OP(SET, 2, C); break; // SET 2, C + case 0xD2: BIT_OP(SET, 2, D); break; // SET 2, D + case 0xD3: BIT_OP(SET, 2, E); break; // SET 2, E + case 0xD4: BIT_OP(SET, 2, H); break; // SET 2, H + case 0xD5: BIT_OP(SET, 2, L); break; // SET 2, L + case 0xD6: BIT_OP_IND(SET, 2, L, H); break; // SET 2, (HL) + case 0xD7: BIT_OP(SET, 2, A); break; // SET 2, A + case 0xD8: BIT_OP(SET, 3, B); break; // SET 3, B + case 0xD9: BIT_OP(SET, 3, C); break; // SET 3, C + case 0xDA: BIT_OP(SET, 3, D); break; // SET 3, D + case 0xDB: BIT_OP(SET, 3, E); break; // SET 3, E + case 0xDC: BIT_OP(SET, 3, H); break; // SET 3, H + case 0xDD: BIT_OP(SET, 3, L); break; // SET 3, L + case 0xDE: BIT_OP_IND(SET, 3, L, H); break; // SET 3, (HL) + case 0xDF: BIT_OP(SET, 3, A); break; // SET 3, A + case 0xE0: BIT_OP(SET, 4, B); break; // SET 4, B + case 0xE1: BIT_OP(SET, 4, C); break; // SET 4, C + case 0xE2: BIT_OP(SET, 4, D); break; // SET 4, D + case 0xE3: BIT_OP(SET, 4, E); break; // SET 4, E + case 0xE4: BIT_OP(SET, 4, H); break; // SET 4, H + case 0xE5: BIT_OP(SET, 4, L); break; // SET 4, L + case 0xE6: BIT_OP_IND(SET, 4, L, H); break; // SET 4, (HL) + case 0xE7: BIT_OP(SET, 4, A); break; // SET 4, A + case 0xE8: BIT_OP(SET, 5, B); break; // SET 5, B + case 0xE9: BIT_OP(SET, 5, C); break; // SET 5, C + case 0xEA: BIT_OP(SET, 5, D); break; // SET 5, D + case 0xEB: BIT_OP(SET, 5, E); break; // SET 5, E + case 0xEC: BIT_OP(SET, 5, H); break; // SET 5, H + case 0xED: BIT_OP(SET, 5, L); break; // SET 5, L + case 0xEE: BIT_OP_IND(SET, 5, L, H); break; // SET 5, (HL) + case 0xEF: BIT_OP(SET, 5, A); break; // SET 5, A + case 0xF0: BIT_OP(SET, 6, B); break; // SET 6, B + case 0xF1: BIT_OP(SET, 6, C); break; // SET 6, C + case 0xF2: BIT_OP(SET, 6, D); break; // SET 6, D + case 0xF3: BIT_OP(SET, 6, E); break; // SET 6, E + case 0xF4: BIT_OP(SET, 6, H); break; // SET 6, H + case 0xF5: BIT_OP(SET, 6, L); break; // SET 6, L + case 0xF6: BIT_OP_IND(SET, 6, L, H); break; // SET 6, (HL) + case 0xF7: BIT_OP(SET, 6, A); break; // SET 6, A + case 0xF8: BIT_OP(SET, 7, B); break; // SET 7, B + case 0xF9: BIT_OP(SET, 7, C); break; // SET 7, C + case 0xFA: BIT_OP(SET, 7, D); break; // SET 7, D + case 0xFB: BIT_OP(SET, 7, E); break; // SET 7, E + case 0xFC: BIT_OP(SET, 7, H); break; // SET 7, H + case 0xFD: BIT_OP(SET, 7, L); break; // SET 7, L + case 0xFE: BIT_OP_IND(SET, 7, L, H); break; // SET 7, (HL) + case 0xFF: BIT_OP(SET, 7, A); break; // SET 7, A + } + } + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs similarity index 59% rename from BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs rename to BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 59c76cdc07..0dba320f46 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -1,8 +1,8 @@ using System; -namespace BizHawk.Emulation.Common.Components.Z80GB +namespace BizHawk.Emulation.Common.Components.LR35902 { - public partial class Z80 + public partial class LR35902 { private bool iff1; public bool IFF1 { get { return iff1; } set { iff1 = value; } } @@ -10,9 +10,6 @@ namespace BizHawk.Emulation.Common.Components.Z80GB private bool iff2; public bool IFF2 { get { return iff2; } set { iff2 = value; } } - private bool interrupt; - public bool Interrupt { get { return interrupt; } set { interrupt = value; } } - private bool nonMaskableInterrupt; public bool NonMaskableInterrupt { @@ -30,23 +27,42 @@ namespace BizHawk.Emulation.Common.Components.Z80GB set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; } } - private bool halted; - public bool Halted { get { return halted; } set { halted = value; } } + private void INTERRUPT_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, PCh, + IDLE, + INT_GET, W,// NOTE: here is where we check for a cancelled IRQ + DEC16, SPl, SPh, + WR, SPl, SPh, PCl, + IDLE, + IDLE, + IDLE, + IDLE, + TR, PCl, W, + ASGN, PCh, 0, + IDLE, + OP }; + } + + private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60, 0x00}; + + public ushort int_src; private void ResetInterrupts() { IFF1 = false; IFF2 = false; - Interrupt = false; NonMaskableInterrupt = false; NonMaskableInterruptPending = false; InterruptMode = 1; - Halted = false; - } - - private void Halt() - { - Halted = true; } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs new file mode 100644 index 0000000000..401f1fe397 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -0,0 +1,503 @@ +using System; +using System.Globalization; +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +// GameBoy CPU (Sharp LR35902) +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public sealed partial class LR35902 + { + // operations that can take place in an instruction + public const ushort IDLE = 0; + public const ushort OP = 1; + public const ushort RD = 2; + public const ushort WR = 3; + public const ushort TR = 4; + public const ushort ADD16 = 5; + public const ushort ADD8 = 6; + public const ushort SUB8 = 7; + public const ushort ADC8 = 8; + public const ushort SBC8 = 9; + public const ushort INC16 = 10; + public const ushort INC8 = 11; + public const ushort DEC16 = 12; + public const ushort DEC8 = 13; + public const ushort RLC = 14; + public const ushort RL = 15; + public const ushort RRC = 16; + public const ushort RR = 17; + public const ushort CPL = 18; + public const ushort DA = 19; + public const ushort SCF = 20; + public const ushort CCF = 21; + public const ushort AND8 = 22; + public const ushort XOR8 = 23; + public const ushort OR8 = 24; + public const ushort CP8 = 25; + public const ushort SLA = 26; + public const ushort SRA = 27; + public const ushort SRL = 28; + public const ushort SWAP = 29; + public const ushort BIT = 30; + public const ushort RES = 31; + public const ushort SET = 32; + public const ushort EI = 33; + public const ushort DI = 34; + public const ushort HALT = 35; + public const ushort STOP = 36; + public const ushort PREFIX = 37; + public const ushort ASGN = 38; + public const ushort ADDS = 39; // signed 16 bit operation used in 2 instructions + public const ushort OP_G = 40; // glitchy opcode read performed by halt when interrupts disabled + public const ushort JAM = 41; // all undocumented opcodes jam the machine + public const ushort RD_F = 42; // special read case to pop value into F + public const ushort EI_RETI = 43; // reti has no delay in interrupt enable + public const ushort INT_GET = 44; + + public LR35902() + { + Reset(); + } + + public void Reset() + { + ResetRegisters(); + ResetInterrupts(); + TotalExecutedCycles = 0; + cur_instr = new ushort[] { OP }; + } + + // Memory Access + + public Func ReadMemory; + public Action WriteMemory; + public Func PeekMemory; + public Func DummyReadMemory; + + //this only calls when the first byte of an instruction is fetched. + public Action OnExecFetch; + + public void UnregisterMemoryMapper() + { + ReadMemory = null; + ReadMemory = null; + PeekMemory = null; + DummyReadMemory = null; + } + + public void SetCallbacks + ( + Func ReadMemory, + Func DummyReadMemory, + Func PeekMemory, + Action WriteMemory + ) + { + this.ReadMemory = ReadMemory; + this.DummyReadMemory = DummyReadMemory; + this.PeekMemory = PeekMemory; + this.WriteMemory = WriteMemory; + } + + // Execute instructions + public void ExecuteOne(ref byte interrupt_src, byte interrupt_enable) + { + switch (cur_instr[instr_pntr++]) + { + case IDLE: + // do nothing + break; + case OP: + // Read the opcode of the next instruction + if (EI_pending > 0 && !CB_prefix) + { + EI_pending--; + if (EI_pending == 0) + { + interrupts_enabled = true; + } + } + + if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + { + interrupts_enabled = false; + + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====IRQ====", + RegisterInfo = "" + }); + } + + // call interrupt processor + // lowest bit set is highest priority + + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; } + else { /*Console.WriteLine("No source"); }*/ throw new Exception("Interrupt without Source"); } + + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + + INTERRUPT_(); + } + else + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + } + instr_pntr = 0; + break; + case RD: + Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR: + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case TR: + TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADD16: + ADD16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADD8: + ADD8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SUB8: + SUB8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADC8: + ADC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SBC8: + SBC8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case INC16: + INC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case INC8: + INC8_Func(cur_instr[instr_pntr++]); + break; + case DEC16: + DEC16_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case DEC8: + DEC8_Func(cur_instr[instr_pntr++]); + break; + case RLC: + RLC_Func(cur_instr[instr_pntr++]); + break; + case RL: + RL_Func(cur_instr[instr_pntr++]); + break; + case RRC: + RRC_Func(cur_instr[instr_pntr++]); + break; + case RR: + RR_Func(cur_instr[instr_pntr++]); + break; + case CPL: + CPL_Func(cur_instr[instr_pntr++]); + break; + case DA: + DA_Func(cur_instr[instr_pntr++]); + break; + case SCF: + SCF_Func(cur_instr[instr_pntr++]); + break; + case CCF: + CCF_Func(cur_instr[instr_pntr++]); + break; + case AND8: + AND8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case XOR8: + XOR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case OR8: + OR8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case CP8: + CP8_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SLA: + SLA_Func(cur_instr[instr_pntr++]); + break; + case SRA: + SRA_Func(cur_instr[instr_pntr++]); + break; + case SRL: + SRL_Func(cur_instr[instr_pntr++]); + break; + case SWAP: + SWAP_Func(cur_instr[instr_pntr++]); + break; + case BIT: + BIT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case RES: + RES_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case SET: + SET_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case EI: + EI_pending = 2; + break; + case DI: + interrupts_enabled = false; + EI_pending = 0; + break; + case HALT: + halted = true; + + if (EI_pending > 0 && !CB_prefix) + { + EI_pending--; + if (EI_pending == 0) + { + interrupts_enabled = true; + } + } + + // if the I flag is asserted at the time of halt, don't halt + + if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + { + interrupts_enabled = false; + + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====IRQ====", + RegisterInfo = "" + }); + } + halted = false; + // call interrupt processor + instr_pntr = 0; + // lowest bit set is highest priority + + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; } + else { /*Console.WriteLine("No source"); } */throw new Exception("Interrupt without Source"); } + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + + INTERRUPT_(); + } + else if (FlagI) + { + // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====un-halted====", + RegisterInfo = "" + }); + } + halted = false; + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + instr_pntr = 0; + } + else + { + instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT }; + } + break; + case STOP: + stopped = true; + + if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though + { + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====un-stop====", + RegisterInfo = "" + }); + } + + stopped = false; + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + instr_pntr = 0; + } + else + { + instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + STOP }; + } + break; + case PREFIX: + CB_prefix = true; + break; + case ASGN: + ASGN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case ADDS: + ADDS_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case OP_G: + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + + FetchInstruction(ReadMemory(RegPC)); // note no increment + + instr_pntr = 0; + break; + case JAM: + jammed = true; + instr_pntr--; + break; + case RD_F: + Read_Func_F(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case EI_RETI: + EI_pending = 1; + break; + case INT_GET: + // check if any interrupts got cancelled along the way + // interrupt src = 5 sets the PC to zero as observed + if (int_src == 0) + { + if (interrupt_enable.Bit(0)) { interrupt_src -= 1; } + else { int_src = 5; } + } + if (int_src == 1) + { + if (interrupt_enable.Bit(1)) { interrupt_src -= 2; } + else { int_src = 5; } + } + if (int_src == 2) + { + if (interrupt_enable.Bit(2)) { interrupt_src -= 4; } + else { int_src = 5; } + } + if (int_src == 3) + { + if (interrupt_enable.Bit(3)) { interrupt_src -= 8; } + else { int_src = 5; } + } + if (int_src == 4) + { + if (interrupt_enable.Bit(4)) { interrupt_src -= 16; } + else { int_src = 5; } + } + + // if we lost the interrupt, find the next highest interrupt, if any + if (int_src == 5) + { + if (interrupt_src.Bit(0) && interrupt_enable.Bit(0)) { int_src = 0; interrupt_src -= 1; } + else if (interrupt_src.Bit(1) && interrupt_enable.Bit(1)) { int_src = 1; interrupt_src -= 2; } + else if (interrupt_src.Bit(2) && interrupt_enable.Bit(2)) { int_src = 2; interrupt_src -= 4; } + else if (interrupt_src.Bit(3) && interrupt_enable.Bit(3)) { int_src = 3; interrupt_src -= 8; } + else if (interrupt_src.Bit(4) && interrupt_enable.Bit(4)) { int_src = 4; interrupt_src -= 16; } + else { int_src = 5; } + } + + if ((interrupt_src & interrupt_enable) == 0) { FlagI = false; } + + Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src]; + break; + } + totalExecutedCycles++; + } + + // tracer stuff + + public Action TraceCallback; + + public string TraceHeader + { + get { return "LR35902: PC, machine code, mnemonic, operands, registers (A, F, B, C, D, E, H, L, SP), Cy, flags (ZNHCI)"; } + } + + public TraceInfo State(bool disassemble = true) + { + ushort notused; + + return new TraceInfo + { + Disassembly = string.Format( + "{0} ", + disassemble ? Disassemble(RegPC, ReadMemory, out notused) : "---").PadRight(40), + RegisterInfo = string.Format( + "A:{0:X2} F:{1:X2} B:{2:X2} C:{3:X2} D:{4:X2} E:{5:X2} H:{6:X2} L:{7:X2} SP:{8:X2} Cy:{9} LY:{10} {11}{12}{13}{14}{15}{16}", + Regs[A], + Regs[F], + Regs[B], + Regs[C], + Regs[D], + Regs[E], + Regs[H], + Regs[L], + Regs[SPl] | (Regs[SPh] << 8), + TotalExecutedCycles, + LY, + FlagZ ? "Z" : "z", + FlagN ? "N" : "n", + FlagH ? "H" : "h", + FlagC ? "C" : "c", + FlagI ? "I" : "i", + interrupts_enabled ? "E" : "e") + }; + } + // State Save/Load + + public void SyncState(Serializer ser) + { + ser.BeginSection("LR35902"); + ser.Sync("Regs", ref Regs, false); + ser.Sync("IRQ", ref interrupts_enabled); + ser.Sync("NMI", ref nonMaskableInterrupt); + ser.Sync("NMIPending", ref nonMaskableInterruptPending); + ser.Sync("IM", ref interruptMode); + ser.Sync("IFF1", ref iff1); + ser.Sync("IFF2", ref iff2); + ser.Sync("Halted", ref halted); + ser.Sync("ExecutedCycles", ref totalExecutedCycles); + ser.Sync("EI_pending", ref EI_pending); + ser.Sync("int_src", ref int_src); + + ser.Sync("instruction_pointer", ref instr_pntr); + ser.Sync("current instruction", ref cur_instr, false); + ser.Sync("CB Preifx", ref CB_prefix); + ser.Sync("Stopped", ref stopped); + ser.Sync("opcode", ref opcode); + ser.Sync("jammped", ref jammed); + ser.Sync("LY", ref LY); + ser.Sync("FlagI", ref FlagI); + + ser.EndSection(); + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs similarity index 94% rename from BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs rename to BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs index a5d798ec9b..4bbb453301 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/NewDisassembler.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/NewDisassembler.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using System.Text; -namespace BizHawk.Emulation.Common.Components.Z80GB +namespace BizHawk.Emulation.Common.Components.LR35902 { // adapted from the information at http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html - public class NewDisassembler + public sealed partial class LR35902 { static string[] table = { diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs new file mode 100644 index 0000000000..582c2cd437 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Operations.cs @@ -0,0 +1,467 @@ +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + public void Read_Func(ushort dest, ushort src_l, ushort src_h) + { + Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + } + + // speical read for POP AF that always clears the lower 4 bits of F + public void Read_Func_F(ushort dest, ushort src_l, ushort src_h) + { + Regs[dest] = (ushort)(ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)) & 0xF0); + } + + public void Write_Func(ushort dest_l, ushort dest_h, ushort src) + { + WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h]) << 8), (byte)Regs[src]); + } + + public void TR_Func(ushort dest, ushort src) + { + Regs[dest] = Regs[src]; + } + + public void ADD16_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + int Reg16_d = Regs[dest_l] | (Regs[dest_h] << 8); + int Reg16_s = Regs[src_l] | (Regs[src_h] << 8); + + Reg16_d += Reg16_s; + + FlagC = Reg16_d.Bit(16); + + ushort ans_l = (ushort)(Reg16_d & 0xFF); + ushort ans_h = (ushort)((Reg16_d & 0xFF00) >> 8); + + // redo for half carry flag + Reg16_d = Regs[dest_l] | ((Regs[dest_h] & 0x0F) << 8); + Reg16_s = Regs[src_l] | ((Regs[src_h] & 0x0F) << 8); + + Reg16_d += Reg16_s; + + FlagH = Reg16_d.Bit(12); + FlagN = false; + + Regs[dest_l] = ans_l; + Regs[dest_h] = ans_h; + } + + public void ADD8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d += Regs[src]; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d += (Regs[src] & 0xF); + + FlagH = Reg16_d.Bit(4); + + FlagN = false; + + Regs[dest] = ans; + } + + public void SUB8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d -= Regs[src]; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= (Regs[src] & 0xF); + + FlagH = Reg16_d.Bit(4); + FlagN = true; + + Regs[dest] = ans; + } + + public void BIT_Func(ushort bit, ushort src) + { + FlagZ = !Regs[src].Bit(bit); + FlagH = true; + FlagN = false; + } + + public void SET_Func(ushort bit, ushort src) + { + Regs[src] |= (ushort)(1 << bit); + } + + public void RES_Func(ushort bit, ushort src) + { + Regs[src] &= (ushort)(0xFF - (1 << bit)); + } + + public void ASGN_Func(ushort src, ushort val) + { + Regs[src] = val; + } + + public void SWAP_Func(ushort src) + { + ushort temp = (ushort)((Regs[src] << 4) & 0xF0); + Regs[src] = (ushort)(temp | (Regs[src] >> 4)); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + FlagC = false; + } + + public void SLA_Func(ushort src) + { + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)((Regs[src] << 1) & 0xFF); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + } + + public void SRA_Func(ushort src) + { + FlagC = Regs[src].Bit(0); + + ushort temp = (ushort)(Regs[src] & 0x80); // MSB doesn't change in this operation + + Regs[src] = (ushort)((Regs[src] >> 1) | temp); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + } + + public void SRL_Func(ushort src) + { + FlagC = Regs[src].Bit(0) ? true : false; + + Regs[src] = (ushort)(Regs[src] >> 1); + + FlagZ = Regs[src] == 0; + FlagH = false; + FlagN = false; + } + + public void CPL_Func(ushort src) + { + Regs[src] = (ushort)((~Regs[src]) & 0xFF); + + FlagH = true; + FlagN = true; + } + + public void CCF_Func(ushort src) + { + FlagC = !FlagC; + FlagH = false; + FlagN = false; + } + + public void SCF_Func(ushort src) + { + FlagC = true; + FlagH = false; + FlagN = false; + } + + public void AND8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] & Regs[src]); + + FlagZ = Regs[dest] == 0; + FlagC = false; + FlagH = true; + FlagN = false; + } + + public void OR8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] | Regs[src]); + + FlagZ = Regs[dest] == 0; + FlagC = false; + FlagH = false; + FlagN = false; + } + + public void XOR8_Func(ushort dest, ushort src) + { + Regs[dest] = (ushort)(Regs[dest] ^ Regs[src]); + + FlagZ = Regs[dest] == 0; + FlagC = false; + FlagH = false; + FlagN = false; + } + + public void CP8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d -= Regs[src]; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= (Regs[src] & 0xF); + + FlagH = Reg16_d.Bit(4); + + FlagN = true; + } + + public void RRC_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)((FlagC ? 0x80 : 0) | (Regs[src] >> 1)); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void RR_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + ushort c = (ushort)(FlagC ? 0x80 : 0); + + FlagC = Regs[src].Bit(0); + + Regs[src] = (ushort)(c | (Regs[src] >> 1)); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void RLC_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + ushort c = (ushort)(Regs[src].Bit(7) ? 1 : 0); + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void RL_Func(ushort src) + { + bool imm = src == Aim; + if (imm) { src = A; } + + ushort c = (ushort)(FlagC ? 1 : 0); + FlagC = Regs[src].Bit(7); + + Regs[src] = (ushort)(((Regs[src] << 1) & 0xFF) | c); + + FlagZ = imm ? false : (Regs[src] == 0); + FlagH = false; + FlagN = false; + } + + public void INC8_Func(ushort src) + { + int Reg16_d = Regs[src]; + Reg16_d += 1; + + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[src] & 0xF; + Reg16_d += 1; + + FlagH = Reg16_d.Bit(4); + FlagN = false; + + Regs[src] = ans; + } + + public void DEC8_Func(ushort src) + { + int Reg16_d = Regs[src]; + Reg16_d -= 1; + + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[src] & 0xF; + Reg16_d -= 1; + + FlagH = Reg16_d.Bit(4); + FlagN = true; + + Regs[src] = ans; + } + + public void INC16_Func(ushort src_l, ushort src_h) + { + int Reg16_d = Regs[src_l] | (Regs[src_h] << 8); + + Reg16_d += 1; + + Regs[src_l] = (ushort)(Reg16_d & 0xFF); + Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8); + } + + public void DEC16_Func(ushort src_l, ushort src_h) + { + int Reg16_d = Regs[src_l] | (Regs[src_h] << 8); + + Reg16_d -= 1; + + Regs[src_l] = (ushort)(Reg16_d & 0xFF); + Regs[src_h] = (ushort)((Reg16_d & 0xFF00) >> 8); + } + + public void ADC8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + int c = FlagC ? 1 : 0; + + Reg16_d += (Regs[src] + c); + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d += ((Regs[src] & 0xF) + c); + + FlagH = Reg16_d.Bit(4); + FlagN = false; + + Regs[dest] = ans; + } + + public void SBC8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + int c = FlagC ? 1 : 0; + + Reg16_d -= (Regs[src] + c); + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + // redo for half carry flag + Reg16_d = Regs[dest] & 0xF; + Reg16_d -= ((Regs[src] & 0xF) + c); + + FlagH = Reg16_d.Bit(4); + FlagN = true; + + Regs[dest] = ans; + } + + // DA code courtesy of AWJ: http://forums.nesdev.com/viewtopic.php?f=20&t=15944 + public void DA_Func(ushort src) + { + byte a = (byte)Regs[src]; + + if (!FlagN) + { // after an addition, adjust if (half-)carry occurred or if result is out of bounds + if (FlagC || a > 0x99) { a += 0x60; FlagC = true; } + if (FlagH || (a & 0x0f) > 0x09) { a += 0x6; } + } + else + { // after a subtraction, only adjust if (half-)carry occurred + if (FlagC) { a -= 0x60; } + if (FlagH) { a -= 0x6; } + } + + a &= 0xFF; + + Regs[src] = a; + + FlagZ = a == 0; + FlagH = false; + } + + // used for signed operations + public void ADDS_Func(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + int Reg16_d = Regs[dest_l]; + int Reg16_s = Regs[src_l]; + + Reg16_d += Reg16_s; + + ushort temp = 0; + + // since this is signed addition, calculate the high byte carry appropriately + if (Reg16_s.Bit(7)) + { + if (((Reg16_d & 0xFF) >= Regs[dest_l])) + { + temp = 0xFF; + } + else + { + temp = 0; + } + } + else + { + temp = (ushort)(Reg16_d.Bit(8) ? 1 : 0); + } + + ushort ans_l = (ushort)(Reg16_d & 0xFF); + + // JR operations do not effect flags + if (dest_l != PCl) + { + FlagC = Reg16_d.Bit(8); + + // redo for half carry flag + Reg16_d = Regs[dest_l] & 0xF; + Reg16_d += Regs[src_l] & 0xF; + + FlagH = Reg16_d.Bit(4); + FlagN = false; + FlagZ = false; + } + + Regs[dest_l] = ans_l; + Regs[dest_h] += temp; + Regs[dest_h] &= 0xFF; + + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt b/BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt new file mode 100644 index 0000000000..115b568cb8 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/ReadMe.txt @@ -0,0 +1 @@ +TODO: STOP for second byte nonzero diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs new file mode 100644 index 0000000000..8456f6a157 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Registers.cs @@ -0,0 +1,73 @@ +using System.Runtime.InteropServices; +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + // registers + + public static ushort PCl = 0; + public static ushort PCh = 1; + public static ushort SPl = 2; + public static ushort SPh = 3; + public static ushort A = 4; + public static ushort F = 5; + public static ushort B = 6; + public static ushort C = 7; + public static ushort D = 8; + public static ushort E = 9; + public static ushort H = 10; + public static ushort L = 11; + public static ushort W = 12; + public static ushort Z = 13; + public static ushort Aim = 14; // use this indicator for RLCA etc., since the Z flag is reset on those + + public ushort[] Regs = new ushort[14]; + + public bool FlagI; + + public bool FlagC + { + get { return (Regs[5] & 0x10) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x10) | (value ? 0x10 : 0x00)); } + } + + public bool FlagH + { + get { return (Regs[5] & 0x20) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x20) | (value ? 0x20 : 0x00)); } + } + + public bool FlagN + { + get { return (Regs[5] & 0x40) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x40) | (value ? 0x40 : 0x00)); } + } + + public bool FlagZ + { + get { return (Regs[5] & 0x80) != 0; } + set { Regs[5] = (ushort)((Regs[5] & ~0x80) | (value ? 0x80 : 0x00)); } + } + + public ushort RegPC + { + get { return (ushort)(Regs[0] | (Regs[1] << 8)); } + set + { + Regs[0] = (ushort)(value & 0xFF); + Regs[1] = (ushort)((value >> 8) & 0xFF); + } + } + + private void ResetRegisters() + { + for (int i=0; i < 14; i++) + { + Regs[i] = 0; + } + } + + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs new file mode 100644 index 0000000000..5979671599 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -0,0 +1,498 @@ +using System; + +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + // this contains the vectors of instrcution operations + // NOTE: This list is NOT confirmed accurate for each individual cycle + + private void NOP_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP }; + } + + private void INC_16(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {INC16, src_l, src_h, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + + private void DEC_16(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {DEC16, src_l, src_h, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {ADD16, dest_l, dest_h, src_l, src_h, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void REG_OP(ushort operation, ushort dest, ushort src) + { + cur_instr = new ushort[] + {operation, dest, src, + IDLE, + IDLE, + OP }; + } + + private void STOP_() + { + cur_instr = new ushort[] + {RD, Z, PCl, PCh, + INC16, PCl, PCh, + IDLE, + STOP }; + } + + private void HALT_() + { + if (!FlagI) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT }; + } + else + { + // if interrupts are disabled, + // a glitchy decrement to the program counter happens + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP_G}; + } + + } + + private void JR_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + ASGN, Z, 0, + IDLE, + ADDS, PCl, PCh, W, Z, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + OP }; + } + } + + private void JP_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + TR, PCl, W, + IDLE, + TR, PCh, Z, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + OP }; + } + } + + private void RET_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, PCl, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, PCh, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void RETI_() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, PCl, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, PCh, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + EI_RETI, + IDLE, + IDLE, + IDLE, + OP }; + } + + + private void RET_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + RD, PCl, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, PCh, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + } + + private void CALL_COND(bool cond) + { + if (cond) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + INC16, PCl, PCh, + IDLE, + IDLE, + RD, Z, PCl, PCh, + INC16, PCl, PCh, + IDLE, + DEC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + IDLE, + WR, SPl, SPh, PCh, + IDLE, + IDLE, + DEC16, SPl, SPh, + WR, SPl, SPh, PCl, + IDLE, + TR, PCl, W, + TR, PCh, Z, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + RD, Z, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + OP }; + } + } + + private void INT_OP(ushort operation, ushort src) + { + cur_instr = new ushort[] + {operation, src, + IDLE, + IDLE, + OP }; + } + + private void BIT_OP(ushort operation, ushort bit, ushort src) + { + cur_instr = new ushort[] + {operation, bit, src, + IDLE, + IDLE, + OP }; + } + + private void PUSH_(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, src_h, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, src_l, + IDLE, + IDLE, + IDLE, + OP }; + } + + // NOTE: this is the only instruction that can write to F + // but the bottom 4 bits of F are always 0, so instead of putting a special check for every read op + // let's just put a special operation here specifically for F + private void POP_(ushort src_l, ushort src_h) + { + if (src_l != F) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, src_l, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, src_h, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + OP }; + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD_F, src_l, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + RD, src_h, SPl, SPh, + IDLE, + INC16, SPl, SPh, + IDLE, + OP }; + } + } + + private void RST_(ushort n) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + IDLE, + IDLE, + WR, SPl, SPh, PCh, + DEC16, SPl, SPh, + IDLE, + IDLE, + WR, SPl, SPh, PCl, + IDLE, + ASGN, PCh, 0, + ASGN, PCl, n, + OP }; + } + + private void PREFIX_() + { + cur_instr = new ushort[] + {PREFIX, + IDLE, + IDLE, + OP }; + } + + private void DI_() + { + cur_instr = new ushort[] + {DI, + IDLE, + IDLE, + OP }; + } + + private void EI_() + { + cur_instr = new ushort[] + {EI, + IDLE, + IDLE, + OP }; + } + + private void JP_HL() + { + cur_instr = new ushort[] + {TR, PCl, L, + IDLE, + TR, PCh, H, + OP }; + } + + private void ADD_SP() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + ASGN, Z, 0, + IDLE, + ADDS, SPl, SPh, W, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_SP_HL() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + TR, SPl, L, + IDLE, + TR, SPh, H, + IDLE, + OP }; + } + + private void LD_HL_SPn() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, PCl, PCh, + IDLE, + INC16, PCl, PCh, + IDLE, + TR, H, SPh, + TR, L, SPl, + ASGN, Z, 0, + ADDS, L, H, W, Z, + OP }; + } + + private void JAM_() + { + cur_instr = new ushort[] + {JAM, + IDLE, + IDLE, + IDLE }; + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs new file mode 100644 index 0000000000..59b3ed63bc --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs @@ -0,0 +1,339 @@ +namespace BizHawk.Emulation.Common.Components.LR35902 +{ + public partial class LR35902 + { + private void INT_OP_IND(ushort operation, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, bit, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void BIT_TE_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, bit, Z, + IDLE, + OP }; + } + + private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, dest, Z, + INC16, src_l, src_h, + OP }; + } + + private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + operation, dest, Z, + IDLE, + OP }; + } + + private void LD_R_IM(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + WR, W, Z, dest_l, + IDLE, + INC16, W, Z, + IDLE, + WR, W, Z, dest_h, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_8_IND_INC(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + WR, dest_l, dest_h, src, + IDLE, + INC16, dest_l, dest_h, + IDLE, + OP }; + } + + private void LD_8_IND_DEC(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + WR, dest_l, dest_h, src, + IDLE, + DEC16, dest_l, dest_h, + IDLE, + OP }; + } + + private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + WR, dest_l, dest_h, src, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + WR, dest_l, dest_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + OP }; + } + + private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest, src_l, src_h, + IDLE, + DEC16, src_l, src_h, + IDLE, + OP }; + } + + private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, dest_l, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, dest_h, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + OP }; + } + + private void INC_8_IND(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC8, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void DEC_8_IND(ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, Z, src_l, src_h, + IDLE, + DEC8, Z, + IDLE, + WR, src_l, src_h, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + + private void LD_8_IND_FF(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, src_l, src_h, + INC16, src_l, src_h, + IDLE, + ASGN, Z , 0xFF, + RD, dest, W, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_FF_IND_8(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, dest_l, dest_h, + INC16, dest_l, dest_h, + IDLE, + ASGN, Z , 0xFF, + WR, W, Z, src, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_8_IND_FFC(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + ASGN, Z , 0xFF, + RD, dest, C, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_FFC_IND_8(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + ASGN, Z , 0xFF, + WR, C, Z, src, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_16_IND_FF(ushort dest, ushort src_l, ushort src_h) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, Z, src_l, src_h, + IDLE, + INC16, src_l, src_h, + IDLE, + RD, dest, W, Z, + IDLE, + IDLE, + IDLE, + OP }; + } + + private void LD_FF_IND_16(ushort dest_l, ushort dest_h, ushort src) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + RD, W, dest_l, dest_h, + IDLE, + INC16, dest_l, dest_h, + IDLE, + RD, Z, dest_l, dest_h, + IDLE, + INC16, dest_l, dest_h, + IDLE, + WR, W, Z, src, + IDLE, + IDLE, + IDLE, + OP }; + } + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs deleted file mode 100644 index a53ef53861..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Execute.cs +++ /dev/null @@ -1,2321 +0,0 @@ -using System; - -/* TODO: -+ http://www.devrs.com/gb/files/opcodes.html was used as a flags and timing reference. -+ [Opt] The DAA Table could be reduced from 128k to 8k. -+ Currently all instructions are using fixed m-cycles. Any instruction with variable cycles (ie branches) need to be checked. -+ The following instructions were rewritten or substantially modified for Z80-GB. They should be - treated with caution and checked further when the emulator is farther along. - - ADD - ADC - SUB - SBC - AND - OR - XOR - CP - SWAP - DAA - RLCA - RLA - RRCA - RRA - RLC - RL - RRC - RR - SLA - SRA - SRL -*/ - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - public int TotalExecutedCycles; - public int PendingCycles; - - bool Interruptable; - - public void ExecuteInstruction() - { - byte TB; byte TB2; sbyte TSB; ushort TUS; int TI1; int TI2; int TIR; - while (RegPC.Word == 0x031A) - break; - byte op = ReadMemory(RegPC.Word++); - int mCycleTime = mCycleTable[op]; - PendingCycles -= mCycleTime; - TotalExecutedCycles += mCycleTime; - switch (op) - { - case 0x00: // NOP - break; - case 0x01: // LD BC, nn - RegBC.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x02: // LD (BC), A - WriteMemory(RegBC.Word, RegAF.High); - break; - case 0x03: // INC BC - ++RegBC.Word; - break; - case 0x04: // INC B - RegAF.Low = (byte)(IncTable[++RegBC.High] | (RegAF.Low & 16)); - break; - case 0x05: // DEC B - RegAF.Low = (byte)(DecTable[--RegBC.High] | (RegAF.Low & 16)); - break; - case 0x06: // LD B, n - RegBC.High = ReadMemory(RegPC.Word++); - break; - case 0x07: // RLCA - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High >> 7) | (RegAF.High << 1)); - break; - case 0x08: // LD (imm), SP - TUS = (ushort)(ReadMemory(RegPC.Word++) | (ReadMemory(RegPC.Word++) << 8)); - WriteMemory(TUS++, RegSP.Low); - WriteMemory(TUS, RegSP.High); - break; - case 0x09: // ADD HL, BC - TI1 = (short)RegHL.Word; TI2 = (short)RegBC.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x0A: // LD A, (BC) - RegAF.High = ReadMemory(RegBC.Word); - break; - case 0x0B: // DEC BC - --RegBC.Word; - break; - case 0x0C: // INC C - RegAF.Low = (byte)(IncTable[++RegBC.Low] | (RegAF.Low & 16)); - break; - case 0x0D: // DEC C - RegAF.Low = (byte)(DecTable[--RegBC.Low] | (RegAF.Low & 16)); - break; - case 0x0E: // LD C, n - RegBC.Low = ReadMemory(RegPC.Word++); - break; - case 0x0F: // RRCA - RegAF.High = (byte)((RegAF.High << 7) | (RegAF.High >> 1)); - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - break; - case 0x10: // STOP - Console.WriteLine("STOP!!!!!!!!!!!!!!!!!!!!!!"); // TODO this instruction is actually STOP. not DJNZ d. - throw new Exception("CPU stopped. What now?"); - case 0x11: // LD DE, nn - RegDE.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x12: // LD (DE), A - WriteMemory(RegDE.Word, RegAF.High); - break; - case 0x13: // INC DE - ++RegDE.Word; - break; - case 0x14: // INC D - RegAF.Low = (byte)(IncTable[++RegDE.High] | (RegAF.Low & 16)); - break; - case 0x15: // DEC D - RegAF.Low = (byte)(DecTable[--RegDE.High] | (RegAF.Low & 16)); - break; - case 0x16: // LD D, n - RegDE.High = ReadMemory(RegPC.Word++); - break; - case 0x17: // RLA - TB = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - break; - case 0x18: // JR d - TSB = (sbyte)ReadMemory(RegPC.Word++); - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x19: // ADD HL, DE - TI1 = (short)RegHL.Word; TI2 = (short)RegDE.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x1A: // LD A, (DE) - RegAF.High = ReadMemory(RegDE.Word); - break; - case 0x1B: // DEC DE - --RegDE.Word; - break; - case 0x1C: // INC E - RegAF.Low = (byte)(IncTable[++RegDE.Low] | (RegAF.Low & 16)); - break; - case 0x1D: // DEC E - RegAF.Low = (byte)(DecTable[--RegDE.Low] | (RegAF.Low & 16)); - break; - case 0x1E: // LD E, n - RegDE.Low = ReadMemory(RegPC.Word++); - break; - case 0x1F: // RRA - TB = (byte)((RegAF.High & 0x1) << 4); - RegAF.High = (byte)((RegAF.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - break; - case 0x20: // JR NZ, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (!FlagZ) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x21: // LD HL, nn - RegHL.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x22: // LDI (HL), A - WriteMemory(RegHL.Word++, RegAF.High); - break; - case 0x23: // INC HL - ++RegHL.Word; - break; - case 0x24: // INC H - RegAF.Low = (byte)(IncTable[++RegHL.High] | (RegAF.Low & 16)); - break; - case 0x25: // DEC H - RegAF.Low = (byte)(DecTable[--RegHL.High] | (RegAF.Low & 16)); - break; - case 0x26: // LD H, n - RegHL.High = ReadMemory(RegPC.Word++); - break; - case 0x27: // DAA - RegAF.Word = TableDaa[RegAF.Word]; - break; - case 0x28: // JR Z, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (FlagZ) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x29: // ADD HL, HL - TI1 = (short)RegHL.Word; TI2 = (short)RegHL.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x2A: // LDI A, (HL) - RegAF.High = ReadMemory(RegHL.Word++); - break; - case 0x2B: // DEC HL - --RegHL.Word; - break; - case 0x2C: // INC L - RegAF.Low = (byte)(IncTable[++RegHL.Low] | (RegAF.Low & 16)); - break; - case 0x2D: // DEC L - RegAF.Low = (byte)(DecTable[--RegHL.Low] | (RegAF.Low & 16)); - break; - case 0x2E: // LD L, n - RegHL.Low = ReadMemory(RegPC.Word++); - break; - case 0x2F: // CPL - RegAF.High ^= 0xFF; FlagH = true; FlagN = true; - break; - case 0x30: // JR NC, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (!FlagC) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x31: // LD SP, nn - RegSP.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0x32: // LDD (HL), A - WriteMemory(RegHL.Word--, RegAF.High); - break; - case 0x33: // INC SP - ++RegSP.Word; - break; - case 0x34: // INC (HL) - TB = ReadMemory(RegHL.Word); RegAF.Low = (byte)(IncTable[++TB] | (RegAF.Low & 16)); WriteMemory(RegHL.Word, TB); - break; - case 0x35: // DEC (HL) - TB = ReadMemory(RegHL.Word); RegAF.Low = (byte)(DecTable[--TB] | (RegAF.Low & 16)); WriteMemory(RegHL.Word, TB); - break; - case 0x36: // LD (HL), n - WriteMemory(RegHL.Word, ReadMemory(RegPC.Word++)); - break; - case 0x37: // SCF - FlagH = false; FlagN = false; FlagC = true; - break; - case 0x38: // JR C, d - TSB = (sbyte)ReadMemory(RegPC.Word++); - if (FlagC) - RegPC.Word = (ushort)(RegPC.Word + TSB); - break; - case 0x39: // ADD HL, SP - TI1 = (short)RegHL.Word; TI2 = (short)RegSP.Word; TIR = TI1 + TI2; - TUS = (ushort)TIR; - FlagH = ((TI1 & 0xFFF) + (TI2 & 0xFFF)) > 0xFFF; - FlagN = false; - FlagC = ((ushort)TI1 + (ushort)TI2) > 0xFFFF; - RegHL.Word = TUS; - break; - case 0x3A: // LDD A, (HL) - RegAF.High = ReadMemory(RegHL.Word--); - break; - case 0x3B: // DEC SP - --RegSP.Word; - break; - case 0x3C: // INC A - RegAF.Low = (byte)(IncTable[++RegAF.High] | (RegAF.Low & 16)); - break; - case 0x3D: // DEC A - RegAF.Low = (byte)(DecTable[--RegAF.High] | (RegAF.Low & 16)); - break; - case 0x3E: // LD A, n - RegAF.High = ReadMemory(RegPC.Word++); - break; - case 0x3F: // CCF - FlagH = FlagC; FlagN = false; FlagC ^= true; - break; - case 0x40: // LD B, B - break; - case 0x41: // LD B, C - RegBC.High = RegBC.Low; - break; - case 0x42: // LD B, D - RegBC.High = RegDE.High; - break; - case 0x43: // LD B, E - RegBC.High = RegDE.Low; - break; - case 0x44: // LD B, H - RegBC.High = RegHL.High; - break; - case 0x45: // LD B, L - RegBC.High = RegHL.Low; - break; - case 0x46: // LD B, (HL) - RegBC.High = ReadMemory(RegHL.Word); - break; - case 0x47: // LD B, A - RegBC.High = RegAF.High; - break; - case 0x48: // LD C, B - RegBC.Low = RegBC.High; - break; - case 0x49: // LD C, C - break; - case 0x4A: // LD C, D - RegBC.Low = RegDE.High; - break; - case 0x4B: // LD C, E - RegBC.Low = RegDE.Low; - break; - case 0x4C: // LD C, H - RegBC.Low = RegHL.High; - break; - case 0x4D: // LD C, L - RegBC.Low = RegHL.Low; - break; - case 0x4E: // LD C, (HL) - RegBC.Low = ReadMemory(RegHL.Word); - break; - case 0x4F: // LD C, A - RegBC.Low = RegAF.High; - break; - case 0x50: // LD D, B - RegDE.High = RegBC.High; - break; - case 0x51: // LD D, C - RegDE.High = RegBC.Low; - break; - case 0x52: // LD D, D - break; - case 0x53: // LD D, E - RegDE.High = RegDE.Low; - break; - case 0x54: // LD D, H - RegDE.High = RegHL.High; - break; - case 0x55: // LD D, L - RegDE.High = RegHL.Low; - break; - case 0x56: // LD D, (HL) - RegDE.High = ReadMemory(RegHL.Word); - break; - case 0x57: // LD D, A - RegDE.High = RegAF.High; - break; - case 0x58: // LD E, B - RegDE.Low = RegBC.High; - break; - case 0x59: // LD E, C - RegDE.Low = RegBC.Low; - break; - case 0x5A: // LD E, D - RegDE.Low = RegDE.High; - break; - case 0x5B: // LD E, E - break; - case 0x5C: // LD E, H - RegDE.Low = RegHL.High; - break; - case 0x5D: // LD E, L - RegDE.Low = RegHL.Low; - break; - case 0x5E: // LD E, (HL) - RegDE.Low = ReadMemory(RegHL.Word); - break; - case 0x5F: // LD E, A - RegDE.Low = RegAF.High; - break; - case 0x60: // LD H, B - RegHL.High = RegBC.High; - break; - case 0x61: // LD H, C - RegHL.High = RegBC.Low; - break; - case 0x62: // LD H, D - RegHL.High = RegDE.High; - break; - case 0x63: // LD H, E - RegHL.High = RegDE.Low; - break; - case 0x64: // LD H, H - break; - case 0x65: // LD H, L - RegHL.High = RegHL.Low; - break; - case 0x66: // LD H, (HL) - RegHL.High = ReadMemory(RegHL.Word); - break; - case 0x67: // LD H, A - RegHL.High = RegAF.High; - break; - case 0x68: // LD L, B - RegHL.Low = RegBC.High; - break; - case 0x69: // LD L, C - RegHL.Low = RegBC.Low; - break; - case 0x6A: // LD L, D - RegHL.Low = RegDE.High; - break; - case 0x6B: // LD L, E - RegHL.Low = RegDE.Low; - break; - case 0x6C: // LD L, H - RegHL.Low = RegHL.High; - break; - case 0x6D: // LD L, L - break; - case 0x6E: // LD L, (HL) - RegHL.Low = ReadMemory(RegHL.Word); - break; - case 0x6F: // LD L, A - RegHL.Low = RegAF.High; - break; - case 0x70: // LD (HL), B - WriteMemory(RegHL.Word, RegBC.High); - break; - case 0x71: // LD (HL), C - WriteMemory(RegHL.Word, RegBC.Low); - break; - case 0x72: // LD (HL), D - WriteMemory(RegHL.Word, RegDE.High); - break; - case 0x73: // LD (HL), E - WriteMemory(RegHL.Word, RegDE.Low); - break; - case 0x74: // LD (HL), H - WriteMemory(RegHL.Word, RegHL.High); - break; - case 0x75: // LD (HL), L - WriteMemory(RegHL.Word, RegHL.Low); - break; - case 0x76: // HALT - Halt(); - break; - case 0x77: // LD (HL), A - WriteMemory(RegHL.Word, RegAF.High); - break; - case 0x78: // LD A, B - RegAF.High = RegBC.High; - break; - case 0x79: // LD A, C - RegAF.High = RegBC.Low; - break; - case 0x7A: // LD A, D - RegAF.High = RegDE.High; - break; - case 0x7B: // LD A, E - RegAF.High = RegDE.Low; - break; - case 0x7C: // LD A, H - RegAF.High = RegHL.High; - break; - case 0x7D: // LD A, L - RegAF.High = RegHL.Low; - break; - case 0x7E: // LD A, (HL) - RegAF.High = ReadMemory(RegHL.Word); - break; - case 0x7F: // LD A, A - break; - case 0x80: // ADD B - TI1 = RegAF.High + RegBC.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x81: // ADD C - TI1 = RegAF.High + RegBC.Low; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.Low & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x82: // ADD D - TI1 = RegAF.High + RegDE.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x83: // ADD E - TI1 = RegAF.High + RegDE.Low; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.Low & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x84: // ADD H - TI1 = RegAF.High + RegHL.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x85: // ADD L - TI1 = RegAF.High + RegHL.Low; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.Low & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x86: // ADD (HL) - TB = ReadMemory(RegHL.Word); - TI1 = RegAF.High + TB; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x87: // ADD A - TI1 = RegAF.High + RegAF.High; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegAF.High & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x88: // ADC B - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegBC.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x89: // ADC C - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegBC.Low + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegBC.Low & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8A: // ADC D - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegDE.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8B: // ADC E - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegDE.Low + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegDE.Low & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8C: // ADC H - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegHL.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8D: // ADC L - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegHL.Low + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegHL.Low & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8E: // ADC (HL) - TB = ReadMemory(RegHL.Word); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + TB + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x8F: // ADC A - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + RegAF.High + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (RegAF.High & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x90: // SUB B - TI1 = RegAF.High - RegBC.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x91: // SUB C - TI1 = RegAF.High - RegBC.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.Low & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x92: // SUB D - TI1 = RegAF.High - RegDE.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x93: // SUB E - TI1 = RegAF.High - RegDE.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.Low & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x94: // SUB H - TI1 = RegAF.High - RegHL.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x95: // SUB L - TI1 = RegAF.High - RegHL.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.Low & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x96: // SUB (HL) - TB = ReadMemory(RegHL.Word); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x97: // SUB A - TI1 = RegAF.High - RegAF.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegAF.High & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x98: // SBC B - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegBC.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x99: // SBC C - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegBC.Low - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.Low & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9A: // SBC D - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegDE.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9B: // SBC E - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegDE.Low - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.Low & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9C: // SBC H - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegHL.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9D: // SBC L - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegHL.Low - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.Low & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9E: // SBC (HL) - TB = ReadMemory(RegHL.Word); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - TB - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0x9F: // SBC A - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - RegAF.High - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegAF.High & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xA0: // AND B - RegAF.High &= RegBC.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA1: // AND C - RegAF.High &= RegBC.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA2: // AND D - RegAF.High &= RegDE.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA3: // AND E - RegAF.High &= RegDE.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA4: // AND H - RegAF.High &= RegHL.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA5: // AND L - RegAF.High &= RegHL.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA6: // AND (HL) - RegAF.High &= ReadMemory(RegHL.Word); - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA7: // AND A - RegAF.High &= RegAF.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xA8: // XOR B - RegAF.High ^= RegBC.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xA9: // XOR C - RegAF.High ^= RegBC.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAA: // XOR D - RegAF.High ^= RegDE.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAB: // XOR E - RegAF.High ^= RegDE.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAC: // XOR H - RegAF.High ^= RegHL.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAD: // XOR L - RegAF.High ^= RegHL.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAE: // XOR (HL) - RegAF.High ^= ReadMemory(RegHL.Word); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xAF: // XOR A - RegAF.High ^= RegAF.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB0: // OR B - RegAF.High |= RegBC.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB1: // OR C - RegAF.High |= RegBC.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB2: // OR D - RegAF.High |= RegDE.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB3: // OR E - RegAF.High |= RegDE.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB4: // OR H - RegAF.High |= RegHL.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB5: // OR L - RegAF.High |= RegHL.Low; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB6: // OR (HL) - RegAF.High |= ReadMemory(RegHL.Word); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB7: // OR A - RegAF.High |= RegAF.High; - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xB8: // CP B - TI1 = RegAF.High - RegBC.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xB9: // CP C - TI1 = RegAF.High - RegBC.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegBC.Low & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBA: // CP D - TI1 = RegAF.High - RegDE.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBB: // CP E - TI1 = RegAF.High - RegDE.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegDE.Low & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBC: // CP H - TI1 = RegAF.High - RegHL.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBD: // CP L - TI1 = RegAF.High - RegHL.Low; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegHL.Low & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBE: // CP (HL) - TB = ReadMemory(RegHL.Word); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xBF: // CP A - TI1 = RegAF.High - RegAF.High; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (RegAF.High & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xC0: // RET NZ - if (!FlagZ) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xC1: // POP BC - RegBC.Low = ReadMemory(RegSP.Word++); RegBC.High = ReadMemory(RegSP.Word++); - break; - case 0xC2: // JP NZ, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagZ) - RegPC.Word = TUS; - break; - case 0xC3: // JP nn - RegPC.Word = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - break; - case 0xC4: // CALL NZ, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagZ) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xC5: // PUSH BC - WriteMemory(--RegSP.Word, RegBC.High); WriteMemory(--RegSP.Word, RegBC.Low); - break; - case 0xC6: // ADD n - TB = ReadMemory(RegPC.Word++); - TI1 = RegAF.High + TB; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xC7: // RST $00 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x00; - break; - case 0xC8: // RET Z - if (FlagZ) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xC9: // RET - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xCA: // JP Z, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagZ) - RegPC.Word = TUS; - break; - case 0xCB: // (Prefix) - op = ReadMemory(RegPC.Word++); - mCycleTime = cbMCycleTable[op]; - PendingCycles -= mCycleTime; - TotalExecutedCycles += mCycleTime; - switch (op) - { - case 0x00: // RLC B - RegAF.Low = (byte)((RegBC.High & 0x80) >> 3); - RegBC.High = (byte)((RegBC.High >> 7) | (RegBC.High << 1)); - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x01: // RLC C - RegAF.Low = (byte)((RegBC.Low & 0x80) >> 3); - RegBC.Low = (byte)((RegBC.Low >> 7) | (RegBC.Low << 1)); - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x02: // RLC D - RegAF.Low = (byte)((RegDE.High & 0x80) >> 3); - RegDE.High = (byte)((RegDE.High >> 7) | (RegDE.High << 1)); - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x03: // RLC E - RegAF.Low = (byte)((RegDE.Low & 0x80) >> 3); - RegDE.Low = (byte)((RegDE.Low >> 7) | (RegDE.Low << 1)); - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x04: // RLC H - RegAF.Low = (byte)((RegHL.High & 0x80) >> 3); - RegHL.High = (byte)((RegHL.High >> 7) | (RegHL.High << 1)); - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x05: // RLC L - RegAF.Low = (byte)((RegHL.Low & 0x80) >> 3); - RegHL.Low = (byte)((RegHL.Low >> 7) | (RegHL.Low << 1)); - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x06: // RLC (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = (byte)((TB & 0x80) >> 3); - TB = (byte)((TB >> 7) | (TB << 1)); - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x07: // RLC A - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High >> 7) | (RegAF.High << 1)); - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x08: // RRC B - RegBC.High = (byte)((RegBC.High << 7) | (RegBC.High >> 1)); - RegAF.Low = (byte)((RegBC.High & 0x80) >> 3); - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x09: // RRC C - RegBC.Low = (byte)((RegBC.Low << 7) | (RegBC.Low >> 1)); - RegAF.Low = (byte)((RegBC.Low & 0x80) >> 3); - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x0A: // RRC D - RegDE.High = (byte)((RegDE.High << 7) | (RegDE.High >> 1)); - RegAF.Low = (byte)((RegDE.High & 0x80) >> 3); - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x0B: // RRC E - RegDE.Low = (byte)((RegDE.Low << 7) | (RegDE.Low >> 1)); - RegAF.Low = (byte)((RegDE.Low & 0x80) >> 3); - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x0C: // RRC H - RegHL.High = (byte)((RegHL.High << 7) | (RegHL.High >> 1)); - RegAF.Low = (byte)((RegHL.High & 0x80) >> 3); - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x0D: // RRC L - RegHL.Low = (byte)((RegHL.Low << 7) | (RegHL.Low >> 1)); - RegAF.Low = (byte)((RegHL.Low & 0x80) >> 3); - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x0E: // RRC (HL) - TB = ReadMemory(RegHL.Word); - TB = (byte)((TB << 7) | (TB >> 1)); - RegAF.Low = (byte)((TB & 0x80) >> 3); - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x0F: // RRC A - RegAF.High = (byte)((RegAF.High << 7) | (RegAF.High >> 1)); - RegAF.Low = (byte)((RegAF.High & 0x80) >> 3); - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x10: // RL B - TB = (byte)((RegBC.High & 0x80) >> 3); - RegBC.High = (byte)((RegBC.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x11: // RL C - TB = (byte)((RegBC.Low & 0x80) >> 3); - RegBC.Low = (byte)((RegBC.Low << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x12: // RL D - TB = (byte)((RegDE.High & 0x80) >> 3); - RegDE.High = (byte)((RegDE.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x13: // RL E - TB = (byte)((RegDE.Low & 0x80) >> 3); - RegDE.Low = (byte)((RegDE.Low << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x14: // RL H - TB = (byte)((RegHL.High & 0x80) >> 3); - RegHL.High = (byte)((RegHL.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x15: // RL L - TB = (byte)((RegHL.Low & 0x80) >> 3); - RegHL.Low = (byte)((RegHL.Low << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x16: // RL (HL) - TB2 = ReadMemory(RegHL.Word); - TB = (byte)((TB2 & 0x80) >> 3); - TB2 = (byte)((TB2 << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (TB2 == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB2); - break; - case 0x17: // RL A - TB = (byte)((RegAF.High & 0x80) >> 3); - RegAF.High = (byte)((RegAF.High << 1) | (FlagC ? 1 : 0)); - RegAF.Low = TB; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x18: // RR B - TB = (byte)((RegBC.High & 0x1) << 4); - RegBC.High = (byte)((RegBC.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x19: // RR C - TB = (byte)((RegBC.Low & 0x1) << 4); - RegBC.Low = (byte)((RegBC.Low >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x1A: // RR D - TB = (byte)((RegDE.High & 0x1) << 4); - RegDE.High = (byte)((RegDE.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x1B: // RR E - TB = (byte)((RegDE.Low & 0x1) << 4); - RegDE.Low = (byte)((RegDE.Low >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x1C: // RR H - TB = (byte)((RegHL.High & 0x1) << 4); - RegHL.High = (byte)((RegHL.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x1D: // RR L - TB = (byte)((RegHL.Low & 0x1) << 4); - RegHL.Low = (byte)((RegHL.Low >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x1E: // RR (HL) - TB2 = ReadMemory(RegHL.Word); - TB = (byte)((TB2 & 0x1) << 4); - TB2 = (byte)((TB2 >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (TB2 == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB2); - break; - case 0x1F: // RR A - TB = (byte)((RegAF.High & 0x1) << 4); - RegAF.High = (byte)((RegAF.High >> 1) | (FlagC ? 0x80 : 0)); - RegAF.Low = TB; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x20: // SLA B - RegAF.Low = 0; - if ((RegBC.High & 0x80) != 0) RegAF.Low |= 0x10; - RegBC.High <<= 1; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x21: // SLA C - RegAF.Low = 0; - if ((RegBC.Low & 0x80) != 0) RegAF.Low |= 0x10; - RegBC.Low <<= 1; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x22: // SLA D - RegAF.Low = 0; - if ((RegDE.High & 0x80) != 0) RegAF.Low |= 0x10; - RegDE.High <<= 1; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x23: // SLA E - RegAF.Low = 0; - if ((RegDE.Low & 0x80) != 0) RegAF.Low |= 0x10; - RegDE.Low <<= 1; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x24: // SLA H - RegAF.Low = 0; - if ((RegHL.High & 0x80) != 0) RegAF.Low |= 0x10; - RegHL.High <<= 1; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x25: // SLA L - RegAF.Low = 0; - if ((RegHL.Low & 0x80) != 0) RegAF.Low |= 0x10; - RegHL.Low <<= 1; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x26: // SLA (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = 0; - if ((TB & 0x80) != 0) RegAF.Low |= 0x10; - TB <<= 1; - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x27: // SLA A - RegAF.Low = 0; - if ((RegAF.High & 0x80) != 0) RegAF.Low |= 0x10; - RegAF.High <<= 1; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x28: // SRA B - RegAF.Low = 0; - if ((RegBC.High & 1) != 0) RegAF.Low |= 0x10; - RegBC.High = (byte)((RegBC.High >> 1) | (RegBC.High & 0x80)); - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x29: // SRA C - RegAF.Low = 0; - if ((RegBC.Low & 1) != 0) RegAF.Low |= 0x10; - RegBC.Low = (byte)((RegBC.Low >> 1) | (RegBC.Low & 0x80)); - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x2A: // SRA D - RegAF.Low = 0; - if ((RegDE.High & 1) != 0) RegAF.Low |= 0x10; - RegDE.High = (byte)((RegDE.High >> 1) | (RegDE.High & 0x80)); - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x2B: // SRA E - RegAF.Low = 0; - if ((RegDE.Low & 1) != 0) RegAF.Low |= 0x10; - RegDE.Low = (byte)((RegDE.Low >> 1) | (RegDE.Low & 0x80)); - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x2C: // SRA H - RegAF.Low = 0; - if ((RegHL.High & 1) != 0) RegAF.Low |= 0x10; - RegHL.High = (byte)((RegHL.High >> 1) | (RegHL.High & 0x80)); - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x2D: // SRA L - RegAF.Low = 0; - if ((RegHL.Low & 1) != 0) RegAF.Low |= 0x10; - RegHL.Low = (byte)((RegHL.Low >> 1) | (RegHL.Low & 0x80)); - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x2E: // SRA (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = 0; - if ((TB & 1) != 0) RegAF.Low |= 0x10; - TB = (byte)((TB >> 1) | (TB & 0x80)); - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x2F: // SRA A - RegAF.Low = 0; - if ((RegAF.High & 1) != 0) RegAF.Low |= 0x10; - RegAF.High = (byte)((RegAF.High >> 1) | (RegAF.High & 0x80)); - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x30: // SWAP B - RegBC.High = SwapTable[RegBC.High]; - FlagZ = (RegBC.High == 0); - break; - case 0x31: // SWAP C - RegBC.Low = SwapTable[RegBC.Low]; - FlagZ = (RegBC.Low == 0); - break; - case 0x32: // SWAP D - RegDE.High = SwapTable[RegDE.High]; - FlagZ = (RegDE.High == 0); - break; - case 0x33: // SWAP E - RegDE.Low = SwapTable[RegDE.Low]; - FlagZ = (RegDE.Low == 0); - break; - case 0x34: // SWAP H - RegHL.High = SwapTable[RegHL.High]; - FlagZ = (RegHL.High == 0); - break; - case 0x35: // SWAP L - RegHL.Low = SwapTable[RegHL.Low]; - FlagZ = (RegHL.Low == 0); - break; - case 0x36: // SWAP (HL) - TB = SwapTable[ReadMemory(RegHL.Word)]; - WriteMemory(RegHL.Word, TB); - FlagZ = (TB == 0); - break; - case 0x37: // SWAP A - RegAF.High = SwapTable[RegAF.High]; - FlagZ = (RegAF.High == 0); - break; - case 0x38: // SRL B - RegAF.Low = 0; - if ((RegBC.High & 1) != 0) RegAF.Low |= 0x10; - RegBC.High >>= 1; - if (RegBC.High == 0) RegAF.Low |= 0x80; - break; - case 0x39: // SRL C - RegAF.Low = 0; - if ((RegBC.Low & 1) != 0) RegAF.Low |= 0x10; - RegBC.Low >>= 1; - if (RegBC.Low == 0) RegAF.Low |= 0x80; - break; - case 0x3A: // SRL D - RegAF.Low = 0; - if ((RegDE.High & 1) != 0) RegAF.Low |= 0x10; - RegDE.High >>= 1; - if (RegDE.High == 0) RegAF.Low |= 0x80; - break; - case 0x3B: // SRL E - RegAF.Low = 0; - if ((RegDE.Low & 1) != 0) RegAF.Low |= 0x10; - RegDE.Low >>= 1; - if (RegDE.Low == 0) RegAF.Low |= 0x80; - break; - case 0x3C: // SRL H - RegAF.Low = 0; - if ((RegHL.High & 1) != 0) RegAF.Low |= 0x10; - RegHL.High >>= 1; - if (RegHL.High == 0) RegAF.Low |= 0x80; - break; - case 0x3D: // SRL L - RegAF.Low = 0; - if ((RegHL.Low & 1) != 0) RegAF.Low |= 0x10; - RegHL.Low >>= 1; - if (RegHL.Low == 0) RegAF.Low |= 0x80; - break; - case 0x3E: // SRL (HL) - TB = ReadMemory(RegHL.Word); - RegAF.Low = 0; - if ((TB & 1) != 0) RegAF.Low |= 0x10; - TB >>= 1; - if (TB == 0) RegAF.Low |= 0x80; - WriteMemory(RegHL.Word, TB); - break; - case 0x3F: // SRL A - RegAF.Low = 0; - if ((RegAF.High & 1) != 0) RegAF.Low |= 0x10; - RegAF.High >>= 1; - if (RegAF.High == 0) RegAF.Low |= 0x80; - break; - case 0x40: // BIT 0, B - FlagZ = (RegBC.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x41: // BIT 0, C - FlagZ = (RegBC.Low & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x42: // BIT 0, D - FlagZ = (RegDE.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x43: // BIT 0, E - FlagZ = (RegDE.Low & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x44: // BIT 0, H - FlagZ = (RegHL.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x45: // BIT 0, L - FlagZ = (RegHL.Low & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x46: // BIT 0, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x47: // BIT 0, A - FlagZ = (RegAF.High & 0x01) == 0; - FlagH = true; - FlagN = false; - break; - case 0x48: // BIT 1, B - FlagZ = (RegBC.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x49: // BIT 1, C - FlagZ = (RegBC.Low & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4A: // BIT 1, D - FlagZ = (RegDE.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4B: // BIT 1, E - FlagZ = (RegDE.Low & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4C: // BIT 1, H - FlagZ = (RegHL.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4D: // BIT 1, L - FlagZ = (RegHL.Low & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4E: // BIT 1, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x4F: // BIT 1, A - FlagZ = (RegAF.High & 0x02) == 0; - FlagH = true; - FlagN = false; - break; - case 0x50: // BIT 2, B - FlagZ = (RegBC.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x51: // BIT 2, C - FlagZ = (RegBC.Low & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x52: // BIT 2, D - FlagZ = (RegDE.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x53: // BIT 2, E - FlagZ = (RegDE.Low & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x54: // BIT 2, H - FlagZ = (RegHL.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x55: // BIT 2, L - FlagZ = (RegHL.Low & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x56: // BIT 2, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x57: // BIT 2, A - FlagZ = (RegAF.High & 0x04) == 0; - FlagH = true; - FlagN = false; - break; - case 0x58: // BIT 3, B - FlagZ = (RegBC.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x59: // BIT 3, C - FlagZ = (RegBC.Low & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5A: // BIT 3, D - FlagZ = (RegDE.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5B: // BIT 3, E - FlagZ = (RegDE.Low & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5C: // BIT 3, H - FlagZ = (RegHL.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5D: // BIT 3, L - FlagZ = (RegHL.Low & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5E: // BIT 3, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x5F: // BIT 3, A - FlagZ = (RegAF.High & 0x08) == 0; - FlagH = true; - FlagN = false; - break; - case 0x60: // BIT 4, B - FlagZ = (RegBC.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x61: // BIT 4, C - FlagZ = (RegBC.Low & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x62: // BIT 4, D - FlagZ = (RegDE.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x63: // BIT 4, E - FlagZ = (RegDE.Low & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x64: // BIT 4, H - FlagZ = (RegHL.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x65: // BIT 4, L - FlagZ = (RegHL.Low & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x66: // BIT 4, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x67: // BIT 4, A - FlagZ = (RegAF.High & 0x10) == 0; - FlagH = true; - FlagN = false; - break; - case 0x68: // BIT 5, B - FlagZ = (RegBC.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x69: // BIT 5, C - FlagZ = (RegBC.Low & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6A: // BIT 5, D - FlagZ = (RegDE.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6B: // BIT 5, E - FlagZ = (RegDE.Low & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6C: // BIT 5, H - FlagZ = (RegHL.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6D: // BIT 5, L - FlagZ = (RegHL.Low & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6E: // BIT 5, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x6F: // BIT 5, A - FlagZ = (RegAF.High & 0x20) == 0; - FlagH = true; - FlagN = false; - break; - case 0x70: // BIT 6, B - FlagZ = (RegBC.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x71: // BIT 6, C - FlagZ = (RegBC.Low & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x72: // BIT 6, D - FlagZ = (RegDE.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x73: // BIT 6, E - FlagZ = (RegDE.Low & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x74: // BIT 6, H - FlagZ = (RegHL.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x75: // BIT 6, L - FlagZ = (RegHL.Low & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x76: // BIT 6, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x77: // BIT 6, A - FlagZ = (RegAF.High & 0x40) == 0; - FlagH = true; - FlagN = false; - break; - case 0x78: // BIT 7, B - FlagZ = (RegBC.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x79: // BIT 7, C - FlagZ = (RegBC.Low & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7A: // BIT 7, D - FlagZ = (RegDE.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7B: // BIT 7, E - FlagZ = (RegDE.Low & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7C: // BIT 7, H - FlagZ = (RegHL.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7D: // BIT 7, L - FlagZ = (RegHL.Low & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7E: // BIT 7, (HL) - FlagZ = (ReadMemory(RegHL.Word) & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x7F: // BIT 7, A - FlagZ = (RegAF.High & 0x80) == 0; - FlagH = true; - FlagN = false; - break; - case 0x80: // RES 0, B - RegBC.High &= unchecked((byte)~0x01); - break; - case 0x81: // RES 0, C - RegBC.Low &= unchecked((byte)~0x01); - break; - case 0x82: // RES 0, D - RegDE.High &= unchecked((byte)~0x01); - break; - case 0x83: // RES 0, E - RegDE.Low &= unchecked((byte)~0x01); - break; - case 0x84: // RES 0, H - RegHL.High &= unchecked((byte)~0x01); - break; - case 0x85: // RES 0, L - RegHL.Low &= unchecked((byte)~0x01); - break; - case 0x86: // RES 0, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x01))); - break; - case 0x87: // RES 0, A - RegAF.High &= unchecked((byte)~0x01); - break; - case 0x88: // RES 1, B - RegBC.High &= unchecked((byte)~0x02); - break; - case 0x89: // RES 1, C - RegBC.Low &= unchecked((byte)~0x02); - break; - case 0x8A: // RES 1, D - RegDE.High &= unchecked((byte)~0x02); - break; - case 0x8B: // RES 1, E - RegDE.Low &= unchecked((byte)~0x02); - break; - case 0x8C: // RES 1, H - RegHL.High &= unchecked((byte)~0x02); - break; - case 0x8D: // RES 1, L - RegHL.Low &= unchecked((byte)~0x02); - break; - case 0x8E: // RES 1, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x02))); - break; - case 0x8F: // RES 1, A - RegAF.High &= unchecked((byte)~0x02); - break; - case 0x90: // RES 2, B - RegBC.High &= unchecked((byte)~0x04); - break; - case 0x91: // RES 2, C - RegBC.Low &= unchecked((byte)~0x04); - break; - case 0x92: // RES 2, D - RegDE.High &= unchecked((byte)~0x04); - break; - case 0x93: // RES 2, E - RegDE.Low &= unchecked((byte)~0x04); - break; - case 0x94: // RES 2, H - RegHL.High &= unchecked((byte)~0x04); - break; - case 0x95: // RES 2, L - RegHL.Low &= unchecked((byte)~0x04); - break; - case 0x96: // RES 2, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x04))); - break; - case 0x97: // RES 2, A - RegAF.High &= unchecked((byte)~0x04); - break; - case 0x98: // RES 3, B - RegBC.High &= unchecked((byte)~0x08); - break; - case 0x99: // RES 3, C - RegBC.Low &= unchecked((byte)~0x08); - break; - case 0x9A: // RES 3, D - RegDE.High &= unchecked((byte)~0x08); - break; - case 0x9B: // RES 3, E - RegDE.Low &= unchecked((byte)~0x08); - break; - case 0x9C: // RES 3, H - RegHL.High &= unchecked((byte)~0x08); - break; - case 0x9D: // RES 3, L - RegHL.Low &= unchecked((byte)~0x08); - break; - case 0x9E: // RES 3, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x08))); - break; - case 0x9F: // RES 3, A - RegAF.High &= unchecked((byte)~0x08); - break; - case 0xA0: // RES 4, B - RegBC.High &= unchecked((byte)~0x10); - break; - case 0xA1: // RES 4, C - RegBC.Low &= unchecked((byte)~0x10); - break; - case 0xA2: // RES 4, D - RegDE.High &= unchecked((byte)~0x10); - break; - case 0xA3: // RES 4, E - RegDE.Low &= unchecked((byte)~0x10); - break; - case 0xA4: // RES 4, H - RegHL.High &= unchecked((byte)~0x10); - break; - case 0xA5: // RES 4, L - RegHL.Low &= unchecked((byte)~0x10); - break; - case 0xA6: // RES 4, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x10))); - break; - case 0xA7: // RES 4, A - RegAF.High &= unchecked((byte)~0x10); - break; - case 0xA8: // RES 5, B - RegBC.High &= unchecked((byte)~0x20); - break; - case 0xA9: // RES 5, C - RegBC.Low &= unchecked((byte)~0x20); - break; - case 0xAA: // RES 5, D - RegDE.High &= unchecked((byte)~0x20); - break; - case 0xAB: // RES 5, E - RegDE.Low &= unchecked((byte)~0x20); - break; - case 0xAC: // RES 5, H - RegHL.High &= unchecked((byte)~0x20); - break; - case 0xAD: // RES 5, L - RegHL.Low &= unchecked((byte)~0x20); - break; - case 0xAE: // RES 5, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x20))); - break; - case 0xAF: // RES 5, A - RegAF.High &= unchecked((byte)~0x20); - break; - case 0xB0: // RES 6, B - RegBC.High &= unchecked((byte)~0x40); - break; - case 0xB1: // RES 6, C - RegBC.Low &= unchecked((byte)~0x40); - break; - case 0xB2: // RES 6, D - RegDE.High &= unchecked((byte)~0x40); - break; - case 0xB3: // RES 6, E - RegDE.Low &= unchecked((byte)~0x40); - break; - case 0xB4: // RES 6, H - RegHL.High &= unchecked((byte)~0x40); - break; - case 0xB5: // RES 6, L - RegHL.Low &= unchecked((byte)~0x40); - break; - case 0xB6: // RES 6, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x40))); - break; - case 0xB7: // RES 6, A - RegAF.High &= unchecked((byte)~0x40); - break; - case 0xB8: // RES 7, B - RegBC.High &= unchecked((byte)~0x80); - break; - case 0xB9: // RES 7, C - RegBC.Low &= unchecked((byte)~0x80); - break; - case 0xBA: // RES 7, D - RegDE.High &= unchecked((byte)~0x80); - break; - case 0xBB: // RES 7, E - RegDE.Low &= unchecked((byte)~0x80); - break; - case 0xBC: // RES 7, H - RegHL.High &= unchecked((byte)~0x80); - break; - case 0xBD: // RES 7, L - RegHL.Low &= unchecked((byte)~0x80); - break; - case 0xBE: // RES 7, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) & unchecked((byte)~0x80))); - break; - case 0xBF: // RES 7, A - RegAF.High &= unchecked((byte)~0x80); - break; - case 0xC0: // SET 0, B - RegBC.High |= unchecked(0x01); - break; - case 0xC1: // SET 0, C - RegBC.Low |= unchecked(0x01); - break; - case 0xC2: // SET 0, D - RegDE.High |= unchecked(0x01); - break; - case 0xC3: // SET 0, E - RegDE.Low |= unchecked(0x01); - break; - case 0xC4: // SET 0, H - RegHL.High |= unchecked(0x01); - break; - case 0xC5: // SET 0, L - RegHL.Low |= unchecked(0x01); - break; - case 0xC6: // SET 0, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x01))); - break; - case 0xC7: // SET 0, A - RegAF.High |= unchecked(0x01); - break; - case 0xC8: // SET 1, B - RegBC.High |= unchecked(0x02); - break; - case 0xC9: // SET 1, C - RegBC.Low |= unchecked(0x02); - break; - case 0xCA: // SET 1, D - RegDE.High |= unchecked(0x02); - break; - case 0xCB: // SET 1, E - RegDE.Low |= unchecked(0x02); - break; - case 0xCC: // SET 1, H - RegHL.High |= unchecked(0x02); - break; - case 0xCD: // SET 1, L - RegHL.Low |= unchecked(0x02); - break; - case 0xCE: // SET 1, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x02))); - break; - case 0xCF: // SET 1, A - RegAF.High |= unchecked(0x02); - break; - case 0xD0: // SET 2, B - RegBC.High |= unchecked(0x04); - break; - case 0xD1: // SET 2, C - RegBC.Low |= unchecked(0x04); - break; - case 0xD2: // SET 2, D - RegDE.High |= unchecked(0x04); - break; - case 0xD3: // SET 2, E - RegDE.Low |= unchecked(0x04); - break; - case 0xD4: // SET 2, H - RegHL.High |= unchecked(0x04); - break; - case 0xD5: // SET 2, L - RegHL.Low |= unchecked(0x04); - break; - case 0xD6: // SET 2, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x04))); - break; - case 0xD7: // SET 2, A - RegAF.High |= unchecked(0x04); - break; - case 0xD8: // SET 3, B - RegBC.High |= unchecked(0x08); - break; - case 0xD9: // SET 3, C - RegBC.Low |= unchecked(0x08); - break; - case 0xDA: // SET 3, D - RegDE.High |= unchecked(0x08); - break; - case 0xDB: // SET 3, E - RegDE.Low |= unchecked(0x08); - break; - case 0xDC: // SET 3, H - RegHL.High |= unchecked(0x08); - break; - case 0xDD: // SET 3, L - RegHL.Low |= unchecked(0x08); - break; - case 0xDE: // SET 3, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x08))); - break; - case 0xDF: // SET 3, A - RegAF.High |= unchecked(0x08); - break; - case 0xE0: // SET 4, B - RegBC.High |= unchecked(0x10); - break; - case 0xE1: // SET 4, C - RegBC.Low |= unchecked(0x10); - break; - case 0xE2: // SET 4, D - RegDE.High |= unchecked(0x10); - break; - case 0xE3: // SET 4, E - RegDE.Low |= unchecked(0x10); - break; - case 0xE4: // SET 4, H - RegHL.High |= unchecked(0x10); - break; - case 0xE5: // SET 4, L - RegHL.Low |= unchecked(0x10); - break; - case 0xE6: // SET 4, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x10))); - break; - case 0xE7: // SET 4, A - RegAF.High |= unchecked(0x10); - break; - case 0xE8: // SET 5, B - RegBC.High |= unchecked(0x20); - break; - case 0xE9: // SET 5, C - RegBC.Low |= unchecked(0x20); - break; - case 0xEA: // SET 5, D - RegDE.High |= unchecked(0x20); - break; - case 0xEB: // SET 5, E - RegDE.Low |= unchecked(0x20); - break; - case 0xEC: // SET 5, H - RegHL.High |= unchecked(0x20); - break; - case 0xED: // SET 5, L - RegHL.Low |= unchecked(0x20); - break; - case 0xEE: // SET 5, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x20))); - break; - case 0xEF: // SET 5, A - RegAF.High |= unchecked(0x20); - break; - case 0xF0: // SET 6, B - RegBC.High |= unchecked(0x40); - break; - case 0xF1: // SET 6, C - RegBC.Low |= unchecked(0x40); - break; - case 0xF2: // SET 6, D - RegDE.High |= unchecked(0x40); - break; - case 0xF3: // SET 6, E - RegDE.Low |= unchecked(0x40); - break; - case 0xF4: // SET 6, H - RegHL.High |= unchecked(0x40); - break; - case 0xF5: // SET 6, L - RegHL.Low |= unchecked(0x40); - break; - case 0xF6: // SET 6, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x40))); - break; - case 0xF7: // SET 6, A - RegAF.High |= unchecked(0x40); - break; - case 0xF8: // SET 7, B - RegBC.High |= unchecked(0x80); - break; - case 0xF9: // SET 7, C - RegBC.Low |= unchecked(0x80); - break; - case 0xFA: // SET 7, D - RegDE.High |= unchecked(0x80); - break; - case 0xFB: // SET 7, E - RegDE.Low |= unchecked(0x80); - break; - case 0xFC: // SET 7, H - RegHL.High |= unchecked(0x80); - break; - case 0xFD: // SET 7, L - RegHL.Low |= unchecked(0x80); - break; - case 0xFE: // SET 7, (HL) - WriteMemory(RegHL.Word, (byte)(ReadMemory(RegHL.Word) | unchecked(0x80))); - break; - case 0xFF: // SET 7, A - RegAF.High |= unchecked(0x80); - break; - } - break; - case 0xCC: // CALL Z, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagZ) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xCD: // CALL nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - break; - case 0xCE: // ADC n - TB = ReadMemory(RegPC.Word++); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High + TB + TI2; - RegAF.Low = 0; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 > 0xFF) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) + (TB & 0x0F) + TI2 > 0x0F) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xCF: // RST $08 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x08; - break; - case 0xD0: // RET NC - if (!FlagC) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xD1: // POP DE - RegDE.Low = ReadMemory(RegSP.Word++); RegDE.High = ReadMemory(RegSP.Word++); - break; - case 0xD2: // JP NC, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagC) - RegPC.Word = TUS; - break; - case 0xD3: // NOP - break; - case 0xD4: // CALL NC, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (!FlagC) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xD5: // PUSH DE - WriteMemory(--RegSP.Word, RegDE.High); WriteMemory(--RegSP.Word, RegDE.Low); - break; - case 0xD6: // SUB n - TB = ReadMemory(RegPC.Word++); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xD7: // RST $10 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x10; - break; - case 0xD8: // RET C - if (FlagC) - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - break; - case 0xD9: // RETI - RegPC.Low = ReadMemory(RegSP.Word++); RegPC.High = ReadMemory(RegSP.Word++); - // TODO Nothing else special needs to be done? - break; - case 0xDA: // JP C, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagC) - RegPC.Word = TUS; - break; - case 0xDB: // NOP - break; - case 0xDC: // CALL C, nn - TUS = (ushort)(ReadMemory(RegPC.Word++) + ReadMemory(RegPC.Word++) * 256); - if (FlagC) - { - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = TUS; - } - break; - case 0xDD: // NOP - break; - case 0xDE: // SBC A, n - TB = ReadMemory(RegPC.Word++); - TI2 = FlagC ? 1 : 0; - TI1 = RegAF.High - TB - TI2; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) - TI2 < 0) RegAF.Low |= 0x20; - RegAF.High = (byte)TI1; - break; - case 0xDF: // RST $18 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x18; - break; - case 0xE0: // LD ($FF00+nn), A - WriteMemory((ushort)(0xFF00 + ReadMemory(RegPC.Word++)), RegAF.High); - break; - case 0xE1: // POP HL - RegHL.Low = ReadMemory(RegSP.Word++); RegHL.High = ReadMemory(RegSP.Word++); - break; - case 0xE2: // LD ($FF00+C), A - WriteMemory((ushort)(0xFF00 + RegBC.Low), RegAF.High); - break; - case 0xE3: // NOP - break; - case 0xE4: // NOP - break; - case 0xE5: // PUSH HL - WriteMemory(--RegSP.Word, RegHL.High); WriteMemory(--RegSP.Word, RegHL.Low); - break; - case 0xE6: // AND n - RegAF.High &= ReadMemory(RegPC.Word++); - RegAF.Low = (byte)(RegAF.High == 0 ? 0xA0 : 0x20); - break; - case 0xE7: // RST $20 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x20; - break; - case 0xE8: // ADD SP, n - Console.WriteLine("E8 : ADD SP, n being executed. verify correctness"); - TSB = (sbyte)ReadMemory(RegPC.Word++); - RegAF.Low = 0; - if (RegSP.Word + TSB > 0xFFFF) RegAF.Low |= 0x10; - if (((RegSP.Word & 0xFFF) + TSB) > 0xFFF) RegAF.Low |= 0x20; - RegSP.Word = (ushort)(RegSP.Word + TSB); - break; - case 0xE9: // JP HL - RegPC.Word = RegHL.Word; - break; - case 0xEA: // LD (imm), A - TUS = (ushort)(ReadMemory(RegPC.Word++) | (ReadMemory(RegPC.Word++) << 8)); - WriteMemory(TUS, RegAF.High); - break; - case 0xEB: // NOP - break; - case 0xEC: // NOP - break; - case 0xED: // NOP - break; - case 0xEE: // XOR n - RegAF.High ^= ReadMemory(RegPC.Word++); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xEF: // RST $28 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x28; - break; - case 0xF0: // LD A, ($FF00+nn) - RegAF.High = ReadMemory((ushort)(0xFF00 + ReadMemory(RegPC.Word++))); - break; - case 0xF1: // POP AF - RegAF.Low = ReadMemory(RegSP.Word++); RegAF.High = ReadMemory(RegSP.Word++); - break; - case 0xF2: // LD A, ($FF00+C) - RegAF.High = ReadMemory((ushort)(0xFF00 + RegBC.Low)); - break; - case 0xF3: // DI - IFF1 = IFF2 = false; - break; - case 0xF4: // NOP - break; - case 0xF5: // PUSH AF - WriteMemory(--RegSP.Word, RegAF.High); WriteMemory(--RegSP.Word, RegAF.Low); - break; - case 0xF6: // OR n - RegAF.High |= ReadMemory(RegPC.Word++); - RegAF.Low = (byte)(RegAF.High == 0 ? 0x80 : 0); - break; - case 0xF7: // RST $30 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x30; - break; - case 0xF8: // LD HL, SP+nn - Console.WriteLine("F8 : LD HL, SP+n being executed. verify correctness"); - TSB = (sbyte)ReadMemory(RegPC.Word++); - RegAF.Low = 0; - if (RegSP.Word + TSB > 0xFFFF) RegAF.Low |= 0x10; - if (((RegSP.Word & 0xFFF) + TSB) > 0xFFF) RegAF.Low |= 0x20; - RegHL.Word = (ushort)(RegSP.Word + TSB); - break; - case 0xF9: // LD SP, HL - RegSP.Word = RegHL.Word; - break; - case 0xFA: // LD A, (nnnn) - TUS = (ushort)(ReadMemory(RegPC.Word++) | (ReadMemory(RegPC.Word++) << 8)); - RegAF.High = ReadMemory(TUS); - break; - case 0xFB: // EI - IFF1 = IFF2 = true; - Interruptable = false; - break; - case 0xFC: // NOP - break; - case 0xFD: // NOP - break; - case 0xFE: // CP n - TB = ReadMemory(RegPC.Word++); - TI1 = RegAF.High - TB; - RegAF.Low = 0x40; - if ((byte)TI1 == 0) RegAF.Low |= 0x80; - if (TI1 < 0) RegAF.Low |= 0x10; - if ((RegAF.High & 0x0F) - (TB & 0x0F) < 0) RegAF.Low |= 0x20; - break; - case 0xFF: // RST $38 - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x38; - break; - default: throw new Exception("unhandled opcode"); - } - LogData(); - } - - void CheckIrq() - { - if (nonMaskableInterruptPending) - { - halted = false; - - PendingCycles -= 3; - TotalExecutedCycles += 3; - nonMaskableInterruptPending = false; - - iff2 = iff1; - iff1 = false; - - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x66; - } - else if (iff1 && interrupt && Interruptable) - { - Halted = false; - - iff1 = iff2 = false; - - switch (interruptMode) - { - case 0: - PendingCycles -= 4; - TotalExecutedCycles += 4; - break; - case 1: - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Word = 0x38; - PendingCycles -= 4; - TotalExecutedCycles += 4; - break; - case 2: - ushort TUS = (ushort)(RegI * 256 + 0); - WriteMemory(--RegSP.Word, RegPC.High); WriteMemory(--RegSP.Word, RegPC.Low); - RegPC.Low = ReadMemory(TUS++); RegPC.High = ReadMemory(TUS); - PendingCycles -= 5; - TotalExecutedCycles += 5; - break; - } - } - } - - public void SingleStepInto() - { - if (halted) return; - ExecuteInstruction(); - CheckIrq(); - } - - public void ExecuteCycles(int cycles) - { - PendingCycles += cycles; - - while (PendingCycles > 0) - { - Interruptable = true; - - if (halted) - { - PendingCycles -= 1; - } - else - { - ExecuteInstruction(); - } - - CheckIrq(); - } - } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs deleted file mode 100644 index 7466eeba2a..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Registers.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System.Runtime.InteropServices; -using System; - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - [StructLayout(LayoutKind.Explicit)] - [Serializable] - public struct RegisterPair - { - [FieldOffset(0)] - public ushort Word; - - [FieldOffset(0)] - public byte Low; - - [FieldOffset(1)] - public byte High; - - public RegisterPair(ushort value) - { - Word = value; - Low = (byte)(Word); - High = (byte)(Word >> 8); - } - - public static implicit operator ushort(RegisterPair rp) - { - return rp.Word; - } - - public static implicit operator RegisterPair(ushort value) - { - return new RegisterPair(value); - } - } - - public bool FlagC - { - get { return (RegAF.Low & 0x10) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x10) | (value ? 0x10 : 0x00)); } - } - - public bool FlagH - { - get { return (RegAF.Low & 0x20) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x20) | (value ? 0x20 : 0x00)); } - } - - public bool FlagN - { - get { return (RegAF.Low & 0x40) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x40) | (value ? 0x40 : 0x00)); } - } - - public bool FlagZ - { - get { return (RegAF.Low & 0x80) != 0; } - set { RegAF.Low = (byte)((RegAF.Low & ~0x80) | (value ? 0x80 : 0x00)); } - } - - private RegisterPair RegAF; - private RegisterPair RegBC; - private RegisterPair RegDE; - private RegisterPair RegHL; - - private byte RegI; // I (interrupt vector) - - private RegisterPair RegSP; // SP (stack pointer) - private RegisterPair RegPC; // PC (program counter) - - private void ResetRegisters() - { - RegAF = 0; RegBC = 0; RegDE = 0; RegHL = 0; - RegI = 0; - RegSP.Word = 0; RegPC.Word = 0; - } - - public byte RegisterA - { - get { return RegAF.High; } - set { RegAF.High = value; } - } - - public byte RegisterF - { - get { return RegAF.Low; } - set { RegAF.Low = (byte)(value & 0xF0); } - } - - public ushort RegisterAF - { - get { return RegAF.Word; } - set { RegAF.Word = (byte)(value & 0xFFF0); } - } - - public byte RegisterB - { - get { return RegBC.High; } - set { RegBC.High = value; } - } - - public byte RegisterC - { - get { return RegBC.Low; } - set { RegBC.Low = value; } - } - - public ushort RegisterBC - { - get { return RegBC.Word; } - set { RegBC.Word = value; } - } - - public byte RegisterD - { - get { return RegDE.High; } - set { RegDE.High = value; } - } - - public byte RegisterE - { - get { return RegDE.Low; } - set { RegDE.Low = value; } - } - public ushort RegisterDE - { - get { return RegDE.Word; } - set { RegDE.Word = value; } - } - - public byte RegisterH - { - get { return RegHL.High; } - set { RegHL.High = value; } - } - - public byte RegisterL - { - get { return RegHL.Low; } - set { RegHL.Low = value; } - } - public ushort RegisterHL - { - get { return RegHL.Word; } - set { RegHL.Word = value; } - } - - public ushort RegisterPC - { - get { return RegPC.Word; } - set { RegPC.Word = value; } - } - public ushort RegisterSP - { - get { return RegSP.Word; } - set { RegSP.Word = value; } - } - public byte RegisterI - { - get { return RegI; } - set { RegI = value; } - } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs deleted file mode 100644 index c2597ef028..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Tables.cs +++ /dev/null @@ -1,149 +0,0 @@ -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public partial class Z80 - { - private void InitializeTables() - { - InitTableDaa(); - } - - private static readonly byte[] IncTable = - { - 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - private static readonly byte[] DecTable = - { - 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96, - 064, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 96 - }; - - private static readonly byte[] SwapTable = - { - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, - 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, - 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, - 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, - 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, - 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, - 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, - 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, - 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, - 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, - 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, - 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, - 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, - 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, - 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, - 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF - }; - - private static readonly byte[] mCycleTable = new byte[] - { - 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1, - 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, - 3, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1, - 3, 3, 2, 2, 1, 3, 3, 3, 3, 2, 2, 2, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, - 5, 3, 4, 4, 6, 4, 2, 4, 5, 4, 4, 1, 6, 6, 2, 4, - 5, 3, 4, 0, 6, 4, 2, 4, 5, 4, 4, 0, 6, 0, 2, 4, - 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4, - 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4, - }; - - private static readonly byte[] cbMCycleTable = new byte[] - { - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2, - }; - - private ushort[] TableDaa; - private void InitTableDaa() - { - TableDaa = new ushort[65536]; - for (int af = 0; af < 65536; ++af) - { - byte a = (byte)(af >> 8); - byte tmp = a; - - if (IsN(af)) - { - if (IsH(af) || ((a & 0x0F) > 0x09)) tmp -= 0x06; - if (IsC(af) || a > 0x99) tmp -= 0x60; - } - else - { - if (IsH(af) || ((a & 0x0F) > 0x09)) tmp += 0x06; - if (IsC(af) || a > 0x99) tmp += 0x60; - } - - TableDaa[af] = (ushort)((tmp * 256) + FlagByte(IsC(af) || a > 0x99, ((a ^ tmp) & 0x10) != 0, IsN(af), tmp == 0)); - } - } - - private static byte FlagByte(bool C, bool H, bool N, bool Z) - { - return (byte)( - (C ? 0x10 : 0) + - (H ? 0x20 : 0) + - (N ? 0x40 : 0) + - (Z ? 0x80 : 0) - ); - } - - private static bool IsC(int value) { return (value & 0x10) != 0; } - private static bool IsH(int value) { return (value & 0x20) != 0; } - private static bool IsN(int value) { return (value & 0x40) != 0; } - private static bool IsZ(int value) { return (value & 0x80) != 0; } - } -} diff --git a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs b/BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs deleted file mode 100644 index eb9842c806..0000000000 --- a/BizHawk.Emulation.Cores/CPUs/Z80-GB/Z80.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Globalization; -using System.IO; - -// This Z80-Gameboy emulator is a modified version of Ben Ryves 'Brazil' emulator. -// It is MIT licensed (not public domain). (See Licenses) - -namespace BizHawk.Emulation.Common.Components.Z80GB -{ - public sealed partial class Z80 - { - private static bool logging = false; - private static StreamWriter log; - - static Z80() - { - if (logging) - log = new StreamWriter("log_Z80.txt"); - } - - public Z80() - { - InitializeTables(); - Reset(); - } - - public void Reset() - { - ResetRegisters(); - ResetInterrupts(); - PendingCycles = 0; - TotalExecutedCycles = 0; - } - - // Memory Access - - public Func ReadMemory; - public Action WriteMemory; - - public void UnregisterMemoryMapper() - { - ReadMemory = null; - WriteMemory = null; - } - - // State Save/Load - - public void SaveStateText(TextWriter writer) - { - writer.WriteLine("[Z80]"); - writer.WriteLine("AF {0:X4}", RegAF.Word); - writer.WriteLine("BC {0:X4}", RegBC.Word); - writer.WriteLine("DE {0:X4}", RegDE.Word); - writer.WriteLine("HL {0:X4}", RegHL.Word); - writer.WriteLine("I {0:X2}", RegI); - writer.WriteLine("SP {0:X4}", RegSP.Word); - writer.WriteLine("PC {0:X4}", RegPC.Word); - writer.WriteLine("IRQ {0}", interrupt); - writer.WriteLine("NMI {0}", nonMaskableInterrupt); - writer.WriteLine("NMIPending {0}", nonMaskableInterruptPending); - writer.WriteLine("IM {0}", InterruptMode); - writer.WriteLine("IFF1 {0}", IFF1); - writer.WriteLine("IFF2 {0}", IFF2); - writer.WriteLine("Halted {0}", Halted); - writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles); - writer.WriteLine("PendingCycles {0}", PendingCycles); - writer.WriteLine("[/Z80]"); - writer.WriteLine(); - } - - public void LoadStateText(TextReader reader) - { - while (true) - { - string[] args = reader.ReadLine().Split(' '); - if (args[0].Trim() == "") continue; - if (args[0] == "[/Z80]") break; - if (args[0] == "AF") - RegAF.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "BC") - RegBC.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "DE") - RegDE.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "HL") - RegHL.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "I") - RegI = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "SP") - RegSP.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "PC") - RegPC.Word = ushort.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "IRQ") - interrupt = bool.Parse(args[1]); - else if (args[0] == "NMI") - nonMaskableInterrupt = bool.Parse(args[1]); - else if (args[0] == "NMIPending") - nonMaskableInterruptPending = bool.Parse(args[1]); - else if (args[0] == "IM") - InterruptMode = int.Parse(args[1]); - else if (args[0] == "IFF1") - IFF1 = bool.Parse(args[1]); - else if (args[0] == "IFF2") - IFF2 = bool.Parse(args[1]); - else if (args[0] == "Halted") - Halted = bool.Parse(args[1]); - else if (args[0] == "ExecutedCycles") - TotalExecutedCycles = int.Parse(args[1]); - else if (args[0] == "PendingCycles") - PendingCycles = int.Parse(args[1]); - - else - Console.WriteLine("Skipping unrecognized identifier " + args[0]); - } - } - - public void SaveStateBinary(BinaryWriter writer) - { - writer.Write(RegAF.Word); - writer.Write(RegBC.Word); - writer.Write(RegDE.Word); - writer.Write(RegHL.Word); - writer.Write(RegI); - writer.Write(RegSP.Word); - writer.Write(RegPC.Word); - writer.Write(interrupt); - writer.Write(nonMaskableInterrupt); - writer.Write(nonMaskableInterruptPending); - writer.Write(InterruptMode); - writer.Write(IFF1); - writer.Write(IFF2); - writer.Write(Halted); - writer.Write(TotalExecutedCycles); - writer.Write(PendingCycles); - } - - public void LoadStateBinary(BinaryReader reader) - { - RegAF.Word = reader.ReadUInt16(); - RegBC.Word = reader.ReadUInt16(); - RegDE.Word = reader.ReadUInt16(); - RegHL.Word = reader.ReadUInt16(); - RegI = reader.ReadByte(); - RegSP.Word = reader.ReadUInt16(); - RegPC.Word = reader.ReadUInt16(); - interrupt = reader.ReadBoolean(); - nonMaskableInterrupt = reader.ReadBoolean(); - nonMaskableInterruptPending = reader.ReadBoolean(); - InterruptMode = reader.ReadInt32(); - IFF1 = reader.ReadBoolean(); - IFF2 = reader.ReadBoolean(); - Halted = reader.ReadBoolean(); - TotalExecutedCycles = reader.ReadInt32(); - PendingCycles = reader.ReadInt32(); - } - - public void LogData() - { - if (!logging) - return; - log.WriteLine("AF {0:X4}", RegAF.Word); - log.WriteLine("BC {0:X4}", RegBC.Word); - log.WriteLine("DE {0:X4}", RegDE.Word); - log.WriteLine("HL {0:X4}", RegHL.Word); - log.WriteLine("SP {0:X4}", RegSP.Word); - log.WriteLine("PC {0:X4}", RegPC.Word); - log.WriteLine("------"); - log.WriteLine(); - log.Flush(); - } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs new file mode 100644 index 0000000000..5d3cbe082c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -0,0 +1,1072 @@ +using System; + +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Audio Emulation + public class Audio : ISoundProvider + { + public GBHawk Core { get; set; } + + public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 0}; + + public static int[] DIVISOR = new int[] { 8, 16, 32, 48, 64, 80, 96, 112 }; + + + public const int NR10 = 0; + public const int NR11 = 1; + public const int NR12 = 2; + public const int NR13 = 3; + public const int NR14 = 4; + public const int NR21 = 5; + public const int NR22 = 6; + public const int NR23 = 7; + public const int NR24 = 8; + public const int NR30 = 9; + public const int NR31 = 10; + public const int NR32 = 11; + public const int NR33 = 12; + public const int NR34 = 13; + public const int NR41 = 14; + public const int NR42 = 15; + public const int NR43 = 16; + public const int NR44 = 17; + public const int NR50 = 18; + public const int NR51 = 19; + public const int NR52 = 20; + + public static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF, + 0x3F, 0x00, 0xFF, 0xBF, + 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, + 0xFF, 0x00, 0x00, 0xBF, + 0x00, 0x00, 0x70}; + + public byte[] Audio_Regs = new byte[21]; + + public byte[] Wave_RAM = new byte[16]; + + + // Audio Variables + // derived + public bool WAVE_DAC_pow; + public bool NOISE_wdth_md; + public bool SQ1_negate; + public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger; + public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en; + public bool SQ1_env_add, SQ2_env_add, NOISE_env_add; + public byte WAVE_vol_code; + public byte NOISE_clk_shft; + public byte NOISE_div_code; + public byte SQ1_shift; + public byte SQ1_duty, SQ2_duty; + public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol; + public byte SQ1_per, SQ2_per, NOISE_per; + public byte SQ1_swp_prd; + public int SQ1_frq, SQ2_frq, WAVE_frq; + public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length; + // state + public bool WAVE_can_get; + public bool SQ1_calc_done; + public bool SQ1_swp_enable; + public bool SQ1_vol_done, SQ2_vol_done, NOISE_vol_done; + public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable; + public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state; + public byte SQ1_duty_cntr, SQ2_duty_cntr; + public byte WAVE_wave_cntr; + public int SQ1_frq_shadow; + public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr; + public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per; + public int SQ1_intl_swp_cnt; + public int NOISE_LFSR; + public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr; + // computed + public int SQ1_output, SQ2_output, WAVE_output, NOISE_output; + + // Contol Variables + public bool AUD_CTRL_vin_L_en; + public bool AUD_CTRL_vin_R_en; + public bool AUD_CTRL_sq1_L_en; + public bool AUD_CTRL_sq2_L_en; + public bool AUD_CTRL_wave_L_en; + public bool AUD_CTRL_noise_L_en; + public bool AUD_CTRL_sq1_R_en; + public bool AUD_CTRL_sq2_R_en; + public bool AUD_CTRL_wave_R_en; + public bool AUD_CTRL_noise_R_en; + public bool AUD_CTRL_power; + public byte AUD_CTRL_vol_L; + public byte AUD_CTRL_vol_R; + + public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + + public int master_audio_clock; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep) + case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %) + case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope) + case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low) + case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi) + case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %) + case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope) + case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low) + case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi) + case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off) + case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length) + case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output) + case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low) + case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi) + case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (length) + case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (envelope) + case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (shift) + case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger) + case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl) + case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl) + case 0xFF26: ret = (byte)(Read_NR52() | unused_bits[NR52]); break; // NR52 (ctrl) + + // wave ram table + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + if (WAVE_enable) + { + if (WAVE_can_get) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; } + else { ret = 0xFF; } + } + else { ret = Wave_RAM[addr & 0x0F]; } + + break; + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + // while power is on, everything is writable + if (AUD_CTRL_power) + { + switch (addr) + { + case 0xFF10: // NR10 (sweep) + Audio_Regs[NR10] = value; + SQ1_swp_prd = (byte)((value & 0x70) >> 4); + SQ1_negate = (value & 8) > 0; + SQ1_shift = (byte)(value & 7); + + if (!SQ1_negate && SQ1_calc_done) { SQ1_enable = false; } + break; + case 0xFF11: // NR11 (sound length / wave pattern duty %) + Audio_Regs[NR11] = value; + SQ1_duty = (byte)((value & 0xC0) >> 6); + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + break; + case 0xFF12: // NR12 (envelope) + Audio_Regs[NR12] = value; + SQ1_st_vol = (byte)((value & 0xF0) >> 4); + SQ1_env_add = (value & 8) > 0; + SQ1_per = (byte)(value & 7); + if (SQ1_per == 0) { SQ1_per = 8; } + if ((value & 0xF8) == 0) { SQ1_enable = SQ1_swp_enable = false; } + break; + case 0xFF13: // NR13 (freq low) + Audio_Regs[NR13] = value; + SQ1_frq &= 0x700; + SQ1_frq |= value; + break; + case 0xFF14: // NR14 (freq hi) + Audio_Regs[NR14] = value; + SQ1_trigger = (value & 0x80) > 0; + SQ1_frq &= 0xFF; + SQ1_frq |= (ushort)((value & 7) << 8); + + if (((sequencer_len & 1) > 0)) + { + if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0)) + { + SQ1_len_cntr--; + if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = SQ1_swp_enable = false; } + } + } + + if (SQ1_trigger) + { + SQ1_enable = true; + SQ1_vol_done = false; + if (SQ1_len_cntr == 0) + { + SQ1_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; } + } + SQ1_vol_state = SQ1_st_vol; + SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; + SQ1_frq_shadow = SQ1_frq; + SQ1_intl_cntr = (2048 - SQ1_frq_shadow) * 4; + + SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + SQ1_calc_done = false; + + if ((SQ1_shift > 0) || (SQ1_swp_prd > 0)) + { + SQ1_swp_enable = true; + } + else + { + SQ1_swp_enable = false; + } + + if (SQ1_shift > 0) + { + int shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + + // disable channel if overflow + if ((uint)shadow_frq > 2047) + { + SQ1_enable = SQ1_swp_enable = false; + } + + // set negate mode flag that disables channel is negate clerar + if (SQ1_negate) { SQ1_calc_done = true; } + } + + if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = SQ1_swp_enable = false; } + } + + SQ1_len_en = (value & 0x40) > 0; + break; + case 0xFF16: // NR21 (sound length / wave pattern duty %) + Audio_Regs[NR21] = value; + SQ2_duty = (byte)((value & 0xC0) >> 6); + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + break; + case 0xFF17: // NR22 (envelope) + Audio_Regs[NR22] = value; + SQ2_st_vol = (byte)((value & 0xF0) >> 4); + SQ2_env_add = (value & 8) > 0; + SQ2_per = (byte)(value & 7); + //if (SQ2_per == 0) { SQ2_per = 8; } + if ((value & 0xF8) == 0) { SQ2_enable = false; } + break; + case 0xFF18: // NR23 (freq low) + Audio_Regs[NR23] = value; + SQ2_frq &= 0x700; + SQ2_frq |= value; + break; + case 0xFF19: // NR24 (freq hi) + Audio_Regs[NR24] = value; + SQ2_trigger = (value & 0x80) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((value & 7) << 8); + + if ((sequencer_len & 1) > 0) + { + if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0)) + { + SQ2_len_cntr--; + if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; } + } + } + + if (SQ2_trigger) + { + SQ2_enable = true; + SQ2_vol_done = false; + + if (SQ2_len_cntr == 0) + { + SQ2_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; } + } + SQ2_intl_cntr = (2048 - SQ2_frq) * 4; + SQ2_vol_state = SQ2_st_vol; + SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; + if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; } + } + + SQ2_len_en = (value & 0x40) > 0; + + break; + case 0xFF1A: // NR30 (on/off) + Audio_Regs[NR30] = value; + WAVE_DAC_pow = (value & 0x80) > 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; } + break; + case 0xFF1B: // NR31 (length) + Audio_Regs[NR31] = value; + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + break; + case 0xFF1C: // NR32 (level output) + Audio_Regs[NR32] = value; + WAVE_vol_code = (byte)((value & 0x60) >> 5); + break; + case 0xFF1D: // NR33 (freq low) + Audio_Regs[NR33] = value; + WAVE_frq &= 0x700; + WAVE_frq |= value; + break; + case 0xFF1E: // NR34 (freq hi) + Audio_Regs[NR34] = value; + WAVE_trigger = (value & 0x80) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((value & 7) << 8); + + if ((sequencer_len & 1) > 0) + { + if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0)) + { + WAVE_len_cntr--; + if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; } + } + } + + if (WAVE_trigger) + { + // some corruption occurs if triggering while reading + if (WAVE_enable && WAVE_intl_cntr == 2) + { + // we want to use the previous wave cntr value since it was just incremented + int t_wave_cntr = (WAVE_wave_cntr + 1) & 31; + if ((t_wave_cntr >> 1) < 4) + { + Wave_RAM[0] = Wave_RAM[t_wave_cntr >> 1]; + } + else + { + Wave_RAM[0] = Wave_RAM[(t_wave_cntr >> 3) * 4]; + Wave_RAM[1] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 1]; + Wave_RAM[2] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 2]; + Wave_RAM[3] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 3]; + } + } + + WAVE_enable = true; + + if (WAVE_len_cntr == 0) + { + WAVE_len_cntr = 256; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; } + } + WAVE_intl_cntr = (2048 - WAVE_frq) * 2 + 6; // trigger delay for wave channel + WAVE_wave_cntr = 0; + if (!WAVE_DAC_pow) { WAVE_enable = false; } + } + + WAVE_len_en = (value & 0x40) > 0; + + break; + case 0xFF20: // NR41 (length) + Audio_Regs[NR41] = value; + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + break; + case 0xFF21: // NR42 (envelope) + Audio_Regs[NR42] = value; + NOISE_st_vol = (byte)((value & 0xF0) >> 4); + NOISE_env_add = (value & 8) > 0; + NOISE_per = (byte)(value & 7); + //if (NOISE_per == 0) { NOISE_per = 8; } + if ((value & 0xF8) == 0) { NOISE_enable = false; } + break; + case 0xFF22: // NR43 (shift) + Audio_Regs[NR43] = value; + NOISE_clk_shft = (byte)((value & 0xF0) >> 4); + NOISE_wdth_md = (value & 8) > 0; + NOISE_div_code = (byte)(value & 7); + break; + case 0xFF23: // NR44 (trigger) + Audio_Regs[NR44] = value; + NOISE_trigger = (value & 0x80) > 0; + + if ((sequencer_len & 1) > 0) + { + if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0)) + { + NOISE_len_cntr--; + if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; } + } + } + + if (NOISE_trigger) + { + NOISE_enable = true; + NOISE_vol_done = false; + + if (NOISE_len_cntr == 0) + { + NOISE_len_cntr = 64; + if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; } + } + NOISE_intl_cntr = (DIVISOR[NOISE_div_code] << NOISE_clk_shft); + NOISE_vol_state = NOISE_st_vol; + NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; + NOISE_LFSR = 0x7FFF; + if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; } + } + + NOISE_len_en = (value & 0x40) > 0; + break; + case 0xFF24: // NR50 (ctrl) + Audio_Regs[NR50] = value; + AUD_CTRL_vin_L_en = (value & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4); + AUD_CTRL_vin_R_en = (value & 8) > 0; + AUD_CTRL_vol_R = (byte)(value & 7); + break; + case 0xFF25: // NR51 (ctrl) + Audio_Regs[NR51] = value; + AUD_CTRL_noise_L_en = (value & 0x80) > 0; + AUD_CTRL_wave_L_en = (value & 0x40) > 0; + AUD_CTRL_sq2_L_en = (value & 0x20) > 0; + AUD_CTRL_sq1_L_en = (value & 0x10) > 0; + AUD_CTRL_noise_R_en = (value & 8) > 0; + AUD_CTRL_wave_R_en = (value & 4) > 0; + AUD_CTRL_sq2_R_en = (value & 2) > 0; + AUD_CTRL_sq1_R_en = (value & 1) > 0; + break; + case 0xFF26: // NR52 (ctrl) + AUD_CTRL_power = (value & 0x80) > 0; + + if (!AUD_CTRL_power) { power_off(); } + break; + + // wave ram table + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + if (WAVE_enable) + { + if (WAVE_can_get) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } + } + else { Wave_RAM[addr & 0xF] = value; } + + break; + } + } + // when power is off, only length counters and waveRAM are effected by writes + else + { + switch (addr) + { + case 0xFF11: // NR11 (sound length / wave pattern duty %) + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + break; + case 0xFF16: // NR21 (sound length / wave pattern duty %) + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + break; + case 0xFF1B: // NR31 (length) + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + break; + case 0xFF20: // NR41 (length) + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + break; + case 0xFF26: // NR52 (ctrl) + AUD_CTRL_power = (value & 0x80) > 0; + if (AUD_CTRL_power) + { + sequencer_vol = 0; + sequencer_len = 0; + sequencer_swp = 0; + } + break; + + // wave ram table + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + Wave_RAM[addr & 0x0F] = value; + break; + } + } + } + + public void tick() + { + // calculate square1's output + if (SQ1_enable) + { + SQ1_intl_cntr--; + if (SQ1_intl_cntr == 0) + { + SQ1_intl_cntr = (2048 - SQ1_frq_shadow) * 4; + SQ1_duty_cntr++; + SQ1_duty_cntr &= 7; + + SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr]; + SQ1_output *= SQ1_vol_state; + } + } + + // calculate square2's output + if (SQ2_enable) + { + SQ2_intl_cntr--; + if (SQ2_intl_cntr == 0) + { + SQ2_intl_cntr = (2048 - SQ2_frq) * 4; + SQ2_duty_cntr++; + SQ2_duty_cntr &= 7; + + SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr]; + SQ2_output *= SQ2_vol_state; + } + } + + // calculate wave output + WAVE_can_get = false; + if (WAVE_enable) + { + WAVE_intl_cntr--; + + if (WAVE_intl_cntr == 0) + { + WAVE_intl_cntr = (2048 - WAVE_frq) * 2; + WAVE_wave_cntr++; + WAVE_wave_cntr &= 0x1F; + + WAVE_can_get = true; + + byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; + + if ((WAVE_wave_cntr & 1) == 0) + { + sample = (byte)(sample >> 4); + } + + if (WAVE_vol_code == 0) + { + sample = (byte)((sample & 0xF) >> 4); + } + else if (WAVE_vol_code == 1) + { + sample = (byte)(sample & 0xF); + } + else if (WAVE_vol_code == 2) + { + sample = (byte)((sample & 0xF) >> 1); + } + else + { + sample = (byte)((sample & 0xF) >> 2); + } + + WAVE_output = sample; + + if (!WAVE_DAC_pow) { WAVE_output = 0; } + } + } + + + // calculate noise output + if (NOISE_enable) + { + NOISE_intl_cntr--; + if (NOISE_intl_cntr == 0) + { + NOISE_intl_cntr = (DIVISOR[NOISE_div_code] << NOISE_clk_shft); + int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1); + + NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF; + NOISE_LFSR |= (bit_lfsr << 14); + + if (NOISE_wdth_md) + { + NOISE_LFSR = NOISE_LFSR & 0x7FBF; + NOISE_LFSR |= (bit_lfsr << 6); + } + + NOISE_output = NOISE_LFSR & 1; + NOISE_output *= NOISE_vol_state; + } + } + + // add up components to each channel + int L_final = 0; + int R_final = 0; + + if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; } + if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; } + if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; } + if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; } + + if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; } + if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; } + if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } + if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } + + L_final *= (AUD_CTRL_vol_L + 1); + R_final *= (AUD_CTRL_vol_R + 1); + + // send out an actual sample every 94 cycles + master_audio_clock++; + if (master_audio_clock == 94) + { + master_audio_clock = 0; + if (AudioClocks < 1500) + { + AudioSamples[AudioClocks] = (short)(L_final * 4); + AudioClocks++; + AudioSamples[AudioClocks] = (short)(R_final * 4); + AudioClocks++; + } + } + + // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) + sequencer_tick++; + + if (sequencer_tick == 8192) + { + sequencer_tick = 0; + + sequencer_vol++; sequencer_vol &= 0x7; + sequencer_len++; sequencer_len &= 0x7; + sequencer_swp++; sequencer_swp &= 0x7; + + // clock the lengths + if ((sequencer_len & 1) > 0) + { + if (SQ1_len_en && SQ1_len_cntr > 0) + { + SQ1_len_cntr--; + if (SQ1_len_cntr == 0) { SQ1_enable = SQ1_swp_enable = false; } + } + if (SQ2_len_en && SQ2_len_cntr > 0) + { + SQ2_len_cntr--; + if (SQ2_len_cntr == 0) { SQ2_enable = false; } + } + if (WAVE_len_en && WAVE_len_cntr > 0) + { + WAVE_len_cntr--; + if (WAVE_len_cntr == 0) { WAVE_enable = false; } + } + if (NOISE_len_en && NOISE_len_cntr > 0) + { + NOISE_len_cntr--; + if (NOISE_len_cntr == 0) { NOISE_enable = false; } + } + } + + // clock the sweep + if ((sequencer_swp == 3) || (sequencer_swp == 7)) + { + SQ1_intl_swp_cnt--; + if ((SQ1_intl_swp_cnt == 0) && SQ1_swp_enable) + { + SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8; + + if ((SQ1_swp_prd > 0)) + { + int shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + + // set negate mode flag that disables channel is negate clerar + if (SQ1_negate) { SQ1_calc_done = true; } + + // disable channel if overflow + if ((uint)shadow_frq > 2047) + { + SQ1_enable = SQ1_swp_enable = false; + } + else + { + if (SQ1_shift > 0) + { + shadow_frq &= 0x7FF; + SQ1_frq = shadow_frq; + SQ1_frq_shadow = shadow_frq; + + // note that we also write back the frequency to the actual register + Audio_Regs[NR13] = (byte)(SQ1_frq & 0xFF); + Audio_Regs[NR14] &= 0xF8; + Audio_Regs[NR14] |= (byte)((SQ1_frq >> 8) & 7); + + // after writing, we repeat the process and do another overflow check + shadow_frq = SQ1_frq_shadow; + shadow_frq = shadow_frq >> SQ1_shift; + if (SQ1_negate) { shadow_frq = -shadow_frq; } + shadow_frq += SQ1_frq_shadow; + + if ((uint)shadow_frq > 2047) + { + SQ1_enable = SQ1_swp_enable = false; + } + } + } + } + } + } + + // clock the volume envelope + if (sequencer_vol == 0) + { + if (SQ1_per > 0) + { + SQ1_vol_per--; + if (SQ1_vol_per == 0) + { + SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8; + if (!SQ1_vol_done) + { + if (SQ1_env_add) + { + if (SQ1_vol_state < 15) { SQ1_vol_state++; } + else { SQ1_vol_done = true; } + } + else + { + if (SQ1_vol_state >= 1) { SQ1_vol_state--; } + else { SQ1_vol_done = true; } + } + } + } + } + if (SQ2_per > 0) + { + SQ2_vol_per--; + if (SQ2_vol_per == 0) + { + SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8; + if (!SQ2_vol_done) + { + if (SQ2_env_add) + { + if (SQ2_vol_state < 15) { SQ2_vol_state++; } + else { SQ2_vol_done = true; } + } + else + { + if (SQ2_vol_state >= 1) { SQ2_vol_state--; } + else { SQ2_vol_done = true; } + } + } + } + } + if (NOISE_per > 0) + { + NOISE_vol_per--; + if (NOISE_vol_per == 0) + { + NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8; + if (!NOISE_vol_done) + { + if (NOISE_env_add) + { + if (NOISE_vol_state < 15) { NOISE_vol_state++; } + else { NOISE_vol_done = true; } + } + else + { + if (NOISE_vol_state >= 1) { NOISE_vol_state--; } + else { NOISE_vol_done = true; } + } + } + } + } + } + } + } + + public void power_off() + { + for (int i = 0; i < 21; i++) + { + Audio_Regs[i] = 0; + } + + // reset derived values + sync_channels(); + + // reset state variables + SQ1_enable = SQ1_swp_enable = false; + SQ2_enable = false; + WAVE_enable = false; + NOISE_enable = false; + + SQ1_len_en = false; + SQ2_len_en = false; + WAVE_len_en = false; + NOISE_len_en = false; + + sequencer_len = 0; + sequencer_vol = 0; + sequencer_swp = 0; + + master_audio_clock = 0; + } + + public void Reset() + { + Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, + 0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA }; + + Audio_Regs = new byte[21]; + + AudioClocks = 0; + master_audio_clock = 0; + + sequencer_len = 0; + sequencer_swp = 0; + sequencer_vol = 0; + sequencer_tick = 0; + } + + public void SyncState(Serializer ser) + { + ser.Sync("Audio_Regs", ref Audio_Regs, false); + ser.Sync("Wave_Ram", ref Wave_RAM, false); + + // save state variables + ser.Sync("WAVE_can_get", ref WAVE_can_get); + ser.Sync("SQ1_vol_done", ref SQ1_vol_done); + ser.Sync("SQ2_vol_done", ref SQ2_vol_done); + ser.Sync("NOISE_vol_done", ref NOISE_vol_done); + ser.Sync("SQ1_calc_done", ref SQ1_calc_done); + ser.Sync("SQ1_swp_enable", ref SQ1_swp_enable); + ser.Sync("SQ1_length_counter", ref SQ1_len_cntr); + ser.Sync("SQ2_length_counter", ref SQ2_len_cntr); + ser.Sync("WAVE_length_counter", ref WAVE_len_cntr); + ser.Sync("NOISE_length_counter", ref NOISE_len_cntr); + ser.Sync("SQ1_enable", ref SQ1_enable); + ser.Sync("SQ2_enable", ref SQ2_enable); + ser.Sync("WAVE_enable", ref WAVE_enable); + ser.Sync("NOISE_enable", ref NOISE_enable); + ser.Sync("SQ1_vol_state", ref SQ1_vol_state); + ser.Sync("SQ2_vol_state", ref SQ2_vol_state); + ser.Sync("NOISE_vol_state", ref NOISE_vol_state); + ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr); + ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr); + ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr); + ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow); + ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr); + ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr); + ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr); + ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr); + ser.Sync("SQ1_vol_per", ref SQ1_vol_per); + ser.Sync("SQ2_vol_per", ref SQ2_vol_per); + ser.Sync("NOISE_vol_per", ref NOISE_vol_per); + ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt); + ser.Sync("NOISE_LFSR", ref NOISE_LFSR); + ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr); + ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr); + ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr); + ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr); + + + ser.Sync("sequencer_len", ref sequencer_len); + ser.Sync("sequencer_vol", ref sequencer_vol); + ser.Sync("sequencer_swp", ref sequencer_swp); + ser.Sync("sequencer_tick", ref sequencer_tick); + + ser.Sync("master_audio_clock", ref master_audio_clock); + + // get derived state + if (ser.IsReader) + { + sync_channels(); + } + } + + public void sync_channels() + { + + SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4); + SQ1_negate = (Audio_Regs[NR10] & 8) > 0; + SQ1_shift = (byte)(Audio_Regs[NR10] & 7); + + SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6); + SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F); + + SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4); + SQ1_env_add = (Audio_Regs[NR12] & 8) > 0; + SQ1_per = (byte)(Audio_Regs[NR12] & 7); + + SQ1_frq &= 0x700; + SQ1_frq |= Audio_Regs[NR13]; + + SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0; + SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0; + SQ1_frq &= 0xFF; + SQ1_frq |= (ushort)((Audio_Regs[NR14] & 7) << 8); + + SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6); + SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F); + + SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4); + SQ2_env_add = (Audio_Regs[NR22] & 8) > 0; + SQ2_per = (byte)(Audio_Regs[NR22] & 7); + + SQ2_frq &= 0x700; + SQ2_frq |= Audio_Regs[NR23]; + + SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0; + SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0; + SQ2_frq &= 0xFF; + SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8); + + WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0; + + WAVE_length = (ushort)(256 - Audio_Regs[NR31]); + + WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5); + + WAVE_frq &= 0x700; + WAVE_frq |= Audio_Regs[NR33]; + + WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0; + WAVE_frq &= 0xFF; + WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8); + + NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F); + + NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4); + NOISE_env_add = (Audio_Regs[NR42] & 8) > 0; + NOISE_per = (byte)(Audio_Regs[NR42] & 7); + + NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4); + NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0; + NOISE_div_code = (byte)(Audio_Regs[NR43] & 7); + + WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0; + WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0; + + AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0; + AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4); + AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0; + AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7); + + AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0; + AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0; + AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0; + AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0; + AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0; + AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0; + AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0; + AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0; + + AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0; + } + + public byte Read_NR52() + { + return (byte)( + ((AUD_CTRL_power ? 1 : 0) << 7) | + ((SQ1_enable ? 1 : 0)) | + ((SQ2_enable ? 1 : 0) << 1) | + ((WAVE_enable ? 1 : 0) << 2) | + ((NOISE_enable ? 1 : 0) << 3)); + } + + #region audio + + public bool CanProvideAsync => false; + + public int AudioClocks; + public short[] AudioSamples = new short[1500]; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + { + throw new InvalidOperationException("Only Sync mode is supported_"); + } + } + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + nsamp = AudioClocks / 2; + short[] temp_samp = new short[AudioClocks]; + + for (int i = 0; i < AudioClocks; i++) + { + temp_samp[i] = AudioSamples[i]; + } + + samples = temp_samp; + + AudioClocks = 0; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + AudioClocks = 0; + } + + private void GetSamples(short[] samples) + { + + } + + #endregion + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs new file mode 100644 index 0000000000..778b17acfc --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + return new Dictionary + { + /* + ["A"] = cpu.A, + ["X"] = cpu.X, + ["Y"] = cpu.Y, + ["S"] = cpu.S, + ["PC"] = cpu.PC, + ["Flag C"] = cpu.FlagC, + ["Flag Z"] = cpu.FlagZ, + ["Flag I"] = cpu.FlagI, + ["Flag D"] = cpu.FlagD, + ["Flag B"] = cpu.FlagB, + ["Flag V"] = cpu.FlagV, + ["Flag N"] = cpu.FlagN, + ["Flag T"] = cpu.FlagT + */ + }; + } + + public void SetCpuRegister(string register, int value) + { + switch (register) + { + default: + throw new InvalidOperationException(); + case "A": + //cpu.A = (byte)value; + break; + case "X": + //cpu.X = (byte)value; + break; + case "Y": + //cpu.Y = (byte)value; + break; + case "S": + //cpu.S = (byte)value; + break; + case "PC": + //cpu.PC = (ushort)value; + break; + case "Flag I": + //cpu.FlagI = value > 0; + break; + } + } + + public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" }); + + public bool CanStep(StepType type) + { + return false; + } + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public int TotalExecutedCycles + { + get { return cpu.TotalExecutedCycles; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs new file mode 100644 index 0000000000..77fe21cc28 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -0,0 +1,156 @@ +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IEmulator, IVideoProvider + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; + + public byte controller_state; + public byte controller_state_old; + public bool in_vblank_old; + public bool in_vblank; + public bool vblank_rise; + + public void FrameAdvance(IController controller, bool render, bool rendersound) + { + //Console.WriteLine("-----------------------FRAME-----------------------"); + + if (_tracer.Enabled) + { + cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + cpu.TraceCallback = null; + } + + _frame++; + + if (controller.IsPressed("Power")) + { + // it seems that theMachine.Reset() doesn't clear ram, etc + // this should leave hsram intact but clear most other things + HardReset(); + } + + _islag = true; + + GetControllerState(controller); + + do_frame(); + + if (_islag) + { + _lagcount++; + } + } + + public void do_frame() + { + // gameboy frames can be variable lengths + // we want to end a frame when VBlank turns from false to true + int ticker = 0; + while (!vblank_rise && (ticker < 100000)) + { + audio.tick(); + timer.tick_1(); + ppu.tick(); + + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + + timer.tick_2(); + + + if (in_vblank && !in_vblank_old) + { + vblank_rise = true; + } + ticker++; + in_vblank_old = in_vblank; + } + + vblank_rise = false; + } + + public void RunCPUCycle() + { + + } + + public void GetControllerState(IController controller) + { + InputCallbacks.Call(); + controller_state = _controllerDeck.ReadPort1(controller); + + // set interrupt flag if a pin went from high to low + if (controller_state < controller_state_old) + { + if (REG_FFFF.Bit(4)) { cpu.FlagI = true; } + REG_FF0F |= 0x10; + } + + controller_state_old = controller_state; + } + + public void serial_transfer() + { + if (serial_control.Bit(7) && !serial_start_old) + { + serial_start_old = true; + + // transfer out on byte of data + // needs to be modelled + } + } + + public int Frame => _frame; + + public string SystemId => "GB"; + + public bool DeterministicEmulation { get; set; } + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + + } + + + #region Video provider + + public int _frameHz = 60; + + public int[] _vidbuffer; + + public int[] GetVideoBuffer() + { + return _vidbuffer; + } + + public int VirtualWidth => 160; + public int VirtualHeight => 144; + public int BufferWidth => 160; + public int BufferHeight => 144; + public int BackgroundColor => unchecked((int)0xFF000000); + public int VsyncNumerator => _frameHz; + public int VsyncDenominator => 1; + + public static readonly uint[] color_palette = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 }; + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs new file mode 100644 index 0000000000..00c5112275 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IInputPollable.cs @@ -0,0 +1,24 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IInputPollable + { + public int LagCount + { + get { return _lagcount; } + set { _lagcount = value; } + } + + public bool IsLagFrame + { + get { return _islag; } + set { _islag = value; } + } + + public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem(); + + public bool _islag = true; + private int _lagcount; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs new file mode 100644 index 0000000000..971b3c3c2d --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + private IMemoryDomains MemoryDomains; + + public void SetupMemoryDomains() + { + var domains = new List + { + new MemoryDomainDelegate( + "Main RAM", + RAM.Length, + MemoryDomain.Endian.Little, + addr => RAM[addr], + (addr, value) => RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "Zero Page RAM", + ZP_RAM.Length, + MemoryDomain.Endian.Little, + addr => ZP_RAM[addr], + (addr, value) => ZP_RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "System Bus", + 0X10000, + MemoryDomain.Endian.Little, + addr => PeekSystemBus(addr), + (addr, value) => PokeSystemBus(addr, value), + 1) + }; + + MemoryDomains = new MemoryDomainList(domains); + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + + private byte PeekSystemBus(long addr) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + return ReadMemory(addr2); + } + + private void PokeSystemBus(long addr, byte value) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + WriteMemory(addr2, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs new file mode 100644 index 0000000000..d1fa270c30 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs @@ -0,0 +1,26 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : ISaveRam + { + public byte[] CloneSaveRam() + { + return (byte[])_sram.Clone(); + } + + public void StoreSaveRam(byte[] data) + { + Buffer.BlockCopy(data, 0, _sram, 0, data.Length); + } + + public bool SaveRamModified + { + get + { + return false; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs new file mode 100644 index 0000000000..55d2cc130e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -0,0 +1,77 @@ +using System; +using System.ComponentModel; + +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IEmulator, IStatable, ISettable + { + public GBSettings GetSettings() + { + return _settings.Clone(); + } + + public GBSyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(GBSettings o) + { + _settings = o; + return false; + } + + public bool PutSyncSettings(GBSyncSettings o) + { + bool ret = GBSyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private GBSettings _settings = new GBSettings(); + public GBSyncSettings _syncSettings = new GBSyncSettings(); + + public class GBSettings + { + public GBSettings Clone() + { + return (GBSettings)MemberwiseClone(); + } + } + + public class GBSyncSettings + { + private string _port1 = GBHawkControllerDeck.DefaultControllerName; + + [JsonIgnore] + public string Port1 + { + get { return _port1; } + set + { + if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) + { + throw new InvalidOperationException("Invalid controller type: " + value); + } + + _port1 = value; + } + } + + public GBSyncSettings Clone() + { + return (GBSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(GBSyncSettings x, GBSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs new file mode 100644 index 0000000000..c228de6e1e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -0,0 +1,102 @@ +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk : IStatable + { + public bool BinarySaveStatesPreferred => true; + + public void SaveStateText(TextWriter writer) + { + SyncState(new Serializer(writer)); + } + + public void LoadStateText(TextReader reader) + { + SyncState(new Serializer(reader)); + } + + public void SaveStateBinary(BinaryWriter bw) + { + SyncState(new Serializer(bw)); + } + + public void LoadStateBinary(BinaryReader br) + { + SyncState(new Serializer(br)); + } + + public byte[] SaveStateBinary() + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + return ms.ToArray(); + } + + private void SyncState(Serializer ser) + { + byte[] core = null; + if (ser.IsWriter) + { + var ms = new MemoryStream(); + ms.Close(); + core = ms.ToArray(); + } + cpu.SyncState(ser); + mapper.SyncState(ser); + timer.SyncState(ser); + ppu.SyncState(ser); + audio.SyncState(ser); + + ser.BeginSection("Gameboy"); + ser.Sync("core", ref core, false); + ser.Sync("Lag", ref _lagcount); + ser.Sync("Frame", ref _frame); + ser.Sync("IsLag", ref _islag); + _controllerDeck.SyncState(ser); + + ser.Sync("controller_state", ref controller_state); + ser.Sync("controller_state_old", ref controller_state_old); + ser.Sync("in_vblank", ref in_vblank); + ser.Sync("in_vblank_old", ref in_vblank_old); + ser.Sync("vblank_rise", ref vblank_rise); + ser.Sync("GB_bios_register", ref GB_bios_register); + ser.Sync("input_register", ref input_register); + + ser.Sync("serial_control", ref serial_control); + ser.Sync("serial_data_out", ref serial_data_out); + ser.Sync("serial_data_in", ref serial_data_in); + ser.Sync("serial_start_old", ref serial_start_old); + + ser.Sync("REG_FFFF", ref REG_FFFF); + ser.Sync("REG_FF0F", ref REG_FF0F); + ser.Sync("enable_VBL", ref enable_VBL); + ser.Sync("enable_LCDC", ref enable_PRS); + ser.Sync("enable_TIMO", ref enable_TIMO); + ser.Sync("enable_SER", ref enable_SER); + ser.Sync("enable_STAT", ref enable_STAT); + + // memory domains + ser.Sync("RAM", ref RAM, false); + ser.Sync("ZP_RAM", ref ZP_RAM, false); + ser.Sync("CHR_RAM", ref CHR_RAM, false); + ser.Sync("BG_map_1", ref BG_map_1, false); + ser.Sync("BG_map_2", ref BG_map_2, false); + ser.Sync("OAM", ref OAM, false); + // probably a better way to do this + if (cart_RAM != null) + { + ser.Sync("cart_RAM", ref cart_RAM, false); + } + + + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs new file mode 100644 index 0000000000..1c36b13680 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -0,0 +1,241 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.LR35902; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + [Core( + "GBHawk", + "", + isPorted: false, + isReleased: true)] + [ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight))] + public partial class GBHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, + ISettable + { + // this register controls whether or not the GB BIOS is mapped into memory + public byte GB_bios_register; + + public byte input_register; + + public byte serial_control; + public byte serial_data_out; + public byte serial_data_in; + public bool serial_start_old; + + // The unused bits in this register are still read/writable + public byte REG_FFFF; + // The unused bits in this register (interrupt flags) are always set + public byte REG_FF0F = 0xE0; + public bool enable_VBL; + public bool enable_STAT; + public bool enable_TIMO; + public bool enable_SER; + public bool enable_PRS; + + + // memory domains + public byte[] RAM = new byte[0x2000]; + public byte[] ZP_RAM = new byte[0x80]; + public byte[] CHR_RAM = new byte[0x1800]; + public byte[] BG_map_1 = new byte[0x400]; + public byte[] BG_map_2 = new byte[0x400]; + public byte[] OAM = new byte[0xA0]; + + public readonly byte[] _rom; + public readonly byte[] _bios; + public readonly byte[] _sram = new byte[2048]; + public readonly byte[] header = new byte[0x50]; + + public byte[] cart_RAM; + + private int _frame = 0; + + public MapperBase mapper; + + private readonly ITraceable _tracer; + + public LR35902 cpu; + public PPU ppu; + public Timer timer; + public Audio audio; + + [CoreConstructor("GB")] + public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + + cpu = new LR35902 + { + ReadMemory = ReadMemory, + WriteMemory = WriteMemory, + PeekMemory = ReadMemory, + DummyReadMemory = ReadMemory, + OnExecFetch = ExecFetch + }; + ppu = new PPU(); + timer = new Timer(); + audio = new Audio(); + + CoreComm = comm; + + _settings = (GBSettings)settings ?? new GBSettings(); + _syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings(); + _controllerDeck = new GBHawkControllerDeck(_syncSettings.Port1); + + byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", false, "BIOS Not Found, Cannot Load"); + _bios = Bios; + + Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); + + string hash_md5 = null; + hash_md5 = "md5:" + rom.HashMD5(0, rom.Length); + Console.WriteLine(hash_md5); + + _rom = rom; + Setup_Mapper(); + + _frameHz = 60; + + timer.Core = this; + audio.Core = this; + ppu.Core = this; + + ser.Register(this); + ser.Register(audio); + ServiceProvider = ser; + + _tracer = new TraceBuffer { Header = cpu.TraceHeader }; + ser.Register(_tracer); + + SetupMemoryDomains(); + HardReset(); + } + + public DisplayType Region => DisplayType.NTSC; + + private readonly GBHawkControllerDeck _controllerDeck; + + private void HardReset() + { + GB_bios_register = 0; // bios enable + in_vblank = true; // we start off in vblank since the LCD is off + in_vblank_old = true; + + Register_Reset(); + timer.Reset(); + ppu.Reset(); + audio.Reset(); + + cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); + + _vidbuffer = new int[VirtualWidth * VirtualHeight]; + } + + private void ExecFetch(ushort addr) + { + MemoryCallbacks.CallExecutes(addr, "System Bus"); + } + + private void Setup_Mapper() + { + // setup up mapper based on header entry + string mppr; + + switch (header[0x47]) + { + case 0x0: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x1: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x2: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x3: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x5: mapper = new MapperMBC2(); mppr = "MBC2"; break; + case 0x6: mapper = new MapperMBC2(); mppr = "MBC2"; break; + case 0x8: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x9: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0xB: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xC: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xD: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xF: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x10: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x11: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x12: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x13: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x19: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1A: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1B: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1C: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1D: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1E: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x20: mapper = new MapperMBC6(); mppr = "MBC6"; break; + case 0x22: mapper = new MapperMBC7(); mppr = "MBC7"; break; + case 0xFC: mapper = new MapperCamera(); mppr = "CAM"; break; + case 0xFD: mapper = new MapperTAMA5(); mppr = "TAMA5"; break; + case 0xFE: mapper = new MapperHuC3(); mppr = "HuC3"; break; + case 0xFF: mapper = new MapperHuC1(); mppr = "HuC1"; break; + + case 0x4: + case 0x7: + case 0xA: + case 0xE: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x1F: + case 0x21: + default: + // mapper not implemented + throw new Exception("Mapper not implemented"); + break; + + } + + // special case for multi cart mappers + if ((_rom.HashMD5(0,_rom.Length) == "97122B9B183AAB4079C8D36A4CE6E9C1") || + (_rom.HashMD5(0, _rom.Length) == "9FB9C42CF52DCFDCFBAD5E61AE1B5777") || + (_rom.HashMD5(0, _rom.Length) == "CF1F58AB72112716D3C615A553B2F481") + ) + { + Console.WriteLine("Using Multi-Cart Mapper"); + mapper = new MapperMBC1Multi(); + } + + Console.Write("Mapper: "); + Console.WriteLine(mppr); + + cart_RAM = null; + + switch (header[0x49]) + { + case 1: + cart_RAM = new byte[0x800]; + break; + case 2: + cart_RAM = new byte[0x2000]; + break; + case 3: + cart_RAM = new byte[0x8000]; + break; + case 4: + cart_RAM = new byte[0x20000]; + break; + case 5: + cart_RAM = new byte[0x10000]; + break; + } + + // mbc2 carts have built in RAM + if (mppr == "MBC2") + { + cart_RAM = new byte[0x200]; + } + + mapper.Core = this; + mapper.Initialize(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs new file mode 100644 index 0000000000..1939f1b4c1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class GBHawkControllerDeck + { + public GBHawkControllerDeck(string controller1Name) + { + if (!ValidControllerTypes.ContainsKey(controller1Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller1Name); + } + + Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1); + + Definition = new ControllerDefinition + { + Name = Port1.Definition.Name, + BoolButtons = Port1.Definition.BoolButtons + .Concat(new[] + { + "Power", + "Reset", + }) + .ToList() + }; + + Definition.FloatControls.AddRange(Port1.Definition.FloatControls); + + Definition.FloatRanges.AddRange(Port1.Definition.FloatRanges); + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("Port1"); + Port1.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(GBHawkControllerDeck).Assembly + .GetTypes() + .Where(t => typeof(IPort).IsAssignableFrom(t)) + .Where(t => !t.IsAbstract && !t.IsInterface) + .ToDictionary(tkey => tkey.DisplayName()); + } + + return _controllerTypes; + } + } + + public static string DefaultControllerName => typeof(StandardControls).DisplayName(); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs new file mode 100644 index 0000000000..e322e4e252 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + /// + /// Represents a GB add on + /// + public interface IPort + { + byte Read(IController c); + + ControllerDefinition Definition { get; } + + void SyncState(Serializer ser); + + int PortNum { get; } + } + + [DisplayName("Standard controls")] + public class StandardControls : IPort + { + public StandardControls(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Game Boy", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList() + }; + } + + public int PortNum { get; } + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + for (int i = 0; i < 8; i++) + { + if (c.IsPressed(Definition.BoolButtons[i])) + { + result -= (byte)(1 << i); + } + } + + return result; + } + + private static readonly string[] BaseDefinition = + { + "Right", "Left", "Up", "Down", "A", "B", "Select", "Start" + }; + + public void SyncState(Serializer ser) + { + //nothing + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs new file mode 100644 index 0000000000..a55e91798e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -0,0 +1,283 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + public byte Read_Registers(int addr) + { + byte ret = 0; + + switch (addr) + { + // Read Input + case 0xFF00: + _islag = false; + + input_register &= 0xF0; + if ((input_register & 0x30) == 0x20) + { + input_register |= (byte)(controller_state & 0xF); + } + else if ((input_register & 0x30) == 0x10) + { + input_register |= (byte)((controller_state & 0xF0) >> 4); + } + else if ((input_register & 0x30) == 0x30) + { + // if both polls are set, then a bit is zero if either or both pins are zero + byte temp = (byte)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4)); + input_register |= temp; + } + else + { + input_register |= 0xF; + } + ret = input_register; + break; + + // Serial data port + case 0xFF01: + ret = serial_data_in; + break; + + // Serial port control + case 0xFF02: + ret = serial_control; + break; + + // Timer Registers + case 0xFF04: + case 0xFF05: + case 0xFF06: + case 0xFF07: + ret = timer.ReadReg(addr); + break; + + // Interrupt flags + case 0xFF0F: + ret = REG_FF0F; + break; + + // audio regs + case 0xFF10: + case 0xFF11: + case 0xFF12: + case 0xFF13: + case 0xFF14: + case 0xFF16: + case 0xFF17: + case 0xFF18: + case 0xFF19: + case 0xFF1A: + case 0xFF1B: + case 0xFF1C: + case 0xFF1D: + case 0xFF1E: + case 0xFF20: + case 0xFF21: + case 0xFF22: + case 0xFF23: + case 0xFF24: + case 0xFF25: + case 0xFF26: + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + ret = audio.ReadReg(addr); + break; + + // PPU Regs + case 0xFF40: + case 0xFF41: + case 0xFF42: + case 0xFF43: + case 0xFF44: + case 0xFF45: + case 0xFF46: + case 0xFF47: + case 0xFF48: + case 0xFF49: + case 0xFF4A: + case 0xFF4B: + ret = ppu.ReadReg(addr); + break; + + // Bios control register. Not sure if it is readable + case 0xFF50: + ret = 0xFF; + break; + + // interrupt control register + case 0xFFFF: + ret = REG_FFFF; + break; + + default: + ret = 0xFF; + break; + + } + return ret; + } + + public void Write_Registers(int addr, byte value) + { + switch (addr) + { + // select input + case 0xFF00: + input_register = (byte)(0xC0 | (value & 0x3F)); // top 2 bits always 1 + break; + + // Serial data port + case 0xFF01: + serial_data_out = value; + serial_data_in = serial_data_out; + break; + + // Serial port control + case 0xFF02: + serial_control = (byte)(0x7E | (value & 0x81)); // middle six bits always 1 + break; + + // Timer Registers + case 0xFF04: + case 0xFF05: + case 0xFF06: + case 0xFF07: + timer.WriteReg(addr, value); + break; + + // Interrupt flags + case 0xFF0F: + REG_FF0F = (byte)(0xE0 | value); + + // check if enabling any of the bits triggered an IRQ + for (int i = 0; i < 5; i++) + { + if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) + { + cpu.FlagI = true; + } + } + + // if no bits are in common between flags and enables, de-assert the IRQ + if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } + + break; + + // audio regs + case 0xFF10: + case 0xFF11: + case 0xFF12: + case 0xFF13: + case 0xFF14: + case 0xFF16: + case 0xFF17: + case 0xFF18: + case 0xFF19: + case 0xFF1A: + case 0xFF1B: + case 0xFF1C: + case 0xFF1D: + case 0xFF1E: + case 0xFF20: + case 0xFF21: + case 0xFF22: + case 0xFF23: + case 0xFF24: + case 0xFF25: + case 0xFF26: + case 0xFF30: + case 0xFF31: + case 0xFF32: + case 0xFF33: + case 0xFF34: + case 0xFF35: + case 0xFF36: + case 0xFF37: + case 0xFF38: + case 0xFF39: + case 0xFF3A: + case 0xFF3B: + case 0xFF3C: + case 0xFF3D: + case 0xFF3E: + case 0xFF3F: + audio.WriteReg(addr, value); + break; + + // PPU Regs + case 0xFF40: + case 0xFF41: + case 0xFF42: + case 0xFF43: + case 0xFF44: + case 0xFF45: + case 0xFF46: + case 0xFF47: + case 0xFF48: + case 0xFF49: + case 0xFF4A: + case 0xFF4B: + ppu.WriteReg(addr, value); + break; + + // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs + case 0xFF50: + //Console.WriteLine(value); + if (GB_bios_register != 1) + { + GB_bios_register = value; + } + break; + + // interrupt control register + case 0xFFFF: + REG_FFFF = value; + enable_VBL = REG_FFFF.Bit(0); + enable_STAT = REG_FFFF.Bit(1); + enable_TIMO = REG_FFFF.Bit(2); + enable_SER = REG_FFFF.Bit(3); + enable_PRS = REG_FFFF.Bit(4); + + // check if enabling any of the bits triggered an IRQ + for (int i = 0; i < 5; i++) + { + if (REG_FFFF.Bit(i) && REG_FF0F.Bit(i)) + { + cpu.FlagI = true; + } + } + + // if no bits are in common between flags and enables, de-assert the IRQ + if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } + + break; + } + } + + public void Register_Reset() + { + input_register = 0xCF; // not reading any input + serial_control = 0x7E; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs new file mode 100644 index 0000000000..0cd2cc11ea --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs @@ -0,0 +1,40 @@ +using BizHawk.Common; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class MapperBase + { + public GBHawk Core { get; set; } + + public virtual byte ReadMemory(ushort addr) + { + return 0; + } + + public virtual byte PeekMemory(ushort addr) + { + return 0; + } + + public virtual void WriteMemory(ushort addr, byte value) + { + } + + public virtual void PokeMemory(ushort addr, byte value) + { + } + + public virtual void SyncState(Serializer ser) + { + } + + public virtual void Dispose() + { + } + + public virtual void Initialize() + { + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs new file mode 100644 index 0000000000..b6ad35bb1b --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Camera.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperCamera : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Default.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Default.cs new file mode 100644 index 0000000000..2df4c92454 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Default.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperDefault : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs new file mode 100644 index 0000000000..cef7f2bbdc --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC1.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperHuC1 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs new file mode 100644 index 0000000000..0daf88475a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_HuC3.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperHuC3 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs new file mode 100644 index 0000000000..c61133bdcd --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1.cs @@ -0,0 +1,151 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // MBC1 with bank switching and RAM + public class MapperMBC1 : MapperBase + { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public bool sel_mode; + public int ROM_mask; + public int RAM_mask; + + public override void Initialize() + { + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + sel_mode = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed, but is still effected by mode + if (sel_mode) + { + return Core._rom[(ROM_bank & 0x60) * 0x4000 + addr]; + } + else + { + return Core._rom[addr]; + } + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + if (addr < 0x2000) + { + RAM_enable = ((value & 0xA) == 0xA) ? true : false; + } + else if (addr < 0x4000) + { + value &= 0x1F; + + // writing zero gets translated to 1 + if (value == 0) { value = 1; } + + ROM_bank &= 0xE0; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + if (sel_mode && Core.cart_RAM != null) + { + RAM_bank = value & 3; + RAM_bank &= RAM_mask; + } + else + { + ROM_bank &= 0x1F; + ROM_bank |= ((value & 3) << 5); + ROM_bank &= ROM_mask; + } + } + else + { + sel_mode = (value & 1) > 0; + + if (sel_mode && Core.cart_RAM != null) + { + ROM_bank &= 0x1F; + ROM_bank &= ROM_mask; + } + else + { + RAM_bank = 0; + } + } + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); + ser.Sync("RAM_enable", ref RAM_enable); + ser.Sync("sel_mode", ref sel_mode); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs new file mode 100644 index 0000000000..d72112aa40 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC1_Multi.cs @@ -0,0 +1,151 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // MBC1 with bank switching and RAM + public class MapperMBC1Multi : MapperBase + { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public bool sel_mode; + public int ROM_mask; + public int RAM_mask; + + public override void Initialize() + { + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + sel_mode = false; + ROM_mask = (Core._rom.Length / 0x4000 * 2) - 1; // due to how mapping workd, we want a 1 bit higher mask + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed, but is still effected by mode + if (sel_mode) + { + return Core._rom[((ROM_bank & 0x60) >> 1) * 0x4000 + addr]; + } + else + { + return Core._rom[addr]; + } + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + (((ROM_bank & 0x60) >> 1) | (ROM_bank & 0xF)) * 0x4000]; + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + if (addr < 0x2000) + { + RAM_enable = ((value & 0xA) == 0xA) ? true : false; + } + else if (addr < 0x4000) + { + value &= 0x1F; + + // writing zero gets translated to 1 + if (value == 0) { value = 1; } + + ROM_bank &= 0xE0; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + if (sel_mode && Core.cart_RAM != null) + { + RAM_bank = value & 3; + RAM_bank &= RAM_mask; + } + else + { + ROM_bank &= 0x1F; + ROM_bank |= ((value & 3) << 5); + ROM_bank &= ROM_mask; + } + } + else + { + sel_mode = (value & 1) > 0; + + if (sel_mode && Core.cart_RAM != null) + { + ROM_bank &= 0x1F; + ROM_bank &= ROM_mask; + } + else + { + RAM_bank = 0; + } + } + } + else + { + if (Core.cart_RAM != null) + { + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); + ser.Sync("RAM_enable", ref RAM_enable); + ser.Sync("sel_mode", ref sel_mode); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs new file mode 100644 index 0000000000..6327d00491 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC2.cs @@ -0,0 +1,83 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // MBC1 with bank switching and RAM + public class MapperMBC2 : MapperBase + { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public int ROM_mask; + + public override void Initialize() + { + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + return Core._rom[addr]; + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else if ((addr >= 0xA000) && (addr < 0xA200)) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0xFF; + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x2000) + { + RAM_enable = (addr & 0x100) > 0; + } + else if (addr < 0x4000) + { + if ((addr & 0x100) > 0) + { + ROM_bank = value & 0xF; + } + } + else if ((addr >= 0xA000) && (addr < 0xA200)) + { + if (RAM_enable) + { + Core.cart_RAM[addr - 0xA000] = (byte)(value & 0xF); + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_enable", ref RAM_enable); + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs new file mode 100644 index 0000000000..18a7f013c7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC3 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs new file mode 100644 index 0000000000..529f48ca4a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC5 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs new file mode 100644 index 0000000000..5cbdd331ff --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC6.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC6 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs new file mode 100644 index 0000000000..24d957c653 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMBC7 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs new file mode 100644 index 0000000000..0a45601998 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MMM01.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperMMM01 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs new file mode 100644 index 0000000000..dc6c6d8d9f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_TAMA5.cs @@ -0,0 +1,59 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Default mapper with no bank switching + public class MapperTAMA5 : MapperBase + { + public override void Initialize() + { + // nothing to initialize + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x8000) + { + return Core._rom[addr]; + } + else + { + if (Core.cart_RAM != null) + { + return Core.cart_RAM[addr - 0xA000]; + } + else + { + return 0; + } + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x8000) + { + // no mapping hardware available + } + else + { + if (Core.cart_RAM != null) + { + Core.cart_RAM[addr - 0xA000] = value; + } + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt new file mode 100644 index 0000000000..0c94c8272c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/ReadMe.txt @@ -0,0 +1,3 @@ +TODO: +Official Mappers +Unofficial Mappers diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs new file mode 100644 index 0000000000..fe6bb017ce --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -0,0 +1,168 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + + +/* + $FFFF Interrupt Enable Flag + $FF80-$FFFE Zero Page - 127 bytes + $FF00-$FF7F Hardware I/O Registers + $FEA0-$FEFF Unusable Memory + $FE00-$FE9F OAM - Object Attribute Memory + $E000-$FDFF Echo RAM - Reserved, Do Not Use + $D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only) + $C000-$CFFF Internal RAM - Bank 0 (fixed) + $A000-$BFFF Cartridge RAM (If Available) + $9C00-$9FFF BG Map Data 2 + $9800-$9BFF BG Map Data 1 + $8000-$97FF Character RAM + $4000-$7FFF Cartridge ROM - Switchable Banks 1-xx + $0150-$3FFF Cartridge ROM - Bank 0 (fixed) + $0100-$014F Cartridge Header Area + $0000-$00FF Restart and Interrupt Vectors +*/ + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public partial class GBHawk + { + public byte ReadMemory(ushort addr) + { + MemoryCallbacks.CallReads(addr, "System Bus"); + + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr < 0x8000) + { + return mapper.ReadMemory(addr); + } + else if (addr < 0x9800) + { + if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } + else { return 0xFF; } + } + else if (addr < 0x9C00) + { + if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } + else { return 0xFF; } + } + else if (addr < 0xA000) + { + if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + else { return 0xFF; } + } + else if (addr < 0xC000) + { + return mapper.ReadMemory(addr); + } + else if (addr < 0xE000) + { + return RAM[addr - 0xC000]; + } + else if (addr < 0xFE00) + { + return RAM[addr - 0xE000]; + } + else if (addr < 0xFEA0) + { + if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } + else { return 0xFF; } + } + else if (addr < 0xFF00) + { + // unmapped memory, returns 0xFF + return 0xFF; + } + else if (addr < 0xFF80) + { + return Read_Registers(addr); + } + else if (addr < 0xFFFF) + { + return ZP_RAM[addr - 0xFF80]; + } + else + { + return Read_Registers(addr); + } + + } + + public void WriteMemory(ushort addr, byte value) + { + MemoryCallbacks.CallWrites(addr, "System Bus"); + + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + // Can't write to BIOS region + } + else + { + mapper.WriteMemory(addr, value); + } + } + else if (addr < 0x8000) + { + mapper.WriteMemory(addr, value); + } + else if (addr < 0x9800) + { + if (ppu.VRAM_access_write) { CHR_RAM[addr - 0x8000] = value; } + } + else if (addr < 0x9C00) + { + if (ppu.VRAM_access_write) { BG_map_1[addr - 0x9800] = value; } + } + else if (addr < 0xA000) + { + if (ppu.VRAM_access_write) { BG_map_2[addr - 0x9C00] = value; } + } + else if (addr < 0xC000) + { + mapper.WriteMemory(addr, value); + } + else if (addr < 0xE000) + { + RAM[addr - 0xC000] = value; + } + else if (addr < 0xFE00) + { + RAM[addr - 0xE000] = value; + } + else if (addr < 0xFEA0) + { + if (ppu.OAM_access_write) { OAM[addr - 0xFE00] = value; } + } + else if (addr < 0xFF00) + { + // unmapped, writing has no effect + } + else if (addr < 0xFF80) + { + Write_Registers(addr, value); + } + else if (addr < 0xFFFF) + { + ZP_RAM[addr - 0xFF80] = value; + } + else + { + Write_Registers(addr, value); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs new file mode 100644 index 0000000000..edf4110745 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -0,0 +1,1197 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class PPU + { + public GBHawk Core { get; set; } + + //public byte BGP_l; + + // register variables + public byte LCDC; + public byte STAT; + public byte scroll_y; + public byte scroll_x; + public byte LY; + public byte LY_inc; + public byte LYC; + public byte DMA_addr; + public byte BGP; + public byte obj_pal_0; + public byte obj_pal_1; + public byte window_y; + public byte window_x; + public bool DMA_start; + public int DMA_clock; + public int DMA_inc; + public byte DMA_byte; + + // state variables + public int cycle; + public bool LYC_INT; + public bool HBL_INT; + public bool VBL_INT; + public bool OAM_INT; + public bool LCD_was_off; + public bool stat_line; + public bool stat_line_old; + public int hbl_countdown; + // OAM scan + public bool OAM_access_read; + public bool OAM_access_write; + public int OAM_scan_index; + public int SL_sprites_index; + public int[] SL_sprites = new int[40]; + public int write_sprite; + public bool no_scan; + // render + public bool VRAM_access_read; + public bool VRAM_access_write; + public int read_case; + public int internal_cycle; + public int y_tile; + public int y_scroll_offset; + public int x_tile; + public int x_scroll_offset; + public int tile_byte; + public int sprite_fetch_cycles; + public bool fetch_sprite; + public int temp_fetch; + public int tile_inc; + public bool pre_render; + public byte[] tile_data = new byte[2]; + public byte[] tile_data_latch = new byte[2]; + public int latch_counter; + public bool latch_new_data; + public int render_counter; + public int render_offset; + public int pixel_counter; + public int pixel; + public byte[] sprite_data = new byte[2]; + public byte[] sprite_sel = new byte[2]; + public int sl_use_index; + public bool no_sprites; + public int sprite_fetch_index; + public int[] SL_sprites_ordered = new int[40]; // (x_end, data_low, data_high, attr) + public int index_used; + public int sprite_ordered_index; + public int bottom_index; + + // windowing state + public int window_counter; + public bool window_pre_render; + public bool window_started; + public int window_tile_inc; + public int window_y_tile; + public int window_x_tile; + public int window_y_tile_inc; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: ret = 0xFF; break; // DMA (not readable?) /*ret = DMA_addr; */ + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + LCDC = value; + break; + case 0xFF41: // STAT + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LY != LYC) { STAT &= 0xFB; } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + } + } + + public void tick() + { + // tick DMA + if (DMA_start) + { + if (DMA_clock >= 4) + { + OAM_access_read = false; + OAM_access_write = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + DMA_byte = Core.ReadMemory((ushort)((DMA_addr << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + if ((DMA_inc % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + } + else + { + Core.OAM[DMA_inc] = DMA_byte; + } + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock == 648) + { + OAM_access_read = true; + OAM_access_write = true; + DMA_start = false; + } + } + + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // start the next scanline + if (cycle == 456) + { + cycle = 0; + LY += LY_inc; + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + no_scan = false; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + if (LY_inc == 1) + { + //LYC_INT = false; + STAT &= 0xFB; + } + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + } + + Core.cpu.LY = LY; + + if (LY == 144) + { + Core.in_vblank = true; + + } + } + + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 8)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (no_scan) + { + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? + + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + else + { + if (cycle < 80) + { + if (cycle == 4) + { + // apparently, writes can make it to OAm one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if (cycle >= 80 && LY < 144) + { + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } + } + + } + + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted + if ((cycle == 4) && (LY != 0)) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + } + else + { + // screen disable sets STAT as though it were vblank, but there is no Stat IRQ asserted + STAT &= 0xFC; + STAT |= 0x01; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + + } + + // might be needed, not sure yet + public void latch_delay() + { + //BGP_l = BGP; + } + + public void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access_read = false; + + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + // (sprite Y - 16) equals LY, we have a sprite + if ((Core.OAM[OAM_scan_index * 4] - 16) <= LY && + ((Core.OAM[OAM_scan_index * 4] - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = Core.OAM[OAM_scan_index * 4]; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + SL_sprites[SL_sprites_index * 4 + write_sprite] = Core.OAM[OAM_scan_index * 4 + write_sprite]; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public void render(int render_cycle) + { + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = 0; + sl_use_index = 0; + index_used = 0; + bottom_index = 0; + sprite_ordered_index = 0; + fetch_sprite = false; + no_sprites = false; + + window_pre_render = false; + if (window_started && LCDC.Bit(5)) + { + window_y_tile_inc++; + if (window_y_tile_inc==8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + window_started = false; + + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + if (SL_sprites_index == 0) + { + no_sprites = true; + } + } + + // before anything else, we have to check if windowing is in effect + if (LCDC.Bit(5) && !window_started && LY >= window_y && pixel_counter >= window_x - 7) + { + /* + Console.Write(LY); + Console.Write(" "); + Console.Write(window_y); + Console.Write(" "); + Console.Write(window_y_tile_inc); + Console.Write(" "); + Console.WriteLine(scroll_y); + */ + if (pixel_counter == 0 && window_x <= 7) + { + // if the window starts at zero, we still do the first access to the BG + // but then restart all over again at the window + window_pre_render = true; + } + else + { + // otherwise, just restart the whole process as if starting BG again + window_pre_render = true; + read_case = 4; + } + window_counter = 0; + + window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x - 7)) / 8); + + window_tile_inc = 0; + window_started = true; + } + + if (!pre_render && !fetch_sprite && !window_pre_render) + { + // start by fetching all the sprites that need to be fetched + if (!no_sprites) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < SL_sprites[i * 4 + 1]) && + !index_used.Bit(i)) + { + fetch_sprite = true; + sprite_fetch_index = 0; + } + } + } + + if (!fetch_sprite) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + pixel = (BGP >> (pixel * 2)) & 3; + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int i = bottom_index; + int s_pixel = 0; + int sprite_attr = 0; + + while (i < sprite_ordered_index) + { + if (SL_sprites_ordered[i * 4] == pixel_counter) + { + bottom_index++; + if (bottom_index == SL_sprites_index) { no_sprites = true; } + } + else if (!have_sprite) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = pixel_counter - (SL_sprites_ordered[i * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = sprite_data[0] + sprite_data[1]; + sprite_attr = SL_sprites_ordered[i * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_sprite = true; + } + } + i++; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + if (s_pixel != 0) { use_sprite = true; } + } + else if (pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core._vidbuffer[LY * 160 + pixel_counter] = (int)GBHawk.color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_countdown = 4; + } + } + render_counter++; + } + } + + if (!fetch_sprite) + { + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + + } + else + { + read_case = 1; + if (!pre_render) + { + tile_inc++; + if (window_pre_render) + { + read_case = 4; + } + } + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = -1; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + if ((window_counter % 2) == 0) + { + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } + else + { + if (!window_pre_render) + { + window_tile_inc++; + } + read_case = 5; + } + window_counter++; + break; + + case 5: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (LCDC.Bit(4)) + { + + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + } + else + { + read_case = 6; + } + window_counter++; + break; + + case 6: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (window_pre_render) + { + // here we set up rendering + window_pre_render = false; + render_offset = 0; + render_counter = -1; + latch_counter = 0; + read_case = 4; + } + else + { + read_case = 7; + } + } + window_counter++; + break; + + case 7: // read from sprite data + if ((window_counter % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 4; + latch_new_data = true; + } + window_counter++; + break; + + case 8: // done reading, we are now in phase 0 + + pre_render = true; + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) + { + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + + break; + } + internal_cycle++; + } + + if (fetch_sprite) + { + if (sprite_fetch_index < SL_sprites_index) + { + if (pixel_counter != 0) { + if ((pixel_counter == (SL_sprites[sprite_fetch_index * 4 + 1] - 8)) && + //(pixel_counter < SL_sprites[sprite_fetch_index * 4 + 1]) && + !index_used.Bit(sprite_fetch_index)) + { + sl_use_index = sprite_fetch_index; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[sprite_fetch_index * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[sprite_fetch_index * 4 + 3]; + sprite_ordered_index++; + index_used |= (1 << sl_use_index); + } + sprite_fetch_index++; + if (sprite_fetch_index == SL_sprites_index) { fetch_sprite = false; } + } + else + { + // whan pixel counter is 0, we want to scan all the points before 0 as well + // certainly non-physical but good enough for now + for (int j = -7; j < 1; j++) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((j == (SL_sprites[i * 4 + 1] - 8)) && + !index_used.Bit(i)) + { + sl_use_index = i; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[i * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[i * 4 + 3]; + sprite_ordered_index++; + index_used |= (1 << sl_use_index); + } + } + } + fetch_sprite = false; + } + } + } + } + + public void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0; + BGP = 0; + obj_pal_0 = 0xFF; + obj_pal_1 = 0xFF; + window_y = 0; + window_x = 0; + LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + + window_counter = 0; + window_pre_render = false; + window_started = false; + window_tile_inc = 0; + window_y_tile = 0; + window_x_tile = 0; + window_y_tile_inc = 0; + } + + public void process_sprite() + { + int y; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + public void SyncState(Serializer ser) + { + ser.Sync("LCDC", ref LCDC); + ser.Sync("STAT", ref STAT); + ser.Sync("scroll_y", ref scroll_y); + ser.Sync("scroll_x", ref scroll_x); + ser.Sync("LY", ref LY); + ser.Sync("LYinc", ref LY_inc); + ser.Sync("LYC", ref LYC); + ser.Sync("DMA_addr", ref DMA_addr); + ser.Sync("BGP", ref BGP); + ser.Sync("obj_pal_0", ref obj_pal_0); + ser.Sync("obj_pal_1", ref obj_pal_1); + ser.Sync("window_y", ref window_y); + ser.Sync("window_x", ref window_x); + ser.Sync("DMA_start", ref DMA_start); + ser.Sync("DMA_clock", ref DMA_clock); + ser.Sync("DMA_inc", ref DMA_inc); + ser.Sync("DMA_byte", ref DMA_byte); + + ser.Sync("cycle", ref cycle); + ser.Sync("LYC_INT", ref LYC_INT); + ser.Sync("HBL_INT", ref HBL_INT); + ser.Sync("VBL_INT", ref VBL_INT); + ser.Sync("OAM_INT", ref OAM_INT); + ser.Sync("stat_line", ref stat_line); + ser.Sync("stat_line_old", ref stat_line_old); + ser.Sync("hbl_countdown", ref hbl_countdown); + ser.Sync("LCD_was_off", ref LCD_was_off); + ser.Sync("OAM_scan_index", ref OAM_scan_index); + ser.Sync("SL_sprites_index", ref SL_sprites_index); + ser.Sync("SL_sprites", ref SL_sprites, false); + ser.Sync("write_sprite", ref write_sprite); + ser.Sync("no_scan", ref no_scan); + + ser.Sync("OAM_access_read", ref OAM_access_read); + ser.Sync("OAM_access_write", ref OAM_access_write); + ser.Sync("VRAM_access_read", ref VRAM_access_read); + ser.Sync("VRAM_access_write", ref VRAM_access_write); + + ser.Sync("read_case", ref read_case); + ser.Sync("internal_cycle", ref internal_cycle); + ser.Sync("y_tile", ref y_tile); + ser.Sync("y_scroll_offset", ref y_scroll_offset); + ser.Sync("x_tile", ref x_tile); + ser.Sync("x_scroll_offset", ref x_scroll_offset); + ser.Sync("tile_byte", ref tile_byte); + ser.Sync("sprite_fetch_cycles", ref sprite_fetch_cycles); + ser.Sync("fetch_sprite", ref fetch_sprite); + ser.Sync("temp_fetch", ref temp_fetch); + ser.Sync("tile_inc", ref tile_inc); + ser.Sync("pre_render", ref pre_render); + ser.Sync("tile_data", ref tile_data, false); + ser.Sync("tile_data_latch", ref tile_data_latch, false); + ser.Sync("latch_counter", ref latch_counter); + ser.Sync("latch_new_data", ref latch_new_data); + ser.Sync("render_counter", ref render_counter); + ser.Sync("render_offset", ref render_offset); + ser.Sync("pixel_counter", ref pixel_counter); + ser.Sync("pixel", ref pixel); + ser.Sync("sprite_data", ref sprite_data, false); + ser.Sync("sl_use_index", ref sl_use_index); + ser.Sync("sprite_sel", ref sprite_sel, false); + ser.Sync("no_sprites", ref no_sprites); + ser.Sync("sprite_fetch_index", ref sprite_fetch_index); + ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false); + ser.Sync("index_used", ref index_used); + ser.Sync("sprite_ordered_index", ref sprite_ordered_index); + ser.Sync("bottom_index", ref bottom_index); + + ser.Sync("window_counter", ref window_counter); + ser.Sync("window_pre_render", ref window_pre_render); + ser.Sync("window_started", ref window_started); + ser.Sync("window_tile_inc", ref window_tile_inc); + ser.Sync("window_y_tile", ref window_y_tile); + ser.Sync("window_x_tile", ref window_x_tile); + ser.Sync("window_y_tile_inc", ref window_y_tile_inc); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/ReadMe.txt @@ -0,0 +1 @@ +TODO: diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs new file mode 100644 index 0000000000..817c28201e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs @@ -0,0 +1,175 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // Timer Emulation + public class Timer + { + public GBHawk Core { get; set; } + + public ushort divider_reg; + public byte timer_reload; + public byte timer; + public byte timer_old; + public byte timer_control; + public byte pending_reload; + public byte write_ignore; + public bool old_state; + public bool state; + public bool reload_block; + public bool TMA_coincidence; + + public byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF04: ret = (byte)(divider_reg >> 8); break; // DIV register + case 0xFF05: ret = timer; break; // TIMA (Timer Counter) + case 0xFF06: ret = timer_reload; break; // TMA (Timer Modulo) + case 0xFF07: ret = timer_control; break; // TAC (Timer Control) + } + + return ret; + } + + public void WriteReg(int addr, byte value) + { + switch (addr) + { + // DIV register + case 0xFF04: + divider_reg = 0; + break; + + // TIMA (Timer Counter) + case 0xFF05: + if (write_ignore == 0) + { + timer_old = timer; + timer = value; + reload_block = true; + } + break; + + // TMA (Timer Modulo) + case 0xFF06: + timer_reload = value; + if (TMA_coincidence) + { + timer = timer_reload; + timer_old = timer; + } + break; + + // TAC (Timer Control) + case 0xFF07: + timer_control = (byte)((timer_control & 0xf8) | (value & 0x7)); // only bottom 3 bits function + break; + } + } + + public void tick_1() + { + if (write_ignore > 0) + { + write_ignore--; + if (write_ignore==0) + { + TMA_coincidence = false; + } + } + + if (pending_reload > 0) + { + pending_reload--; + if (pending_reload == 0 && !reload_block) + { + timer = timer_reload; + timer_old = timer; + write_ignore = 4; + TMA_coincidence = true; + + // set interrupts + if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x04; + } + } + } + + public void tick_2() + { + divider_reg++; + + // pick a bit to test based on the current value of timer control + switch (timer_control & 3) + { + case 0: + state = divider_reg.Bit(9); + break; + case 1: + state = divider_reg.Bit(3); + break; + case 2: + state = divider_reg.Bit(5); + break; + case 3: + state = divider_reg.Bit(7); + break; + } + + // And it with the state of the timer on/off bit + state &= timer_control.Bit(2); + + // this procedure allows several glitchy timer ticks, since it only measures falling edge of the state + // so things like turning the timer off and resetting the divider will tick the timer + if (old_state && !state) + { + timer_old = timer; + timer++; + + // if overflow, set the interrupt flag and reload the timer (4 clocks later) + if (timer < timer_old) + { + pending_reload = 4; + reload_block = false; + } + } + + old_state = state; + } + + public void Reset() + { + divider_reg = 0; + timer_reload = 0; + timer = 0; + timer_old = 0; + timer_control = 0xF8; + pending_reload = 0; + write_ignore = 0; + old_state = false; + state = false; + reload_block = false; + TMA_coincidence = false; + } + + public void SyncState(Serializer ser) + { + ser.Sync("divider_reg", ref divider_reg); + ser.Sync("timer_reload", ref timer_reload); + ser.Sync("timer", ref timer); + ser.Sync("timer_old", ref timer_old); + ser.Sync("timer_control", ref timer_control); + ser.Sync("pending_reload", ref pending_reload); + ser.Sync("write_ignore", ref write_ignore); + ser.Sync("old_state", ref old_state); + ser.Sync("state", ref state); + ser.Sync("reload_block", ref reload_block); + ser.Sync("TMA_coincidence", ref TMA_coincidence); + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs index 2f2fdf81c5..099c25e30e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GBDisassembler.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Common.Components.Z80GB; +using BizHawk.Emulation.Common.Components.LR35902; namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { @@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public override string Disassemble(MemoryDomain m, uint addr, out int length) { ushort tmp; - string ret = NewDisassembler.Disassemble((ushort)addr, a => m.PeekByte(a), out tmp); + string ret = LR35902.Disassemble((ushort)addr, a => m.PeekByte(a), out tmp); length = tmp; return ret; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index fb41292935..7e2ba05432 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -80,11 +80,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public class GambatteSyncSettings { - [DisplayName("Enable BIOS: WARNING: File must exist!")] - [Description("Boots game using system BIOS. Should be used for TASing")] - [DefaultValue(false)] - public bool EnableBIOS { get; set; } - public enum ConsoleModeType { Auto, diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs index f2c92adc34..b7b7499e4a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs @@ -1,7 +1,7 @@ using System; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Common.Components.Z80GB; +using BizHawk.Emulation.Common.Components.LR35902; namespace BizHawk.Emulation.Cores.Nintendo.Gameboy { @@ -19,8 +19,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy Tracer.Put(new TraceInfo { Disassembly = - NewDisassembler - .Disassemble((ushort)s[1], addr => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused) + LR35902.Disassemble((ushort)s[1], addr => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused) .PadRight(36), RegisterInfo = string.Format( diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index fcfa9a0486..f4e67dae32 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -99,16 +99,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy break; } - if (_syncSettings.EnableBIOS && BiosRom == null) - { - throw new MissingFirmwareException("Boot Rom not found"); - } - // to disable BIOS loading into gambatte, just set bios_length to 0 - if (!_syncSettings.EnableBIOS) - { - bios_length = 0; - } + bios_length = 0; if (_syncSettings.GBACGB) { diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1 @@ +TODO: