Skip to content
@mplx/d64

The D64 format on disk

Every byte offset the deck implements. Offsets are hexadecimal, relative to the start of their block.

The image

Blocks of a 1541 disk stored back to back: track order, then sector order, nothing in between. Optionally one error byte per block follows.

TracksBlocksPlainWith error table
35683174,848175,531
40768196,608197,376
42802205,312206,114

No header: length alone identifies the geometry. The six sizes are distinct.

Zones

TracksSectors each
1-1721
18-2419
25-3018
31-4217

Tracks 36-42 do not exist on a stock drive; every extended format repeats the innermost zone.

offset(track, sector) = (totalSectors(track - 1) + sector) * 256

Sector 18/0 - the BAM

OffsetContent
$00-$01track/sector of the first directory block - 18 / 1
$02DOS version byte, $41 ('A')
$03unused, $00
$04-$8fBAM entries for tracks 1-35, four bytes each
$90-$9fdisk name, 16 bytes, PETSCII padded with $a0
$a0-$a1$a0 $a0
$a2-$a3disk ID, two bytes
$a4$a0
$a5-$a6DOS type, "2A" ($32 $41)
$a7-$aa$a0 $a0 $a0 $a0
$ab-$ffunused on a stock disk - see below

A BAM entry

Four bytes per track: a free-sector count, then a 24-bit map where a set bit means free. Sector n is bit n % 8 of byte 1 + n / 8. Bits past the end of the track stay clear.

track 1, wholly free:   15 ff ff 1f    21 free, bits 0-20 set
track 18, just formatted: 11 fc ff 07  17 free, bits 0 and 1 clear

The 40-track extensions

The five entries for tracks 36-40 go in the unused tail. The three speeder DOSes disagreed about where:

FormatTracks 36-40Disk header
CBM-$90
SpeedDOS$c0-$d3$90
DolphinDOS$ac-$bf$90
PrologicDOS$90-$a3$a4

PrologicDOS used the disk-name bytes and moved the whole header - name, filler, ID, filler, DOS type, filler - 20 bytes forward, so its DOS type lands at $b9-$ba. detectFormat keys on that.

No format ever defined entries for tracks 41 and 42.

Track 18 - the directory

A chain from 18/1, each block linked by its first two bytes, at interleave 3 (18/1, 18/4, 18/7, …). 18 blocks fit, 8 slots each: 144 files maximum.

On the last block of the chain the link is $00 $ff.

A directory slot

OffsetContent
$00-$01track/sector of the next directory block - first slot only, $00 $00 in the rest
$02file type byte
$03-$04track/sector of the file's first block
$05-$14filename, 16 bytes, PETSCII padded with $a0
$15-$16track/sector of the first side-sector block, REL only
$17record length, REL only, at most 254
$18-$1dunused (GEOS uses them)
$1e-$1ffile length in blocks, low byte first

Slots start at $00, $20, $40, $60, $80, $a0, $c0, $e0.

The file type byte

Bits 0-3 are the type. 5-15 are illegal and a real drive is unpredictable; the deck reads them as DEL.

ValueType
0DEL
1SEQ
2PRG
3USR
4REL
BitMeaning
6locked - lists as <, SCRATCH refuses it
7closed - clear means a "splat" file, listed with a leading *

So $82 is a closed PRG, $c2 a closed locked PRG, $02 an unclosed PRG, and $00 a scratched slot.

A scratch zeroes the type byte and nothing else. Name and first-block pointer remain; only the BAM changes. A listing must filter on the type byte alone.

A file

A linked list of blocks at an interleave of 10.

OffsetContent
$00track of the next block, or $00 in the last one
$01sector of the next block - or, when $00 is zero, the length
$02-$ffpayload, 254 bytes

In the last block a length byte of n means bytes $02-$n are file data: n - 1 payload bytes. $02 is one byte, $ff the full 254, $01 none - how an empty file is stored. $00 is not a length; the deck raises on it.

A PRG's first two payload bytes are its load address, low byte first - $01 $08 is $0801, where BASIC starts on a C64.

Sources

  • C64-Wiki: D64 - container, sizes, zones.
  • Peter Schepers, D64 (Electronic form of a physical 1541 disk), rev 1.11 - BAM, directory, chain, and the three speeder BAM offsets.
  • VICE - reference implementation these offsets were checked against.