2024-01-06 20:48:23 +00:00
|
|
|
package izapple2
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2024-02-08 19:24:37 +00:00
|
|
|
"strings"
|
2024-01-06 20:48:23 +00:00
|
|
|
|
|
|
|
"github.com/ivanizag/iz6502"
|
|
|
|
)
|
|
|
|
|
|
|
|
func configure(configuration *configuration) (*Apple2, error) {
|
|
|
|
a := newApple2()
|
|
|
|
a.Name = configuration.get(confName)
|
|
|
|
|
|
|
|
// Configure the board
|
|
|
|
board := configuration.get(confBoard)
|
|
|
|
a.board = board
|
|
|
|
a.isApple2e = board == "2e"
|
|
|
|
|
|
|
|
addApple2SoftSwitches(a.io)
|
|
|
|
if a.isApple2e {
|
|
|
|
a.mmu.initExtendedRAM(1)
|
|
|
|
addApple2ESoftSwitches(a.io)
|
|
|
|
}
|
|
|
|
if board == "base64a" {
|
|
|
|
addBase64aSoftSwitches(a.io)
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu := configuration.get(confCpu)
|
|
|
|
switch cpu {
|
|
|
|
case "6502":
|
|
|
|
a.cpu = iz6502.NewNMOS6502(a.mmu)
|
|
|
|
case "65c02":
|
|
|
|
a.cpu = iz6502.NewCMOS65c02(a.mmu)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := a.loadRom(configuration.get(confRom))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = setupCharactedGenerator(a, board, configuration.get(confCharRom))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
a.setProfiling(configuration.getFlag(confProfile))
|
|
|
|
a.SetForceCaps(configuration.getFlag(confForceCaps))
|
|
|
|
|
|
|
|
err = a.setClockSpeed(configuration.get(confSpeed))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add cards on the slots
|
|
|
|
for i := 0; i < 8; i++ {
|
|
|
|
cardConfig := configuration.get(fmt.Sprintf("s%v", i))
|
|
|
|
if cardConfig != "" {
|
|
|
|
_, err := setupCard(a, i, cardConfig)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-08 19:24:37 +00:00
|
|
|
// Add mods
|
|
|
|
mods := strings.Split(configuration.get(confMods), ",")
|
|
|
|
for _, mod := range mods {
|
|
|
|
switch strings.TrimSpace(mod) {
|
|
|
|
//case "shift":
|
|
|
|
// setupShiftedKeyboard(a)
|
|
|
|
case "four-colors":
|
|
|
|
// This removes the mod to have 6 colors sent by Wozniak to Byte
|
|
|
|
// magazine. See: https://archive.org/details/byte-magazine-1979-06/page/n67/mode/2up?view=theater
|
|
|
|
a.isFourColors = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-06 20:48:23 +00:00
|
|
|
// Add optional accesories including the aux slot
|
|
|
|
ramWorksSize := configuration.get(confRamworks)
|
|
|
|
if ramWorksSize != "" && ramWorksSize != "none" {
|
|
|
|
err = setupRAMWorksCard(a, ramWorksSize)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if configuration.getFlag(confRgb) {
|
|
|
|
setupRGBCard(a)
|
|
|
|
}
|
|
|
|
|
|
|
|
nsc := configuration.get(confNsc)
|
|
|
|
if nsc != "none" && nsc != "" {
|
|
|
|
err = setupNoSlotClock(a, nsc)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if configuration.getFlag(confRomx) {
|
|
|
|
err := setupRomX(a)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = setupTracers(a, configuration.get(confTrace))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newApple2() *Apple2 {
|
|
|
|
var a Apple2
|
|
|
|
|
|
|
|
a.Name = "Pending"
|
|
|
|
a.mmu = newMemoryManager(&a)
|
|
|
|
a.io = newIoC0Page(&a)
|
|
|
|
a.commandChannel = make(chan command, 100)
|
|
|
|
|
|
|
|
return &a
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Apple2) setClockSpeed(speed string) error {
|
|
|
|
if speed == "full" {
|
|
|
|
a.cycleDurationNs = 0
|
|
|
|
} else if speed == "ntsc" {
|
|
|
|
a.cycleDurationNs = 1000.0 / CPUClockMhz
|
|
|
|
} else if speed == "pal" {
|
|
|
|
a.cycleDurationNs = 1000.0 / cpuClockEuroMhz
|
|
|
|
} else {
|
|
|
|
clockMhz, err := strconv.ParseFloat(speed, 64)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid clock speed: %s", speed)
|
|
|
|
}
|
|
|
|
a.cycleDurationNs = 1000.0 / clockMhz
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Apple2) setProfiling(value bool) {
|
|
|
|
a.profile = value
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetForceCaps allows the caps state to be toggled at runtime
|
|
|
|
func (a *Apple2) SetForceCaps(value bool) {
|
|
|
|
a.forceCaps = value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Apple2) loadRom(filename string) error {
|
2024-01-15 22:54:15 +00:00
|
|
|
if a.board == "base64a" && filename == "<custom>" {
|
2024-01-06 20:48:23 +00:00
|
|
|
// The ROM of the base64a has several file and pages
|
|
|
|
loadBase64aRom(a)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
data, _, err := LoadResource(filename)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
size := len(data)
|
|
|
|
|
|
|
|
romBase := 0x10000 - size
|
2024-01-22 22:09:35 +00:00
|
|
|
a.mmu.physicalROM = newMemoryRangeROM(uint16(romBase), data, "Main ROM")
|
2024-01-06 20:48:23 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateConfiguredApple is a device independent main. Video, keyboard and speaker won't be defined
|
|
|
|
func CreateConfiguredApple() (*Apple2, error) {
|
|
|
|
// Get configuration from defaults and the command line
|
|
|
|
configuration, filename, err := getConfigurationFromCommandLine()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if filename != "" {
|
|
|
|
// Try loading as diskette
|
|
|
|
_, err := LoadDiskette(filename)
|
|
|
|
isDiskette := err == nil
|
|
|
|
if isDiskette {
|
|
|
|
// Let's force a DiskII with the diskette in slot 6
|
|
|
|
configuration.set(confS6, fmt.Sprintf("diskii,disk1=\"%s\"", filename))
|
|
|
|
} else {
|
|
|
|
// Let's force a Smartport card with a block device in slot 7
|
|
|
|
configuration.set(confS7, fmt.Sprintf("smartport,image1=\"%s\"", filename))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a, err := configure(configuration)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return a, nil
|
|
|
|
}
|