ROM memory changes and some speed improvements

This commit is contained in:
Ivan Izaguirre 2020-08-14 17:19:24 +02:00
parent 81c77c7700
commit c82e57d895
8 changed files with 109 additions and 42 deletions

View File

@ -21,6 +21,7 @@ func newApple2plus() *Apple2 {
func newApple2e() *Apple2 { func newApple2e() *Apple2 {
var a Apple2 var a Apple2
a.Name = "Apple IIe" a.Name = "Apple IIe"
a.isApple2e = true
a.mmu = newMemoryManager(&a) a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu) a.cpu = core6502.NewNMOS6502(a.mmu)
a.io = newIoC0Page(&a) a.io = newIoC0Page(&a)
@ -34,6 +35,7 @@ func newApple2e() *Apple2 {
func newApple2eEnhanced() *Apple2 { func newApple2eEnhanced() *Apple2 {
var a Apple2 var a Apple2
a.Name = "Apple //e" a.Name = "Apple //e"
a.isApple2e = true
a.mmu = newMemoryManager(&a) a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewCMOS65c02(a.mmu) a.cpu = core6502.NewCMOS65c02(a.mmu)
a.io = newIoC0Page(&a) a.io = newIoC0Page(&a)
@ -82,18 +84,8 @@ func (a *Apple2) LoadRom(filename string) error {
return errors.New("Rom size not supported") return errors.New("Rom size not supported")
} }
romStart := 0 romBase := 0x10000 - size
mmu := a.mmu a.mmu.physicalROM[0] = newMemoryRangeROM(uint16(romBase), data)
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
mmu.physicalROMe = newMemoryRange(0xc000, data[0:extraRomSize])
romStart = extraRomSize
}
mmu.physicalROM[0] = newMemoryRange(0xd000, data[romStart:])
return nil return nil
} }

View File

@ -7,9 +7,9 @@ type card interface {
type cardBase struct { type cardBase struct {
a *Apple2 a *Apple2
romCsxx *memoryRange romCsxx *memoryRangeROM
romC8xx *memoryRange romC8xx *memoryRangeROM
romCxxx *memoryRange romCxxx *memoryRangeROM
slot int slot int
_ssr [16]softSwitchR _ssr [16]softSwitchR
_ssw [16]softSwitchW _ssw [16]softSwitchW
@ -23,16 +23,16 @@ func (c *cardBase) loadRom(data []uint8) {
} }
if len(data) == 0x100 { if len(data) == 0x100 {
// Just 256 bytes in Cs00 // Just 256 bytes in Cs00
c.romCsxx = newMemoryRange(0, data) c.romCsxx = newMemoryRangeROM(0, data)
} else if len(data) == 0x800 { } else if len(data) == 0x800 {
// The file has C800 to C8FF // The file has C800 to C8FF
// The 256 bytes in Cx00 are copied from the first page in C800 // The 256 bytes in Cx00 are copied from the first page in C800
c.romCsxx = newMemoryRange(0, data) c.romCsxx = newMemoryRangeROM(0, data)
c.romC8xx = newMemoryRange(0xc800, data) c.romC8xx = newMemoryRangeROM(0xc800, data)
} else if len(data) == 0x1000 { } else if len(data) == 0x1000 {
// The file covers the full Cxxx range. Only showing the page // The file covers the full Cxxx range. Only showing the page
// corresponding to the slot used. // corresponding to the slot used.
c.romCxxx = newMemoryRange(0xc000, data) c.romCxxx = newMemoryRangeROM(0xc000, data)
} else { } else {
panic("Invalid ROM size") panic("Invalid ROM size")
} }
@ -44,7 +44,8 @@ func (c *cardBase) assign(a *Apple2, slot int) {
if slot != 0 { if slot != 0 {
if c.romCsxx != nil { if c.romCsxx != nil {
// Relocate to the assigned slot // Relocate to the assigned slot
c.romCsxx.base = uint16(0xc000 + slot*0x100) //c.romCsxx.base = uint16(0xc000 + slot*0x100)
c.romCsxx.setBase(uint16(0xc000 + slot*0x100))
a.mmu.setCardROM(slot, c.romCsxx) a.mmu.setCardROM(slot, c.romCsxx)
} }
if c.romC8xx != nil { if c.romC8xx != nil {

View File

@ -71,7 +71,7 @@ func (c *cardInOut) assign(a *Apple2, slot int) {
} }
} }
c.romCsxx = newMemoryRange(0xC200, data[0:255]) c.romCsxx = newMemoryRangeROM(0xC200, data[0:255])
if slot != 2 { if slot != 2 {
// To make ifwork on other slots, patch C2, A0 and A1 // To make ifwork on other slots, patch C2, A0 and A1

View File

@ -46,12 +46,7 @@ func (c *cardLanguage) assign(a *Apple2, slot int) {
c.writeState = lcWriteEnabled c.writeState = lcWriteEnabled
c.altBank = true // Start on bank2 c.altBank = true // Start on bank2
if a.isApple2e { a.mmu.initLanguageRAM(1)
// The Apple //e with 128kb has two blocks of language upper RAM
a.mmu.initLanguageRAM(2)
} else {
a.mmu.initLanguageRAM(1)
}
for i := uint8(0x0); i <= 0xf; i++ { for i := uint8(0x0); i <= 0xf; i++ {
iCopy := i iCopy := i
c.addCardSoftSwitchR(iCopy, func(*ioC0Page) uint8 { c.addCardSoftSwitchR(iCopy, func(*ioC0Page) uint8 {

View File

@ -54,7 +54,7 @@ func (s *State) executeLine(line []uint8) {
// ExecuteInstruction transforms the state given after a single instruction is executed. // ExecuteInstruction transforms the state given after a single instruction is executed.
func (s *State) ExecuteInstruction() { func (s *State) ExecuteInstruction() {
pc := s.reg.getPC() pc := s.reg.getPC()
opcodeID := s.mem.Peek(pc) opcodeID := s.mem.PeekCode(pc)
opcode := s.opcodes[opcodeID] opcode := s.opcodes[opcodeID]
if opcode.cycles == 0 { if opcode.cycles == 0 {
@ -65,7 +65,7 @@ func (s *State) ExecuteInstruction() {
s.lineCache = make([]uint8, maxInstructionSize) s.lineCache = make([]uint8, maxInstructionSize)
} }
for i := uint16(0); i < opcode.bytes; i++ { for i := uint16(0); i < opcode.bytes; i++ {
s.lineCache[i] = s.mem.Peek(pc) s.lineCache[i] = s.mem.PeekCode(pc)
pc++ pc++
} }
s.reg.setPC(pc) s.reg.setPC(pc)

View File

@ -6,6 +6,10 @@ import "io/ioutil"
type Memory interface { type Memory interface {
Peek(address uint16) uint8 Peek(address uint16) uint8
Poke(address uint16, value uint8) Poke(address uint16, value uint8)
// PeekCode can bu used to optimize the memory manager to requests with more
// locality. It must return the same as a call to Peek()
PeekCode(address uint16) uint8
} }
func getWord(m Memory, address uint16) uint16 { func getWord(m Memory, address uint16) uint16 {

View File

@ -12,10 +12,9 @@ type memoryManager struct {
// Slots area: 0xc000 to 0xcfff // Slots area: 0xc000 to 0xcfff
cardsROM [8]memoryHandler //0xcs00 to 0xcSff. 256 bytes for each card cardsROM [8]memoryHandler //0xcs00 to 0xcSff. 256 bytes for each card
cardsROMExtra [8]memoryHandler // 0xc800 to 0xcfff. 2048 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 ROM: 0xd000 to 0xffff // Upper area ROM: 0xc000 to 0xffff (or 0xd000 to 0xffff on the II+)
physicalROM [4]memoryHandler // 0xd000 to 0xffff, 12 Kb. Up to four physicalROM [4]memoryHandler // 0xc000 (or 0xd000) to 0xffff, 16 (or 12) Kb. Up to four banks
// Language card upper area RAM: 0xd000 to 0xffff. One bank for regular LC cards, up to 8 with Saturn // Language card upper area RAM: 0xd000 to 0xffff. One bank for regular LC cards, up to 8 with Saturn
physicalLangRAM []*memoryRange // 0xd000 to 0xffff, 12KB. Up to 8 banks. physicalLangRAM []*memoryRange // 0xd000 to 0xffff, 12KB. Up to 8 banks.
@ -43,6 +42,10 @@ type memoryManager struct {
// Configuration switches, Base64A // Configuration switches, Base64A
romPage uint8 // Active ROM page romPage uint8 // Active ROM page
// Resolution cache
lastAddressPage uint16 // The first byte is the page. The second is zero when the cached is valid.
lastAddressHandler memoryHandler
} }
const ( const (
@ -57,6 +60,8 @@ const (
addressLimitSlots uint16 = 0xc7ff addressLimitSlots uint16 = 0xc7ff
addressLimitSlotsExtra uint16 = 0xcfff addressLimitSlotsExtra uint16 = 0xcfff
addressLimitDArea uint16 = 0xdfff addressLimitDArea uint16 = 0xdfff
invalidAddressPage uint16 = 0x0001
) )
type memoryHandler interface { type memoryHandler interface {
@ -74,14 +79,14 @@ func newMemoryManager(a *Apple2) *memoryManager {
func (mmu *memoryManager) accessCArea(address uint16) memoryHandler { func (mmu *memoryManager) accessCArea(address uint16) memoryHandler {
if mmu.intCxROMActive { if mmu.intCxROMActive {
return mmu.physicalROMe return mmu.physicalROM[mmu.romPage]
} }
// First slot area // First slot area
if address <= addressLimitSlots { if address <= addressLimitSlots {
slot := uint8((address >> 8) & 0x07) slot := uint8((address >> 8) & 0x07)
mmu.activeSlot = slot mmu.activeSlot = slot
if !mmu.slotC3ROMActive && (slot == 3) { if !mmu.slotC3ROMActive && (slot == 3) {
return mmu.physicalROMe return mmu.physicalROM[mmu.romPage]
} }
return mmu.cardsROM[slot] return mmu.cardsROM[slot]
} }
@ -89,10 +94,11 @@ func (mmu *memoryManager) accessCArea(address uint16) memoryHandler {
if address == ioC8Off { if address == ioC8Off {
// Reset extra slot area owner // Reset extra slot area owner
mmu.activeSlot = 0 mmu.activeSlot = 0
mmu.lastAddressPage = invalidAddressPage
} }
if !mmu.slotC3ROMActive && (mmu.activeSlot == 3) { if !mmu.slotC3ROMActive && (mmu.activeSlot == 3) {
return mmu.physicalROMe return mmu.physicalROM[mmu.romPage]
} }
return mmu.cardsROMExtra[mmu.activeSlot] return mmu.cardsROMExtra[mmu.activeSlot]
} }
@ -130,6 +136,23 @@ func (mmu *memoryManager) getVideoRAM(ext bool) *memoryRange {
return mmu.physicalMainRAM return mmu.physicalMainRAM
} }
func (mmu *memoryManager) accessReadCached(address uint16) memoryHandler {
page := address & 0xff00
if address&0xff00 == mmu.lastAddressPage {
//fmt.Printf(" hit %v\n", mmu.apple2.cpu.GetCycles())
return mmu.lastAddressHandler
}
//fmt.Printf("Not hit %v\n", mmu.apple2.cpu.GetCycles())
mh := mmu.accessRead(address)
if address&0xf000 != 0xc000 {
// Do not cache 0xC area as it may reconfigure the MMU
mmu.lastAddressPage = page
mmu.lastAddressHandler = mh
}
return mh
}
func (mmu *memoryManager) accessRead(address uint16) memoryHandler { func (mmu *memoryManager) accessRead(address uint16) memoryHandler {
if address <= addressLimitZero { if address <= addressLimitZero {
return mmu.getPhysicalMainRAM(mmu.altZeroPage) return mmu.getPhysicalMainRAM(mmu.altZeroPage)
@ -148,6 +171,7 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler {
return mmu.getPhysicalMainRAM(mmu.altMainRAMActiveRead) return mmu.getPhysicalMainRAM(mmu.altMainRAMActiveRead)
} }
if address <= addressLimitIO { if address <= addressLimitIO {
mmu.lastAddressPage = invalidAddressPage
return mmu.apple2.io return mmu.apple2.io
} }
if address <= addressLimitSlotsExtra { if address <= addressLimitSlotsExtra {
@ -192,7 +216,27 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler {
func (mmu *memoryManager) Peek(address uint16) uint8 { func (mmu *memoryManager) Peek(address uint16) uint8 {
mh := mmu.accessRead(address) mh := mmu.accessRead(address)
if mh == nil { if mh == nil {
//fmt.Printf("Reading void addressing 0x%x\n", address) return 0xf4 // Or some random number
}
return mh.peek(address)
}
// Peek returns the data on the given address optimized for more local requests
func (mmu *memoryManager) PeekCode(address uint16) uint8 {
page := address & 0xff00
var mh memoryHandler
if page == mmu.lastAddressPage {
mh = mmu.lastAddressHandler
} else {
mh = mmu.accessRead(address)
if address&0xf000 != 0xc000 {
// Do not cache 0xC area as it may reconfigure the MMU
mmu.lastAddressPage = page
mmu.lastAddressHandler = mh
}
}
if mh == nil {
return 0xf4 // Or some random number return 0xf4 // Or some random number
} }
return mh.peek(address) return mh.peek(address)
@ -201,11 +245,9 @@ func (mmu *memoryManager) Peek(address uint16) uint8 {
// Poke sets the data at the given address // Poke sets the data at the given address
func (mmu *memoryManager) Poke(address uint16, value uint8) { func (mmu *memoryManager) Poke(address uint16, value uint8) {
mh := mmu.accessWrite(address) mh := mmu.accessWrite(address)
if mh == nil { if mh != nil {
//fmt.Printf("Writing to void addressing 0x%x\n", address) mh.poke(address, value)
return
} }
mh.poke(address, value)
} }
// Memory initialization // Memory initialization

View File

@ -1,19 +1,48 @@
package apple2 package apple2
type memoryRange struct { type memoryRange struct {
base uint16 base uint16
data []uint8 data []uint8
basePtr uintptr
}
type memoryRangeROM struct {
memoryRange
} }
func newMemoryRange(base uint16, data []uint8) *memoryRange { func newMemoryRange(base uint16, data []uint8) *memoryRange {
var m memoryRange var m memoryRange
m.base = base m.base = base
m.data = data m.data = data
m.setBase(base)
return &m return &m
} }
func newMemoryRangeROM(base uint16, data []uint8) *memoryRangeROM {
var m memoryRangeROM
m.base = base
m.data = data
m.setBase(base)
return &m
}
func (m *memoryRange) setBase(base uint16) {
m.base = base
//p := unsafe.Pointer(&m.data[0])
//m.basePtr = (uintptr)(p) - (uintptr)(base)
}
func (m *memoryRange) peek(address uint16) uint8 { func (m *memoryRange) peek(address uint16) uint8 {
// Safe version:
return m.data[address-m.base] return m.data[address-m.base]
// Really overkill
// go-vet warns the caching of basePtr
// This wouldn't have a warning
// indexp := unsafe.Pointer((uintptr)(unsafe.Pointer(&m.data[0])) - (uintptr)(m.base) + uintptr(address))
// But it makes sense to precalculate that
//indexp := unsafe.Pointer(m.basePtr + uintptr(address))
//return *(*uint8)(indexp)
} }
func (m *memoryRange) poke(address uint16, value uint8) { func (m *memoryRange) poke(address uint16, value uint8) {
@ -23,3 +52,7 @@ func (m *memoryRange) poke(address uint16, value uint8) {
func (m *memoryRange) subRange(a, b uint16) []uint8 { func (m *memoryRange) subRange(a, b uint16) []uint8 {
return m.data[a-m.base : b-m.base] return m.data[a-m.base : b-m.base]
} }
func (m *memoryRangeROM) poke(address uint16, value uint8) {
// Ignore
}