Home
Home
First Prev Contents Next Last
First - Prev - Contents - Next - Last
Deutsch Español Italiano
Deutsch - Español - Italiano

Appendix E: Emulator File Formats

This section describes all the common file formats in the field of Commodore 64 emulation. It is intended for the interested programmer, who might wish to learn the internal workings of the emulator. If you are only interested in playing your favorite C64 game, then you do not need to worry about these details.

Power64 uses different icons for the various file format. Since Power64 allows the use of GZ/ZIP or LHA compressed files, many of the icons come in three subtypes. A plain icon for uncompressed files, a icon with a yellow ribbon to indicate GZ compression and an icon with a red ribbon for ZIP compression. LHA compressed files do not carry a special icon indicating the file type. Since they are so rare, a simple LHA-document icon is used for all types of LHA compressed files.

E.1 *.P00 Program Format

P00 Icon

The *.P00 format is a flexible file format, that is able to support all common types of C64 files. As it has a magic constant at the head of the file it also enables the emulator to make sure that no invalid files are used. This makes it the recommended file format for single files.

typedef struct
 {
  Byte P00Magic[8];    /* $00 - $07 */
  Byte OrigFName[17];  /* $08 - $18 */
  Byte RecordSize;     /* $19 */
  Byte Data[n];        /* $1A - ... */
 } P00File;
P00Magic - Magic constant 'C64File' = {$43, $36, $34, $46, $69, $6C, $65, $00}
OrigFName - Original C64 filename (CBM Charset)
RecordSize- Record size for RELative files
Data - The actual data

This file format was first used by Wolfgang Lorenz in PC64. Note that the extension *.P00 is not fix. rather the 'P' stands for 'PRG' and can become an 'S' for 'SEQ', or 'R' for 'REL'. Furthermore the '00' can be used count to '01','02','03'... to resolve name conflicts caused by truncation a 16 character C64 filename to a 8+3 MS-DOS name. Of course this is not relevant on a Macintosh, it's just a hint if you should ever happen to run across a *.P01 file.

E.2 *.C64 Program Format

C64/PRG Icon

The *.C64 format is the exact format that the original Commodore 64 uses to store a programs to either disk or tape.

typedef struct
 {
  Byte LoadAddrLow, LoadAddrHigh; /* $00 - $01 */
  Byte PrgData[n];                /* $02 - ... */
 } C64File;
LoadAddr - The address in C64 RAM at which the program will get stored when loaded with secondary device number 1.
PrgData - The actual program.

This file format was introduced to the world of emulators by Miha Peternel in C64S. As can be seen from the above description this is a rather simple file format. Still it's use is not recommended as it does not provide the emulator with any possibility to make sure that only valid files are used.

E.3 *.X64 Floppy Disk Image Format

X64 Icon

The *.X64 format is flexible format for disk image files that supports a wide range of floppy disks. Note so, that the implementation of *.X64 in Power64 can only handle 1541 disks.

typedef struct
 {
  Byte X64Magic[4];   /* $00 - $03 */
  Byte Version[2];    /* $04 - $05 */
  Byte DiskType;      /* $06 */
  Byte TrackCnt;      /* $07 */
  Byte SecondSide;    /* $08 */
  Byte ErrorFlag;     /* $09 */
  Byte Reserved[22];  /* $0A - $1F */
  Byte DiskInfo[31];  /* $20 - $3E */
  Byte ConstZero;     /* $3F */
  Byte DiskImage[683 * 256];
 } X64File;
X64Magic - Magic constant 'C'1541/64 = {$43, $15, $41, $64}
Version - C1541 Version 2.6 = {$02, $06}
DiskType - Floppy disk type: 1541 = {$01} Other defined values: (not usable for Power64)
0..1540, 1..1541, 2..1542, 3..1551,
4..1570, 5..1571, 6..1572, 8..1581,
16..2031&4031, 17..2040&3040, 18..2041
24..4040,
32..8050, 33..8060, 34..8061,
48..SFD 1001, 49..8250, 50..8280
TrackCnt - Number of Tracks on the Disk (Side 0) = {35}
SecondSide- Is it a Double Sided Disks (0..No, 1..Yes) = {$00}
ErrorFlag - Flag for Error (precise meaning unclear) (unused)
Reserved - Must be $00
DiskInfo - Description of the Disk Image (in ASCII or ISO Latin/1)
ConstZero - Must be $00
DiskImage - 683 disk sectors of 256 bytes each.

For more information on DiskImage see also: *.D64.

The *.X64 disk image format was originally created by Teemu Rantanen for use in X64.
As *.X64 uses a well defined header the emulator is able to make sure that only valid files are used. This is a clear advantage over the (unfortunately more widespread) *.D64 format.

E.4 *.D64 Floppy Disk Image Format

D64 Icon

The *.D64 file format is a 1:1 copy of all sectors as they appear on a floppy disk. On a C1541 formatted disk (and thus also in a *.D64 file) each sector consists of 256 Byte. In order to be able to deal with bad sectors the *.D64 format optionally features an additional error byte per sector. A disk that was formatted by using the standard C1541 command NEW will have 35 tracks containing a total of 683 sectors. With the use of special software it is possible to format and write disks with up to 40 (or rarely even 42) tracks. While the extra tracks are not within the original specification such disks are readable with most C1541 drives (Note: Reading does not require special software.).

Currently there are four different types of *.D64 formats:

Note that tracks at the outside edge of the disk contain more sectors than those near to the disks center (Zone Bit Recording). The number of sectors on each track is:
Tracks 1..17 - 21 Sectors
Tracks 18..24 - 19 Sectors
Tracks 25..30 - 18 Sectors
Tracks 31..35 - 17 Sectors
Tracks 36..42 - 17 Sectors (non standard!)

The Block Allocation Map (BAM) is stored on track 18 - sector 0; the directory starts at track 18 - sector 1.

