Skip to content

LowLevel

dfgordon edited this page Sep 10, 2023 · 9 revisions

Low Level Services

This deals with low level disk operations and associated types.

File Images and the any Type

File images are a way of abstracting any file that could exist on the file systems that a2kit handles, including sparse files. When you want to specify that an item is a file image you use the any type. As an example, suppose we have a binary file named thechip containing the 4 byte sequence 6,5,0, and 2. We can get the any representation using

a2kit get -f thechip -t any -d mydos33.dsk

Assuming console output, this would display

{
    "fimg_version": "2.0.0",
    "file_system": "a2 dos",
    "chunk_len": 256,
    "eof": "",
    "fs_type": "04",
    "aux": "",
    "access": "",
    "created": "",
    "modified": "",
    "version": "",
    "min_version": "",
    "chunks": {
        "0": "00030400060500020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
    }
}

For DOS, most of the metadata is empty. In this case there is only one "chunk," but generally there could be many. The same file retrieved from ProDOS would look different:

{
    "fimg_version": "2.0.0",
    "file_system": "prodos",
    "chunk_len": 512,
    "eof": "040000",
    "fs_type": "06",
    "aux": "0003",
    "access": "E3",
    "created": "842D1C0A",
    "modified": "842D1C0A",
    "version": "24",
    "min_version": "00",
    "chunks": {
        "0": "0605000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
    }
}

A few things to note:

  • The file image is pure JSON, it can be processed with any JSON parser.
  • All fields after the first three are hex strings. The interpretation of metadata depends on the file system, but wherever possible, the bytes are in direct correspondence with what is stored on disk.
  • For DOS 3.3, the starting address and length of the data are in the first two words of chunk 0. This is a characteristic of the file system, not the file image representation.
  • For ProDOS, the starting address is in the aux value, and the length is in the eof value.
  • The chunk keys do not have to be in any kind of sequence, or even be numbers. The file system implementation must know how to interpret its own keys. Generally sequential files will have keys in an unbroken sequence, while sparse files will have "missing" keys.

You can pass file images through the a2kit pipeline the same as any other object, but you cannot move data between disparate file systems this way. For that you must use the high level file types.

Chunk or Block?

The "chunks" that appear in a file image are nearly identical to the file system's allocation blocks. The only difference is that a chunk number is purely abstract, whereas a block number corresponds to a definite location on disk. The way the chunk numbers get mapped to physical sectors will depend on the particular disk the file image is restored to.

Track Operations

This allows you to look at or copy the track data that is stored with WOZ images. To get the bytes exactly as they are stored in the WOZ track buffer use

a2kit get -t raw_track -f 17,0 -d mydos33.woz

Here the -f argument is <cylinder>,<head>. You can also align the nibbles:

a2kit get -t track -f 17,0 -d mydos33.woz

The output for track is easier to interpret, and also will display the following mnemonics alongside:

Mnemonic Pattern Meaning Comment
>... repeated $FF sync bytes false matches are possible
(A: $D5 AA 96 address prolog 3.5 inch, 5.25 inch 16 sector
(A: $D5 AA B5 address prolog 5.25 inch 13 sector
(D: $D5 AA AD data prolog
::) $DE AA EB either epilog 5.25 inch
:) $DE AA either epilog 3.5 inch
0-F address field decoded 4&4 address nibbles 5.25 inch
0-F or ^ address field decoded 6&2 address nibbles 3.5 inch, ^ means >15
R $D5 or $AA reserved bytes in case they appear outside prologs/epilogs
? invalid nibble possible bad track OK in sync gaps

If you want to copy a track, you can only use raw_track, and the source buffer must be the same size as the destination buffer. The encoding of the source is carried over without modification to the destination.

Track operations are only for image types that store the detailed track data. If the image type does not have such data an error will be returned.

Sector Operations

This allows you to read or write directly to physical sectors, without any need to identify a file system. The -f argument is <cylinder>,<head>,<sector>. For example

a2kit get -t sec -f 17,0,1 -d mydos33.woz

would read cylinder 17, side 0, sector 1, and display it to stdout. For images with the full track data, like WOZ images, there is no problem defining exactly what a physical sector is, i.e., the track is searched for the given address. For image types that rely on an assumed ordering, such as DSK, it cannot always be guaranteed that the retrieved sector corresponds to the sector address on the original (e.g. if the DSK is ProDOS ordered this will not work). In such cases you can use block operations to identify the file system and retrieve it's native allocation unit.

You can also write a sector by pipelining some data into put:

# dangerous operation
a2kit get -f some_local_file | a2kit put -t sec -f 17,0,1 -d mydos33.woz

It is important to understand this is a blind write, there are no checks against breaking whatever file system is on the disk.

Block Operations

You can use block operations to get or put the file system's native allocation unit. The block is always identified by a single unsigned integer. For this reason, if the file system is sector-oriented (e.g. DOS 3.x), one needs the following formula to relate track and sector numbers to the block number:

// only needed for DOS 3.x
block_number = track_number * sectors_per_track + logical_sector_number;

In order to get a block, simply specify the block "file type" and use the block number as the "path":

a2kit get -t block -f 272 -d mydos33.dsk

This will display the VTOC if the disk is a 5.25 inch DOS 3.3 floppy.

You can also write a block by pipelining some data into put:

# dangerous operation
a2kit get -f some_local_file | a2kit put -t block -f 272 -d mydos33.dsk

It is important to understand this is a blind write, there are no checks against breaking whatever file system is on the disk.

Accessing Multiple Sectors or Blocks

You can access any number of sectors or blocks all at once using non-contiguous range specifications. Apart from being a CLI convenience, this can make an enormous difference in script performance. A non-contiguous region is specified by putting the ,, separator between contiguous regions. Contiguous regions are formed using the .. separator. As an example, suppose we want the boot tracks and directory track of a DOS disk. We can use

a2kit get -d mydos33.dsk -t sec -f 0..4,0,0..16,,17,0,0..16

Remember, the single comma separates cyl,head,sec, while the double-comma separates contiguous regions. Note the form beg..end, where beg is included, but end is not (C loop thinking). For blocks the notation is similar. The following would grab blocks 0,3,4,5,6,7:

a2kit get -d prodos.dsk -t block -f 0,,3..8

Troubleshooting

  • Don't confuse physical sectors with logical sectors. Logical sectors are never referenced except through blocks. See above for how DOS blocks are defined.
  • Sector access is not allowed for logical volumes, such as PO images.
  • CP/M reserved tracks are only accessible by sector. The user area is accessible by either sector or block.
  • Do not use sector access on a DSK unless it is known to be DOS ordered.
  • If you pipe the result of a multi-sector or multi-block get, the receiving node (probably put) must use the same range specification. N.b. a range mismatch that happens to match in size will fail silently.
Clone this wiki locally