Skip to content

Commit

Permalink
GBC: Hopefully improved interrupt accuracy in regards to cpu HALT
Browse files Browse the repository at this point in the history
This fixes Link's Awakening, more testing needed to see if it breaks others
  • Loading branch information
ducalex committed Dec 31, 2020
1 parent 77f8eb1 commit dd2aed8
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 43 deletions.
14 changes: 9 additions & 5 deletions gnuboy-go/components/gnuboy/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ static inline void timer_advance(int cycles)
cpu.timer &= 0x1ff;
if (tima >= 256)
{
hw_interrupt(IF_TIMER, IF_TIMER);
hw_interrupt(0, IF_TIMER);
hw_interrupt(IF_TIMER, 1);
hw_interrupt(IF_TIMER, 0);
tima = R_TMA;
}
R_TIMA = tima;
Expand All @@ -330,8 +330,8 @@ static inline void serial_advance(int cycles)
R_SB = 0xFF;
R_SC &= 0x7f;
hw.serial = 0;
hw_interrupt(IF_SERIAL, IF_SERIAL);
hw_interrupt(0, IF_SERIAL);
hw_interrupt(IF_SERIAL, 1);
hw_interrupt(IF_SERIAL, 0);
}
}
}
Expand Down Expand Up @@ -836,7 +836,11 @@ int cpu_emulate(int cycles)
break;

case 0x76: /* HALT */
cpu.halted = 1;
if (IME) {
cpu.halted = 1;
} else {
printf("FIX ME: HALT requested with IME = 0\n");
}
break;

case 0xCB: /* CB prefix */
Expand Down
60 changes: 35 additions & 25 deletions gnuboy-go/components/gnuboy/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,28 @@ hw_t hw;


/*
* hw_interrupt changes the virtual interrupt lines included in the
* specified mask to the values the corresponding bits in i take, and
* in doing so, raises the appropriate bit of R_IF for any interrupt
* lines that transition from low to high.
* hw_interrupt changes the virtual interrupt line(s) defined by i
* The interrupt fires (added to R_IF) when the line transitions from 0 to 1.
* It does not refire if the line was already high.
*/
void hw_interrupt(byte i, byte mask)
void hw_interrupt(byte i, int level)
{
i &= mask;
R_IF |= i & (hw.ilines ^ i);
if (i) {
// HALT shouldn't even be entered when interrupts are disabled.
// No need to check for IME, and it works around a stall bug.
cpu.halted = 0;
if (level == 0)
{
hw.ilines &= ~i;
}
else if ((hw.ilines & i) == 0)
{
hw.ilines |= i;
R_IF |= i; // Fire!

if ((R_IE & i) != 0)
{
// Wake up the CPU when an enabled interrupt occurs
// IME doesn't matter at this point, only IE
cpu.halted = 0;
}
}
hw.ilines &= ~mask;
hw.ilines |= i;
}


Expand Down Expand Up @@ -104,26 +110,30 @@ void IRAM_ATTR pad_refresh()
R_P1 ^= 0x0F;
if (oldp1 & ~R_P1 & 0x0F)
{
hw_interrupt(IF_PAD, IF_PAD);
hw_interrupt(0, IF_PAD);
hw_interrupt(IF_PAD, 1);
hw_interrupt(IF_PAD, 0);
}
}


/*
* These simple functions just update the state of a button on the
* pad.
* pad_set updates the state of one or more buttons on the pad and calls
* pad_refresh() to fire an interrupt if the pad changed.
*/
void IRAM_ATTR pad_set(byte k, int st)
void IRAM_ATTR pad_set(byte btn, int set)
{
if (st) {
if (hw.pad & k) return;
hw.pad |= k;
} else {
if (!(hw.pad & k)) return;
hw.pad &= ~k;
int new_pad = hw.pad;

if (set)
new_pad |= btn;
else
new_pad &= ~btn;

if (hw.pad != new_pad)
{
hw.pad = new_pad;
pad_refresh();
}
pad_refresh();
}

void hw_reset()
Expand Down
4 changes: 2 additions & 2 deletions gnuboy-go/components/gnuboy/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ void hw_hdma();
void hw_dma(byte b);
void hw_hdma_cmd(byte c);
void hw_reset();
void pad_set(byte k, int st);
void pad_set(byte btn, int set);
void pad_refresh();
void hw_interrupt(byte i, byte mask);
void hw_interrupt(byte i, int level);

#endif
19 changes: 9 additions & 10 deletions gnuboy-go/components/gnuboy/lcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,20 +707,20 @@ void pal_dirty()
void stat_trigger()
{
const byte condbits[4] = { 0x08, 0x10, 0x20, 0x00 };
byte flag = 0;
int level = 0;

if (R_LY == R_LYC)
{
R_STAT |= 0x04;
if (R_STAT & 0x40) flag = IF_STAT;
if (R_STAT & 0x40) level = 1;
}
else R_STAT &= ~0x04;

if (R_STAT & condbits[R_STAT&3]) flag = IF_STAT;
if (R_STAT & condbits[R_STAT&3]) level = 1;

if (!(R_LCDC & 0x80)) flag = 0;
if (!(R_LCDC & 0x80)) level = 0;

hw_interrupt(flag, IF_STAT);
hw_interrupt(IF_STAT, level);
}


Expand All @@ -736,8 +736,7 @@ static void inline stat_change(int stat)
stat &= 3;
R_STAT = (R_STAT & 0x7C) | stat;

if (stat != 1) hw_interrupt(0, IF_VBLANK);
/* hw_interrupt((stat == 1) ? IF_VBLANK : 0, IF_VBLANK); */
if (stat != 1) hw_interrupt(IF_VBLANK, 0);
stat_trigger();
}

Expand Down Expand Up @@ -834,7 +833,7 @@ void lcd_emulate()
before interrupt is triggered */
if (cpu.halted)
{
hw_interrupt(IF_VBLANK, IF_VBLANK);
hw_interrupt(IF_VBLANK, 1);
CYCLES += 228;
}
else CYCLES += 10;
Expand All @@ -844,7 +843,7 @@ void lcd_emulate()

// Hack for Worms Armageddon
if (R_STAT == 0x48)
hw_interrupt(0, IF_STAT);
hw_interrupt(IF_STAT, 0);

stat_change(2); /* -> search */
CYCLES += 40;
Expand All @@ -853,7 +852,7 @@ void lcd_emulate()
/* vblank -> */
if (!(hw.ilines & IF_VBLANK))
{
hw_interrupt(IF_VBLANK, IF_VBLANK);
hw_interrupt(IF_VBLANK, 1);
CYCLES += 218;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion gnuboy-go/components/gnuboy/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ static inline void ioreg_write(byte r, byte b)
case RI_STAT:
R_STAT = (R_STAT & 0x07) | (b & 0x78);
if (!hw.cgb && !(R_STAT & 2)) /* DMG STAT write bug => interrupt */
hw_interrupt(IF_STAT, IF_STAT);
hw_interrupt(IF_STAT, 1);
stat_trigger();
break;
case RI_LYC:
Expand Down

0 comments on commit dd2aed8

Please sign in to comment.