mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
- added ONSTEP command. similar to ONHALT but run every step - implemented LAST command. debugger no longer runs the result of the last instruction unless it is part of the ONSTEP command sequence - fixed printing of HEX. step mode video no longer crashes if print style includes hex. o video - sketched in collisions support o cpu - BIT instruction fixed (zero flag is result of AND with accumulator but sign and overflow flags are set with reference to the value from memory)
469 lines
12 KiB
Go
469 lines
12 KiB
Go
package cpu_test
|
|
|
|
import (
|
|
"gopher2600/assert"
|
|
"gopher2600/hardware/cpu"
|
|
"testing"
|
|
)
|
|
|
|
func step(t *testing.T, mc *cpu.CPU) *cpu.InstructionResult {
|
|
result, err := mc.ExecuteInstruction(func(*cpu.InstructionResult) {})
|
|
if err != nil {
|
|
t.Fatalf("error during CPU step (%v)\n", err)
|
|
}
|
|
err = result.IsValid()
|
|
if err != nil {
|
|
t.Fatalf("error during CPU step (%v)\n", err)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func testStatusInstructions(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// SEC; CLC; CLI; SEI; SED; CLD; CLV
|
|
origin = mem.putInstructions(origin, 0x38, 0x18, 0x58, 0x78, 0xf8, 0xd8, 0xb8)
|
|
step(t, mc) // SEC
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZC")
|
|
step(t, mc) // CLC
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
step(t, mc) // CLI
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdiZc")
|
|
step(t, mc) // SEI
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
step(t, mc) // SED
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BDIZc")
|
|
step(t, mc) // CLD
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
step(t, mc) // CLV
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
|
|
// PHP; PLP
|
|
origin = mem.putInstructions(origin, 0x08, 0x28)
|
|
step(t, mc) // PHP
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
assert.CheckValueVCS(t, mc.SP, 254)
|
|
|
|
// mangle status register
|
|
mc.Status.Sign = true
|
|
mc.Status.Overflow = true
|
|
mc.Status.Break = false
|
|
|
|
// restore status register
|
|
step(t, mc) // PLP
|
|
assert.CheckValueVCS(t, mc.SP, 255)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
}
|
|
|
|
func testRegsiterArithmetic(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// LDA immediate; ADC immediate
|
|
origin = mem.putInstructions(origin, 0xa9, 1, 0x69, 10)
|
|
step(t, mc) // LDA #1
|
|
step(t, mc) // ADC #10
|
|
assert.CheckValueVCS(t, mc.A, 11)
|
|
|
|
// SEC; SBC immediate
|
|
origin = mem.putInstructions(origin, 0x38, 0xe9, 8)
|
|
step(t, mc) // SEC
|
|
step(t, mc) // SBC #8
|
|
assert.CheckValueVCS(t, mc.A, 3)
|
|
}
|
|
|
|
func testRegsiterBitwiseInstructions(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// ORA immediate; EOR immediate; AND immediate
|
|
origin = mem.putInstructions(origin, 0x09, 0xff, 0x49, 0xf0, 0x29, 0x01)
|
|
assert.CheckValueVCS(t, mc.A, 0)
|
|
step(t, mc) // ORA #$FF
|
|
assert.CheckValueVCS(t, mc.A, 255)
|
|
step(t, mc) // EOR #$F0
|
|
assert.CheckValueVCS(t, mc.A, 15)
|
|
step(t, mc) // AND #$01
|
|
assert.CheckValueVCS(t, mc.A, 1)
|
|
|
|
// ASL implied; LSR implied; LSR implied
|
|
origin = mem.putInstructions(origin, 0x0a, 0x4a, 0x4a)
|
|
step(t, mc) // ASL
|
|
assert.CheckValueVCS(t, mc.A, 2)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIzc")
|
|
step(t, mc) // LSR
|
|
assert.CheckValueVCS(t, mc.A, 1)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIzc")
|
|
step(t, mc) // LSR
|
|
assert.CheckValueVCS(t, mc.A, 0)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZC")
|
|
|
|
// ROL implied; ROR implied; ROR implied; ROR implied
|
|
origin = mem.putInstructions(origin, 0x2a, 0x6a, 0x6a, 0x6a)
|
|
step(t, mc) // ROL
|
|
assert.CheckValueVCS(t, mc.A, 1)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIzc")
|
|
step(t, mc) // ROR
|
|
assert.CheckValueVCS(t, mc.A, 0)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZC")
|
|
step(t, mc) // ROR
|
|
assert.CheckValueVCS(t, mc.A, 128)
|
|
assert.CheckValueVCS(t, mc.Status, "Sv-BdIzc")
|
|
step(t, mc) // ROR
|
|
assert.CheckValueVCS(t, mc.A, 64)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIzc")
|
|
}
|
|
|
|
func testImmediateImplied(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// LDX immediate; INX; DEX
|
|
origin = mem.putInstructions(origin, 0xa2, 5, 0xe8, 0xca)
|
|
step(t, mc) // LDX #5
|
|
assert.CheckValueVCS(t, mc.X, 5)
|
|
step(t, mc) // INX
|
|
assert.CheckValueVCS(t, mc.X, 6)
|
|
step(t, mc) // DEX
|
|
assert.CheckValueVCS(t, mc.X, 5)
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIzc")
|
|
|
|
// PHA; LDA immediate; PLA
|
|
origin = mem.putInstructions(origin, 0xa9, 5, 0x48, 0xa9, 0, 0x68)
|
|
step(t, mc) // LDA #5
|
|
step(t, mc) // PHA
|
|
assert.CheckValueVCS(t, mc.SP, 254)
|
|
step(t, mc) // LDA #0
|
|
assert.CheckValueVCS(t, mc.A, 0)
|
|
assert.CheckValueVCS(t, mc.Status.Zero, true)
|
|
step(t, mc) // PLA
|
|
assert.CheckValueVCS(t, mc.A, 5)
|
|
|
|
// TAX; TAY; LDX immediate; TXA; LDY immediate; TYA; INY; DEY
|
|
origin = mem.putInstructions(origin, 0xaa, 0xa8, 0xa2, 1, 0x8a, 0xa0, 2, 0x98, 0xc8, 0x88)
|
|
step(t, mc) // TAX
|
|
assert.CheckValueVCS(t, mc.X, 5)
|
|
step(t, mc) // TAY
|
|
assert.CheckValueVCS(t, mc.Y, 5)
|
|
step(t, mc) // LDX #1
|
|
step(t, mc) // TXA
|
|
assert.CheckValueVCS(t, mc.A, 1)
|
|
step(t, mc) // LDY #2
|
|
step(t, mc) // TYA
|
|
assert.CheckValueVCS(t, mc.A, 2)
|
|
step(t, mc) // INY
|
|
assert.CheckValueVCS(t, mc.Y, 3)
|
|
step(t, mc) // DEY
|
|
assert.CheckValueVCS(t, mc.Y, 2)
|
|
|
|
// TSX; LDX immediate; TXS
|
|
origin = mem.putInstructions(origin, 0xba, 0xa2, 100, 0x9a)
|
|
step(t, mc) // TSX
|
|
assert.CheckValueVCS(t, mc.X, 255)
|
|
step(t, mc) // LDX #100
|
|
step(t, mc) // TXS
|
|
assert.CheckValueVCS(t, mc.SP, 100)
|
|
}
|
|
|
|
func testOtherAddressingModes(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.putInstructions(0x0100, 123, 43)
|
|
mem.putInstructions(0x01a2, 47)
|
|
|
|
// LDA zero page
|
|
origin = mem.putInstructions(origin, 0xa5, 0x00)
|
|
step(t, mc) // LDA $00
|
|
assert.CheckValueVCS(t, mc.A, 0xa5)
|
|
|
|
// LDX immediate; LDA zero page,X
|
|
origin = mem.putInstructions(origin, 0xa2, 1, 0xb5, 0x01)
|
|
step(t, mc) // LDX #1
|
|
step(t, mc) // LDA 01,X
|
|
assert.CheckValueVCS(t, mc.A, 0xa2)
|
|
|
|
// LDY immediate; LDX zero page,Y
|
|
origin = mem.putInstructions(origin, 0xa0, 3, 0xb6, 0x01)
|
|
step(t, mc) // LDX #3
|
|
step(t, mc) // LDA 01,Y
|
|
assert.CheckValueVCS(t, mc.A, 0xa2)
|
|
|
|
// LDA absolute
|
|
origin = mem.putInstructions(origin, 0xad, 0x00, 0x01)
|
|
step(t, mc) // LDA $0100
|
|
assert.CheckValueVCS(t, mc.A, 123)
|
|
|
|
// LDX immediate; LDA absolute,X
|
|
origin = mem.putInstructions(origin, 0xa2, 1, 0xbd, 0x01, 0x00)
|
|
step(t, mc) // LDX #1
|
|
assert.CheckValueVCS(t, mc.X, 1)
|
|
step(t, mc) // LDA $0001,X
|
|
assert.CheckValueVCS(t, mc.A, 0xa2)
|
|
|
|
// LDY immediate; LDA absolute,Y
|
|
origin = mem.putInstructions(origin, 0xa0, 1, 0xb9, 0x01, 0x00)
|
|
step(t, mc) // LDY #1
|
|
assert.CheckValueVCS(t, mc.X, 1)
|
|
step(t, mc) // LDA $0001,Y
|
|
assert.CheckValueVCS(t, mc.A, 0xa2)
|
|
|
|
// pre-indexed indirect
|
|
// X = 1
|
|
// INX; LDA (Indirect, X)
|
|
origin = mem.putInstructions(origin, 0xe8, 0xa1, 0x0b)
|
|
step(t, mc) // INX (x equals 2)
|
|
step(t, mc) // LDA (0x0b,X)
|
|
assert.CheckValueVCS(t, mc.A, 47)
|
|
|
|
// post-indexed indirect
|
|
// Y = 1
|
|
// LDA (Indirect), Y
|
|
origin = mem.putInstructions(origin, 0xb1, 0x0b)
|
|
step(t, mc) // LDA (0x0b),Y
|
|
assert.CheckValueVCS(t, mc.A, 43)
|
|
|
|
// pre-indexed indirect (with wraparound)
|
|
// X = 1
|
|
// INX; LDA (Indirect, X)
|
|
origin = mem.putInstructions(origin, 0xe8, 0xa1, 0xff)
|
|
step(t, mc) // INX (x equals 2)
|
|
step(t, mc) // LDA (0xff,X)
|
|
assert.CheckValueVCS(t, mc.A, 47)
|
|
|
|
// post-indexed indirect (with page-fault)
|
|
// Y = 1
|
|
// INY; INY; LDA (Indirect), Y
|
|
mem.putInstructions(0xc0, 0xfd, 0x00)
|
|
origin = mem.putInstructions(origin, 0xc8, 0xc8, 0xb1, 0xc0)
|
|
step(t, mc) // INY (y = 2)
|
|
step(t, mc) // INY (y = 2)
|
|
result := step(t, mc) // LDA (0x0b),Y
|
|
assert.CheckValueVCS(t, mc.A, 123)
|
|
assert.CheckValueVCS(t, result.PageFault, true)
|
|
}
|
|
|
|
func testStorageInstructions(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// LDA immediate; STA absolute
|
|
origin = mem.putInstructions(origin, 0xa9, 0x54, 0x8d, 0x00, 0x01)
|
|
step(t, mc) // LDA 0x54
|
|
step(t, mc) // STA 0x0100
|
|
mem.assert(t, 0x0100, 0x54)
|
|
|
|
// LDX immediate; STX absolute
|
|
origin = mem.putInstructions(origin, 0xa2, 0x63, 0x8e, 0x01, 0x01)
|
|
step(t, mc) // LDX 0x63
|
|
step(t, mc) // STX 0x0101
|
|
mem.assert(t, 0x0101, 0x63)
|
|
|
|
// LDY immediate; STY absolute
|
|
origin = mem.putInstructions(origin, 0xa0, 0x72, 0x8c, 0x02, 0x01)
|
|
step(t, mc) // LDY 0x72
|
|
step(t, mc) // STY 0x0102
|
|
mem.assert(t, 0x0101, 0x63)
|
|
|
|
// INC zero page
|
|
origin = mem.putInstructions(origin, 0xe6, 0x01)
|
|
step(t, mc) // INC $01
|
|
mem.assert(t, 0x01, 0x55)
|
|
|
|
// DEC absolute
|
|
origin = mem.putInstructions(origin, 0xce, 0x00, 0x01)
|
|
step(t, mc) // DEC 0x0100
|
|
mem.assert(t, 0x0100, 0x53)
|
|
}
|
|
|
|
func testBranching(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
// TODO: test page faults
|
|
// TODO: test backwards branching
|
|
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
origin = mem.putInstructions(origin, 0x10, 0x10)
|
|
step(t, mc) // BPL $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x12)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
origin = mem.putInstructions(origin, 0x50, 0x10)
|
|
step(t, mc) // BVC $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x12)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
origin = mem.putInstructions(origin, 0x90, 0x10)
|
|
step(t, mc) // BCC $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x12)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
origin = mem.putInstructions(origin, 0x38, 0xb0, 0x10)
|
|
step(t, mc) // SEC
|
|
step(t, mc) // BCS $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x13)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
origin = mem.putInstructions(origin, 0xe8, 0xd0, 0x10)
|
|
step(t, mc) // INX
|
|
step(t, mc) // BNE $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x13)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
origin = mem.putInstructions(origin, 0xca, 0x30, 0x10)
|
|
step(t, mc) // DEX
|
|
step(t, mc) // BMI $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x13)
|
|
|
|
origin = mem.putInstructions(0x13, 0xe8, 0xf0, 0x10)
|
|
step(t, mc) // INX
|
|
step(t, mc) // BEQ $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x26)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
// fudging overflow test
|
|
mc.Status.Overflow = true
|
|
origin = mem.putInstructions(origin, 0x70, 0x10)
|
|
step(t, mc) // BVS $10
|
|
assert.CheckValueVCS(t, mc.PC, 0x12)
|
|
}
|
|
|
|
func testJumps(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// JMP absolute
|
|
origin = mem.putInstructions(origin, 0x4c, 0x00, 0x01)
|
|
step(t, mc) // JMP $100
|
|
assert.CheckValueVCS(t, mc.PC, 0x0100)
|
|
|
|
// JMP indirect
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.putInstructions(0x0050, 0x49, 0x01)
|
|
origin = mem.putInstructions(origin, 0x6c, 0x50, 0x00)
|
|
step(t, mc) // JMP ($50)
|
|
assert.CheckValueVCS(t, mc.PC, 0x0149)
|
|
|
|
// JMP indirect (bug)
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.putInstructions(0x01FF, 0x03)
|
|
mem.putInstructions(0x0100, 0x00)
|
|
origin = mem.putInstructions(origin, 0x6c, 0xFF, 0x01)
|
|
step(t, mc) // JMP ($0x01FF)
|
|
assert.CheckValueVCS(t, mc.PC, 0x0003)
|
|
}
|
|
|
|
func testComparisonInstructions(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// CMP immediate (equality)
|
|
origin = mem.putInstructions(origin, 0xc9, 0x00)
|
|
step(t, mc) // CMP $00
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZC")
|
|
|
|
// LDA immediate; CMP immediate
|
|
origin = mem.putInstructions(origin, 0xa9, 0xf6, 0xc9, 0x18)
|
|
step(t, mc) // LDA $F6
|
|
step(t, mc) // CMP $10
|
|
assert.CheckValueVCS(t, mc.Status, "Sv-BdIzC")
|
|
|
|
// LDX immediate; CMP immediate
|
|
origin = mem.putInstructions(origin, 0xa2, 0xf6, 0xe0, 0x18)
|
|
step(t, mc) // LDX $F6
|
|
step(t, mc) // CMP $10
|
|
assert.CheckValueVCS(t, mc.Status, "Sv-BdIzC")
|
|
|
|
// LDY immediate; CMP immediate
|
|
origin = mem.putInstructions(origin, 0xa0, 0xf6, 0xc0, 0x18)
|
|
step(t, mc) // LDY $F6
|
|
step(t, mc) // CMP $10
|
|
assert.CheckValueVCS(t, mc.Status, "Sv-BdIzC")
|
|
|
|
// LDA immediate; CMP immediate
|
|
origin = mem.putInstructions(origin, 0xa9, 0x18, 0xc9, 0xf6)
|
|
step(t, mc) // LDA $F6
|
|
step(t, mc) // CMP $10
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIzc")
|
|
|
|
// BIT zero page
|
|
origin = mem.putInstructions(origin, 0x24, 0x01)
|
|
step(t, mc) // BIT $01
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
|
|
// BIT immediate
|
|
origin = mem.putInstructions(origin, 0x24, 0x01)
|
|
step(t, mc) // BIT $01
|
|
assert.CheckValueVCS(t, mc.Status, "sv-BdIZc")
|
|
}
|
|
|
|
func testSubroutineInstructions(t *testing.T, mc *cpu.CPU, mem *MockMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// JSR absolute
|
|
origin = mem.putInstructions(origin, 0x20, 0x00, 0x01)
|
|
step(t, mc) // JSR $0100
|
|
assert.CheckValueVCS(t, mc.PC, 0x0100)
|
|
mem.assert(t, 255, 0x00)
|
|
mem.assert(t, 254, 0x02)
|
|
assert.CheckValueVCS(t, mc.SP, 253)
|
|
|
|
origin = mem.putInstructions(0x100, 0x60)
|
|
step(t, mc) // RTS
|
|
assert.CheckValueVCS(t, mc.PC, 0x0003)
|
|
mem.assert(t, 255, 0x00)
|
|
mem.assert(t, 254, 0x02)
|
|
assert.CheckValueVCS(t, mc.SP, 255)
|
|
}
|
|
|
|
func TestCPU(t *testing.T) {
|
|
mem := NewMockMem()
|
|
mc, err := cpu.New(mem)
|
|
if err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
testStatusInstructions(t, mc, mem)
|
|
testRegsiterArithmetic(t, mc, mem)
|
|
testRegsiterBitwiseInstructions(t, mc, mem)
|
|
testImmediateImplied(t, mc, mem)
|
|
testOtherAddressingModes(t, mc, mem)
|
|
testStorageInstructions(t, mc, mem)
|
|
testBranching(t, mc, mem)
|
|
testJumps(t, mc, mem)
|
|
testComparisonInstructions(t, mc, mem)
|
|
testSubroutineInstructions(t, mc, mem)
|
|
}
|