diff options
Diffstat (limited to 'ppu/ppu_test.go')
| -rw-r--r-- | ppu/ppu_test.go | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/ppu/ppu_test.go b/ppu/ppu_test.go new file mode 100644 index 0000000..9426031 --- /dev/null +++ b/ppu/ppu_test.go @@ -0,0 +1,108 @@ +package ppu + +// more tests for the scroll registers since they can't be checked outside +// the package without a $2006 write that would mess up the bits under test + +import ( + "testing" + + "crecher.net/gones/v2/cart" +) + +// nilMapper is enough for these tests +type nilMapper struct{} + +func (nilMapper) CPURead(addr uint16) uint8 { return 0 } +func (nilMapper) CPUWrite(addr uint16, v uint8) {} +func (nilMapper) PPURead(addr uint16) uint8 { return 0 } +func (nilMapper) PPUWrite(addr uint16, v uint8) {} +func (nilMapper) Mirroring() cart.Mirroring { return cart.Horizontal } + +func newPPU() *PPU { return New(nilMapper{}, cart.Horizontal) } + +func TestCtrlNametableBitsSetT(t *testing.T) { + p := newPPU() + p.WriteRegister(0x2000, 0x03) // nametable select = 3 -> t bits 10-11 + if got := p.t & 0x0C00; got != 0x0C00 { + t.Errorf("t&0x0C00 = %#04x, want 0x0C00", got) + } + + p.WriteRegister(0x2000, 0x00) + if got := p.t & 0x0C00; got != 0 { + t.Errorf("t&0x0C00 = %#04x, want 0 after clearing nametable select", got) + } +} + +func TestScrollFirstWriteSetsCoarseXAndFineX(t *testing.T) { + p := newPPU() + p.WriteRegister(0x2005, 0b11101_011) // coarseX=0b11101=29, fineX=0b011=3 + + if got := p.t & 0x001F; got != 29 { + t.Errorf("coarse X in t = %d, want 29", got) + } + if p.x != 3 { + t.Errorf("fine X = %d, want 3", p.x) + } + if !p.w { + t.Error("w should be set after the first $2005 write") + } +} + +func TestScrollSecondWriteSetsCoarseYAndFineY(t *testing.T) { + p := newPPU() + p.WriteRegister(0x2005, 0x00) // first write, establishes w=true + p.WriteRegister(0x2005, 0b10110_101) // coarseY=0b10110=22, fineY=0b101=5 + + if got := (p.t >> 5) & 0x1F; got != 22 { + t.Errorf("coarse Y in t = %d, want 22", got) + } + if got := (p.t >> 12) & 0x7; got != 5 { + t.Errorf("fine Y in t = %d, want 5", got) + } + if p.w { + t.Error("w should be cleared after the second $2005 write") + } +} + +func TestScrollWriteDoesNotTouchNametableBits(t *testing.T) { + p := newPPU() + p.WriteRegister(0x2000, 0x03) // select nametable 3 + p.WriteRegister(0x2005, 0xFF) + p.WriteRegister(0x2005, 0xFF) + + if got := p.t & 0x0C00; got != 0x0C00 { + t.Errorf("PPUSCROLL writes clobbered the nametable-select bits: t&0x0C00 = %#04x, want 0x0C00", got) + } +} + +func TestAddrWriteSequenceSetsTThenV(t *testing.T) { + p := newPPU() + p.v = 0x1234 // poison v so we can tell the first write didn't touch it + + p.WriteRegister(0x2006, 0x3D) // high byte: only the low 6 bits are kept, bit 14 cleared + if got := p.t & 0xFF00; got != 0x3D00 { + t.Errorf("t high byte = %#04x, want 0x3D00", got) + } + if p.v != 0x1234 { + t.Errorf("v changed after the first $2006 write: %#04x", p.v) + } + + p.WriteRegister(0x2006, 0x56) + if p.t != 0x3D56 { + t.Errorf("t = %#04x, want 0x3D56", p.t) + } + if p.v != 0x3D56 { + t.Errorf("v = %#04x, want 0x3D56 (should be copied from t on the second write)", p.v) + } +} + +func TestAddrHighByteClearsBit14(t *testing.T) { + p := newPPU() + p.WriteRegister(0x2006, 0xFF) // top 2 bits of the written byte are discarded + if p.t&0x4000 != 0 { + t.Errorf("t bit 14 should always be cleared by a $2006 high-byte write, t = %#04x", p.t) + } + if got := p.t & 0xFF00; got != 0x3F00 { + t.Errorf("t high byte = %#04x, want 0x3F00 (0xFF masked to 6 bits)", got) + } +} |