Skip to content

Commit dd2aed8

Browse files
committed
GBC: Hopefully improved interrupt accuracy in regards to cpu HALT
This fixes Link's Awakening, more testing needed to see if it breaks others
1 parent 77f8eb1 commit dd2aed8

File tree

5 files changed

+56
-43
lines changed

5 files changed

+56
-43
lines changed

gnuboy-go/components/gnuboy/cpu.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ static inline void timer_advance(int cycles)
310310
cpu.timer &= 0x1ff;
311311
if (tima >= 256)
312312
{
313-
hw_interrupt(IF_TIMER, IF_TIMER);
314-
hw_interrupt(0, IF_TIMER);
313+
hw_interrupt(IF_TIMER, 1);
314+
hw_interrupt(IF_TIMER, 0);
315315
tima = R_TMA;
316316
}
317317
R_TIMA = tima;
@@ -330,8 +330,8 @@ static inline void serial_advance(int cycles)
330330
R_SB = 0xFF;
331331
R_SC &= 0x7f;
332332
hw.serial = 0;
333-
hw_interrupt(IF_SERIAL, IF_SERIAL);
334-
hw_interrupt(0, IF_SERIAL);
333+
hw_interrupt(IF_SERIAL, 1);
334+
hw_interrupt(IF_SERIAL, 0);
335335
}
336336
}
337337
}
@@ -836,7 +836,11 @@ int cpu_emulate(int cycles)
836836
break;
837837

838838
case 0x76: /* HALT */
839-
cpu.halted = 1;
839+
if (IME) {
840+
cpu.halted = 1;
841+
} else {
842+
printf("FIX ME: HALT requested with IME = 0\n");
843+
}
840844
break;
841845

842846
case 0xCB: /* CB prefix */

gnuboy-go/components/gnuboy/hw.c

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,28 @@ hw_t hw;
1212

1313

1414
/*
15-
* hw_interrupt changes the virtual interrupt lines included in the
16-
* specified mask to the values the corresponding bits in i take, and
17-
* in doing so, raises the appropriate bit of R_IF for any interrupt
18-
* lines that transition from low to high.
15+
* hw_interrupt changes the virtual interrupt line(s) defined by i
16+
* The interrupt fires (added to R_IF) when the line transitions from 0 to 1.
17+
* It does not refire if the line was already high.
1918
*/
20-
void hw_interrupt(byte i, byte mask)
19+
void hw_interrupt(byte i, int level)
2120
{
22-
i &= mask;
23-
R_IF |= i & (hw.ilines ^ i);
24-
if (i) {
25-
// HALT shouldn't even be entered when interrupts are disabled.
26-
// No need to check for IME, and it works around a stall bug.
27-
cpu.halted = 0;
21+
if (level == 0)
22+
{
23+
hw.ilines &= ~i;
24+
}
25+
else if ((hw.ilines & i) == 0)
26+
{
27+
hw.ilines |= i;
28+
R_IF |= i; // Fire!
29+
30+
if ((R_IE & i) != 0)
31+
{
32+
// Wake up the CPU when an enabled interrupt occurs
33+
// IME doesn't matter at this point, only IE
34+
cpu.halted = 0;
35+
}
2836
}
29-
hw.ilines &= ~mask;
30-
hw.ilines |= i;
3137
}
3238

3339

@@ -104,26 +110,30 @@ void IRAM_ATTR pad_refresh()
104110
R_P1 ^= 0x0F;
105111
if (oldp1 & ~R_P1 & 0x0F)
106112
{
107-
hw_interrupt(IF_PAD, IF_PAD);
108-
hw_interrupt(0, IF_PAD);
113+
hw_interrupt(IF_PAD, 1);
114+
hw_interrupt(IF_PAD, 0);
109115
}
110116
}
111117

112118

