Extract components from the memory manager

This commit is contained in:
Ivan Izaguirre 2019-03-02 18:33:50 +01:00
parent 55ad73d01d
commit 5b186f9b15
12 changed files with 223 additions and 132 deletions

View File

@ -18,15 +18,15 @@ Those tricks do not work with the Apple2e ROM
*/
type ansiConsoleFrontend struct {
mmu *memoryManager
apple2 *Apple2
keyChannel chan uint8
extraLineFeeds chan int
textUpdated bool
}
func newAnsiConsoleFrontend(mmu *memoryManager) *ansiConsoleFrontend {
func newAnsiConsoleFrontend(a *Apple2) *ansiConsoleFrontend {
var fe ansiConsoleFrontend
fe.mmu = mmu
fe.apple2 = a
fe.subscribeToTextPages()
return &fe
}
@ -36,7 +36,7 @@ func (fe *ansiConsoleFrontend) subscribeToTextPages() {
fe.textUpdated = true
}
for i := 0x04; i < 0x08; i++ {
fe.mmu.physicalMainRAM[i].observer = observer
fe.apple2.mmu.physicalMainRAM[i].observer = observer
}
}
@ -102,13 +102,13 @@ func (fe *ansiConsoleFrontend) textModeGoRoutine() {
// See "Understand the Apple II", page 5-10
// http://www.applelogic.org/files/UNDERSTANDINGTHEAII.pdf
isAltText := fe.mmu.isApple2e && fe.mmu.ioPage.isSoftSwitchExtActive(ioFlagAltChar)
isAltText := fe.apple2.isApple2e && fe.apple2.ioPage.isSoftSwitchExtActive(ioFlagAltChar)
var i, j, h, c uint8
// Top, middle and botton screen
for i = 0; i < 120; i = i + 40 {
// Memory pages
for j = 0x04; j < 0x08; j++ {
p := fe.mmu.physicalMainRAM[j]
p := fe.apple2.mmu.physicalMainRAM[j]
// The two half pages
for _, h = range []uint8{0, 128} {
line := ""

View File

@ -1,19 +1,88 @@
package apple2
import "go6502/core6502"
import (
"bufio"
"go6502/core6502"
"os"
)
// Run instantiates an apple2 and start emulation
func Run(romFile string, log bool) {
mmu := newAddressSpace(romFile)
s := core6502.NewNMOS6502(mmu)
fe := newAnsiConsoleFrontend(mmu)
mmu.ioPage.setKeyboardProvider(fe)
type Apple2 struct {
cpu *core6502.State
mmu *memoryManager
isApple2e bool
ioPage *ioC0Page // 0xc000 to 0xc080
activeSlot int // Slot that has the addressing 0xc800 to 0ccfff
}
// NewApple2 instantiates an apple2
func NewApple2(romFile string) *Apple2 {
var a Apple2
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu)
a.loadRom(romFile)
a.mmu.resetPaging()
// Set the io in 0xc000
a.ioPage = newIoC0Page(&a)
a.mmu.setPage(0xc0, a.ioPage)
return &a
}
func (a *Apple2) Run(log bool) {
// Init frontend
fe := newAnsiConsoleFrontend(a)
a.ioPage.setKeyboardProvider(fe)
go fe.textModeGoRoutine()
// Start the processor
s.Reset()
a.cpu.Reset()
for {
s.ExecuteInstruction(log)
a.cpu.ExecuteInstruction(log)
}
}
// LoadRom loads a binary file to the top of the memory.
const (
apple2RomSize = 12 * 1024
apple2eRomSize = 16 * 1024
)
func (a *Apple2) 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 != apple2RomSize && size != apple2eRomSize {
panic("Rom size not supported")
}
bytes := make([]byte, size)
buf := bufio.NewReader(f)
buf.Read(bytes)
romStart := 0
if size == apple2eRomSize {
// The extra 4kb ROM is first in the rom file.
// It starts with 256 unused bytes not mapped to 0xc000.
a.isApple2e = true
extraRomSize := apple2eRomSize - apple2RomSize
a.mmu.physicalROMe = make([]romPage, extraRomSize>>8)
for i := 0; i < extraRomSize; i++ {
a.mmu.physicalROMe[i>>8].burn(uint8(i), bytes[i])
}
romStart = extraRomSize
}
a.mmu.physicalROM = make([]romPage, apple2RomSize>>8)
for i := 0; i < apple2RomSize; i++ {
a.mmu.physicalROM[i>>8].burn(uint8(i), bytes[i+romStart])
}
}

13
apple2/cardDisk2.go Normal file
View File

@ -0,0 +1,13 @@
package apple2
type disk2 struct {
mmu *memoryManager
slot int
}
func insertCardDisk2(mmu *memoryManager, slot int) disk2 {
var c disk2
c.mmu = mmu
c.slot = slot
return c
}

78
apple2/expansionCard.go Normal file
View File

@ -0,0 +1,78 @@
package apple2
import (
"bufio"
"os"
)
type expansionSlots struct {
mmu *memoryManager
cards [7]expansionCard
}
type expansionCard interface {
insert(es *expansionSlots, slot int) cardBase
activateRom()
}
type cardBase struct {
es *expansionSlots
rom []romPage
slot int
softSwitchesR [16]softSwitchR
softSwitchesW [16]softSwitchW
}
/*
https://applesaucefdc.com/woz/reference2/
http://yesterbits.com/media/pubs/AppleOrchard/articles/disk-ii-part-1-1983-apr.pdf
*/
type cardDisk2 struct {
cardBase
}
func newCardDisk2(filename string) *cardDisk2 {
var c cardDisk2
c.rom = loadCardRom(filename)
return &c
}
func (c *cardBase) insert(es *expansionSlots, slot int) {
c.es = es
c.slot = slot
if c.rom != nil {
//rom = c.rom[0]
}
}
func loadCardRom(filename string) []romPage {
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)
pages := size / 256
if (size % 256) > 0 {
pages++
}
rom := make([]romPage, pages)
for i := int64(0); i < size; i++ {
rom[i>>8].burn(uint8(i), bytes[i])
}
return rom
}

View File

@ -9,7 +9,7 @@ type ioC0Page struct {
softSwitchesW [128]softSwitchW
softSwitchesData [128]uint8
keyboard keyboardProvider
mmu *memoryManager
apple2 *Apple2
}
type softSwitchR func(io *ioC0Page) uint8
@ -27,12 +27,12 @@ const (
ssOff uint8 = 0x00
)
func newIoC0Page(mmu *memoryManager) *ioC0Page {
func newIoC0Page(a *Apple2) *ioC0Page {
var io ioC0Page
io.mmu = mmu
io.apple2 = a
addApple2SoftSwitches(&io)
if mmu.isApple2e {
if a.isApple2e {
addApple2ESoftSwitches(&io)
}

View File

@ -1,30 +1,28 @@
package apple2
import (
"bufio"
"os"
)
// See https://fabiensanglard.net/fd_proxy/prince_of_persia/Inside%20the%20Apple%20IIe.pdf
// See https://i.stack.imgur.com/yn21s.gif
type memoryManager struct {
apple2 *Apple2
// Map of assigned pages
activeMemory *pagedMemory
activeMemory [256]memoryPage
// Pages prepared to be paged in and out
physicalMainRAM []ramPage // 0x0000 to 0xbfff, Up to 48 Kb
physicalROM []romPage // 0xd000 to 0xffff, 12 Kb
physicalROMe []romPage // 0xc000 to 0xcfff, Zero or 4bk in the Apple2e
unassignedExpansionROM []unassignedPage // 0xc000 to 0xcfff
ioPage *ioC0Page // 0xc000 to 0xc080
isApple2e bool
activeSlot int // Slot that has the addressing 0xc800 to 0ccfff
}
// memoryPage is a data page of 256 bytes
type memoryPage interface {
Peek(uint8) uint8
Poke(uint8, uint8)
}
const (
ioAreaMask uint16 = 0xFF80
ioAreaValue uint16 = 0xC000
ioC8Off uint16 = 0xCFFF
ioC8Off uint16 = 0xCFFF
)
// Peek returns the data on the given address
@ -32,7 +30,10 @@ func (mmu *memoryManager) Peek(address uint16) uint8 {
if address == ioC8Off {
mmu.resetSlotExpansionRoms()
}
return mmu.activeMemory.Peek(address)
hi := uint8(address >> 8)
lo := uint8(address)
return mmu.activeMemory[hi].Peek(lo)
}
// Poke sets the data at the given address
@ -40,96 +41,54 @@ func (mmu *memoryManager) Poke(address uint16, value uint8) {
if address == ioC8Off {
mmu.resetSlotExpansionRoms()
}
mmu.activeMemory.Poke(address, value)
hi := uint8(address >> 8)
lo := uint8(address)
mmu.activeMemory[hi].Poke(lo, value)
}
// SetPage assigns a MemoryPage implementation on the page given
func (mmu *memoryManager) setPage(index uint8, page memoryPage) {
//fmt.Printf("Assigning page 0x%02x type %s\n", index, reflect.TypeOf(page))
mmu.activeMemory[index] = page
}
// When 0xcfff is accessed the card expansion rom is unassigned
func (mmu *memoryManager) resetSlotExpansionRoms() {
if mmu.ioPage.isSoftSwitchExtActive(ioFlagIntCxRom) {
if mmu.apple2.ioPage.isSoftSwitchExtActive(ioFlagIntCxRom) {
// Ignore if the Apple2 shadow ROM is active
return
}
for i := 8; i < 16; i++ {
p := mmu.unassignedExpansionROM[i]
mmu.activeMemory.SetPage(uint8(i+0xc0), &p)
mmu.setPage(uint8(i+0xc0), &p)
}
}
func newAddressSpace(romImage string) *memoryManager {
func newMemoryManager(a *Apple2) *memoryManager {
var mmu memoryManager
var m pagedMemory
mmu.activeMemory = &m
mmu.apple2 = a
// Assign RAM from 0x0000 to 0xbfff, 48kb
mmu.physicalMainRAM = make([]ramPage, 0xc0)
for i := 0; i <= 0xbf; i++ {
m.SetPage(uint8(i), &(mmu.physicalMainRAM[i]))
mmu.setPage(uint8(i), &(mmu.physicalMainRAM[i]))
}
mmu.loadRom(romImage)
// Assign the first 12kb of ROM from 0xd000 to 0xfff
for i := 0xd0; i <= 0xff; i++ {
m.SetPage(uint8(i), &(mmu.physicalROM[i-0xd0]))
}
// Set the io in 0xc000
mmu.ioPage = newIoC0Page(&mmu)
m.SetPage(0xc0, mmu.ioPage)
// Set the 0xc100 to 0xcfff as unasigned, 4kb. It wil be taken by slot cards.
mmu.unassignedExpansionROM = make([]unassignedPage, 0x10)
for i := 1; i < 0x10; i++ {
page := uint8(i + 0xc0)
p := &mmu.unassignedExpansionROM[i]
p.page = page
m.SetPage(page, p)
mmu.setPage(page, p)
}
return &mmu
}
// LoadRom loads a binary file to the top of the memory.
const (
apple2RomSize = 12 * 1024
apple2eRomSize = 16 * 1024
)
func (mmu *memoryManager) 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 != apple2RomSize && size != apple2eRomSize {
panic("Rom size not supported")
}
bytes := make([]byte, size)
buf := bufio.NewReader(f)
buf.Read(bytes)
romStart := 0
if size == apple2eRomSize {
// The extra 4kb ROM is first in the rom file.
// It starts with 256 unused bytes not mapped to 0xc000.
mmu.isApple2e = true
extraRomSize := apple2eRomSize - apple2RomSize
mmu.physicalROMe = make([]romPage, extraRomSize>>8)
for i := 0; i < extraRomSize; i++ {
mmu.physicalROMe[i>>8].burn(uint8(i), bytes[i])
}
romStart = extraRomSize
}
mmu.physicalROM = make([]romPage, apple2RomSize>>8)
for i := 0; i < apple2RomSize; i++ {
mmu.physicalROM[i>>8].burn(uint8(i), bytes[i+romStart])
func (mmu *memoryManager) resetPaging() {
// Assign the first 12kb of ROM from 0xd000 to 0xfff
for i := 0xd0; i <= 0xff; i++ {
mmu.setPage(uint8(i), &(mmu.physicalROM[i-0xd0]))
}
}

View File

@ -1,32 +0,0 @@
package apple2
// 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)
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("Assigning page 0x%02x type %s\n", index, reflect.TypeOf(page))
m.data[index] = page
}

BIN
apple2/romdumps/DISK2.rom Normal file

Binary file not shown.

View File

@ -45,15 +45,17 @@ func getSoftSwitchExt(ioFlag uint8, dstValue uint8, action softSwitchExtAction)
}
func softSwitchIntCxRomOn(io *ioC0Page) {
mmu := io.apple2.mmu
for i := uint8(1); i < 16; i++ {
io.mmu.activeMemory.SetPage(uint8(0xc0+i), &io.mmu.physicalROMe[i])
mmu.setPage(uint8(0xc0+i), &mmu.physicalROMe[i])
}
}
func softSwitchIntCxRomOff(io *ioC0Page) {
// TODO restore all the ROM from the slot for 0xc1 to 0xc7
mmu := io.apple2.mmu
for i := 1; i < 16; i++ {
io.mmu.activeMemory.SetPage(uint8(0xc0+i), &io.mmu.unassignedExpansionROM[i])
mmu.setPage(uint8(0xc0+i), &mmu.unassignedExpansionROM[i])
}
}
@ -62,12 +64,14 @@ func softSwitchSlotC3RomOn(io *ioC0Page) {
return // Ignore if allt the Apple2 shadow ROM is active
}
// TODO restore the slot 3 ROM
io.mmu.activeMemory.SetPage(0xC3, &io.mmu.unassignedExpansionROM[3])
mmu := io.apple2.mmu
mmu.setPage(0xC3, &mmu.unassignedExpansionROM[3])
}
func softSwitchSlotC3RomOff(io *ioC0Page) {
if io.isSoftSwitchExtActive(ioFlagIntCxRom) {
return // Ignore if allt the Apple2 shadow ROM is active
}
io.mmu.activeMemory.SetPage(0xC3, &io.mmu.physicalROMe[3])
mmu := io.apple2.mmu
mmu.setPage(0xC3, &mmu.physicalROMe[3])
}

View File

@ -29,5 +29,4 @@ func TestFunctional(t *testing.T) {
}
}
t.Errorf("Tests complited in %v megacycles.\n", s.cycles/1000000)
}

View File

@ -8,5 +8,6 @@ func main() {
//romFile := "apple2/romdumps/Apple2e.rom"
log := false
apple2.Run(romFile, log)
a := apple2.NewApple2(romFile)
a.Run(log)
}