aboutsummaryrefslogtreecommitdiff
path: root/nes/nes.go
diff options
context:
space:
mode:
Diffstat (limited to 'nes/nes.go')
-rw-r--r--nes/nes.go20
1 files changed, 19 insertions, 1 deletions
diff --git a/nes/nes.go b/nes/nes.go
index 7f36b1a..e3845f6 100644
--- a/nes/nes.go
+++ b/nes/nes.go
@@ -3,26 +3,44 @@ package nes
import (
"crecher.net/gones/v2/cart"
"crecher.net/gones/v2/cpu"
+ "crecher.net/gones/v2/ppu"
)
type Console struct {
CPU *cpu.CPU
+ PPU *ppu.PPU
Cart *cart.Cartridge
RAM [0x800]uint8
- // PPU, APU, controllers once they exist
+ // APU, controllers once they exist
cycles uint64
}
func New(c *cart.Cartridge) *Console {
n := &Console{Cart: c}
+ n.PPU = ppu.New(c.Mapper, c.Mirroring)
n.CPU = cpu.NewCPU(n)
n.CPU.Reset()
return n
}
+// Step runs one CPU instruction and the PPU dots (3 per CPU cycle) that
+// elapse alongside it, firing NMI on a PPUCTRL/vblank edge. PPU state seen
+// by bus accesses mid-instruction still lags behind by up to one
+// instruction's worth of dots, since the CPU isn't cycle-stepped yet (see
+// README: "CPU stall hook and proper IRQ/NMI line (polled between
+// instructions)") - timing-sensitive register logic (the $2002 race window,
+// NMI edge detection) is implemented to the real dot granularity and is
+// exercised directly in the ppu package's tests, but end-to-end precision
+// against real games/test ROMs needs that CPU work to land too.
func (n *Console) Step() int {
cycles := n.CPU.Step()
n.cycles += uint64(cycles)
+ for i := 0; i < cycles*3; i++ {
+ n.PPU.Tick()
+ if n.PPU.PollNMI() {
+ n.CPU.NMI()
+ }
+ }
return cycles
}