aboutsummaryrefslogtreecommitdiff
path: root/tests/controller_test.go
diff options
context:
space:
mode:
authorcrecher <crecher@crecher.net>2026-10-13 01:13:13 +0200
committercrecher <crecher@crecher.net>2026-10-13 01:13:13 +0200
commit99cae5a5a37f41f878a84857361eda47910303ba (patch)
tree31c8665ca991f95e09e93bb2e6ec91d81d2427ff /tests/controller_test.go
parent96ff6d8eda2ba434682909f4ff4ce350d78b2273 (diff)
downloadgones-99cae5a5a37f41f878a84857361eda47910303ba.tar.gz
gones-99cae5a5a37f41f878a84857361eda47910303ba.zip
sprites!!!!!!
Diffstat (limited to 'tests/controller_test.go')
-rw-r--r--tests/controller_test.go119
1 files changed, 119 insertions, 0 deletions
diff --git a/tests/controller_test.go b/tests/controller_test.go
new file mode 100644
index 0000000..026e68a
--- /dev/null
+++ b/tests/controller_test.go
@@ -0,0 +1,119 @@
+package tests
+
+import (
+ "testing"
+
+ "crecher.net/gones/v2/cart"
+ "crecher.net/gones/v2/nes"
+)
+
+func newTestConsole(t *testing.T) *nes.Console {
+ t.Helper()
+ c, err := cart.Load("testdata/nestest.nes")
+ if err != nil {
+ t.Fatal(err)
+ }
+ return nes.New(c)
+}
+
+func strobe(n *nes.Console) {
+ n.Write(0x4016, 1)
+ n.Write(0x4016, 0)
+}
+
+func readBits(n *nes.Console, addr uint16, count int) []uint8 {
+ bits := make([]uint8, count)
+ for i := range bits {
+ bits[i] = n.Read(addr) & 1
+ }
+ return bits
+}
+
+func equalBits(a, b []uint8) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+func TestControllerReadsButtonsInOrder(t *testing.T) {
+ n := newTestConsole(t)
+ n.SetButtons(0, nes.ButtonA|nes.ButtonStart|nes.ButtonRight)
+ strobe(n)
+
+ // +---------------------- A
+ // | +------------------- B
+ // | | +---------------- Select
+ // | | | +------------- Start
+ // | | | | +---------- Up
+ // | | | | | +------- Down
+ // | | | | | | +---- Left
+ // | | | | | | | +- Right
+ // | | | | | | | |
+ want := []uint8{1, 0, 0, 1, 0, 0, 0, 1}
+ if got := readBits(n, 0x4016, 8); !equalBits(got, want) {
+ t.Errorf("bits = %v, want %v", got, want)
+ }
+}
+
+func TestControllerReturnsOneAfterEightReads(t *testing.T) {
+ n := newTestConsole(t)
+ strobe(n)
+ readBits(n, 0x4016, 8)
+ if got := readBits(n, 0x4016, 3); !equalBits(got, []uint8{1, 1, 1}) {
+ t.Errorf("bits after 8 reads = %v, want [1 1 1]", got)
+ }
+}
+
+func TestControllerStrobeHighKeepsReturningA(t *testing.T) {
+ n := newTestConsole(t)
+ n.SetButtons(0, nes.ButtonA)
+ n.Write(0x4016, 1)
+ if got := readBits(n, 0x4016, 3); !equalBits(got, []uint8{1, 1, 1}) {
+ t.Errorf("bits = %v, want [1 1 1]", got)
+ }
+
+ n.SetButtons(0, nes.ButtonB)
+ if got := readBits(n, 0x4016, 2); !equalBits(got, []uint8{0, 0}) {
+ t.Errorf("bits after releasing A = %v, want [0 0]", got)
+ }
+}
+
+func TestControllerLatchesOnStrobe(t *testing.T) {
+ n := newTestConsole(t)
+ n.SetButtons(0, nes.ButtonA)
+ strobe(n)
+ n.SetButtons(0, 0) // released after the latch, the readout should not change
+
+ want := []uint8{1, 0, 0, 0, 0, 0, 0, 0}
+ if got := readBits(n, 0x4016, 8); !equalBits(got, want) {
+ t.Errorf("bits = %v, want %v", got, want)
+ }
+}
+
+func TestControllerPortsAreIndependentWithSharedStrobe(t *testing.T) {
+ n := newTestConsole(t)
+ n.SetButtons(0, nes.ButtonA)
+ n.SetButtons(1, nes.ButtonSelect)
+ strobe(n)
+
+ if got := readBits(n, 0x4016, 3); !equalBits(got, []uint8{1, 0, 0}) {
+ t.Errorf("port 1 bits = %v, want [1 0 0]", got)
+ }
+ if got := readBits(n, 0x4017, 3); !equalBits(got, []uint8{0, 0, 1}) {
+ t.Errorf("port 2 bits = %v, want [0 0 1]", got)
+ }
+}
+
+func TestControllerReadHasOpenBusBit6(t *testing.T) {
+ n := newTestConsole(t)
+ strobe(n)
+ if got := n.Read(0x4016); got != 0x40 {
+ t.Errorf("$4016 with nothing pressed = %#02x, want 0x40", got)
+ }
+}