For storage in a *.D64 image the sectors are arranged in the following way:

  Track  1 - Sector  0: Offset   0 * 256
  Track  1 - Sector  1: Offset   1 * 256
                .....
  Track  1 - Sector 20: Offset  20 * 256
  Track  2 - Sector  0: Offset  21 * 256
  Track  2 - Sector  1: Offset  22 * 256
                .....
  Track  2 - Sector 20: Offset  41 * 256
  Track  3 - Sector  0: Offset  42 * 256
                .....
  Track 18 - Sector  0: Offset 357 * 256
  Track 18 - Sector  1: Offset 358 * 256
                .....
  Track 35 - Sector  0: Offset 666 * 256
  Track 35 - Sector  1: Offset 667 * 256
                .....
  Track 35 - Sector 16: Offset 682 * 256

Note that tracks are counted starting with track 1, while sector numbers start with 0.

typedef struct
 {
  Byte DiskImage[SektorCnt][256];
  Byte ErrorInfo[SektorCnt]; /* Optional */
 } D64File;
DiskImage- 256 Bytes per Sector
ErrorInfo- 1 Byte per Sector

Note that the error information for all sectors is concentrated at the end of the file. There is no interleaving of disk image data and error information on a per sector basis.
The meaning of the ErrorInfo is given in the following table:

    Code  Error  Type   1541 error description
    ----  -----  ----   ------------------------------
     01    00    N/A    No error, Sektor ok.
     02    20    Read   Header block not found
     03    21    Seek   No sync character
     04    22    Read   Data block not present
     05    23    Read   Checksum error in data block
     06    24    Write  Write verify (on format)
     07    25    Write  Write verify error
     08    26    Write  Write protect on
     09    27    Seek   Checksum error in header block
     0A    28    Write  Write error
     0B    29    Seek   Disk ID mismatch
     0F    74    Read   Disk Not Ready (no device 1)

E.5 ZipCode Disk Format (1!*, 2!*, etc.)

ZipCode Icon

This is a compressed version of a D64 file that is frequently found on C64 sites that have already been around for a long time (and have served C64 owners before emulation became common). A single D64 file is split into 4 (35 tracks) or 5 (40 tracks) segments. Each part is then packed using the simple (and poorly compressing) Run Length Encoding compression method.

The primary reason for the existence of this file format is that every segment of such a ZipCode File is less than 44 KByte in size, and can thus easily be handled by a real C64 and its 1541 disk drive (say after transmission via modem from a BBS). Furthermore packing and unpacking are very fast on a C64.

Modern computers, like a Power Macintosh, that are used to run C64 emulators have no problem handling files 171KByte in size (like D64 files), so they do not benefit from splitting one 'large' file in several smaller. In fact, due to the large allocations units of modern hard disks and CD-ROMs the space wasted by 4 half filled last blocks of the 4 files is likely to exceeds the savings from compression for reasonable well filled floppy disks. Furthermore modern compression formats like Zip, GZ, LHA or SIT offer much better compression that the RLE used in the ZipCode.

While ZipCode is not the best choice of a file format for an emulator it is still a good format on a real C64 and many sites offer their files in ZipCode format to support the faithful C64-addicts that stick to the real hardware.

Note that there are also two other Zip formats for the C64, that are not supported by Power64. They use the filename-prefixes 1!!, 2!!, 3!!… and A!, B!, C!… respectively. They are described in the File Format collection of Peter Schepers the author of 64COPY (schepers@dcs1.uwaterloo.ca), but I have never seen files in either format anywhere on the web.

The contents of the floppy disk is divided onto the 4 or 5 segments according to the following table:

   FileName  Track Range  Block Count
   --------  -----------  -----------
   1!xxxxxx     1 - 8     168 Sectors
   2!xxxxxx     9 - 16    168 Sectors
   3!xxxxxx    17 - 25    172 Sectors
   4!xxxxxx    26 - 35    175 Sectors
   5!xxxxxx    36 - 40     85 Sectors (only for 40 track disk images)

All segments have a similar structure based on compressed disk sectors. Note that the first segments header is slightly different from the rest.

typedef struct
 {
  Byte LoadAddr[2];     /* Konst. $03FE = { 0xFE, 0x03}; */
  Byte FloppyID[2];
  ZipSektor Image[168];
 } ZipSegment_1!;
typedef struct
 {
  Byte LoadAddr[2];     /* Konst. $0400 = { 0x00, 0x04}; */
  ZipSektor Image[...];
 } ZipSegment_n!;       /* n = {2, 3, 4, 5} */

Compression takes place on a sector per sector basis. For each sector there is the following structure:

typedef struct
 {
  Byte Track;     /* Track Number and Compression Mode */
  Byte Sector;    /* Sector Number */
  union {
    { Byte NoCompression[256]; } Mode00;
    { Byte SingleByte; } Mode01;
    { Byte Length;
      Byte RepeatChar;
      Byte RLEData[Length]; } Mode10;
   } Data;
 } ZipSector;
Track - Gives the track number of the compressed sector in Bit 5 to Bit 0. Bits 7&6 give the compression mode.
Sector- Gives the sector number of the compressed sector.
Data - The meaning of Data depends on the compression mode.

Compression Modes:

00 - No compression; The sector is stored in full. The 256 Data Bytes give the sectors contents.
01 - The entire sector is filled with a single Byte. A single Data Byte will be repeated 256 times to fill the sector.
10 - Run Length Encoded Sector - Length gives the number of Bytes of compressed RLEData. RepeatChar is a Byte that does not appear in the original sector and that is now used as a marker for runlength encoded pieces of data. RLEData different from RepeatChar is just plain, uncompressed data, that can be directly copied. After RepeatChar is encountered, the next Byte of RLEData gives the Length of the Run and a third Byte denotes the Byte to be repeated.
11 -Unused

