Initial Apple 2e enhanced support

This commit is contained in:
Ivan Izaguirre 2019-10-12 21:37:37 +02:00 committed by Iván Izaguirre
parent 1d0a3e0b34
commit eeebf1bb0f
8 changed files with 263 additions and 163 deletions

View File

@ -6,13 +6,39 @@ import (
"github.com/ivanizag/apple2/core6502"
)
// NewApple2 instantiates an apple2
func NewApple2(clockMhz float64, isColor bool, fastMode bool) *Apple2 {
// newApple2 instantiates an apple2
func newApple2plus() *Apple2 {
var a Apple2
a.Name = "Apple ][+"
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu)
// Set the io in 0xc000
a.io = newIoC0Page(&a)
a.mmu.setPages(0xc0, 0xc0, a.io)
addApple2SoftSwitches(a.io)
return &a
}
func newApple2eEnhanced() *Apple2 {
var a Apple2
a.Name = "Apple //e"
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewCMOS65c02(a.mmu)
// Set the io in 0xc000
a.io = newIoC0Page(&a)
a.mmu.setPages(0xc0, 0xc0, a.io)
addApple2SoftSwitches(a.io)
addApple2ESoftSwitches(a.io)
return &a
}
func (a *Apple2) setup(isColor bool, clockMhz float64, fastMode bool) {
a.commandChannel = make(chan int, 100)
a.isColor = isColor
a.fastMode = fastMode
@ -23,12 +49,6 @@ func NewApple2(clockMhz float64, isColor bool, fastMode bool) *Apple2 {
} else {
a.cycleDurationNs = 1000.0 / clockMhz
}
// Set the io in 0xc000
a.io = newIoC0Page(&a)
a.mmu.setPages(0xc0, 0xc0, a.io)
return &a
}
func (a *Apple2) insertCard(c card, slot int) {

View File

@ -5,11 +5,13 @@ import (
"os"
)
const defaultInternal = "<default>"
// MainApple is a device independant main. Video, keyboard and speaker won't be defined
func MainApple() *Apple2 {
romFile := flag.String(
"rom",
"<internal>/Apple2_Plus.rom",
defaultInternal,
"main rom file")
disk2RomFile := flag.String(
"diskRom",
@ -37,7 +39,7 @@ func MainApple() *Apple2 {
"cpu speed in Mhz, use 0 for full speed. Use F5 to toggle.")
charRomFile := flag.String(
"charRom",
"<internal>/Apple2rev7CharGen.rom",
defaultInternal,
"rom file for the character generator")
languageCardSlot := flag.Int(
"languageCardSlot",
@ -81,10 +83,10 @@ func MainApple() *Apple2 {
false,
"shows the character map",
)
base64a := flag.Bool(
"base64a",
false,
"setup a Base64A clone",
model := flag.String(
"model",
"2plus",
"set base model. Models available 2plus, 2e, base64a",
)
profile := flag.Bool(
"profile",
@ -93,29 +95,73 @@ func MainApple() *Apple2 {
)
flag.Parse()
a := NewApple2(*cpuClock, !*mono, *fastDisk)
var a *Apple2
var charGenMap charColumnMap
initialCharGenPage := 0
switch *model {
case "2plus":
a = newApple2plus()
if *romFile == defaultInternal {
*romFile = "<internal>/Apple2_Plus.rom"
}
if *charRomFile == defaultInternal {
*charRomFile = "<internal>/Apple2rev7CharGen.rom"
}
charGenMap = charGenColumnsMap2Plus
case "2e":
a = newApple2eEnhanced()
if *romFile == defaultInternal {
*romFile = "<internal>/Apple2e_Enhanced.rom"
}
if *charRomFile == defaultInternal {
*charRomFile = "<internal>/Apple IIe Video Enhanced - 342-0265-A - 2732.bin"
}
a.isApple2e = true
charGenMap = charGenColumnsMap2e
case "base64a":
a = newBase64a()
if *romFile == defaultInternal {
err := loadBase64aRom(a)
if err != nil {
panic(err)
}
*romFile = ""
}
if *charRomFile == defaultInternal {
*charRomFile = "<internal>/BASE64A_ROM7_CharGen.BIN"
initialCharGenPage = 1
}
charGenMap = charGenColumnsMapBase64a
default:
panic("Model not supported")
}
a.setup(!*mono, *cpuClock, *fastDisk)
a.cpu.SetTrace(*traceCPU)
a.io.setTrace(*traceSS)
a.io.setPanicNotImplemented(*panicSS)
a.setProfile(*profile)
if *charRomFile != "" {
cg, err := NewCharacterGenerator(*charRomFile)
if err != nil {
panic(err)
}
a.cg = cg
}
if *base64a {
NewBase64a(a)
} else {
// Load ROM if not loaded already
if *romFile != "" {
err := a.LoadRom(*romFile)
if err != nil {
panic(err)
}
}
// Load character generator if it loaded already
cg, err := newCharacterGenerator(*charRomFile, charGenMap)
if err != nil {
panic(err)
}
cg.setPage(initialCharGenPage)
a.cg = cg
// Externsion cards
if *languageCardSlot >= 0 {
a.AddLanguageCard(*languageCardSlot)
}

View File

@ -1,42 +1,43 @@
package apple2
import "fmt"
import (
"github.com/ivanizag/apple2/core6502"
)
/*
Copam BASE64A adaptation.
*/
// Base64a extends an Apple2
type Base64a struct {
a *Apple2
romBanks [4]*memoryRange
romBank uint8
}
// newBase64a instantiates an apple2
func newBase64a() *Apple2 {
var a Apple2
// NewBase64a instantiates an apple2
func NewBase64a(a *Apple2) (*Base64a, error) {
var b Base64a
b.a = a
a.Name = "Base 64A"
err := b.loadRom()
if err != nil {
return nil, err
}
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu)
// Configure the character generator
if !a.cg.customRom {
err := a.cg.load("<internal>/BASE64A_ROM7_CharGen.BIN")
if err != nil {
return nil, err
}
}
a.cg.setColumnMap(base64aCharGenColumnsMap)
a.cg.setPage(1)
// Set the io in 0xc000
a.io = newIoC0Page(&a)
a.mmu.setPages(0xc0, 0xc0, a.io)
addApple2SoftSwitches(a.io)
addBase64aSoftSwitches(a.io)
return &b, nil
return &a
}
func base64aCharGenColumnsMap(column int) int {
func addBase64aSoftSwitches(io *ioC0Page) {
// Other softswitches
io.addSoftSwitchW(0x0C, notImplementedSoftSwitchW) // 80 columns off?
io.addSoftSwitchW(0x0E, notImplementedSoftSwitchW) // Alt char off?
// Write on the speaker. That is a double access and should do nothing
// but works somehow on the BASE64A
io.addSoftSwitchW(0x30, func(io *ioC0Page, value uint8) {
speakerSoftSwitch(io)
})
}
func charGenColumnsMapBase64a(column int) int {
bit := column + 2
// Weird positions
if column == 6 {
@ -46,76 +47,3 @@ func base64aCharGenColumnsMap(column int) int {
}
return bit
}
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 (b *Base64a) loadRom() 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]...)
}
}
for j := range romBanksBytes {
b.romBanks[j] = newMemoryRange(0xd000, romBanksBytes[j])
}
// Start with first bank active
b.changeRomBank(0)
// Add rom soft switches. They use the annunciator 0 and 1
b.a.io.addSoftSwitchRW(0x58, func(*ioC0Page) uint8 {
b.changeRomBank(b.romBank & 2)
return 0
})
b.a.io.addSoftSwitchRW(0x59, func(*ioC0Page) uint8 {
b.changeRomBank(b.romBank | 1)
return 0
})
b.a.io.addSoftSwitchRW(0x5A, func(*ioC0Page) uint8 {
b.changeRomBank(b.romBank & 1)
return 0
})
b.a.io.addSoftSwitchRW(0x5B, func(*ioC0Page) uint8 {
b.changeRomBank(b.romBank | 2)
return 0
})
// Other softswitches
b.a.io.addSoftSwitchW(0x0C, notImplementedSoftSwitchW) // 80 columns off?
b.a.io.addSoftSwitchW(0x0E, notImplementedSoftSwitchW) // Alt char off?
// Write on the speaker. That is a double access and should do nothing
// but works somehow on the BASE64A
b.a.io.addSoftSwitchW(0x30, func(io *ioC0Page, value uint8) {
speakerSoftSwitch(io)
})
return nil
}
func (b *Base64a) changeRomBank(bank uint8) {
b.romBank = bank
//fmt.Printf("Change to ROM bank #%v\n", b.romBank)
b.a.mmu.physicalROM = b.romBanks[b.romBank]
b.a.mmu.resetRomPaging() // If rom was not active. This is going to far?
}

80
base64aRom.go Normal file
View File

@ -0,0 +1,80 @@
package apple2
import (
"fmt"
)
/*
Copam BASE64A uses paginated ROM
*/
// Base64aROM Models the paginated ROM on a BASE64A clone
type base64aROM struct {
romBanks [4]*memoryRange
romBank uint8
}
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
var r base64aROM
for j := range romBanksBytes {
r.romBanks[j] = newMemoryRange(0xd000, romBanksBytes[j])
}
// Start with first bank active
r.changeBank(a.mmu, 0)
// Add rom soft switches. They use the annunciator 0 and 1
a.io.addSoftSwitchRW(0x58, func(*ioC0Page) uint8 {
r.changeBank(a.mmu, r.romBank&2)
return 0
})
a.io.addSoftSwitchRW(0x59, func(*ioC0Page) uint8 {
r.changeBank(a.mmu, r.romBank|1)
return 0
})
a.io.addSoftSwitchRW(0x5A, func(*ioC0Page) uint8 {
r.changeBank(a.mmu, r.romBank&1)
return 0
})
a.io.addSoftSwitchRW(0x5B, func(*ioC0Page) uint8 {
r.changeBank(a.mmu, r.romBank|2)
return 0
})
return nil
}
func (r *base64aROM) changeBank(mmu *memoryManager, bank uint8) {
r.romBank = bank
//fmt.Printf("Change to ROM bank #%v\n", r.romBank)
mmu.physicalROM = r.romBanks[r.romBank]
mmu.resetRomPaging() // If rom was not active. This is going too far?
}

View File

@ -7,56 +7,57 @@ import (
/*
See:
hhttps://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Companies/Apple/Documentation/Apple%20Technical%20Information%20Library/a2til041.txt
https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Companies/Apple/Documentation/Apple%20Technical%20Information%20Library/a2til041.txt
*/
// CharacterGenerator represents the ROM wth the characters bitmaps
type CharacterGenerator struct {
data []uint8
customRom bool
columnMap charColumnMap
page int
}
type charColumnMap func(column int) int
func charGenColumnsMap2Plus(column int) int {
return 6 - column
}
func charGenColumnsMap2e(column int) int {
return column
}
const (
rev7CharGenSize = 2048
defaultCharGenROM = "<internal>/Apple2rev7CharGen.rom"
charGenPageSize = 2048
)
// NewCharacterGenerator instantiates a new Character Generator with the rom on the file given
func NewCharacterGenerator(filename string) (*CharacterGenerator, error) {
func newCharacterGenerator(filename string, order charColumnMap) (*CharacterGenerator, error) {
var cg CharacterGenerator
err := cg.load(filename)
if err != nil {
return nil, err
}
cg.columnMap = order
return &cg, nil
}
func (cg *CharacterGenerator) load(filename string) error {
cg.customRom = !isInternalResource(filename)
bytes, err := loadResource(filename)
if err != nil {
return err
}
size := len(bytes)
if size < rev7CharGenSize {
if size < charGenPageSize {
return errors.New("Character ROM size not supported")
}
cg.data = bytes
return nil
}
func (cg *CharacterGenerator) setColumnMap(columnMap charColumnMap) {
// Regular Apple II uses bits 6 to 0 but some clones have other mappings
cg.columnMap = columnMap
}
func (cg *CharacterGenerator) setPage(page int) {
// Some clones had a switch to change codepage with extra characters
pages := len(cg.data) / rev7CharGenSize
pages := len(cg.data) / charGenPageSize
cg.page = page % pages
}
@ -65,14 +66,8 @@ func (cg *CharacterGenerator) nextPage() {
}
func (cg *CharacterGenerator) getPixel(char uint8, row int, column int) bool {
bits := cg.data[int(char)*8+row+cg.page*rev7CharGenSize]
var bit int
if cg.columnMap != nil {
bit = cg.columnMap(column)
} else {
// Standard Apple 2 mapping
bit = 6 - column
}
bits := cg.data[int(char)*8+row+cg.page*charGenPageSize]
bit := cg.columnMap(column)
value := bits >> uint(bit) & 1
return value == 1
}
@ -113,7 +108,7 @@ func (cg *CharacterGenerator) dumpChar(char uint8) {
// Dump to sdtout all the character maps
func (cg *CharacterGenerator) Dump() {
pages := len(cg.data) / rev7CharGenSize
pages := len(cg.data) / charGenPageSize
for p := 0; p < pages; p++ {
cg.setPage(p)
for i := 0; i < 256; i++ {

View File

@ -50,12 +50,6 @@ const (
func newIoC0Page(a *Apple2) *ioC0Page {
var io ioC0Page
io.apple2 = a
addApple2SoftSwitches(&io)
if a.isApple2e {
addApple2ESoftSwitches(&io)
}
return &io
}

File diff suppressed because one or more lines are too long

View File

@ -9,24 +9,37 @@ const (
)
func addApple2ESoftSwitches(io *ioC0Page) {
io.addSoftSwitchW(0x00, getSoftSwitchExt(ioFlag80Store, ssOff, nil))
io.addSoftSwitchW(0x01, getSoftSwitchExt(ioFlag80Store, ssOn, nil))
// New MMU read softswithes
io.addSoftSwitchW(0x06, getSoftSwitchExt(ioFlagIntCxRom, ssOff, softSwitchIntCxRomOff))
io.addSoftSwitchW(0x07, getSoftSwitchExt(ioFlagIntCxRom, ssOn, softSwitchIntCxRomOn))
io.addSoftSwitchR(0x15, getStatusSoftSwitch(ioFlagIntCxRom))
io.addSoftSwitchW(0x0A, getSoftSwitchExt(ioFlagSlotC3Rom, ssOff, softSwitchSlotC3RomOff))
io.addSoftSwitchW(0x0B, getSoftSwitchExt(ioFlagSlotC3Rom, ssOn, softSwitchSlotC3RomOn))
io.addSoftSwitchR(0x17, getStatusSoftSwitch(ioFlagSlotC3Rom))
// Previous read softswithes
io.addSoftSwitchR(0x1A, getStatusSoftSwitch(ioFlagText))
io.addSoftSwitchR(0x1B, getStatusSoftSwitch(ioFlagMixed))
io.addSoftSwitchR(0x1C, getStatusSoftSwitch(ioFlagSecondPage))
io.addSoftSwitchR(0x1D, getStatusSoftSwitch(ioFlagHiRes))
// New IOU read softswithes
io.addSoftSwitchW(0x00, getSoftSwitchExt(ioFlag80Store, ssOff, nil))
io.addSoftSwitchW(0x01, getSoftSwitchExt(ioFlag80Store, ssOn, nil))
io.addSoftSwitchR(0x18, getStatusSoftSwitch(ioFlag80Store))
io.addSoftSwitchW(0x0C, getSoftSwitchExt(ioFlag80Col, ssOff, nil))
io.addSoftSwitchW(0x0D, getSoftSwitchExt(ioFlag80Col, ssOn, nil))
io.addSoftSwitchR(0x1F, getStatusSoftSwitch(ioFlag80Col))
io.addSoftSwitchW(0x0E, getSoftSwitchExt(ioFlagAltChar, ssOff, nil))
io.addSoftSwitchW(0x0F, getSoftSwitchExt(ioFlagAltChar, ssOn, nil))
io.softSwitchesData[ioFlagAltChar] = ssOn // Not sure about this.
io.addSoftSwitchR(0x1E, getStatusSoftSwitch(ioFlagAltChar))
// TOOD:
// AKD read on 0x10
// VBL read on 0x19
//io.softSwitchesData[ioFlagAltChar] = ssOn // Not sure about this.
io.addSoftSwitchR(0x15, getStatusSoftSwitch(ioFlagIntCxRom))
io.addSoftSwitchR(0x17, getStatusSoftSwitch(ioFlagSlotC3Rom))
io.addSoftSwitchR(0x18, getStatusSoftSwitch(ioFlag80Store))
io.addSoftSwitchR(0x1C, getStatusSoftSwitch(ioFlagSecondPage))
io.addSoftSwitchR(0x1F, getStatusSoftSwitch(ioFlag80Col))
}
type softSwitchExtAction func(io *ioC0Page)