Added IRQ/BRK/NMI support + tests

This commit is contained in:
Will Angenent 2018-05-04 15:47:22 +01:00
parent 4b2a4c3730
commit d45e1a4aac
6 changed files with 2557 additions and 76 deletions

1025
6502_interrupt_test.a65 Normal file

File diff suppressed because it is too large Load Diff

BIN
6502_interrupt_test.bin.gz Normal file

Binary file not shown.

1375
6502_interrupt_test.lst Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,45 +3,75 @@ package main
import ( import (
"encoding/hex" "encoding/hex"
"flag" "flag"
"fmt"
"mos6502go/cpu" "mos6502go/cpu"
"mos6502go/utils" "mos6502go/utils"
) )
func main() { func main() {
cpu.InitDisasm() cpu.InitDisasm()
var s cpu.State
s.Init()
bytes, err := utils.ReadMemoryFromFile("6502_functional_test.bin.gz")
if err != nil {
panic(err)
}
for i := 0; i < len(bytes); i++ {
s.Memory[i] = bytes[i]
}
showInstructions := flag.Bool("show-instructions", false, "Show instructions code while running") showInstructions := flag.Bool("show-instructions", false, "Show instructions code while running")
skipTest0 := flag.Bool("skip-functional-test", false, "Skip functional test")
skipTest1 := flag.Bool("skip-interrupt-test", false, "Skip interrupt test")
breakAddressString := flag.String("break", "", "Break on address") breakAddressString := flag.String("break", "", "Break on address")
flag.Parse() flag.Parse()
var breakAddress *uint16 var Roms = []string{
if *breakAddressString != "" { "6502_functional_test.bin.gz",
breakAddressValue, err := hex.DecodeString(*breakAddressString) "6502_interrupt_test.bin.gz",
}
for i, rom := range Roms {
if (i == 0) && *skipTest0 {
continue
}
if (i == 1) && *skipTest1 {
continue
}
fmt.Printf("Running %s\n", rom)
var s cpu.State
s.Init()
cpu.RunningTests = true
if i == 0 {
cpu.RunningFunctionalTests = true
}
if i == 1 {
cpu.RunningInterruptTests = true
}
bytes, err := utils.ReadMemoryFromFile(rom)
if err != nil { if err != nil {
panic(err) panic(err)
} }
var foo uint16 for i := 0; i < len(bytes); i++ {
if len(breakAddressValue) == 1 { s.Memory[i] = bytes[i]
foo = uint16(breakAddressValue[0])
} else if len(breakAddressValue) == 2 {
foo = uint16(breakAddressValue[0])*uint16(0x100) + uint16(breakAddressValue[1])
} else {
panic("Invalid break address")
} }
breakAddress = &foo
}
cpu.Run(&s, *showInstructions, breakAddress) var breakAddress *uint16
if *breakAddressString != "" {
breakAddressValue, err := hex.DecodeString(*breakAddressString)
if err != nil {
panic(err)
}
var foo uint16
if len(breakAddressValue) == 1 {
foo = uint16(breakAddressValue[0])
} else if len(breakAddressValue) == 2 {
foo = uint16(breakAddressValue[0])*uint16(0x100) + uint16(breakAddressValue[1])
} else {
panic("Invalid break address")
}
breakAddress = &foo
}
cpu.Run(&s, *showInstructions, breakAddress)
fmt.Printf("Finished running %s\n\n", rom)
}
} }

View File

