izapple2/cardBuilder.go

224 lines
5.7 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"},
{"panicss", "Panic on unimplemented 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["prodosromdrive"] = newCardProDOSRomDriveBuilder()
cardFactory["prodosromcard3"] = newCardProDOSRomCard3Builder()
//cardFactory["prodosnvramdrive"] = newCardProDOSNVRAMDriveBuilder()
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 (cb *cardBuilder) fullDefaultParams() map[string]string {
finalParams := make(map[string]string)
for _, commonParam := range commonParams {
finalParams[commonParam.name] = commonParam.defaultValue
}
if cb.defaultParams != nil {
for _, defaultParam := range *cb.defaultParams {
finalParams[defaultParam.name] = defaultParam.defaultValue
}
}
return finalParams
}
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 := builder.fullDefaultParams()
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
if paramsGetBool(finalParams, "tracess") {
a.io.traceSlot(slot)
}
if paramsGetBool(finalParams, "panicss") {
a.io.panicNotImplementedSlot(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)
}
func paramsGetUInt8(params map[string]string, name string) (uint8, error) {
value, ok := params[name]
if !ok {
return 0, fmt.Errorf("missing parameter %s", name)
}
result, err := strconv.ParseUint(value, 10, 8)
return uint8(result), err
}
// 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
}