mirror of
https://github.com/freewilll/apple2-go.git
synced 2025-02-08 13:30:34 +00:00
Added IRQ/BRK/NMI support + tests
This commit is contained in:
parent
4b2a4c3730
commit
d45e1a4aac
1025
6502_interrupt_test.a65
Normal file
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
BIN
6502_interrupt_test.bin.gz
Normal file
Binary file not shown.
1375
6502_interrupt_test.lst
Normal file
1375
6502_interrupt_test.lst
Normal file
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
130
cpu/cpu.go
130
cpu/cpu.go
@ -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)
|
||||||
|
25
cpu_test.go
25
cpu_test.go
@ -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)
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user