1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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()
}
}
|