mirror of
https://github.com/zellyn/diskii.git
synced 2024-12-22 15:30:13 +00:00
Working on NakedOS/Super-Mon data structures
This commit is contained in:
parent
fe96da5d48
commit
692414ab6d
@ -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 {
|
||||
|
@ -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
128
lib/supermon/supermon.go
Normal 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
|
||||
}
|
60
lib/supermon/supermon_test.go
Normal file
60
lib/supermon/supermon_test.go
Normal 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
BIN
lib/supermon/testdata/chacha20.dsk
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user