Skip to content

Commit

Permalink
Refactored towards a better design for sector maps
Browse files Browse the repository at this point in the history
- Loading a .dsk (or any other file type) should yield a SectorMap
  interface, which reads and writes *physical*
  sectors. (Auto-detection of sector mapping will come later.)
- The various filesystem packages (dos33, supermon, etc.) are
  responsible for logical sector mapping.
- The weird part about this is that .dsk images are already mapped to
  dos33 logical sector order, so they have to be un-mapped, then
  re-mapped by the dos33 package. It's still cleaner that way.
  • Loading branch information
zellyn committed Nov 13, 2016
1 parent 692414a commit 68ee8a4
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 81 deletions.
141 changes: 120 additions & 21 deletions lib/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,90 @@ type TrackSector struct {
}

type SectorDisk interface {
// ReadPhysicalSector reads a single physical sector from the disk. It
// always returns 256 byes.
ReadPhysicalSector(track byte, sector byte) ([]byte, error)
// WritePhysicalSector writes a single physical sector to a disk. It
// expects exactly 256 bytes.
WritePhysicalSector(track byte, sector byte, data []byte) error
// Sectors returns the number of sectors on the SectorDisk
Sectors() byte
// Tracks returns the number of tracks on the SectorDisk
Tracks() byte
}

type LogicalSectorDisk interface {
// ReadLogicalSector reads a single logical sector from the disk. It
// always returns 256 byes.
ReadLogicalSector(track byte, sector byte) ([]byte, error)
// WriteLogicalSector writes a single logical sector to a disk. It
// expects exactly 256 bytes.
WriteLogicalSector(track byte, sector byte, data []byte) error
// Sectors returns the number of sectors on the SectorDisk
Sectors() byte
// Tracks returns the number of tracks on the SectorDisk
Tracks() byte
}

// MappedDisk wraps a SectorDisk as a LogicalSectorDisk, handling the
// logical-to-physical sector mapping.
type MappedDisk struct {
sectorDisk SectorDisk // The underlying physical sector disk.
logicalToPhysical []byte // The mapping of logical to physical sectors.
}

var _ LogicalSectorDisk = MappedDisk{}

func NewMappedDisk(sd SectorDisk, logicalToPhysical []byte) (MappedDisk, error) {
if logicalToPhysical != nil && len(logicalToPhysical) != int(sd.Sectors()) {
return MappedDisk{}, fmt.Errorf("NewMappedDisk called on a disk image with %d sectors per track, but a mapping of length %d", sd.Sectors(), len(logicalToPhysical))
}
if logicalToPhysical == nil {
logicalToPhysical = make([]byte, int(sd.Sectors()))
for i := range logicalToPhysical {
logicalToPhysical[i] = byte(i)
}
}
return MappedDisk{
sectorDisk: sd,
logicalToPhysical: logicalToPhysical,
}, nil
}

// ReadLogicalSector reads a single logical sector from the disk. It
// always returns 256 byes.
func (md MappedDisk) ReadLogicalSector(track byte, sector byte) ([]byte, error) {
if track >= md.sectorDisk.Tracks() {
return nil, fmt.Errorf("ReadLogicalSector expected track between 0 and %d; got %d", md.sectorDisk.Tracks()-1, track)
}
if sector >= md.sectorDisk.Sectors() {
return nil, fmt.Errorf("ReadLogicalSector expected sector between 0 and %d; got %d", md.sectorDisk.Sectors()-1, sector)
}
physicalSector := md.logicalToPhysical[int(sector)]
return md.sectorDisk.ReadPhysicalSector(track, physicalSector)
}

// WriteLogicalSector writes a single logical sector to a disk. It
// expects exactly 256 bytes.
func (md MappedDisk) WriteLogicalSector(track byte, sector byte, data []byte) error {
if track >= md.sectorDisk.Tracks() {
return fmt.Errorf("WriteLogicalSector expected track between 0 and %d; got %d", md.sectorDisk.Tracks()-1, track)
}
if sector >= md.sectorDisk.Sectors() {
return fmt.Errorf("WriteLogicalSector expected sector between 0 and %d; got %d", md.sectorDisk.Sectors()-1, sector)
}
physicalSector := md.logicalToPhysical[int(sector)]
return md.sectorDisk.WritePhysicalSector(track, physicalSector, data)
}

// Sectors returns the number of sectors on the DSK image.
func (md MappedDisk) Sectors() byte {
return md.sectorDisk.Sectors()
}

// Tracks returns the number of tracks on the DSK image.
func (md MappedDisk) Tracks() byte {
return md.sectorDisk.Tracks()
}

