aboutsummaryrefslogtreecommitdiff
path: root/ppu/registers.go
diff options
context:
space:
mode:
Diffstat (limited to 'ppu/registers.go')
-rw-r--r--ppu/registers.go141
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
+}