2019-02-23 23:41:32 +00:00
|
|
|
package apple2
|
|
|
|
|
2019-05-16 20:55:19 +00:00
|
|
|
import (
|
2019-09-28 11:37:42 +00:00
|
|
|
"encoding/binary"
|
2019-05-17 21:28:20 +00:00
|
|
|
"io"
|
2019-05-16 20:55:19 +00:00
|
|
|
)
|
2019-05-16 20:51:04 +00:00
|
|
|
|
2019-02-23 23:41:32 +00:00
|
|
|
// 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 {
|
2019-03-02 17:33:50 +00:00
|
|
|
apple2 *Apple2
|
2019-02-23 23:41:32 +00:00
|
|
|
// Map of assigned pages
|
2019-05-16 20:51:04 +00:00
|
|
|
activeMemoryRead [256]memoryHandler
|
|
|
|
activeMemoryWrite [256]memoryHandler
|
2019-03-02 17:33:50 +00:00
|
|
|
|
2019-02-23 23:41:32 +00:00
|
|
|
// Pages prepared to be paged in and out
|
2019-09-28 11:37:42 +00:00
|
|
|
physicalMainRAM *memoryRange // 0x0000 to 0xbfff, Up to 48 Kb
|
|
|
|
physicalROM memoryHandler // 0xd000 to 0xffff, 12 Kb
|
|
|
|
physicalROMe memoryHandler // 0xc000 to 0xcfff, Zero or 4bk in the Apple2e
|
|
|
|
|
|
|
|
// Pages prapared for optional card ROM banks
|
|
|
|
activeSlot uint8
|
|
|
|
cardsROMExtra [8]memoryHandler // 0xc800 to 0xcfff. for each card
|
2019-03-02 17:33:50 +00:00
|
|
|
}
|
|
|
|
|
2019-05-16 20:51:04 +00:00
|
|
|
type memoryHandler interface {
|
|
|
|
peek(uint16) uint8
|
|
|
|
poke(uint16, uint8)
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2019-03-02 17:33:50 +00:00
|
|
|
ioC8Off uint16 = 0xCFFF
|
2019-02-23 23:41:32 +00:00
|
|
|
)
|
|
|
|
|
2019-10-19 16:41:10 +00:00
|
|
|
func (mmu *memoryManager) access(address uint16, activeMemory *[256]memoryHandler) memoryHandler {
|
2019-02-23 23:41:32 +00:00
|
|
|
if address == ioC8Off {
|
|
|
|
mmu.resetSlotExpansionRoms()
|
|
|
|
}
|
2019-03-02 17:33:50 +00:00
|
|
|
|
|
|
|
hi := uint8(address >> 8)
|
2019-09-28 11:37:42 +00:00
|
|
|
if hi >= 0xC1 && hi <= 0xC7 {
|
|
|
|
slot := hi - 0xC0
|
|
|
|
if slot != mmu.activeSlot {
|
|
|
|
mmu.activateCardRomExtra(slot)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mh := activeMemory[hi]
|
|
|
|
if mh == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return mh
|
|
|
|
}
|
|
|
|
|
|
|
|
// Peek returns the data on the given address
|
|
|
|
func (mmu *memoryManager) Peek(address uint16) uint8 {
|
2019-10-19 16:41:10 +00:00
|
|
|
mh := mmu.access(address, &mmu.activeMemoryRead)
|
2019-05-16 20:51:04 +00:00
|
|
|
if mh == nil {
|
|
|
|
return 0xf4 // Or some random number
|
|
|
|
}
|
|
|
|
return mh.peek(address)
|
2019-04-26 16:08:30 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 23:41:32 +00:00
|
|
|
// Poke sets the data at the given address
|
|
|
|
func (mmu *memoryManager) Poke(address uint16, value uint8) {
|
2019-10-19 16:41:10 +00:00
|
|
|
mh := mmu.access(address, &mmu.activeMemoryWrite)
|
2019-05-16 20:51:04 +00:00
|
|
|
if mh == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
mh.poke(address, value)
|
|
|
|
}
|
|
|
|
|
2019-05-16 20:55:19 +00:00
|
|
|
func (mmu *memoryManager) setPages(begin uint8, end uint8, mh memoryHandler) {
|
|
|
|
mmu.setPagesRead(begin, end, mh)
|
|
|
|
mmu.setPagesWrite(begin, end, mh)
|
2019-03-02 17:33:50 +00:00
|
|
|
}
|
|
|
|
|
2019-05-16 20:55:19 +00:00
|
|
|
func (mmu *memoryManager) setPagesRead(begin uint8, end uint8, mh memoryHandler) {
|
|
|
|
i := begin
|
|
|
|
for {
|
|
|
|
mmu.activeMemoryRead[i] = mh
|
|
|
|
if i == end {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
2019-05-16 20:51:04 +00:00
|
|
|
}
|
2019-03-02 17:33:50 +00:00
|
|
|
|
2019-05-16 20:55:19 +00:00
|
|
|
func (mmu *memoryManager) setPagesWrite(begin uint8, end uint8, mh memoryHandler) {
|
|
|
|
i := begin
|
|
|
|
for {
|
|
|
|
mmu.activeMemoryWrite[i] = mh
|
|
|
|
if i == end {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|
|
|
|
|
2019-09-28 11:37:42 +00:00
|
|
|
func (mmu *memoryManager) prepareCardExtraRom(slot int, mh memoryHandler) {
|
|
|
|
mmu.cardsROMExtra[slot] = mh
|
|
|
|
}
|
|
|
|
|
2019-02-23 23:41:32 +00:00
|
|
|
// When 0xcfff is accessed the card expansion rom is unassigned
|
|
|
|
func (mmu *memoryManager) resetSlotExpansionRoms() {
|
2019-04-21 19:04:02 +00:00
|
|
|
if mmu.apple2.io.isSoftSwitchActive(ioFlagIntCxRom) {
|
2019-02-23 23:41:32 +00:00
|
|
|
// Ignore if the Apple2 shadow ROM is active
|
|
|
|
return
|
|
|
|
}
|
2019-09-28 11:37:42 +00:00
|
|
|
mmu.activeSlot = 0
|
2019-05-16 20:55:19 +00:00
|
|
|
mmu.setPagesRead(0xc8, 0xcf, nil)
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|
|
|
|
|
2019-09-28 11:37:42 +00:00
|
|
|
// When a card base ROM is accesed the extra rom is assigned if available
|
|
|
|
func (mmu *memoryManager) activateCardRomExtra(slot uint8) {
|
|
|
|
//fmt.Printf("Activate slot %v\n", slot)
|
|
|
|
if mmu.cardsROMExtra[slot] != nil {
|
|
|
|
mmu.setPagesRead(0xC8, 0xCF, mmu.cardsROMExtra[slot])
|
|
|
|
}
|
|
|
|
mmu.activeSlot = slot
|
|
|
|
}
|
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
func (mmu *memoryManager) resetRomPaging() {
|
|
|
|
// Assign the first 12kb of ROM from 0xd000 to 0xffff
|
|
|
|
mmu.setPagesRead(0xd0, 0xff, mmu.physicalROM)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mmu *memoryManager) resetBaseRamPaging() {
|
|
|
|
// Assign the base RAM from 0x0000 to 0xbfff
|
|
|
|
mmu.setPages(0x00, 0xbf, mmu.physicalMainRAM)
|
|
|
|
}
|
|
|
|
|
2019-06-07 18:01:20 +00:00
|
|
|
func newMemoryManager(a *Apple2) *memoryManager {
|
2019-02-23 23:41:32 +00:00
|
|
|
var mmu memoryManager
|
2019-03-02 17:33:50 +00:00
|
|
|
mmu.apple2 = a
|
2019-02-23 23:41:32 +00:00
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
ram := make([]uint8, 0xc000) // Reserve 48kb
|
2019-05-16 20:51:04 +00:00
|
|
|
mmu.physicalMainRAM = newMemoryRange(0, ram)
|
2019-05-17 21:28:20 +00:00
|
|
|
mmu.resetBaseRamPaging()
|
2019-02-23 23:41:32 +00:00
|
|
|
|
|
|
|
return &mmu
|
|
|
|
}
|
|
|
|
|
2019-10-05 23:26:00 +00:00
|
|
|
func (mmu *memoryManager) save(w io.Writer) error {
|
|
|
|
err := mmu.physicalMainRAM.save(w)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = binary.Write(w, binary.BigEndian, mmu.activeSlot)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
2019-05-17 21:28:20 +00:00
|
|
|
}
|
|
|
|
|
2019-10-05 23:26:00 +00:00
|
|
|
func (mmu *memoryManager) load(r io.Reader) error {
|
|
|
|
err := mmu.physicalMainRAM.load(r)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
err = binary.Read(r, binary.BigEndian, &mmu.activeSlot)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-09-28 11:37:42 +00:00
|
|
|
mmu.activateCardRomExtra(mmu.activeSlot)
|
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
mmu.resetBaseRamPaging()
|
2019-10-05 23:26:00 +00:00
|
|
|
return nil
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|