const (
Expand All @@ -48,55 +126,76 @@ const (

// DSK represents a .dsk disk image.
type DSK struct {
data [DOS33DiskBytes]byte
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) {
d := DSK{}
bb, err := ioutil.ReadFile(filename)
if err != nil {
return d, err
return DSK{}, err
}
// TODO(zellyn): handle 13-sector disks.
if len(bb) != DOS33DiskBytes {
return d, fmt.Errorf("Expected file %q to contain %d bytes, but got %d.", filename, DOS33DiskBytes, len(bb))
return DSK{}, fmt.Errorf("Expected file %q to contain %d bytes, but got %d.", filename, DOS33DiskBytes, len(bb))
}
copy(d.data[:], bb)
return d, nil
return DSK{
data: bb,
sectors: 16,
physicalToStored: Dos33PhysicalToLogicalSectorMap,
bytesPerTrack: 16 * 256,
tracks: DOS33Tracks,
}, nil
}

// ReadLogicalSector reads a single logical sector from the disk. It
// ReadPhysicalSector reads a single physical sector from the disk. It
// always returns 256 byes.
func (d DSK) ReadLogicalSector(track byte, sector byte) ([]byte, error) {
if track >= DOS33Tracks {
return nil, fmt.Errorf("Expected track between 0 and %d; got %d", DOS33Tracks-1, track)
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 >= DOS33Sectors {
return nil, fmt.Errorf("Expected sector between 0 and %d; got %d", DOS33Sectors-1, sector)
if sector >= d.sectors {
return nil, fmt.Errorf("ReadPhysicalSector expected sector between 0 and %d; got %d", d.sectors-1, sector)
}

start := int(track)*DOS33TrackBytes + 256*int(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
}

// WriteLogicalSector writes a single logical sector to a disk. It
// WritePhysicalSector writes a single physical sector to a disk. It
// expects exactly 256 bytes.
func (d DSK) WriteLogicalSector(track byte, sector byte, data []byte) error {
if track >= DOS33Tracks {
return fmt.Errorf("Expected track between 0 and %d; got %d", DOS33Tracks-1, track)
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 >= DOS33Sectors {
return fmt.Errorf("Expected sector between 0 and %d; got %d", DOS33Sectors-1, sector)
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("WriteLogicalSector expects data of length 256; got %d", len(data))
return fmt.Errorf("WritePhysicalSector expects data of length 256; got %d", len(data))
}

start := int(track)*DOS33TrackBytes + 256*int(sector)
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
}
4 changes: 2 additions & 2 deletions lib/disk/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type SectorSink interface {

// UnmarshalLogicalSector reads a sector from a SectorDisk, and
// unmarshals it into a SectorSink, setting its track and sector.
func UnmarshalLogicalSector(d SectorDisk, ss SectorSink, track, sector byte) error {
func UnmarshalLogicalSector(d LogicalSectorDisk, ss SectorSink, track, sector byte) error {
bytes, err := d.ReadLogicalSector(track, sector)
if err != nil {
return err
Expand All @@ -41,7 +41,7 @@ func UnmarshalLogicalSector(d SectorDisk, ss SectorSink, track, sector byte) err

// MarshalLogicalSector marshals a SectorSource to its sector on a
// SectorDisk.
func MarshalLogicalSector(d SectorDisk, ss SectorSource) error {
func MarshalLogicalSector(d LogicalSectorDisk, ss SectorSource) error {
track := ss.GetTrack()
sector := ss.GetSector()
bytes := ss.ToSector()
Expand Down
4 changes: 2 additions & 2 deletions lib/dos33/dos33.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ func (tsl *TrackSectorList) FromSector(data []byte) {

// readCatalogSectors reads the raw CatalogSector structs from a DOS
// 3.3 disk.
func readCatalogSectors(d disk.SectorDisk) ([]CatalogSector, error) {
func readCatalogSectors(d disk.LogicalSectorDisk) ([]CatalogSector, error) {
v := &VTOC{}
err := disk.UnmarshalLogicalSector(d, v, VTOCTrack, VTOCSector)
if err != nil {
Expand Down Expand Up @@ -431,7 +431,7 @@ func readCatalogSectors(d disk.SectorDisk) ([]CatalogSector, error) {
}

// ReadCatalog reads the catalog of a DOS 3.3 disk.
func ReadCatalog(d disk.SectorDisk) (files, deleted []FileDesc, err error) {
func ReadCatalog(d disk.LogicalSectorDisk) (files, deleted []FileDesc, err error) {
css, err := readCatalogSectors(d)
if err != nil {
return nil, nil, err
Expand Down
6 changes: 5 additions & 1 deletion lib/dos33/dos33_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ func TestTrackSectorListMarshalRoundtrip(t *testing.T) {

// TestReadCatalog tests the reading of the catalog of a test disk.
func TestReadCatalog(t *testing.T) {
dsk, err := disk.LoadDSK("testdata/dos33test.dsk")
sd, err := disk.LoadDSK("testdata/dos33test.dsk")
if err != nil {
t.Fatal(err)
}
dsk, err := disk.NewMappedDisk(sd, disk.Dos33LogicalToPhysicalSectorMap)
if err != nil {
t.Fatal(err)
}
Expand Down
Loading

0 comments on commit 68ee8a4

Please sign in to comment.