diff options
Diffstat (limited to 'tests/nestest_test.go')
| -rw-r--r-- | tests/nestest_test.go | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/tests/nestest_test.go b/tests/nestest_test.go new file mode 100644 index 0000000..de98156 --- /dev/null +++ b/tests/nestest_test.go @@ -0,0 +1,111 @@ +package tests + +import ( + "bufio" + "os" + "regexp" + "strconv" + "testing" + + "crecher.net/gones/v2/cart" + "crecher.net/gones/v2/cpu" +) + +type nestestBus struct { + ram [0x0800]uint8 + cart cart.Mapper +} + +func (b *nestestBus) Read(addr uint16) uint8 { + switch { + case addr < 0x2000: + return b.ram[addr%0x0800] + case addr >= 0x4020: + return b.cart.CPURead(addr) + default: + return 0 + } +} + +func (b *nestestBus) Write(addr uint16, v uint8) { + switch { + case addr < 0x2000: + b.ram[addr%0x0800] = v + case addr >= 0x4020: + b.cart.CPUWrite(addr, v) + } +} + +type cpuState struct { + pc uint16 + a, x, y, p, sp uint8 + cyc uint64 +} + +var nestestLogRE = regexp.MustCompile( + `^([0-9A-F]{4}).*A:([0-9A-F]{2}) X:([0-9A-F]{2}) Y:([0-9A-F]{2}) P:([0-9A-F]{2}) SP:([0-9A-F]{2}).*CYC:\s*(\d+)`) + +func loadNestestLog(path string) ([]cpuState, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + parseHex8 := func(s string) uint8 { + v, _ := strconv.ParseUint(s, 16, 8) + return uint8(v) + } + + var lines []cpuState + scanner := bufio.NewScanner(f) + for scanner.Scan() { + m := nestestLogRE.FindStringSubmatch(scanner.Text()) + if m == nil { + continue + } + pc, _ := strconv.ParseUint(m[1], 16, 16) + cyc, _ := strconv.ParseUint(m[7], 10, 64) + lines = append(lines, cpuState{ + pc: uint16(pc), + a: parseHex8(m[2]), + x: parseHex8(m[3]), + y: parseHex8(m[4]), + p: parseHex8(m[5]), + sp: parseHex8(m[6]), + cyc: cyc, + }) + } + return lines, scanner.Err() +} + +func TestNestest(t *testing.T) { + const romPath = "testdata/nestest.nes" + const logPath = "testdata/nestest.log" + + c, err := cart.Load(romPath) + if err != nil { + t.Fatalf("loading ROM: %v", err) + } + want, err := loadNestestLog(logPath) + if err != nil { + t.Fatalf("loading log: %v", err) + } + if len(want) == 0 { + t.Fatal("no log lines parsed") + } + + core := cpu.NewCPU(&nestestBus{cart: c.Mapper}) + core.PC = 0xC000 + core.SP = 0xFD + core.Status = 0x24 + core.Cycles = want[0].cyc + + for i, w := range want { + got := cpuState{core.PC, core.A, core.X, core.Y, core.Status, core.SP, core.Cycles} + if got != w { + t.Fatalf("line %d: got %+v, want %+v", i+1, got, w) + } + core.Step() + } +} |