More Apple //e softswitches

This commit is contained in:
Ivan Izaguirre 2019-11-04 00:23:03 +01:00 committed by Iván Izaguirre
parent 3660e0ae98
commit 09117fd7c5
8 changed files with 160 additions and 166 deletions

View File

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

View File

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

View File

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

View File

@ -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("<internal>/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 {

View File

@ -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("<internal>/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
}

View File

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

View File

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

View File

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