From 98438f51566e722ae8440456bc4674124cb91929 Mon Sep 17 00:00:00 2001 From: Zellyn Hunter Date: Mon, 11 Mar 2013 20:15:49 -0700 Subject: [PATCH] Added comparing test. --- tests/compare_test.go | 158 +++++++++++++++++++++++++++++++++++++++ tests/functional_test.go | 18 ++--- 2 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 tests/compare_test.go diff --git a/tests/compare_test.go b/tests/compare_test.go new file mode 100644 index 0000000..30293cd --- /dev/null +++ b/tests/compare_test.go @@ -0,0 +1,158 @@ +/* +Tests for the 6502 CPU emulator, comparing it with the transistor-level simulation. +*/ + +package tests + +import ( + "fmt" + "io/ioutil" + "testing" + + "github.com/zellyn/go6502/cpu" + "github.com/zellyn/go6502/visual" +) + +type memOp struct { + read bool + address uint16 + data byte +} + +func (op memOp) String() string { + rw := "W" + if op.read { + rw = "R" + } + return fmt.Sprintf("{%s $%04X: %02X}", rw, op.address, op.data) +} + +// Memory for the tests. Satisfies the cpu.Memory interface. +type memorizer struct { + mem1 [65536]byte + mem2 [65536]byte + ops []memOp + verify bool + errors []string +} + +func (m *memorizer) Reset() { + m.verify = false + m.errors = m.errors[:0] + m.ops = m.ops[:0] +} + +func (m *memorizer) Record() { + m.verify = false +} + +func (m *memorizer) Verify() { + m.verify = true +} + +func (m *memorizer) checkOp(newOp memOp) { + oldOp := m.ops[0] + m.ops = m.ops[1:] + if oldOp != newOp { + // fmt.Println(newOp, "expected:", oldOp) + m.errors = append(m.errors, fmt.Sprintf("Bad op: %v, expected %v", newOp, oldOp)) + } else { + // fmt.Println(newOp) + } +} + +func (m *memorizer) Read(address uint16) byte { + if !m.verify { // recording + data := m.mem1[address] + newOp := memOp{true, address, data} + m.ops = append(m.ops, newOp) + return data + } + data := m.mem2[address] + m.checkOp(memOp{true, address, data}) + return data +} + +func (m *memorizer) Write(address uint16, value byte) { + newOp := memOp{false, address, value} + if !m.verify { // recording + m.ops = append(m.ops, newOp) + m.mem1[address] = value + return + } + m.checkOp(newOp) + m.mem2[address] = value +} + +// Run the first few thousand steps of Klaus Dormann's comprehensive +// test against the instruction- and gate-level CPU emulations, making +// sure they have the same memory access patterns. +func TestFunctionalTestCompare(t *testing.T) { + _ = []uint16{ + 0, + 4327, + 4288, + 4253, + 4221, + 4194, + 4171, + 4152, + 4137, + 4105, + 4374, + 4368, + } + bytes, err := ioutil.ReadFile("6502_functional_test.bin") + if err != nil { + panic("Cannot read file") + } + var m memorizer + OFFSET := 0xa + copy(m.mem1[OFFSET:len(bytes)+OFFSET], bytes) + copy(m.mem2[OFFSET:len(bytes)+OFFSET], bytes) + // Set the RESET vector to jump to the tests + m.mem1[0xFFFC] = 0x00 + m.mem1[0xFFFD] = 0x10 + m.mem2[0xFFFC] = 0x00 + m.mem2[0xFFFD] = 0x10 + + v := visual.NewCPU(&m) + v.Reset() + for i := 0; i < 8; i++ { + v.Step() + } + + var cc CycleCount + c := cpu.NewCPU(&m, &cc, cpu.VERSION_6502) + c.Reset() + + m.Reset() + v.Step() + v.Step() + m.Verify() + c.Step() + if len(m.errors) > 0 { + t.Fatal("Errors on reset", m.errors) + } + + for { + m.Record() + for i := 0; i < 1000; i++ { + v.Step() + } + m.Verify() + for len(m.ops) > 7 { + s := status(c, &m.mem2, uint64(cc)) + c.Step() + if len(m.errors) > 0 { + t.Fatalf("Error at %v: %v", s, m.errors) + } + if cc%1000 == 0 { + pc := c.PC() + if cc > 20000 || pc == 0x3CC5 { + return + } + } + } + } +} diff --git a/tests/functional_test.go b/tests/functional_test.go index f159f3e..4cb9a8d 100644 --- a/tests/functional_test.go +++ b/tests/functional_test.go @@ -37,18 +37,18 @@ func randomize(k *K64) { } } -// printStatus prints out the current CPU instruction and register status. -func printStatus(c cpu.Cpu, m K64) { +// status prints out the current CPU instruction and register status. +func status(c cpu.Cpu, m *[65536]byte, cc uint64) string { bytes, text, _ := asm.Disasm(c.PC(), m[c.PC()], m[c.PC()+1], m[c.PC()+2]) - fmt.Printf("$%04X: %s %s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b\n", - c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P()) + return fmt.Sprintf("$%04X: %s %s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b - %d\n", + c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P(), cc) } // Run Klaus Dormann's amazing comprehensive test against the // instruction-level CPU emulation. func TestFunctionalTestInstructions(t *testing.T) { unused := map[byte]bool{} - for k, _ := range cpu.Opcodes { + for k := range cpu.Opcodes { unused[k] = true } bytes, err := ioutil.ReadFile("6502_functional_test.bin") @@ -66,7 +66,7 @@ func TestFunctionalTestInstructions(t *testing.T) { for { unused[m[c.PC()]] = false oldPC := c.PC() - // printStatus(c, m, cc) + // status(c, m, cc) err := c.Step() if err != nil { t.Error(err) @@ -133,7 +133,7 @@ func TestFunctionalTestGates(t *testing.T) { return } } - // printStatus(c, m) + // status(c, m, cc) c.Step() } } @@ -155,7 +155,7 @@ func TestDecimalMode6502(t *testing.T) { c.SetPC(0x1000) for { oldPC := c.PC() - // printStatus(c, m, cc) + // status(c, m, cc) err := c.Step() if err != nil { t.Error(err) @@ -191,7 +191,7 @@ func TestDecimalMode65C02(t *testing.T) { c.SetPC(0x1000) for { oldPC := c.PC() - // printStatus(c, m, cc) + // status(c, m, cc) err := c.Step() if err != nil { t.Error(err)