The sectors of a track are NOT stored in a linear fashion (1, 2, 3, 4…). To make packing and unpacking much faster on a real 1541 interleaving is used. The proper sequence of sectors depends on the number of sectors in a track and can be seen below.

Track  1-17: 0 11 1 12 2 13 3 14 4 15 5 16 6 17 7 18 8 19 9 20 10
Track 18-24: 0 10 1 11 2 12 3 13 4 14 5 15 6 16 7 17 8 18 9
Track 25-30: 0  9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8 17
Track 31-40: 0  9 1 10 2 11 3 12 4 13 5 14 6 15 7 16 8

Example for the first few sectors of the first segment (1!*):

$0000: 0xFE, 0x03,       /* Load Addr. Const $03FE */
$0002: 0x36, 0x34,       /* Floppy ID */
$0004: 0x41, 0x00, 0x00, /* Track 1, Sector  0 filled with 0x00 */
$0007: 0x41, 0x0B, 0x00, /* Track 1, Sector 11 filled with 0x00 */
$000B: 0x41, 0x01, 0x00, /* Track 1, Sector  1 filled with 0x00 */
$0007: 0x01, 0x0C,       /* Track 1, Sector 12 uncompressed Data */
... 256 Byte Data ...
$0109: 0x81, 0x02,       /* Track 1 ,Sector 2 RLE Encoded */
       0x12, 0xEA,       /* 0x12(18) Byte, RepeatChar: 0xEA */
       0x45, 0x22, 0x34, 0x08,/* Plain Data */
       0xEA, 0xD0, 0x77,      /* 0x77 repeated 0xD0 times */
       0x12, 0xFF, 0x00, 0x00, 0x32, 0x11 /* Plain Data */
       0xEA, 0x24, 0x55,      /* 0x55 repeated 0x24 times */
       0xEE, 0x98,            /* Plain Data */
$011F: 0x41, 0x0D, 0x11  /* Track 1, Sector 13 filled with 0x11 */
$0122: ...

E.6 *.D71 Floppy Disk Image Format

D71 Icon

The *.D71 file format is a 1:1 copy of all sectors as they appear on a 1571 floppy disk. On a C1571 formatted disk (and thus also in a *.D71 file) each sector consists of 256 Byte. In order to be able to deal with bad sectors the *.D71 format optionally features an additional error byte per sector.

The 1571 disk drive uses double sided 5.25" disks that contain 35 Tracks per side (making a total of 70 tracks). Tracks at the outside edge of the disk contain more sectors than those near to the disks center (Zone Bit Recording). The number of sectors on each track is:

      Tracks  1..17 - 21 Sectors
      Tracks 18..24 - 19 Sectors
      Tracks 25..30 - 18 Sectors
      Tracks 31..35 - 17 Sectors
      Tracks 36..52 - 21 Sectors (corresponding to Tracks  1..17 - second side)
      Tracks 53..59 - 19 Sectors (corresponding to Tracks 18..24 - second side)
      Tracks 60..65 - 18 Sectors (corresponding to Tracks 25..30 - second side)
      Tracks 66..70 - 17 Sectors (corresponding to Tracks 31..35 - second side)

Comparing this to the Layout of a *.D64 disk image as described in Appendix E.4 it is easy to see that a *.D71 disk image simply consists of two *.D64 images glued together.

The Block Allocation Map (BAM) is stored in Track 18 - Sector 0 and Track 53 - Sector 0. The Directory starts at Track 18 - Sector 1.

For storage in a *.D71 image the sectors are arranged in the following way:

  Track  1 - Sector  0: Offset   0 * 256
  Track  1 - Sector  1: Offset   1 * 256
                .....
  Track  1 - Sector 20: Offset  20 * 256
  Track  2 - Sector  0: Offset  21 * 256
  Track  2 - Sector  1: Offset  22 * 256
                .....
  Track  2 - Sector 20: Offset  41 * 256
  Track  3 - Sector  0: Offset  42 * 256
                .....
  Track 18 - Sector  0: Offset 357 * 256
  Track 18 - Sector  1: Offset 358 * 256
                .....
  Track 35 - Sector  0: Offset 666 * 256
  Track 35 - Sector  1: Offset 667 * 256
                .....
  Track 35 - Sector 16: Offset 682 * 256
                .....
  Track 36 - Sector  0: Offset 683 * 256
  Track 36 - Sector  1: Offset 684 * 256
                .....
  Track 36 - Sector 20: Offset 703 * 256
                .....
  Track 70 - Sector  0: Offset 1349 * 256
  Track 70 - Sector  1: Offset 1350 * 256
                .....
  Track 70 - Sector 16: Offset 1365 * 256

Note that tracks are counted starting with track 1, while sector numbers start with 0.

E.7 *.D81 Floppy Disk Image Format

D81 Icon

The *.D81 file format is a 1:1 copy of all sectors as they appear on a 1581 floppy disk. On a C1581 formatted disk (and thus also in a *.D81 file) each sector consists of 256 Byte. In order to be able to deal with bad sectors the *.D81 format optionally features an additional error byte per sector.

The 1581 disk drive uses double sided 3.5" disks that contain 40 Tracks per side (making a total of 80 tracks). All even numbered tracks are located on one side of the disk, all odd numbered tracks on the other. Thus track 40 is located in the middle of the disk. Each track contains 40 sectors; Thus a complete 1581 disk image contains 80*40 = 3200 sectors.

The floppy master block is located at Track 40 - Sector 0. It is followed by the Block Allocation Map (BAM) at Track 40 - Sector 1 and Sector 2 and the Directory starts at Track 40 - Sector 3.

Since all tracks are the same size the offset of a sector in a *.D81 disk can be computed with a simple formula:
Offset = (40 * (Track -1) + Sector) * 256

Note that tracks are counted starting with track 1, while sector numbers start with 0.

E.8 *.Lynx File Format

Lynx Icon

