Skip to content

Commit

Permalink
Implement interrupt polling
Browse files Browse the repository at this point in the history
Enough to pass EI & DI tests in Blargg's interrupts ROM
  • Loading branch information
maxfierke committed Jan 7, 2024
1 parent 9c994f6 commit 86f9e12
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 13 deletions.
33 changes: 33 additions & 0 deletions cpu/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/bits"

"github.com/maxfierke/gogo-gb/cpu/isa"
"github.com/maxfierke/gogo-gb/devices"
"github.com/maxfierke/gogo-gb/mem"
)

Expand Down Expand Up @@ -1703,6 +1704,38 @@ func (cpu *CPU) Execute(mmu *mem.MMU, inst *isa.Instruction) (nextPC uint16, cyc
return cpu.PC.Read() + uint16(opcode.Bytes), uint8(opcode.Cycles[0]), nil
}

func (cpu *CPU) PollInterrupts(mmu *mem.MMU, ic *devices.InterruptController) uint8 {
if cpu.ime {
interrupt := ic.ConsumeRequest()
if interrupt == devices.INT_NONE {
return 0
}

// Disable interrupts while we process this one
cpu.ime = false

// Jump to interrupt handler
cpu.push(mmu, cpu.PC.Read())
cpu.PC.Write(uint16(interrupt))

// If we were halted, we're not now!
cpu.halted = false

// Consuming an IRQ is 16 cycles
return 16
} else if cpu.halted {
if interrupt := ic.NextRequest(); interrupt != 0 {
// Wakey-wakey
cpu.halted = false
}

return 0
} else {
// Ignore
return 0
}
}

func (cpu *CPU) Reset() {
cpu.Reg.Reset()
cpu.PC.Write(0x0000)
Expand Down
8 changes: 5 additions & 3 deletions debug/debugger.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Debugger interface {
Setup(cpu *cpu.CPU, mmu *mem.MMU)
OnDecode(cpu *cpu.CPU, mmu *mem.MMU)
OnExecute(cpu *cpu.CPU, mmu *mem.MMU)
OnInterrupt(cpu *cpu.CPU, mmu *mem.MMU)
OnRead(mmu *mem.MMU, addr uint16) mem.MemRead
OnWrite(mmu *mem.MMU, addr uint16, value byte) mem.MemWrite
}
Expand All @@ -32,9 +33,10 @@ func NewNullDebugger() *NullDebugger {
return &NullDebugger{}
}

func (nd *NullDebugger) Setup(cpu *cpu.CPU, mmu *mem.MMU) {}
func (nd *NullDebugger) OnDecode(cpu *cpu.CPU, mmu *mem.MMU) {}
func (nd *NullDebugger) OnExecute(cpu *cpu.CPU, mmu *mem.MMU) {}
func (nd *NullDebugger) Setup(cpu *cpu.CPU, mmu *mem.MMU) {}
func (nd *NullDebugger) OnDecode(cpu *cpu.CPU, mmu *mem.MMU) {}
func (nd *NullDebugger) OnExecute(cpu *cpu.CPU, mmu *mem.MMU) {}
func (nd *NullDebugger) OnInterrupt(cpu *cpu.CPU, mmu *mem.MMU) {}

func (nd *NullDebugger) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead {
return mem.ReadPassthrough()
Expand Down
2 changes: 2 additions & 0 deletions debug/gb_doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ func (gbd *GBDoctorDebugger) OnExecute(cpu *cpu.CPU, mmu *mem.MMU) {
gbd.printState(cpu, mmu)
}

func (gbd *GBDoctorDebugger) OnInterrupt(cpu *cpu.CPU, mmu *mem.MMU) {}

func (gbd *GBDoctorDebugger) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead {
if addr == devices.REG_LCD_LY {
return mem.ReadReplace(0x90) // gameboy-doctor needs a stubbed out LCD
Expand Down
60 changes: 51 additions & 9 deletions devices/interrupts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
const (
REG_IE = 0xFFFF
REG_IF = 0xFF0F
INT_NONE = 0x00
INT_VBLANK = 0x40
INT_STAT = 0x48
INT_TIMER = 0x50
Expand Down Expand Up @@ -92,22 +93,63 @@ func NewInterruptController() *InterruptController {
}
}

func (ic *InterruptController) Reset() {
ic.enabled.Write(0x00)
ic.requested.Write(0x00)
}
func (ic *InterruptController) ConsumeRequest() byte {
nextReq := ic.NextRequest()

func (ic *InterruptController) RequestSerial() {
ic.requested.serial = true
if nextReq == INT_VBLANK {
ic.requested.vblank = false
}

if nextReq == INT_STAT {
ic.requested.lcd = false
}

if nextReq == INT_JOYPAD {
ic.requested.joypad = false
}

if nextReq == INT_SERIAL {
ic.requested.serial = false
}

if nextReq == INT_TIMER {
ic.requested.timer = false
}

return nextReq
}

func (ic *InterruptController) ConsumeSerial() byte {
func (ic *InterruptController) NextRequest() byte {
if ic.enabled.vblank && ic.requested.vblank {
return INT_VBLANK
}

if ic.enabled.lcd && ic.requested.lcd {
return INT_STAT
}

if ic.enabled.timer && ic.requested.timer {
return INT_TIMER
}

if ic.enabled.serial && ic.requested.serial {
ic.requested.serial = false
return INT_SERIAL
}

return 0
if ic.enabled.joypad && ic.requested.joypad {
return INT_JOYPAD
}

return INT_NONE
}

func (ic *InterruptController) Reset() {
ic.enabled.Write(0x00)
ic.requested.Write(0x00)
}

func (ic *InterruptController) RequestSerial() {
ic.requested.serial = true
}

func (ic *InterruptController) OnRead(mmu *mem.MMU, addr uint16) mem.MemRead {
Expand Down
6 changes: 5 additions & 1 deletion hardware/dmg.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ func (dmg *DMG) DebugPrint() {
func (dmg *DMG) Step() bool {
dmg.debugger.OnDecode(dmg.cpu, dmg.mmu)

_, err := dmg.cpu.Step(dmg.mmu)
cycles, err := dmg.cpu.Step(dmg.mmu)
if err != nil {
dmg.logger.Printf("Unexpected error while executing instruction: %v\n", err)
return false
}

cycles += dmg.cpu.PollInterrupts(dmg.mmu, dmg.ic)

dmg.serial.Step(uint(cycles), dmg.ic)

return true
}

Expand Down

0 comments on commit 86f9e12

Please sign in to comment.