aboutsummaryrefslogtreecommitdiff
path: root/tests/cpu_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'tests/cpu_test.go')
-rw-r--r--tests/cpu_test.go143
1 files changed, 135 insertions, 8 deletions
diff --git a/tests/cpu_test.go b/tests/cpu_test.go
index 269f9ee..e8489f1 100644
--- a/tests/cpu_test.go
+++ b/tests/cpu_test.go
@@ -226,14 +226,18 @@ func TestCompareFlags(t *testing.T) {
}
}
-func TestUnimplementedOpcodePanics(t *testing.T) {
- defer func() {
- if r := recover(); r == nil {
- t.Fatalf("expected panic for illegal opcode")
- }
- }()
- c, _ := newTestCPU([]uint8{0x8B})
- c.Step()
+func TestEveryOpcodeIsDefined(t *testing.T) {
+ for op := 0; op < 256; op++ {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Errorf("opcode 0x%02X panics: %v", op, r)
+ }
+ }()
+ c, _ := newTestCPU([]uint8{uint8(op), 0x00, 0x00})
+ c.Step()
+ }()
+ }
}
func TestJamStopsCPU(t *testing.T) {
@@ -269,3 +273,126 @@ func TestJammedIgnoresInterruptsUntilReset(t *testing.T) {
t.Fatal("Jammed = true after Reset, want false")
}
}
+
+func runSteps(prog []uint8, steps int) (*cpu.CPU, *ramBus) {
+ c, bus := newTestCPU(prog)
+ for i := 0; i < steps; i++ {
+ c.Step()
+ }
+ return c, bus
+}
+
+func TestANC(t *testing.T) {
+ for _, op := range []uint8{0x0B, 0x2B} {
+ c, _ := runSteps([]uint8{0xA9, 0xFF, op, 0x80}, 2) // lda #$ff ; anc #$80
+ if c.A != 0x80 || !has(c, cpu.FlagC) || !has(c, cpu.FlagN) || has(c, cpu.FlagZ) {
+ t.Errorf("op 0x%02X: A = 0x%02X, P = 0x%02X; want A=0x80, C and N set", op, c.A, c.Status)
+ }
+ }
+}
+
+func TestALR(t *testing.T) {
+ c, _ := runSteps([]uint8{0xA9, 0xFF, 0x4B, 0x03}, 2) // lda #$ff ; alr #$03
+ if c.A != 0x01 || !has(c, cpu.FlagC) || has(c, cpu.FlagN) || has(c, cpu.FlagZ) {
+ t.Errorf("A = 0x%02X, P = 0x%02X; want A=0x01, C set", c.A, c.Status)
+ }
+}
+
+func TestARR(t *testing.T) {
+ tests := []struct {
+ name string
+ prog []uint8
+ wantA uint8
+ c, v bool
+ }{
+ {"carry in, bits 6 and 5 set", []uint8{0x38, 0xA9, 0xFF, 0x6B, 0xFF}, 0xFF, true, false},
+ {"no carry in, bit 6 only", []uint8{0x18, 0xA9, 0xFF, 0x6B, 0x80}, 0x40, true, true},
+ }
+ for _, tt := range tests {
+ c, _ := runSteps(tt.prog, 3)
+ if c.A != tt.wantA || has(c, cpu.FlagC) != tt.c || has(c, cpu.FlagV) != tt.v {
+ t.Errorf("%s: A = 0x%02X, P = 0x%02X; want A=0x%02X, C=%v, V=%v", tt.name, c.A, c.Status, tt.wantA, tt.c, tt.v)
+ }
+ }
+}
+
+func TestAXS(t *testing.T) {
+ // lda #$f0 ; ldx #$3c ; axs #imm (a & x = 0x30)
+ c, _ := runSteps([]uint8{0xA9, 0xF0, 0xA2, 0x3C, 0xCB, 0x10}, 3)
+ if c.X != 0x20 || !has(c, cpu.FlagC) {
+ t.Errorf("X = 0x%02X, P = 0x%02X; want X=0x20, C set", c.X, c.Status)
+ }
+ c, _ = runSteps([]uint8{0xA9, 0xF0, 0xA2, 0x3C, 0xCB, 0x40}, 3)
+ if c.X != 0xF0 || has(c, cpu.FlagC) || !has(c, cpu.FlagN) {
+ t.Errorf("X = 0x%02X, P = 0x%02X; want X=0xF0, C clear, N set", c.X, c.Status)
+ }
+}
+
+func TestLAS(t *testing.T) {
+ c, bus := newTestCPU([]uint8{0xA0, 0x01, 0xBB, 0x00, 0x02}) // ldy #1 ; las $0200,y
+ bus.mem[0x0201] = 0xF0
+ c.Step()
+ if cycles := c.Step(); cycles != 4 {
+ t.Errorf("cycles = %d, want 4", cycles)
+ }
+ // 0xF0 & SP(0xFD) = 0xF0
+ if c.A != 0xF0 || c.X != 0xF0 || c.SP != 0xF0 || !has(c, cpu.FlagN) {
+ t.Errorf("A = 0x%02X, X = 0x%02X, SP = 0x%02X; want all 0xF0, N set", c.A, c.X, c.SP)
+ }
+}
+
+func TestANE(t *testing.T) {
+ // lda #$01 ; ldx #$0f ; ane #$ff -> (0x01 | 0xEE) & 0x0F & 0xFF
+ c, _ := runSteps([]uint8{0xA9, 0x01, 0xA2, 0x0F, 0x8B, 0xFF}, 3)
+ if c.A != 0x0F {
+ t.Errorf("A = 0x%02X, want 0x0F", c.A)
+ }
+}
+
+func TestLXA(t *testing.T) {
+ // lda #$01 ; lxa #$f0 -> (0x01 | 0xEE) & 0xF0
+ c, _ := runSteps([]uint8{0xA9, 0x01, 0xAB, 0xF0}, 2)
+ if c.A != 0xE0 || c.X != 0xE0 || !has(c, cpu.FlagN) {
+ t.Errorf("A = 0x%02X, X = 0x%02X; want both 0xE0, N set", c.A, c.X)
+ }
+}
+
+func TestStoreHighAnd(t *testing.T) {
+ tests := []struct {
+ name string
+ prog []uint8
+ zp bool // pointer $0200 at $10 for the (zp),y form
+ steps int
+ addr uint16
+ want uint8
+ }{
+ // the stored value is reg & (high byte of the base + 1) = reg & 0x03
+ {"shx", []uint8{0xA2, 0xFF, 0xA0, 0x01, 0x9E, 0x00, 0x02}, false, 3, 0x0201, 0x03},
+ {"shy", []uint8{0xA0, 0xFF, 0xA2, 0x01, 0x9C, 0x00, 0x02}, false, 3, 0x0201, 0x03},
+ {"ahx abs,y", []uint8{0xA9, 0xFF, 0xA2, 0x0F, 0xA0, 0x01, 0x9F, 0x00, 0x02}, false, 4, 0x0201, 0x03},
+ {"ahx (zp),y", []uint8{0xA9, 0xFF, 0xA2, 0x0F, 0xA0, 0x01, 0x93, 0x10}, true, 4, 0x0201, 0x03},
+ {"tas", []uint8{0xA9, 0xFF, 0xA2, 0x07, 0xA0, 0x01, 0x9B, 0x00, 0x02}, false, 4, 0x0201, 0x03},
+ // $02ff + 1 crosses a page: the value (X & 0x03 = 1) becomes the new high byte
+ {"shx page cross", []uint8{0xA2, 0x01, 0xA0, 0x01, 0x9E, 0xFF, 0x02}, false, 3, 0x0100, 0x01},
+ }
+ for _, tt := range tests {
+ c, bus := newTestCPU(tt.prog)
+ if tt.zp {
+ bus.mem[0x10], bus.mem[0x11] = 0x00, 0x02
+ }
+ for i := 0; i < tt.steps; i++ {
+ c.Step()
+ }
+ if got := bus.mem[tt.addr]; got != tt.want {
+ t.Errorf("%s: mem[0x%04X] = 0x%02X, want 0x%02X", tt.name, tt.addr, got, tt.want)
+ }
+ }
+}
+
+func TestTASSetsStackPointer(t *testing.T) {
+ // lda #$ff ; ldx #$07 ; ldy #$01 ; tas $0200,y
+ c, _ := runSteps([]uint8{0xA9, 0xFF, 0xA2, 0x07, 0xA0, 0x01, 0x9B, 0x00, 0x02}, 4)
+ if c.SP != 0x07 {
+ t.Errorf("SP = 0x%02X, want 0x07", c.SP)
+ }
+}