The Lynx (or LNX or Ultimate Lynx) file format was developed by Will Corley for use on the C64(!). It is designed around blocks of 254 Bytes.This corresponds to the 256 Bytes of a 1541 disk sector minus the 2 Bytes that contain the next track/sector information and makes it easy to copy files between 1541 disks and Lynx files.
Unfortunately the Lynx header is written in a format that is somewhat harder to handle than that of a T64 or D64 file, and thus the format has received little attention by emulator authors.

typedef struct
 {
  Byte Directory[DirSectorCnt * 254];
  Byte Data[DataSectorCnt * 254];
 } LynxFile;

The Directory starts out with a small BASIC program which, when loaded and run, displays the message "Use LYNX to dissolve this file". The actual message and size of the program can change. Usually, its 94 bytes long, from $0000 to $005D. Note that some emulators depend on the exact size of 94 Bytes and also require that the text 'LYNX' can be found at offset $003C to $003F.

$0000: 0x01, 0x08, 0x5B, 0x08, 0x0A, 0x00, 0x97, 0x35, /* .......5 */
$0008: 0x33, 0x32, 0x38, 0x30, 0x2C, 0x30, 0x3A, 0x97, /* 3280,0:. */
$0010: 0x35, 0x33, 0x32, 0x38, 0x31, 0x2C, 0x30, 0x3A, /* 53281,0: */
$0018: 0x97, 0x36, 0x34, 0x36, 0x2C, 0xC2, 0x28, 0x31, /* .646,.(1 */
$0020: 0x36, 0x32, 0x29, 0x3A, 0x99, 0x22, 0x93, 0x11, /* 62):.".. */
$0028: 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22, /* ......." */
$0030: 0x3A, 0x99, 0x22, 0x20, 0x20, 0x20, 0x20, 0x20, /* :."      */
$0038: 0x55, 0x53, 0x45, 0x20, 0x4C, 0x59, 0x4E, 0x58, /* USE LYNX */
$0040: 0x20, 0x54, 0x4F, 0x20, 0x44, 0x49, 0x53, 0x53, /*  TO DISS */
$0048: 0x4F, 0x4C, 0x56, 0x45, 0x20, 0x54, 0x48, 0x49, /* OLVE THI */
$0050: 0x53, 0x20, 0x46, 0x49, 0x4C, 0x45, 0x22, 0x3A, /* S FILE": */
$0058: 0x89, 0x31, 0x30, 0x00, 0x00, 0x00,             /* .10...   */

This is equivalent to the following BASIC program:

   10 POKE53280,0:POKE53281,0:POKE646,PEEK(162):
        PRINT"<CLS><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN><DOWN>":
        PRINT"     USE LYNX TO DISSOLVE THIS FILE":
        GOTO10

Following this is a <CR> and the number of blocks of the directory in ASCII with spaces on both sides. For a directory with just 1 block that would be:

$005E:  0x0D, 0x20, 0x31, 0x20, 0x20                    /* . 1     */

After this there is the "signature" of the archive, an CBM lower case (ASCII for the most part) text ending in <CR> that describes the Lynx archive. Normally this signature will contain the string 'LYNX'.
Power64 uses:

$0063: 0x2A, 0x4C, 0x59, 0x4E, 0x58, 0x20, 0x41, 0x52, /* *LYNX AR */
$006B: 0x43, 0x48, 0x49, 0x56, 0x45, 0x20, 0x42, 0x59, /* CHIVE BY */
$0073: 0x20, 0x50, 0x4F, 0x57, 0x45, 0x52, 0x36, 0x34  /*  POWER64 */
$007B: 0x0D

This is followed by the number of files in the Lynx archive, as before this is given as a ASCII number surrounded by spaces and delimited by <CR>.
For a directory with 3 files that would be:

$007C: 0x20, 0x33, 0x20, 0x0D                          /*  3 .     */

Following these headers is the actual directory. Each file is described by its filename (in PETASCII, often padded to 16 characters by shifted-spaces), followed by the size of the file (plus 2 bytes for the address) in blocks of 254 bytes, the file type (P, S, R, U), the size in bytes of the last block. If the file type is Relative, this entry is the Record size, and the next entry is the last block size. If the file type is not Relative, the next entry will be the next filename. Relative files are not supported by Power64. Every entry is terminated by a <CR>, numbers are surrounded by spaces. For example this could be:

File 1: "Block Out", PRG, 2+2519 Byte (2521 = (10-1) * 254 + 235)
$0080: 0x42, 0x4C, 0x4F, 0x43, 0x4B, 0x20, 0x4F, 0x55, /* BLOCK OU */
$0088: 0x54, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* T        */
$0090: 0x0D, 0x20, 0x31, 0x30, 0x20, 0x0D, 0x50, 0x0D, /* . 10 .P. */
$0098: 0x20, 0x32, 0x33, 0x35, 0x20, 0x0D,             /*  235 .   */

File 2: "Serpentine", PRG, 2+8703 Byte (8705 = (35-1) * 254 + 69)
$009E: 0x53, 0x45, 0x42, 0x4C, 0x45, 0x4E, 0x54, 0x49, /* SERPENTI */
$00A6: 0x4E, 0x45, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* NE       */
$00AE: 0x0D, 0x20, 0x33, 0x35, 0x20, 0x0D, 0x50, 0x0D, /* . 35 .P. */
$00B6: 0x20, 0x36, 0x39, 0x20, 0x0D,                   /*  69 .    */

File 3: "Quadromania", PRG, 2+7056 Byte (7058 = (28-1) * 254 + 200)
$00BB: 0x51, 0x55, 0x41, 0x44, 0x52, 0x4F, 0x4D, 0x41, /* QUADROMA */
$00C3: 0x4E, 0x49, 0x41, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, /* NIA      */
$00CB: 0x0D, 0x20, 0x32, 0x38, 0x20, 0x0D, 0x50, 0x0D, /* . 28 .P. */
$00D3: 0x20, 0x32, 0x30, 0x30, 0x20, 0x0D,             /*  200 .   */

