diff options
Diffstat (limited to 'nes')
| -rw-r--r-- | nes/bus.go | 4 | ||||
| -rw-r--r-- | nes/nes.go | 20 |
2 files changed, 23 insertions, 1 deletions
@@ -22,6 +22,8 @@ func (n *Console) Read(addr uint16) uint8 { switch { case addr < 0x2000: // internal RAM and mirrors return n.RAM[addr&0x7FF] + case addr < 0x4000: // PPU registers, mirrored every 8 bytes + return n.PPU.ReadRegister(addr) case addr >= 0x4020: // cartridge return n.Cart.Mapper.CPURead(addr) default: @@ -33,6 +35,8 @@ func (n *Console) Write(addr uint16, v uint8) { switch { case addr < 0x2000: n.RAM[addr&0x7FF] = v + case addr < 0x4000: + n.PPU.WriteRegister(addr, v) case addr >= 0x4020: n.Cart.Mapper.CPUWrite(addr, v) } @@ -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 } |