GBHawk: Attempt at some halt bugs

This commit is contained in:
alyosha-tas 2018-05-01 21:53:20 -04:00
parent e977826c5e
commit 19c509e9c2
5 changed files with 109 additions and 40 deletions

View File

@ -4,12 +4,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902
{ {
public partial class 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; private bool nonMaskableInterrupt;
public bool NonMaskableInterrupt public bool NonMaskableInterrupt
{ {
@ -87,11 +81,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902
public int stop_time; public int stop_time;
public bool stop_check; public bool stop_check;
public bool is_GBC; // GBC automatically adds a NOP to avoid the HALT bug (according to Sinimas) 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() private void ResetInterrupts()
{ {
IFF1 = false; I_use = false;
IFF2 = false; skip_once = false;
Halt_bug_2 = false;
Halt_bug_3 = false;
NonMaskableInterrupt = false; NonMaskableInterrupt = false;
NonMaskableInterruptPending = false; NonMaskableInterruptPending = false;
InterruptMode = 1; InterruptMode = 1;

View File

@ -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 EI_RETI = 43; // reti has no delay in interrupt enable
public const ushort INT_GET = 44; public const ushort INT_GET = 44;
public const ushort GBC_INTERRUPT = 45; public const ushort GBC_INTERRUPT = 45;
public const ushort HALT_CHK = 46; // when in halt mode, actually check I Flag here
public LR35902() public LR35902()
{ {
@ -68,7 +69,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902
{ {
ResetRegisters(); ResetRegisters();
ResetInterrupts(); ResetInterrupts();
TotalExecutedCycles = 0; TotalExecutedCycles = 8;
stop_check = false; stop_check = false;
cur_instr = new ushort[] { OP }; cur_instr = new ushort[] { OP };
} }
@ -255,6 +256,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902
case HALT: case HALT:
halted = true; halted = true;
bool temp = false;
if (cur_instr[instr_pntr++] == 1)
{
temp = FlagI;
}
else
{
temp = I_use;
}
if (EI_pending > 0 && !CB_prefix) if (EI_pending > 0 && !CB_prefix)
{ {
EI_pending--; 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 the I flag is asserted at the time of halt, don't halt
if (temp && interrupts_enabled && !CB_prefix && !jammed)
if (FlagI && interrupts_enabled && !CB_prefix && !jammed)
{ {
interrupts_enabled = false; interrupts_enabled = false;
@ -298,7 +309,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902
INTERRUPT_(); INTERRUPT_();
} }
} }
else if (FlagI) else if (temp)
{ {
// even if interrupt servicing is disabled, any interrupt flag raised still resumes execution // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution
if (TraceCallback != null) if (TraceCallback != null)
@ -324,17 +335,44 @@ namespace BizHawk.Emulation.Common.Components.LR35902
} }
else 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 else
{ {
cur_instr = new ushort[] if (skip_once)
{IDLE, {
IDLE, cur_instr = new ushort[]
IDLE, {IDLE,
HALT }; IDLE,
IDLE,
HALT, 0 };
skip_once = false;
}
else
{
cur_instr = new ushort[]
{
IDLE,
HALT_CHK,
IDLE,
HALT, 0 };
}
} }
I_use = false;
instr_pntr = 0; instr_pntr = 0;
break; break;
case STOP: case STOP:
@ -453,6 +491,18 @@ namespace BizHawk.Emulation.Common.Components.LR35902
INTERRUPT_(); INTERRUPT_();
instr_pntr = 0; instr_pntr = 0;
break; 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++; totalExecutedCycles++;
} }
@ -506,8 +556,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902
ser.Sync("NMI", ref nonMaskableInterrupt); ser.Sync("NMI", ref nonMaskableInterrupt);
ser.Sync("NMIPending", ref nonMaskableInterruptPending); ser.Sync("NMIPending", ref nonMaskableInterruptPending);
ser.Sync("IM", ref interruptMode); ser.Sync("IM", ref interruptMode);
ser.Sync("IFF1", ref iff1); ser.Sync("I_use", ref I_use);
ser.Sync("IFF2", ref iff2); 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("Halted", ref halted);
ser.Sync("ExecutedCycles", ref totalExecutedCycles); ser.Sync("ExecutedCycles", ref totalExecutedCycles);
ser.Sync("EI_pending", ref EI_pending); ser.Sync("EI_pending", ref EI_pending);

View File

@ -76,8 +76,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902
private void HALT_() private void HALT_()
{ {
if (FlagI && (EI_pending == 0) && !interrupts_enabled)
if (FlagI && (EI_pending == 0))
{ {
if (is_GBC) if (is_GBC)
{ {
@ -96,21 +95,31 @@ namespace BizHawk.Emulation.Common.Components.LR35902
else else
{ // if interrupts are disabled, { // if interrupts are disabled,
// a glitchy decrement to the program counter happens // a glitchy decrement to the program counter happens
cur_instr = new ushort[] {
cur_instr = new ushort[]
{IDLE, {IDLE,
IDLE, IDLE,
IDLE, IDLE,
OP_G}; OP_G};
}
} }
} }
else else
{ {
cur_instr = new ushort[] cur_instr = new ushort[]
{IDLE, {
IDLE, IDLE,
IDLE, HALT_CHK,
HALT }; 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) private void JR_COND(bool cond)

View File

@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
[DisplayName("Timer Div Initial Time")] [DisplayName("Timer Div Initial Time")]
[Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] [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 public int DivInitialTime
{ {
get { return _DivInitialTime; } get { return _DivInitialTime; }

View File

@ -75,7 +75,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
break; break;
case 0xFF45: // LYC case 0xFF45: // LYC
LYC = value; 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; break;
case 0xFF46: // DMA case 0xFF46: // DMA
DMA_addr = value; DMA_addr = value;
@ -124,7 +128,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
LY += LY_inc; LY += LY_inc;
no_scan = false; 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) if (LY_inc == 1)
{ {
LYC_INT = false; 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 // also, the LCD does not enter mode 2 on scanline 0 when first turned on
no_scan = true; no_scan = true;
cycle = 8; cycle = 4;
} }
// the VBL stat is continuously asserted // 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; HBL_INT = false;
@ -205,6 +209,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; }
Core.REG_FF0F |= 0x01; Core.REG_FF0F |= 0x01;
//Console.WriteLine(Core.cpu.TotalExecutedCycles);
} }
if ((LY >= 144) && (cycle == 4)) 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) // there is no mode 2 (presumably it missed the trigger)
// mode 3 is very short, probably in some self test mode before turning on? // mode 3 is very short, probably in some self test mode before turning on?
if (cycle == 12) if (cycle == 8)
{ {
LYC_INT = false; if (LY != LYC)
STAT &= 0xFB; {
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 // set STAT coincidence FLAG and interrupt flag if it is enabled
STAT |= 0x04; STAT |= 0x04;
@ -314,7 +322,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
STAT &= 0xFB; STAT &= 0xFB;
// Special case of LY = LYC // 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 // set STAT coincidence FLAG and interrupt flag if it is enabled
STAT |= 0x04; STAT |= 0x04;
@ -331,7 +339,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// here LY=LYC will be asserted // here LY=LYC will be asserted
if ((cycle == 4) && (LY != 0)) 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 // set STAT coincidence FLAG and interrupt flag if it is enabled
STAT |= 0x04; STAT |= 0x04;
@ -343,7 +351,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
} }
else else
{ {
STAT &= 0xF8; STAT &= 0xFC;
VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false;