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-11-03 17:22:10 +00:00
|
|
|
// Main RAM area: 0x0000 to 0xbfff
|
|
|
|
physicalMainRAM *memoryRange // 0x0000 to 0xbfff, Up to 48 Kb
|
|
|
|
physicalMainRAMAlt *memoryRange // 0x0000 to 0xbfff, Up to 48 Kb. Additional
|
2019-09-28 11:37:42 +00:00
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
// Slots area: 0xc000 to 0xcfff
|
|
|
|
cardsROM [8]memoryHandler //0xcs00 to 0xcsff. 256 bytes for each card
|
|
|
|
cardsROMExtra [8]memoryHandler // 0xc800 to 0xcfff. 2048 bytes for each card
|
|
|
|
physicalROMe memoryHandler // 0xc100 to 0xcfff, Zero or 4kb in the Apple2e
|
|
|
|
|
|
|
|
// Upper area: 0xd000 to 0xffff
|
|
|
|
physicalROM [4]memoryHandler // 0xd000 to 0xffff, 12 Kb. Up to four banks
|
|
|
|
physicalDRAM []memoryHandler // 0xd000 to 0xdfff, 4KB. Up to 8 banks.
|
|
|
|
physicalDAltRAM []memoryHandler // 0xd000 to 0xdfff, 4KB. Up to 8 banks.
|
|
|
|
physicalEFRAM []memoryHandler // 0xe000 to 0xffff, 8KB. Up to 8 banks.
|
|
|
|
|
2019-11-03 23:23:03 +00:00
|
|
|
// Configuration switches, Language cards
|
2019-11-03 17:22:10 +00:00
|
|
|
lcSelectedBlock uint8 // Language card block selected. Usually, allways 0. But Saturn has 8
|
|
|
|
lcActiveRead bool // Upper RAM active for read
|
|
|
|
lcActiveWrite bool // Upper RAM active for read
|
|
|
|
lcAltBank bool // Alternate
|
2019-11-03 23:23:03 +00:00
|
|
|
|
|
|
|
// Configuration switches, Apple //e
|
|
|
|
altZeroPage bool // Use extra RAM from 0x0000 to 0x01ff. And additional language card block
|
|
|
|
altMainRAMActiveRead bool // Use extra RAM from 0x0200 to 0xbfff for read
|
|
|
|
altMainRAMActiveWrite bool // Use extra RAM from 0x0200 to 0xbfff for write
|
2019-11-08 22:56:54 +00:00
|
|
|
store80Active bool // Special pagination for text and graphics areas
|
2019-11-05 23:02:03 +00:00
|
|
|
slotC3ROMActive bool // Apple2e slot 3 ROM shadow
|
|
|
|
intCxROMActive bool // Apple2e slots internal ROM shadow
|
2019-11-03 23:23:03 +00:00
|
|
|
activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff
|
|
|
|
|
|
|
|
// Configuration switches, Base64A
|
|
|
|
romPage uint8 // Active ROM page
|
2019-03-02 17:33:50 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
const (
|
|
|
|
ioC8Off uint16 = 0xcfff
|
2019-11-03 23:23:03 +00:00
|
|
|
addressLimitZero uint16 = 0x01ff
|
2019-11-08 22:56:54 +00:00
|
|
|
addressStartText uint16 = 0x0400
|
|
|
|
addressLimitText uint16 = 0x07ff
|
|
|
|
addressStartHgr uint16 = 0x2000
|
|
|
|
addressLimitHgr uint16 = 0x3fff
|
2019-11-03 17:22:10 +00:00
|
|
|
addressLimitMainRAM uint16 = 0xbfff
|
|
|
|
addressLimitIO uint16 = 0xc0ff
|
|
|
|
addressLimitSlots uint16 = 0xc7ff
|
|
|
|
addressLimitSlotsExtra uint16 = 0xcfff
|
|
|
|
addressLimitDArea uint16 = 0xdfff
|
|
|
|
)
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func newMemoryManager(a *Apple2) *memoryManager {
|
|
|
|
var mmu memoryManager
|
|
|
|
mmu.apple2 = a
|
2019-11-03 23:23:03 +00:00
|
|
|
mmu.physicalMainRAM = newMemoryRange(0, make([]uint8, 0xc000))
|
2019-02-23 23:41:32 +00:00
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
return &mmu
|
|
|
|
}
|
|
|
|
|
2019-11-05 23:02:03 +00:00
|
|
|
func (mmu *memoryManager) accessCArea(address uint16) memoryHandler {
|
|
|
|
if mmu.intCxROMActive {
|
|
|
|
return mmu.physicalROMe
|
|
|
|
}
|
|
|
|
// First slot area
|
|
|
|
if address <= addressLimitSlots {
|
|
|
|
slot := uint8((address >> 8) & 0x07)
|
|
|
|
mmu.activeSlot = slot
|
|
|
|
if !mmu.slotC3ROMActive && (slot == 3) {
|
|
|
|
return mmu.physicalROMe
|
|
|
|
}
|
|
|
|
return mmu.cardsROM[slot]
|
|
|
|
}
|
|
|
|
// Extra slot area
|
|
|
|
if address == ioC8Off {
|
|
|
|
// Reset extra slot area owner
|
|
|
|
mmu.activeSlot = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if !mmu.slotC3ROMActive && (mmu.activeSlot == 3) {
|
|
|
|
return mmu.physicalROMe
|
|
|
|
}
|
|
|
|
return mmu.cardsROMExtra[mmu.activeSlot]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mmu *memoryManager) accessLCArea(address uint16) memoryHandler {
|
|
|
|
block := mmu.lcSelectedBlock
|
|
|
|
if mmu.altZeroPage {
|
|
|
|
block = 1
|
|
|
|
}
|
|
|
|
if address <= addressLimitDArea {
|
|
|
|
if mmu.lcAltBank {
|
|
|
|
return mmu.physicalDAltRAM[block]
|
|
|
|
}
|
|
|
|
return mmu.physicalDRAM[block]
|
|
|
|
}
|
|
|
|
return mmu.physicalEFRAM[block]
|
|
|
|
}
|
|
|
|
|
2019-11-08 22:56:54 +00:00
|
|
|
func (mmu *memoryManager) getPhysicalMainRAM(alt bool) *memoryRange {
|
|
|
|
if alt {
|
|
|
|
return mmu.physicalMainRAMAlt
|
|
|
|
}
|
|
|
|
return mmu.physicalMainRAM
|
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func (mmu *memoryManager) accessRead(address uint16) memoryHandler {
|
2019-11-03 23:23:03 +00:00
|
|
|
if address <= addressLimitZero {
|
2019-11-08 22:56:54 +00:00
|
|
|
return mmu.getPhysicalMainRAM(mmu.altZeroPage)
|
|
|
|
}
|
|
|
|
if mmu.store80Active && address <= addressLimitHgr {
|
|
|
|
altPage := mmu.apple2.io.isSoftSwitchActive(ioFlagSecondPage)
|
|
|
|
if address >= addressStartText && address <= addressLimitText {
|
|
|
|
return mmu.getPhysicalMainRAM(altPage)
|
|
|
|
}
|
|
|
|
hires := mmu.apple2.io.isSoftSwitchActive(ioFlagHiRes)
|
|
|
|
if hires && address >= addressStartHgr && address <= addressLimitHgr {
|
|
|
|
return mmu.getPhysicalMainRAM(altPage)
|
2019-11-03 23:23:03 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-03 17:22:10 +00:00
|
|
|
if address <= addressLimitMainRAM {
|
2019-11-08 22:56:54 +00:00
|
|
|
return mmu.getPhysicalMainRAM(mmu.altMainRAMActiveRead)
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|
2019-11-03 17:22:10 +00:00
|
|
|
if address <= addressLimitIO {
|
|
|
|
return mmu.apple2.io
|
|
|
|
}
|
|
|
|
if address <= addressLimitSlotsExtra {
|
2019-11-05 23:02:03 +00:00
|
|
|
return mmu.accessCArea(address)
|
2019-09-28 11:37:42 +00:00
|
|
|
}
|
2019-11-03 17:22:10 +00:00
|
|
|
if mmu.lcActiveRead {
|
2019-11-05 23:02:03 +00:00
|
|
|
return mmu.accessLCArea(address)
|
2019-09-28 11:37:42 +00:00
|
|
|
}
|
2019-11-03 17:22:10 +00:00
|
|
|
return mmu.physicalROM[mmu.romPage]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mmu *memoryManager) accessWrite(address uint16) memoryHandler {
|
2019-11-03 23:23:03 +00:00
|
|
|
if address <= addressLimitZero {
|
2019-11-08 22:56:54 +00:00
|
|
|
return mmu.getPhysicalMainRAM(mmu.altZeroPage)
|
|
|
|
}
|
|
|
|
if address <= addressLimitHgr && mmu.store80Active {
|
|
|
|
altPage := mmu.apple2.io.isSoftSwitchActive(ioFlagSecondPage)
|
|
|
|
if address >= addressStartText && address <= addressLimitText {
|
|
|
|
return mmu.getPhysicalMainRAM(altPage)
|
|
|
|
}
|
|
|
|
hires := mmu.apple2.io.isSoftSwitchActive(ioFlagHiRes)
|
2019-11-20 21:43:37 +00:00
|
|
|
if hires && address >= addressStartHgr && address <= addressLimitHgr {
|
2019-11-08 22:56:54 +00:00
|
|
|
return mmu.getPhysicalMainRAM(altPage)
|
2019-11-03 23:23:03 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-03 17:22:10 +00:00
|
|
|
if address <= addressLimitMainRAM {
|
2019-11-08 22:56:54 +00:00
|
|
|
return mmu.getPhysicalMainRAM(mmu.altMainRAMActiveWrite)
|
2019-11-03 17:22:10 +00:00
|
|
|
}
|
|
|
|
if address <= addressLimitIO {
|
|
|
|
return mmu.apple2.io
|
|
|
|
}
|
|
|
|
if address <= addressLimitSlotsExtra {
|
2019-11-05 23:02:03 +00:00
|
|
|
return mmu.accessCArea(address)
|
2019-11-03 17:22:10 +00:00
|
|
|
}
|
|
|
|
if mmu.lcActiveWrite {
|
2019-11-05 23:02:03 +00:00
|
|
|
return mmu.accessLCArea(address)
|
2019-11-03 17:22:10 +00:00
|
|
|
}
|
|
|
|
return mmu.physicalROM[mmu.romPage]
|
2019-09-28 11:37:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Peek returns the data on the given address
|
|
|
|
func (mmu *memoryManager) Peek(address uint16) uint8 {
|
2019-11-03 17:22:10 +00:00
|
|
|
mh := mmu.accessRead(address)
|
2019-05-16 20:51:04 +00:00
|
|
|
if mh == nil {
|
2019-11-03 23:23:03 +00:00
|
|
|
//fmt.Printf("Reading void addressing 0x%x\n", address)
|
2019-05-16 20:51:04 +00:00
|
|
|
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-11-03 17:22:10 +00:00
|
|
|
mh := mmu.accessWrite(address)
|
2019-05-16 20:51:04 +00:00
|
|
|
if mh == nil {
|
2019-11-03 23:23:03 +00:00
|
|
|
//fmt.Printf("Writing to void addressing 0x%x\n", address)
|
2019-05-16 20:51:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
mh.poke(address, value)
|
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
// Memory initialization
|
|
|
|
func (mmu *memoryManager) setCardROM(slot int, mh memoryHandler) {
|
|
|
|
mmu.cardsROM[slot] = mh
|
2019-05-16 20:51:04 +00:00
|
|
|
}
|
2019-03-02 17:33:50 +00:00
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func (mmu *memoryManager) setCardROMExtra(slot int, mh memoryHandler) {
|
2019-09-28 11:37:42 +00:00
|
|
|
mmu.cardsROMExtra[slot] = mh
|
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func (mmu *memoryManager) initLanguageRAM(groups int) {
|
|
|
|
mmu.physicalDRAM = make([]memoryHandler, groups)
|
|
|
|
mmu.physicalDAltRAM = make([]memoryHandler, groups)
|
|
|
|
mmu.physicalEFRAM = make([]memoryHandler, groups)
|
|
|
|
for i := 0; i < groups; i++ {
|
|
|
|
mmu.physicalDRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x1000))
|
|
|
|
mmu.physicalDAltRAM[i] = newMemoryRange(0xd000, make([]uint8, 0x1000))
|
|
|
|
mmu.physicalEFRAM[i] = newMemoryRange(0xe000, make([]uint8, 0x2000))
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-03 23:23:03 +00:00
|
|
|
func (mmu *memoryManager) InitRAMalt() {
|
|
|
|
mmu.physicalMainRAMAlt = newMemoryRange(0, make([]uint8, 0xc000))
|
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
// Memory configuration
|
|
|
|
func (mmu *memoryManager) setActiveROMPage(page uint8) {
|
|
|
|
mmu.romPage = page
|
2019-09-28 11:37:42 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func (mmu *memoryManager) getActiveROMPage() uint8 {
|
|
|
|
return mmu.romPage
|
2019-05-17 21:28:20 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func (mmu *memoryManager) setLanguageRAM(readActive bool, writeActive bool, altBank bool) {
|
|
|
|
mmu.lcActiveRead = readActive
|
|
|
|
mmu.lcActiveWrite = writeActive
|
|
|
|
mmu.lcAltBank = altBank
|
2019-05-17 21:28:20 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
func (mmu *memoryManager) setLanguageRAMBlock(block uint8) {
|
|
|
|
block = block % uint8(len(mmu.physicalDRAM))
|
|
|
|
mmu.lcSelectedBlock = block
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|
|
|
|
|
2019-11-03 17:22:10 +00:00
|
|
|
// TODO: complete save and load
|
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-11-03 17:22:10 +00:00
|
|
|
// mmu.activateCardRomExtra(mmu.activeSlot)
|
2019-09-28 11:37:42 +00:00
|
|
|
|
2019-10-05 23:26:00 +00:00
|
|
|
return nil
|
2019-02-23 23:41:32 +00:00
|
|
|
}
|