2016-11-04 00:49:49 +00:00
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
// Package disk contains routines for reading and writing various disk
// file formats.
package disk
import (
"fmt"
2016-11-23 03:38:20 +00:00
"io"
2016-11-13 21:59:38 +00:00
"path"
"strings"
)
2016-12-10 21:29:41 +00:00
// Various DOS33 disk characteristics.
2016-11-13 21:59:38 +00:00
const (
2016-12-10 21:29:41 +00:00
DOS33Tracks = 35
2016-11-13 21:59:38 +00:00
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
2016-11-04 00:49:49 +00:00
)
2016-11-09 04:37:15 +00:00
// 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 ,
}
2017-03-12 21:47:18 +00:00
// ProDOSLogicalToPhysicalSectorMap maps logical sector numbers to pysical ones.
// See [UtA2e 9-43 - Sectors vs. Blocks].
var ProDOSLogicalToPhysicalSectorMap = [ ] byte {
0x00 , 0x02 , 0x04 , 0x06 , 0x08 , 0x0A , 0x0C , 0x0E ,
0x01 , 0x03 , 0x05 , 0x07 , 0x09 , 0x0B , 0x0D , 0x0F ,
}
// ProDosPhysicalToLogicalSectorMap maps physical sector numbers to logical ones.
// See [UtA2e 9-43 - Sectors vs. Blocks].
var ProDosPhysicalToLogicalSectorMap = [ ] byte {
0x00 , 0x08 , 0x01 , 0x09 , 0x02 , 0x0A , 0x03 , 0x0B ,
0x04 , 0x0C , 0x05 , 0x0D , 0x06 , 0x0E , 0x07 , 0x0F ,
}
2016-11-09 04:37:15 +00:00
// TrackSector is a pair of track/sector bytes.
type TrackSector struct {
Track byte
Sector byte
}
2017-03-12 21:47:18 +00:00
// SectorDisk is the interface used to read and write disks by
// physical (matches sector header) sector number.
2016-11-04 00:49:49 +00:00
type SectorDisk interface {
2016-11-13 03:15:45 +00:00
// 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
2016-11-23 03:38:20 +00:00
// Write writes the disk contents to the given file.
Write ( io . Writer ) ( int , error )
2018-06-07 02:27:15 +00:00
// Order returns the sector order.
Order ( ) string
2016-11-13 03:15:45 +00:00
}
2016-12-10 21:29:41 +00:00
// LogicalSectorDisk is the interface used to read and write a disk by
// *logical* sector number.
2016-11-13 03:15:45 +00:00
type LogicalSectorDisk interface {
2016-11-04 00:49:49 +00:00
// 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
2016-11-13 03:15:45 +00:00
// Sectors returns the number of sectors on the SectorDisk
Sectors ( ) byte
// Tracks returns the number of tracks on the SectorDisk
Tracks ( ) byte
2016-11-23 03:38:20 +00:00
// Write writes the disk contents to the given file.
Write ( io . Writer ) ( int , error )
2018-06-07 02:27:15 +00:00
// Order returns the underlying sector ordering.
Order ( ) string
2016-11-13 03:15:45 +00:00
}
// 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 { }
2016-12-10 21:29:41 +00:00
// NewMappedDisk returns a MappedDisk with the given
// logical-to-physical sector mapping.
2016-11-13 03:15:45 +00:00
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 )
}
2016-11-13 21:59:38 +00:00
// Sectors returns the number of sectors in the disk image.
2016-11-13 03:15:45 +00:00
func ( md MappedDisk ) Sectors ( ) byte {
return md . sectorDisk . Sectors ( )
}
2016-11-13 21:59:38 +00:00
// Tracks returns the number of tracks in the disk image.
2016-11-13 03:15:45 +00:00
func ( md MappedDisk ) Tracks ( ) byte {
return md . sectorDisk . Tracks ( )
2016-11-04 00:49:49 +00:00
}
2016-11-23 03:38:20 +00:00
// Write writes the disk contents to the given file.
func ( md MappedDisk ) Write ( w io . Writer ) ( n int , err error ) {
return md . sectorDisk . Write ( w )
}
2018-06-07 02:27:15 +00:00
// Order returns the sector order of the underlying sector disk.
func ( md MappedDisk ) Order ( ) string {
return md . sectorDisk . Order ( )
}
2017-03-18 02:26:15 +00:00
// OpenDisk opens a disk image by filename.
func OpenDisk ( filename string ) ( SectorDisk , error ) {
2016-11-13 21:59:38 +00:00
ext := strings . ToLower ( path . Ext ( filename ) )
switch ext {
case ".dsk" :
return LoadDSK ( filename )
2016-11-04 00:49:49 +00:00
}
2016-11-13 21:59:38 +00:00
return nil , fmt . Errorf ( "Unimplemented/unknown disk file extension %q" , ext )
2016-11-13 03:15:45 +00:00
}
2017-03-18 02:26:15 +00:00
2017-03-23 02:27:27 +00:00
// OpenDev opens a device image by filename.
func OpenDev ( filename string ) ( BlockDevice , error ) {
ext := strings . ToLower ( path . Ext ( filename ) )
switch ext {
case ".po" :
return LoadDev ( filename )
}
return nil , fmt . Errorf ( "Unimplemented/unknown device file extension %q" , ext )
}
2017-03-18 02:26:15 +00:00
// Open opens a disk image by filename, returning an Operator.
func Open ( filename string ) ( Operator , error ) {
sd , err := OpenDisk ( filename )
2017-03-23 02:27:27 +00:00
if err == nil {
var op Operator
op , err = OperatorForDisk ( sd )
if err == nil {
return op , nil
}
}
dev , err2 := OpenDev ( filename )
if err2 == nil {
var op Operator
op , err2 = OperatorForDevice ( dev )
if err2 != nil {
return nil , err2
}
return op , nil
}
return nil , err
}
type DiskBlockDevice struct {
lsd LogicalSectorDisk
blocks uint16
}
// BlockDeviceFromSectorDisk creates a ProDOS block device from a
// SectorDisk. It reads maps ProDOS to physical sectors.
func BlockDeviceFromSectorDisk ( sd SectorDisk ) ( BlockDevice , error ) {
lsd , err := NewMappedDisk ( sd , ProDOSLogicalToPhysicalSectorMap )
2017-03-18 02:26:15 +00:00
if err != nil {
return nil , err
}
2017-03-23 02:27:27 +00:00
return DiskBlockDevice {
lsd : lsd ,
blocks : uint16 ( lsd . Tracks ( ) ) / 2 * uint16 ( lsd . Sectors ( ) ) ,
} , nil
}
// ReadBlock reads a single block from the device. It always returns
// 512 byes.
func ( dbv DiskBlockDevice ) ReadBlock ( index uint16 ) ( Block , error ) {
var b Block
if index >= dbv . blocks {
return b , fmt . Errorf ( "device has %d blocks; tried to read block %d (index=%d)" , dbv . blocks , index + 1 , index )
}
i := int ( index ) * 2
sectors := int ( dbv . lsd . Sectors ( ) )
track0 := i / sectors
sector0 := i % sectors
sector1 := sector0 + 1
track1 := track0
if sector1 == sectors {
sector1 = 0
track1 ++
}
b0 , err := dbv . lsd . ReadLogicalSector ( byte ( track0 ) , byte ( sector0 ) )
2017-03-18 02:26:15 +00:00
if err != nil {
2017-03-23 02:27:27 +00:00
return b , fmt . Errorf ( "error reading first half of block %d (t:%d s:%d): %v" , index , track0 , sector0 , err )
}
b1 , err := dbv . lsd . ReadLogicalSector ( byte ( track1 ) , byte ( sector1 ) )
if err != nil {
return b , fmt . Errorf ( "error reading second half of block %d (t:%d s:%d): %v" , index , track1 , sector1 , err )
2017-03-18 02:26:15 +00:00
}
2017-03-23 02:27:27 +00:00
copy ( b [ : 256 ] , b0 )
copy ( b [ 256 : ] , b1 )
return b , nil
}
// WriteBlock writes a single block to a device. It expects exactly
// 512 bytes.
func ( dbv DiskBlockDevice ) WriteBlock ( index uint16 , data Block ) error {
if index >= dbv . blocks {
return fmt . Errorf ( "device has %d blocks; tried to read block %d (index=%d)" , dbv . blocks , index + 1 , index )
}
i := int ( index ) * 2
sectors := int ( dbv . lsd . Sectors ( ) )
track0 := i / sectors
sector0 := i % sectors
sector1 := sector0 + 1
track1 := track0
if sector1 == sectors {
sector1 = 0
track1 ++
}
if err := dbv . lsd . WriteLogicalSector ( byte ( track0 ) , byte ( sector0 ) , data [ : 256 ] ) ; err != nil {
return fmt . Errorf ( "error writing first half of block %d (t:%d s:%d): %v" , index , track0 , sector0 , err )
}
if err := dbv . lsd . WriteLogicalSector ( byte ( track1 ) , byte ( sector1 ) , data [ 256 : ] ) ; err != nil {
return fmt . Errorf ( "error writing second half of block %d (t:%d s:%d): %v" , index , track1 , sector1 , err )
}
return nil
}
// Blocks returns the number of blocks on the device.
func ( dbv DiskBlockDevice ) Blocks ( ) uint16 {
return dbv . blocks
}
2018-06-07 02:27:15 +00:00
// Order returns the underlying sector or block order of the storage.
func ( dbv DiskBlockDevice ) Order ( ) string {
return dbv . lsd . Order ( )
}
2017-03-23 02:27:27 +00:00
// Write writes the device contents to the given Writer.
func ( dbv DiskBlockDevice ) Write ( w io . Writer ) ( int , error ) {
return dbv . lsd . Write ( w )
2017-03-18 02:26:15 +00:00
}