113119
/*
114-
* These simple functions just update the state of a button on the
115-
* pad.
120+
* pad_set updates the state of one or more buttons on the pad and calls
121+
* pad_refresh() to fire an interrupt if the pad changed.
116122
*/
117-
void IRAM_ATTR pad_set(byte k, int st)
123+
void IRAM_ATTR pad_set(byte btn, int set)
118124
{
119-
if (st) {
120-
if (hw.pad & k) return;
121-
hw.pad |= k;
122-
} else {
123-
if (!(hw.pad & k)) return;
124-
hw.pad &= ~k;
125+
int new_pad = hw.pad;
126+
127+
if (set)
128+
new_pad |= btn;
129+
else
130+
new_pad &= ~btn;
131+
132+
if (hw.pad != new_pad)
133+
{
134+
hw.pad = new_pad;
135+
pad_refresh();
125136
}
126-
pad_refresh();
127137
}
128138

129139
void hw_reset()

gnuboy-go/components/gnuboy/hw.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ void hw_hdma();
3737
void hw_dma(byte b);
3838
void hw_hdma_cmd(byte c);
3939
void hw_reset();
40-
void pad_set(byte k, int st);
40+
void pad_set(byte btn, int set);
4141
void pad_refresh();
42-
void hw_interrupt(byte i, byte mask);
42+
void hw_interrupt(byte i, int level);
4343

4444
#endif

gnuboy-go/components/gnuboy/lcd.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -707,20 +707,20 @@ void pal_dirty()
707707
void stat_trigger()
708708
{
709709
const byte condbits[4] = { 0x08, 0x10, 0x20, 0x00 };
710-
byte flag = 0;
710+
int level = 0;
711711

712712
if (R_LY == R_LYC)
713713
{
714714
R_STAT |= 0x04;
715-
if (R_STAT & 0x40) flag = IF_STAT;
715+
if (R_STAT & 0x40) level = 1;
716716
}
717717
else R_STAT &= ~0x04;
718718

719-
if (R_STAT & condbits[R_STAT&3]) flag = IF_STAT;
719+
if (R_STAT & condbits[R_STAT&3]) level = 1;
720720

721-
if (!(R_LCDC & 0x80)) flag = 0;
721+
if (!(R_LCDC & 0x80)) level = 0;
722722

723-
hw_interrupt(flag, IF_STAT);
723+
hw_interrupt(IF_STAT, level);
724724
}
725725

726726

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

739-
if (stat != 1) hw_interrupt(0, IF_VBLANK);
740-
/* hw_interrupt((stat == 1) ? IF_VBLANK : 0, IF_VBLANK); */
739+
if (stat != 1) hw_interrupt(IF_VBLANK, 0);
741740
stat_trigger();
742741
}
743742

@@ -834,7 +833,7 @@ void lcd_emulate()
834833
before interrupt is triggered */
835834
if (cpu.halted)
836835
{
837-
hw_interrupt(IF_VBLANK, IF_VBLANK);
836+
hw_interrupt(IF_VBLANK, 1);
838837
CYCLES += 228;
839838
}
840839
else CYCLES += 10;
@@ -844,7 +843,7 @@ void lcd_emulate()
844843

845844
// Hack for Worms Armageddon
846845
if (R_STAT == 0x48)
847-
hw_interrupt(0, IF_STAT);
846+
hw_interrupt(IF_STAT, 0);
848847

849848
stat_change(2); /* -> search */
850849
CYCLES += 40;
@@ -853,7 +852,7 @@ void lcd_emulate()
853852
/* vblank -> */
854853
if (!(hw.ilines & IF_VBLANK))
855854
{
856-
hw_interrupt(IF_VBLANK, IF_VBLANK);
855+
hw_interrupt(IF_VBLANK, 1);
857856
CYCLES += 218;
858857
break;
859858
}

gnuboy-go/components/gnuboy/mem.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ static inline void ioreg_write(byte r, byte b)
158158
case RI_STAT:
159159
R_STAT = (R_STAT & 0x07) | (b & 0x78);
160160
if (!hw.cgb && !(R_STAT & 2)) /* DMG STAT write bug => interrupt */
161-
hw_interrupt(IF_STAT, IF_STAT);
161+
hw_interrupt(IF_STAT, 1);
162162
stat_trigger();
163163
break;
164164
case RI_LYC:

0 commit comments

Comments
 (0)