meta data for this page
Adding Support for a Hard Sectored Disk Format
As of Applesauce 1.66, it is possible for users to be able to add their own format definitions to be used by Applesauce. When you add a format, it will automatically be recognized everywhere in the client software such as the Disk Analyzer and Fast Imager. In order to add new formats, there is an Applesauce directory in your Documents folder. Within this folder there is a Formats one that contains the file hardsector_config.json
. The hard sector configuration files are text files in JSON format. The root object of the JSON should be an array. A single file can have as may format descriptions as desired. Whenever you edit the hardsector_config.json
, you will need to restart the Applesauce client in order to have it load the changes.
Creating a format definition is a highly technical process and if you are not very familiar with how disks are structured, this will likely seem a daunting task. The hard sector configuration files are text files in JSON format. The root object of the JSON should be an array. A single file can have as may format descriptions as desired.
There is an example of a configuration file with 2 definitions in it at the end of this document.
Format Identification
name
String - Required
This is a descriptive name of the format. It must be unique, but otherwise is only used for informational purposes.
ex: “name”: “North Star SD”
settings
String - Required
This name is used by the Fast Imager to remember user preferences related to this format.
ex: “settings”: “525NorthStar_Hard10x256”
fastname
String - Required
This is used by the Fast Imager when displaying the name of this format.
ex: “fastname”: “North Star”
export
[String] - Required
An array of disk image format names that can be used to save images of this format. It uses the internal names of formats.
ex: “export”: [“northstar_nsi”]
enabled
[Boolean] - Required
Set to true
if you would like this named format definition to be considered when analyzing disks. If no value is provided, then it defaults to false
.
ex: “enabled”: true
Media and Geometry
media
String - Required
The physical type of media used.
Value | Description |
---|---|
5.25 | 5.25“ Floppy Disk |
3.5 | 3.5” Floppy Disk |
8 | 8“ Floppy Disk |
ex: “media”: “5.25”
encoding
String - Required
Encoding and bitrate used.
Value | Description |
---|---|
fm_250 | FM encoding with 250kbps (4µs cell window) |
fm_500 | FM encoding with 500kbps (2µs cell window) |
mfm_250 | MFM encoding with 250kbps (4µs cell window) |
mfm_500 | MFM encoding with 500kbps (2µs cell window) |
m2fm_500 | M2FM encoding with 500kbps (2µs cell window) |
ex: “encoding”: “fm_250”
bitOrder
String
Bytes stored on disk are most commonly stored in “most significant bit” order, although some platforms store bytes in “least significant bit” order. This value allows you to specify which order should be used. If no value is provided, then it defaults to msb
.
Value | Description |
---|---|
msb | Most significant bit comes first |
lsb | Least significant bit comes first |
ex: “bitOrder”: “lsb”
trackCount
String
Expected number of tracks per disk side (cylinders).
If this value is not provided, then the following values will be used based on the media type:
Media | Default Value |
---|---|
5.25” | 40 |
3.5“ | 80 |
8” | 77 |
ex: “trackCount”: 35
trackBase
Int
Logical track number of the first track on the disk (typically 0 or 1). If no value is provided, then it defaults to 0
.
ex: “trackBase”: 0
headCount
Int
Number of drive heads. If no value is provided, then it defaults to 2
.
ex: “headCount”: 1
sectorsPerTrack
Int - Required
Number of hard sectors of the media. This is used to help identify the format as well as for determining disk geometry.
ex: “sectorsPerTrack”: 10
sectorBase
Int
Logical sector number of the first sector on each track (typically 0 or 1). If no value is provided, then it defaults to 0
.
ex: “sectorBase”: 0
sideOrganization
String
This is the method of organization for double sided disks. If no value is provided, then it defaults to norm
.
Value | Description |
---|---|
norm | Track and Head values used as normal. Track number increments per cylinder and Head increments per side |
trks | Track value increments per side and Head is ignored |
sect+ | Side 1 sector numbers are a continuation of side 0 |
ex: “sideOrganization”: “norm”
Sector Structure and Synchronization
sectorFields
String - Required
This describes which structures/fields are used for recording sectors.
Value | Description |
---|---|
adda | Each sector consists of separate address and data fields |
blob | Each sector consists of a single blob of data |
ex: “sectorFields”: “blob”
syncPatterns
[String] - Required
All disk formats have sync pattern(s) that inform the disk controller about how to synchronize itself to the proper start of bytes. Applesauce uses a sequence of 48 bits (6 bytes) that are represented by their hex values. Please note that the values used here are the values physically on the media for FM or MFM encoding, so every logical byte (8 bits) is represented by 16 bits. The last value of the sync pattern is also typically used as a start marker for the disk controller. If there are multiple possible sync sequences, then include them all.
ex: “syncPatterns”: [“AAAAAAAAFFEF”]
Address Field Structure
If the disk uses address and data fields (and your sectorFields
is set to adda
), then the following settings allow you to specify how the address field is structured. If your sectorFields
is blob
, then you can skip this section.
addrSync
String - Required
Sync values leading up to the marker. These values are in hex.
ex: “addrSync”: “0000”
addrMarker
String - Required
Address field marker value in hex.
ex: “addrMarker”: “FB”
addrMarkerBitMask
String - Optional
Address field marker bit mask to apply - value in hex.
ex: “addrMarkerBitMask”: “0F”
addrFieldSize
Int - Required
Byte size of the address field following the marker.
ex: “addrFieldSize”: 6
addrCksumType
String - Required
Type of checksum to be used to validate the address field.
Value | Description |
---|---|
none | There is no checksum |
8xrl | 8-bit rolling xor - xor data into register and then rotate left |
8add | 8-bit add |
8adc | 8-bit add with carry |
16add | 16-bit add |
12adc | 16-bit add with carry |
crc16 | 16-bit CRC (additional parameters can be set below) |
ex: “addrCksumType”: “8xrl”
addrCksumIndex
Int - Required
Byte index of the start of the stored checksum relative to the address marker (0 = address marker).
ex: “addrCksumIndex”: 5
addrCksumByteSize
Int - Required
Number of bytes for the stored checksum.
ex: “addrCksumByteSize”: 1
addrCksumEndian
String
Byte order of the stored checksum. If no value is provided, then it defaults to “be”
.
Value | Description |
---|---|
be | Stored checksum is big endian |
le | Stored checksum is little endian |
ex: “addrCksumEndian”: “le”
addrCksumFromIndex
Int - Required
Byte index of the first byte of the address field that needs to be checksummed, relative to the address marker (0 = address marker).
ex: “addrCksumFromIndex”: 1
addrCksumToIndex
Int - Required
Byte index of the last byte of the address field that needs to be checksummed, relative to the address marker (0 = address marker).
ex: “addrCksumToIndex”: 4
addrCksumInit
Int
A 32-bit value that is used as the initial value of the checksum. If no value is provided, then it defaults to 0
.
ex: “addrCksumInit”: 0
addrCksumXorOut
Int
A 32-bit value that is xor'ed with checksum post calculation. If no value is provided, then it defaults to 0
which means not to perform an xor.
ex: “addrCksumXorOut”: 0
addrCksumPoly
Int
A 32-bit value that is the polynomial to be used for CRC checksums. If no value is provided, then it defaults to 4129
(0x1021) for crc16 checksums.
ex: “addrCksumPoly”: 4129
addrCksumRefIn
Boolean
If true
, then the data bytes will be reflected (bit-endian reversed (eg: 0x80 becomes 0x01)) on input to the checksum algorithm. If no value is provided, then it defaults to false
.
ex: “addrCksumRefIn”: true
addrCksumRefOut
Boolean
If true
, then the result of the checksum algorithm will have its data reflected (bit-endian reversed) on output. If no value is provided, then it defaults to false
.
ex: “addrCksumRefOut”: true
addrEpilog
String
Some formats use bytes that follow the address field as part of the data integrity check. If an epilog is included, then it will be verified in order to consider a sector as being valid. Epilog values are in hex.
ex: “addrEpilog”: “DEAAEB”
addrEpilogIndex
Int - Required if including an addrEpilog
Byte index of the start of the epilog relative to the address marker (0 = address marker).
ex: “addrEpilogIndex”: 258
Sector Identification
If the format uses an address field, then the bit indexes referenced below are relative to the address marker. Otherwise they are relative to the data marker.
volumeDerived
String
Method that is to be used to determine the volume number for a sector. If no value is provided, then it defaults to none
.
Value | Description |
---|---|
none | This format does not use a volume number |
data | The volume number should be derived from data on the disk |
ex: “volumeDerived”: “data”
volumeBitIndex
Int - Required if volumeDerived is “data”
The bit index of the start of the volume number. This index is relative to the field marker with 0
being the first bit of the marker.
ex: “volumeBitIndex”: 8
volumeBitSize
Int - Only used if volumeDerived is “data”
The number of bits that comprise the volume number. If no value is provided, then it defaults to 8
.
ex: “volumeBitSize”: 8
trackDerived
String - Required
Method that is to be used to determine the track number for a sector.
Value | Description |
---|---|
data | The track number should be derived from data on the disk |
phys | The track number should be derived from the physical track |
ex: “trackDerived”: “phys”
trackBitIndex
Int - Required if trackDerived is “data”
The bit index of the start of the track number. This index is relative to the field marker with 0
being the first bit of the marker.
ex: “trackBitIndex”: 8
trackBitSize
Int - Only used if trackDerived is “data”
The number of bits that comprise the track number. If no value is provided, then it defaults to 8
.
ex: “trackBitSize”: 8
trackVerify
Boolean - Only used if trackDerived is “data”
Set to true
if you would like to validate that the track number matches the physical track number. If no value is provided, then it defaults to false
.
ex: “trackVerify”: true
headDerived
String
Method that is to be used to determine the head number for a sector. If no value is provided, then it defaults to none
.
Value | Description |
---|---|
none | This format does not use a head number |
data | The head number should be derived from data on the disk |
phys | The head number should be derived from the physical head |
ex: “headDerived”: “data”
headBitIndex
Int - Required if headDerived is “data”
The bit index of the start of the head number. This index is relative to the field marker with 0
being the first bit of the marker.
ex: “headBitIndex”: 16
headBitSize
Int - Only used if headDerived is “data”
The number of bits that comprise the head number. If no value is provided, then it defaults to 8
.
ex: “headBitSize”: 8
sectorDerived
String - Required
Method that is to be used to determine the sector number for a sector.
Value | Description |
---|---|
data | The sector number should be derived from data on the disk |
phys | The sector number should be derived from the physical sector index holes |
ex: “sectorDerived”: “data”
sectorBitIndex
Int - Required if sectorDerived is “data”
The bit index of the start of the sector number. This index is relative to the field marker with 0
being the first bit of the marker.
ex: “sectorBitIndex”: 24
sectorBitSize
Int - Only used if sectorDerived is “data”
The number of bits that comprise the sector number. If no value is provided, then it defaults to 8
.
ex: “sectorBitSize”: 8
sectorVerify
Boolean - Only used if sectorDerived is “data”
Set to true
if you would like to validate that the sector number matches the physical sector number. If no value is provided, then it defaults to false
.
ex: “sectorVerify”: true
Data Field Structure
dataSync
String - Required
Sync values leading up to the marker. These values are in hex.
ex: “dataSync”: “0000”
dataMarker
String - Required
Data field marker value in hex.
ex: “dataMarker”: “FB”
dataMarkerBitMask
String - Optional
Data field marker bit mask to apply - value in hex. The length of the bit mask should equal the length of the marker. This value is used to “mask off” particular bits to exclude them from consideration when locating a marker. A binary value of 1 in any position will retain that bit for comparison. A binary value of 0 in any position will exclude that bit from comparison.
ex: “dataMarkerBitMask”: “0F”
dataStartIndex
Int - Required
Byte index of the start of the data relative to the data marker (0 = data marker byte).
ex: “dataStartIndex”: 1
dataSize
Int - Required
Number of bytes of data.
ex: “dataSize”: 256
dataCksumType
String - Required
Type of checksum to be used to validate the data.
Value | Description |
---|---|
none | There is no checksum |
8xrl | 8-bit rolling xor - xor data into register and then rotate left |
8add | 8-bit add |
8adc | 8-bit add with carry |
16add | 16-bit add |
12adc | 16-bit add with carry |
crc16 | 16-bit crc (additional parameters can be set below) |
ex: “dataCksumType”: “8xrl”
dataCksumIndex
Int - Required
Byte index of the start of the stored checksum relative to the data marker (0 = data marker).
ex: “dataCksumIndex”: 257
dataCksumByteSize
Int - Required
Number of bytes for the stored checksum.
ex: “dataCksumByteSize”: 1
dataCksumEndian
String
Byte order of the stored checksum. If no value is provided, then it defaults to “be”
.
Value | Description |
---|---|
be | Stored checksum is big endian |
le | Stored checksum is little endian |
ex: “dataCksumEndian”: “le”
dataCksumFromIndex
Int - Required
Byte index of the first byte of data that needs to be checksummed, relative to the data marker (0 = data marker).
ex: “dataCksumFromIndex”: 1
dataCksumToIndex
Int - Required
Byte index of the last byte of disk data that needs to be checksummed, relative to the data marker (0 = data marker).
ex: “dataCksumToIndex”: 256
dataCksumInit
Int
A 32-bit value that is used as the initial value of the checksum. If no value is provided, then it defaults to 0
.
ex: “dataCksumInit”: 0
dataCksumXorOut
Int
A 32-bit value that is xor'ed with checksum post calculation. If no value is provided, then it defaults to 0
which means not to perform an xor.
ex: “dataCksumXorOut”: 0
dataCksumPoly
Int
A 32-bit value that is the polynomial to be used for CRC checksums. If no value is provided, then it defaults to 4129
(0x1021) for crc16 checksums.
ex: “dataCksumPoly”: 4129
dataCksumRefIn
Boolean
If true
, then the data bytes will be reflected (bit-endian reversed (eg: 0x80 becomes 0x01)) on input to the checksum algorithm. If no value is provided, then it defaults to false
.
ex: “dataCksumRefIn”: true
dataCksumRefOut
Boolean
If true
, then the result of the checksum algorithm will have its data reflected (bit-endian reversed) on output. If no value is provided, then it defaults to false
.
ex: “dataCksumRefOut”: true
dataEpilog
String
Some formats use bytes that follow the sector data as part of the data integrity check. If an epilog is included, then it will be verified in order to consider a sector as being valid. Data epilog values are in hex.
ex: “dataEpilog”: “DEAAEB”
dataEpilogIndex
Int - Required if including a dataEpilog
Byte index of the start of the epilog relative to the data marker (0 = data marker).
ex: “dataEpilogIndex”: 258
dataProcess
[String]
Used if the sector data requires any kind of special processing. Multiple values can be used.
Value | Description |
---|---|
flip16 | Data is 16-bit words that need to be endian flipped |
negate | Bitwise negation of sector data |
ex: “dataProcess”: [“negate”]
Example Configuration File
[ { "name": "North Star", "enabled": true, "settings": "525NorthStar_Hard10x512", "export": ["northstar_nsi"], "media": "5.25", "encoding": "mfm_250", "trackCount": 35, "trackBase": 0, "headCount": 2, "sectorsPerTrack": 10, "sectorBase": 0, "syncPatterns": ["AAAAAAAA5545"], "sectorFields": "blob", "trackDerived": "phys", "headDerived": "phys", "sectorDerived": "phys", "dataSync": "0000", "dataMarker": "FB", "dataStartIndex": 2, "dataSize": 512, "dataCksumType": "8xrl", "dataCksumIndex": 514, "dataCksumByteSize": 1, "dataCksumFromIndex": 2, "dataCksumToIndex": 513 }, { "name": "Heath/Zenith H-17", "enabled": true, "settings": "525HeathH17_Hard10x256", "export": ["heath_h8d"], "media": "5.25", "encoding": "fm_250", "trackCount": 40, "trackBase": 0, "headCount": 2, "sectorsPerTrack": 10, "sectorBase": 0, "sideOrganization": "trks", "syncPatterns": ["AAAAAAAAEFFF"], "bitOrder": "lsb", "sectorFields": "adda", "addrSync": "0000", "addrMarker": "FD", "addrFieldSize": 3, "addrCksumType": "8xrl", "addrCksumIndex": 4, "addrCksumByteSize": 1, "addrCksumFromIndex": 1, "addrCksumToIndex": 3, "volumeDerived": "data", "volumeBitIndex": 8, "trackDerived": "data", "trackBitIndex": 16, "trackVerify": true, "headDerived": "phys", "sectorDerived": "data", "sectorBitIndex": 24, "dataSync": "0000", "dataMarker": "FD", "dataStartIndex": 1, "dataSize": 256, "dataCksumType": "8xrl", "dataCksumIndex": 257, "dataCksumByteSize": 1, "dataCksumFromIndex": 1, "dataCksumToIndex": 256 } ]