mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-02-06 07:30:16 +00:00
Changes on the memory abstractions to support memory bank switching
This commit is contained in:
parent
922ae7839e
commit
a28536d561
110
apple2/addressSpace.go
Normal file
110
apple2/addressSpace.go
Normal 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))
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
35
apple2/pagedMemory.go
Normal 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
29
apple2/rxmPage.go
Normal 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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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…
x
Reference in New Issue
Block a user