|
|
|
This section describes all the file formats supported by Power20; Most of them were originally invented for Commodore 64 emulators. The following 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 VIC-20 game, then you do not need to worry about these details.
Power20 uses different icons for the various file format. Since Power20 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 |
The *.P00 format is a flexible file format, that is able to support all common types of VIC-20 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 VIC-20 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 VIC-20 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 |
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 VIC-20 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 *.ROM, *.CRT, *.x000, *.x0 Cartridges |
There are two cartridge file formats, that can only be distinguished by their file length.
If the file length is a multiple of 256 Bytes (usually a multiple of 1KByte), then the file contains a plain ROM image. The proper location for the memory must be determined from the filename and its extension (e.g. PacMan.A000 must be loaded at $A000).
Otherwise the file format is similar to a *.PRG file. Two Bytes load address plus ROM image (usually a multiple of 1 KByte). This format is definitely better than the above, as files can not be damaged, simply by being renamed (or truncated to a 8+3 MS-DOS filename).
E.4 *.X64 Floppy Disk Image Format |
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 Power20 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 Power20) 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.5 *.D64 Floppy Disk Image Format |
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 six 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.6 ZipCode Disk Format (1!*, 2!*, etc.) |
This is a compressed version of a D64 file that is frequently found on VIC-20 sites that have already been around for a long time (and have served VIC-20 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 VIC-20 and its 1541 disk drive (say after transmission via modem from a BBS). Furthermore packing and unpacking are very fast on a VIC-20.
Modern computers, like a Power Macintosh, that are used to run VIC-20 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 VIC-20 and many sites offer their files in ZipCode format to support the faithful VIC-20-addicts that stick to the real hardware.
Note that there are also two other Zip formats for the VIC-20, that are not supported by Power20. 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:
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.7 *.Lynx File Format |
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'.
Power20 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, 0x32, 0x30 /* POWER20 */ $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 Power20. 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 Power20 (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.8 *.T64 Tape Image Format |
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 Power20 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. Power20 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 Power20 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 VIC-20 memory. (Low/High) |
EndAddr | - End of the destination range in VIC-20 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. Power20 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 VIC-20.
E.9 *.TAP (*.RAW) Tape Image Format |
The *.TAP (or *.RAW) tape image format, offers a very precise image of a VIC-20 data tape. Due to the high level of precision and detail most of the tricks that were possible with the VIC-20 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 VIC-20 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 VIC-20 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 VIC-20 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.10 S20-RAM Snapshot File |
The S20-format for RAM Snapshots was adapted from a suggestion made by Arne Bockholdt for the Pfau Zeh on DOS systems. The original description, that explains the meaning of the individual header fields in detail, can be found at: http://www.classicgaming.com/pfauzeh/s20spec.htm.
S20 is currently the only RAM snapshot format supported by Power20. If you are seriously interested in VIC-20 RAM snapshots, please note that there is currently an attempt among various emulator authors to define a new, universal VIC-20 Snapshot format that (hopefully) will be supported by all emulators. This will NOT be S20!
Source: http://www.salto.at/Power20/Documentation/Power20-ReadMe/AE-File_Formats.html Power20 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 |