mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 13:48:02 -04:00
642 lines
16 KiB
Go
642 lines
16 KiB
Go
// This file is part of Gopher2600.
|
|
//
|
|
// Gopher2600 is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Gopher2600 is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package cpu_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/jetsetilly/gopher2600/hardware/cpu"
|
|
rtest "github.com/jetsetilly/gopher2600/hardware/cpu/registers/test"
|
|
"github.com/jetsetilly/gopher2600/test"
|
|
)
|
|
|
|
type testMem struct {
|
|
internal []uint8
|
|
}
|
|
|
|
func newTestMem() *testMem {
|
|
return &testMem{
|
|
internal: make([]uint8, 0x10000),
|
|
}
|
|
}
|
|
|
|
func (mem *testMem) putInstructions(origin uint16, bytes ...uint8) uint16 {
|
|
for i, b := range bytes {
|
|
_ = mem.Write(uint16(i)+origin, b)
|
|
}
|
|
return origin + uint16(len(bytes))
|
|
}
|
|
|
|
func (mem testMem) assert(t *testing.T, address uint16, value uint8) {
|
|
t.Helper()
|
|
d, _ := mem.Read(address)
|
|
if d != value {
|
|
t.Errorf("memory assertion failed (%v - wanted %v at address %04x", d, value, address)
|
|
}
|
|
}
|
|
|
|
// Clear sets all bytes in memory to zero.
|
|
func (mem *testMem) Clear() {
|
|
for i := 0; i < len(mem.internal); i++ {
|
|
mem.internal[i] = 0
|
|
}
|
|
}
|
|
|
|
func (mem testMem) Read(address uint16) (uint8, error) {
|
|
return mem.internal[address], nil
|
|
}
|
|
|
|
func (mem testMem) ReadZeroPage(address uint8) (uint8, error) {
|
|
return mem.Read(uint16(address))
|
|
}
|
|
|
|
func (mem *testMem) Write(address uint16, data uint8) error {
|
|
mem.internal[address] = data
|
|
return nil
|
|
}
|
|
|
|
func step(t *testing.T, mc *cpu.CPU) {
|
|
t.Helper()
|
|
err := mc.ExecuteInstruction(cpu.NilCycleCallback)
|
|
if err != nil {
|
|
fmt.Println(mc.LastResult.Defn)
|
|
t.Fatal(err)
|
|
}
|
|
err = mc.LastResult.IsValid()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func testStatusInstructions(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
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
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZC")
|
|
step(t, mc) // CLC
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZc")
|
|
step(t, mc) // CLI
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZc")
|
|
step(t, mc) // SEI
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdIZc")
|
|
step(t, mc) // SED
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BDIZc")
|
|
step(t, mc) // CLD
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdIZc")
|
|
step(t, mc) // CLV
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdIZc")
|
|
|
|
// PHP; PLP
|
|
_ = mem.putInstructions(origin, 0x08, 0x28)
|
|
step(t, mc) // PHP
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdIZc")
|
|
rtest.EquateRegisters(t, mc.SP.Register, 254)
|
|
|
|
// mangle status register
|
|
mc.Status.Sign = true
|
|
mc.Status.Overflow = true
|
|
mc.Status.Break = false
|
|
|
|
// restore status register
|
|
step(t, mc) // PLP
|
|
rtest.EquateRegisters(t, mc.SP.Register, 255)
|
|
|
|
// break flag is always set on PLP
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdIZc")
|
|
}
|
|
|
|
func testRegsiterArithmetic(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
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
|
|
rtest.EquateRegisters(t, mc.A, 11)
|
|
|
|
// SEC; SBC immediate
|
|
_ = mem.putInstructions(origin, 0x38, 0xe9, 8)
|
|
step(t, mc) // SEC
|
|
step(t, mc) // SBC #8
|
|
rtest.EquateRegisters(t, mc.A, 3)
|
|
}
|
|
|
|
func testRegsiterBitwiseInstructions(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// ORA immediate; EOR immediate; AND immediate
|
|
origin = mem.putInstructions(origin, 0x09, 0xff, 0x49, 0xf0, 0x29, 0x01)
|
|
rtest.EquateRegisters(t, mc.A, 0)
|
|
step(t, mc) // ORA #$FF
|
|
rtest.EquateRegisters(t, mc.A, 255)
|
|
step(t, mc) // EOR #$F0
|
|
rtest.EquateRegisters(t, mc.A, 15)
|
|
step(t, mc) // AND #$01
|
|
rtest.EquateRegisters(t, mc.A, 1)
|
|
|
|
// ASL implied; LSR implied; LSR implied
|
|
origin = mem.putInstructions(origin, 0x0a, 0x4a, 0x4a)
|
|
step(t, mc) // ASL
|
|
rtest.EquateRegisters(t, mc.A, 2)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-Bdizc")
|
|
step(t, mc) // LSR
|
|
rtest.EquateRegisters(t, mc.A, 1)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-Bdizc")
|
|
step(t, mc) // LSR
|
|
rtest.EquateRegisters(t, mc.A, 0)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZC")
|
|
|
|
// ROL implied; ROR implied; ROR implied; ROR implied
|
|
_ = mem.putInstructions(origin, 0x2a, 0x6a, 0x6a, 0x6a)
|
|
step(t, mc) // ROL
|
|
rtest.EquateRegisters(t, mc.A, 1)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-Bdizc")
|
|
step(t, mc) // ROR
|
|
rtest.EquateRegisters(t, mc.A, 0)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZC")
|
|
step(t, mc) // ROR
|
|
rtest.EquateRegisters(t, mc.A, 128)
|
|
rtest.EquateRegisters(t, mc.Status, "Sv-Bdizc")
|
|
step(t, mc) // ROR
|
|
rtest.EquateRegisters(t, mc.A, 64)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-Bdizc")
|
|
}
|
|
|
|
func testImmediateImplied(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// LDX immediate; INX; DEX
|
|
origin = mem.putInstructions(origin, 0xa2, 5, 0xe8, 0xca)
|
|
step(t, mc) // LDX #5
|
|
rtest.EquateRegisters(t, mc.X, 5)
|
|
step(t, mc) // INX
|
|
rtest.EquateRegisters(t, mc.X, 6)
|
|
step(t, mc) // DEX
|
|
rtest.EquateRegisters(t, mc.X, 5)
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(t, mc.SP.Register, 254)
|
|
step(t, mc) // LDA #0
|
|
rtest.EquateRegisters(t, mc.A, 0)
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZc")
|
|
step(t, mc) // PLA
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(t, mc.X, 5)
|
|
step(t, mc) // TAY
|
|
rtest.EquateRegisters(t, mc.Y, 5)
|
|
step(t, mc) // LDX #1
|
|
step(t, mc) // TXA
|
|
rtest.EquateRegisters(t, mc.A, 1)
|
|
step(t, mc) // LDY #2
|
|
step(t, mc) // TYA
|
|
rtest.EquateRegisters(t, mc.A, 2)
|
|
step(t, mc) // INY
|
|
rtest.EquateRegisters(t, mc.Y, 3)
|
|
step(t, mc) // DEY
|
|
rtest.EquateRegisters(t, mc.Y, 2)
|
|
|
|
// TSX; LDX immediate; TXS
|
|
_ = mem.putInstructions(origin, 0xba, 0xa2, 100, 0x9a)
|
|
step(t, mc) // TSX
|
|
rtest.EquateRegisters(t, mc.X, 255)
|
|
step(t, mc) // LDX #100
|
|
step(t, mc) // TXS
|
|
rtest.EquateRegisters(t, mc.SP.Register, 100)
|
|
}
|
|
|
|
func testOtherAddressingModes(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
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
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(t, mc.A, 0xa2)
|
|
|
|
// LDA absolute
|
|
origin = mem.putInstructions(origin, 0xad, 0x00, 0x01)
|
|
step(t, mc) // LDA $0100
|
|
rtest.EquateRegisters(t, mc.A, 123)
|
|
|
|
// LDX immediate; LDA absolute,X
|
|
origin = mem.putInstructions(origin, 0xa2, 1, 0xbd, 0x01, 0x00)
|
|
step(t, mc) // LDX #1
|
|
rtest.EquateRegisters(t, mc.X, 1)
|
|
step(t, mc) // LDA $0001,X
|
|
rtest.EquateRegisters(t, mc.A, 0xa2)
|
|
|
|
// LDY immediate; LDA absolute,Y
|
|
origin = mem.putInstructions(origin, 0xa0, 1, 0xb9, 0x01, 0x00)
|
|
step(t, mc) // LDY #1
|
|
rtest.EquateRegisters(t, mc.X, 1)
|
|
step(t, mc) // LDA $0001,Y
|
|
rtest.EquateRegisters(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)
|
|
|
|
// post-indexed indirect (see below)
|
|
|
|
// 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)
|
|
rtest.EquateRegisters(t, mc.A, 47)
|
|
|
|
// post-indexed indirect (with page-fault)
|
|
// Y = 1
|
|
// INY; INY; LDA (Indirect), Y
|
|
mem.putInstructions(0xc0, 0xfd, 0x00)
|
|
_ = mem.putInstructions(origin, 0xc8, 0xc8, 0xb1, 0xc0)
|
|
step(t, mc) // INY (y = 2)
|
|
step(t, mc) // INY (y = 2)
|
|
step(t, mc) // LDA (0x0b),Y
|
|
rtest.EquateRegisters(t, mc.A, 123)
|
|
if mc.LastResult.PageFault != true {
|
|
t.Errorf("expected page-fault")
|
|
}
|
|
}
|
|
|
|
func testPostIndexedIndirect(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.putInstructions(0xee00, 0x01, 0x02, 0x03)
|
|
|
|
mc.PC.Load(0x04)
|
|
origin = mem.putInstructions(origin, 0x01, 0xee, 0xfe, 0xfd)
|
|
origin = mem.putInstructions(origin, 0xa0, 0x01)
|
|
step(t, mc)
|
|
rtest.EquateRegisters(t, mc.Y, 1)
|
|
_ = mem.putInstructions(origin, 0xb1, 0x00)
|
|
step(t, mc)
|
|
rtest.EquateRegisters(t, mc.A, 0x03)
|
|
}
|
|
|
|
func testStorageInstructions(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
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
|
|
_ = 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 *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
_ = mem.putInstructions(origin, 0x10, 0x10)
|
|
step(t, mc) // BPL $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x12)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
_ = mem.putInstructions(origin, 0x50, 0x10)
|
|
step(t, mc) // BVC $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x12)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
_ = mem.putInstructions(origin, 0x90, 0x10)
|
|
step(t, mc) // BCC $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x12)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
_ = mem.putInstructions(origin, 0x38, 0xb0, 0x10)
|
|
step(t, mc) // SEC
|
|
step(t, mc) // BCS $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x13)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
_ = mem.putInstructions(origin, 0xe8, 0xd0, 0x10)
|
|
step(t, mc) // INX
|
|
step(t, mc) // BNE $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x13)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
_ = mem.putInstructions(origin, 0xca, 0x30, 0x10)
|
|
step(t, mc) // DEX
|
|
step(t, mc) // BMI $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x13)
|
|
|
|
_ = mem.putInstructions(0x13, 0xe8, 0xf0, 0x10)
|
|
step(t, mc) // INX
|
|
step(t, mc) // BEQ $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x26)
|
|
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
// fudging overflow test
|
|
mc.Status.Overflow = true
|
|
_ = mem.putInstructions(origin, 0x70, 0x10)
|
|
step(t, mc) // BVS $10
|
|
rtest.EquateRegisters(t, mc.PC, 0x12)
|
|
}
|
|
|
|
func testBranchingBackwards(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
origin = 0x20
|
|
err := mc.LoadPC(0x20)
|
|
test.ExpectSuccess(t, err)
|
|
|
|
// BPL backwards
|
|
_ = mem.putInstructions(origin, 0x10, 0xfd)
|
|
step(t, mc) // BPL $FF
|
|
rtest.EquateRegisters(t, mc.PC, 0x1f)
|
|
|
|
// BVS backwards
|
|
origin = 0x20
|
|
err = mc.LoadPC(0x20)
|
|
test.ExpectSuccess(t, err)
|
|
mc.Status.Overflow = true
|
|
_ = mem.putInstructions(origin, 0x70, 0xfd)
|
|
step(t, mc) // BVS $FF
|
|
rtest.EquateRegisters(t, mc.PC, 0x1f)
|
|
}
|
|
|
|
func testBranchingPageFaults(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// BNE backwards - with PC wrap (causing a page fault)
|
|
origin = 0x20
|
|
err := mc.LoadPC(0x20)
|
|
test.ExpectSuccess(t, err)
|
|
mc.Status.Zero = false
|
|
_ = mem.putInstructions(origin, 0xd0, 0x80)
|
|
step(t, mc) // BNE $F0
|
|
rtest.EquateRegisters(t, mc.PC, 0xffa2)
|
|
|
|
// pagefault flag should be set
|
|
if !mc.LastResult.PageFault {
|
|
t.Errorf("expected pagefault on branch")
|
|
}
|
|
|
|
// number of cycles should be 4 instead of 2
|
|
// +1 for failed branch test (causing PC to jump)
|
|
// +1 for page fault
|
|
if mc.LastResult.Cycles != 4 {
|
|
t.Errorf("expected pagefault on branch")
|
|
}
|
|
}
|
|
|
|
func testJumps(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// JMP absolute
|
|
_ = mem.putInstructions(origin, 0x4c, 0x00, 0x01)
|
|
step(t, mc) // JMP $100
|
|
rtest.EquateRegisters(t, mc.PC, 0x0100)
|
|
|
|
// JMP indirect
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.putInstructions(0x0050, 0x49, 0x01)
|
|
_ = mem.putInstructions(origin, 0x6c, 0x50, 0x00)
|
|
step(t, mc) // JMP ($50)
|
|
rtest.EquateRegisters(t, mc.PC, 0x0149)
|
|
|
|
// JMP indirect (bug)
|
|
origin = 0
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
mem.putInstructions(0x01FF, 0x03)
|
|
mem.putInstructions(0x0100, 0x00)
|
|
_ = mem.putInstructions(origin, 0x6c, 0xFF, 0x01)
|
|
step(t, mc) // JMP ($0x01FF)
|
|
rtest.EquateRegisters(t, mc.PC, 0x0003)
|
|
}
|
|
|
|
func testComparisonInstructions(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// CMP immediate (equality)
|
|
origin = mem.putInstructions(origin, 0xc9, 0x00)
|
|
step(t, mc) // CMP $00
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(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
|
|
rtest.EquateRegisters(t, mc.Status, "sv-Bdizc")
|
|
|
|
// BIT zero page
|
|
origin = mem.putInstructions(origin, 0x24, 0x01)
|
|
step(t, mc) // BIT $01
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZc")
|
|
|
|
// BIT immediate
|
|
_ = mem.putInstructions(origin, 0x24, 0x01)
|
|
step(t, mc) // BIT $01
|
|
rtest.EquateRegisters(t, mc.Status, "sv-BdiZc")
|
|
}
|
|
|
|
func testSubroutineInstructions(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
// JSR absolute
|
|
_ = mem.putInstructions(origin, 0x20, 0x00, 0x01)
|
|
step(t, mc) // JSR $0100
|
|
rtest.EquateRegisters(t, mc.PC, 0x0100)
|
|
mem.assert(t, 0x01ff, 0x00)
|
|
mem.assert(t, 0x01fe, 0x02)
|
|
rtest.EquateRegisters(t, mc.SP.Register, 253)
|
|
|
|
_ = mem.putInstructions(0x100, 0x60)
|
|
step(t, mc) // RTS
|
|
rtest.EquateRegisters(t, mc.PC, 0x0003)
|
|
mem.assert(t, 0x01ff, 0x00)
|
|
mem.assert(t, 0x01fe, 0x02)
|
|
rtest.EquateRegisters(t, mc.SP.Register, 255)
|
|
}
|
|
|
|
func testDecimalMode(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
_ = mem.putInstructions(origin, 0xf8, 0xa9, 0x20, 0x38, 0xe9, 0x01)
|
|
step(t, mc) // SED
|
|
step(t, mc) // LDA #$20
|
|
step(t, mc) // SEC
|
|
step(t, mc) // SBC #$00
|
|
rtest.EquateRegisters(t, mc.A, 0x19)
|
|
}
|
|
|
|
func testBRK(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
_ = mem.putInstructions(origin, 0x69, 0x01, 0x00)
|
|
step(t, mc) // ADC #$01
|
|
rtest.EquateRegisters(t, mc.PC, 0x02)
|
|
rtest.EquateRegisters(t, mc.A, 0x01)
|
|
step(t, mc) // BRK
|
|
rtest.EquateRegisters(t, mc.PC, 0x00)
|
|
}
|
|
|
|
func testKIL(t *testing.T, mc *cpu.CPU, mem *testMem) {
|
|
var origin uint16
|
|
mem.Clear()
|
|
mc.Reset()
|
|
|
|
_ = mem.putInstructions(origin, 0x02, 0x69, 0x01)
|
|
step(t, mc) // KIL
|
|
rtest.EquateRegisters(t, mc.PC, 0x01)
|
|
step(t, mc) // ADC #$01
|
|
rtest.EquateRegisters(t, mc.PC, 0x01)
|
|
rtest.EquateRegisters(t, mc.A, 0x00)
|
|
}
|
|
|
|
func TestCPU(t *testing.T) {
|
|
mem := newTestMem()
|
|
mc := cpu.NewCPU(nil, mem)
|
|
|
|
testStatusInstructions(t, mc, mem)
|
|
testRegsiterArithmetic(t, mc, mem)
|
|
testRegsiterBitwiseInstructions(t, mc, mem)
|
|
testImmediateImplied(t, mc, mem)
|
|
testOtherAddressingModes(t, mc, mem)
|
|
testPostIndexedIndirect(t, mc, mem)
|
|
testStorageInstructions(t, mc, mem)
|
|
testBranching(t, mc, mem)
|
|
testBranchingBackwards(t, mc, mem)
|
|
testBranchingPageFaults(t, mc, mem)
|
|
testJumps(t, mc, mem)
|
|
testComparisonInstructions(t, mc, mem)
|
|
testSubroutineInstructions(t, mc, mem)
|
|
testDecimalMode(t, mc, mem)
|
|
testBRK(t, mc, mem)
|
|
testKIL(t, mc, mem)
|
|
}
|