Changes on the memory abstractions to support memory bank switching
This commit is contained in:
parent
922ae7839e
commit
a28536d561
|
@ -0,0 +1,110 @@
|
||||||
|
package apple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://i.stack.imgur.com/yn21s.gif
|
||||||
|
|
||||||
|
type addressSpace struct {
|
||||||
|
activeMemory *pagedMemory
|
||||||
|
physicalRAM [256]ramPage // up to 64 Kb
|
||||||
|
physicalROM [48]romPage // up to 12 Kb
|
||||||
|
ioPage ioC0Page
|
||||||
|
textPages1 *textPages
|
||||||
|
activeSlow int // Slot that has the addressing 0xc800 to 0ccfff
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ioAreaMask uint16 = 0xFF80
|
||||||
|
ioAreaValue uint16 = 0xC000
|
||||||
|
ioC8Off uint16 = 0xCFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
// Peek returns the data on the given address
|
||||||
|
func (a *addressSpace) Peek(address uint16) uint8 {
|
||||||
|
if address == ioC8Off {
|
||||||
|
a.resetSlotRoms()
|
||||||
|
}
|
||||||
|
if (address & ioAreaMask) == ioAreaValue {
|
||||||
|
return a.ioPage.Peek(uint8(address))
|
||||||
|
}
|
||||||
|
return a.activeMemory.Peek(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poke sets the data at the given address
|
||||||
|
func (a *addressSpace) Poke(address uint16, value uint8) {
|
||||||
|
if address == ioC8Off {
|
||||||
|
a.resetSlotRoms()
|
||||||
|
}
|
||||||
|
if (address & ioAreaMask) == ioAreaValue {
|
||||||
|
a.ioPage.Poke(uint8(address), value)
|
||||||
|
}
|
||||||
|
a.activeMemory.Poke(address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *addressSpace) resetSlotRoms() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAddressSpace() *addressSpace {
|
||||||
|
var a addressSpace
|
||||||
|
|
||||||
|
var m pagedMemory
|
||||||
|
a.activeMemory = &m
|
||||||
|
|
||||||
|
// Assign RAM from 0x0000 to 0xbfff, 48kb
|
||||||
|
for i := 0; i <= 0xbf; i++ {
|
||||||
|
m.SetPage(uint8(i), &(a.physicalRAM[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign ROM from 0xd000 to 0xfff, 12 kb. The ROM is empty
|
||||||
|
for i := 0xd0; i <= 0xff; i++ {
|
||||||
|
m.SetPage(uint8(i), &(a.physicalROM[i-0xd0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the 0xc000 to 0xcfff as unasigned, 4kb. It wil be taken by slot cards.
|
||||||
|
for i := uint8(0xc0); i <= 0xcf; i++ {
|
||||||
|
var p unassignedPage
|
||||||
|
p.page = i
|
||||||
|
m.SetPage(i, &p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace RAM in the TEXT1 area.
|
||||||
|
// TODO: treat as normal ram. Add is dirty in all RAM pages
|
||||||
|
var t textPages
|
||||||
|
a.textPages1 = &t
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
m.SetPage(uint8(4+i), &(t.pages[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadRom loads a binary file to the top of the memory.
|
||||||
|
func (a *addressSpace) loadRom(filename string) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
stats, statsErr := f.Stat()
|
||||||
|
if statsErr != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := stats.Size()
|
||||||
|
if size != 12288 {
|
||||||
|
panic("Rom size not supported")
|
||||||
|
}
|
||||||
|
bytes := make([]byte, size)
|
||||||
|
|
||||||
|
buf := bufio.NewReader(f)
|
||||||
|
buf.Read(bytes)
|
||||||
|
|
||||||
|
for i, v := range bytes {
|
||||||
|
a.physicalROM[i>>8].burn(uint8(i), uint8(v))
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,30 +4,15 @@ import "go6502/core6502"
|
||||||
|
|
||||||
// Run instantiates an apple2 and start emulation
|
// Run instantiates an apple2 and start emulation
|
||||||
func Run(romFile string, log bool) {
|
func Run(romFile string, log bool) {
|
||||||
|
a := newAddressSpace()
|
||||||
// Setup the Apple ][ address space
|
a.loadRom(romFile)
|
||||||
var m core6502.PagedMemory
|
|
||||||
m.InitWithRAM()
|
|
||||||
m.LoadRom(romFile)
|
|
||||||
var io ioC0Page
|
|
||||||
m.SetPage(0xc0, &io)
|
|
||||||
var t textPages
|
|
||||||
for j := 0; j < 4; j++ {
|
|
||||||
m.SetPage(uint8(4+j), &(t.pages[j]))
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := uint8(0xc1); j < 0xd0; j++ {
|
|
||||||
var p tracePage
|
|
||||||
p.page = j
|
|
||||||
m.SetPage(j, &p)
|
|
||||||
}
|
|
||||||
|
|
||||||
var s core6502.State
|
var s core6502.State
|
||||||
s.Mem = &m
|
s.Mem = a
|
||||||
|
|
||||||
var fe ansiConsoleFrontend
|
var fe ansiConsoleFrontend
|
||||||
io.setKeyboardProvider(&fe)
|
a.ioPage.setKeyboardProvider(&fe)
|
||||||
go fe.textModeGoRoutine(&t)
|
go fe.textModeGoRoutine(a.textPages1)
|
||||||
|
|
||||||
// Start the processor
|
// Start the processor
|
||||||
core6502.Reset(&s)
|
core6502.Reset(&s)
|
||||||
|
|
|
@ -5,9 +5,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type ioC0Page struct {
|
type ioC0Page struct {
|
||||||
ioFlags uint64
|
ioFlags uint64
|
||||||
data [1]uint8
|
data [1]uint8
|
||||||
keyboard keyboardProvider
|
keyboard keyboardProvider
|
||||||
|
addressSpace *addressSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
type keyboardProvider interface {
|
type keyboardProvider interface {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package apple2
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// memoryPage is a data page of 256 bytes
|
||||||
|
type memoryPage interface {
|
||||||
|
Peek(uint8) uint8
|
||||||
|
Poke(uint8, uint8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pagedMemory represents the addressable space of the processor
|
||||||
|
type pagedMemory struct {
|
||||||
|
data [256]memoryPage
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek returns the data on the given address
|
||||||
|
func (m *pagedMemory) Peek(address uint16) uint8 {
|
||||||
|
hi := uint8(address >> 8)
|
||||||
|
lo := uint8(address)
|
||||||
|
return m.data[hi].Peek(lo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poke sets the data at the given address
|
||||||
|
func (m *pagedMemory) Poke(address uint16, value uint8) {
|
||||||
|
hi := uint8(address >> 8)
|
||||||
|
lo := uint8(address)
|
||||||
|
//fmt.Println(hi)
|
||||||
|
m.data[hi].Poke(lo, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPage assigns a MemoryPage implementation on the page given
|
||||||
|
func (m *pagedMemory) SetPage(index uint8, page memoryPage) {
|
||||||
|
fmt.Printf("Seeting page 0x%02x\n", index)
|
||||||
|
m.data[index] = page
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package apple2
|
||||||
|
|
||||||
|
type ramPage struct {
|
||||||
|
data [256]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type romPage struct {
|
||||||
|
data [256]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ramPage) Peek(address uint8) uint8 {
|
||||||
|
return p.data[address]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ramPage) Poke(address uint8, value uint8) {
|
||||||
|
p.data[address] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *romPage) Peek(address uint8) uint8 {
|
||||||
|
return p.data[address]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *romPage) Poke(address uint8, value uint8) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *romPage) burn(address uint8, value uint8) {
|
||||||
|
p.data[address] = value
|
||||||
|
}
|
|
@ -2,17 +2,17 @@ package apple2
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
type tracePage struct {
|
type unassignedPage struct {
|
||||||
page uint8
|
page uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tracePage) Peek(address uint8) uint8 {
|
func (p *unassignedPage) Peek(address uint8) uint8 {
|
||||||
fmt.Printf("Read on address 0x%02x%02x\n", p.page, address)
|
fmt.Printf("Read on address 0x%02x%02x\n", p.page, address)
|
||||||
panic(address)
|
//panic(address)
|
||||||
return 0xcc
|
return 0xcc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tracePage) Poke(address uint8, value uint8) {
|
func (p *unassignedPage) Poke(address uint8, value uint8) {
|
||||||
fmt.Printf("Write on address 0x%02x%02x\n", p.page, address)
|
fmt.Printf("Write on address 0x%02x%02x\n", p.page, address)
|
||||||
panic(address)
|
//panic(address)
|
||||||
}
|
}
|
|
@ -27,6 +27,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
vectorNMI uint16 = 0xfffa
|
||||||
vectorReset uint16 = 0xfffc
|
vectorReset uint16 = 0xfffc
|
||||||
vectorBreak uint16 = 0xfffe
|
vectorBreak uint16 = 0xfffe
|
||||||
)
|
)
|
||||||
|
@ -527,13 +528,21 @@ var opcodes = [256]opcode{
|
||||||
|
|
||||||
func executeLine(s *State, line []uint8) {
|
func executeLine(s *State, line []uint8) {
|
||||||
opcode := opcodes[line[0]]
|
opcode := opcodes[line[0]]
|
||||||
|
if opcode.cycles == 0 {
|
||||||
|
panic(fmt.Sprintf("Unknown opcode 0x%02x\n", line[0]))
|
||||||
|
}
|
||||||
opcode.action(s, line, opcode)
|
opcode.action(s, line, opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteInstruction transforms the state given after a single instruction is executed.
|
// ExecuteInstruction transforms the state given after a single instruction is executed.
|
||||||
func ExecuteInstruction(s *State, log bool) {
|
func ExecuteInstruction(s *State, log bool) {
|
||||||
pc := s.Reg.getPC()
|
pc := s.Reg.getPC()
|
||||||
opcode := opcodes[s.Mem.Peek(pc)]
|
opcodeID := s.Mem.Peek(pc)
|
||||||
|
opcode := opcodes[opcodeID]
|
||||||
|
|
||||||
|
if opcode.cycles == 0 {
|
||||||
|
panic(fmt.Sprintf("Unknown opcode 0x%02x\n", opcodeID))
|
||||||
|
}
|
||||||
|
|
||||||
line := make([]uint8, opcode.bytes)
|
line := make([]uint8, opcode.bytes)
|
||||||
for i := uint8(0); i < opcode.bytes; i++ {
|
for i := uint8(0); i < opcode.bytes; i++ {
|
||||||
|
@ -548,8 +557,9 @@ func ExecuteInstruction(s *State, log bool) {
|
||||||
opcode.action(s, line, opcode)
|
opcode.action(s, line, opcode)
|
||||||
if log {
|
if log {
|
||||||
// Warning: this create double accesses and can interfere on memory mapped I/O
|
// Warning: this create double accesses and can interfere on memory mapped I/O
|
||||||
value, address, _ := resolve(s, line, opcode)
|
//value, address, _ := resolve(s, line, opcode)
|
||||||
fmt.Printf("%v, [%04x:%02x], [%02x]\n", s.Reg, address, value, line)
|
//fmt.Printf("%v, [%04x:%02x], [%02x]\n", s.Reg, address, value, line)
|
||||||
|
fmt.Printf("%v, [%02x]\n", s.Reg, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package core6502
|
|
||||||
|
|
||||||
// MemoryPage is a data page of 256 bytes
|
|
||||||
type MemoryPage interface {
|
|
||||||
Peek(uint8) uint8
|
|
||||||
Poke(uint8, uint8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PagedMemory represents the addressable space of the processor
|
|
||||||
type PagedMemory struct {
|
|
||||||
data [256]MemoryPage
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peek returns the data on the given address
|
|
||||||
func (m *PagedMemory) Peek(address uint16) uint8 {
|
|
||||||
hi := uint8(address >> 8)
|
|
||||||
lo := uint8(address)
|
|
||||||
return m.data[hi].Peek(lo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poke sets the data at the given address
|
|
||||||
func (m *PagedMemory) Poke(address uint16, value uint8) {
|
|
||||||
hi := uint8(address >> 8)
|
|
||||||
lo := uint8(address)
|
|
||||||
//fmt.Println(hi)
|
|
||||||
m.data[hi].Poke(lo, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPage assigns a MemoryPage implementation on the page given
|
|
||||||
func (m *PagedMemory) SetPage(index uint8, page MemoryPage) {
|
|
||||||
m.data[index] = page
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
package core6502
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ramPage struct {
|
|
||||||
data [256]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type romPage struct {
|
|
||||||
data [256]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ramPage) Peek(address uint8) uint8 {
|
|
||||||
return p.data[address]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ramPage) Poke(address uint8, value uint8) {
|
|
||||||
p.data[address] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *romPage) Peek(address uint8) uint8 {
|
|
||||||
return p.data[address]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *romPage) Poke(address uint8, value uint8) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *romPage) burn(address uint8, value uint8) {
|
|
||||||
p.data[address] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitWithRAM adds RAM memory to all the memory pages
|
|
||||||
func (m *PagedMemory) InitWithRAM() {
|
|
||||||
var ramPages [256]ramPage
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
m.SetPage(uint8(i), &ramPages[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *PagedMemory) transformToRom(page uint8) {
|
|
||||||
var romPage romPage
|
|
||||||
address := uint16(page) << 8
|
|
||||||
for i := 0; i < 256; i++ {
|
|
||||||
romPage.burn(uint8(i), m.Peek(address))
|
|
||||||
address++
|
|
||||||
}
|
|
||||||
m.SetPage(page, &romPage)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRom loads a binary file to the top of the memory and makes those pages read only.
|
|
||||||
func (m *PagedMemory) LoadRom(filename string) {
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
stats, statsErr := f.Stat()
|
|
||||||
if statsErr != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
size := stats.Size()
|
|
||||||
bytes := make([]byte, size)
|
|
||||||
|
|
||||||
buf := bufio.NewReader(f)
|
|
||||||
buf.Read(bytes)
|
|
||||||
|
|
||||||
romStart := uint16(0xFFFF - size + 1)
|
|
||||||
fmt.Printf("ROM start in in 0x%04x\n", romStart)
|
|
||||||
|
|
||||||
for i, v := range bytes {
|
|
||||||
m.Poke(uint16(i)+romStart, uint8(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := uint8(romStart >> 8); i != 0; i++ {
|
|
||||||
m.transformToRom(i)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue