mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-06-07 16:46:34 +00:00
Improve the expansion card abstraction
This commit is contained in:
parent
cb21a1fefc
commit
684c8f7172
|
@ -1,8 +1,12 @@
|
||||||
package apple2
|
package apple2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go6502/core6502"
|
"go6502/core6502"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -12,6 +16,7 @@ type Apple2 struct {
|
||||||
mmu *memoryManager
|
mmu *memoryManager
|
||||||
io *ioC0Page
|
io *ioC0Page
|
||||||
cg *CharacterGenerator
|
cg *CharacterGenerator
|
||||||
|
cards [8]card
|
||||||
isApple2e bool
|
isApple2e bool
|
||||||
panicSS bool
|
panicSS bool
|
||||||
commandChannel chan int
|
commandChannel chan int
|
||||||
|
@ -19,7 +24,6 @@ type Apple2 struct {
|
||||||
isColor bool
|
isColor bool
|
||||||
fastMode bool
|
fastMode bool
|
||||||
fastRequestsCounter int
|
fastRequestsCounter int
|
||||||
persistance *persistance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -97,10 +101,10 @@ func (a *Apple2) executeCommand(command int) {
|
||||||
a.isColor = !a.isColor
|
a.isColor = !a.isColor
|
||||||
case CommandSaveState:
|
case CommandSaveState:
|
||||||
fmt.Println("Saving state")
|
fmt.Println("Saving state")
|
||||||
a.persistance.save("apple2.state")
|
a.save("apple2.state")
|
||||||
case CommandLoadState:
|
case CommandLoadState:
|
||||||
fmt.Println("Loading state")
|
fmt.Println("Loading state")
|
||||||
a.persistance.load("apple2.state")
|
a.load("apple2.state")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,3 +120,55 @@ func (a *Apple2) releaseFastMode() {
|
||||||
a.fastRequestsCounter--
|
a.fastRequestsCounter--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type persistent interface {
|
||||||
|
save(io.Writer)
|
||||||
|
load(io.Reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) save(filename string) {
|
||||||
|
f, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
w := bufio.NewWriter(f)
|
||||||
|
defer w.Flush()
|
||||||
|
|
||||||
|
a.cpu.Save(w)
|
||||||
|
a.mmu.save(w)
|
||||||
|
a.io.save(w)
|
||||||
|
binary.Write(w, binary.BigEndian, a.isColor)
|
||||||
|
binary.Write(w, binary.BigEndian, a.fastMode)
|
||||||
|
binary.Write(w, binary.BigEndian, a.fastRequestsCounter)
|
||||||
|
|
||||||
|
for _, c := range a.cards {
|
||||||
|
if c != nil {
|
||||||
|
c.save(w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) load(filename string) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
// Ignore error if can't load the file
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
r := bufio.NewReader(f)
|
||||||
|
|
||||||
|
a.cpu.Load(r)
|
||||||
|
a.mmu.load(r)
|
||||||
|
a.io.load(r)
|
||||||
|
binary.Read(r, binary.BigEndian, &a.isColor)
|
||||||
|
binary.Read(r, binary.BigEndian, &a.fastMode)
|
||||||
|
binary.Read(r, binary.BigEndian, &a.fastRequestsCounter)
|
||||||
|
|
||||||
|
for _, c := range a.cards {
|
||||||
|
if c != nil {
|
||||||
|
c.load(r)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,9 +8,7 @@ import (
|
||||||
func NewApple2(romFile string, charRomFile string, clockMhz float64,
|
func NewApple2(romFile string, charRomFile string, clockMhz float64,
|
||||||
isColor bool, fastMode bool, panicSS bool) *Apple2 {
|
isColor bool, fastMode bool, panicSS bool) *Apple2 {
|
||||||
var a Apple2
|
var a Apple2
|
||||||
a.persistance = newPersistance(&a)
|
|
||||||
a.mmu = newMemoryManager(&a, romFile)
|
a.mmu = newMemoryManager(&a, romFile)
|
||||||
a.persistance.register(a.mmu)
|
|
||||||
a.cpu = core6502.NewNMOS6502(a.mmu)
|
a.cpu = core6502.NewNMOS6502(a.mmu)
|
||||||
if charRomFile != "" {
|
if charRomFile != "" {
|
||||||
a.cg = NewCharacterGenerator(charRomFile)
|
a.cg = NewCharacterGenerator(charRomFile)
|
||||||
|
@ -29,39 +27,37 @@ func NewApple2(romFile string, charRomFile string, clockMhz float64,
|
||||||
|
|
||||||
// Set the io in 0xc000
|
// Set the io in 0xc000
|
||||||
a.io = newIoC0Page(&a)
|
a.io = newIoC0Page(&a)
|
||||||
a.persistance.register(a.io)
|
|
||||||
a.mmu.setPages(0xc0, 0xc0, a.io)
|
a.mmu.setPages(0xc0, 0xc0, a.io)
|
||||||
|
|
||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) insertCard(c card, slot int) {
|
||||||
|
c.assign(a, slot)
|
||||||
|
a.cards[slot] = c
|
||||||
|
}
|
||||||
|
|
||||||
// AddDisk2 insterts a DiskII controller
|
// AddDisk2 insterts a DiskII controller
|
||||||
func (a *Apple2) AddDisk2(slot int, diskRomFile string, diskImage string) {
|
func (a *Apple2) AddDisk2(slot int, diskRomFile string, diskImage string) {
|
||||||
d := newCardDisk2(diskRomFile)
|
var c cardDisk2
|
||||||
d.cardBase.insert(a, slot)
|
c.loadRom(diskRomFile)
|
||||||
a.persistance.register(d)
|
a.insertCard(&c, slot)
|
||||||
|
|
||||||
if diskImage != "" {
|
if diskImage != "" {
|
||||||
diskette := loadDisquette(diskImage)
|
diskette := loadDisquette(diskImage)
|
||||||
//diskette.saveNib(diskImage + "bak")
|
//diskette.saveNib(diskImage + "bak")
|
||||||
d.drive[0].insertDiskette(diskette)
|
c.drive[0].insertDiskette(diskette)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddLanguageCard inserts a 16Kb card
|
// AddLanguageCard inserts a 16Kb card
|
||||||
func (a *Apple2) AddLanguageCard(slot int) {
|
func (a *Apple2) AddLanguageCard(slot int) {
|
||||||
d := newCardLanguage()
|
a.insertCard(&cardLanguage{}, slot)
|
||||||
d.cardBase.insert(a, slot)
|
|
||||||
d.applyState()
|
|
||||||
a.persistance.register(d)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSaturnCard inserts a 128Kb card
|
// AddSaturnCard inserts a 128Kb card
|
||||||
func (a *Apple2) AddSaturnCard(slot int) {
|
func (a *Apple2) AddSaturnCard(slot int) {
|
||||||
d := newCardSaturn()
|
a.insertCard(&cardSaturn{}, slot)
|
||||||
d.cardBase.insert(a, slot)
|
|
||||||
d.applyState()
|
|
||||||
a.persistance.register(d)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigureStdConsole uses stdin and stdout to interface with the Apple2
|
// ConfigureStdConsole uses stdin and stdout to interface with the Apple2
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
package apple2
|
package apple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type card interface {
|
||||||
|
loadRom(filename string)
|
||||||
|
assign(a *Apple2, slot int)
|
||||||
|
persistent
|
||||||
|
}
|
||||||
|
|
||||||
type cardBase struct {
|
type cardBase struct {
|
||||||
a *Apple2
|
a *Apple2
|
||||||
rom *memoryRange
|
rom *memoryRange
|
||||||
|
@ -8,7 +19,18 @@ type cardBase struct {
|
||||||
ssw [16]softSwitchW
|
ssw [16]softSwitchW
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) insert(a *Apple2, slot int) {
|
func (c *cardBase) loadRom(filename string) {
|
||||||
|
if c.a != nil {
|
||||||
|
panic("Rom must be loaded before inserting the card in the slot")
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
c.rom = newMemoryRange(0, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cardBase) assign(a *Apple2, slot int) {
|
||||||
c.a = a
|
c.a = a
|
||||||
c.slot = slot
|
c.slot = slot
|
||||||
if slot != 0 && c.rom != nil {
|
if slot != 0 && c.rom != nil {
|
||||||
|
@ -21,3 +43,11 @@ func (c *cardBase) insert(a *Apple2, slot int) {
|
||||||
a.io.addSoftSwitchW(uint8(0xC80+slot*0x10+i), c.ssw[i])
|
a.io.addSoftSwitchW(uint8(0xC80+slot*0x10+i), c.ssw[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *cardBase) save(w io.Writer) {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cardBase) load(r io.Reader) {
|
||||||
|
// Empty
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package apple2
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -32,10 +31,7 @@ type cardDisk2Drive struct {
|
||||||
position int
|
position int
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCardDisk2(filename string) *cardDisk2 {
|
func (c *cardDisk2) assign(a *Apple2, slot int) {
|
||||||
var c cardDisk2
|
|
||||||
c.rom = newMemoryRange(0, loadCardRom(filename))
|
|
||||||
|
|
||||||
// Phase control soft switches
|
// Phase control soft switches
|
||||||
// Lazy emulation. It only checks for phases on and move the head
|
// Lazy emulation. It only checks for phases on and move the head
|
||||||
// up or down depending on the previous phase.
|
// up or down depending on the previous phase.
|
||||||
|
@ -138,15 +134,8 @@ func newCardDisk2(filename string) *cardDisk2 {
|
||||||
//fmt.Printf("DISKII: Set write mode\n")
|
//fmt.Printf("DISKII: Set write mode\n")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadCardRom(filename string) []uint8 {
|
c.cardBase.assign(a, slot)
|
||||||
data, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *cardDisk2Drive) insertDiskette(dt *diskette16sector) {
|
func (d *cardDisk2Drive) insertDiskette(dt *diskette16sector) {
|
||||||
|
|
|
@ -45,8 +45,7 @@ const (
|
||||||
lcWriteEnabled = 2
|
lcWriteEnabled = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCardLanguage() *cardLanguage {
|
func (c *cardLanguage) assign(a *Apple2, slot int) {
|
||||||
var c cardLanguage
|
|
||||||
c.readState = false
|
c.readState = false
|
||||||
c.writeState = lcWriteEnabled
|
c.writeState = lcWriteEnabled
|
||||||
c.activeBank = 1
|
c.activeBank = 1
|
||||||
|
@ -66,7 +65,9 @@ func newCardLanguage() *cardLanguage {
|
||||||
c.writeState = lcWriteDisabled
|
c.writeState = lcWriteDisabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &c
|
|
||||||
|
c.cardBase.assign(a, slot)
|
||||||
|
c.applyState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardLanguage) ssAction(ss int) {
|
func (c *cardLanguage) ssAction(ss int) {
|
||||||
|
@ -131,6 +132,8 @@ func (c *cardLanguage) save(w io.Writer) {
|
||||||
c.ramBankA.save(w)
|
c.ramBankA.save(w)
|
||||||
c.ramBankB.save(w)
|
c.ramBankB.save(w)
|
||||||
c.ramUpper.save(w)
|
c.ramUpper.save(w)
|
||||||
|
|
||||||
|
c.cardBase.save(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardLanguage) load(r io.Reader) {
|
func (c *cardLanguage) load(r io.Reader) {
|
||||||
|
@ -142,4 +145,5 @@ func (c *cardLanguage) load(r io.Reader) {
|
||||||
c.ramUpper.load(r)
|
c.ramUpper.load(r)
|
||||||
|
|
||||||
c.applyState()
|
c.applyState()
|
||||||
|
c.cardBase.load(r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,7 @@ const (
|
||||||
saturnBlocks = 8
|
saturnBlocks = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCardSaturn() *cardSaturn {
|
func (c *cardSaturn) assign(a *Apple2, slot int) {
|
||||||
var c cardSaturn
|
|
||||||
c.readState = false
|
c.readState = false
|
||||||
c.writeState = lcWriteEnabled
|
c.writeState = lcWriteEnabled
|
||||||
c.activeBank = 1
|
c.activeBank = 1
|
||||||
|
@ -57,7 +56,8 @@ func newCardSaturn() *cardSaturn {
|
||||||
c.writeState = lcWriteDisabled
|
c.writeState = lcWriteDisabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &c
|
c.cardBase.assign(a, slot)
|
||||||
|
c.applyState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardSaturn) ssAction(ss int) {
|
func (c *cardSaturn) ssAction(ss int) {
|
||||||
|
@ -163,6 +163,7 @@ func (c *cardSaturn) save(w io.Writer) {
|
||||||
c.ramBankB[i].save(w)
|
c.ramBankB[i].save(w)
|
||||||
c.ramUpper[i].save(w)
|
c.ramUpper[i].save(w)
|
||||||
}
|
}
|
||||||
|
c.cardBase.save(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardSaturn) load(r io.Reader) {
|
func (c *cardSaturn) load(r io.Reader) {
|
||||||
|
@ -177,4 +178,5 @@ func (c *cardSaturn) load(r io.Reader) {
|
||||||
|
|
||||||
c.applyState()
|
c.applyState()
|
||||||
}
|
}
|
||||||
|
c.cardBase.load(r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
package apple2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"encoding/binary"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
type persistent interface {
|
|
||||||
save(w io.Writer)
|
|
||||||
load(r io.Reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
type persistance struct {
|
|
||||||
a *Apple2
|
|
||||||
items []persistent
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPersistance(a *Apple2) *persistance {
|
|
||||||
var p persistance
|
|
||||||
p.a = a
|
|
||||||
return &p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *persistance) register(i persistent) {
|
|
||||||
p.items = append(p.items, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *persistance) save(filename string) {
|
|
||||||
f, err := os.Create(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
w := bufio.NewWriter(f)
|
|
||||||
defer w.Flush()
|
|
||||||
|
|
||||||
binary.Write(w, binary.BigEndian, p.a.isColor)
|
|
||||||
binary.Write(w, binary.BigEndian, p.a.fastMode)
|
|
||||||
binary.Write(w, binary.BigEndian, p.a.fastRequestsCounter)
|
|
||||||
p.a.cpu.Save(w)
|
|
||||||
|
|
||||||
for _, v := range p.items {
|
|
||||||
v.save(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *persistance) load(filename string) {
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
// Ignore error if can't load the file
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
r := bufio.NewReader(f)
|
|
||||||
|
|
||||||
binary.Read(r, binary.BigEndian, &p.a.isColor)
|
|
||||||
binary.Read(r, binary.BigEndian, &p.a.fastMode)
|
|
||||||
binary.Read(r, binary.BigEndian, &p.a.fastRequestsCounter)
|
|
||||||
p.a.cpu.Load(r)
|
|
||||||
|
|
||||||
for _, v := range p.items {
|
|
||||||
v.load(r)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user