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 {
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
}

View File

@ -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 {

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 {
// 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.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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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

View File

@ -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
}