GBHawk: Attempt at some halt bugs
This commit is contained in:
parent
e977826c5e
commit
19c509e9c2
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue