mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-12-03 08:51:44 +00:00
Improve usage message and automate the inclusion in README.md
This commit is contained in:
parent
9178372942
commit
51a7f17e5b
55
README.md
55
README.md
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
@ -83,11 +82,11 @@ 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)
|
||||
@ -95,7 +94,7 @@ func initConfigurationModels() (*configurationModels, error) {
|
||||
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()
|
||||
@ -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:])
|
||||
@ -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)
|
||||
}
|
||||
*/
|
||||
defaultConfig, err := models.get(defaultConfiguration)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defaultConfig.set(confModel, defaultConfiguration)
|
||||
|
||||
// Todo check that all configs have valid keys
|
||||
|
||||
return &models, nil
|
||||
return models, defaultConfig, nil
|
||||
}
|
||||
|
||||
func mergeConfigs(base *configuration, addition *configuration) *configuration {
|
||||
@ -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 {
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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",
|
||||
@ -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)
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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.`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
56
doc/update_readme.go
Normal file
56
doc/update_readme.go
Normal file
@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Define the file paths
|
||||
readmePath := "../README.md"
|
||||
usagePath := "usage.txt"
|
||||
newUsagePath := "usage_new.txt"
|
||||
|
||||
err := os.Rename(newUsagePath, usagePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Read the contents of the usage file
|
||||
usageBytes, err := ioutil.ReadFile(usagePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Convert the usage bytes to string
|
||||
usage := string(usageBytes)
|
||||
|
||||
// Read the contents of the readme file
|
||||
readmeBytes, err := ioutil.ReadFile(readmePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Convert the readme bytes to string
|
||||
readme := string(readmeBytes)
|
||||
|
||||
// Find the start and end markers
|
||||
startMarker := "<!-- doc/usage.txt start -->"
|
||||
endMarker := "<!-- doc/usage.txt end -->"
|
||||
startIndex := strings.Index(readme, startMarker)
|
||||
endIndex := strings.Index(readme, endMarker)
|
||||
|
||||
// Replace the lines between start and end markers with the usage
|
||||
newReadme := readme[:startIndex+len(startMarker)] + "\n```terminal\n" + usage + "\n```\n" + readme[endIndex:]
|
||||
|
||||
// Write the updated readme back to the file
|
||||
err = ioutil.WriteFile(readmePath, []byte(newReadme), os.ModePerm)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("README.md updated successfully!")
|
||||
}
|
86
doc/usage.txt
Normal file
86
doc/usage.txt
Normal file
@ -0,0 +1,86 @@
|
||||
Usage: izapple2 [file]
|
||||
file
|
||||
path to image to use on the boot device
|
||||
-charrom string
|
||||
rom file for the character generator (default "<internal>/Apple IIe Video Enhanced.bin")
|
||||
-cpu string
|
||||
cpu type, can be '6502' or '65c02' (default "65c02")
|
||||
-forceCaps
|
||||
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 "main")
|
||||
-profile
|
||||
generate profile trace to analyse with pprof
|
||||
-ramworks string
|
||||
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
|
||||
main rom file (default "<internal>/Apple2e_Enhanced.rom")
|
||||
-romx
|
||||
emulate a RomX
|
||||
-s0 string
|
||||
slot 0 configuration. (default "language")
|
||||
-s1 string
|
||||
slot 1 configuration. (default "empty")
|
||||
-s2 string
|
||||
slot 2 configuration. (default "vidhd")
|
||||
-s3 string
|
||||
slot 3 configuration. (default "fastchip")
|
||||
-s4 string
|
||||
slot 4 configuration. (default "mouse")
|
||||
-s5 string
|
||||
slot 5 configuration. (default "empty")
|
||||
-s6 string
|
||||
slot 6 configuration. (default "diskii,disk1=<internal>/dos33.dsk")
|
||||
-s7 string
|
||||
slot 7 configuration. (default "empty")
|
||||
-speed string
|
||||
cpu speed in Mhz, can be 'ntsc', 'pal', 'full' or a decimal nunmber (default "ntsc")
|
||||
-trace string
|
||||
trace CPU execution with one or more comma separated tracers (default "none")
|
||||
|
||||
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
|
Loading…
Reference in New Issue
Block a user