GBHawk: mode 1 overhaul, fixes several bugs

This commit is contained in:
alyosha-tas 2020-12-06 15:50:21 -05:00
parent e9f365a4a5
commit a46ffefc77
4 changed files with 161 additions and 95 deletions

View File

@ -147,7 +147,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80);
if (((STAT & 3) == 0) && STAT.Bit(3) && !glitch_state) { HBL_INT = true; } else { HBL_INT = false; } if (((STAT & 3) == 0) && STAT.Bit(3) && !glitch_state) { HBL_INT = true; } else { HBL_INT = false; }
if (((STAT & 3) == 1) && STAT.Bit(4)) { VBL_INT = true; } else { VBL_INT = false; } //if (((STAT & 3) == 1) && STAT.Bit(4)) { VBL_INT = true; } else if ((STAT & 3) == 1) { VBL_INT = false; }
// OAM not triggered? // OAM not triggered?
// if (((STAT & 3) == 2) && STAT.Bit(5)) { OAM_INT = true; } else { OAM_INT = false; } // if (((STAT & 3) == 2) && STAT.Bit(5)) { OAM_INT = true; } else { OAM_INT = false; }
@ -533,6 +533,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LY == 0 && LY_inc == 0) if (LY == 0 && LY_inc == 0)
{ {
LY_inc = 1; LY_inc = 1;
in_vbl = false;
Core.in_vblank = false; Core.in_vblank = false;
//STAT &= 0xFC; //STAT &= 0xFC;
@ -556,6 +557,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LY == 144) if (LY == 144)
{ {
in_vbl = true;
Core.in_vblank = true; Core.in_vblank = true;
} }
} }
@ -564,6 +566,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LCD_was_off) if (LCD_was_off)
{ {
//VBL_INT = false; //VBL_INT = false;
in_vbl = false;
Core.in_vblank = false; Core.in_vblank = false;
LCD_was_off = false; LCD_was_off = false;
@ -579,31 +582,51 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
cycle = 8; cycle = 8;
} }
// the VBL stat is continuously asserted // NOTE: timing differences between when ios is one (GBC mode) and off do not occur in the bios code so are not
if (LY >= 144) // represented here, see the GBC PPU for more details.
if (in_vbl)
{ {
if (STAT.Bit(4)) if (cycle == 4)
{ {
if ((cycle >= 5) && (LY == 144)) // Timing note: LYC interrupt cannot block mode 1 stat by itself
// But, the glitchy mode 2 stat check combined with LYC does block it. So adjust old stat line here
stat_line_old = VBL_INT | HBL_INT | OAM_INT;
if (LY == 144) { HBL_INT = false; }
}
// glitchy check of mode 2
if (LY == 144)
{
if (cycle <= 4)
{ {
VBL_INT = true; if (!STAT.Bit(5)) { VBL_INT = false; }
if ((cycle == 4) && !STAT.Bit(4)) { VBL_INT = false; }
} }
else if (LY > 144)
if ((cycle >= 2) && (cycle < 4))
{ {
VBL_INT = true; // there is an edge case where a VBL INT is triggered if STAT bit 5 is set
if (STAT.Bit(5)) { VBL_INT = true; }
} }
} }
if ((cycle == 2) && (LY == 144)) // mode 1 starts here, is asserted continuously
if (STAT.Bit(4))
{ {
// there is an edge case where a VBL INT is triggered if STAT bit 5 is set if (LY == 144)
if (STAT.Bit(5)) { VBL_INT = true; } {
if (cycle >= 4) { VBL_INT = true; }
}
else
{
VBL_INT = true;
}
} }
if ((cycle == 4) && (LY == 144)) if ((cycle == 4) && (LY == 144))
{ {
HBL_INT = false;
// set STAT mode to 1 (VBlank) and interrupt flag if it is enabled // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled
STAT &= 0xFC; STAT &= 0xFC;
STAT |= 0x01; STAT |= 0x01;
@ -612,11 +635,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Core.REG_FF0F |= 0x01; Core.REG_FF0F |= 0x01;
} }
if ((cycle == 4) && (LY == 144))
{
if (STAT.Bit(5)) { VBL_INT = false; }
}
if ((cycle == 8) && (LY == 153)) if ((cycle == 8) && (LY == 153))
{ {
LY = 0; LY = 0;
@ -624,8 +642,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Core.cpu.LY = LY; Core.cpu.LY = LY;
} }
} }
else
if (!Core.in_vblank)
{ {
if (no_scan) if (no_scan)
{ {
@ -689,25 +706,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{ {
if (cycle < 83) if (cycle < 83)
{ {
if (cycle == 2) if (cycle <= 4)
{ {
if (LY != 0) if ((cycle == 2) && (LY != 0))
{ {
HBL_INT = false; HBL_INT = false;
if (STAT.Bit(5)) { OAM_INT = true; } if (STAT.Bit(5)) { OAM_INT = true; }
} }
}
else if (cycle == 4)
{
// here mode 2 will be set to true and interrupts fired if enabled
STAT &= 0xFC;
STAT |= 0x2;
if (LY == 0) // the last few cycles of mode 1 still trigger mode 1 int
if (cycle < 4)
{ {
VBL_INT = false; if (STAT.Bit(4) && ((STAT & 3) == 1)) { VBL_INT = true; }
if (STAT.Bit(5)) { OAM_INT = true; } }
else
{
// here mode 2 will be set to true and interrupts fired if enabled
STAT &= 0xFC;
STAT |= 0x2;
if (LY == 0)
{
VBL_INT = false;
if (STAT.Bit(5)) { OAM_INT = true; }
}
} }
} }
@ -799,6 +822,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false;
in_vbl = true;
Core.in_vblank = true; Core.in_vblank = true;
LCD_was_off = true; LCD_was_off = true;
@ -1849,7 +1873,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
scroll_y = 0; scroll_y = 0;
scroll_x = 0; scroll_x = 0;
LY = 0; LY = 0;
LYC = 0; LYC = 0xFF;
LY_read = 0; LY_read = 0;
DMA_addr = 0; DMA_addr = 0;
BGP = 0xFF; BGP = 0xFF;
@ -1920,6 +1944,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
pal_change_blocked = false; pal_change_blocked = false;
glitch_state = false; glitch_state = false;
in_vbl = true;
} }
} }
} }

View File

@ -142,10 +142,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
break; break;
case 0xFF41: // STAT case 0xFF41: // STAT
// note that their is no stat interrupt bug in GBC // note that their is no stat interrupt bug in GBC
Console.WriteLine("stat " + " " + STAT + " " + value + " " + LY + " " + cycle + " " + Core.REG_FF0F);
STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80);
if (((STAT & 3) == 0) && STAT.Bit(3) && !glitch_state) { HBL_INT = true; } else { HBL_INT = false; } if (((STAT & 3) == 0) && STAT.Bit(3) && !glitch_state) { HBL_INT = true; } else { HBL_INT = false; }
if (((STAT & 3) == 1) && STAT.Bit(4)) { VBL_INT = true; } else { VBL_INT = false; } //if (((STAT & 3) == 1) && STAT.Bit(4)) { VBL_INT = true; } else if ((STAT & 3) == 1) { VBL_INT = false; }
// OAM not triggered? // OAM not triggered?
// if (((STAT & 3) == 2) && STAT.Bit(5)) { OAM_INT = true; } else { OAM_INT = false; } // if (((STAT & 3) == 2) && STAT.Bit(5)) { OAM_INT = true; } else { OAM_INT = false; }
@ -532,6 +533,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LY == 0 && LY_inc == 0) if (LY == 0 && LY_inc == 0)
{ {
LY_inc = 1; LY_inc = 1;
in_vbl = false;
Core.in_vblank = false; Core.in_vblank = false;
//STAT &= 0xFC; //STAT &= 0xFC;
@ -556,6 +558,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LY == 144) if (LY == 144)
{ {
in_vbl = true;
Core.in_vblank = true; Core.in_vblank = true;
} }
} }
@ -564,6 +567,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LCD_was_off) if (LCD_was_off)
{ {
//VBL_INT = false; //VBL_INT = false;
in_vbl = false;
Core.in_vblank = false; Core.in_vblank = false;
LCD_was_off = false; LCD_was_off = false;
@ -582,31 +586,49 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
//cycle = 8; //cycle = 8;
} }
// the VBL stat is continuously asserted if (in_vbl)
if (LY >= 144)
{ {
if (STAT.Bit(4)) if (cycle == 4)
{ {
if ((cycle >= (3 + 1 * LYC_offset)) && (LY == 144)) // Timing note: LYC interrupt cannot block mode 1 stat by itself
// But, the glitchy mode 2 stat check combined with LYC does block it. So adjust old stat line here
stat_line_old = VBL_INT | HBL_INT | OAM_INT;
if (LY == 144) { HBL_INT = false; }
}
// glitchy check of mode 2
if (LY == 144)
{
if (cycle <= 4)
{ {
VBL_INT = true; if (!STAT.Bit(5)) { VBL_INT = false; }
if ((cycle == 4) && !STAT.Bit(4)) { VBL_INT = false; }
} }
else if (LY > 144)
if ((cycle >= 2) && (cycle < 4))
{ {
VBL_INT = true; // there is an edge case where a VBL INT is triggered if STAT bit 5 is set
if (STAT.Bit(5)) { VBL_INT = true; }
} }
} }
if ((cycle == 2) && (LY == 144)) // mode 1 starts here, is asserted continuously
if (STAT.Bit(4))
{ {
// there is an edge case where a VBL INT is triggered if STAT bit 5 is set if (LY == 144)
if (STAT.Bit(5)) { VBL_INT = true; } {
if (cycle >= 4) { VBL_INT = true; }
}
else
{
VBL_INT = true;
}
} }
if ((cycle == 4) && (LY == 144)) if ((cycle == 4) && (LY == 144))
{ {
HBL_INT = false;
// set STAT mode to 1 (VBlank) and interrupt flag if it is enabled // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled
STAT &= 0xFC; STAT &= 0xFC;
STAT |= 0x01; STAT |= 0x01;
@ -615,11 +637,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Core.REG_FF0F |= 0x01; Core.REG_FF0F |= 0x01;
} }
if ((cycle == 4) && (LY == 144))
{
if (STAT.Bit(5)) { VBL_INT = false; }
}
if ((cycle == 8) && (LY == 153)) if ((cycle == 8) && (LY == 153))
{ {
LY = 0; LY = 0;
@ -627,8 +644,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Core.cpu.LY = LY; Core.cpu.LY = LY;
} }
} }
else
if (!Core.in_vblank)
{ {
if (no_scan) if (no_scan)
{ {
@ -693,25 +709,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{ {
if (cycle < 83) if (cycle < 83)
{ {
if (cycle == 2) if (cycle <= 4)
{ {
if (LY != 0) if ((cycle == 2) && (LY != 0))
{ {
HBL_INT = false; HBL_INT = false;
if (STAT.Bit(5)) { OAM_INT = true; } if (STAT.Bit(5)) { OAM_INT = true; }
} }
}
else if (cycle == 4)
{
// here mode 2 will be set to true and interrupts fired if enabled
STAT &= 0xFC;
STAT |= 0x2;
if (LY == 0) // the last few cycles of mode 1 still trigger mode 1 int
if (cycle < 4)
{ {
VBL_INT = false; if (STAT.Bit(4) && ((STAT & 3) == 1)) { VBL_INT = true; }
if (STAT.Bit(5)) { OAM_INT = true; } }
else
{
// here mode 2 will be set to true and interrupts fired if enabled
STAT &= 0xFC;
STAT |= 0x2;
if (LY == 0)
{
VBL_INT = false;
if (STAT.Bit(5)) { OAM_INT = true; }
}
} }
} }
@ -804,6 +826,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false;
in_vbl = true;
Core.in_vblank = true; Core.in_vblank = true;
LCD_was_off = true; LCD_was_off = true;
@ -1794,7 +1817,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
scroll_y = 0; scroll_y = 0;
scroll_x = 0; scroll_x = 0;
LY = 0; LY = 0;
LYC = 0; LYC = 0xFF;
LY_read = 0; LY_read = 0;
DMA_addr = 0; DMA_addr = 0;
BGP = 0xFF; BGP = 0xFF;
@ -1865,6 +1888,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
LYC_offset = 2; LYC_offset = 2;
glitch_state = false; glitch_state = false;
in_vbl = true;
LY_153_change = 10; LY_153_change = 10;
} }

View File

@ -162,6 +162,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LY == 0 && LY_inc == 0) if (LY == 0 && LY_inc == 0)
{ {
LY_inc = 1; LY_inc = 1;
in_vbl = false;
Core.in_vblank = false; Core.in_vblank = false;
STAT &= 0xFC; STAT &= 0xFC;
@ -183,6 +184,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LY == 144) if (LY == 144)
{ {
in_vbl = true;
Core.in_vblank = true; Core.in_vblank = true;
} }
} }
@ -191,6 +193,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (LCD_was_off) if (LCD_was_off)
{ {
//VBL_INT = false; //VBL_INT = false;
in_vbl = false;
Core.in_vblank = false; Core.in_vblank = false;
LCD_was_off = false; LCD_was_off = false;
@ -206,22 +209,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
cycle = 8; cycle = 8;
} }
// Timing note: LYC=143 does not by itself block mode1 stat int. But, the glitchy mode 2 stat check combined with
// LYC=143 does block it
// the VBL stat is continuously asserted // the VBL stat is continuously asserted
if (LY >= 144) if (in_vbl)
{ {
if ((cycle <= 4) && (LY == 144))
{
if (!STAT.Bit(5)) { VBL_INT = false; }
if ((cycle == 4) && !STAT.Bit(4)) { VBL_INT = false; }
}
if (STAT.Bit(4)) if (STAT.Bit(4))
{ {
if ((cycle >= 4) && (LY == 144)) if (LY == 144)
{ {
VBL_INT = true; if (cycle >= 4) { VBL_INT = true; }
} }
else if (LY > 144) else
{ {
VBL_INT = true; VBL_INT = true;
} }
} }
if ((cycle == 2) && (LY == 144)) if ((cycle >= 2) && (cycle < 4) && (LY == 144))
{ {
// there is an edge case where a VBL INT is triggered if STAT bit 5 is set // there is an edge case where a VBL INT is triggered if STAT bit 5 is set
if (STAT.Bit(5)) { VBL_INT = true; } if (STAT.Bit(5)) { VBL_INT = true; }
@ -239,11 +251,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Core.REG_FF0F |= 0x01; Core.REG_FF0F |= 0x01;
} }
if ((cycle == 4) && (LY == 144))
{
if (STAT.Bit(5)) { VBL_INT = false; }
}
if ((cycle == 6) && (LY == 153)) if ((cycle == 6) && (LY == 153))
{ {
LY = 0; LY = 0;
@ -251,8 +258,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
Core.cpu.LY = LY; Core.cpu.LY = LY;
} }
} }
else
if (!Core.in_vblank)
{ {
if (no_scan) if (no_scan)
{ {
@ -315,27 +321,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{ {
if (cycle < 83) if (cycle < 83)
{ {
if (cycle == 2) if (cycle <= 4)
{ {
if (LY != 0) if ((cycle == 2) && (LY != 0))
{ {
HBL_INT = false; HBL_INT = false;
if (STAT.Bit(5)) { OAM_INT = true; } if (STAT.Bit(5)) { OAM_INT = true; }
} }
}
else 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 // the last few cycles of mode 1 still trigger mode 1 int
STAT &= 0xFC; if (cycle < 4)
STAT |= 0x2;
if (LY == 0)
{ {
VBL_INT = false; if (STAT.Bit(4) && ((STAT & 3) == 1)) { VBL_INT = true; }
if (STAT.Bit(5)) { OAM_INT = true; } }
else
{
// 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 (LY == 0)
{
VBL_INT = false;
if (STAT.Bit(5)) { OAM_INT = true; }
}
} }
} }
@ -415,6 +428,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false;
in_vbl = true;
Core.in_vblank = true; Core.in_vblank = true;
LCD_was_off = true; LCD_was_off = true;
@ -1242,7 +1256,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
scroll_y = 0; scroll_y = 0;
scroll_x = 0; scroll_x = 0;
LY = 0; LY = 0;
LYC = 0; LYC = 0xFF;
DMA_addr = 0xFF; DMA_addr = 0xFF;
BGP = 0xFF; BGP = 0xFF;
obj_pal_0 = 0xFF; obj_pal_0 = 0xFF;
@ -1278,6 +1292,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
window_y_tile_inc = 0; window_y_tile_inc = 0;
glitch_state = false; glitch_state = false;
in_vbl = true;
} }
} }
} }

View File

@ -118,6 +118,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public int LYC_offset; // in double speed mode it appears timing changes for LYC int public int LYC_offset; // in double speed mode it appears timing changes for LYC int
public bool glitch_state; // writing to STAT to enable HBL interrupt won't trigger it if the ppu just turned on public bool glitch_state; // writing to STAT to enable HBL interrupt won't trigger it if the ppu just turned on
public int LY_153_change; // the timing of LYC chaning to 153 looks like it varies with speed mode public int LY_153_change; // the timing of LYC chaning to 153 looks like it varies with speed mode
public bool in_vbl; // writes to turn on mode 2 stat interrupts can trigger vbl stat at the start of vbl
// variables not in state // variables not in state
public int total_counter; public int total_counter;
@ -286,6 +287,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync(nameof(LYC_offset), ref LYC_offset); ser.Sync(nameof(LYC_offset), ref LYC_offset);
ser.Sync(nameof(glitch_state), ref glitch_state); ser.Sync(nameof(glitch_state), ref glitch_state);
ser.Sync(nameof(LY_153_change), ref LY_153_change); ser.Sync(nameof(LY_153_change), ref LY_153_change);
ser.Sync(nameof(in_vbl), ref in_vbl);
} }
} }
} }