Skip to content

Commit

Permalink
Improve usage message and automate the inclusion in README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanizag committed Feb 12, 2024
1 parent 9178372 commit 51a7f17
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 65 deletions.
55 changes: 48 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,9 @@ Only valid on SDL mode

### Command line options

<!-- doc/usage.txt start -->
```terminal
Usage: izapple [file]
Usage: izapple2 [file]
file
path to image to use on the boot device
-charrom string
Expand All @@ -200,12 +201,14 @@ Usage: izapple [file]
force all letters to be uppercased (no need for caps lock!)
-model string
set base model (default "2enh")
-mods string
comma separated list of mods applied to the board, available mods are 'shift', 'four-colors
-nsc string
add a DS1216 No-Slot-Clock on the main ROM (use 'main') or a slot ROM (default "none")
add a DS1216 No-Slot-Clock on the main ROM (use 'main') or a slot ROM (default "main")
-profile
generate profile trace to analyse with pprof
-ramworks string
memory to use with RAMWorks card, max is 16384 (default "none")
memory to use with RAMWorks card, max is 16384 (default "8192")
-rgb
emulate the RGB modes of the 80col RGB card for DHGR
-rom string
Expand All @@ -215,7 +218,7 @@ Usage: izapple [file]
-s0 string
slot 0 configuration. (default "language")
-s1 string
slot 1 configuration. (default "parallel")
slot 1 configuration. (default "empty")
-s2 string
slot 2 configuration. (default "vidhd")
-s3 string
Expand All @@ -233,11 +236,49 @@ Usage: izapple [file]
-trace string
trace CPU execution with one or more comma separated tracers (default "none")
The available pre configured models are: swyft, 2e, 2enh, 2plus, base64a.
The available cards are: brainboard, diskii, memexp, mouse, swyftcard, inout, smartport, thunderclock, fujinet, videx, vidhd, diskiiseq, fastchip, language, softswitchlogger, parallel, saturn.
The available tracers are: ucsd, cpu, ss, ssreg, panicSS, mos, mosfull, mli.
The available pre-configured models are:
2: Apple ][
2e: Apple IIe
2enh: Apple //e
2plus: Apple ][+
base64a: Base 64A
swyft: swyft
The available cards are:
brainboard: Firmware card. It has two ROM banks
brainboard2: Firmware card. It has up to four ROM banks
dan2sd: Apple II Peripheral Card that Interfaces to a ATMEGA328P for SD card storage
diskii: Disk II interface card
diskiiseq: Disk II interface card emulating the Woz state machine
fastchip: Accelerator card for Apple IIe (limited support)
fujinet: SmartPort interface card hosting the Fujinet
inout: Card to test I/O
language: Language card with 16 extra KB for the Apple ][ and ][+
memexp: Memory expansion card
mouse: Mouse card implementation, does not emulate a real card, only the firmware behaviour
multirom: Multiple Image ROM card
parallel: Card to dump to a file what would be printed to a parallel printer
saturn: RAM card with 128Kb, it's like 8 language cards
smartport: SmartPort interface card
softswitchlogger: Card to log softswitch accesses
swyftcard: Card with the ROM needed to run the Swyftcard word processing system
thunderclock: Clock card
videx: Videx compatible 80 columns card
vidhd: Firmware signature of the VidHD card to trick Total Replay to use the SHR mode
The available tracers are:
cpm65: Trace CPM65 BDOS calls
cpu: Trace CPU execution
mli: Trace ProDOS MLI calls
mos: Trace MOS calls with Applecorn skipping terminal IO
mosfull: Trace MOS calls with Applecorn
panicSS: Panic on unimplemented softswitches
ss: Trace sotfswiches calls
ssreg: Trace sotfswiches registrations
ucsd: Trace UCSD system calls
```
<!-- doc/usage.txt end -->

## Building from source

Expand Down
7 changes: 6 additions & 1 deletion apple2Tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ type apple2Tester struct {
}

