diff --git a/cmd/applesoft.go b/cmd/applesoft.go index cc0a02f..94f4d9c 100644 --- a/cmd/applesoft.go +++ b/cmd/applesoft.go @@ -34,7 +34,7 @@ func init() { // 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 rawControlCodes bool // flag for whether to skip escaping control codes diff --git a/cmd/nakedos.go b/cmd/nakedos.go index 692bbd7..5718e3e 100644 --- a/cmd/nakedos.go +++ b/cmd/nakedos.go @@ -2,7 +2,14 @@ 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 var nakedosCmd = &cobra.Command{ @@ -16,3 +23,107 @@ with NakedOS (and Super-Mon) disks`, func init() { 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 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 ") + } + 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 +} diff --git a/lib/supermon/supermon.go b/lib/supermon/supermon.go index ea82b04..785ed82 100644 --- a/lib/supermon/supermon.go +++ b/lib/supermon/supermon.go @@ -639,44 +639,44 @@ func (st SymbolTable) FilesForCompoundName(filename string) (numFile byte, named 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. -type operator struct { - sd disk.SectorDisk - sm SectorMap - st SymbolTable +type Operator struct { + SD disk.SectorDisk + SM SectorMap + ST SymbolTable } -var _ disk.Operator = operator{} +var _ disk.Operator = Operator{} // operatorName is the keyword name for the operator that undestands // NakedOS/Super-Mon disks. const operatorName = "nakedos" -// Name returns the name of the operator. -func (o operator) Name() string { +// Name returns the name of the Operator. +func (o Operator) Name() string { return operatorName } // HasSubdirs returns true if the underlying operating system on the // disk allows subdirectories. -func (o operator) HasSubdirs() bool { +func (o Operator) HasSubdirs() bool { return false } // Catalog returns a catalog of disk entries. subdir should be empty // 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 - sectorsByFile := o.sm.SectorsByFile() + sectorsByFile := o.SM.SectorsByFile() for file := byte(1); file < FileReserved; file++ { l := len(sectorsByFile[file]) if l == 0 { continue } descs = append(descs, disk.Descriptor{ - Name: NameForFile(file, o.st), - Fullname: FullnameForFile(file, o.st), + Name: NameForFile(file, o.ST), + Fullname: FullnameForFile(file, o.ST), Sectors: l, Length: l * 256, Locked: false, @@ -687,12 +687,12 @@ func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) { } // GetFile retrieves a file by name. -func (o operator) GetFile(filename string) (disk.FileInfo, error) { - file, err := o.st.FileForName(filename) +func (o Operator) GetFile(filename string) (disk.FileInfo, error) { + file, err := o.ST.FileForName(filename) if err != nil { return disk.FileInfo{}, err } - data, err := o.sm.ReadFile(o.sd, file) + data, err := o.SM.ReadFile(o.SD, file) if err != nil { 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) } desc := disk.Descriptor{ - Name: NameForFile(file, o.st), + Name: NameForFile(file, o.ST), Sectors: len(data) / 256, Length: len(data), 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 // deleted, false if it didn't exist. -func (o operator) Delete(filename string) (bool, error) { - file, err := o.st.FileForName(filename) +func (o Operator) Delete(filename string) (bool, error) { + file, err := o.ST.FileForName(filename) if err != nil { return false, err } - existed := len(o.sm.SectorsForFile(file)) > 0 - o.sm.Delete(file) - if err := o.sm.Persist(o.sd); err != nil { + existed := len(o.SM.SectorsForFile(file)) > 0 + o.SM.Delete(file) + if err := o.SM.Persist(o.SD); err != nil { return existed, err } - if o.st != nil { - changed := o.st.DeleteSymbol(filename) + if o.ST != nil { + changed := o.ST.DeleteSymbol(filename) 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 } } @@ -742,7 +742,7 @@ func (o operator) Delete(filename string) (bool, error) { // PutFile writes a file by name. If the file exists and overwrite // is false, it returns with an error. Otherwise it returns true if // 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 { 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)) } - numFile, namedFile, symbol, err := o.st.FilesForCompoundName(fileInfo.Descriptor.Name) + numFile, namedFile, symbol, err := o.ST.FilesForCompoundName(fileInfo.Descriptor.Name) if err != nil { return false, err } 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") } if _, err := encodeSymbol(symbol); err != nil { @@ -763,20 +763,20 @@ func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool, } } if numFile == 0 { - numFile = o.sm.FirstFreeFile() + numFile = o.SM.FirstFreeFile() if numFile == 0 { 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 { return existed, err } 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 } - 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 } } @@ -794,11 +794,11 @@ func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) { return nil, err } - op := operator{sd: sd, sm: sm} + op := Operator{SD: sd, SM: sm} st, err := sm.ReadSymbolTable(sd) if err == nil { - op.st = st + op.ST = st } return op, nil