The actual files follow the directory. Each file consists of 2 bytes load address plus the actual data.

Note that the directory and every files size is a multiple of 254 bytes. If the actual information requires less space, then there is a wasted gap, that is filled with 0x00 in Power64 (other implementations vary).

In the above example the directory consists of just one block, and thus Block Out starts at offset 1 * 254. The second file (Serpentine) starts at offset (1+10) * 254 and Quadromania begins at (1+10+35) * 254.

$00FE: 0x01, 0x08, 0x26, 0x08, 0xC1, 0x07, 0x9E, 0x32, /* ..&.i..2 */
$0106: 0x30, 0x38, 0x38, 0x3A, 0x12, 0x42, 0x4C, 0x4F, /* 088:.BLO */
$011E: 0x43, 0x4B, 0xAB, 0x4F, 0x55, 0x54, ...         /* CK-OUT.. */
... /* (1+10) * 254 = $0AEA */
$0AEA: 0x01, 0x08, 0x26, 0x08, 0xC1, 0x07, 0x9E, 0x32, /* ..&.¡..2 */
$0AF2: 0x30, 0x38, 0x38, 0x3A, 0x12, 0x53, 0x45, 0x52, /* 088:.SER */
$0AFA: 0x50, 0x45, 0x4E, 0x54, 0x49, 0x4E, 0x45,...    /* PENTINE  */
...

E.9 *.T64 Tape Image Format

T64 Icon

The *.T64 file format is a very well designed (so in former times poorly documented) format, developed by Miha Peternel for C64S. Like a disk image file it is able to contain many logical files; a great help when keeping lots of files organized. On the other hand there is very little organizational overhead, both in terms of lost space on the host file system and in terms loading time for the emulator. As a *.T64 file contains a magic header it is furthermore possible for the emulator to prevent invalid files from being used.

typedef struct
 {
  TapeHeader Header;          /*  $0000  -  $003F  */
  TapeEntry  Entry[MaxFiles]; /*  $0040  - ($03FF) */
  Byte       Data[n];         /* ($0400) -   ...   */
 } T64File;
Header- General information; containing a magic string to identify a *.T64 file, the number of files on the tape etc... (see below for details)
Entry - Tape Directory; A list of all files stored on the tape. The value MaxFiles is defined in Header, but is usually 30. (see below for details)
Data - The actual data for all the files.
typedef struct
 {
  Byte TapeDescr[32]; /* $00 - $1F */
  Byte Version[2];    /* $20 - $21 */
  Byte MaxFiles[2];   /* $22 - $23 */
  Byte CurrFiles[2];  /* $24 - $25 */
  Byte Reserved[2];   /* $26 - $27 */
  Byte UserDescr[24]; /* $28 - $3F */
 } TapeHeader;
TapeDescr - Magic string: "C64 tape image file"; padded with $00
Note this magic string need not be verbatim. Search for substrings "C64" and "tape" to identify a *.T64 file. Warning: TAP tape image files (see below) use "C64-TAPE-RAW" for a magic string. This too contains "C64" and "tape"!
Version - Tape Version 1.0 = {$00, $01}
MaxFiles - Number of places in the tape directory. There are some some emulators that rely on fact that there are exactly 30 places in the directory. Thus all *.T64 files created by Power64 will supply this value. = {$1E, $00}
CurrFiles - Number of Files currently stored on the tape. This number can never exceed MaxFiles. Note that some emulators can only read the first file stored in a *.T64 image.
Reserved - Must be $00.
UsedDescr - User Description of the Tape. (CBM character set)
typedef struct
 {
  Byte EntryUsed;     /* $00 */
  Byte FileType;      /* $01 */
  Byte StartAddr[2];  /* $02 - $03 */
  Byte EndAddr[2];    /* $04 - $05 */
  Byte ReservedA[2];  /* $06 - $07 */
  Byte TapePos[4];    /* $08 - $0B */
  Byte ReservedB[4];  /* $0C - $0F */
  Byte FileName[16];  /* $10 - $1F */
 } TapeEntry;
EntryUsed - Is this entry in the *.T64 directory used? (0..No, 1..Yes)
FileType - There is very little documentation on this field.
Power64 adopts the notion of Star Commander that corresponds to the file type identifiers used on floppy disk:
$82..Relocatable Prg, $81..Data File
Earlier versions of Power64 implemented the following meaning:
0..Relocatable Prg., 1..Force Load Prg., 2..Data File
To prevent error messages all other values are also treated a relocatable programs.
StartAddr - Start of the destination range in C64 memory. (Low/High)
EndAddr - End of the destination range in C64 memory. (Low/High)
ReservedA- Must be $00
TapePos - Offset from the start of the tape file to the logical files data
ReservedB - Must be $00
FileName - filename (CBM character set)

There was very little official documentation on the *.T64 file format. What is known was generally learned by examining existing *.T64 files. Sometimes that was not done with sufficient scrutiny. Thus there are many *.T64 files on the internet that do not follow the described format. It is, for example quite common, that the difference between EndAddr and StartAddr of a file is not consistent with the file size of the *.T64 file. Power64 attempts to automatically fix such inconsistencies.

As stated above there are some emulators that rely on the fact that there are always exactly 30 directory entries, while at the same time they will always load the first file. Please keep this in mind when making Commodore 64 files available to a general public that might use a wide range of emulators.

All multi-byte values are stored in the Little-Endian Format (Low/High) that is generally used by the C64.

E.10 *.TAP (*.RAW) Tape Image Format

TAP Icon

The *.TAP (or *.RAW) tape image format, offers a very precise image of a C64 data tape. Due to the high level of precision and detail most of the tricks that were possible with the C64 tape drive can be emulated. Even fastloaders offer no problem. The drawbacks of the *.TAP format are their huge memory footprint (at least 8 times, usually 10-12 times, at worst up to 45 times the file size of a T64 image containing the same data) and the long, slow loading process (using the original C64 ROM routines about 110 bytes/second can be loaded at peak rate, but due to redundancy for error correction the net throughput is only 50 bytes/second; using special fastloaders about 200-500 bytes/second can be achieved).

The information on a C64 tape is encoded in single pulses. The time delay between two sucessive pulses carries the actual information. Exactly these delays are stored in a *.TAP file.

typedef struct
 {
  Byte TAPMagic[12];    /* $00 - $0B */
  Byte Version;         /* $0C */
  Byte Reserved[3];     /* $0D - $0F */
  Byte Size[4];         /* $10 - $13 */
  Byte Data[Size];      /* $14 - ... */
 } TAPFile;
TAPMagic - Magic Constant "C64-TAPE-RAW" = {$43, $36, $34, $2D, $54, $41, $50, $45, $2D, $52, $41, $57}
Version - $00 or $01: see Data
Reserved - Always $00
Size - Size of the *.TAP file in Little-Endian format, excluding the header.
Data - The time delay between two sucessive pulses on the tape.
Values $01-$FF directly represent the timespan between two pulses, measured in multiples of 8 CPU cycles. (i.e. a value of $2E means that there are 8*$2E = $170 CPU Cycles between two pulses).
The Value $00 has a special meaning. In version $00 of the TAP format, a value of $00 simply means any time longer that 8*$FF CPU cycles. In Version $01 the three bytes following the $00 give (in Little-Endian format) the number of CPU Cycles (without a multiplyer of 8) that will pass by before the next pulse comes along.

The original C64 ROM routines use a rather inefficient, but fairly well error correcting algorithm to encode data. Each byte of user data is written using a start bit, 8 user bits (LSB first, MSB last) and a parity bit (making the number of '1' bits odd). The start bit consists of a very long interval followed by a long interval. '0' bits are encoded by a short interval followed by a long interval and '1' bits are encoded by a long interval followed by a short interval. Short Intervals are about 0x164 (= 0x2C*8) CPU cycles long. Long intervals last about 0x1FC (= 0x3F*8) cycles, and very long intervals are about 0x2AC (= 0x55*8) cycles long. The actual timing values can vary significantly and rapidly, since the tape drive was never optimised for even tape movement. The ROM routines attempt to compensate for this by permanently adjusting a tape speed parameter. A very long interval followed by a short interval indicates the end of a block of data.
Efficiency estimate: For one byte of user data the tape requires (0x2AC + 0x1FC) + 9*(0x1FC+0x164) = 0x2308 = 8968 CPU Cycles, giving a performance of about 110 byte/seconds. Since every block is stored twice to allow for error correction and there are unused gaps between the copies only half of that performance (about 50 byte/second) is available for actual use.
On the other hand storing a user byte requires 2+9*2 = 20 intervals each encoded in a byte. To allow for error compensation all data is stored twice, making it a total of 40 byte of TAP file size of each byte of user information. In addition to that more TAP files contain generous gaps between the used areas, increasing file sizes even more.

Various fastloaders store their data in different, propriatary formats. In general they gain efficiency using just a single interval for each user bit, reducing the size of each interval, dropping startbits and parity and storing the complete data only once. While this drastically reduces the possibility of error detection and correction it does improve loading speeds to 200-500 byte/second. At the same time the size of the TAP file is reduced to about 8 times the size of the user data contained.

The Commodore 64 Tape Info Central available at http://www.geocities.com/SiliconValley/Platform/8224/c64tape/index.html provides detailed information on lots of tape formats.

If you are interested in transfering data from physical tapes to your Mac, you should read the Commodore 64 Tape Transfer FAQ available at: http://www.geocities.com/SiliconValley/Platform/8224/c64tape/faq.html. You will also find links too (DOS/Windows-, unfortunatly not Mac-) tools that will convert WAV-files to TAPs or T64 tape images.

E.11 *.CRT ROM Modul Format

Cartridge Icon

The *.CRT ROM Cartridge Format is quite flexible and makes it possible to encode a wide variety of different types of cartridges efficiently. Furthermore it is easy to read, has an easily recognizable Magic String and requires only little administrative overhead.

Basically a CRT file consists of a Cartridge Header, describing to cartridge in general, followed by one or more chip packets that each describe the contents of one ROM chip.

All Multibyte-Values are stored in Big-Endian Format.

typedef struct
 {
  Byte CartrigeMagic[16];  /* $00 - $0F */
  Long HeaderSize;         /* $10 - $13 */
  Word Version;            /* $14 - $15 */
  Word HardwareType;       /* $16 - $17 */
  Byte ExROM_Line          /* $18 */
  Byte Game_Line           /* $19 */
  Byte Unused[6];          /* $1A - $1F */
  Byte CartridgeName[32];  /* $20 - $3F */
 } CartridgeHeader;
CartridgeMagic- Magic Constant 'C64 CARTRIDGE ' =
{$43, $36, $34, $20, $43, $41, $52, $54,
$52, $49, $44, $47, $45, $20, $20, $20}
HeaderSize- sizeof(CartridgeHeader) = 64 = {$00, $00, $00, $40}
Version- CRT Version 1.0 = {$01, $00}
HardwareType- There is a wide variety of different cartridge types that are all summarized under the CRT format. All cartridges contain some ROM (as planned by the designers of the C64), but in addition to that some cartridges also contain RAM and/or logic circuits. Usually these designs were used for just a single project each, so that the effort for their emulation is hardly worth the trouble. Power64 supports cartridge types 0, 4, 6, 7, 8, 11 and 12 covering the entire range of games.
0 - Normal cartridge
1 - Action Replay
2 - KCS Power Cartridge
3 - Final Cartridge III
4 - Simons Basic
5 - Ocean type 1 (256 and 128 KByte)
6 - Expert Cartridge
7 - Fun Play
8 - Super Games
9 - Atomic Power
10 - Epyx Fastload
11 - Westermann
12 - Rex
13 - Final Cartridge I
14 - Magic Formel
ExROM_Line- Signal of the ExROM Line (for Memory configuration)
Game_Line- Signal of the Game Line (for Memory configuration)
CartridgeName- Human-Readable Name of the Cartridge (Null-terminated String)
typedef struct {
  Byte ChipMagic[4];  /* 0x00 - 0x03 */
  Long PacketLength;  /* 0x04 - 0x07 */
  Word ChipType;      /* 0x08 - 0x09 */
  Word Bank;          /* 0x0A - 0x0B */
  Word Address;       /* 0x0C - 0x0D */
  Word Length;        /* 0x0E - 0x0F */
  Byte Data[Length];  /* 0x10 - ...  */
 } ChipPacket;
