Working on NakedOS/Super-Mon data structures

This commit is contained in:
Zellyn Hunter 2016-11-08 23:37:15 -05:00
parent fe96da5d48
commit 692414ab6d
5 changed files with 213 additions and 10 deletions

View File

@ -9,6 +9,26 @@ import (
"io/ioutil"
)
// Dos33LogicalToPhysicalSectorMap maps logical sector numbers to physical ones.
// See [UtA2 9-42 - Read Routines].
var Dos33LogicalToPhysicalSectorMap = []byte{
0x00, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x01,
0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x0F,
}
// Dos33PhysicalToLogicalSectorMap maps physical sector numbers to logical ones.
// See [UtA2 9-42 - Read Routines].
var Dos33PhysicalToLogicalSectorMap = []byte{
0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F,
}
// TrackSector is a pair of track/sector bytes.
type TrackSector struct {
Track byte
Sector byte
}
type SectorDisk interface {
// ReadLogicalSector reads a single logical sector from the disk. It
// always returns 256 byes.
@ -50,10 +70,10 @@ func LoadDSK(filename string) (DSK, error) {
// ReadLogicalSector reads a single logical sector from the disk. It
// always returns 256 byes.
func (d DSK) ReadLogicalSector(track byte, sector byte) ([]byte, error) {
if track < 0 || track >= DOS33Tracks {
if track >= DOS33Tracks {
return nil, fmt.Errorf("Expected track between 0 and %d; got %d", DOS33Tracks-1, track)
}
if sector < 0 || sector >= DOS33Sectors {
if sector >= DOS33Sectors {
return nil, fmt.Errorf("Expected sector between 0 and %d; got %d", DOS33Sectors-1, sector)
}
@ -66,10 +86,10 @@ func (d DSK) ReadLogicalSector(track byte, sector byte) ([]byte, error) {
// WriteLogicalSector writes a single logical sector to a disk. It
// expects exactly 256 bytes.
func (d DSK) WriteLogicalSector(track byte, sector byte, data []byte) error {
if track < 0 || track >= DOS33Tracks {
if track >= DOS33Tracks {
return fmt.Errorf("Expected track between 0 and %d; got %d", DOS33Tracks-1, track)
}
if sector < 0 || sector >= DOS33Sectors {
if sector >= DOS33Sectors {
return fmt.Errorf("Expected sector between 0 and %d; got %d", DOS33Sectors-1, sector)
}
if len(data) != 256 {

View File

@ -346,11 +346,6 @@ func (fd *FileDesc) FilenameString() string {
return strings.TrimRight(string(slice), " ")
}
type TrackSector struct {
Track byte
Sector byte
}
// TrackSectorList is the struct used to represent DOS 3.3
// Track/Sector List sectors.
type TrackSectorList struct {
@ -361,7 +356,7 @@ type TrackSectorList struct {
Unused2 [2]byte // Not used
SectorOffset uint16 // Sector offset in file of the first sector described by this list.
Unused3 [5]byte // Not used
TrackSectors [122]TrackSector
TrackSectors [122]disk.TrackSector
}
// ToSector marshals the TrackSectorList to bytes.

128
lib/supermon/supermon.go Normal file
View File

@ -0,0 +1,128 @@
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
// Package supermon contains routines for working with the on-disk
// structures of NakedOS/Super-Mon disks.
package supermon
import (
"fmt"
"github.com/zellyn/diskii/lib/disk"
)
const (
// FileIllegal (zero) is not allowed in the sector map.
FileIllegal = 0
// FileFree signifies unused space in the sector map.
FileFree = 0xff
// FileReserved signifies space used by NakedOS in the sector map.
FileReserved = 0xfe
)
// SectorDiskShim is a shim to undo DOS 3.3 sector mapping:
// NakedOS/Super-Mon disks are typically represented as DOS 3.3 .dsk
// images, but NakedOS does no sector mapping.
type SectorDiskShim struct {
Dos33 disk.SectorDisk
}
// ReadLogicalSector reads a single logical sector from the disk. It
// always returns 256 byes.
func (s SectorDiskShim) ReadLogicalSector(track byte, sector byte) ([]byte, error) {
if sector >= 16 {
return nil, fmt.Errorf("Expected sector between 0 and 15; got %d", sector)
}
sector = disk.Dos33PhysicalToLogicalSectorMap[int(sector)]
return s.Dos33.ReadLogicalSector(track, sector)
}
// WriteLogicalSector writes a single logical sector to a disk. It
// expects exactly 256 bytes.
func (s SectorDiskShim) WriteLogicalSector(track byte, sector byte, data []byte) error {
if sector >= 16 {
return fmt.Errorf("Expected sector between 0 and 15; got %d", sector)
}
sector = disk.Dos33PhysicalToLogicalSectorMap[int(sector)]
return s.Dos33.WriteLogicalSector(track, sector, data)
}
// SectorMap is the list of sectors by file. It's always 560 bytes
// long (35 tracks * 16 sectors).
type SectorMap []byte
// LoadSectorMap loads a NakedOS sector map.
func LoadSectorMap(sd disk.SectorDisk) (SectorMap, error) {
sm := SectorMap(make([]byte, 560))
sector09, err := sd.ReadLogicalSector(0, 9)
if err != nil {
return sm, err
}
sector0A, err := sd.ReadLogicalSector(0, 0xA)
if err != nil {
return sm, err
}
sector0B, err := sd.ReadLogicalSector(0, 0xB)
if err != nil {
return sm, err
}
copy(sm[0:0x30], sector09[0xd0:])
copy(sm[0x30:0x130], sector0A)
copy(sm[0x130:0x230], sector0B)
return sm, nil
}
// FileForSector returns the file that owns the given track/sector.
func (sm SectorMap) FileForSector(track, sector byte) byte {
if track >= 35 {
panic(fmt.Sprintf("FileForSector called with track=%d > 34", track))
}
if sector >= 16 {
panic(fmt.Sprintf("FileForSector called with sector=%d > 15", sector))
}
return sm[int(track)*16+int(sector)]
}
// SectorsForFile returns the list of sectors that belong to the given
// file.
func (sm SectorMap) SectorsForFile(file byte) []disk.TrackSector {
var result []disk.TrackSector
for track := byte(0); track < 35; track++ {
for sector := byte(0); sector < 16; sector++ {
if file == sm.FileForSector(track, sector) {
result = append(result, disk.TrackSector{Track: track, Sector: sector})
}
}
}
return result
}
func (sm SectorMap) Verify() error {
for sector := byte(0); sector <= 0xB; sector++ {
if file := sm.FileForSector(0, sector); file != FileReserved {
return fmt.Errorf("Expected track 0, sectors 0-C to be reserved (0xFE), but got 0x%02X in sector %X", file, sector)
}
}
for track := byte(0); track < 35; track++ {
for sector := byte(0); sector < 16; sector++ {
file := sm.FileForSector(track, sector)
if file == FileIllegal {
return fmt.Errorf("Found illegal sector map value (%02X), in track %X sector %X", FileIllegal, track, sector)
}
}
}
return nil
}
// SectorsByFile returns a map of file number to slice of sectors.
func (sm SectorMap) SectorsByFile() map[byte][]disk.TrackSector {
result := map[byte][]disk.TrackSector{}
for file := byte(0x01); file < FileReserved; file++ {
sectors := sm.SectorsForFile(file)
if len(sectors) > 0 {
result[file] = sectors
}
}
return result
}

View File

@ -0,0 +1,60 @@
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
package supermon
import (
"testing"
"github.com/zellyn/diskii/lib/disk"
)
// loadSectorMap loads a sector map for the disk image contained in
// filename.
func loadSectorMap(filename string) (SectorMap, error) {
dsk, err := disk.LoadDSK(filename)
if err != nil {
return nil, err
}
sd := SectorDiskShim{Dos33: dsk}
sm, err := LoadSectorMap(sd)
if err != nil {
return nil, err
}
return sm, nil
}
// TestReadSectorMap tests the reading of the sector map of a test
// disk.
func TestReadSectorMap(t *testing.T) {
sm, err := loadSectorMap("testdata/chacha20.dsk")
if err != nil {
t.Fatal(err)
}
if err := sm.Verify(); err != nil {
t.Fatal(err)
}
testData := []struct {
file byte
length int
name string
}{
{1, 0x02, "FHELLO"},
{2, 0x17, "FSUPERMON"},
{3, 0x10, "FSYMTBL1"},
{4, 0x10, "FSYMTBL2"},
{5, 0x1E, "FMONHELP"},
{6, 0x08, "FSHORTSUP"},
{7, 0x1F, "FSHRTHELP"},
{8, 0x04, "FSHORT"},
{9, 0x60, "FCHACHA"},
}
sectorsByFile := sm.SectorsByFile()
for _, tt := range testData {
sectors := sectorsByFile[tt.file]
if len(sectors) != tt.length {
t.Errorf("Want %q to be %d sectors long; got %d", tt.name, tt.length, len(sectors))
}
}
}

BIN
lib/supermon/testdata/chacha20.dsk vendored Normal file

Binary file not shown.