add `nakedos mkhello` command; export more things

This commit is contained in:
Zellyn Hunter 2016-12-09 23:08:47 -05:00
parent 2d994dec8d
commit 34a26dd1d6
3 changed files with 147 additions and 36 deletions

View File

@ -34,7 +34,7 @@ func init() {
// applesoftCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // applesoftCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
} }
// ----- applesoft decode commane ------------------------------------------- // ----- applesoft decode command -------------------------------------------
var location uint16 // flag for starting location in memory var location uint16 // flag for starting location in memory
var rawControlCodes bool // flag for whether to skip escaping control codes var rawControlCodes bool // flag for whether to skip escaping control codes

View File

@ -2,7 +2,14 @@
package cmd package cmd
import "github.com/spf13/cobra" import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/zellyn/diskii/lib/disk"
"github.com/zellyn/diskii/lib/supermon"
)
// nakedosCmd represents the nakedos command // nakedosCmd represents the nakedos command
var nakedosCmd = &cobra.Command{ var nakedosCmd = &cobra.Command{
@ -16,3 +23,107 @@ with NakedOS (and Super-Mon) disks`,
func init() { func init() {
RootCmd.AddCommand(nakedosCmd) RootCmd.AddCommand(nakedosCmd)
} }
// ----- mkhello command ----------------------------------------------------
var address uint16 // flag for address to load at
var start uint16 // flag for address to start execution at
const helloName = "FHELLO" // filename to use (if Super-Mon)
// mkhelloCmd represents the mkhello command
var mkhelloCmd = &cobra.Command{
Use: "mkhello <disk-image> filename",
Short: "create an FHELLO program that loads and runs another file",
Long: `
mkhello creates file DF01:FHELLO that loads and runs another program at a specific address.
Examples:
mkhello test.dsk FDEMO # load and run FDEMO at the default address, then jump to the start of the loaded code.
mkhello test.dsk --address 0x2000 --start 0x2100 DF06 # load and run file DF06 at address 0x2000, and jump to 0x2100.`,
Run: func(cmd *cobra.Command, args []string) {
if err := runMkhello(args); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(-1)
}
},
}
func init() {
nakedosCmd.AddCommand(mkhelloCmd)
// Here you will define your flags and configuration settings.
mkhelloCmd.Flags().Uint16VarP(&address, "address", "a", 0x6000, "memory location to load code at")
mkhelloCmd.Flags().Uint16VarP(&start, "start", "s", 0x6000, "memory location to jump to")
}
// runMkhello performs the actual mkhello logic.
func runMkhello(args []string) error {
if len(args) != 2 {
return fmt.Errorf("usage: diskii mkhello <disk image> <file-to-load>")
}
if address%256 != 0 {
return fmt.Errorf("address %d (%04X) not on a page boundary", address, address)
}
if start < address {
return fmt.Errorf("start address %d (%04X) < load address %d (%04X)", start, start, address, address)
}
sd, err := disk.Open(args[0])
if err != nil {
return err
}
op, err := disk.OperatorFor(sd)
if err != nil {
return err
}
if op.Name() != "nakedos" {
return fmt.Errorf("mkhello only works on disks of type %q; got %q", "nakedos", op.Name())
}
nakOp, ok := op.(supermon.Operator)
if !ok {
return fmt.Errorf("internal error: cannot cast to expected supermon.Operator type")
}
addr, symbolAddr, _, err := nakOp.ST.FilesForCompoundName(args[1])
if err != nil {
return err
}
if addr == 0 && symbolAddr == 0 {
return fmt.Errorf("cannot parse %q as valid filename", args[1])
}
toLoad := addr
if addr == 0 {
toLoad = symbolAddr
}
contents := []byte{
0x20, 0x40, 0x03, // JSR NAKEDOS
0x6D, 0x01, 0xDC, // ADC NKRDFILE
0x2C, toLoad, 0xDF, // BIT ${file number to load}
0x2C, 0x00, byte(address >> 8), // BIT ${target page}
0xF8, // CLD
0x4C, byte(start), byte(start >> 8), // JMP ${target page}
}
fileInfo := disk.FileInfo{
Descriptor: disk.Descriptor{
Name: fmt.Sprintf("DF01:%s", helloName),
Length: len(contents),
Type: disk.FiletypeBinary,
},
Data: contents,
}
_, err = op.PutFile(fileInfo, true)
if err != nil {
return err
}
f, err := os.Create(args[0])
if err != nil {
return err
}
_, err = sd.Write(f)
if err != nil {
return err
}
if err = f.Close(); err != nil {
return err
}
return nil
}