ChipMagic- Magic Constant 'CHIP' = {$43, $48, $49, $50}
PacketLength- Size of the ChipPacket (= Length + 0x10)
ChipType- 0..ROM, 1..RAM (No Data Field), 2..EEPROM
Bank- for Multi-Bank Cartridges (for single bank cartridges: 0)
Address- 'Load'-Address of the ROM chip (usually $8000 or $A000)
Length- Size of the ROM chip (usually $1000 or $2000 or $4000)
Data- Contents of the ROM

Example: Simons Basic Cartridge

$0000: 0x43, 0x36, 0x34, 0x20, 0x43, 0x41, 0x52, 0x54, /* C64 CART */
$0008: 0x52, 0x49, 0x44, 0x47, 0x45, 0x20, 0x20, 0x20, /* RIDGE    */
$0010: 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x04, /* ...@.... */
$0018: 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */
$0020: 0x53, 0x69, 0x6D, 0x6F, 0x6E, 0x27, 0x73, 0x20, /* Simon's  */
$0028: 0x42, 0x61, 0x73, 0x69, 0x63, 0x00, 0x00, 0x04, /* Basic... */
$0030: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */
$0038: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */

$0040: 0x43, 0x48, 0x49, 0x50, 0x00, 0x00, 0x20, 0x10, /* CHIP.. . */
$0048: 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00  /* ...... . */
$0050: .... $2000 Byte for Bank 0: $8000 - $9FFF ....

$2050: 0x43, 0x48, 0x49, 0x50, 0x00, 0x00, 0x20, 0x10, /* CHIP.. . */
$2058: 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x20, 0x00  /* ...... . */
$2060: .... $2000 Byte for Bank 0: $A000 - $BFFF ....

$4060: End of File

E.12 RAM Snapshot File

RAM Snapshot Icon

The format for RAM Snapshots was adapted from a suggestion made by Jouko Valta for the Vice-Emulator on Unix systems. The original description, that explains the meaning of the individual header fields in more detail, can be found at: http://stekt.oulu.fi:/~jopi/x64/RAM-format

typedef struct
 {
  Byte MagicCode[6];     /* $00 - $05 */
  Byte ReservedA[2];     /* $06 - $07 */
  Byte Version[2];       /* $08 - $09 */
  Byte EmulatorID;       /* $0A */
  Byte SnapType;         /* $0B */
  Byte RAMStart[2];      /* $0C - $0D */
  Byte RAMSizeKB[2];     /* $0E - $0F */
  Byte REUSizeKB[2];     /* $10 - $11 */
  Byte ReservedB[14];    /* $12 - $1F */
  Byte Config[6];        /* $20 - $25 */
  Byte IORAM;            /* $26 */
  Byte CPURestore;       /* $27 */
  Byte PCHigh, PCLow;    /* $28 - $29 */
  Byte RegA, RegX, RegY; /* $2A - 2C */
  Byte RegFlags, RegSP;  /* $2D - $2E */
  Byte IOPort;           /* $2F */
  Byte Z80_CPU[16];      /* $30 - $3F */
  Byte REC[16];          /* $40 - $4F */
  Byte OSVersion;        /* $50 */
  Byte OSPatch;          /* $51 */
  Byte PatchID[2];       /* $52 - $53 */
  Byte ReservedC[44];    /* $54 - $7F */
  Byte Custom[128];      /* $80 - $FF */
  Byte RAM[65536];
  Byte IOArea[4096];
  Byte REU[REUSize];
  FloppyState Floppy1541[FloppyCnt];
 } Snapshot;

Note: All multi-byte values are stored in Big-Endian Format (High/Low) rather than in the Little-Endian Format (Low/High) used by the C64.

