mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-01-02 20:29:44 +00:00
262 lines
6.3 KiB
Go
262 lines
6.3 KiB
Go
package izapple2
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/ivanizag/iz6502"
|
|
)
|
|
|
|
func configure(configuration *configuration) (*Apple2, error) {
|
|
var a Apple2
|
|
a.Name = configuration.get(confName)
|
|
a.mmu = newMemoryManager(&a)
|
|
a.video = newVideo(&a)
|
|
a.io = newIoC0Page(&a)
|
|
a.commandChannel = make(chan command, 100)
|
|
|
|
// Configure the board
|
|
board := configuration.get(confBoard)
|
|
a.board = board
|
|
|
|
err := setupCharactedGenerator(&a, board, configuration.get(confCharRom))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addApple2SoftSwitches(a.io)
|
|
switch board {
|
|
case "2plus":
|
|
a.mmu.initMainRAM()
|
|
case "2e":
|
|
a.isApple2e = true
|
|
a.mmu.initMainRAM()
|
|
a.mmu.initExtendedRAM(1)
|
|
a.hasLowerCase = true
|
|
addApple2ESoftSwitches(a.io)
|
|
case "base64a":
|
|
a.mmu.initMainRAM()
|
|
a.hasLowerCase = true
|
|
addBase64aSoftSwitches(a.io)
|
|
case "basis108":
|
|
memBasis108 := newMemoryRangeBasis108()
|
|
videoBasis108 := newVideoBasis108(&a, memBasis108)
|
|
a.mmu.initCustomRAM(memBasis108)
|
|
a.video = videoBasis108
|
|
a.hasLowerCase = true
|
|
addBasis108SoftSwitches(a.io, memBasis108, videoBasis108, a.cg)
|
|
default:
|
|
return nil, fmt.Errorf("board %s not supported it must be '2plus', '2e', 'base64a', 'basis108", board)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
|
|
// 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 (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 {
|
|
if filename == "<custom>" {
|
|
switch a.board {
|
|
case "base64a":
|
|
return loadBase64aRom(a)
|
|
case "basis108":
|
|
return loadBasis108Rom(a)
|
|
default:
|
|
return fmt.Errorf("no custom ROM defined for board %s", a.board)
|
|
}
|
|
}
|
|
|
|
data, _, err := LoadResource(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
size := len(data)
|
|
|
|
romBase := 0x10000 - size
|
|
a.mmu.physicalROM = newMemoryRangeROM(uint16(romBase), data, "Main ROM")
|
|
return nil
|
|
}
|
|
|
|
const pagedRomChipWindowSize = 0x800 // 2 KB
|
|
const pagedRomChipCount = 6 // There has to be six ROM chips
|
|
const pagedRomWindowSize = pagedRomChipWindowSize * pagedRomChipCount // To cover 0xd000 to 0xffff
|
|
func loadMultiPageRom(a *Apple2, filenames []string) error {
|
|
if len(filenames) != pagedRomChipCount {
|
|
return fmt.Errorf("expected %d ROM files, got %d", pagedRomChipCount, len(filenames))
|
|
}
|
|
|
|
// Load the 6 PROM dumps
|
|
proms := make([][]uint8, pagedRomChipCount)
|
|
banks := 1
|
|
for i, filename := range filenames {
|
|
var err error
|
|
proms[i], _, err = LoadResource(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pages := len(proms[i]) / pagedRomChipWindowSize
|
|
if pages > banks {
|
|
banks = pages
|
|
}
|
|
}
|
|
|
|
// Init the array of banks
|
|
romBanksBytes := make([][]uint8, banks)
|
|
for bank := range romBanksBytes {
|
|
romBanksBytes[bank] = make([]uint8, 0, pagedRomWindowSize)
|
|
}
|
|
|
|
// Distribute the per chip banks on the full rom banks
|
|
for _, romData := range proms {
|
|
for bank := range romBanksBytes {
|
|
start := (bank * pagedRomChipWindowSize) % len(romData)
|
|
romBanksBytes[bank] = append(romBanksBytes[bank], romData[start:start+pagedRomChipWindowSize]...)
|
|
}
|
|
}
|
|
|
|
// Create paged ROM
|
|
romData := make([]uint8, 0, pagedRomWindowSize*banks)
|
|
for _, bank := range romBanksBytes {
|
|
romData = append(romData, bank...)
|
|
}
|
|
rom := newMemoryRangePagedROM(0xd000, romData, "Multipage main ROM", uint8(banks))
|
|
|
|
// Start with first bank active
|
|
rom.setPage(0)
|
|
|
|
a.mmu.physicalROM = rom
|
|
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
|
|
}
|