2019-05-17 23:28:20 +02:00
|
|
|
package apple2
|
|
|
|
|
2019-10-06 01:26:00 +02:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/ivanizag/apple2/core6502"
|
|
|
|
)
|
2019-05-17 23:28:20 +02:00
|
|
|
|
2019-10-12 21:37:37 +02:00
|
|
|
// newApple2 instantiates an apple2
|
|
|
|
func newApple2plus() *Apple2 {
|
2019-05-17 23:28:20 +02:00
|
|
|
var a Apple2
|
2019-06-09 23:54:27 +02:00
|
|
|
a.Name = "Apple ][+"
|
2019-06-07 20:01:20 +02:00
|
|
|
a.mmu = newMemoryManager(&a)
|
2019-05-17 23:28:20 +02:00
|
|
|
a.cpu = core6502.NewNMOS6502(a.mmu)
|
2019-10-12 21:37:37 +02:00
|
|
|
a.io = newIoC0Page(&a)
|
|
|
|
addApple2SoftSwitches(a.io)
|
|
|
|
|
|
|
|
return &a
|
|
|
|
}
|
|
|
|
|
2019-10-21 00:06:28 +02:00
|
|
|
func newApple2e() *Apple2 {
|
|
|
|
var a Apple2
|
|
|
|
a.Name = "Apple IIe"
|
|
|
|
a.mmu = newMemoryManager(&a)
|
2019-11-06 00:32:34 +01:00
|
|
|
a.cpu = core6502.NewNMOS6502(a.mmu)
|
2019-10-21 00:06:28 +02:00
|
|
|
a.io = newIoC0Page(&a)
|
2019-11-04 00:23:03 +01:00
|
|
|
a.mmu.InitRAMalt()
|
2019-10-21 00:06:28 +02:00
|
|
|
addApple2SoftSwitches(a.io)
|
|
|
|
addApple2ESoftSwitches(a.io)
|
|
|
|
|
|
|
|
return &a
|
|
|
|
}
|
|
|
|
|
2019-10-12 21:37:37 +02:00
|
|
|
func newApple2eEnhanced() *Apple2 {
|
|
|
|
var a Apple2
|
|
|
|
a.Name = "Apple //e"
|
|
|
|
a.mmu = newMemoryManager(&a)
|
|
|
|
a.cpu = core6502.NewCMOS65c02(a.mmu)
|
|
|
|
a.io = newIoC0Page(&a)
|
2019-11-04 00:23:03 +01:00
|
|
|
a.mmu.InitRAMalt()
|
2019-10-12 21:37:37 +02:00
|
|
|
addApple2SoftSwitches(a.io)
|
|
|
|
addApple2ESoftSwitches(a.io)
|
|
|
|
|
|
|
|
return &a
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Apple2) setup(isColor bool, clockMhz float64, fastMode bool) {
|
2019-05-17 23:28:20 +02:00
|
|
|
a.commandChannel = make(chan int, 100)
|
|
|
|
a.isColor = isColor
|
|
|
|
a.fastMode = fastMode
|
|
|
|
|
|
|
|
if clockMhz <= 0 {
|
|
|
|
// Full speed
|
|
|
|
a.cycleDurationNs = 0
|
|
|
|
} else {
|
|
|
|
a.cycleDurationNs = 1000.0 / clockMhz
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-18 16:43:51 +02:00
|
|
|
func (a *Apple2) insertCard(c card, slot int) {
|
|
|
|
c.assign(a, slot)
|
|
|
|
a.cards[slot] = c
|
|
|
|
}
|
|
|
|
|
2019-06-07 20:01:20 +02:00
|
|
|
const (
|
|
|
|
apple2RomSize = 12 * 1024
|
|
|
|
apple2eRomSize = 16 * 1024
|
|
|
|
)
|
|
|
|
|
|
|
|
// LoadRom loads a standard Apple2+ or 2e ROM
|
2019-10-06 01:26:00 +02:00
|
|
|
func (a *Apple2) LoadRom(filename string) error {
|
|
|
|
data, err := loadResource(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-06-07 20:01:20 +02:00
|
|
|
size := len(data)
|
|
|
|
if size != apple2RomSize && size != apple2eRomSize {
|
2019-10-06 01:26:00 +02:00
|
|
|
return errors.New("Rom size not supported")
|
2019-06-07 20:01:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-11-03 18:22:10 +01:00
|
|
|
mmu.physicalROM[0] = newMemoryRange(0xd000, data[romStart:])
|
2019-10-06 01:26:00 +02:00
|
|
|
return nil
|
2019-06-07 20:01:20 +02:00
|
|
|
}
|
|
|
|
|
2019-10-02 23:39:39 +02:00
|
|
|
// AddDisk2 inserts a DiskII controller
|
2019-10-06 01:26:00 +02:00
|
|
|
func (a *Apple2) AddDisk2(slot int, diskRomFile string, diskImage string) error {
|
2019-05-18 16:43:51 +02:00
|
|
|
var c cardDisk2
|
2019-10-06 01:26:00 +02:00
|
|
|
data, err := loadResource(diskRomFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.loadRom(data)
|
2019-05-18 16:43:51 +02:00
|
|
|
a.insertCard(&c, slot)
|
2019-05-17 23:28:20 +02:00
|
|
|
|
|
|
|
if diskImage != "" {
|
2019-12-22 14:15:19 +01:00
|
|
|
diskette, err := loadDisquette(diskImage)
|
2019-10-06 01:26:00 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-05-18 16:43:51 +02:00
|
|
|
c.drive[0].insertDiskette(diskette)
|
2019-05-17 23:28:20 +02:00
|
|
|
}
|
2019-10-06 01:26:00 +02:00
|
|
|
|
|
|
|
return nil
|
2019-05-17 23:28:20 +02:00
|
|
|
}
|
|
|
|
|
2019-10-02 23:39:39 +02:00
|
|
|
// AddHardDisk adds a ProDos hard dirve with a 2MG image
|
2019-11-01 18:48:39 +01:00
|
|
|
func (a *Apple2) AddHardDisk(slot int, hdImage string, trace bool) error {
|
2019-10-02 23:39:39 +02:00
|
|
|
var c cardHardDisk
|
2019-11-01 18:48:39 +01:00
|
|
|
c.setTrace(trace)
|
2019-10-02 23:39:39 +02:00
|
|
|
c.loadRom(buildHardDiskRom(slot))
|
|
|
|
a.insertCard(&c, slot)
|
|
|
|
|
2019-10-06 01:26:00 +02:00
|
|
|
hd, err := openHardDisk2mg(hdImage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-10-02 23:39:39 +02:00
|
|
|
c.addDisk(hd)
|
2019-10-06 01:26:00 +02:00
|
|
|
return nil
|
2019-10-02 23:39:39 +02:00
|
|
|
}
|
|
|
|
|
2019-11-10 14:20:31 +01:00
|
|
|
// AddVidHD adds a card with the signature of VidHD
|
|
|
|
func (a *Apple2) AddVidHD(slot int) {
|
|
|
|
var c cardVidHD
|
|
|
|
c.loadRom(buildVidHDRom())
|
|
|
|
a.insertCard(&c, slot)
|
|
|
|
}
|
|
|
|
|
2019-11-12 23:47:48 +01:00
|
|
|
// AddFastChip adds a card with the signature of VidHD
|
|
|
|
func (a *Apple2) AddFastChip(slot int) {
|
|
|
|
var c cardFastChip
|
|
|
|
c.loadRom(buildFastChipRom())
|
|
|
|
a.insertCard(&c, slot)
|
|
|
|
}
|
|
|
|
|
2019-05-17 23:28:20 +02:00
|
|
|
// AddLanguageCard inserts a 16Kb card
|
|
|
|
func (a *Apple2) AddLanguageCard(slot int) {
|
2019-05-18 16:43:51 +02:00
|
|
|
a.insertCard(&cardLanguage{}, slot)
|
2019-05-17 23:28:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddSaturnCard inserts a 128Kb card
|
|
|
|
func (a *Apple2) AddSaturnCard(slot int) {
|
2019-05-18 16:43:51 +02:00
|
|
|
a.insertCard(&cardSaturn{}, slot)
|
2019-05-17 23:28:20 +02:00
|
|
|
}
|
|
|
|
|
2019-09-28 13:37:42 +02:00
|
|
|
// AddThunderClockPlusCard inserts a ThunderClock Plus clock card
|
2019-10-06 01:26:00 +02:00
|
|
|
func (a *Apple2) AddThunderClockPlusCard(slot int, romFile string) error {
|
2019-09-28 13:37:42 +02:00
|
|
|
var c cardThunderClockPlus
|
2019-10-06 01:26:00 +02:00
|
|
|
data, err := loadResource(romFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
c.loadRom(data)
|
2019-09-28 13:37:42 +02:00
|
|
|
a.insertCard(&c, slot)
|
2019-10-06 01:26:00 +02:00
|
|
|
return nil
|
2019-09-28 13:37:42 +02:00
|
|
|
}
|
|
|
|
|
2019-05-18 23:40:59 +02:00
|
|
|
// AddCardLogger inserts a fake card that logs accesses
|
|
|
|
func (a *Apple2) AddCardLogger(slot int) {
|
|
|
|
a.insertCard(&cardLogger{}, slot)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddCardInOut inserts a fake card that interfaces with the emulator host
|
|
|
|
func (a *Apple2) AddCardInOut(slot int) {
|
|
|
|
a.insertCard(&cardInOut{}, slot)
|
|
|
|
}
|
|
|
|
|
2019-05-17 23:28:20 +02:00
|
|
|
// SetKeyboardProvider attaches an external keyboard provider
|
|
|
|
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
|
|
|
|
a.io.setKeyboardProvider(kb)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetSpeakerProvider attaches an external keyboard provider
|
|
|
|
func (a *Apple2) SetSpeakerProvider(s SpeakerProvider) {
|
|
|
|
a.io.setSpeakerProvider(s)
|
|
|
|
}
|
2019-08-06 00:37:27 +02:00
|
|
|
|
|
|
|
// SetJoysticksProvider attaches an external joysticks provider
|
|
|
|
func (a *Apple2) SetJoysticksProvider(j JoysticksProvider) {
|
|
|
|
a.io.setJoysticksProvider(j)
|
|
|
|
}
|