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 }