GBHawk: HDMA and GBC halt commits

This commit is contained in:
alyosha-tas 2018-03-25 21:22:27 -04:00
parent 380b9f085e
commit d4ee8f480b
3 changed files with 92 additions and 17 deletions

View File

@ -57,6 +57,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902
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 const ushort GBC_INTERRUPT = 45;
public LR35902()
{
@ -278,8 +279,21 @@ namespace BizHawk.Emulation.Common.Components.LR35902
});
}
halted = false;
// call interrupt processor
INTERRUPT_();
if (is_GBC)
{
// call the interrupt processor after 4 extra cycles
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
GBC_INTERRUPT };
}
else
{
// call interrupt processor
INTERRUPT_();
}
}
else if (FlagI)
{
@ -295,7 +309,19 @@ namespace BizHawk.Emulation.Common.Components.LR35902
halted = false;
if (OnExecFetch != null) OnExecFetch(RegPC);
if (TraceCallback != null && !CB_prefix) TraceCallback(State());
FetchInstruction(ReadMemory(RegPC++));
if (is_GBC)
{
// extra 4 cycles for GBC
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
OP };
}
else
{
FetchInstruction(ReadMemory(RegPC++));
}
}
else
{
@ -419,6 +445,11 @@ namespace BizHawk.Emulation.Common.Components.LR35902
Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src];
break;
case GBC_INTERRUPT:
// only used when exiting HALT from GBC, an extra NOP is added to avoid HALT bug
INTERRUPT_();
instr_pntr = 0;
break;
}
totalExecutedCycles++;
}

View File

@ -76,25 +76,42 @@ namespace BizHawk.Emulation.Common.Components.LR35902
private void HALT_()
{
if (FlagI && (EI_pending == 0))
{
// if interrupts are disabled,
// a glitchy decrement to the program counter happens
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
OP_G};
if (is_GBC)
{
// in GBC mode, the HALT bug is worked around by simply adding a NOP
// so it just takes 4 cycles longer to reach the next instruction
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
IDLE,
IDLE,
IDLE,
IDLE,
OP};
}
else
{ // if interrupts are disabled,
// a glitchy decrement to the program counter happens
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
OP_G};
}
}
else
{
cur_instr = new ushort[]
{IDLE,
IDLE,
IDLE,
HALT };
}
IDLE,
IDLE,
HALT };
}
}
private void JR_COND(bool cond)

View File

@ -31,6 +31,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public ushort cur_DMA_dest;
public int HDMA_length;
public int HDMA_countdown;
public int HBL_HDMA_count;
public int last_HBL;
public bool HBL_HDMA_go;
public bool HBL_test;
public override byte ReadReg(int addr)
{
@ -158,6 +162,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
// HDMA during HBlank only
HDMA_active = true;
HBL_HDMA_count = 0x10;
last_HBL = LY;
HBL_test = true;
HBL_HDMA_go = false;
}
else
{
@ -228,8 +236,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
else
{
// only transfer during mode 3, otherwise
if ((STAT & 3) == 3)
// only transfer during mode 3, and only 16 bytes at a time
if (((STAT & 3) == 3) && (LY != last_HBL) && HBL_test)
{
HBL_HDMA_go = true;
HBL_test = false;
}
if (HBL_HDMA_go && (HBL_HDMA_count > 0))
{
Core.HDMA_transfer = true;
@ -239,16 +253,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF);
cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF);
HDMA_length--;
HBL_HDMA_count--;
Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src);
cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF);
cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF);
HDMA_length--;
HBL_HDMA_count--;
if (HDMA_length == 0)
{
HDMA_active = false;
Core.HDMA_transfer = false;
}
if ((HBL_HDMA_count == 0) && (HDMA_length != 0))
{
HBL_test = true;
last_HBL = LY;
HBL_HDMA_count = 0x10;
}
}
}
else
@ -1294,6 +1317,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync("cur_DMA_dest", ref cur_DMA_dest);
ser.Sync("HDMA_length", ref HDMA_length);
ser.Sync("HDMA_countdown", ref HDMA_countdown);
ser.Sync("HBL_HDMA_count", ref HBL_HDMA_count);
ser.Sync("last_HBL", ref last_HBL);
ser.Sync("HBL_HDMA_go", ref HBL_HDMA_go);
ser.Sync("HBL_test", ref HBL_test);
base.SyncState(ser);
}