View File

@ -639,44 +639,44 @@ func (st SymbolTable) FilesForCompoundName(filename string) (numFile byte, named
return numFile, namedFile, parts[1], nil return numFile, namedFile, parts[1], nil
} }
// operator is a disk.Operator - an interface for performing // Operator is a disk.Operator - an interface for performing
// high-level operations on files and directories. // high-level operations on files and directories.
type operator struct { type Operator struct {
sd disk.SectorDisk SD disk.SectorDisk
sm SectorMap SM SectorMap
st SymbolTable ST SymbolTable
} }
var _ disk.Operator = operator{} var _ disk.Operator = Operator{}
// operatorName is the keyword name for the operator that undestands // operatorName is the keyword name for the operator that undestands
// NakedOS/Super-Mon disks. // NakedOS/Super-Mon disks.
const operatorName = "nakedos" const operatorName = "nakedos"
// Name returns the name of the operator. // Name returns the name of the Operator.
func (o operator) Name() string { func (o Operator) Name() string {
return operatorName return operatorName
} }
// HasSubdirs returns true if the underlying operating system on the // HasSubdirs returns true if the underlying operating system on the
// disk allows subdirectories. // disk allows subdirectories.
func (o operator) HasSubdirs() bool { func (o Operator) HasSubdirs() bool {
return false return false
} }
// Catalog returns a catalog of disk entries. subdir should be empty // Catalog returns a catalog of disk entries. subdir should be empty
// for operating systems that do not support subdirectories. // for operating systems that do not support subdirectories.
func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) { func (o Operator) Catalog(subdir string) ([]disk.Descriptor, error) {
var descs []disk.Descriptor var descs []disk.Descriptor
sectorsByFile := o.sm.SectorsByFile() sectorsByFile := o.SM.SectorsByFile()
for file := byte(1); file < FileReserved; file++ { for file := byte(1); file < FileReserved; file++ {
l := len(sectorsByFile[file]) l := len(sectorsByFile[file])
if l == 0 { if l == 0 {
continue continue
} }
descs = append(descs, disk.Descriptor{ descs = append(descs, disk.Descriptor{
Name: NameForFile(file, o.st), Name: NameForFile(file, o.ST),
Fullname: FullnameForFile(file, o.st), Fullname: FullnameForFile(file, o.ST),
Sectors: l, Sectors: l,
Length: l * 256, Length: l * 256,
Locked: false, Locked: false,
@ -687,12 +687,12 @@ func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) {
} }
// GetFile retrieves a file by name. // GetFile retrieves a file by name.
func (o operator) GetFile(filename string) (disk.FileInfo, error) { func (o Operator) GetFile(filename string) (disk.FileInfo, error) {
file, err := o.st.FileForName(filename) file, err := o.ST.FileForName(filename)
if err != nil { if err != nil {
return disk.FileInfo{}, err return disk.FileInfo{}, err
} }
data, err := o.sm.ReadFile(o.sd, file) data, err := o.SM.ReadFile(o.SD, file)
if err != nil { if err != nil {
return disk.FileInfo{}, fmt.Errorf("error reading file DF%02x: %v", file, err) return disk.FileInfo{}, fmt.Errorf("error reading file DF%02x: %v", file, err)
} }
@ -700,7 +700,7 @@ func (o operator) GetFile(filename string) (disk.FileInfo, error) {
return disk.FileInfo{}, fmt.Errorf("file DF%02x not fount", file) return disk.FileInfo{}, fmt.Errorf("file DF%02x not fount", file)
} }
desc := disk.Descriptor{ desc := disk.Descriptor{
Name: NameForFile(file, o.st), Name: NameForFile(file, o.ST),
Sectors: len(data) / 256, Sectors: len(data) / 256,
Length: len(data), Length: len(data),
Locked: false, Locked: false,
@ -718,20 +718,20 @@ func (o operator) GetFile(filename string) (disk.FileInfo, error) {
// Delete deletes a file by name. It returns true if the file was // Delete deletes a file by name. It returns true if the file was
// deleted, false if it didn't exist. // deleted, false if it didn't exist.
func (o operator) Delete(filename string) (bool, error) { func (o Operator) Delete(filename string) (bool, error) {
file, err := o.st.FileForName(filename) file, err := o.ST.FileForName(filename)
if err != nil { if err != nil {
return false, err return false, err
} }
existed := len(o.sm.SectorsForFile(file)) > 0 existed := len(o.SM.SectorsForFile(file)) > 0
o.sm.Delete(file) o.SM.Delete(file)
if err := o.sm.Persist(o.sd); err != nil { if err := o.SM.Persist(o.SD); err != nil {
return existed, err return existed, err
} }
if o.st != nil { if o.ST != nil {
changed := o.st.DeleteSymbol(filename) changed := o.ST.DeleteSymbol(filename)
if changed { if changed {
if err := o.sm.WriteSymbolTable(o.sd, o.st); err != nil { if err := o.SM.WriteSymbolTable(o.SD, o.ST); err != nil {
return existed, err return existed, err
} }
} }
@ -742,7 +742,7 @@ func (o operator) Delete(filename string) (bool, error) {
// PutFile writes a file by name. If the file exists and overwrite // PutFile writes a file by name. If the file exists and overwrite
// is false, it returns with an error. Otherwise it returns true if // is false, it returns with an error. Otherwise it returns true if
// an existing file was overwritten. // an existing file was overwritten.
func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool, err error) { func (o Operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool, err error) {
if fileInfo.Descriptor.Type != disk.FiletypeBinary { if fileInfo.Descriptor.Type != disk.FiletypeBinary {
return false, fmt.Errorf("%s: only binary file type supported", operatorName) return false, fmt.Errorf("%s: only binary file type supported", operatorName)
} }
@ -750,12 +750,12 @@ func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool,
return false, fmt.Errorf("mismatch between FileInfo.Descriptor.Length (%d) and actual length of FileInfo.Data field (%d)", fileInfo.Descriptor.Length, len(fileInfo.Data)) return false, fmt.Errorf("mismatch between FileInfo.Descriptor.Length (%d) and actual length of FileInfo.Data field (%d)", fileInfo.Descriptor.Length, len(fileInfo.Data))
} }
numFile, namedFile, symbol, err := o.st.FilesForCompoundName(fileInfo.Descriptor.Name) numFile, namedFile, symbol, err := o.ST.FilesForCompoundName(fileInfo.Descriptor.Name)
if err != nil { if err != nil {
return false, err return false, err
} }
if symbol != "" { if symbol != "" {
if o.st == nil { if o.ST == nil {
return false, fmt.Errorf("cannot use symbolic names on disks without valid symbol tables in files 0x03 and 0x04") return false, fmt.Errorf("cannot use symbolic names on disks without valid symbol tables in files 0x03 and 0x04")
} }
if _, err := encodeSymbol(symbol); err != nil { if _, err := encodeSymbol(symbol); err != nil {
@ -763,20 +763,20 @@ func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool,
} }
} }
if numFile == 0 { if numFile == 0 {
numFile = o.sm.FirstFreeFile() numFile = o.SM.FirstFreeFile()
if numFile == 0 { if numFile == 0 {
return false, fmt.Errorf("all files already used") return false, fmt.Errorf("all files already used")
} }
} }
existed, err = o.sm.WriteFile(o.sd, numFile, fileInfo.Data, overwrite) existed, err = o.SM.WriteFile(o.SD, numFile, fileInfo.Data, overwrite)
if err != nil { if err != nil {
return existed, err return existed, err
} }
if namedFile != numFile && symbol != "" { if namedFile != numFile && symbol != "" {
if err := o.st.AddSymbol(symbol, 0xDF00+uint16(numFile)); err != nil { if err := o.ST.AddSymbol(symbol, 0xDF00+uint16(numFile)); err != nil {
return existed, err return existed, err
} }
if err := o.sm.WriteSymbolTable(o.sd, o.st); err != nil { if err := o.SM.WriteSymbolTable(o.SD, o.ST); err != nil {
return existed, err return existed, err
} }
} }
@ -794,11 +794,11 @@ func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
return nil, err return nil, err
} }
op := operator{sd: sd, sm: sm} op := Operator{SD: sd, SM: sm}
st, err := sm.ReadSymbolTable(sd) st, err := sm.ReadSymbolTable(sd)
if err == nil { if err == nil {
op.st = st op.ST = st
} }
return op, nil return op, nil