func makeApple2Tester(model string, overrides *configuration) (*apple2Tester, error) {
config, err := getConfigurationFromModel(model, overrides)
models, _, err := loadConfigurationModelsAndDefault()
if err != nil {
return nil, err
}

config, err := models.getWithOverrides(model, overrides)
if err != nil {
return nil, err
}
Expand Down
86 changes: 36 additions & 50 deletions configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"embed"
"flag"
"fmt"
"os"
"strings"

"golang.org/x/exp/slices"
Expand Down Expand Up @@ -83,19 +82,19 @@ func (c *configuration) set(key string, value string) {
c.data[key] = value
}

func initConfigurationModels() (*configurationModels, error) {
models := configurationModels{}
func loadConfigurationModelsAndDefault() (*configurationModels, *configuration, error) {
models := &configurationModels{}
dir, err := configurationFiles.ReadDir("configs")
if err != nil {
return nil, err
return nil, nil, err
}

models.preconfiguredConfigs = make(map[string]*configuration)
for _, file := range dir {
if file.Type().IsRegular() && strings.HasSuffix(strings.ToLower(file.Name()), configSuffix) {
content, err := configurationFiles.ReadFile("configs/" + file.Name())
if err != nil {
return nil, err
return nil, nil, err
}
lines := strings.Split(string(content), "\n")
config := newConfiguration()
Expand All @@ -106,7 +105,7 @@ func initConfigurationModels() (*configurationModels, error) {
}
colonPos := strings.Index(line, ":")
if colonPos < 0 {
return nil, fmt.Errorf("invalid configuration in %s:%d", file.Name(), iLine)
return nil, nil, fmt.Errorf("invalid configuration in %s:%d", file.Name(), iLine)
}
key := strings.TrimSpace(line[:colonPos])
value := strings.TrimSpace(line[colonPos+1:])
Expand All @@ -117,23 +116,13 @@ func initConfigurationModels() (*configurationModels, error) {
}
}

// Check validity of base configuration
/* base, ok := configs.preconfiguredConfigs[baseConfigurationName]
if !ok {
return nil, fmt.Errorf("base configuration %s.cfg not found", baseConfigurationName)
}
model, ok := base[argModel]
if !ok {
return nil, fmt.Errorf("model not found in base configuration %s.cfg", baseConfigurationName)
}
if _, ok := configs.preconfiguredConfigs[model]; !ok {
return nil, fmt.Errorf("model %s not found and used in base configuration %s.cfg", model, baseConfigurationName)
}
*/

// Todo check that all configs have valid keys
defaultConfig, err := models.get(defaultConfiguration)
if err != nil {
return nil, nil, err
}
defaultConfig.set(confModel, defaultConfiguration)

return &models, nil
return models, defaultConfig, nil
}

func mergeConfigs(base *configuration, addition *configuration) *configuration {
Expand All @@ -147,7 +136,7 @@ func mergeConfigs(base *configuration, addition *configuration) *configuration {
return result
}

func (c *configurationModels) getFromModel(name string) (*configuration, error) {
func (c *configurationModels) get(name string) (*configuration, error) {
name = strings.TrimSpace(name)
config, ok := c.preconfiguredConfigs[name]
if !ok {
Expand All @@ -159,7 +148,7 @@ func (c *configurationModels) getFromModel(name string) (*configuration, error)
return config, nil
}

parent, err := c.getFromModel(parentName)
parent, err := c.get(parentName)
if err != nil {
return nil, err
}
Expand All @@ -179,13 +168,8 @@ func (c *configurationModels) availableModels() []string {
return models
}

func getConfigurationFromModel(model string, overrides *configuration) (*configuration, error) {
configurationModels, err := initConfigurationModels()
if err != nil {
return nil, err
}

configValues, err := configurationModels.getFromModel(model)
func (c *configurationModels) getWithOverrides(model string, overrides *configuration) (*configuration, error) {
configValues, err := c.get(model)
if err != nil {
return nil, err
}
Expand All @@ -196,12 +180,7 @@ func getConfigurationFromModel(model string, overrides *configuration) (*configu
return configValues, nil
}

func getConfigurationFromCommandLine() (*configuration, string, error) {
configurationModels, err := initConfigurationModels()
if err != nil {
return nil, "", err
}

func setupFlags(models *configurationModels, configuration *configuration) error {
paramDescription := map[string]string{
confModel: "set base model",
confRom: "main rom file",
Expand All @@ -228,16 +207,10 @@ func getConfigurationFromCommandLine() (*configuration, string, error) {

boolParams := []string{confProfile, confForceCaps, confRgb, confRomx}

configuration, err := configurationModels.getFromModel(defaultConfiguration)
if err != nil {
return nil, "", err
}
configuration.set(confModel, defaultConfiguration)

for name, description := range paramDescription {
defaultValue, ok := configuration.getHas(name)
if !ok {
return nil, "", fmt.Errorf("default value not found for %s", name)
return fmt.Errorf("default value not found for %s", name)
}
if slices.Contains(boolParams, name) {
flag.Bool(name, defaultValue == "true", description)
Expand All @@ -248,14 +221,14 @@ func getConfigurationFromCommandLine() (*configuration, string, error) {

flag.Usage = func() {
out := flag.CommandLine.Output()
fmt.Fprintf(out, "Usage: %s [file]\n", os.Args[0])
fmt.Fprintf(out, "Usage: %s [file]\n", flag.CommandLine.Name())
fmt.Fprintf(out, " file\n")
fmt.Fprintf(out, " path to image to use on the boot device\n")
flag.PrintDefaults()

fmt.Fprintf(out, "\nThe available pre configured models are:\n")
for _, model := range configurationModels.availableModels() {
config, _ := configurationModels.getFromModel(model)
fmt.Fprintf(out, "\nThe available pre-configured models are:\n")
for _, model := range models.availableModels() {
config, _ := models.get(model)
fmt.Fprintf(out, " %s: %s\n", model, config.get(confName))
}

Expand All @@ -272,12 +245,23 @@ func getConfigurationFromCommandLine() (*configuration, string, error) {
}
}

return nil
}

func getConfigurationFromCommandLine() (*configuration, string, error) {
models, configuration, err := loadConfigurationModelsAndDefault()
if err != nil {
return nil, "", err
}

setupFlags(models, configuration)

flag.Parse()

modelFlag := flag.Lookup(confModel)
if modelFlag != nil && strings.TrimSpace(modelFlag.Value.String()) != defaultConfiguration {
// Replace the model
configuration, err = configurationModels.getFromModel(modelFlag.Value.String())
configuration, err = models.get(modelFlag.Value.String())
if err != nil {
return nil, "", err
}
Expand All @@ -287,5 +271,7 @@ func getConfigurationFromCommandLine() (*configuration, string, error) {
configuration.set(f.Name, f.Value.String())
})

return configuration, flag.Arg(0), nil
filename := flag.Arg(0)

return configuration, filename, nil
}
45 changes: 38 additions & 7 deletions configuration_test.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package izapple2

import (
"flag"
"os"
"strings"
"testing"
)

func TestConfigurationModel(t *testing.T) {

t.Run("test that the default model exists", func(t *testing.T) {
models, err := initConfigurationModels()
if err != nil {
t.Fatal(err)
}
_, err = models.getFromModel(defaultConfiguration)
_, _, err := loadConfigurationModelsAndDefault()
if err != nil {
t.Error(err)
}
})

t.Run("test preconfigured models are complete", func(t *testing.T) {
models, err := initConfigurationModels()
models, _, err := loadConfigurationModelsAndDefault()
if err != nil {
t.Fatal(err)
}
Expand All @@ -30,7 +29,7 @@ func TestConfigurationModel(t *testing.T) {
}
availabledModels := models.availableModels()
for _, modelName := range availabledModels {
model, err := models.getFromModel(modelName)
model, err := models.get(modelName)
if err != nil {
t.Error(err)
}
Expand All @@ -43,3 +42,35 @@ func TestConfigurationModel(t *testing.T) {
}
})
}

func TestCommandLineHelp(t *testing.T) {
t.Run("test command line help", func(t *testing.T) {
models, configuration, err := loadConfigurationModelsAndDefault()
if err != nil {
t.Fatal(err)
}

prevFlags := flag.CommandLine
flag.CommandLine = flag.NewFlagSet("izapple2", flag.ExitOnError)

setupFlags(models, configuration)

buffer := strings.Builder{}
flag.CommandLine.SetOutput(&buffer)
flag.Usage()
usage := buffer.String()

flag.CommandLine = prevFlags

prevous, err := os.ReadFile("doc/usage.txt")
if err != nil {
t.Fatal(err)
}

if usage != string(prevous) {
os.WriteFile("doc/usage_new.txt", []byte(usage), 0644)
t.Errorf(`Usage has changed, check doc/usage_new.txt for the new version.
If it is correct, execute \"go run update_readme.go\" in the doc folder.`)
}
})
}
Loading

0 comments on commit 51a7f17

Please sign in to comment.