@ -6,35 +6,47 @@ import (
) )
const ( const (
CpuFlagC byte = 1 << iota CpuFlagC byte = 1 << iota // 0x01
CpuFlagZ CpuFlagZ // 0x02
CpuFlagI CpuFlagI // 0x04
CpuFlagD CpuFlagD // 0x08
CpuFlagB CpuFlagB // 0x10
CpuFlagR CpuFlagR // 0x20
CpuFlagV CpuFlagV // 0x40
CpuFlagN CpuFlagN // 0x80
) )
const runningTests = true var (
RunningTests bool
RunningFunctionalTests bool
RunningInterruptTests bool
)
type State struct { type State struct {
Memory [0x10000]uint8 Memory [0x10000]uint8
A uint8 pendingInterrupt bool
X uint8 pendingNMI bool
Y uint8 A uint8
PC uint16 X uint8
SP uint8 Y uint8
P uint8 PC uint16
SP uint8
P uint8
} }
func (s *State) Init() { func (s *State) Init() {
RunningTests = false
RunningFunctionalTests = false
RunningInterruptTests = false
s.A = 0 s.A = 0
s.X = 0 s.X = 0
s.Y = 0 s.Y = 0
s.P = CpuFlagR | CpuFlagB | CpuFlagZ s.P = CpuFlagR | CpuFlagB | CpuFlagZ
s.PC = 0x800 s.PC = 0x800
s.SP = 0xff s.SP = 0xff
s.pendingInterrupt = false
s.pendingNMI = false
} }
func (s *State) setC(value bool) { func (s *State) setC(value bool) {
@ -120,10 +132,36 @@ func readMemory(s *State, address uint16) uint8 {
return s.Memory[address] return s.Memory[address]
} }
// Handle a write to a magic test address that triggers an interrupt and/or an NMI
func writeInterruptTestOpenCollector(s *State, address uint16, value uint8) {
oldValue := s.Memory[address]
oldInterrupt := (oldValue & 0x1) == 0x1
oldNMI := (oldValue & 0x2) == 0x2
interrupt := (value & 0x1) == 0x1
NMI := (value & 0x2) == 0x2
if oldInterrupt != interrupt {
s.pendingInterrupt = interrupt
}
if oldNMI != NMI {
s.pendingNMI = NMI
}
s.Memory[address] = value
}
func writeMemory(s *State, address uint16, value uint8) { func writeMemory(s *State, address uint16, value uint8) {
if RunningInterruptTests && address == 0xbffc {
writeInterruptTestOpenCollector(s, address, value)
return
}
s.Memory[address] = value s.Memory[address] = value
if runningTests && address == 0x200 { if RunningFunctionalTests && address == 0x200 {
testNumber := s.Memory[0x200] testNumber := s.Memory[0x200]
if testNumber == 0xf0 { if testNumber == 0xf0 {
fmt.Println("Opcode testing completed") fmt.Println("Opcode testing completed")
@ -145,7 +183,7 @@ func branch(s *State, cycles *int, instructionName string, doBranch bool) {
*cycles += 2 *cycles += 2
if doBranch { if doBranch {
if runningTests && s.PC == relativeAddress { if RunningTests && s.PC == relativeAddress {
fmt.Printf("Trap at $%04x\n", relativeAddress) fmt.Printf("Trap at $%04x\n", relativeAddress)
os.Exit(0) os.Exit(0)
} }
@ -516,15 +554,59 @@ func postProcessIncDec(s *State, cycles *int, addressMode byte) {
} }
} }
func brk(s *State, cycles *int) {
push16(s, s.PC+2)
s.P |= CpuFlagB
push8(s, s.P)
s.P |= CpuFlagI
s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe])
*cycles += 7
}
func irq(s *State, cycles *int) {
push16(s, s.PC)
s.P &= ^CpuFlagB
push8(s, s.P)
s.P |= CpuFlagI
s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe])
*cycles += 7
}
func nmi(s *State, cycles *int) {
push16(s, s.PC)
s.P &= ^CpuFlagB
push8(s, s.P)
s.P |= CpuFlagI
s.PC = uint16(s.Memory[0xfffb])<<8 + uint16(s.Memory[0xfffa])
*cycles += 7
}
func Run(s *State, showInstructions bool, breakAddress *uint16) { func Run(s *State, showInstructions bool, breakAddress *uint16) {
cycles := 0 cycles := 0
for { for {
if runningTests && (s.PC == 0x3869) { if RunningTests && (s.PC == 0x3869) {
fmt.Println("Functional tests passed") fmt.Println("Functional tests passed")
return return
} }
if RunningTests && (s.PC == 0x0af5) {
fmt.Println("Interrupt tests passed")
return
}
if s.pendingInterrupt && ((s.P & CpuFlagI) == 0) {
irq(s, &cycles)
s.pendingInterrupt = false
continue
}
if s.pendingNMI {
nmi(s, &cycles)
s.pendingNMI = false
continue
}
if showInstructions { if showInstructions {
PrintInstruction(s) PrintInstruction(s)
} }
@ -541,7 +623,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
case 0x4c: // JMP $0000 case 0x4c: // JMP $0000
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8 value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
if runningTests && s.PC == value { if RunningTests && s.PC == value {
fmt.Printf("Trap at $%04x\n", value) fmt.Printf("Trap at $%04x\n", value)
os.Exit(0) os.Exit(0)
} }
@ -729,13 +811,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
cycles += 2 cycles += 2
case 0x00: // BRK case 0x00: // BRK
push16(s, s.PC+2) brk(s, &cycles)
s.P |= CpuFlagB
push8(s, s.P)
s.P |= CpuFlagI
s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe])
cycles += 7
case 0x40: // RTI case 0x40: // RTI
s.P = pop8(s) | CpuFlagR s.P = pop8(s) | CpuFlagR
value := pop16(s) value := pop16(s)

View File

@ -1,25 +0,0 @@
package test_cpu
import (
"mos6502go/cpu"
"mos6502go/utils"
"testing"
)
func TestFunctionalTests(*testing.T) {
cpu.InitDisasm()
var s cpu.State
s.Init()
bytes, err := utils.ReadMemoryFromFile("6502_functional_test.bin.gz")
if err != nil {
panic(err)
}
for i := 0; i < len(bytes); i++ {
s.Memory[i] = bytes[i]
}
cpu.Run(&s, false, nil)
}