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")
}
// ----- 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

View File

@ -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 <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
}
// 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