MagicCode - Magic Constant 'CBM'64 = {$43, $42, $4D, $64, $00, $00}
ReservedA - Should be {$00, $00}
Version - Major and Minor Version Number 1.0 = {$01, $00}
EmulatorID- Power64 uses '/' = $2F
SnapType - RAM Snapshot = $00
RAMStart - Offset to the RAM Image in the File. The header currently uses 256 Bytes. Thus RAMStart = {$01, $00}.
RAMSizeKB - Size of the RAM Image in KByte (not Byte!) = {$00, $40}
REUSizeKB - Size of the RAM Expansion Unit in KByte
ReservedB - Should be all $00
Config - Reserved for configuration bit masks - Currently all $00
IORAM - Bit 2/1: VDC RAM available (00..No, 01..16KB, 10..64KB, 11..32KB)
Bit 0: I/O data available (0..No, 1..Yes)
Value used by Power64: $01
CPURestore- CPU(s) available: (Bit 0: M6502, Bit 1: Z80, Bit 2: REC) = $01
PCHigh, PCLow- M6502 Program Counter
RegA, RegX, RegY, RegFlags, RegSP - M6502 Registers
IOPort - 6502 CPU I/O latch
Z80_CPU - Reserved for internal state of Z80 (not used)
REC - Reserved for internal state of REC (not used)
OSVersion - C64 Kernal ID byte (not used)
OSPatch - Kernal patch ID for extensions (fastloaders etc.) (not used)
PatchID - Patch version information (not used)
ReservedC - Kernal version information (not used)
Custom - Information on peripherals, external devices etc. (not used)
RAM - Contents of the RAM
IOArea - Contents of the I/O-Area (VIC, SID, ColorRAM, CIA1/2, REU)
Every device is stored only once, even so several shadows appear in the C64 address space. The gaps are filled with Power64 internal state information and lots of $00.
REU - The Contents of the REU RAM, if a RAM Expansion is present.
0, 128, 256 or 512 KByte - as specified by REUSizeKB.
Floppy1541- The current state of each attached, completely emulated Floppy 1541.
typedef struct
 {
  Byte MagicCode[6];     /* $00 - $05 ('CBM',$15, $41, $00) */
  Byte ReservedA[2];     /* $06 - $07 */
  Byte Version[2];       /* $08 - $09 */
  Byte EmulatorID;       /* $0A */
  Byte SnapType;         /* $0B */
  Byte RAMStart[2];      /* $0C - $0D */
  Byte RAMSizeKB[2];     /* $0E - $0F */
  Byte REUSizeKB[2];     /* $10 - $11 */
  Byte ReservedB[14];    /* $12 - $1F */
  Byte Config[6];        /* $20 - $25 */
  Byte IORAM;            /* $26 */
  Byte CPURestore;       /* $27 */
  Byte PCHigh, PCLow;    /* $28 - $29 */
  Byte RegA, RegX, RegY; /* $2A - 2C */
  Byte RegFlags, RegSP;  /* $2D - $2E */
  Byte ReservedC[81];    /* $2F - $7F */
  Byte Custom[128];      /* $80 - $FF */
  Byte RAM[2048];
  Byte IOArea[512];
 } FloppyState;

E.13 Commodore64 Audio File (reSID Sound File)

Sound Icon

The Commodore64 Audio File Format was developed by Roland Lieger for Power64 and reSID. It is based on a idea used by Richard Bannister for Frodo, but has been improved for flexibility and compactness of resulting files.

Note: All multi-byte values are stored in Big-Endian Format (High/Low) rather than in the Little-Endian Format (Low/High) used by the C64.

typedef struct
 {
  Byte MagicString[24];    /* $00 - $17 */
                           /* -> "Commodore64 Audio File\n\0" */
  Word Version;            /* $18 - $19 */
  Word HeaderSize;         /* $1A - $1B */
  Byte EmulatorID;         /* $1C */
  Byte Reserved;           /* $1D */
  Word EmulatorVersion;    /* $1E - $1F */
  Word PrivHeaderSize;     /* $20 - $21 */
 } GlobalHeader;
typedef struct
 {
  Long SystemClockSpeed;   /* $22 - $26 */
  Word ClockGrain;         /* $27 - $28 */
 } SystemHeader;
typedef struct
 {
  GlobalHeader GlobalHdr;
  SystemHeader SystemHdr;
  Byte         PrivateHdr[PrivHeaderSize];
  Byte         InitialSIDState[25];
  Byte         Reserved[7];
  Byte         SIDCommands[...];
  Byte         EOFMarker[3];
 } C64_Audio_File;
MagicString- "Commodore64 Audio File\n\0"
Version - Major and Minor Version Number 1.0 = {$01, $00}
HeaderSize - Size of SystemHeader (currently 6 Byte = {$00, $06}, additional fields may be added in future versions)
EmulatorID - Power64 uses '/' = $2F
EmulatorVersion- Major and Minor Version Number of the creating Emulator.
PrivHeaderSize - Size of private data stored by the emulator.
SystemClockSpeed- Speed of the system clock. 982800Hz for PAL, 1021800Hz for NTSC
ClockGrain- Granularity of the SID recording timer measured in system clock ticks. Usually one scanline (63 ticks for PAL, 65 ticks for NTSC) - This gives sufficiently good accuracy while keeping file sizes small. Do not use a granularity of 1 tick just because you think it is closer to the original. You will not notice an audible difference!
InitialSIDState- Contents of the 25 writable SID registers at the start of the recording.
SIDCommands- This is a sequence of 2 and 3 Byte commands. Each command consists of a delay (measured in clock grains) and usually a write operation to a SID register.
The first byte of a command indicates the duration of the Delay (Bits 7..5) and the choice of SID_Register to write to (Bits 4..0).
If the value of Delay is in the range 0..5, then this is the number of clock grains that the SID produces sound without change.
It Delay equals 6, then the next Byte contains an ExtDelay value, and there is a delay of 6+ExtDelay clock grains.
It Delay equals 7, then the next Byte contains an ExtDelay value, and there is a delay of 6+256+ExtDelay clock grains. ExtDelay must be less or equal 254 making the maximal delay equal to 516 clock grains (almost 2 complete NTSC frames if one clock grain is chosen to be equal to one scanline). The ExtDelay value of 255 is reserved as a End-of-File marker.
If SID_Register is in the range 0x00..0x18, then the following Byte (possibly after the ExtDelay Byte), indicates the value to write to the SID register.
The SID_Register values of 0x19..0x1E are reserved and should not be used.
The SID_Register value 0x1F indicates that no write to a SID register is done. This is necessary if there is a delay of more than 516 clock grains between two SID writes.
EOFMarker- End of File Marker = {$FF, $FF, $FF}
Home First Prev Contents Next Last Top of Page

Source: http://www.infinite-loop.at/Power64/Documentation/Power64-ReadMe/AE-File_Formats.html
Power64 Homepage: http://www.infinite-loop.at and http://www.salto.at - EMail:
© Roland Lieger, Goethegasse 39, A-2340 Mödling, Austria - Europe
Last Changed: Feb. 29, 2008
Valid HTML 4.01!