mirror of https://github.com/zellyn/diskii.git
Starting work on ProDOS data structures
This commit is contained in:
parent
7a03a28621
commit
40d39fe78f
|
@ -34,14 +34,28 @@ var Dos33PhysicalToLogicalSectorMap = []byte{
|
||||||
0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F,
|
0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
|
||||||
// TrackSector is a pair of track/sector bytes.
|
// TrackSector is a pair of track/sector bytes.
|
||||||
type TrackSector struct {
|
type TrackSector struct {
|
||||||
Track byte
|
Track byte
|
||||||
Sector byte
|
Sector byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// SectorDisk is the interface use to read and write disks by physical
|
// SectorDisk is the interface used to read and write disks by
|
||||||
// (matches sector header) sector number.
|
// physical (matches sector header) sector number.
|
||||||
type SectorDisk interface {
|
type SectorDisk interface {
|
||||||
// ReadPhysicalSector reads a single physical sector from the disk. It
|
// ReadPhysicalSector reads a single physical sector from the disk. It
|
||||||
// always returns 256 byes.
|
// always returns 256 byes.
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright © 2017 Zellyn Hunter <zellyn@gmail.com>
|
||||||
|
|
||||||
|
// Package prodos contains routines for working with the on-disk
|
||||||
|
// structures of Apple ProDOS.
|
||||||
|
package prodos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A single ProDOS block.
|
||||||
|
type Block [512]byte
|
||||||
|
|
||||||
|
// BlockDevice is the interface used to read and write devices by
|
||||||
|
// logical block number.
|
||||||
|
type BlockDevice interface {
|
||||||
|
// ReadBlock reads a single block from the device. It always returns
|
||||||
|
// 512 byes.
|
||||||
|
ReadBlock(index uint16) (Block, error)
|
||||||
|
// WriteBlock writes a single block to a device. It expects exactly
|
||||||
|
// 512 bytes.
|
||||||
|
WriteBlock(index uint16, data Block) error
|
||||||
|
// Blocks returns the number of blocks on the device.
|
||||||
|
Blocks() uint16
|
||||||
|
// Write writes the device contents to the given Writer.
|
||||||
|
Write(io.Writer) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type VolumeBitMap []Block
|
||||||
|
|
||||||
|
func NewVolumeBitMap(blocks uint16) VolumeBitMap {
|
||||||
|
vbm := VolumeBitMap(make([]Block, (blocks+(512*8)-1)/(512*8)))
|
||||||
|
for b := 0; b < int(blocks); b++ {
|
||||||
|
vbm.MarkUnused(uint16(b))
|
||||||
|
}
|
||||||
|
return vbm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vbm VolumeBitMap) MarkUsed(block uint16) {
|
||||||
|
vbm.mark(block, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vbm VolumeBitMap) MarkUnused(block uint16) {
|
||||||
|
vbm.mark(block, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vbm VolumeBitMap) mark(block uint16, set bool) {
|
||||||
|
byteIndex := block >> 3
|
||||||
|
blockIndex := byteIndex / 512
|
||||||
|
blockByteIndex := byteIndex % 512
|
||||||
|
bit := byte(1 << (7 - (block & 7)))
|
||||||
|
if set {
|
||||||
|
vbm[blockIndex][blockByteIndex] |= bit
|
||||||
|
} else {
|
||||||
|
vbm[blockIndex][blockByteIndex] &^= bit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vbm VolumeBitMap) Free(block uint16) bool {
|
||||||
|
byteIndex := block >> 3
|
||||||
|
blockIndex := byteIndex / 512
|
||||||
|
blockByteIndex := byteIndex % 512
|
||||||
|
bit := byte(1 << (7 - (block & 7)))
|
||||||
|
return vbm[blockIndex][blockByteIndex]&bit > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadVolumeBitMap
|
||||||
|
func ReadVolumeBitMap(bd BlockDevice, startBlock uint16) (VolumeBitMap, error) {
|
||||||
|
blocks := bd.Blocks() / 4096
|
||||||
|
vbm := make([]Block, blocks)
|
||||||
|
for i := uint16(0); i < blocks; i++ {
|
||||||
|
block, err := bd.ReadBlock(startBlock + i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot read block %d of Volume Bit Map: %v", err)
|
||||||
|
}
|
||||||
|
vbm[i] = block
|
||||||
|
}
|
||||||
|
return VolumeBitMap(vbm), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the Volume Bit Map to a block device, starting at the
|
||||||
|
// given block.
|
||||||
|
func (vbm VolumeBitMap) Write(bd BlockDevice, startBlock uint16) error {
|
||||||
|
for i, block := range vbm {
|
||||||
|
if err := bd.WriteBlock(startBlock+uint16(i), block); err != nil {
|
||||||
|
return fmt.Errorf("cannot write block %d of Volume Bit Map: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DateTime struct {
|
||||||
|
YMD [2]byte
|
||||||
|
HM [2]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeDirectoryKeyBlock is the struct used to hold the ProDOS Volume Directory Key
|
||||||
|
// Block structure. See page 4-4 of Beneath Apple ProDOS.
|
||||||
|
type VolumeDirectoryKeyBlock struct {
|
||||||
|
Prev uint16 // Pointer to previous block (always zero: the KeyBlock is the first Volume Directory block
|
||||||
|
Next uint16 // Pointer to next block in the Volume Directory
|
||||||
|
Header VolumeDirectoryHeader
|
||||||
|
Descriptors [12]FileDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumeDirectoryBlock is a normal (non-key) segment in the Volume Directory Header.
|
||||||
|
type VolumeDirectoryBlock struct {
|
||||||
|
Prev uint16 // Pointer to previous block in the Volume Directory.
|
||||||
|
Next uint16 // Pointer to next block in the Volume Directory.
|
||||||
|
Descriptors [13]FileDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
type VolumeDirectoryHeader struct {
|
||||||
|
TypeAndNameLength byte // Storage type (top four bits) and volume name length (lower four).
|
||||||
|
VolumeName [15]byte // Volume name (actual length defined in TypeAndNameLength)
|
||||||
|
Unused1 [8]byte
|
||||||
|
Creation DateTime // Date and time volume was formatted
|
||||||
|
Version byte
|
||||||
|
MinVersion byte
|
||||||
|
Access byte
|
||||||
|
EntryLength byte // Length of each entry in the Volume Directory: usually $27
|
||||||
|
EntriesPerBlock byte // Usually $0D
|
||||||
|
FileCount uint16 // Number of active entries in the Volume Directory, not counting the Volume Directory Header
|
||||||
|
BitMapPointer uint16 // Block number of start of VolumeBitMap. Usually 6
|
||||||
|
TotalBlocks uint16 // Total number of blocks on the device. $118 (280) for a 35-track diskette.
|
||||||
|
}
|
||||||
|
|
||||||
|
type Access byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccessReadable Access = 0x01
|
||||||
|
AccessWritable Access = 0x02
|
||||||
|
AccessChangedSinceBackup Access = 0x20
|
||||||
|
AccessRenamable Access = 0x40
|
||||||
|
AccessDestroyable Access = 0x80
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileDescriptor is the entry in the volume directory for a file or
|
||||||
|
// subdirectory.
|
||||||
|
type FileDescriptor struct {
|
||||||
|
TypeAndNameLength byte // Storage type (top four bits) and volume name length (lower four)
|
||||||
|
FileName [15]byte // Filename (actual length defined in TypeAndNameLength)
|
||||||
|
FileType byte // ProDOS / SOS filetype
|
||||||
|
KeyPointer uint16 // block number of key block for file
|
||||||
|
BlocksUsed uint16 // Total number of blocks used including index blocks and data blocks. For a subdirectory, the number of directory blocks
|
||||||
|
Eof [3]byte // 3-byte offset of EOF from first byte. For sequential files, just the length
|
||||||
|
Creation DateTime // Date and time of of file creation
|
||||||
|
Version byte
|
||||||
|
MinVersion byte
|
||||||
|
Access Access
|
||||||
|
// For TXT files, random access record length (L from OPEN)
|
||||||
|
// For BIN files, load address for binary image (A from BSAVE)
|
||||||
|
// For BAS files, load address for program image (when SAVEd)
|
||||||
|
// For VAR files, address of compressed variables image (when STOREd)
|
||||||
|
// For SYS files, load address for system program (usually $2000)
|
||||||
|
AuxType uint16
|
||||||
|
LastMod DateTime
|
||||||
|
HeaderPointer uint16 // Block number of the key block for the directory which describes this file.
|
||||||
|
}
|
||||||
|
|
||||||
|
// An index block contains 256 16-bit block numbers, pointing to other
|
||||||
|
// blocks. The LSBs are stored in the first half, MSBs in the second.
|
||||||
|
type IndexBlock Block
|
||||||
|
|
||||||
|
// Get the blockNum'th block number from an index block.
|
||||||
|
func (i IndexBlock) Get(blockNum byte) uint16 {
|
||||||
|
return uint16(i[blockNum]) + uint16(i[256+int(blockNum)])<<8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the blockNum'th block number in an index block.
|
||||||
|
func (i IndexBlock) Set(blockNum byte, block uint16) {
|
||||||
|
i[blockNum] = byte(block)
|
||||||
|
i[256+int(blockNum)] = byte(block >> 8)
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
package prodos
|
Binary file not shown.
Loading…
Reference in New Issue