mirror of https://github.com/zellyn/diskii.git
Implemented `cat` command for dos3.3 and NakedOS
- Added a generic `Operator` registry and implemented Operators for dos3.3 and NakeOS/Super-Mon disks. - Currently Operators implement only the `Catalog` command.
This commit is contained in:
parent
c7cb3bb5ce
commit
f57c3a0c06
|
@ -1,6 +1,10 @@
|
||||||
diskii
|
diskii
|
||||||
======
|
======
|
||||||
|
|
||||||
|
**Note:** diskii is not stable yet! I don't expect to remove
|
||||||
|
functionality, but I'm still experimenting with the command syntax and
|
||||||
|
organization, so don't get too comfy with it yet.
|
||||||
|
|
||||||
diskii is a commandline tool for working with Apple II disk images.
|
diskii is a commandline tool for working with Apple II disk images.
|
||||||
|
|
||||||
It is also a library of code that can be used by other Go programs.
|
It is also a library of code that can be used by other Go programs.
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/zellyn/diskii/lib/disk"
|
||||||
|
_ "github.com/zellyn/diskii/lib/dos33"
|
||||||
|
_ "github.com/zellyn/diskii/lib/supermon"
|
||||||
|
)
|
||||||
|
|
||||||
|
// catCmd represents the cat command, used to catalog a disk or
|
||||||
|
// directory.
|
||||||
|
var catCmd = &cobra.Command{
|
||||||
|
Use: "cat",
|
||||||
|
Short: "print a list of files",
|
||||||
|
Long: `Catalog a disk or subdirectory.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if err := runCat(args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(catCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runCat performs the actual catalog logic.
|
||||||
|
func runCat(args []string) error {
|
||||||
|
if len(args) < 1 || len(args) > 2 {
|
||||||
|
return fmt.Errorf("cat expects a disk image filename, and an optional subdirectory")
|
||||||
|
}
|
||||||
|
sd, err := disk.Open(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
op, err := disk.OperatorFor(sd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
subdir := ""
|
||||||
|
if len(args) == 2 {
|
||||||
|
if !op.HasSubdirs() {
|
||||||
|
return fmt.Errorf("Disks of type %q cannot have subdirectories", op.Name())
|
||||||
|
}
|
||||||
|
subdir = args[1]
|
||||||
|
}
|
||||||
|
fds, err := op.Catalog(subdir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, fd := range fds {
|
||||||
|
fmt.Println(fd.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -32,6 +32,19 @@ decode - # read stdin`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
applesoftCmd.AddCommand(decodeCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// decodeCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
decodeCmd.Flags().Uint16VarP(&location, "location", "l", 0x801, "Starting program location in memory")
|
||||||
|
decodeCmd.Flags().BoolVarP(&rawControlCodes, "raw", "r", false, "Print raw control codes (no escaping)")
|
||||||
|
}
|
||||||
|
|
||||||
// runDecode performs the actual decode logic.
|
// runDecode performs the actual decode logic.
|
||||||
func runDecode(args []string) error {
|
func runDecode(args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
|
@ -52,16 +65,3 @@ func runDecode(args []string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
applesoftCmd.AddCommand(decodeCmd)
|
|
||||||
|
|
||||||
// Here you will define your flags and configuration settings.
|
|
||||||
|
|
||||||
// Cobra supports Persistent Flags which will work for this command
|
|
||||||
// and all subcommands, e.g.:
|
|
||||||
// decodeCmd.PersistentFlags().String("foo", "", "A help for foo")
|
|
||||||
|
|
||||||
decodeCmd.Flags().Uint16VarP(&location, "location", "l", 0x801, "Starting program location in memory")
|
|
||||||
decodeCmd.Flags().BoolVarP(&rawControlCodes, "raw", "r", false, "Print raw control codes (no escaping)")
|
|
||||||
}
|
|
||||||
|
|
105
lib/disk/disk.go
105
lib/disk/disk.go
|
@ -6,7 +6,16 @@ package disk
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DOS33Tracks = 35 // Tracks per disk
|
||||||
|
DOS33Sectors = 16 // Sectors per track
|
||||||
|
// DOS33DiskBytes is the number of bytes on a DOS 3.3 disk.
|
||||||
|
DOS33DiskBytes = 143360 // 35 tracks * 16 sectors * 256 bytes
|
||||||
|
DOS33TrackBytes = 256 * DOS33Sectors // Bytes per track
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dos33LogicalToPhysicalSectorMap maps logical sector numbers to physical ones.
|
// Dos33LogicalToPhysicalSectorMap maps logical sector numbers to physical ones.
|
||||||
|
@ -106,96 +115,22 @@ func (md MappedDisk) WriteLogicalSector(track byte, sector byte, data []byte) er
|
||||||
return md.sectorDisk.WritePhysicalSector(track, physicalSector, data)
|
return md.sectorDisk.WritePhysicalSector(track, physicalSector, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sectors returns the number of sectors on the DSK image.
|
// Sectors returns the number of sectors in the disk image.
|
||||||
func (md MappedDisk) Sectors() byte {
|
func (md MappedDisk) Sectors() byte {
|
||||||
return md.sectorDisk.Sectors()
|
return md.sectorDisk.Sectors()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tracks returns the number of tracks on the DSK image.
|
// Tracks returns the number of tracks in the disk image.
|
||||||
func (md MappedDisk) Tracks() byte {
|
func (md MappedDisk) Tracks() byte {
|
||||||
return md.sectorDisk.Tracks()
|
return md.sectorDisk.Tracks()
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// Open opens a disk image by filename.
|
||||||
DOS33Tracks = 35 // Tracks per disk
|
func Open(filename string) (SectorDisk, error) {
|
||||||
DOS33Sectors = 16 // Sectors per track
|
ext := strings.ToLower(path.Ext(filename))
|
||||||
// DOS33DiskBytes is the number of bytes on a DOS 3.3 disk.
|
switch ext {
|
||||||
DOS33DiskBytes = 143360 // 35 tracks * 16 sectors * 256 bytes
|
case ".dsk":
|
||||||
DOS33TrackBytes = 256 * DOS33Sectors // Bytes per track
|
return LoadDSK(filename)
|
||||||
)
|
}
|
||||||
|
return nil, fmt.Errorf("Unimplemented/unknown disk file extension %q", ext)
|
||||||
// DSK represents a .dsk disk image.
|
|
||||||
type DSK struct {
|
|
||||||
data []byte // The actual data in the file
|
|
||||||
sectors byte // Number of sectors per track
|
|
||||||
physicalToStored []byte // Map of physical on-disk sector numbers to sectors in the disk image
|
|
||||||
bytesPerTrack int // Number of bytes per track
|
|
||||||
tracks byte // Number of tracks
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ SectorDisk = (*DSK)(nil)
|
|
||||||
|
|
||||||
// LoadDSK loads a .dsk image from a file.
|
|
||||||
func LoadDSK(filename string) (DSK, error) {
|
|
||||||
bb, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return DSK{}, err
|
|
||||||
}
|
|
||||||
// TODO(zellyn): handle 13-sector disks.
|
|
||||||
if len(bb) != DOS33DiskBytes {
|
|
||||||
return DSK{}, fmt.Errorf("Expected file %q to contain %d bytes, but got %d.", filename, DOS33DiskBytes, len(bb))
|
|
||||||
}
|
|
||||||
return DSK{
|
|
||||||
data: bb,
|
|
||||||
sectors: 16,
|
|
||||||
physicalToStored: Dos33PhysicalToLogicalSectorMap,
|
|
||||||
bytesPerTrack: 16 * 256,
|
|
||||||
tracks: DOS33Tracks,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadPhysicalSector reads a single physical sector from the disk. It
|
|
||||||
// always returns 256 byes.
|
|
||||||
func (d DSK) ReadPhysicalSector(track byte, sector byte) ([]byte, error) {
|
|
||||||
if track >= d.tracks {
|
|
||||||
return nil, fmt.Errorf("ReadPhysicalSector expected track between 0 and %d; got %d", d.tracks-1, track)
|
|
||||||
}
|
|
||||||
if sector >= d.sectors {
|
|
||||||
return nil, fmt.Errorf("ReadPhysicalSector expected sector between 0 and %d; got %d", d.sectors-1, sector)
|
|
||||||
}
|
|
||||||
|
|
||||||
storedSector := d.physicalToStored[int(sector)]
|
|
||||||
start := int(track)*d.bytesPerTrack + 256*int(storedSector)
|
|
||||||
buf := make([]byte, 256)
|
|
||||||
copy(buf, d.data[start:start+256])
|
|
||||||
return buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WritePhysicalSector writes a single physical sector to a disk. It
|
|
||||||
// expects exactly 256 bytes.
|
|
||||||
func (d DSK) WritePhysicalSector(track byte, sector byte, data []byte) error {
|
|
||||||
if track >= d.tracks {
|
|
||||||
return fmt.Errorf("WritePhysicalSector expected track between 0 and %d; got %d", d.tracks-1, track)
|
|
||||||
}
|
|
||||||
if sector >= d.sectors {
|
|
||||||
return fmt.Errorf("WritePhysicalSector expected sector between 0 and %d; got %d", d.sectors-1, sector)
|
|
||||||
}
|
|
||||||
if len(data) != 256 {
|
|
||||||
return fmt.Errorf("WritePhysicalSector expects data of length 256; got %d", len(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
storedSector := d.physicalToStored[int(sector)]
|
|
||||||
start := int(track)*d.bytesPerTrack + 256*int(storedSector)
|
|
||||||
copy(d.data[start:start+256], data)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sectors returns the number of sectors on the DSK image.
|
|
||||||
func (d DSK) Sectors() byte {
|
|
||||||
return d.sectors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracks returns the number of tracks on the DSK image.
|
|
||||||
func (d DSK) Tracks() byte {
|
|
||||||
return d.tracks
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
|
||||||
|
|
||||||
|
// dsk.go contains logic for reading ".dsk" disk images.
|
||||||
|
|
||||||
|
package disk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DSK represents a .dsk disk image.
|
||||||
|
type DSK struct {
|
||||||
|
data []byte // The actual data in the file
|
||||||
|
sectors byte // Number of sectors per track
|
||||||
|
physicalToStored []byte // Map of physical on-disk sector numbers to sectors in the disk image
|
||||||
|
bytesPerTrack int // Number of bytes per track
|
||||||
|
tracks byte // Number of tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SectorDisk = (*DSK)(nil)
|
||||||
|
|
||||||
|
// LoadDSK loads a .dsk image from a file.
|
||||||
|
func LoadDSK(filename string) (DSK, error) {
|
||||||
|
bb, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return DSK{}, err
|
||||||
|
}
|
||||||
|
// TODO(zellyn): handle 13-sector disks.
|
||||||
|
if len(bb) != DOS33DiskBytes {
|
||||||
|
return DSK{}, fmt.Errorf("Expected file %q to contain %d bytes, but got %d.", filename, DOS33DiskBytes, len(bb))
|
||||||
|
}
|
||||||
|
return DSK{
|
||||||
|
data: bb,
|
||||||
|
sectors: 16,
|
||||||
|
physicalToStored: Dos33PhysicalToLogicalSectorMap,
|
||||||
|
bytesPerTrack: 16 * 256,
|
||||||
|
tracks: DOS33Tracks,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadPhysicalSector reads a single physical sector from the disk. It
|
||||||
|
// always returns 256 byes.
|
||||||
|
func (d DSK) ReadPhysicalSector(track byte, sector byte) ([]byte, error) {
|
||||||
|
if track >= d.tracks {
|
||||||
|
return nil, fmt.Errorf("ReadPhysicalSector expected track between 0 and %d; got %d", d.tracks-1, track)
|
||||||
|
}
|
||||||
|
if sector >= d.sectors {
|
||||||
|
return nil, fmt.Errorf("ReadPhysicalSector expected sector between 0 and %d; got %d", d.sectors-1, sector)
|
||||||
|
}
|
||||||
|
|
||||||
|
storedSector := d.physicalToStored[int(sector)]
|
||||||
|
start := int(track)*d.bytesPerTrack + 256*int(storedSector)
|
||||||
|
buf := make([]byte, 256)
|
||||||
|
copy(buf, d.data[start:start+256])
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePhysicalSector writes a single physical sector to a disk. It
|
||||||
|
// expects exactly 256 bytes.
|
||||||
|
func (d DSK) WritePhysicalSector(track byte, sector byte, data []byte) error {
|
||||||
|
if track >= d.tracks {
|
||||||
|
return fmt.Errorf("WritePhysicalSector expected track between 0 and %d; got %d", d.tracks-1, track)
|
||||||
|
}
|
||||||
|
if sector >= d.sectors {
|
||||||
|
return fmt.Errorf("WritePhysicalSector expected sector between 0 and %d; got %d", d.sectors-1, sector)
|
||||||
|
}
|
||||||
|
if len(data) != 256 {
|
||||||
|
return fmt.Errorf("WritePhysicalSector expects data of length 256; got %d", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
storedSector := d.physicalToStored[int(sector)]
|
||||||
|
start := int(track)*d.bytesPerTrack + 256*int(storedSector)
|
||||||
|
copy(d.data[start:start+256], data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sectors returns the number of sectors on the DSK image.
|
||||||
|
func (d DSK) Sectors() byte {
|
||||||
|
return d.sectors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tracks returns the number of tracks on the DSK image.
|
||||||
|
func (d DSK) Tracks() byte {
|
||||||
|
return d.tracks
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
|
||||||
|
|
||||||
|
// ops.go contains the interfaces and helper functions for operating
|
||||||
|
// on disk images logically: catalog, rename, delete, create files,
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
package disk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Descriptor describes a file's characteristics.
|
||||||
|
type Descriptor struct {
|
||||||
|
Name string
|
||||||
|
Sectors int
|
||||||
|
Length int
|
||||||
|
Locked bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operator is the interface that can operate on disks.
|
||||||
|
type Operator interface {
|
||||||
|
// Name returns the name of the operator.
|
||||||
|
Name() string
|
||||||
|
// HasSubdirs returns true if the underlying operating system on the
|
||||||
|
// disk allows subdirectories.
|
||||||
|
HasSubdirs() bool
|
||||||
|
// Catalog returns a catalog of disk entries. subdir should be empty
|
||||||
|
// for operating systems that do not support subdirectories.
|
||||||
|
Catalog(subdir string) ([]Descriptor, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// operatorFactory is the type of functions that accept a SectorDisk,
|
||||||
|
// and may return an Operator interface to operate on it.
|
||||||
|
type operatorFactory func(SectorDisk) (Operator, error)
|
||||||
|
|
||||||
|
// operatorFactories is the map of currently-registered operator
|
||||||
|
// factories.
|
||||||
|
var operatorFactories map[string]operatorFactory
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
operatorFactories = make(map[string]operatorFactory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterOperatorFactory registers an operator factory with the
|
||||||
|
// given name: a function that accepts a SectorDisk, and may return an
|
||||||
|
// Operator. It doesn't lock operatorFactories: it is expected to be
|
||||||
|
// called only from package `init` functions.
|
||||||
|
func RegisterOperatorFactory(name string, factory operatorFactory) {
|
||||||
|
operatorFactories[name] = factory
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorFor returns an Operator for the given SectorDisk, if possible.
|
||||||
|
func OperatorFor(sd SectorDisk) (Operator, error) {
|
||||||
|
if len(operatorFactories) == 0 {
|
||||||
|
return nil, errors.New("Cannot find an operator matching the given disk image (none registered)")
|
||||||
|
}
|
||||||
|
for _, factory := range operatorFactories {
|
||||||
|
if operator, err := factory(sd); err == nil {
|
||||||
|
return operator, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
names := make([]string, 0, len(operatorFactories))
|
||||||
|
for name := range operatorFactories {
|
||||||
|
names = append(names, `"`+name+`"`)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return nil, fmt.Errorf("Cannot find an operator matching the given disk image (tried %s)", strings.Join(names, ", "))
|
||||||
|
}
|
|
@ -454,3 +454,63 @@ func ReadCatalog(d disk.LogicalSectorDisk) (files, deleted []FileDesc, err error
|
||||||
}
|
}
|
||||||
return files, deleted, nil
|
return files, deleted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// operator is a disk.Operator - an interface for performing
|
||||||
|
// high-level operations on files and directories.
|
||||||
|
type operator struct {
|
||||||
|
lsd disk.LogicalSectorDisk
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ disk.Operator = operator{}
|
||||||
|
|
||||||
|
// operatorName is the keyword name for the operator that undestands
|
||||||
|
// dos33 disks.
|
||||||
|
const operatorName = "dos33"
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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) {
|
||||||
|
fds, _, err := ReadCatalog(o.lsd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
descs := make([]disk.Descriptor, 0, len(fds))
|
||||||
|
for _, fd := range fds {
|
||||||
|
descs = append(descs, disk.Descriptor{
|
||||||
|
Name: fd.FilenameString(),
|
||||||
|
Sectors: int(fd.SectorCount),
|
||||||
|
Length: -1, // TODO(zellyn): read actual file length
|
||||||
|
Locked: (fd.Filetype & FiletypeLocked) > 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return descs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// operatorFactory is the factory that returns dos33 operators given
|
||||||
|
// disk images.
|
||||||
|
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
||||||
|
lsd, err := disk.NewMappedDisk(sd, disk.Dos33LogicalToPhysicalSectorMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, _, err = ReadCatalog(lsd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Cannot read catalog. Underlying error: %v", err)
|
||||||
|
}
|
||||||
|
return operator{lsd: lsd}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
disk.RegisterOperatorFactory(operatorName, operatorFactory)
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ package supermon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/zellyn/diskii/lib/disk"
|
"github.com/zellyn/diskii/lib/disk"
|
||||||
)
|
)
|
||||||
|
@ -211,3 +212,88 @@ func (st SymbolTable) SymbolsByAddress() map[uint16][]Symbol {
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FilenameString(file byte, symbols []Symbol) string {
|
||||||
|
if len(symbols) > 0 {
|
||||||
|
for _, symbol := range symbols {
|
||||||
|
if strings.HasPrefix(symbol.Name, "F") {
|
||||||
|
return symbol.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return symbols[0].Name
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%02X", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
symbols map[uint16][]Symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return operatorName
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSubdirs returns true if the underlying operating system on the
|
||||||
|
// disk allows subdirectories.
|
||||||
|
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) {
|
||||||
|
var descs []disk.Descriptor
|
||||||
|
sectorsByFile := o.sm.SectorsByFile()
|
||||||
|
for file := byte(1); file < FileReserved; file++ {
|
||||||
|
l := len(sectorsByFile[file])
|
||||||
|
if l == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fileAddr := uint16(0xDF00) + uint16(file)
|
||||||
|
descs = append(descs, disk.Descriptor{
|
||||||
|
Name: FilenameString(file, o.symbols[fileAddr]),
|
||||||
|
Sectors: l,
|
||||||
|
Length: l * 256,
|
||||||
|
Locked: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return descs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// operatorFactory is the factory that returns dos33 operators given
|
||||||
|
// disk images.
|
||||||
|
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
||||||
|
sm, err := LoadSectorMap(sd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := sm.Verify(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
op := operator{sd: sd, sm: sm}
|
||||||
|
|
||||||
|
st, err := sm.ReadSymbolTable(sd)
|
||||||
|
if err == nil {
|
||||||
|
op.st = st
|
||||||
|
op.symbols = st.SymbolsByAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
return op, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
disk.RegisterOperatorFactory(operatorName, operatorFactory)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue