From c82e57d895f55d40ab49a44118cb21650e43ebcc Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Fri, 14 Aug 2020 17:19:24 +0200 Subject: [PATCH] ROM memory changes and some speed improvements --- apple2Setup.go | 16 +++--------- cardBase.go | 17 ++++++------ cardInOut.go | 2 +- cardLanguage.go | 7 +---- core6502/execute.go | 4 +-- core6502/memory.go | 4 +++ memoryManager.go | 64 +++++++++++++++++++++++++++++++++++++-------- memoryRange.go | 37 ++++++++++++++++++++++++-- 8 files changed, 109 insertions(+), 42 deletions(-) diff --git a/apple2Setup.go b/apple2Setup.go index e57ab7a..febecab 100644 --- a/apple2Setup.go +++ b/apple2Setup.go @@ -21,6 +21,7 @@ func newApple2plus() *Apple2 { func newApple2e() *Apple2 { var a Apple2 a.Name = "Apple IIe" + a.isApple2e = true a.mmu = newMemoryManager(&a) a.cpu = core6502.NewNMOS6502(a.mmu) a.io = newIoC0Page(&a) @@ -34,6 +35,7 @@ func newApple2e() *Apple2 { func newApple2eEnhanced() *Apple2 { var a Apple2 a.Name = "Apple //e" + a.isApple2e = true a.mmu = newMemoryManager(&a) a.cpu = core6502.NewCMOS65c02(a.mmu) a.io = newIoC0Page(&a) @@ -82,18 +84,8 @@ func (a *Apple2) LoadRom(filename string) error { return errors.New("Rom size not supported") } - romStart := 0 - mmu := a.mmu - 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:]) + romBase := 0x10000 - size + a.mmu.physicalROM[0] = newMemoryRangeROM(uint16(romBase), data) return nil } diff --git a/cardBase.go b/cardBase.go index 3c54169..7c1cdaf 100644 --- a/cardBase.go +++ b/cardBase.go @@ -7,9 +7,9 @@ type card interface { type cardBase struct { a *Apple2 - romCsxx *memoryRange - romC8xx *memoryRange - romCxxx *memoryRange + romCsxx *memoryRangeROM + romC8xx *memoryRangeROM + romCxxx *memoryRangeROM slot int _ssr [16]softSwitchR _ssw [16]softSwitchW @@ -23,16 +23,16 @@ func (c *cardBase) loadRom(data []uint8) { } if len(data) == 0x100 { // Just 256 bytes in Cs00 - c.romCsxx = newMemoryRange(0, data) + c.romCsxx = newMemoryRangeROM(0, data) } else if len(data) == 0x800 { // The file has C800 to C8FF // The 256 bytes in Cx00 are copied from the first page in C800 - c.romCsxx = newMemoryRange(0, data) - c.romC8xx = newMemoryRange(0xc800, data) + c.romCsxx = newMemoryRangeROM(0, data) + c.romC8xx = newMemoryRangeROM(0xc800, data) } else if len(data) == 0x1000 { // The file covers the full Cxxx range. Only showing the page // corresponding to the slot used. - c.romCxxx = newMemoryRange(0xc000, data) + c.romCxxx = newMemoryRangeROM(0xc000, data) } else { panic("Invalid ROM size") } @@ -44,7 +44,8 @@ func (c *cardBase) assign(a *Apple2, slot int) { if slot != 0 { if c.romCsxx != nil { // 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) } if c.romC8xx != nil { diff --git a/cardInOut.go b/cardInOut.go index c58782e..6f6337e 100644 --- a/cardInOut.go +++ b/cardInOut.go @@ -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 { // To make ifwork on other slots, patch C2, A0 and A1 diff --git a/cardLanguage.go b/cardLanguage.go index 288a18f..a78f61d 100644 --- a/cardLanguage.go +++ b/cardLanguage.go @@ -46,12 +46,7 @@ func (c *cardLanguage) assign(a *Apple2, slot int) { c.writeState = lcWriteEnabled c.altBank = true // Start on bank2 - if a.isApple2e { - // The Apple //e with 128kb has two blocks of language upper RAM - a.mmu.initLanguageRAM(2) - } else { - a.mmu.initLanguageRAM(1) - } + a.mmu.initLanguageRAM(1) for i := uint8(0x0); i <= 0xf; i++ { iCopy := i c.addCardSoftSwitchR(iCopy, func(*ioC0Page) uint8 { diff --git a/core6502/execute.go b/core6502/execute.go index 42065a5..25c5071 100644 --- a/core6502/execute.go +++ b/core6502/execute.go @@ -54,7 +54,7 @@ func (s *State) executeLine(line []uint8) { // ExecuteInstruction transforms the state given after a single instruction is executed. func (s *State) ExecuteInstruction() { pc := s.reg.getPC() - opcodeID := s.mem.Peek(pc) + opcodeID := s.mem.PeekCode(pc) opcode := s.opcodes[opcodeID] if opcode.cycles == 0 { @@ -65,7 +65,7 @@ func (s *State) ExecuteInstruction() { s.lineCache = make([]uint8, maxInstructionSize) } for i := uint16(0); i < opcode.bytes; i++ { - s.lineCache[i] = s.mem.Peek(pc) + s.lineCache[i] = s.mem.PeekCode(pc) pc++ } s.reg.setPC(pc) diff --git a/core6502/memory.go b/core6502/memory.go index 6e15b27..40c47d1 100644 --- a/core6502/memory.go +++ b/core6502/memory.go @@ -6,6 +6,10 @@ import "io/ioutil" type Memory interface { Peek(address uint16) 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 { diff --git a/memoryManager.go b/memoryManager.go index 8bb44bc..948f0be 100644 --- a/memoryManager.go +++ b/memoryManager.go @@ -12,10 +12,9 @@ type memoryManager struct { // 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 ROM: 0xd000 to 0xffff - physicalROM [4]memoryHandler // 0xd000 to 0xffff, 12 Kb. Up to four + // Upper area ROM: 0xc000 to 0xffff (or 0xd000 to 0xffff on the II+) + 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 physicalLangRAM []*memoryRange // 0xd000 to 0xffff, 12KB. Up to 8 banks. @@ -43,6 +42,10 @@ type memoryManager struct { // Configuration switches, Base64A 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 ( @@ -57,6 +60,8 @@ const ( addressLimitSlots uint16 = 0xc7ff addressLimitSlotsExtra uint16 = 0xcfff addressLimitDArea uint16 = 0xdfff + + invalidAddressPage uint16 = 0x0001 ) type memoryHandler interface { @@ -74,14 +79,14 @@ func newMemoryManager(a *Apple2) *memoryManager { func (mmu *memoryManager) accessCArea(address uint16) memoryHandler { if mmu.intCxROMActive { - return mmu.physicalROMe + return mmu.physicalROM[mmu.romPage] } // First slot area if address <= addressLimitSlots { slot := uint8((address >> 8) & 0x07) mmu.activeSlot = slot if !mmu.slotC3ROMActive && (slot == 3) { - return mmu.physicalROMe + return mmu.physicalROM[mmu.romPage] } return mmu.cardsROM[slot] } @@ -89,10 +94,11 @@ func (mmu *memoryManager) accessCArea(address uint16) memoryHandler { if address == ioC8Off { // Reset extra slot area owner mmu.activeSlot = 0 + mmu.lastAddressPage = invalidAddressPage } if !mmu.slotC3ROMActive && (mmu.activeSlot == 3) { - return mmu.physicalROMe + return mmu.physicalROM[mmu.romPage] } return mmu.cardsROMExtra[mmu.activeSlot] } @@ -130,6 +136,23 @@ func (mmu *memoryManager) getVideoRAM(ext bool) *memoryRange { 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 { if address <= addressLimitZero { return mmu.getPhysicalMainRAM(mmu.altZeroPage) @@ -148,6 +171,7 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler { return mmu.getPhysicalMainRAM(mmu.altMainRAMActiveRead) } if address <= addressLimitIO { + mmu.lastAddressPage = invalidAddressPage return mmu.apple2.io } if address <= addressLimitSlotsExtra { @@ -192,7 +216,27 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler { func (mmu *memoryManager) Peek(address uint16) uint8 { mh := mmu.accessRead(address) 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 mh.peek(address) @@ -201,11 +245,9 @@ func (mmu *memoryManager) Peek(address uint16) uint8 { // Poke sets the data at the given address func (mmu *memoryManager) Poke(address uint16, value uint8) { mh := mmu.accessWrite(address) - if mh == nil { - //fmt.Printf("Writing to void addressing 0x%x\n", address) - return + if mh != nil { + mh.poke(address, value) } - mh.poke(address, value) } // Memory initialization diff --git a/memoryRange.go b/memoryRange.go index 95285e1..a0d5767 100644 --- a/memoryRange.go +++ b/memoryRange.go @@ -1,19 +1,48 @@ package apple2 type memoryRange struct { - base uint16 - data []uint8 + base uint16 + data []uint8 + basePtr uintptr +} + +type memoryRangeROM struct { + memoryRange } func newMemoryRange(base uint16, data []uint8) *memoryRange { var m memoryRange m.base = base m.data = data + m.setBase(base) 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 { + // Safe version: 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) { @@ -23,3 +52,7 @@ func (m *memoryRange) poke(address uint16, value uint8) { func (m *memoryRange) subRange(a, b uint16) []uint8 { return m.data[a-m.base : b-m.base] } + +func (m *memoryRangeROM) poke(address uint16, value uint8) { + // Ignore +}