mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-02-20 23:29:14 +00:00
Extract components from the memory manager
This commit is contained in:
parent
55ad73d01d
commit
5b186f9b15
@ -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 := ""
|
||||
|
@ -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
13
apple2/cardDisk2.go
Normal 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
78
apple2/expansionCard.go
Normal 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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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]))
|
||||
}
|
||||
}
|
||||
|
@ -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
BIN
apple2/romdumps/DISK2.rom
Normal file
Binary file not shown.
@ -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])
|
||||
}
|
||||
|
@ -29,5 +29,4 @@ func TestFunctional(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
t.Errorf("Tests complited in %v megacycles.\n", s.cycles/1000000)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user