Changes on the memory abstractions to support memory bank switching

This commit is contained in:
Ivan Izaguirre 2019-02-22 22:19:08 +01:00
parent 922ae7839e
commit a28536d561
10 changed files with 202 additions and 148 deletions

110
apple2/addressSpace.go Normal file
View File

@ -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))
}
}

View File

@ -4,30 +4,15 @@ import "go6502/core6502"
// Run instantiates an apple2 and start emulation
func Run(romFile string, log bool) {
// Setup the Apple ][ address space
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)
}
a := newAddressSpace()
a.loadRom(romFile)
var s core6502.State
s.Mem = &m
s.Mem = a
var fe ansiConsoleFrontend
io.setKeyboardProvider(&fe)
go fe.textModeGoRoutine(&t)
a.ioPage.setKeyboardProvider(&fe)
go fe.textModeGoRoutine(a.textPages1)
// Start the processor
core6502.Reset(&s)

View File

@ -5,9 +5,10 @@ import (
)
type ioC0Page struct {
ioFlags uint64
data [1]uint8
keyboard keyboardProvider
ioFlags uint64
data [1]uint8
keyboard keyboardProvider
addressSpace *addressSpace
}
type keyboardProvider interface {

35
apple2/pagedMemory.go Normal file
View File

@ -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
}

29
apple2/rxmPage.go Normal file
View File

@ -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
}

View File

@ -2,17 +2,17 @@ package apple2
import "fmt"
type tracePage struct {
type unassignedPage struct {
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)
panic(address)
//panic(address)
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)
panic(address)
//panic(address)
}

View File

@ -27,6 +27,7 @@ const (
)
const (
vectorNMI uint16 = 0xfffa
vectorReset uint16 = 0xfffc
vectorBreak uint16 = 0xfffe
)
@ -527,13 +528,21 @@ var opcodes = [256]opcode{
func executeLine(s *State, line []uint8) {
opcode := opcodes[line[0]]
if opcode.cycles == 0 {
panic(fmt.Sprintf("Unknown opcode 0x%02x\n", line[0]))
}
opcode.action(s, line, opcode)
}
// ExecuteInstruction transforms the state given after a single instruction is executed.
func ExecuteInstruction(s *State, log bool) {
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)
for i := uint8(0); i < opcode.bytes; i++ {
@ -548,8 +557,9 @@ func ExecuteInstruction(s *State, log bool) {
opcode.action(s, line, opcode)
if log {
// Warning: this create double accesses and can interfere on memory mapped I/O
value, address, _ := resolve(s, line, opcode)
fmt.Printf("%v, [%04x:%02x], [%02x]\n", s.Reg, address, value, line)
//value, address, _ := resolve(s, line, opcode)
//fmt.Printf("%v, [%04x:%02x], [%02x]\n", s.Reg, address, value, line)
fmt.Printf("%v, [%02x]\n", s.Reg, line)
}
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -6,7 +6,7 @@ func main() {
romFile := "../roms/apple.rom"
//romFile := "../roms/APPLE2.ROM"
log := false
log := true
apple2.Run(romFile, log)
}