mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-01-14 07:30:04 +00:00
203 lines
5.0 KiB
Go
203 lines
5.0 KiB
Go
package izapple2
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/maps"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
type paramSpec struct {
|
|
name string
|
|
description string
|
|
defaultValue string
|
|
}
|
|
|
|
type cardBuilder struct {
|
|
name string
|
|
description string
|
|
defaultParams *[]paramSpec
|
|
requiresIIe bool
|
|
buildFunc func(params map[string]string) (Card, error)
|
|
}
|
|
|
|
const noCardName = "empty"
|
|
|
|
var commonParams = []paramSpec{
|
|
{"trace", "Enable debug messages", "false"},
|
|
{"tracess", "Trace softswitches", "false"},
|
|
}
|
|
|
|
var cardFactory map[string]*cardBuilder
|
|
|
|
func getCardFactory() map[string]*cardBuilder {
|
|
if cardFactory != nil {
|
|
return cardFactory
|
|
}
|
|
cardFactory = make(map[string]*cardBuilder)
|
|
cardFactory["brainboard"] = newCardBrainBoardBuilder()
|
|
cardFactory["brainboard2"] = newCardBrainBoardIIBuilder()
|
|
cardFactory["dan2sd"] = newCardDan2ControllerBuilder()
|
|
cardFactory["diskii"] = newCardDisk2Builder()
|
|
cardFactory["diskiiseq"] = newCardDisk2SequencerBuilder()
|
|
cardFactory["fastchip"] = newCardFastChipBuilder()
|
|
cardFactory["fujinet"] = newCardSmartPortFujinetBuilder()
|
|
cardFactory["inout"] = newCardInOutBuilder()
|
|
cardFactory["language"] = newCardLanguageBuilder()
|
|
cardFactory["softswitchlogger"] = newCardLoggerBuilder()
|
|
cardFactory["memexp"] = newCardMemoryExpansionBuilder()
|
|
cardFactory["mouse"] = newCardMouseBuilder()
|
|
cardFactory["multirom"] = newMultiRomCardBuilder()
|
|
cardFactory["parallel"] = newCardParallelPrinterBuilder()
|
|
cardFactory["saturn"] = newCardSaturnBuilder()
|
|
cardFactory["smartport"] = newCardSmartPortStorageBuilder()
|
|
cardFactory["swyftcard"] = newCardSwyftBuilder()
|
|
cardFactory["thunderclock"] = newCardThunderClockPlusBuilder()
|
|
cardFactory["videx"] = newCardVidexBuilder()
|
|
cardFactory["vidhd"] = newCardVidHDBuilder()
|
|
return cardFactory
|
|
}
|
|
|
|
func availableCards() []string {
|
|
names := maps.Keys(getCardFactory())
|
|
slices.Sort(names)
|
|
return names
|
|
}
|
|
|
|
func setupCard(a *Apple2, slot int, paramString string) (Card, error) {
|
|
actualArgs := splitConfigurationString(paramString, ',')
|
|
|
|
cardName := actualArgs[0]
|
|
if cardName == "" || cardName == noCardName {
|
|
return nil, nil
|
|
}
|
|
|
|
builder, ok := getCardFactory()[cardName]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown card %s", cardName)
|
|
}
|
|
|
|
if builder.requiresIIe && !a.isApple2e {
|
|
return nil, fmt.Errorf("card %s requires an Apple IIe", builder.name)
|
|
}
|
|
|
|
finalParams := make(map[string]string)
|
|
for _, commonParam := range commonParams {
|
|
finalParams[commonParam.name] = commonParam.defaultValue
|
|
}
|
|
if builder.defaultParams != nil {
|
|
for _, defaultParam := range *builder.defaultParams {
|
|
finalParams[defaultParam.name] = defaultParam.defaultValue
|
|
}
|
|
}
|
|
|
|
for i := 1; i < len(actualArgs); i++ {
|
|
actualArgSides := splitConfigurationString(actualArgs[i], '=')
|
|
actualArgName := strings.ToLower(actualArgSides[0])
|
|
|
|
if _, ok := finalParams[actualArgName]; !ok {
|
|
return nil, fmt.Errorf("unknown parameter %s", actualArgSides[0])
|
|
}
|
|
if len(actualArgSides) > 2 {
|
|
return nil, fmt.Errorf("invalid parameter value for %s", actualArgSides[0])
|
|
}
|
|
if len(actualArgSides) == 1 {
|
|
finalParams[actualArgName] = "true"
|
|
} else {
|
|
finalParams[actualArgName] = actualArgSides[1]
|
|
}
|
|
}
|
|
|
|
card, err := builder.buildFunc(finalParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Common parameters
|
|
traceSS := paramsGetBool(finalParams, "tracess")
|
|
if traceSS {
|
|
a.io.traceSlot(slot)
|
|
}
|
|
|
|
debug := paramsGetBool(finalParams, "trace")
|
|
|
|
card.setName(builder.name)
|
|
card.setDebug(debug)
|
|
card.assign(a, slot)
|
|
a.cards[slot] = card
|
|
return card, err
|
|
}
|
|
|
|
func paramsGetBool(params map[string]string, name string) bool {
|
|
value, ok := params[name]
|
|
if !ok {
|
|
value = "false"
|
|
}
|
|
return value == "true"
|
|
}
|
|
|
|
func paramsGetString(params map[string]string, name string) string {
|
|
value, ok := params[name]
|
|
if !ok {
|
|
value = ""
|
|
}
|
|
return value
|
|
}
|
|
|
|
func paramsGetPath(params map[string]string, name string) string {
|
|
value := paramsGetString(params, name)
|
|
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
|
|
value = value[1 : len(value)-1]
|
|
}
|
|
return value
|
|
}
|
|
|
|
func paramsGetInt(params map[string]string, name string) (int, error) {
|
|
value, ok := params[name]
|
|
if !ok {
|
|
return 0, fmt.Errorf("missing parameter %s", name)
|
|
}
|
|
return strconv.Atoi(value)
|
|
}
|
|
|
|
// Returns a 1 based array of bools
|
|
func paramsGetDIPs(params map[string]string, name string, size int) ([]bool, error) {
|
|
value, ok := params[name]
|
|
if !ok {
|
|
return nil, fmt.Errorf("missing parameter %s", name)
|
|
}
|
|
if len(value) != 8 {
|
|
return nil, fmt.Errorf("DIP switches must be 8 characters long")
|
|
}
|
|
result := make([]bool, size+1)
|
|
for i := 0; i < 8; i++ {
|
|
result[i+1] = value[i] == '1'
|
|
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func splitConfigurationString(s string, separator rune) []string {
|
|
// Split by comma, but not inside quotes
|
|
var result []string
|
|
var current string
|
|
inQuote := false
|
|
for _, c := range s {
|
|
if c == '"' {
|
|
inQuote = !inQuote
|
|
}
|
|
if c == separator && !inQuote {
|
|
result = append(result, current)
|
|
current = ""
|
|
} else {
|
|
current += string(c)
|
|
}
|
|
}
|
|
if current != "" {
|
|
result = append(result, current)
|
|
}
|
|
return result
|
|
}
|