diff --git a/apple2Setup.go b/apple2Setup.go index 6ed892e..e7c0a3e 100644 --- a/apple2Setup.go +++ b/apple2Setup.go @@ -8,7 +8,6 @@ import ( // newApple2 instantiates an apple2 func newApple2plus() *Apple2 { - var a Apple2 a.Name = "Apple ][+" a.mmu = newMemoryManager(&a) @@ -20,12 +19,12 @@ func newApple2plus() *Apple2 { } func newApple2e() *Apple2 { - var a Apple2 a.Name = "Apple IIe" a.mmu = newMemoryManager(&a) a.cpu = core6502.NewCMOS65c02(a.mmu) a.io = newIoC0Page(&a) + a.mmu.InitRAMalt() addApple2SoftSwitches(a.io) addApple2ESoftSwitches(a.io) @@ -33,12 +32,12 @@ func newApple2e() *Apple2 { } func newApple2eEnhanced() *Apple2 { - var a Apple2 a.Name = "Apple //e" a.mmu = newMemoryManager(&a) a.cpu = core6502.NewCMOS65c02(a.mmu) a.io = newIoC0Page(&a) + a.mmu.InitRAMalt() addApple2SoftSwitches(a.io) addApple2ESoftSwitches(a.io) diff --git a/apple2main.go b/apple2main.go index 21b68e0..68c2526 100644 --- a/apple2main.go +++ b/apple2main.go @@ -90,7 +90,7 @@ func MainApple() *Apple2 { ) model := flag.String( "model", - "2e", + "2enh", "set base model. Models available 2plus, 2e, 2enh, base64a", ) profile := flag.Bool( diff --git a/apple2sdl/sdlJoysticks.go b/apple2sdl/sdlJoysticks.go index 8db61af..52e136d 100644 --- a/apple2sdl/sdlJoysticks.go +++ b/apple2sdl/sdlJoysticks.go @@ -1,8 +1,6 @@ package main import ( - "fmt" - "github.com/veandco/go-sdl2/sdl" ) @@ -98,7 +96,6 @@ func (j *sdlJoysticks) ReadButton(i int) bool { case 2: value = j.button[3] || j.keys[2] } - fmt.Printf("Button %v: %v.\n", i, value) return value } diff --git a/base64a.go b/base64a.go index fc66c0f..ce3a017 100644 --- a/base64a.go +++ b/base64a.go @@ -1,6 +1,8 @@ package apple2 import ( + "fmt" + "github.com/ivanizag/apple2/core6502" ) @@ -22,6 +24,45 @@ func newBase64a() *Apple2 { return &a } +const ( + // There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill + // 2 or 4 banks with 2kb windows. + base64aRomBankSize = 12 * 1024 + base64aRomBankCount = 4 + base64aRomWindowSize = 2 * 1024 + base64aRomChipCount = 6 +) + +func loadBase64aRom(a *Apple2) error { + // Load the 6 PROM dumps + romBanksBytes := make([][]uint8, base64aRomBankCount) + for j := range romBanksBytes { + romBanksBytes[j] = make([]uint8, 0, base64aRomBankSize) + } + + for i := 0; i < base64aRomChipCount; i++ { + filename := fmt.Sprintf("/BASE64A_%X.BIN", 0xd0+i*0x08) + data, err := loadResource(filename) + if err != nil { + return err + } + for j := range romBanksBytes { + start := (j * base64aRomWindowSize) % len(data) + romBanksBytes[j] = append(romBanksBytes[j], data[start:start+base64aRomWindowSize]...) + } + } + + // Create banks + for j := range romBanksBytes { + a.mmu.physicalROM[j] = newMemoryRange(0xd000, romBanksBytes[j]) + } + + // Start with first bank active + a.mmu.setActiveROMPage(0) + + return nil +} + func addBase64aSoftSwitches(io *ioC0Page) { // Other softswitches, not implemented but called from the ROM io.addSoftSwitchW(0x0C, notImplementedSoftSwitchW, "80COLOFF") @@ -32,6 +73,30 @@ func addBase64aSoftSwitches(io *ioC0Page) { io.addSoftSwitchW(0x30, func(io *ioC0Page, value uint8) { speakerSoftSwitch(io) }, "SPEAKER") + + // ROM pagination softswitches. They use the annunciator 0 and 1 + mmu := io.apple2.mmu + io.addSoftSwitchRW(0x58, func(*ioC0Page) uint8 { + p := mmu.getActiveROMPage() + mmu.setActiveROMPage(p & 2) + return 0 + }, "ANN0OFF-ROM") + io.addSoftSwitchRW(0x59, func(*ioC0Page) uint8 { + p := mmu.getActiveROMPage() + mmu.setActiveROMPage(p | 1) + return 0 + }, "ANN0ON-ROM") + io.addSoftSwitchRW(0x5A, func(*ioC0Page) uint8 { + p := mmu.getActiveROMPage() + mmu.setActiveROMPage(p & 1) + return 0 + }, "ANN1OFF-ROM") + io.addSoftSwitchRW(0x5B, func(*ioC0Page) uint8 { + p := mmu.getActiveROMPage() + mmu.setActiveROMPage(p | 2) + return 0 + }, "ANN1ON-ROM") + } func charGenColumnsMapBase64a(column int) int { diff --git a/base64aRom.go b/base64aRom.go deleted file mode 100644 index c9fe131..0000000 --- a/base64aRom.go +++ /dev/null @@ -1,70 +0,0 @@ -package apple2 - -import ( - "fmt" -) - -/* - Copam BASE64A uses paginated ROM -*/ - -const ( - // There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill - // 2 or 4 banks with 2kb windows. - base64aRomBankSize = 12 * 1024 - base64aRomBankCount = 4 - base64aRomWindowSize = 2 * 1024 - base64aRomChipCount = 6 -) - -func loadBase64aRom(a *Apple2) error { - // Load the 6 PROM dumps - romBanksBytes := make([][]uint8, base64aRomBankCount) - for j := range romBanksBytes { - romBanksBytes[j] = make([]uint8, 0, base64aRomBankSize) - } - - for i := 0; i < base64aRomChipCount; i++ { - filename := fmt.Sprintf("/BASE64A_%X.BIN", 0xd0+i*0x08) - data, err := loadResource(filename) - if err != nil { - return err - } - for j := range romBanksBytes { - start := (j * base64aRomWindowSize) % len(data) - romBanksBytes[j] = append(romBanksBytes[j], data[start:start+base64aRomWindowSize]...) - } - } - - // Create banks - for j := range romBanksBytes { - a.mmu.physicalROM[j] = newMemoryRange(0xd000, romBanksBytes[j]) - } - - // Start with first bank active - a.mmu.setActiveROMPage(0) - - // Add rom soft switches. They use the annunciator 0 and 1 - a.io.addSoftSwitchRW(0x58, func(*ioC0Page) uint8 { - p := a.mmu.getActiveROMPage() - a.mmu.setActiveROMPage(p & 2) - return 0 - }, "ANN0OFF-ROM") - a.io.addSoftSwitchRW(0x59, func(*ioC0Page) uint8 { - p := a.mmu.getActiveROMPage() - a.mmu.setActiveROMPage(p | 1) - return 0 - }, "ANN0ON-ROM") - a.io.addSoftSwitchRW(0x5A, func(*ioC0Page) uint8 { - p := a.mmu.getActiveROMPage() - a.mmu.setActiveROMPage(p & 1) - return 0 - }, "ANN1OFF-ROM") - a.io.addSoftSwitchRW(0x5B, func(*ioC0Page) uint8 { - p := a.mmu.getActiveROMPage() - a.mmu.setActiveROMPage(p | 2) - return 0 - }, "ANN1ON-ROM") - - return nil -} diff --git a/cardLanguage.go b/cardLanguage.go index 59be0bc..bb06d2e 100644 --- a/cardLanguage.go +++ b/cardLanguage.go @@ -47,8 +47,12 @@ func (c *cardLanguage) assign(a *Apple2, slot int) { c.writeState = lcWriteEnabled c.altBank = true - a.mmu.initLanguageRAM(1) - + if a.isApple2e { + // 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++ { iCopy := i c.addCardSoftSwitchR(iCopy, func(*ioC0Page) uint8 { diff --git a/memoryManager.go b/memoryManager.go index 70f8fbc..8c9c1fd 100644 --- a/memoryManager.go +++ b/memoryManager.go @@ -2,7 +2,6 @@ package apple2 import ( "encoding/binary" - "fmt" "io" ) @@ -27,23 +26,27 @@ type memoryManager struct { physicalDAltRAM []memoryHandler // 0xd000 to 0xdfff, 4KB. Up to 8 banks. physicalEFRAM []memoryHandler // 0xe000 to 0xffff, 8KB. Up to 8 banks. - // Pages prapared for optional card ROM banks in 0xc800 to 0xcfff - altMainRAMActiveRead bool // Use extra RAM on the 128KB Apple2e for read - altMainRAMActiveWrite bool // Use extra RAM on the 128KB Apple2e for write - - c3ROMActive bool // Apple2e slot 3 ROM shadow - cxROMActive bool // Apple2e slots ROM shadow - activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff - + // Configuration switches, Language cards 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 - romPage uint8 // Active ROM page + + // 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 + c3ROMActive bool // Apple2e slot 3 ROM shadow + cxROMActive bool // Apple2e slots ROM shadow + activeSlot uint8 // Active slot owner of 0xc800 to 0xcfff + + // Configuration switches, Base64A + romPage uint8 // Active ROM page } const ( ioC8Off uint16 = 0xcfff + addressLimitZero uint16 = 0x01ff addressLimitMainRAM uint16 = 0xbfff addressLimitIO uint16 = 0xc0ff addressLimitSlots uint16 = 0xc7ff @@ -59,14 +62,20 @@ type memoryHandler interface { func newMemoryManager(a *Apple2) *memoryManager { var mmu memoryManager mmu.apple2 = a - - ram := make([]uint8, 0xc000) // Reserve 48kb - mmu.physicalMainRAM = newMemoryRange(0, ram) + mmu.physicalMainRAM = newMemoryRange(0, make([]uint8, 0xc000)) return &mmu } func (mmu *memoryManager) accessRead(address uint16) memoryHandler { + // First two pages + if address <= addressLimitZero { + if mmu.altZeroPage { + return mmu.physicalMainRAMAlt + } + return mmu.physicalMainRAM + } + // Main RAM area if address <= addressLimitMainRAM { if mmu.altMainRAMActiveRead { @@ -104,13 +113,17 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler { // Upper address area if mmu.lcActiveRead { + block := mmu.lcSelectedBlock + if mmu.altZeroPage { + block = 1 + } if address <= addressLimitDArea { if mmu.lcAltBank { - return mmu.physicalDAltRAM[mmu.lcSelectedBlock] + return mmu.physicalDAltRAM[block] } - return mmu.physicalDRAM[mmu.lcSelectedBlock] + return mmu.physicalDRAM[block] } - return mmu.physicalEFRAM[mmu.lcSelectedBlock] + return mmu.physicalEFRAM[block] } // Use ROM @@ -118,6 +131,14 @@ func (mmu *memoryManager) accessRead(address uint16) memoryHandler { } func (mmu *memoryManager) accessWrite(address uint16) memoryHandler { + // First two pages + if address <= addressLimitZero { + if mmu.altZeroPage { + return mmu.physicalMainRAMAlt + } + return mmu.physicalMainRAM + } + // Main RAM area if address <= addressLimitMainRAM { if mmu.altMainRAMActiveWrite { @@ -149,13 +170,17 @@ func (mmu *memoryManager) accessWrite(address uint16) memoryHandler { // Upper address area if mmu.lcActiveWrite { + block := mmu.lcSelectedBlock + if mmu.altZeroPage { + block = 1 + } if address <= addressLimitDArea { if mmu.lcAltBank { - return mmu.physicalDAltRAM[mmu.lcSelectedBlock] + return mmu.physicalDAltRAM[block] } - return mmu.physicalDRAM[mmu.lcSelectedBlock] + return mmu.physicalDRAM[block] } - return mmu.physicalEFRAM[mmu.lcSelectedBlock] + return mmu.physicalEFRAM[block] } // Use ROM @@ -166,7 +191,7 @@ 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) + //fmt.Printf("Reading void addressing 0x%x\n", address) return 0xf4 // Or some random number } return mh.peek(address) @@ -176,7 +201,7 @@ func (mmu *memoryManager) Peek(address uint16) uint8 { 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) + //fmt.Printf("Writing to void addressing 0x%x\n", address) return } mh.poke(address, value) @@ -202,6 +227,10 @@ func (mmu *memoryManager) initLanguageRAM(groups int) { } } +func (mmu *memoryManager) InitRAMalt() { + mmu.physicalMainRAMAlt = newMemoryRange(0, make([]uint8, 0xc000)) +} + // Memory configuration func (mmu *memoryManager) setActiveROMPage(page uint8) { mmu.romPage = page diff --git a/softSwitches2e.go b/softSwitches2e.go index 58e4722..a38c3ba 100644 --- a/softSwitches2e.go +++ b/softSwitches2e.go @@ -6,38 +6,25 @@ package apple2 */ const ( - ioFlagRamRd uint8 = 0x13 - ioFlagRamWrt uint8 = 0x14 - ioFlagIntCxRom uint8 = 0x15 - ioFlagAltZp uint8 = 0x16 - ioFlagSlotC3Rom uint8 = 0x17 - ioFlag80Store uint8 = 0x18 - ioFlagAltChar uint8 = 0x1E - ioFlag80Col uint8 = 0x1F + ioFlag80Store uint8 = 0x18 + ioFlagAltChar uint8 = 0x1E + ioFlag80Col uint8 = 0x1F // ??? ioVertBlank uin8 = 0x19 ) func addApple2ESoftSwitches(io *ioC0Page) { // New MMU read softswithes - io.addSoftSwitchW(0x02, getSoftSwitchExt(ioFlagRamRd, ssOff, nil), "RAMRDOFF") - io.addSoftSwitchW(0x03, getSoftSwitchExt(ioFlagRamWrt, ssOn, nil), "RAMRDON") - io.addSoftSwitchR(0x13, getStatusSoftSwitch(ioFlagRamWrt), "RAMRD") + mmu := io.apple2.mmu + addSoftSwitchesMmu(io, 0x02, 0x03, 0x13, &mmu.altMainRAMActiveRead, "RAMRD") + addSoftSwitchesMmu(io, 0x04, 0x05, 0x14, &mmu.altMainRAMActiveWrite, "RAMWRT") + addSoftSwitchesMmu(io, 0x06, 0x07, 0x15, &mmu.cxROMActive, "INTCXROM") + addSoftSwitchesMmu(io, 0x08, 0x09, 0x16, &mmu.altZeroPage, "ALTZP") + addSoftSwitchesMmu(io, 0x0a, 0x0b, 0x17, &mmu.c3ROMActive, "SLOTC3ROM") - io.addSoftSwitchW(0x04, getSoftSwitchExt(ioFlagRamWrt, ssOff, nil), "RAMWRTOFF") - io.addSoftSwitchW(0x05, getSoftSwitchExt(ioFlagRamWrt, ssOn, nil), "RAMWRTON") - io.addSoftSwitchR(0x14, getStatusSoftSwitch(ioFlagRamWrt), "RAMWRT") - - io.addSoftSwitchW(0x06, getSoftSwitchExt(ioFlagIntCxRom, ssOff, softSwitchIntCxRomOff), "INTCXROMOFF") - io.addSoftSwitchW(0x07, getSoftSwitchExt(ioFlagIntCxRom, ssOn, softSwitchIntCxRomOn), "INTCXROMON") - io.addSoftSwitchR(0x15, getStatusSoftSwitch(ioFlagIntCxRom), "INTCXROM") - - io.addSoftSwitchW(0x08, getSoftSwitchExt(ioFlagAltZp, ssOff, nil), "ALTZPOFF") - io.addSoftSwitchW(0x09, getSoftSwitchExt(ioFlagAltZp, ssOn, nil), "ALTZPON") - io.addSoftSwitchR(0x16, getStatusSoftSwitch(ioFlagAltZp), "ALTZP") - - io.addSoftSwitchW(0x0A, getSoftSwitchExt(ioFlagSlotC3Rom, ssOff, softSwitchSlotC3RomOff), "SLOTC3ROMOFF") - io.addSoftSwitchW(0x0B, getSoftSwitchExt(ioFlagSlotC3Rom, ssOn, softSwitchSlotC3RomOn), "SLOTC3ROMON") - io.addSoftSwitchR(0x17, getStatusSoftSwitch(ioFlagSlotC3Rom), "SLOTC3ROM") + // New IOU read softswithes + addSoftSwitchesIou(io, 0x00, 0x01, 0x18, ioFlag80Store, "80STORE") + addSoftSwitchesIou(io, 0x0c, 0x0d, 0x1f, ioFlag80Col, "80COL") + addSoftSwitchesIou(io, 0x0e, 0x0f, 0x1e, ioFlagAltChar, "ALTCHARSET") // Previous read softswithes io.addSoftSwitchR(0x1A, getStatusSoftSwitch(ioFlagText), "TEXT") @@ -45,19 +32,6 @@ func addApple2ESoftSwitches(io *ioC0Page) { io.addSoftSwitchR(0x1C, getStatusSoftSwitch(ioFlagSecondPage), "PAGE2") io.addSoftSwitchR(0x1D, getStatusSoftSwitch(ioFlagHiRes), "HIRES") - // New IOU read softswithes - io.addSoftSwitchW(0x00, getSoftSwitchExt(ioFlag80Store, ssOff, nil), "80STOREOFF") - io.addSoftSwitchW(0x01, getSoftSwitchExt(ioFlag80Store, ssOn, nil), "80STOREON") - io.addSoftSwitchR(0x18, getStatusSoftSwitch(ioFlag80Store), "80STORE") - - io.addSoftSwitchW(0x0C, getSoftSwitchExt(ioFlag80Col, ssOff, nil), "80COLOFF") - io.addSoftSwitchW(0x0D, getSoftSwitchExt(ioFlag80Col, ssOn, nil), "80COLON") - io.addSoftSwitchR(0x1F, getStatusSoftSwitch(ioFlag80Col), "80COL") - - io.addSoftSwitchW(0x0E, getSoftSwitchExt(ioFlagAltChar, ssOff, nil), "ALTCHARSETOFF") - io.addSoftSwitchW(0x0F, getSoftSwitchExt(ioFlagAltChar, ssOn, nil), "ALTCHARSETON") - io.addSoftSwitchR(0x1E, getStatusSoftSwitch(ioFlagAltChar), "ALTCHARSET") - // TOOD: // AKD read on 0x10 // VBL read on 0x19 @@ -66,37 +40,33 @@ func addApple2ESoftSwitches(io *ioC0Page) { } -type softSwitchExtAction func(io *ioC0Page) +func addSoftSwitchesMmu(io *ioC0Page, addressClear uint8, addressSet uint8, AddressGet uint8, flag *bool, name string) { + io.addSoftSwitchW(addressClear, func(_ *ioC0Page, _ uint8) { + *flag = false + }, name+"OFF") -func getSoftSwitchExt(ioFlag uint8, dstValue uint8, action softSwitchExtAction) softSwitchW { - return func(io *ioC0Page, _ uint8) { - currentValue := io.softSwitchesData[ioFlag] - if currentValue == dstValue { - return // Already switched, ignore + io.addSoftSwitchW(addressSet, func(_ *ioC0Page, _ uint8) { + *flag = true + }, name+"ON") + + io.addSoftSwitchR(AddressGet, func(_ *ioC0Page) uint8 { + if *flag { + return ssOn } - if action != nil { - action(io) - } - io.softSwitchesData[ioFlag] = dstValue - } + return ssOff + }, name) } -func softSwitchIntCxRomOn(io *ioC0Page) { - //io.apple2.mmu.setPagesRead(0xc1, 0xcf, io.apple2.mmu.physicalROMe) -} +func addSoftSwitchesIou(io *ioC0Page, addressClear uint8, addressSet uint8, AddressGet uint8, ioFlag uint8, name string) { + io.addSoftSwitchW(addressClear, func(_ *ioC0Page, _ uint8) { + io.softSwitchesData[ioFlag] = ssOff + }, name+"OFF") -func softSwitchIntCxRomOff(io *ioC0Page) { - // TODO restore all the ROM from the slots for 0xc1 to 0xc7 - //io.apple2.mmu.setPages(0xc1, 0xc7, nil) -} + io.addSoftSwitchW(addressSet, func(_ *ioC0Page, _ uint8) { + io.softSwitchesData[ioFlag] = ssOn + }, name+"ON") -func softSwitchSlotC3RomOn(io *ioC0Page) { - // TODO restore the slot 3 ROM - //io.apple2.mmu.setPages(0xc3, 0xc3, nil) + io.addSoftSwitchR(AddressGet, func(_ *ioC0Page) uint8 { + return io.softSwitchesData[ioFlag] + }, name) } - -func softSwitchSlotC3RomOff(io *ioC0Page) { - //io.apple2.mmu.setPagesRead(0xc3, 0xc3, io.apple2.mmu.physicalROMe) -} - -// TODO: apply state after persistance load