From 8b0292cccf768efdd5295fe0b662488cbc671079 Mon Sep 17 00:00:00 2001 From: crecher Date: Mon, 12 Oct 2026 04:07:09 +0200 Subject: added cart package, more tests, tests are now at ./tests and added .gitignore --- cpu/cpu_test.go | 231 -------------------------------------------------------- 1 file changed, 231 deletions(-) delete mode 100644 cpu/cpu_test.go (limited to 'cpu/cpu_test.go') diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go deleted file mode 100644 index e4a32a6..0000000 --- a/cpu/cpu_test.go +++ /dev/null @@ -1,231 +0,0 @@ -package cpu - -import "testing" - -type ramBus struct { - mem [65536]uint8 -} - -func (b *ramBus) Read(addr uint16) uint8 { return b.mem[addr] } -func (b *ramBus) Write(addr uint16, v uint8) { b.mem[addr] = v } - -func newTestCPU(prog []uint8) (*CPU, *ramBus) { - bus := &ramBus{} - copy(bus.mem[0x8000:], prog) - bus.mem[0xFFFC] = 0x00 - bus.mem[0xFFFD] = 0x80 - c := NewCPU(bus) - c.Reset() - return c, bus -} - -func TestResetVector(t *testing.T) { - c, _ := newTestCPU([]uint8{0xEA}) - if c.PC != 0x8000 { - t.Fatalf("PC = 0x%04X, want 0x8000", c.PC) - } - if c.SP != 0xFD { - t.Fatalf("SP = 0x%02X, want 0xFD", c.SP) - } -} - -func TestLDAImmediate(t *testing.T) { - c, _ := newTestCPU([]uint8{0xA9, 0x42}) - cycles := c.Step() - if c.A != 0x42 { - t.Fatalf("A = 0x%02X, want 0x42", c.A) - } - if cycles != 2 { - t.Fatalf("cycles = %d, want 2", cycles) - } - if c.flag(FlagZ) || c.flag(FlagN) { - t.Fatalf("unexpected flags: 0x%02X", c.Status) - } -} - -func TestLDAZeroAndNegativeFlags(t *testing.T) { - c, _ := newTestCPU([]uint8{0xA9, 0x00}) - c.Step() - if !c.flag(FlagZ) { - t.Fatalf("Z flag should be set for A=0") - } - - c, _ = newTestCPU([]uint8{0xA9, 0x80}) - c.Step() - if !c.flag(FlagN) { - t.Fatalf("N flag should be set for A=0x80") - } -} - -func TestSTAAbsolute(t *testing.T) { - c, bus := newTestCPU([]uint8{0xA9, 0x55, 0x8D, 0x00, 0x02}) // lda #$55 ; sta $0200 - c.Step() - c.Step() - if bus.mem[0x0200] != 0x55 { - t.Fatalf("mem[0x0200] = 0x%02X, want 0x55", bus.mem[0x0200]) - } -} - -func TestAbsoluteXPageCross(t *testing.T) { - // lda $80ff,X with X=1 crosses into page $8100 -> extra cycle - c, bus := newTestCPU([]uint8{0xA2, 0x01, 0xBD, 0xFF, 0x80}) - bus.mem[0x8100] = 0x99 - c.Step() // ldx #1 - cycles := c.Step() - if c.A != 0x99 { - t.Fatalf("A = 0x%02X, want 0x99", c.A) - } - if cycles != 5 { - t.Fatalf("cycles = %d, want 5 (4 base + 1 page cross)", cycles) - } -} - -func TestAdcCarryAndOverflow(t *testing.T) { - // 0x50 + 0x50 = 0xa0, signed overflow (positive+positive=negative), no carry - c, _ := newTestCPU([]uint8{0xA9, 0x50, 0x69, 0x50}) - c.Step() - c.Step() - if c.A != 0xA0 { - t.Fatalf("A = 0x%02X, want 0xA0", c.A) - } - if !c.flag(FlagV) { - t.Fatalf("V flag should be set") - } - if c.flag(FlagC) { - t.Fatalf("C flag should be clear") - } - - // 0xff + 0x01 = 0x00, carry out, no overflow - c, _ = newTestCPU([]uint8{0xA9, 0xFF, 0x69, 0x01}) - c.Step() - c.Step() - if c.A != 0x00 { - t.Fatalf("A = 0x%02X, want 0x00", c.A) - } - if !c.flag(FlagC) { - t.Fatalf("C flag should be set") - } - if !c.flag(FlagZ) { - t.Fatalf("Z flag should be set") - } - if c.flag(FlagV) { - t.Fatalf("V flag should be clear") - } -} - -func TestSbcBorrow(t *testing.T) { - // 0x10 - 0x01 with carry preset (no borrow): result 0x0f, carry stays set - c, _ := newTestCPU([]uint8{0x38, 0xA9, 0x10, 0xE9, 0x01}) // sec ; lda #$10 ; sbc #$01 - c.Step() - c.Step() - c.Step() - if c.A != 0x0F { - t.Fatalf("A = 0x%02X, want 0x0F", c.A) - } - if !c.flag(FlagC) { - t.Fatalf("C flag should be set (no borrow)") - } -} - -func TestBranchTakenAndPageCross(t *testing.T) { - // beq with Z set, forward branch within the same page: +1 cycle - c, _ := newTestCPU([]uint8{0xA9, 0x00, 0xF0, 0x02, 0xEA, 0xEA, 0xEA}) // lda #0 ; beq +2 ; nop ; nop ; nop - c.Step() - cycles := c.Step() - if cycles != 3 { - t.Fatalf("cycles = %d, want 3 (2 base + 1 taken)", cycles) - } - if c.PC != 0x8006 { - t.Fatalf("PC = 0x%04X, want 0x8006", c.PC) - } -} - -func TestJsrRts(t *testing.T) { - // jsr $8004 ; (pad) ; rts at $8004, followed by nop at $8003 after return - prog := []uint8{0x20, 0x04, 0x80, 0xEA, 0x60} - c, _ := newTestCPU(prog) - c.Step() // jsr - if c.PC != 0x8004 { - t.Fatalf("PC after JSR = 0x%04X, want 0x8004", c.PC) - } - c.Step() // rts - if c.PC != 0x8003 { - t.Fatalf("PC after RTS = 0x%04X, want 0x8003", c.PC) - } -} - -func TestStackPushPullRoundTrip(t *testing.T) { - c, _ := newTestCPU([]uint8{0xA9, 0x7E, 0x48, 0xA9, 0x00, 0x68}) // lda #$7e ; pha ; lda #0 ; pla - c.Step() - c.Step() - c.Step() - c.Step() - if c.A != 0x7E { - t.Fatalf("A after PLA = 0x%02X, want 0x7E", c.A) - } -} - -func TestPhpPlpPreservesFlags(t *testing.T) { - c, _ := newTestCPU([]uint8{0x38, 0x08, 0x18, 0x28}) // sec ; php ; clc ; plp - c.Step() // sec - c.Step() // php - c.Step() // clc - if c.flag(FlagC) { - t.Fatalf("C flag should be clear after CLC") - } - c.Step() // plp - if !c.flag(FlagC) { - t.Fatalf("C flag should be restored by PLP") - } -} - -func TestIndirectJmpPageBug(t *testing.T) { - // jmp ($30ff) must read the high byte from $3000, not $3100 - c, bus := newTestCPU([]uint8{0x6C, 0xFF, 0x30}) - bus.mem[0x30FF] = 0x34 - bus.mem[0x3000] = 0x12 // wrap target for the bug - bus.mem[0x3100] = 0x99 // must not be used - c.Step() - if c.PC != 0x1234 { - t.Fatalf("PC = 0x%04X, want 0x1234", c.PC) - } -} - -func TestNmiPushesStateAndJumps(t *testing.T) { - c, bus := newTestCPU([]uint8{0xEA}) - bus.mem[0xFFFA] = 0x00 - bus.mem[0xFFFB] = 0x90 - pcBefore := c.PC - spBefore := c.SP - c.NMI() - if c.PC != 0x9000 { - t.Fatalf("PC after NMI = 0x%04X, want 0x9000", c.PC) - } - if c.SP != spBefore-3 { - t.Fatalf("SP after NMI = 0x%02X, want 0x%02X", c.SP, spBefore-3) - } - c.pop() // discard pushed status - if pc := c.pop16(); pc != pcBefore { - t.Fatalf("pushed PC = 0x%04X, want 0x%04X", pc, pcBefore) - } -} - -func TestCompareFlags(t *testing.T) { - // cmp #$10 with A=$10 -> Z=1, C=1, N=0 - c, _ := newTestCPU([]uint8{0xA9, 0x10, 0xC9, 0x10}) - c.Step() - c.Step() - if !c.flag(FlagZ) || !c.flag(FlagC) || c.flag(FlagN) { - t.Fatalf("flags = 0x%02X, want Z and C set, N clear", c.Status) - } -} - -func TestUnimplementedOpcodePanics(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Fatalf("expected panic for illegal opcode") - } - }() - c, _ := newTestCPU([]uint8{0x02}) // illegal/unofficial opcode - c.Step() -} -- cgit v1.2.3