From 19c509e9c2f62c3191a9f123284b871cc561e521 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 1 May 2018 21:53:20 -0400 Subject: [PATCH] GBHawk: Attempt at some halt bugs --- .../CPUs/LR35902/Interrupts.cs | 16 ++-- .../CPUs/LR35902/LR35902.cs | 76 ++++++++++++++++--- .../CPUs/LR35902/Tables_Direct.cs | 25 ++++-- .../Nintendo/GBHawk/GBHawk.ISettable.cs | 2 +- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 30 +++++--- 5 files changed, 109 insertions(+), 40 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 04e9eb38fb..e20cbcd90e 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -4,12 +4,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private bool iff1; - public bool IFF1 { get { return iff1; } set { iff1 = value; } } - - private bool iff2; - public bool IFF2 { get { return iff2; } set { iff2 = value; } } - private bool nonMaskableInterrupt; public bool NonMaskableInterrupt { @@ -87,11 +81,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public int stop_time; public bool stop_check; public bool is_GBC; // GBC automatically adds a NOP to avoid the HALT bug (according to Sinimas) + public bool I_use; // in halt mode, the I flag is checked earlier then when deicision to IRQ is taken + public bool skip_once; + public bool Halt_bug_2; + public bool Halt_bug_3; private void ResetInterrupts() { - IFF1 = false; - IFF2 = false; + I_use = false; + skip_once = false; + Halt_bug_2 = false; + Halt_bug_3 = false; NonMaskableInterrupt = false; NonMaskableInterruptPending = false; InterruptMode = 1; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 02205e5c09..cf4576b297 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -58,6 +58,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public const ushort EI_RETI = 43; // reti has no delay in interrupt enable public const ushort INT_GET = 44; public const ushort GBC_INTERRUPT = 45; + public const ushort HALT_CHK = 46; // when in halt mode, actually check I Flag here public LR35902() { @@ -68,7 +69,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { ResetRegisters(); ResetInterrupts(); - TotalExecutedCycles = 0; + TotalExecutedCycles = 8; stop_check = false; cur_instr = new ushort[] { OP }; } @@ -255,6 +256,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case HALT: halted = true; + bool temp = false; + + if (cur_instr[instr_pntr++] == 1) + { + temp = FlagI; + } + else + { + temp = I_use; + } + if (EI_pending > 0 && !CB_prefix) { EI_pending--; @@ -265,8 +277,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } // if the I flag is asserted at the time of halt, don't halt - - if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + if (temp && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; @@ -298,7 +309,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 INTERRUPT_(); } } - else if (FlagI) + else if (temp) { // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution if (TraceCallback != null) @@ -324,17 +335,44 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } else { - FetchInstruction(ReadMemory(RegPC++)); + if (Halt_bug_3) + { + //special variant of halt bug where RegPC also isn't incremented post fetch + RegPC++; + FetchInstruction(ReadMemory(RegPC)); + Halt_bug_3 = false; + } + else + { + FetchInstruction(ReadMemory(RegPC++)); + } } } else { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; + if (skip_once) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT, 0 }; + + skip_once = false; + } + else + { + cur_instr = new ushort[] + { + IDLE, + HALT_CHK, + IDLE, + + HALT, 0 }; + } + } + I_use = false; instr_pntr = 0; break; case STOP: @@ -453,6 +491,18 @@ namespace BizHawk.Emulation.Common.Components.LR35902 INTERRUPT_(); instr_pntr = 0; break; + case HALT_CHK: + // only used when exiting HALT from GBC, an extra NOP is added to avoid HALT bug + I_use = FlagI; + if (Halt_bug_2 && I_use) + { + RegPC--; + + if (!interrupts_enabled) { Halt_bug_3 = true; } + } + + Halt_bug_2 = false; + break; } totalExecutedCycles++; } @@ -506,8 +556,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 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("I_use", ref I_use); + ser.Sync("skip_once", ref skip_once); + ser.Sync("Halt_bug_2", ref Halt_bug_2); + ser.Sync("Halt_bug_3", ref Halt_bug_3); ser.Sync("Halted", ref halted); ser.Sync("ExecutedCycles", ref totalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index 78e64d7846..3325ffcc1c 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -76,8 +76,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void HALT_() { - - if (FlagI && (EI_pending == 0)) + if (FlagI && (EI_pending == 0) && !interrupts_enabled) { if (is_GBC) { @@ -96,21 +95,31 @@ namespace BizHawk.Emulation.Common.Components.LR35902 else { // if interrupts are disabled, // a glitchy decrement to the program counter happens - cur_instr = new ushort[] + { + cur_instr = new ushort[] {IDLE, IDLE, IDLE, OP_G}; + } } } else { cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; - } + { + IDLE, + HALT_CHK, + IDLE, + HALT, 1 }; + skip_once = true; + // If the interrupt flag is not currently set, but it does get set in the first check + // then a bug is triggered + // With interrupts enabled, this runs the halt command twice + // when they are disabled, it reads the next byte twice + if (!FlagI) { Halt_bug_2 = true; } + + } } private void JR_COND(bool cond) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 5de963bc1f..695c1a1481 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk [DisplayName("Timer Div Initial Time")] [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] - [DefaultValue(0)] + [DefaultValue(8)] public int DivInitialTime { get { return _DivInitialTime; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 209af304ed..d819b614d7 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -75,7 +75,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 0xFF45: // LYC LYC = value; - if (LY != LYC) { STAT &= 0xFB; } + if (LCDC.Bit(7)) + { + if (LY != LYC) { STAT &= 0xFB; } + else { STAT |= 0x4; /*if (STAT.Bit(6)) { LYC_INT = true; } */} + } break; case 0xFF46: // DMA DMA_addr = value; @@ -124,7 +128,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY += LY_inc; no_scan = false; - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + // 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; @@ -177,7 +181,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 8; + cycle = 4; } // the VBL stat is continuously asserted @@ -195,7 +199,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if ((cycle == 4) && (LY == 144)) { + if ((cycle == 0) && (LY == 144)) { HBL_INT = false; @@ -205,6 +209,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } Core.REG_FF0F |= 0x01; + //Console.WriteLine(Core.cpu.TotalExecutedCycles); } if ((LY >= 144) && (cycle == 4)) @@ -229,12 +234,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // 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) + if (cycle == 8) { - LYC_INT = false; - STAT &= 0xFB; + if (LY != LYC) + { + LYC_INT = false; + STAT &= 0xFB; + } - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -314,7 +322,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk STAT &= 0xFB; // Special case of LY = LYC - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -331,7 +339,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here LY=LYC will be asserted if ((cycle == 4) && (LY != 0)) { - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -343,7 +351,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - STAT &= 0xF8; + STAT &= 0xFC; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false;