mirror of
https://github.com/freewilll/apple2-go.git
synced 2025-01-21 17:32:09 +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
|
||||
mem.prof
|
||||
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"
|
||||
"fmt"
|
||||
"mos6502go/cpu"
|
||||
"mos6502go/mmu"
|
||||
"mos6502go/utils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cpu.InitDisasm()
|
||||
|
||||
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")
|
||||
flag.Parse()
|
||||
|
||||
cpu.InitDisasm()
|
||||
memory := mmu.InitRAM()
|
||||
|
||||
var Roms = []string{
|
||||
"6502_functional_test.bin.gz",
|
||||
"6502_interrupt_test.bin.gz",
|
||||
@ -32,8 +34,10 @@ func main() {
|
||||
}
|
||||
|
||||
fmt.Printf("Running %s\n", rom)
|
||||
|
||||
var s cpu.State
|
||||
s.Init()
|
||||
s.PC = 0x800
|
||||
cpu.RunningTests = true
|
||||
|
||||
if i == 0 {
|
||||
@ -44,15 +48,28 @@ func main() {
|
||||
cpu.RunningInterruptTests = true
|
||||
}
|
||||
|
||||
bytes, err := utils.ReadMemoryFromFile(rom)
|
||||
bytes, err := utils.ReadMemoryFromGzipFile(rom)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(bytes); i++ {
|
||||
s.Memory[i] = bytes[i]
|
||||
// Copy main RAM area 0x0000-0xbfff
|
||||
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
|
||||
if *breakAddressString != "" {
|
||||
breakAddressValue, err := hex.DecodeString(*breakAddressString)
|
||||
@ -71,7 +88,7 @@ func main() {
|
||||
breakAddress = &foo
|
||||
}
|
||||
|
||||
cpu.Run(&s, *showInstructions, breakAddress)
|
||||
cpu.Run(&s, *showInstructions, breakAddress, false, 0)
|
||||
fmt.Printf("Finished running %s\n\n", rom)
|
||||
}
|
||||
}
|
||||
|
135
cpu/cpu.go
135
cpu/cpu.go
@ -2,6 +2,7 @@ package cpu
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mos6502go/mmu"
|
||||
"os"
|
||||
)
|
||||
|
||||
@ -23,7 +24,8 @@ var (
|
||||
)
|
||||
|
||||
type State struct {
|
||||
Memory [0x10000]uint8
|
||||
Memory *mmu.Memory
|
||||
MemoryMap *mmu.MemoryMap // For easy access, this is a shortcut for Memory.MemoryMap
|
||||
pendingInterrupt bool
|
||||
pendingNMI bool
|
||||
A uint8
|
||||
@ -43,7 +45,6 @@ func (s *State) Init() {
|
||||
s.X = 0
|
||||
s.Y = 0
|
||||
s.P = CpuFlagR | CpuFlagB | CpuFlagZ
|
||||
s.PC = 0x800
|
||||
s.SP = 0xff
|
||||
s.pendingInterrupt = false
|
||||
s.pendingNMI = false
|
||||
@ -102,14 +103,14 @@ func (s *State) isN() bool {
|
||||
}
|
||||
|
||||
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 &= 0xff
|
||||
}
|
||||
|
||||
func push16(s *State, value uint16) {
|
||||
s.Memory[0x100+uint16(s.SP)] = uint8(value >> 8)
|
||||
s.Memory[0xff+uint16(s.SP)] = uint8(value & 0xff)
|
||||
(*s.MemoryMap)[mmu.StackPage][s.SP] = uint8(value >> 8)
|
||||
(*s.MemoryMap)[mmu.StackPage][s.SP-1] = uint8(value & 0xff)
|
||||
s.SP -= 2
|
||||
s.SP &= 0xff
|
||||
}
|
||||
@ -117,24 +118,29 @@ func push16(s *State, value uint16) {
|
||||
func pop8(s *State) uint8 {
|
||||
s.SP += 1
|
||||
s.SP &= 0xff
|
||||
return s.Memory[0x100+uint16(s.SP)]
|
||||
return (*s.MemoryMap)[mmu.StackPage][s.SP]
|
||||
}
|
||||
|
||||
func pop16(s *State) uint16 {
|
||||
s.SP += 2
|
||||
s.SP &= 0xff
|
||||
lsb := uint16(s.Memory[0xff+uint16(s.SP)])
|
||||
msb := uint16(s.Memory[0x100+uint16(s.SP)])
|
||||
msb := uint16((*s.MemoryMap)[mmu.StackPage][s.SP])
|
||||
lsb := uint16((*s.MemoryMap)[mmu.StackPage][s.SP-1])
|
||||
return lsb + msb<<8
|
||||
}
|
||||
|
||||
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
|
||||
func writeInterruptTestOpenCollector(s *State, address uint16, value uint8) {
|
||||
oldValue := s.Memory[address]
|
||||
oldValue := readMemory(s, address)
|
||||
|
||||
oldInterrupt := (oldValue & 0x1) == 0x1
|
||||
oldNMI := (oldValue & 0x2) == 0x2
|
||||
@ -150,7 +156,7 @@ func writeInterruptTestOpenCollector(s *State, address uint16, value uint8) {
|
||||
s.pendingNMI = NMI
|
||||
}
|
||||
|
||||
s.Memory[address] = value
|
||||
(*s.MemoryMap)[uint8(address>>8)][uint8(address&0xff)] = value
|
||||
}
|
||||
|
||||
func writeMemory(s *State, address uint16, value uint8) {
|
||||
@ -159,20 +165,35 @@ func writeMemory(s *State, address uint16, value uint8) {
|
||||
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 {
|
||||
testNumber := s.Memory[0x200]
|
||||
testNumber := readMemory(s, 0x200)
|
||||
if testNumber == 0xf0 {
|
||||
fmt.Println("Opcode testing completed")
|
||||
} 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) {
|
||||
value := s.Memory[s.PC+1]
|
||||
value := readMemory(s, s.PC+1)
|
||||
|
||||
var relativeAddress uint16
|
||||
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) {
|
||||
switch addressMode {
|
||||
case AmZeroPage:
|
||||
result = uint16(s.Memory[s.PC+1])
|
||||
result = uint16(readMemory(s, s.PC+1))
|
||||
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:
|
||||
result = (uint16(s.Memory[s.PC+1]) + uint16(s.Y)) & 0xff
|
||||
result = (uint16(readMemory(s, s.PC+1)) + uint16(s.Y)) & 0xff
|
||||
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:
|
||||
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)
|
||||
result = value + uint16(s.X)
|
||||
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)
|
||||
result = value + uint16(s.Y)
|
||||
case AmIndirectX:
|
||||
zeroPageAddress := (s.Memory[s.PC+1] + s.X) & 0xff
|
||||
result = uint16(s.Memory[zeroPageAddress]) + uint16(s.Memory[zeroPageAddress+1])<<8
|
||||
zeroPageAddress := (readMemory(s, s.PC+1) + s.X) & 0xff
|
||||
result = uint16(readMemory(s, uint16(zeroPageAddress))) + uint16(readMemory(s, uint16(zeroPageAddress)+1))<<8
|
||||
case AmIndirectY:
|
||||
address := s.Memory[s.PC+1]
|
||||
lsb := uint16(s.Memory[address])
|
||||
msb := uint16(s.Memory[address+1])
|
||||
address := uint16(readMemory(s, s.PC+1))
|
||||
lsb := uint16(readMemory(s, address))
|
||||
msb := uint16(readMemory(s, address+1))
|
||||
value := lsb + msb<<8
|
||||
pageBoundaryCrossed = (value & 0xff00) != ((value + uint16(s.Y)) & 0xff00)
|
||||
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) {
|
||||
switch addressMode {
|
||||
case AmImmediate:
|
||||
result = s.Memory[s.PC+1]
|
||||
result = readMemory(s, s.PC+1)
|
||||
s.PC += 2
|
||||
case AmZeroPage:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 2
|
||||
case AmZeroPageX:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 2
|
||||
case AmZeroPageY:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 2
|
||||
case AmAbsolute:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 3
|
||||
case AmAbsoluteX:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 3
|
||||
case AmAbsoluteY:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 3
|
||||
case AmIndirectX:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 2
|
||||
case AmIndirectY:
|
||||
var address uint16
|
||||
address, pageBoundaryCrossed = getAddressFromAddressMode(s, addressMode)
|
||||
result = s.Memory[address]
|
||||
result = readMemory(s, address)
|
||||
s.PC += 2
|
||||
default:
|
||||
result = 0
|
||||
@ -482,7 +503,7 @@ func sbc(s *State, cycles *int, addressMode byte) {
|
||||
}
|
||||
|
||||
func bit(s *State, address uint16) {
|
||||
value := s.Memory[address]
|
||||
value := readMemory(s, address)
|
||||
s.setN(value)
|
||||
s.setV((value & 0x40) != 0)
|
||||
s.setZ(value & s.A)
|
||||
@ -494,14 +515,14 @@ func preProcessShift(s *State, cycles *int, addressMode byte) (address uint16, v
|
||||
value = s.A
|
||||
} else {
|
||||
address, _ = getAddressFromAddressMode(s, addressMode)
|
||||
value = s.Memory[address]
|
||||
value = readMemory(s, address)
|
||||
}
|
||||
|
||||
if addressMode == AmAccumulator {
|
||||
value = s.A
|
||||
} else {
|
||||
address, _ = getAddressFromAddressMode(s, addressMode)
|
||||
value = s.Memory[address]
|
||||
value = readMemory(s, address)
|
||||
}
|
||||
|
||||
return
|
||||
@ -559,7 +580,7 @@ func brk(s *State, cycles *int) {
|
||||
s.P |= CpuFlagB
|
||||
push8(s, s.P)
|
||||
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
|
||||
}
|
||||
|
||||
@ -568,7 +589,7 @@ func irq(s *State, cycles *int) {
|
||||
s.P &= ^CpuFlagB
|
||||
push8(s, s.P)
|
||||
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
|
||||
}
|
||||
|
||||
@ -577,14 +598,18 @@ func nmi(s *State, cycles *int) {
|
||||
s.P &= ^CpuFlagB
|
||||
push8(s, s.P)
|
||||
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
|
||||
}
|
||||
|
||||
func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
func Run(s *State, showInstructions bool, breakAddress *uint16, disableBell bool, wantedCycles int) {
|
||||
cycles := 0
|
||||
|
||||
for {
|
||||
if (wantedCycles != 0) && (cycles >= wantedCycles) {
|
||||
return
|
||||
}
|
||||
|
||||
if RunningTests && (s.PC == 0x3869) {
|
||||
fmt.Println("Functional tests passed")
|
||||
return
|
||||
@ -611,7 +636,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
PrintInstruction(s)
|
||||
}
|
||||
|
||||
opcode := s.Memory[s.PC]
|
||||
opcode := readMemory(s, s.PC)
|
||||
addressMode := OpCodes[opcode].AddressingMode.Mode
|
||||
|
||||
if breakAddress != nil && s.PC == *breakAddress {
|
||||
@ -622,7 +647,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
switch opcode {
|
||||
|
||||
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 {
|
||||
fmt.Printf("Trap at $%04x\n", value)
|
||||
os.Exit(0)
|
||||
@ -630,15 +655,21 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
s.PC = value
|
||||
cycles += 3
|
||||
case 0x6c: // JMP ($0000)
|
||||
value := uint16(s.Memory[s.PC+1]) + uint16(s.Memory[s.PC+2])<<8
|
||||
s.PC = uint16(s.Memory[value]) + uint16(s.Memory[value+1])<<8
|
||||
value := uint16(readMemory(s, s.PC+1)) + uint16(readMemory(s, s.PC+2))<<8
|
||||
s.PC = uint16(readMemory(s, value)) + uint16(readMemory(s, value+1))<<8
|
||||
cycles += 5
|
||||
|
||||
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)
|
||||
s.PC = value
|
||||
cycles += 6
|
||||
|
||||
case 0x60: // RTS
|
||||
value := pop16(s)
|
||||
@ -819,12 +850,12 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
cycles += 6
|
||||
|
||||
case 0x24: // BIT $00
|
||||
address := s.Memory[s.PC+1]
|
||||
address := readMemory(s, s.PC+1)
|
||||
bit(s, uint16(address))
|
||||
s.PC += 2
|
||||
cycles += 3
|
||||
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)
|
||||
s.PC += 3
|
||||
cycles += 4
|
||||
@ -869,7 +900,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
|
||||
case 0xe6, 0xf6, 0xee, 0xfe: // INC
|
||||
address, _ := getAddressFromAddressMode(s, addressMode)
|
||||
value := s.Memory[address]
|
||||
value := readMemory(s, address)
|
||||
value = (value + 1) & 0xff
|
||||
s.setZ(value)
|
||||
s.setN(value)
|
||||
@ -878,7 +909,7 @@ func Run(s *State, showInstructions bool, breakAddress *uint16) {
|
||||
|
||||
case 0xc6, 0xd6, 0xce, 0xde: // DEC
|
||||
address, _ := getAddressFromAddressMode(s, addressMode)
|
||||
value := s.Memory[address]
|
||||
value := readMemory(s, address)
|
||||
value = (value - 1) & 0xff
|
||||
s.setZ(value)
|
||||
s.setN(value)
|
||||
|
@ -37,7 +37,7 @@ func printInstruction(s *State, instruction string) {
|
||||
}
|
||||
|
||||
func PrintInstruction(s *State) {
|
||||
opcodeValue := s.Memory[s.PC]
|
||||
opcodeValue := (*s.MemoryMap)[uint8((s.PC)>>8)][uint8((s.PC)&0xff)]
|
||||
opcode := OpCodes[opcodeValue]
|
||||
mnemonic := opcode.Mnemonic
|
||||
size := opcode.AddressingMode.OperandSize
|
||||
@ -53,7 +53,7 @@ func PrintInstruction(s *State) {
|
||||
var suffix string
|
||||
|
||||
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
|
||||
if (value & 0x80) == 0 {
|
||||
relativeAddress = s.PC + 2 + uint16(value)
|
||||
@ -64,15 +64,15 @@ func PrintInstruction(s *State) {
|
||||
suffix = fmt.Sprintf(stringFormat, relativeAddress)
|
||||
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
|
||||
} 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)
|
||||
opcodes = fmt.Sprintf("%02x %02x ", opcodeValue, value)
|
||||
} else if size == 2 {
|
||||
lower := s.Memory[s.PC+1]
|
||||
higher := s.Memory[s.PC+2]
|
||||
value = uint16(lower) + uint16(higher)*0x100
|
||||
lsb := (*s.MemoryMap)[uint8((s.PC+1)>>8)][uint8((s.PC+1)&0xff)]
|
||||
msb := (*s.MemoryMap)[uint8((s.PC+2)>>8)][uint8((s.PC+2)&0xff)]
|
||||
value = uint16(lsb) + uint16(msb)*0x100
|
||||
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))
|
||||
@ -90,7 +90,7 @@ func DumpMemory(s *State, offset uint16) {
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
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"
|
||||
)
|
||||
|
||||
func ReadMemoryFromFile(filename string) (data []byte, err error) {
|
||||
func ReadMemoryFromGzipFile(filename string) (data []byte, err error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
Loading…
x
Reference in New Issue
Block a user