mirror of
https://github.com/freewilll/apple2-go.git
synced 2024-06-12 17:45:38 +00:00
Added MMU and WIP rudimentary apple //e boot test
Basic memory management has been implemented since $c100-$cfff needs flipping with soft switches during Apple //e boot. All memory reads & writes now go through the MMU. Memory is also dynamically allocated and associated with the CPU state.
This commit is contained in:
parent
d45e1a4aac
commit
5d1c25a724
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ test-cpu
|
||||||
cpu.prof
|
cpu.prof
|
||||||
mem.prof
|
mem.prof
|
||||||
mos6502go.test
|
mos6502go.test
|
||||||
|
apple2e.rom
|
||||||
|
|
28
cmd/test-apple-iie-boot.go
Normal file
28
cmd/test-apple-iie-boot.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"mos6502go/cpu"
|
||||||
|
"mos6502go/mmu"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
showInstructions := flag.Bool("show-instructions", false, "Show instructions code while running")
|
||||||
|
disableBell := flag.Bool("disable-bell", false, "Disable bell")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cpu.InitDisasm()
|
||||||
|
memory := mmu.InitRAM()
|
||||||
|
mmu.InitApple2eROM(memory)
|
||||||
|
|
||||||
|
var s cpu.State
|
||||||
|
s.Memory = memory
|
||||||
|
s.MemoryMap = &memory.MemoryMap
|
||||||
|
s.Init()
|
||||||
|
|
||||||
|
bootVector := 0xfffc
|
||||||
|
lsb := (*s.MemoryMap)[uint8(bootVector>>8)][uint8(bootVector&0xff)] // TODO move readMemory to mmu
|
||||||
|
msb := (*s.MemoryMap)[uint8((bootVector+1)>>8)][uint8((bootVector+1)&0xff)]
|
||||||
|
s.PC = uint16(lsb) + uint16(msb)<<8
|
||||||
|
cpu.Run(&s, *showInstructions, nil, *disableBell, 0)
|
||||||
|
}
|
|
@ -5,18 +5,20 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mos6502go/cpu"
|
"mos6502go/cpu"
|
||||||
|
"mos6502go/mmu"
|
||||||
"mos6502go/utils"
|
"mos6502go/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cpu.InitDisasm()
|
|
||||||
|
|
||||||
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")
|
skipTest0 := flag.Bool("skip-functional-test", false, "Skip functional test")
|
||||||
skipTest1 := flag.Bool("skip-interrupt-test", false, "Skip interrupt 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()
|
||||||
|
|
||||||
|
cpu.InitDisasm()
|
||||||
|
memory := mmu.InitRAM()
|
||||||
|
|
||||||
var Roms = []string{
|
var Roms = []string{
|
||||||
"6502_functional_test.bin.gz",
|
"6502_functional_test.bin.gz",
|
||||||
"6502_interrupt_test.bin.gz",
|
"6502_interrupt_test.bin.gz",
|
||||||
|
@ -32,8 +34,10 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Running %s\n", rom)
|
fmt.Printf("Running %s\n", rom)
|
||||||
|
|
||||||
var s cpu.State
|
var s cpu.State
|
||||||
s.Init()
|
s.Init()
|
||||||
|
s.PC = 0x800
|
||||||
cpu.RunningTests = true
|
cpu.RunningTests = true
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
@ -44,15 +48,28 @@ func main() {
|
||||||
cpu.RunningInterruptTests = true
|
cpu.RunningInterruptTests = true
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, err := utils.ReadMemoryFromFile(rom)
|
bytes, err := utils.ReadMemoryFromGzipFile(rom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(bytes); i++ {
|
// Copy main RAM area 0x0000-0xbfff
|
||||||
s.Memory[i] = bytes[i]
|
for i := 0; i < 0xc000; i++ {
|
||||||
|
memory.PhysicalMemory.MainMemory[i] = bytes[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map writable RAM area in 0xc000-0xffff
|
||||||
|
var RomPretendingToBeRAM [0x4000]uint8
|
||||||
|
for i := 0x0; i < 0x4000; i++ {
|
||||||
|
RomPretendingToBeRAM[i] = bytes[0xc000+i]
|
||||||
|
}
|
||||||
|
for i := 0x0; i < 0x40; i++ {
|
||||||
|
memory.MemoryMap[0xc0+uint8(i)] = RomPretendingToBeRAM[i*0x100 : i*0x100+0x100]
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Memory = memory
|
||||||
|
s.MemoryMap = &memory.MemoryMap
|
||||||
|
|
||||||
var breakAddress *uint16
|
var breakAddress *uint16
|
||||||
if *breakAddressString != "" {
|
if *breakAddressString != "" {
|
||||||
breakAddressValue, err := hex.DecodeString(*breakAddressString)
|
breakAddressValue, err := hex.DecodeString(*breakAddressString)
|
||||||
|
@ -71,7 +88,7 @@ func main() {
|
||||||
breakAddress = &foo
|
breakAddress = &foo
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.Run(&s, *showInstructions, breakAddress)
|
cpu.Run(&s, *showInstructions, breakAddress, false, 0)
|
||||||
fmt.Printf("Finished running %s\n\n", rom)
|
fmt.Printf("Finished running %s\n\n", rom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
135
cpu/cpu.go
135
cpu/cpu.go
|
@ -2,6 +2,7 @@ package cpu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"mos6502go/mmu"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,7 +24,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
Memory [0x10000]uint8
|
Memory *mmu.Memory
|
||||||
|
MemoryMap *mmu.MemoryMap // For easy access, this is a shortcut for Memory.MemoryMap
|
||||||
pendingInterrupt bool
|
pendingInterrupt bool
|
||||||
pendingNMI bool
|
pendingNMI bool
|
||||||
A uint8
|
A uint8
|
||||||
|
@ -43,7 +45,6 @@ func (s *State) Init() {
|
||||||
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.SP = 0xff
|
s.SP = 0xff
|
||||||
s.pendingInterrupt = false
|
s.pendingInterrupt = false
|
||||||
s.pendingNMI = false
|
s.pendingNMI = false
|
||||||
|
@ -102,14 +103,14 @@ func (s *State) isN() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func push8(s *State, value uint8) {
|
func push8(s *State, value uint8) {
|
||||||
s.Memory[0x100+uint16(s.SP)] = value
|
(*s.MemoryMap)[mmu.StackPage][s.SP] = value
|
||||||
s.SP -= 1
|
s.SP -= 1
|
||||||
s.SP &= 0xff
|
s.SP &= 0xff
|
||||||
}
|
}
|
||||||
|
|
||||||
func push16(s *State, value uint16) {
|
func push16(s *State, value uint16) {
|
||||||
s.Memory[0x100+uint16(s.SP)] = uint8(value >> 8)
|
(*s.MemoryMap)[mmu.StackPage][s.SP] = uint8(value >> 8)
|
||||||
s.Memory[0xff+uint16(s.SP)] = uint8(value & 0xff)
|
(*s.MemoryMap)[mmu.StackPage][s.SP-1] = uint8(value & 0xff)
|
||||||
s.SP -= 2
|
s.SP -= 2
|
||||||
s.SP &= 0xff
|
s.SP &= 0xff
|
||||||
}
|
}
|
||||||
|
@ -117,24 +118,29 @@ func push16(s *State, value uint16) {
|
||||||
func pop8(s *State) uint8 {
|
func pop8(s *State) uint8 {
|
||||||
s.SP += 1
|
s.SP += 1
|
||||||
s.SP &= 0xff
|
s.SP &= 0xff
|
||||||
return s.Memory[0x100+uint16(s.SP)]
|
return (*s.MemoryMap)[mmu.StackPage][s.SP]
|
||||||
}
|
}
|
||||||
|
|
||||||
func pop16(s *State) uint16 {
|
func pop16(s *State) uint16 {
|
||||||
s.SP += 2
|
s.SP += 2
|
||||||
s.SP &= 0xff
|
s.SP &= 0xff
|
||||||
lsb := uint16(s.Memory[0xff+uint16(s.SP)])
|
msb := uint16((*s.MemoryMap)[mmu.StackPage][s.SP])
|
||||||
msb := uint16(s.Memory[0x100+uint16(s.SP)])
|
lsb := uint16((*s.MemoryMap)[mmu.StackPage][s.SP-1])
|
||||||
return lsb + msb<<8
|
return lsb + msb<<8
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMemory(s *State, address uint16) uint8 {
|
func readMemory(s *State, address uint16) uint8 {
|
||||||
return s.Memory[address]
|
if (address >= 0xc000) && (address < 0xc100) {
|
||||||
|
fmt.Printf("TODO read %04x\n", address)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*s.MemoryMap)[uint8(address>>8)][uint8(address&0xff)]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a write to a magic test address that triggers an interrupt and/or an NMI
|
// Handle a write to a magic test address that triggers an interrupt and/or an NMI
|
||||||
func writeInterruptTestOpenCollector(s *State, address uint16, value uint8) {
|
func writeInterruptTestOpenCollector(s *State, address uint16, value uint8) {
|
||||||
oldValue := s.Memory[address]
|
oldValue := readMemory(s, address)
|
||||||
|
|
||||||
oldInterrupt := (oldValue & 0x1) == 0x1
|
oldInterrupt := (oldValue & 0x1) == 0x1
|
||||||
oldNMI := (oldValue & 0x2) == 0x2
|
oldNMI := (oldValue & 0x2) == 0x2
|
||||||
|
@ -150,7 +156,7 @@ func writeInterruptTestOpenCollector(s *State, address uint16, value uint8) {
|
||||||
s.pendingNMI = NMI
|
s.pendingNMI = NMI
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Memory[address] = value
|
(*s.MemoryMap)[uint8(address>>8)][uint8(address&0xff)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMemory(s *State, address uint16, value uint8) {
|
func writeMemory(s *State, address uint16, value uint8) {
|
||||||
|
@ -159,20 +165,35 @@ func writeMemory(s *State, address uint16, value uint8) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Memory[address] = value
|
if address >= 0xc000 {
|
||||||
|
if address == mmu.CLRCXROM {
|
||||||
|
mmu.MapFirstHalfOfIO(s.Memory)
|
||||||
|
} else if address == mmu.SETCXROM {
|
||||||
|
mmu.MapSecondHalfOfIO(s.Memory)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("TODO write %04x\n", address)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if address >= 0x400 && address < 0x800 {
|
||||||
|
fmt.Printf("Text page write %04x: %02x\n", address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
(*s.MemoryMap)[uint8(address>>8)][uint8(address&0xff)] = value
|
||||||
|
|
||||||
if RunningFunctionalTests && address == 0x200 {
|
if RunningFunctionalTests && address == 0x200 {
|
||||||
testNumber := s.Memory[0x200]
|
testNumber := readMemory(s, 0x200)
|
||||||
if testNumber == 0xf0 {
|
if testNumber == 0xf0 {
|
||||||
fmt.Println("Opcode testing completed")
|
fmt.Println("Opcode testing completed")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Test %d OK\n", s.Memory[0x200])
|
fmt.Printf("Test %d OK\n", readMemory(s, 0x200))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func branch(s *State, cycles *int, instructionName string, doBranch bool) {
|
func branch(s *State, cycles *int, instructionName string, doBranch bool) {
|
||||||
value := s.Memory[s.PC+1]
|
value := readMemory(s, s.PC+1)
|
||||||
|
|
||||||
var relativeAddress uint16
|
var relativeAddress uint16
|
||||||
if (value & 0x80) == 0 {
|
if (value & 0x80) == 0 {
|
||||||
|
@ -203,28 +224,28 @@ func branch(s *State, cycles *int, instructionName string, doBranch bool) {
|
||||||
func getAddressFromAddressMode(s *State, addressMode byte) (result uint16, pageBoundaryCrossed bool) {
|
func getAddressFromAddressMode(s *State, addressMode byte) (result uint16, pageBoundaryCrossed bool) {
|
||||||
switch addressMode {
|
switch addressMode {
|
||||||
case AmZeroPage:
|
case AmZeroPage:
|
||||||
result = uint16(s.Memory[s.PC+1])
|
result = uint16(readMemory(s, s.PC+1))
|
||||||
case AmZeroPageX:
|
case AmZeroPageX:
|
||||||
result = (uint16(s.Memory[s.PC+1]) + uint16(s.X)) & 0xff
|
result = (uint16(readMemory(s, s.PC+1)) + uint16(s.X)) & 0xff
|
||||||
case AmZeroPageY:
|
case AmZeroPageY:
|
||||||
result = (uint16(s.Memory[s.PC+1]) + uint16(s.Y)) & 0xff
|
result = (uint16(readMemory(s, s.PC+1)) + uint16(s.Y)) & 0xff
|
||||||
case AmAbsolute:
|
case AmAbsolute:
|
||||||
result = uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
result = uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||||
case AmAbsoluteX:
|
case AmAbsoluteX:
|
||||||
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
value := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||||
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.X)) & 0xff00)
|
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.X)) & 0xff00)
|
||||||
result = value + uint16(s.X)
|
result = value + uint16(s.X)
|
||||||
case AmAbsoluteY:
|
case AmAbsoluteY:
|
||||||
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
value := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||||
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
|
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
|
||||||
result = value + uint16(s.Y)
|
result = value + uint16(s.Y)
|
||||||
case AmIndirectX:
|
case AmIndirectX:
|
||||||
zeroPageAddress := (s.Memory[s.PC+1] + s.X) & 0xff
|
zeroPageAddress := (readMemory(s, s.PC+1) + s.X) & 0xff
|
||||||
result = uint16(s.Memory[zeroPageAddress]) + uint16(s.Memory[zeroPageAddress+1])<<8
|
result = uint16(readMemory(s, uint16(zeroPageAddress))) + uint16(readMemory(s, uint16(zeroPageAddress)+1))<<8
|
||||||
case AmIndirectY:
|
case AmIndirectY:
|
||||||
address := s.Memory[s.PC+1]
|
address := uint16(readMemory(s, s.PC+1))
|
||||||
lsb := uint16(s.Memory[address])
|
lsb := uint16(readMemory(s, address))
|
||||||
msb := uint16(s.Memory[address+1])
|
msb := uint16(readMemory(s, address+1))
|
||||||
value := lsb + msb<<8
|
value := lsb + msb<<8
|
||||||
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
|
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
|
||||||
result = value + uint16(s.Y)
|
result = value + uint16(s.Y)
|
||||||
|
@ -238,47 +259,47 @@ func getAddressFromAddressMode(s *State, addressMode byte) (result uint16, pageB
|
||||||
func readMemoryWithAddressMode(s *State, addressMode byte) (result uint8, pageBoundaryCrossed bool) {
|
func readMemoryWithAddressMode(s *State, addressMode byte) (result uint8, pageBoundaryCrossed bool) {
|
||||||
switch addressMode {
|
switch addressMode {
|
||||||
case AmImmediate:
|
case AmImmediate:
|
||||||
result = s.Memory[s.PC+1]
|
result = readMemory(s, s.PC+1)
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
case AmZeroPage:
|
case AmZeroPage:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
case AmZeroPageX:
|
case AmZeroPageX:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
case AmZeroPageY:
|
case AmZeroPageY:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
case AmAbsolute:
|
case AmAbsolute:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 3
|
s.PC += 3
|
||||||
case AmAbsoluteX:
|
case AmAbsoluteX:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 3
|
s.PC += 3
|
||||||
case AmAbsoluteY:
|
case AmAbsoluteY:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 3
|
s.PC += 3
|
||||||
case AmIndirectX:
|
case AmIndirectX:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
case AmIndirectY:
|
case AmIndirectY:
|
||||||
var address uint16
|
var address uint16
|
||||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||||
result = s.Memory[address]
|
result = readMemory(s, address)
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
default:
|
default:
|
||||||
result = 0
|
result = 0
|
||||||
|
@ -482,7 +503,7 @@ func sbc(s *State, cycles *int, addressMode byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func bit(s *State, address uint16) {
|
func bit(s *State, address uint16) {
|
||||||
value := s.Memory[address]
|
value := readMemory(s, address)
|
||||||
s.setN(value)
|
s.setN(value)
|
||||||
s.setV((value & 0x40) != 0)
|
s.setV((value & 0x40) != 0)
|
||||||
s.setZ(value & s.A)
|
s.setZ(value & s.A)
|
||||||
|
@ -494,14 +515,14 @@ func preProcessShift(s *State, cycles *int, addressMode byte) (address uint16, v
|
||||||
value = s.A
|
value = s.A
|
||||||
} else {
|
} else {
|
||||||
address, _ = getAddressFromAddressMode(s, addressMode)
|
address, _ = getAddressFromAddressMode(s, addressMode)
|
||||||
value = s.Memory[address]
|
value = readMemory(s, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
if addressMode == AmAccumulator {
|
if addressMode == AmAccumulator {
|
||||||
value = s.A
|
value = s.A
|
||||||
} else {
|
} else {
|
||||||
address, _ = getAddressFromAddressMode(s, addressMode)
|
address, _ = getAddressFromAddressMode(s, addressMode)
|
||||||
value = s.Memory[address]
|
value = readMemory(s, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -559,7 +580,7 @@ func brk(s *State, cycles *int) {
|
||||||
s.P |= CpuFlagB
|
s.P |= CpuFlagB
|
||||||
push8(s, s.P)
|
push8(s, s.P)
|
||||||
s.P |= CpuFlagI
|
s.P |= CpuFlagI
|
||||||
s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe])
|
s.PC = uint16(readMemory(s, 0xffff))<<8 + uint16(readMemory(s, 0xfffe))
|
||||||
*cycles += 7
|
*cycles += 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +589,7 @@ func irq(s *State, cycles *int) {
|
||||||
s.P &= ^CpuFlagB
|
s.P &= ^CpuFlagB
|
||||||
push8(s, s.P)
|
push8(s, s.P)
|
||||||
s.P |= CpuFlagI
|
s.P |= CpuFlagI
|
||||||
s.PC = uint16(s.Memory[0xffff])<<8 + uint16(s.Memory[0xfffe])
|
s.PC = uint16(readMemory(s, 0xffff))<<8 + uint16(readMemory(s, 0xfffe))
|
||||||
*cycles += 7
|
*cycles += 7
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -577,14 +598,18 @@ func nmi(s *State, cycles *int) {
|
||||||
s.P &= ^CpuFlagB
|
s.P &= ^CpuFlagB
|
||||||
push8(s, s.P)
|
push8(s, s.P)
|
||||||
s.P |= CpuFlagI
|
s.P |= CpuFlagI
|
||||||
s.PC = uint16(s.Memory[0xfffb])<<8 + uint16(s.Memory[0xfffa])
|
s.PC = uint16(readMemory(s, 0xfffb))<<8 + uint16(readMemory(s, 0xfffa))
|
||||||
*cycles += 7
|
*cycles += 7
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
func Run(s *State, showInstructions bool, breakAddress *uint16, disableBell bool, wantedCycles int) {
|
||||||
cycles := 0
|
cycles := 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
if (wantedCycles != 0) && (cycles >= wantedCycles) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if RunningTests && (s.PC == 0x3869) {
|
if RunningTests && (s.PC == 0x3869) {
|
||||||
fmt.Println("Functional tests passed")
|
fmt.Println("Functional tests passed")
|
||||||
return
|
return
|
||||||
|
@ -611,7 +636,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||||
PrintInstruction(s)
|
PrintInstruction(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
opcode := s.Memory[s.PC]
|
opcode := readMemory(s, s.PC)
|
||||||
addressMode := OpCodes[opcode].AddressingMode.Mode
|
addressMode := OpCodes[opcode].AddressingMode.Mode
|
||||||
|
|
||||||
if breakAddress != nil && s.PC == *breakAddress {
|
if breakAddress != nil && s.PC == *breakAddress {
|
||||||
|
@ -622,7 +647,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||||
switch opcode {
|
switch opcode {
|
||||||
|
|
||||||
case 0x4c: // JMP $0000
|
case 0x4c: // JMP $0000
|
||||||
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
value := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, 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)
|
||||||
|
@ -630,15 +655,21 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||||
s.PC = value
|
s.PC = value
|
||||||
cycles += 3
|
cycles += 3
|
||||||
case 0x6c: // JMP ($0000)
|
case 0x6c: // JMP ($0000)
|
||||||
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
value := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||||
s.PC = uint16(s.Memory[value]) + uint16(s.Memory[value+1])<<8
|
s.PC = uint16(readMemory(s, value)) + uint16(readMemory(s, value+1))<<8
|
||||||
cycles += 5
|
cycles += 5
|
||||||
|
|
||||||
case 0x20: // JSR $0000
|
case 0x20: // JSR $0000
|
||||||
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
value := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||||
|
cycles += 6
|
||||||
|
|
||||||
|
if disableBell && value == 0xfca8 {
|
||||||
|
s.PC += 3
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
push16(s, s.PC+2)
|
push16(s, s.PC+2)
|
||||||
s.PC = value
|
s.PC = value
|
||||||
cycles += 6
|
|
||||||
|
|
||||||
case 0x60: // RTS
|
case 0x60: // RTS
|
||||||
value := pop16(s)
|
value := pop16(s)
|
||||||
|
@ -819,12 +850,12 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||||
cycles += 6
|
cycles += 6
|
||||||
|
|
||||||
case 0x24: // BIT $00
|
case 0x24: // BIT $00
|
||||||
address := s.Memory[s.PC+1]
|
address := readMemory(s, s.PC+1)
|
||||||
bit(s, uint16(address))
|
bit(s, uint16(address))
|
||||||
s.PC += 2
|
s.PC += 2
|
||||||
cycles += 3
|
cycles += 3
|
||||||
case 0x2C: // BIT $0000
|
case 0x2C: // BIT $0000
|
||||||
address := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
address := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||||
bit(s, address)
|
bit(s, address)
|
||||||
s.PC += 3
|
s.PC += 3
|
||||||
cycles += 4
|
cycles += 4
|
||||||
|
@ -869,7 +900,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||||
|
|
||||||
case 0xe6, 0xf6, 0xee, 0xfe: // INC
|
case 0xe6, 0xf6, 0xee, 0xfe: // INC
|
||||||
address, _ := getAddressFromAddressMode(s, addressMode)
|
address, _ := getAddressFromAddressMode(s, addressMode)
|
||||||
value := s.Memory[address]
|
value := readMemory(s, address)
|
||||||
value = (value + 1) & 0xff
|
value = (value + 1) & 0xff
|
||||||
s.setZ(value)
|
s.setZ(value)
|
||||||
s.setN(value)
|
s.setN(value)
|
||||||
|
@ -878,7 +909,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||||
|
|
||||||
case 0xc6, 0xd6, 0xce, 0xde: // DEC
|
case 0xc6, 0xd6, 0xce, 0xde: // DEC
|
||||||
address, _ := getAddressFromAddressMode(s, addressMode)
|
address, _ := getAddressFromAddressMode(s, addressMode)
|
||||||
value := s.Memory[address]
|
value := readMemory(s, address)
|
||||||
value = (value - 1) & 0xff
|
value = (value - 1) & 0xff
|
||||||
s.setZ(value)
|
s.setZ(value)
|
||||||
s.setN(value)
|
s.setN(value)
|
||||||
|
|
|
@ -37,7 +37,7 @@ func printInstruction(s *State, instruction string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrintInstruction(s *State) {
|
func PrintInstruction(s *State) {
|
||||||
opcodeValue := s.Memory[s.PC]
|
opcodeValue := (*s.MemoryMap)[uint8((s.PC)>>8)][uint8((s.PC)&0xff)]
|
||||||
opcode := OpCodes[opcodeValue]
|
opcode := OpCodes[opcodeValue]
|
||||||
mnemonic := opcode.Mnemonic
|
mnemonic := opcode.Mnemonic
|
||||||
size := opcode.AddressingMode.OperandSize
|
size := opcode.AddressingMode.OperandSize
|
||||||
|
@ -53,7 +53,7 @@ func PrintInstruction(s *State) {
|
||||||
var suffix string
|
var suffix string
|
||||||
|
|
||||||
if opcode.AddressingMode.Mode == AmRelative {
|
if opcode.AddressingMode.Mode == AmRelative {
|
||||||
value = uint16(s.Memory[s.PC+1])
|
value = uint16((*s.MemoryMap)[uint8((s.PC+1)>>8)][uint8((s.PC+1)&0xff)])
|
||||||
var relativeAddress uint16
|
var relativeAddress uint16
|
||||||
if (value & 0x80) == 0 {
|
if (value & 0x80) == 0 {
|
||||||
relativeAddress = s.PC + 2 + uint16(value)
|
relativeAddress = s.PC + 2 + uint16(value)
|
||||||
|
@ -64,15 +64,15 @@ func PrintInstruction(s *State) {
|
||||||
suffix = fmt.Sprintf(stringFormat, relativeAddress)
|
suffix = fmt.Sprintf(stringFormat, relativeAddress)
|
||||||
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
|
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
|
||||||
} else if size == 1 {
|
} else if size == 1 {
|
||||||
value = uint16(s.Memory[s.PC+1])
|
value = uint16((*s.MemoryMap)[uint8((s.PC+1)>>8)][uint8((s.PC+1)&0xff)])
|
||||||
suffix = fmt.Sprintf(stringFormat, value)
|
suffix = fmt.Sprintf(stringFormat, value)
|
||||||
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
|
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
|
||||||
} else if size == 2 {
|
} else if size == 2 {
|
||||||
lower := s.Memory[s.PC+1]
|
lsb := (*s.MemoryMap)[uint8((s.PC+1)>>8)][uint8((s.PC+1)&0xff)]
|
||||||
higher := s.Memory[s.PC+2]
|
msb := (*s.MemoryMap)[uint8((s.PC+2)>>8)][uint8((s.PC+2)&0xff)]
|
||||||
value = uint16(lower) + uint16(higher)*0x100
|
value = uint16(lsb) + uint16(msb)*0x100
|
||||||
suffix = fmt.Sprintf(stringFormat, value)
|
suffix = fmt.Sprintf(stringFormat, value)
|
||||||
opcodes = fmt.Sprintf("%02x %02x %02x ", opcodeValue, lower, higher)
|
opcodes = fmt.Sprintf("%02x %02x %02x ", opcodeValue, lsb, msb)
|
||||||
}
|
}
|
||||||
|
|
||||||
printInstruction(s, fmt.Sprintf("%s %s %s", opcodes, mnemonic, suffix))
|
printInstruction(s, fmt.Sprintf("%s %s %s", opcodes, mnemonic, suffix))
|
||||||
|
@ -90,7 +90,7 @@ func DumpMemory(s *State, offset uint16) {
|
||||||
}
|
}
|
||||||
fmt.Printf("%04x ", offset+i)
|
fmt.Printf("%04x ", offset+i)
|
||||||
}
|
}
|
||||||
fmt.Printf(" %02x", s.Memory[offset+i])
|
fmt.Printf(" %02x", (*s.MemoryMap)[uint8((offset+i)>>8)][uint8((offset+i)&0xff)])
|
||||||
}
|
}
|
||||||
fmt.Print("\n")
|
fmt.Print("\n")
|
||||||
}
|
}
|
||||||
|
|
101
mmu/mmu.go
Normal file
101
mmu/mmu.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package mmu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RomPath = "apple2e.rom"
|
||||||
|
|
||||||
|
const StackPage = 1
|
||||||
|
|
||||||
|
// https://mirrors.apple2.org.za/apple.cabi.net/Languages.Programming/MemoryMap.IIe.64K.128K.txt
|
||||||
|
|
||||||
|
const (
|
||||||
|
KEYBOARD = 0xC000 // keyboard data (latched) (RD-only)
|
||||||
|
CLR80COL = 0xC000 // use 80-column memory mapping (WR-only)
|
||||||
|
SET80COL = 0xC001
|
||||||
|
CLRAUXRD = 0xC002 // read from auxilliary 48K
|
||||||
|
SETAUXRD = 0xC003
|
||||||
|
CLRAUXWR = 0xC004 // write to auxilliary 48K
|
||||||
|
SETAUXWR = 0xC005
|
||||||
|
CLRCXROM = 0xC006 // use external slot ROM
|
||||||
|
SETCXROM = 0xC007
|
||||||
|
CLRAUXZP = 0xC008 // use auxilliary ZP, stack, & LC
|
||||||
|
SETAUXZP = 0xC009
|
||||||
|
CLRC3ROM = 0xC00A // use external slot C3 ROM
|
||||||
|
SETC3ROM = 0xC00B
|
||||||
|
CLR80VID = 0xC00C // use 80-column display mode
|
||||||
|
SET80VID = 0xC00D
|
||||||
|
CLRALTCH = 0xC00E // use alternate character set ROM
|
||||||
|
SETALTCH = 0xC00F
|
||||||
|
STROBE = 0xC010 // strobe (unlatch) keyboard data
|
||||||
|
)
|
||||||
|
|
||||||
|
type PhysicalMemory struct {
|
||||||
|
MainMemory [0xc000]uint8
|
||||||
|
UpperROM [0x3000]uint8
|
||||||
|
RomC1 [0x1000]uint8
|
||||||
|
RomC2 [0x1000]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryMap map[uint8][]uint8
|
||||||
|
|
||||||
|
type Memory struct {
|
||||||
|
MemoryMap MemoryMap
|
||||||
|
PhysicalMemory PhysicalMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapFirstHalfOfIO(m *Memory) {
|
||||||
|
for i := 0x1; i < 0x10; i++ {
|
||||||
|
m.MemoryMap[uint8(i)+0xc0] = m.PhysicalMemory.RomC1[i*0x100 : i*0x100+0x100]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MapSecondHalfOfIO(m *Memory) {
|
||||||
|
for i := 0x1; i < 0x10; i++ {
|
||||||
|
m.MemoryMap[uint8(i)+0xc0] = m.PhysicalMemory.RomC2[i*0x100 : i*0x100+0x100]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readApple2eROM(m *Memory) {
|
||||||
|
bytes, err := ioutil.ReadFile(RomPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to read ROM: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy both I/O areas over c000-cfff, including unused c000-c0ff
|
||||||
|
for i := 0x0000; i < 0x1000; i++ {
|
||||||
|
m.PhysicalMemory.RomC1[i] = bytes[i]
|
||||||
|
m.PhysicalMemory.RomC2[i] = bytes[i+0x4000]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy ROM over for 0xd000-0xffff area
|
||||||
|
for i := 0x0; i < 0x3000; i++ {
|
||||||
|
m.PhysicalMemory.UpperROM[i] = bytes[i+0x1000]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitApple2eROM(m *Memory) {
|
||||||
|
readApple2eROM(m)
|
||||||
|
|
||||||
|
// Map 0xc100-0xcfff
|
||||||
|
MapFirstHalfOfIO(m)
|
||||||
|
|
||||||
|
// Map 0xd000-0xffff
|
||||||
|
for i := 0x0; i < 0x30; i++ {
|
||||||
|
m.MemoryMap[uint8(i)+0xd0] = m.PhysicalMemory.UpperROM[i*0x100 : i*0x100+0x100]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitRAM() (memory *Memory) {
|
||||||
|
memory = new(Memory)
|
||||||
|
memory.MemoryMap = make(MemoryMap)
|
||||||
|
|
||||||
|
// Map main RAM
|
||||||
|
for i := 0x0; i < 0xc0; i++ {
|
||||||
|
memory.MemoryMap[uint8(i)] = memory.PhysicalMemory.MainMemory[i*0x100 : i*0x100+0x100]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ReadMemoryFromFile(filename string) (data []byte, err error) {
|
func ReadMemoryFromGzipFile(filename string) (data []byte, err error) {
|
||||||
f, err := os.Open(filename)
|
f, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Reference in New Issue
Block a user