diff options
| author | crecher <crecher@crecher.net> | 2026-10-12 16:30:32 +0200 |
|---|---|---|
| committer | crecher <crecher@crecher.net> | 2026-10-12 16:30:32 +0200 |
| commit | 0be10a289132f7ed9b42d5949db844227075e7ce (patch) | |
| tree | 305a468b3c8e1fecb86c22bbe920c8140c46c3a0 /ppu/registers.go | |
| parent | f5969959b81bce1841974cedcac0289d44f7c69e (diff) | |
| download | gones-0be10a289132f7ed9b42d5949db844227075e7ce.tar.gz gones-0be10a289132f7ed9b42d5949db844227075e7ce.zip | |
ITS ALIVE (not rly no image yet)
Diffstat (limited to 'ppu/registers.go')
| -rw-r--r-- | ppu/registers.go | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/ppu/registers.go b/ppu/registers.go new file mode 100644 index 0000000..5445619 --- /dev/null +++ b/ppu/registers.go @@ -0,0 +1,141 @@ +package ppu + +func (p *PPU) ReadRegister(addr uint16) uint8 { + switch addr & 0x7 { + case 2: + return p.readStatus() + case 4: + return p.OAM[p.OAMAddr] + case 7: + return p.readData() + default: + return p.busLatch // $2000,2001,2003,2005,2006 are write only: open bus + } +} + +// handles cpu write to $2000-2007 +func (p *PPU) WriteRegister(addr uint16, v uint8) { + p.busLatch = v + switch addr & 0x7 { + case 0: + p.Ctrl = v + p.t = (p.t &^ 0x0C00) | (uint16(v&ctrlNametableMask) << 10) + p.nmiOutput = v&ctrlNMIEnable != 0 + p.updateNMILine() + case 1: + p.Mask = v + case 3: + p.OAMAddr = v + case 4: + p.OAM[p.OAMAddr] = v + p.OAMAddr++ + case 5: + p.writeScroll(v) + case 6: + p.writeAddr(v) + case 7: + p.writeData(v) + } +} + +// implements the $2002 read, including the hardware race around the dot +// vblank gets set (see https://www.nesdev.org/wiki/NMI) +func (p *PPU) readStatus() uint8 { + result := p.busLatch & 0x1F + if p.vblank { + result |= statusVBlank + } + if p.sprite0Hit { + result |= statusSprite0 + } + if p.spriteOverflow { + result |= statusOverflow + } + + if p.Scanline == 241 { + switch p.Dot { + case 0: + p.suppressVBlankSet = true + p.suppressNMIForFrame = true + case 1, 2: + p.suppressNMIForFrame = true + p.nmiPending = false + } + } + + p.vblank = false + p.w = false + p.updateNMILine() + return result +} + +func (p *PPU) writeScroll(v uint8) { + if !p.w { + p.t = (p.t &^ 0x001F) | uint16(v>>3) + p.x = v & 0x07 + } else { + p.t = (p.t &^ 0x73E0) | (uint16(v&0x07) << 12) | (uint16(v&0xF8) << 2) + } + p.w = !p.w +} + +func (p *PPU) writeAddr(v uint8) { + if !p.w { + p.t = (p.t & 0x00FF) | (uint16(v&0x3F) << 8) + } else { + p.t = (p.t & 0xFF00) | uint16(v) + p.v = p.t + } + p.w = !p.w +} + +// implements buffered $2007 and a quirk which i am too lazy to write down +// right now :D +// https://www.nesdev.org/wiki/PPU_registers#The_PPUDATA_data_port +func (p *PPU) readData() uint8 { + addr := p.v & 0x3FFF + var result uint8 + if addr >= 0x3F00 { + result = p.readPalette(addr) + p.readBuffer = p.readVRAM(addr & 0x2FFF) + } else { + result = p.readBuffer + p.readBuffer = p.readVRAM(addr) + } + p.incrementV() + return result +} + +func (p *PPU) writeData(v uint8) { + addr := p.v & 0x3FFF + if addr >= 0x3F00 { + p.writePalette(addr, v) + } else { + p.writeVRAM(addr, v) + } + p.incrementV() +} + +func (p *PPU) incrementV() { + if p.Ctrl&ctrlIncrement32 != 0 { + p.v += 32 + } else { + p.v++ + } + p.v &= 0x7FFF +} + +func (p *PPU) readVRAM(addr uint16) uint8 { + if addr < 0x2000 { + return p.mapper.PPURead(addr) + } + return p.vram[p.nametableOffset(addr)] +} + +func (p *PPU) writeVRAM(addr uint16, v uint8) { + if addr < 0x2000 { + p.mapper.PPUWrite(addr, v) + return + } + p.vram[p.nametableOffset(addr)] = v +} |