Emulator Formats Last updated October 1, 1996 --------------------------------------------------------------------------- There are always questions asked regarding the various formats which are commonly seen on either the emulators or the real C64. Most often the question entails conversion... "What do I do with xxx files?", "How do I make these work on xxx emulator?". This document is an attempt to explain their internal structure, what to do with them, and some of their respective strengths and weaknesses. Some of what is contained in this document has been compiled from various sources, and I have given the appropriate attributions at the end of this file. Some of the information may not be accurate as it may have been taken from other documents, and I have no first-hand experience with it. I will try to make it as thorough as possible, but if there is something wrong, please alert me so I can make the appropriate changes for future releases. So far, this document covers the following emulator types: * D64 (with a description of RELATIVE files), and some D64 variants. * X64 * T64 * T64 .FRZ (FRoZen Files, saved emulator sessions) * PC64 (P/S/U/Rxx) * PC64 .C64 (saved emulator sessions) as well as the following native C64 types: * ZipCode (4 files, diskpacked) * ZipCode (6 files, diskpacked) * ZipCode (? files, filepacked) * LNX * Binary * ARK * GEOS VLIR * LHA (-lh1- only) * SFX * SDA * ZIP * CKIT (Compression Kit) * CPK At the end of this document is a BINARY/HEX/Decimal conversion chart, useful if you don't know how to convert the number bases around. It might come in handy when working with and understanding the GCR conversion that the SixPack ZipCode format uses. I will also add that in order to work with some of the rare or unusual image types like LHA or ARK, I highly recommend the program Star Commander, by Joe Forster/STA. Contained with it are several smaller utilities for working with such files (StarArk, StarLHA, StarLynx, etc) Peter Schepers, University of Waterloo. August 24, 1996 Email: schepers@dcs1.uwaterloo.ca --------------------------------------------------------------------------- *** D64 First and foremost we have D64, which is basically a sector-for-sector copy of a 1540/1541 disk. There are several versions of these which I will cover shortly. The standard D64 is a 174848 byte file comprised of 256 byte blocks arranged in 35 tracks with a varying number of sectors per track for a total of 683 blocks (or sectors). Track counting starts at 1, not 0, and goes up to 35. Sector counting starts at 0, not 1, for the first sector, therefore a track with 21 sectors will go from 0 to 20. The original media (a 5.25" disk) has the tracks laid out as track 1 on the very outside of the disk to track 35 being on the inside of the disk. Commodore (in their infinite wisdom) varied the number of sectors/track to optimize available storage, resulting in the chart below. It shows the sectors/track for a standard D64. Since the outside diameter of a circle is the largest (versus closer to the center), the outside tracks have the largest amount of storage. Track Sectors/track # Blocks ----- ------------- -------- 1-17 21 357 18-24 19 133 25-30 18 108 31-35 17 85 --- 683 Track #Sect #Blocks in D64 Offset ----- ----- ---------- ---------- 1 21 0 $00000 2 21 21 $01500 3 21 42 $02A00 4 21 63 $03F00 5 21 84 $05400 6 21 105 $06900 7 21 126 $07E00 8 21 147 $09300 9 21 168 $0A800 10 21 189 $0BC00 11 21 210 $0E200 12 21 231 $0E700 13 21 252 $0FC00 14 21 273 $11100 15 21 294 $12600 16 21 315 $13B00 17 21 336 $15000 18 19 357 $16500 19 19 376 $17800 20 19 395 $18B00 21 19 414 $19E00 22 19 433 $1B100 23 19 452 $1C400 24 19 471 $1D700 25 18 490 $1EA00 26 18 508 $1FC00 27 18 526 $20E00 28 18 544 $22000 29 18 562 $23200 30 18 580 $24400 31 17 598 $25600 32 17 615 $26700 33 17 632 $27800 34 17 649 $28900 35 17 666 $29A00 The directory track is contained totally on track 18. Sectors 1-18 contain the entries and sector 0 contains the BAM and disk name/ID. Since the directory is only 18 sectors large (19 less one for the BAM), and each block can contain only 8 entries (32 bytes per entry), the maximum number of directory entries is 144. The first directory block is always 18,1, even though the t/s pointer at 18,0 (first two bytes) might point somewhere else. It then follows the same chain structure as a normal file. Each directory block has the following layout (18,1 partial): 00: 12 04 81 11 00 4E 41 4D 45 53 20 26 20 50 4F 53 <-notice the T/S link 10: 49 54 A0 A0 A0 00 00 00 00 00 00 00 00 00 15 00 <-to 18,4 ($12 $04) 20: 00 00 84 11 02 41 44 44 49 54 49 4F 4E 41 4C 20 <-and how its not here! 30: 49 4E 46 4F A0 11 0C FE 00 00 00 00 00 00 61 01 The first two bytes of the block ($12,$04) indicate the location of the next track/sector of the directory. If the track is set to $00, then it is the last block of the directory, and the sector will contain $FF, meaning the whole sector is allocated. Bytes: $00-1F: First directory entry 00: Track location of next directory block 01: Sector location of next directory block 02: File type $00 - Scratched (deleted file entry) 80-84 - Normal filetype (bit 8 on) 80 - DEL 81 - SEQ 82 - PRG 83 - USR 84 - REL 85-8F - Not used, but when used results in *wierd* filetypes 00-04 - Same, but "unclosed" (or '*PRG', except 00 - see above) C0-C4 - Same, but "locked" (or 'PRG<', bit 7 on) A0-A4 - Same, but only present during a 'save-@', "@0:" file replacement. 03: Starting track location of first block of file 04: Starting sector location of first block of file 05-14: 16 character filename (in PETASCII, padded with $A0) 15: Starting track location of side-sector blocks (REL file only) 16: Starting sector location of side-sector blocks (REL file only) 17: REL file record length (REL file only) 18-1D: Unused 1E-1F: File size in blocks, low-byte/high-byte order ($1E+$1F*256). The filesize in bytes is <= #blocks*254 20-3F: Second dir entry. From now on the first two bytes of each entry in this block should be $00, $00, as they are unused. 40-5F: Third dir entry 60-7F: Fourth dir entry 80-9F: Fifth dir entry A0-BF: Sixth dir entry C0-DF: Seventh dir entry E0-FF: Eighth dir entry The layout of the BAM block is a bit more complicated. 00: 12 01 41 00 12 FF F9 17 15 FF FF 1F 15 FF FF 1F 10: 15 FF FF 1F 12 FF F9 17 00 00 00 00 00 00 00 00 20: 00 00 00 00 0E FF 74 03 15 FF FF 1F 15 FF FF 1F 30: 0E 3F FC 11 07 E1 80 01 15 FF FF 1F 15 FF FF 1F 40: 15 FF FF 1F 15 FF FF 1F 0D C0 FF 07 13 FF FF 07 50: 13 FF FF 07 11 FF CF 07 13 FF FF 07 12 7F FF 07 60: 13 FF FF 07 0A 75 55 01 00 00 00 00 00 00 00 00 70: 00 00 00 00 00 00 00 00 01 08 00 00 03 02 48 00 80: 11 FF FF 01 11 FF FF 01 11 FF FF 01 11 FF FF 01 90: 53 48 41 52 45 57 41 52 45 20 31 20 20 A0 A0 A0 A0: A0 A0 56 54 A0 32 41 A0 A0 A0 A0 00 00 00 00 00 B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Bytes:$00-01: Track/Sector location of the first directory entry block (should be set to 18/1 but it doesn't matter) 02: Disk DOS version type $41=1541 03: Unused 04-8F: BAM entries for each track, in groups of four bytes per track, starting on track 1 (see below for more details) 90-9F: Disk Name (padded with $A0) A0-A1: Filled with $A0 A2-A3: Disk ID A4: Usually $A0 A5-A6: DOS type, usually "2A" A7-AA: Filled with $A0 AB-FF: Unused The BAM entries require a bit (no pun intended) more of a breakdown. Take the first entry at bytes $04-$07 ($12 $FF $F9 $17). The first byte ($12) is the number of free sectors on that track. Since we are looking at the track 1 entry, this means it only has 18 (decimal) free sectors. The next three bytes represent the bitmap of which sectors are used/free. Since it is 3 bytes (8 bits/byte) we have 24 bits of storage. Remember that at most, each track only has 21 sectors, so there are a few unused bits. Bytes: 04-07: 12 FF F9 17 Track 1 BAM 08-0B: 15 FF FF FF Track 2 BAM 0C-0F: 15 FF FF 1F Track 3 BAM ... 8C-8F: 11 FF FF 01 Track 35 BAM These entries must be viewed in binary to make any sense. We will use the first entry (track 1) at bytes 04-07: FF=11111111, F9=11111001, 17=00010111 In order to make any sense out of the binary, flip the bits around. 111111 11112222 01234567 89012345 67890123 -------------------------- 11111111 10011111 11101000 ^ ^ sector 0 sector 20 Since we are on the first track, we have 21 sectors, and only use up to the bit 20 position. If a bit is on (1), the sector is free. Therefore, track 1 has sectors 9,10 and 19 used, all the rest are free. Each filetype has its own unique properties, but most follow one simple structure. The starting block is pointed to by the directory, and follows a t/s chain, until the track value reaches $00. When this happens, the value in the sector location indicates how much of the block is used. For example, the following chain indicates a file 6 blocks long, and ends when we encounter the $00 $34 chain. At this point the last block occupies from bytes $02-$33 (one less than the value actually indicated). 1 2 3 4 5 6 ---- ----- ----- ---- ----- ---- 17,0 17,10 17,20 17,1 17,11 0,34 The REL filetype, however, has a most unusual structure. It was designed to make access to data *anywhere* on the disk very fast. Take a look at this directory entry... 20: 00 00 84 11 02 41 44 44 49 54 49 4F 4E 41 4C 20 30: 49 4E 46 4F A0 11 0C FE 00 00 00 00 00 00 61 01 Note with this directory entry (REL file) that the three normally empty entries at $35, $36 and $37 are now used, as they are explained above. It's the sector chain that this entry points to (called the SIDE SECTORS) which are of interest here (in this case, track 17, sector 12). Here is a dump of the beginning of that sector... 00: 0C 13 00 FE 11 0C 0C 13 06 09 00 00 00 00 00 00 10: 11 02 11 0D 11 03 11 0E 11 04 11 0F 11 05 11 10 ... Bytes: $00: Track location of next side-sector ($00 if last block) 01: Sector location of next side-sector 02: Side-sector block number (this block is $00, the next is $01, then $02, etc) 03: REL file RECORD size (from directory entry) 04-0F: Track/sector locations of the other side-sectors. Note the first entry is this very sector we have listed here. The next is the next t/s listed at the beginning of the sector. All of this information must be correct. If one of these chains is 00,00, then we have no more side sectors. 10-FF: T/S chains of *each* block of the data portion. When we get a 00,00, we are at the end of the file. If the speed advantage regarding this type file file isn't obvious yet, consider the following scenario... If we need record 4000, its only a couple of calculations to see how many bytes into the file it is (record# * record length). Once we know this, we can calculate how many sectors into the file it is (result/254). Now that we know the number of sectors, we can look it up in our side-sector tables to see where the record is. The speed of this system is truly amazing, given the era of the C64, and a floppy drive. --- These are some variations of the D64 layout: 1. Standard 35 track layout but with 683 error bytes added on to the end of the file, thus making the file size 175531. These are not unusual to find. Each byte of the error info corresponds to a single sector stored in the D64, indicating if the sector on the original disk contained an error. The first byte is for track 1, sector 0, and the last byte is for track 35, sector 16. 2. A 40 track layout, following the same sector/track as a 35 track disk, but the last 5 tracks (36-40) contain 17 sectors each. Logically, this would make the file size 196608 bytes (197376 with error bytes). This is a very rare format to find! The location of the extra BAM information in sector 18/0 will be different depending on what standard the disks have been formatted with. SPEED DOS stores them from $C0 to $D3, and DOLPHIN DOS stores them from $AC to $BF. Here is the meaning of the error bytes added onto the end of any extended D64 (the CODE is the same as that generated by the 1541 drive controller... it reports these numbers, not the error code we usually see when an error occurs): Code Disk Error 1541 error description ---- ---------- ------------------------------ 01 00 No error, sector ok. 02 20 Header block not found 03 21 No sync character 04 22 Data block not present 05 23 Checksum error in data block 06 24 N/A 07 25 N/A 08 26 N/A 09 27 Checksum error in header block 0A 28 N/A 0B 29 Disk ID mismatch The advantage with using the D64 format (at least staying with the normal 35 track, with or without error bytes) is it can be converted directly back to a 1541 disk by either using the proper cable and software on the PC or send it down to the C64 and writing it back to a 1541. It is the best documented format since it is also native to the C64, with many books explaining the disk layout and the internals of the 1541. --------------------------------------------------------------------------- *** X64 This is a format which is used on the X64 emulator (for UNIX systems), an emulator which is not too common any more, and has been replaced by VICE, which supports the X64 file standard, as well as the regular D64/T64 files. Although the X64 format seems to be unused, it exists and should be mentioned. It is almost the same as a D64, except there is a 64-byte header at the beginning of the file. Here is the header layout. 0000: 43 15 41 64 01 01 00 00 00 00 00 00 00 00 00 00 0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0040: <- standard D64 file starts here.... Bytes:$00-03: This is the "Magic header" ($43 $15 $41 $64) 04: Header version major ($01) 05: Header version minor ($01) 06: Device type represented $00=1541 $01=1570 $02=1571 $03=1581 07: Maximum tracks in image (only used on disks with a version higher than $01 $01) 08: Flag indicating a second side of the disk exists $00=No second side $01=Second side 09: Error data flag (I assume the data is added onto the end of the image) 0A-1F: Unused 20-3E: Disk image description (in ASCII) 3F: Always set to $00 40: Standard D64 file begins here In most images, bytes 07-3F are set to zero. Some versions of the X64 emulator don't use bytes 08-3F. The header used above is exactly what 64COPY uses to create X64's. There is no advantage for PC users to use this format since no *PC* emulator that I know of uses them, and it is functionally the same as a D64. --------------------------------------------------------------------------- *** T64 This format was designed by Miha Peternel for use with his C64s emulator. It has a very structured directory (similar to a D64 directory entry), with each entry taking up 32 bytes, and a reasonably well-documented format. It has a large header at the beginning of the file used for file signature, tape name, number of directory entries, used entries, and the remainder for actual tape directory entries. Following immediately after the end of the directory comes the data for each file. Each directory entry includes the information of where its data starts in the file (referenced to the beginning of the file), as well as how long the file is (end address - start address), so you know how to delineate each file. Here is a HEX dump of the first few bytes of a standard T64 file: 000000: 43 36 34 53 20 74 61 70 65 20 69 6D 61 67 65 20 C64S.tape.image. 000010: 66 69 6C 65 00 00 00 00 00 00 00 00 00 00 00 00 file............ 000020: 01 01 90 01 05 00 00 00 43 36 34 53 20 44 45 4D ........C64S.DEM 000030: 4F 20 54 41 50 45 20 20 20 20 20 20 20 20 20 20 O.TAPE.......... 000040: 01 01 01 08 85 1F 00 00 00 04 00 00 00 00 00 00 ................ 000050: 53 50 59 4A 4B 45 52 48 4F 45 4B 20 20 20 20 20 SPYJKERHOEK..... 000060: 01 01 01 08 B0 Ca 00 00 84 1B 00 00 00 00 00 00 ................ 000070: 49 4D 50 4F 53 53 49 42 4C 45 20 4D 49 53 53 2E IMPOSSIBLE MISS. ... ... 0003E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0003F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000400: 1A 08 E4 07 9E 32 30 38 30 14 14 14 14 14 14 14 ................ The first 32 bytes (0000-001F) represent the signature of the file, telling us it is a tape image file for C64S. 000000: 43 36 34 53 20 74 61 70 65 20 69 6D 61 67 65 20 C64S.tape.image. 000010: 66 69 6C 65 00 00 00 00 00 00 00 00 00 00 00 00 file............ It is important that the string "C64" be at the beginning of the file because it is the string which is common enough to be used to identify the file type. There are several variations of this string like "C64S tape file" or "C64 tape image file". The string is stored in DOS ASCII characters. The next 32 bytes contain all the info about the directory size, number of used entries, tape image name, tape version#, etc. 000020: 01 01 90 01 05 00 00 00 43 36 34 53 20 44 45 4D ........C64S.DEM 000030: 4F 20 54 41 50 45 20 20 20 20 20 20 20 20 20 20 O TAPE.......... Bytes:$20-21: Tape version number of either $0100 or $0101. I am not sure what this is used for. 22-23: Maximum number of entries in the directory, stored in low-byte/high-byte order (in this case $90 = 144, so total = (144 + 1 * 256) = 400 entries. 24-25: Total number of used entries, once again in low-byte/high-byte. Used = (5 + 0 * 256) = 5 entries. 26-27: Unused 29-3F: Tape Image Name, padded with $20 (space) The next 32 bytes (and on until the end of the directory) contain individual directory entries. 000040: 01 01 01 08 85 1F 00 00 00 04 00 00 00 00 00 00 ................ 000050: 53 50 59 4A 4B 45 52 48 4F 45 4B 20 20 20 20 20 SPYJKERHOEK..... 000060: 01 01 01 08 B0 CA 00 00 84 1B 00 00 00 00 00 00 ................ 000070: 49 4D 50 4F 53 53 49 42 4C 45 20 4D 49 53 53 2E IMPOSSIBLE MISS. Bytes $40: Directory entry type: 0 = free (usually) 1 = Normal tape file 3 = Memory Snapshot, v .9, uncompressed 2-255 = Reserved (for memory snapshots) 41: C64 file type. Its usually set to 1, but it does change if whats stored is a snapshot (FRZ file). 42-43: Start address (or Load address). This is the first two bytes of the C64 file which is usually the load address (typically $01 $08) 44-45: End address (actual end address in memory, if the file was loaded into a C64) 46-47: Not used 48-4B: Offset into the image file (from the beginning) of where the C64 file starts (stored as low-byte/high-byte) 4C-4F: Not used 50-5F: C64 filename (in PETASCII, padded with $20, not $A0) Typically, an empty entry will have no contents at all, and not just have the first byte set to 00. If you do set only the filetype byte to $00 and then use the file in C64S, you will see the entry is still on the directory. 0003E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0003F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Starting at $000400 (assuming a directory with 30 entries) we now have actual file data. 000400: 1A 08 E4 07 9E 32 30 38 30 14 14 14 14 14 14 14 .....2080....... ... This format has some advantages over D64's in that there is very little wasted space, except for the empty directory entries. It is also laid out very logically, with entries and headers all being in 32-byte chunks. This makes for easy support. It has several disadvantages as well, the biggest being that C64s is really the only emulator to support it extensively. PC64 will (if it doesn't already) support it but ALEC64 (as of now) does not. Also, it is not a native C64 filetype, so if you want to make the files available for a C64, they will have to be converted to something else first. One other drawback is it does not support multi-file games. If you have a program which requires several sub-files to be loaded, a T64 file will not work. It would be best to use a D64 in this case. --------------------------------------------------------------------------- *** C64s ".FRZ" FRoZen Files These files, similar in nature to PC64's .C64 files, are a complete memory dump of the 64K RAM, color RAM, and all I/O ports and CPU registers. The only emulator file which can hold FRZ files is a T64, as its directory/filetype values allow for it. These files (up to C64s V2.0), when converted to raw binaries, are 66806 bytes large. 00000: 00 00 .. .. .. .. .. .. .. .. .. .. .. .. .. .. <- Load Address ... 00000: .. .. 2F 47 00 AA B1 91 B3 00 00 00 00 00 00 FF <- 64k main RAM 00010: 00 00 00 40 02 00 BE 00 19 16 00 00 C9 9E 00 00 00020: 00 00 00 00 40 02 69 00 00 00 03 00 00 01 08 2A 00030: 3A A8 3A AA 4D C9 9E C9 9E 00 A0 BE 00 00 00 D9 ... 10000: .. .. 01 01 01 01 01 01 01 01 01 01 01 01 01 01 <- Color RAM 10010: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 10020: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 10030: 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ... 10400: .. .. F1 B0 F1 01 69 20 3A 00 00 00 00 00 00 00 <- I/O 10410: 00 00 00 00 00 00 00 00 00 00 00 9B 37 00 00 00 10420: 08 00 15 0F F0 00 00 00 00 00 00 00 01 02 03 04 10430: 00 01 02 03 04 05 06 07 4C 00 00 00 00 00 00 00 ... Bytes:$00000-00001: Load address (always $00 $00) 00002-10001: Main 64k RAM ($0000-FFFF) 10002-10401: Color RAM ($D800-DBFF) 10402-10409: CPU registers (8 bytes) in the following order: - PC (Program Counter, 2 bytes), - SP (Stack Pointer, 2 bytes), - A (Accumulator, 2 bytes) - X/Y (X and Y registers, 2 bytes) 1040A-10438: VIC-II ($D000, 47 bytes) 10439-10455: SID ($D400, 29 bytes) 10456-10465: CIA #1 ($DC00, 16 bytes) 10466-10475: CIA #2 ($DD00, 16 bytes) 10476-104F5: CPU Task data (128 bytes), containing: - Shadow registers - Emulation Registers Seeing as each emulator uses its own format for its "snapshot" images, there is no simple conversion method to take one snapshot (i.e. a PC64 .C64 file) and convert it to another emulator snapshot format (i.e. a C64s FRZ file). The only commonalities would be the main 64k RAM and color RAM areas, whereas the rest of the file would be quite different. --------------------------------------------------------------------------- *** DiskPacked ZipCode (4-file version, 1!xxxxxx, 2!xxxxxx, etc) This format works directly on D64 images only. It is a compressed form of a D64 disk (or D64 files) and is comprised of 4 files, with each file containing approximately the same number of sectors. Here is a chart of the filenames, the range of tracks, and the total sectors that each file contains: 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 This format uses sector interleaving to read the disk. It reads each sector using an interleave of -10 for even #'s sectors (0,2,4...) and +11 for odd numbered sectors (1,3,5...). The actual value for both interleaves varies as we progress further into the disk. At track 18, it goes to -9 for evens and +10 for odds, and at track 25 it changes to -8 for evens and +9 for odds. This is important to better understand the layout, as it means the sectors are *not* stored in order (0,1,2...), but the changing interleave makes reading/writing the sectors much faster. This also means that in order to reconstruct each track, a buffer of memory the size of the largest track (track 1, 21 sectors*256 bytes = 5.25 kbytes) must be set aside, and the sector information copied into the appropriate area until the whole track is assembled. Here is a partial HEX dump of the first file and description of the layout... 0000: FE 03 36 34 41 00 00 41 0B 00 41 01 00 41 0C 00 0010: 41 02 00 41 0D 00 41 03 00 41 0E 00 41 04 00 41 0020: 0F 00 41 05 00 41 10 00 41 06 00 81 11 33 02 00 0030: 2F 80 25 9D FA 66 AF 9B 6A 14 A0 E4 10 CA 18 90 0040: 7B 67 51 47 92 2B 4C 52 83 78 01 A9 58 D0 31 70 0050: 34 30 B7 85 2C D4 9F 1C 1F A9 EA EA EA 15 3E 02 0060: D0 00 41 07 00 41 12 00 41 08 00 41 13 00 41 09 Byte: $00-01: Load address, low-byte/high-byte (03FE for first file, 0400 for the next three files) 02-03: Disk ID, only contained in first file. From here on, the format can vary, depending on the contents of each sector. The next two bytes contain the track/sector, but with the track containing the compression used, encoded on the top two bits... Bit: 76543210 xxyyyyyy ^^| | | ------ | ^ | | | | | These are the track bits. | These are the compression flags Since the track range is from 1-35, only the bottom 5 bit are used. This leaves the top two empty, and useable. Here is their useage: 00 - No compression, the sector is stored in full. The next 256 bytes contain the full sector information. 01 - Sector is filled with *one* character. The next character is the fill byte. Repeat it 256 times, and fill the sector. 10 - Sector is compressed using RLE compression (see below for details) 11 - Unused Lets look at each method of storing a sector with different compression methods, using the above example... 00: This is the simplest method, as it entails no compression. All we have it a track, sector, and 256 bytes of data follow it. 01: At byte 04 we have 41 00 00. Breaking down $41 to binary we have "01000001". The top two bits indicate we have type 01 compression (fill sector), and the bottom 6 indicate we have track 1. The next byte is the sector ($00), and the next byte is also a $00, indicating this sector is completely filled with $00. 0000: .. .. .. .. 41 00 00 41 0B 00 41 01 00 41 0C 00 0010: 41 02 00 41 0D 00 41 03 00 41 0E 00 41 04 00 41 10: The RLE encoding takes some explanation. RLE stands for "RUN LENGTH ENCODING", and is simply a means of encoding a series of the same numbers into a much smaller string (i.e. encoding the 1's in the string "0456111111111645" into something much shorter). Looking at the example below, when we encounter a 10 type, we have track ($81), sector ($11), the length of the encoded string ($33, 51 decimal) and a REP code, a unique byte used as a flag to show when we encounter an encoded repeated string. The REP code is a single byte whose value doesn't occur in the decoded sector. 0020: .. .. .. .. .. .. .. .. .. .. .. 81 11 33 02 00 0030: 2F 80 25 9D FA 66 AF 9B 6A 14 A0 E4 10 CA 18 90 0040: 7B 67 51 47 92 2B 4C 52 83 78 01 A9 58 D0 31 70 0050: 34 30 B7 85 2C D4 9F 1C 1F A9 EA EA EA 15 3E 02 0060: D0 00 .. .. .. .. .. .. .. .. .. .. .. .. .. .. We know with this example that the encoded data is 51 bytes long ($33), and with a REP code of $02, whenever we encounter a $02, we have an encoded sequence. If we do not encounter a $02, we have normal bytes. In the above sequence, we do not encounter a $02 until 005F, so all the rest are normal bytes, which would go into the sector starting at position 00. Once we hit a $02, the next two bytes are encoded this way... repeat count ($D0, decimal 208), byte to repeat ($00). So we fill the next $D0 (208 bytes) with $00's. If you add up what we had before the $02, it was 48 bytes long, add this to the 208 bytes and we have a full sector of 256 bytes. A 256-byte sector stored in 55 (51 + 4 byte header) bytes represents a good savings. Notice the byte sequence in the above example 'EA EA EA'. Why was this not encoded? Simple. The encoding sequence (REP, LENGTH, CHAR) takes three bytes. It would not make any sense to encode something which is no shorter than the original string. ZipCode will only encode a repeated string of 4 bytes or longer. Now, lets break down the above sample into its encoded parts, to see how its made... 0000: FE 03 - Load Address 0002: 36 34 - Disk ID 0004: 41 00 00 - T/S 1,00, fill $00 0007: 41 0B 00 - T/S 1,11, fill $00 000A: 41 01 00 - T/S 1,01, fill $00 000D: 41 0C 00 - T/S 1,12, fill $00 0010: 41 02 00 - T/S 1,02, fill $00 0013: 41 0D 00 - T/S 1,13, fill $00 0016: 41 03 00 - T/S 1,03, fill $00 0019: 41 0E 00 - T/S 1,14, fill $00 001C: 41 04 00 - T/S 1,04, fill $00 001F: 41 0F 00 - T/S 1,15, fill $00 0022: 41 05 00 - T/S 1,05, fill $00 0025: 41 10 00 - T/S 1,16, fill $00 0028: 41 06 00 - T/S 1,06, fill $00 002B: 81 11 33 02 - T/S 1,17, RLE, length 51 bytes, REP byte $02 00 2F 80 25 - Normal data 9D FA 66 AF 9B 6A 14 A0 E4 10 CA 18 90 7B 67 51 47 92 2B 4C 52 83 78 01 A9 58 D0 31 70 34 30 B7 85 2C D4 9F 1C 1F A9 EA EA EA 15 3E 005F: 02 D0 00 - REP byte found, repeat 208, fill with $00 0062: 41 07 00 - T/S 1,07, fill $00 0065: 41 12 00 - T/S 1,18, fill $00 This listing is basically what you would see if you ran CheckZipCode from inside 64COPY. It will dump out the ZipCode files into their constituent parts, so you can see how the file is made, and if any errors exist. There is no benefit for using ZipCode as it is only used for making disks easier to upload/download (for a BBS) or store. It is not a format that any of the emulators use directly. You can find utilites for the PC to undo and create the images, if you need to. --------------------------------------------------------------------------- *** SixPack Zipcode (6-file version, 1!!xxxxx, 2!!xxxxx, etc) This is another rare form of ZipCode, spanning 6 archive files, and hence the use of the name SixPack. Note the difference in the filename structure from a 4-pack ZipCode. Name Track Range -------- ----------- 1!!xxxxx 1 - 6 2!!xxxxx 7 - 12 3!!xxxxx 13 - 18 4!!xxxxx 19 - 25 5!!xxxxx 26 - 32 6!!xxxxx 33 - 35 (or 40 for 40-track images) The format for these is *nothing* like the 4-pack. It contains no compression and no track/sector references. Rather, all the sector data is stored in its native GCR (Group Code Recording) code. GCR is the method used to store information, at the lowest level, on a 1541 drive. It converts 4-bit nybbles (2 nybbles per byte) into an encoded 5-bit GCR code. The conversion chart for 4-bit to 5-bit conversion is as follows: Hex Binary GCR Hex Binary GCR --- ------ ----- --- ------ ----- $0 - 0000 - 01010 $8 - 1000 - 01001 1 - 0001 - 01011 9 - 1001 - 11001 2 - 0010 - 10010 A - 1010 - 11010 3 - 0011 - 10011 B - 1011 - 11011 4 - 0100 - 01110 C - 1100 - 01101 5 - 0101 - 01111 D - 1101 - 11101 6 - 0110 - 10110 E - 1110 - 11110 7 - 0111 - 10111 F - 1111 - 10101 If you look over the GCR table, there are two details that should be noted. 1. You *cannot* combine any two GCR bytes into numbers that contain more than 10 consecutive 1-bits (the most we can get is 8). Ten (or more) consecutive 1-bits is used for a SYNC mark, used to tell the disk controller that sector data is coming up. (In actual fact, the 1541 records an overkill of 40 sequential 1-bits to the disk as a SYNC mark to ensure the controller can find it) 2. There will never be any more than two consecutive 0-bits. This is done to insure the accuracy of clocking data back to the 1541 controller. Using the above table, let's convert some numbers. For reasons I will explain later, we must work in groups of four bytes in order to convert normal HEX to GCR. Using these HEX numbers... 0D F5 E4 37 now, split these values into nybbles and convert to binary... 0 D F 5 E 4 3 7 ---- ---- ---- ---- ---- ---- ---- ---- 0000 1101 1111 0101 1110 0100 0011 0111 convert nybbles to GCR using the conversion table... 0000 1101 1111 0101 1110 0100 0011 0111 ----- ----- ----- ----- ----- ----- ----- ----- 01010 11101 10101 01111 11110 01110 10011 10111 now, recombine the bit into groups of 8... 01010 11101 10101 01111 11110 01110 10011 10111 | || || || || | | byte 1|| byte 2 || byte 3 || byte 4 || byte 5| | || || || || | ------- ---------- --------- ---------- ------- 01010111 01101010 11111111 00111010 01110111 and convert back to HEX... 01010111 01101010 11111111 00111010 01110111 -------- -------- -------- -------- -------- 57 6A FF 3A 77 So, now we have converted a group of 4 bytes into 5 GCR bytes. The reason we must encode in groups of 4 is that it is the *minimum* number of bytes which, when converted to GCR bits, is divisible by 8 bits... 1 byte would be 10 bits, 2 bytes would be 20, 3 bytes would be 30, but 4 bytes is 40 bits, divisible by 8 since it leaves us with 5 groups of 8 bits. Now that we have a foundation of GCR encoding, we can begin to analyse the layout of the 6-pack zipcode. Below is a sample of the first file (1!!): 0000: FF 03 24 52 55 25 29 4B 9A E7 25 55 55 52 55 35 0010: 2D 4B 9A E7 25 55 55 52 54 A5 49 4B 9A E7 25 55 0020: 55 52 54 B5 4D 4B 9A E7 25 55 55 52 55 65 39 4B 0030: 9A E7 25 55 55 52 55 75 3D 4B 9A E7 25 55 55 52 0040: 54 E5 59 4B 9A E7 25 55 55 52 54 F5 5D 4B 9A E7 0050: 25 55 55 52 55 A5 25 4B 9A E7 25 55 55 52 55 B5 0060: 65 4B 9A E7 25 55 55 52 54 95 69 4B 9A E7 25 55 0070: 55 52 55 95 6D 4B 9A E7 25 55 55 52 55 E5 35 4B 0080: 9A E7 25 55 55 52 55 55 75 4B 9A E7 25 55 55 52 0090: 54 D5 79 4B 9A E7 25 55 55 52 55 D5 55 4B 9A E7 00A0: 25 55 55 52 57 25 A9 4B 9A E7 25 55 55 52 57 35 00B0: AD 4B 9A E7 25 55 55 52 56 A5 C9 4B 9A E7 25 55 00C0: 55 52 56 B5 CD 4B 9A E7 25 55 55 52 57 65 B9 4B 00D0: 9A E7 25 55 55 29 0F 05 C0 99 00 02 C8 D0 D4 A9 00E0: 02 8D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00F0: 00 00 00 00 00 00 00 00 00 41 4D 45 3A 20 31 32 0100: 33 34 15 D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D 0110: 4B 52 D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D 4B 0120: 52 D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D 4B 52 0130: D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 0140: B5 2D 4B 52 D4 B5 29 4D 55 55 D4 A5 2D 4B 52 D4 0150: B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 0160: 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D 4B 52 D4 B5 2D Each file starts with a 3-byte signature, $FF, $03, and then either a $24 for a 35 track image, or a $29 for a 40 track image. 0000: FF 03 24 .. .. .. .. .. .. .. .. .. .. .. .. .. Track information follows, and is comprised of a 256 byte track descriptor block (mostly GCR encoded), followed by sector information at 326 bytes/sector. The track descriptor contains the header information of each sector in that track, as well as the number of sectors encoded on that track. They are stored in the descriptor block in linear order, from 0 to the maximum sector on that track. Note that each of the entries in the descriptor take 10 bytes, as 10 bytes (GCR) decodes to 8 bytes normal HEX. These also correspond to the header info on a real 1541 disk. Here is the layout (re-arranged from the above block)... Sec Byte CGR Contents Normal Contents (decoded) --- ---- ----------------------------- ------------------------- 0 00: 52 55 25 29 4B 9A E7 25 55 55 08 02 00 01 31 32 0F 0F 1 0A: 52 55 35 2D 4B 9A E7 25 55 55 08 03 01 01 31 32 0F 0F 2 14: 52 54 A5 49 4B 9A E7 25 55 55 08 00 02 01 31 32 0F 0F 3 1E: 52 54 B5 4D 4B 9A E7 25 55 55 08 01 03 01 31 32 0F 0F 4 28: 52 55 65 39 4B 9A E7 25 55 55 08 06 04 01 31 32 0F 0F 5 32: 52 55 75 3D 4B 9A E7 25 55 55 08 07 05 01 31 32 0F 0F 6 3C: 52 54 E5 59 4B 9A E7 25 55 55 08 04 06 01 31 32 0F 0F 7 46: 52 54 F5 5D 4B 9A E7 25 55 55 08 05 07 01 31 32 0F 0F 8 50: 52 55 A5 25 4B 9A E7 25 55 55 08 0A 08 01 31 32 0F 0F 9 5A: 52 55 B5 65 4B 9A E7 25 55 55 08 0B 09 01 31 32 0F 0F 10 64: 52 54 95 69 4B 9A E7 25 55 55 08 08 0A 01 31 32 0F 0F 11 6E: 52 55 95 6D 4B 9A E7 25 55 55 08 09 0B 01 31 32 0F 0F 12 78: 52 55 E5 35 4B 9A E7 25 55 55 08 0E 0C 01 31 32 0F 0F 13 82: 52 55 55 75 4B 9A E7 25 55 55 08 0F 0D 01 31 32 0F 0F 14 8C: 52 54 D5 79 4B 9A E7 25 55 55 08 0C 0E 01 31 32 0F 0F 15 96: 52 55 D5 55 4B 9A E7 25 55 55 08 0D 0F 01 31 32 0F 0F 16 A0: 52 57 25 A9 4B 9A E7 25 55 55 08 12 10 01 31 32 0F 0F 17 AA: 52 57 35 AD 4B 9A E7 25 55 55 08 13 11 01 31 32 0F 0F 18 B4: 52 56 A5 C9 4B 9A E7 25 55 55 08 10 12 01 31 32 0F 0F 19 BE: 52 56 B5 CD 4B 9A E7 25 55 55 08 11 13 01 31 32 0F 0F 20 C8: 52 57 65 B9 4B 9A E7 25 55 55 08 16 14 01 31 32 0F 0F D2: 29 0F 05 C0 99 00 02 C8 D0 D4 Unused DC: A9 02 8D 00 00 00 00 00 00 00 Unused E6: 00 00 00 00 00 00 00 00 00 00 Unused F0: 00 00 00 00 00 00 Unused F6: 41 4D 45 3A 20 31 32 33 34 ASCII "AME: 1234" (not GCR) FF: 15 Number of sectors in track (21) The entries for Sectors 17-20 will only be valid if the track being operated on has that many sectors in it. If the value at $FF is $00, then we have an empty track (bad track from the original disk). When this happens, all the sector header info in the track descriptor block will be invalid. If we only use up to sector 16 (for tracks 31 and up), then all the info following the entry for sector 16 will be invalid. Invalid data can be anything, just ignore it. There is a good trick here which involves the track descriptor block. If you are looking over a set of 6-pack files, you can tell where a new track starts by looking for the ASCII string "AME: 1234". Since it always seems to be at the end of the track descriptor block, it is a useful marker. It is a little more difficult to determine what track you are on, but given the track ranges for each file, you can figure it out. Each sector header block is laid out in the following order (after decoding the 10 GCR bytes to 8 normal bytes): Byte: $00 - Always $08, the ID for a "header block" 01 - Header checksum (EOR of bytes 02-05) 02 - Sector number 03 - Track number 04 - Second byte of disk ID 05 - First byte of disk ID 06-07 - Filler bytes (usually $0F) Each sector is 326 bytes long (GCR encoded), and each track is 256 bytes + (# of sectors/track * 326) bytes. Track 1 would be 256 + (21 * 326) = 7102 bytes. The sector information stored in a specific interleave pattern, depending on the track we are on (unlike the entries in the descriptor block, which are stored in linear order). Note that if this was a 40 track image, the interleave pattern for the last set of tracks would apply up to track 40 instead of 35. Track Sector interleave storage pattern ----- ---------------------------------------------------- 1-17 - 0,8,16,3,11,19,6,14,1,9,17,4,12,20,7,15,2,10,18,5,13 18-24 - 0,8,16,5,13,2,10,18,7,15,4,12,1,9,17,6,14,3,11 25-30 - 0,8,16,6,14,4,12,2,10,1,9,17,7,15,5,13,3,11 31-35 - 0,8,16,7,15,6,14,5,13,4,12,3,11,2,10,1,9 The data within each sector is stored *out of order*. The last 70 bytes are first, then the first 256 bytes follow. We actually only decode 325 bytes (from 0-324), so the last byte is left out. Once decoded, we have the following information: Bytes: $000 - Contains $07, the ID for a "data block" 001-100 - Normal sector info 101 - Sector checksum (EOR of the data block, from 001-100) 102-103 - Filler bytes, usually $00 ZipCode disks are usually used to transfer disks which contain errors (copy-protected disks). It also is used for those disks which use unusual fastloaders such as Vorpal or Warp25, which use a different GCR encoding method. The offset for each track into its respective file can vary. Assuming that the image contains no errors, we can look at each file and calculate the offset position of where each track should be. Note that the entries for files 1!!, 2!! and 3!! are the same. The only difference among these three is 3!!, which contains track 18, a track with only 19 sectors, and hence the file will be slightly shorter. File 1!! Offset File 2!! Offset -------- ------------- -------- ------------- Track 1 $0003 (3) Track 7 $0003 (3) Track 2 $1BC1 (7105) Track 8 $1BC1 (7105) Track 3 $377F (14207) Track 9 $377F (14207) Track 4 $533D (21309) Track 10 $533D (21309) Track 5 $6EFB (28411) Track 11 $6EFB (28411) Track 6 $8AB9 (35513) Track 12 $8AB9 (35513) File 3!! Offset File 4!! Offset -------- ------------- -------- ------------- Track 13 $0003 (3) Track 19 $0003 (3) Track 14 $1BC1 (7105) Track 20 $1935 (6453) Track 15 $377F (14207) Track 21 $3267 (12903) Track 16 $533D (21309) Track 22 $4B99 (19353) Track 17 $6EFB (28411) Track 23 $64CB (25803) Track 18 $8AB9 (35513) Track 24 $7DFD (32253) Track 25 $972F (38703) File 5!! Offset File 6!! Offset -------- ------------- -------- ------------- Track 26 $0003 (3) Track 33 $0003 (3) Track 27 $17EF (6127) Track 34 $16A9 (5801) Track 28 $2FDB (12251) Track 35 $2D4F (11599) Track 29 $47C7 (18375) Track 30 $5FB3 (24499) Track 31 $779F (30623) Track 32 $8E45 (36421) Looking at the error codes for a normal 1541 disk, lets look at how some of the errors can be stored in the ZipCode images... Error Error description and method ----- ----------------------------------------------------------------- 20 Header block not found: This applies to individual sectors where the value for the header ($08) wasn't found. The actual header is still read and stored, just that special value $08 was not where it should be. 21 No SYNC character: Before we read a sector from a track, the drive will look for a special "sync" marker, a minimum series of 10 1-bits. If this sync marker is not found, the track is presumed bad, since in order to read any sector data, the electronics of the drive must read the sync mark. 22 Data block not found: This is similar to error# 20. The data block has the descriptor value $07 (from above). If this value is not found, the sector is presumed bad. The SixPack will still contain the sector data. 23 Checksum error in data block: This one comes from the checksum value stored after the sector data. If the checksum present doesn't match the checksum you calculate, we get the error. 27 Checksum error in header block: This is where the checksum stored in the sector header block doesn't correspond to the checksum you calculate for the header. 29 Disk ID mismatch: This one is not an error per se, but simply that the ID contained in the sector header block doesn't match the disks master ID (from track 18/0, the one contained in the header block, and not the one at offset $A2/A3). The real strength of this format is its usefulness in transmitting error-protected and non-standard low-level fast-loaded disks. It is about the only format (next to using extended D64's) which does so. If the disk is completely normal, using this format is a waste of space as normal ZipCode (4-pack) would do even better. --------------------------------------------------------------------------- *** FilePacked ZipCode (A!xxxxxx, B!xxxxxx, etc) This is another rarely seen format, and is very similar to the Diskpacked (4-pack) Zipcode in structure. It is comprised of a series of files denoted by A!xxxxxx/B!xxxxxx, using letters instead of numbers as the first characters of the filename. It also contains one other file called X!xxxxxx which contains a directory of the files stored in the overall archive. Here is the layout of the files: 0000: FF 03 A6 92 01 2D 01 41 00 05 04 52 08 01 40 00 0010: 10 EC FF 07 01 44 00 42 4C 41 53 54 20 56 4F 4C 0020: 55 4D 45 20 31 33 38 A0 A0 30 31 20 32 41 01 04 0030: A0 01 55 00 92 04 70 03 C0 12 00 03 10 2D 03 0D Bytes: 00-01: Load Address, usually $FF, $03, or $03FF) 02: Number of sectors in the file (maximum 166, or $A6) 03-??: Zipcoded sector data starts here If 166 sectors was not enough to store the file then more x!xxxxxx files are needed, and the layout is the same. The sector information is zipcoded in the same manner as Diskpacked 4-pack Zipcodes, but there is one significant difference. Since files have track and sector links, the compression is stored in the top two bits of the track link, and the remainder of the sector (254 bytes) is encoded. Each block of the file is stored in full, even the last one, though it is rarely completely used. Bits ---- 00 - No compression, the sector is stored in full. The next 256 bytes contain the full sector information. 01 - Sector is filled with *one* character. The next character is the fill byte. Repeat it 256 times, and fill the sector. 10 - Sector is compressed using RLE compression (see 4-pack ZipCode for details) 11 - Unused If the track is decoded as $00, then we are on the last block of the file, and the sector represents the number of bytes used. The sector is still decoded as a full 254 byte block. The special file, X!xxxxxx, as mentioned above, contains a listing of all the files in the archive. It has most of the information that the D64/1541 entry would. Here is the layout: 0800: 00 04 08 00 00 9E 32 30 36 31 00 00 00 A0 00 8C 0810: 20 D0 8C 21 D0 B9 C7 08 F0 06 20 D2 FF C8 D0 F5 0820: 85 FF 85 FB A0 0A 84 FC 85 FD 85 FE A0 11 B1 FB 0830: 48 AA C8 B1 FB 48 A8 20 4B 09 20 41 09 8A 20 41 0840: 09 98 20 41 09 68 AA 68 18 65 FD 85 FD 8A 65 FE 0850: 85 FE 20 39 09 20 3C 09 A0 00 B1 FB C9 A0 F0 08 0860: 20 41 09 C8 C0 10 D0 F2 20 3C 09 C0 13 F0 06 20 0870: 39 09 C8 D0 F6 A0 10 B1 FB 29 7F 20 41 09 20 3F 0880: 09 A5 FB 18 69 15 85 FB 90 02 E6 FC E6 FF A5 FF 0890: CD FF 09 D0 97 AA A0 00 20 4B 09 8E 08 09 8C 09 08A0: 09 A6 FD A4 FE 20 4B 09 8D 1A 09 8E 1B 09 8C 1C 08B0: 09 AD FE 09 09 30 8D 2D 09 A0 00 B9 F7 08 F0 06 08C0: 20 41 09 C8 D0 F5 60 93 9B 11 11 11 11 11 11 11 08D0: 11 11 11 11 11 11 11 11 11 48 4F 4C 44 20 53 54 08E0: 4F 50 20 54 4F 20 50 41 55 53 45 20 4C 49 53 54 08F0: 49 4E 47 3A 0D 0D 00 0D 0D 54 4F 54 41 4C 20 46 0900: 49 4C 45 53 20 20 3D 20 30 30 0D 54 4F 54 41 4C 0910: 20 42 4C 4F 43 4B 53 20 3D 20 30 30 30 0D 50 41 0920: 43 4B 45 44 20 46 49 4C 45 53 20 3D 20 30 0D 05 0930: 5B 5A 43 20 49 49 5D 0D 00 A9 20 2C A9 22 2C A9 0940: 0D 20 D2 FF A5 91 C9 7F F0 FA 60 8E 92 09 8C 93 0950: 09 A0 02 A9 30 99 96 09 88 10 FA A2 02 AD 92 09 0960: 38 FD 8F 09 8D 94 09 AD 93 09 E9 00 8D 95 09 90 0970: 11 AD 94 09 8D 92 09 AD 95 09 8D 93 09 FE 96 09 0980: D0 DB CA 10 D8 AD 98 09 AE 97 09 AC 96 09 60 01 0990: 0A 64 00 00 00 00 31 32 33 00 00 00 00 00 00 00 09A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09C0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09E0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 0D Bytes: 0801-0998: BASIC program which lists the contents of the archive 0999-09FD: Not used, can contain garbage. 09FE: Number of archive (x!xxxxxx) files. If the value is 3, we have A!xxxxxx, B!xxxxxx and C!xxxxxx 09FF: Number of C64 files contained in the archive 0A00: Start of the directory Each file has a directory entry in the X!xxxxxx file, starting at $0A00, each 21 bytes long, and laid out as follows: 0A00: 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 0A10: C4 03 00 12 00 42 4C 41 53 54 20 23 31 33 38 20 0A20: 20 2F 53 43 49 D0 20 00 02 01 2D 2D 2D 2D 2D 2D 0A30: 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D C4 03 00 12 00 31 0A40: A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 D0 0A50: 8A 00 13 00 32 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 0A60: A0 A0 A0 A0 D0 36 00 1A 01 33 A0 A0 A0 A0 A0 A0 0A70: A0 A0 A0 A0 A0 A0 A0 A0 A0 D0 17 00 1D 01 34 A0 0A80: A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 D0 2C 0A90: 00 1E 02 35 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 0AA0: A0 A0 A0 D0 1B 00 21 01 36 A0 A0 A0 A0 A0 A0 A0 0AB0: A0 A0 A0 A0 A0 A0 A0 A0 D0 38 00 22 05 37 A0 A0 0AC0: A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 D0 8D 00 0AD0: 10 02 38 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 A0 0AE0: A0 A0 D0 5A 00 09 02 39 A0 A0 A0 A0 A0 A0 A0 A0 0AF0: A0 A0 A0 A0 A0 A0 A0 D0 36 00 05 02 2D 2D 2D 2D 0B00: 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D 2D C4 03 00 12 0B10: 00 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. Bytes:$00-0F: 16 character filename (PETASCII, padded with $A0) 10: Filetype (The same as a D64/1541) 11-12: Length of the file in blocks 13-14: Starting track/sector of the file In order to extract *one* specific file, you would have to read the entire archive until you came across the directory entry that you wanted, then process that specific file. This format does not contain any offset references in the central directory for each file, just the original file size, which means that we don't know anything about where a file might be in the archive, since the original block size doesn't apply as the stored file in compressed. Since this format seems to be uncommon, I can't see any benefit of using it over LNX as a native C64 file. The only plus is it uses simple compression, whereas LNX does not. However, LNX only uses one file for the entire archive. --------------------------------------------------------------------------- *** LNX (Lynx or Ultimate Lynx) This is a suprisingly good format, beautifully designed around the block size of the 1541. It consists of "blocks" of 254 bytes each (256 if we are on a real 1541 or a D64 due to the inclusion of the track/sector info at the beginning of each sector). When these files are on *any* other format they do not have the track/sector info, and therefore use only 254 bytes/block. Here is a dump of the directory header of one permutation of the layout... 0000: 01 08 5B 08 0A 00 97 35 33 32 38 30 2C 30 3A 97 0010: 35 33 32 38 31 2C 30 3A 97 36 34 36 2C C2 28 31 0020: 36 32 29 3A 99 22 93 11 11 11 11 11 11 11 11 22 0030: 3A 99 22 20 20 20 20 20 55 53 45 20 4C 49 4E 58 0040: 20 54 4F 20 44 49 53 53 4F 4C 56 45 20 54 48 49 0050: 53 20 46 49 4C 45 22 3A 89 31 30 00 00 00 0D 20 0060: 31 20 20 2A 55 4C 54 52 41 2D 4C 59 4E 58 20 32 0070: 30 30 31 20 42 59 20 53 2E 42 2E 0D 20 33 20 0D 0080: 45 4C 45 50 2E 20 41 4E 54 49 43 2B 34 2F 47 50 0090: 0D 20 31 36 31 20 0D 50 0D 20 39 20 0D 45 2E 41 00A0: 4E 54 49 43 20 49 4E 54 52 4F 2F 47 50 0D 20 34 00B0: 34 20 0D 50 0D 20 32 36 20 0D 45 2E 41 4E 54 49 00C0: 43 20 50 49 43 2E 20 2F 47 50 0D 20 32 38 20 0D 00D0: 50 0D 20 33 31 20 0D BD BD BD BD BD BD BD BD BD 00E0: BD BD BD BD BD BD BD BD BD BD BD BD BD BD BD BD 00F0: BD BD BD BD BD BD BD BD BD BD BD BD BD BD 01 08 0100: 0A 08 58 1B 9E 32 30 36 31 00 00 00 78 A9 34 85 It starts out with a 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. 0000: 01 08 5B 08 0A 00 97 35 33 32 38 30 2C 30 3A 97 0010: 35 33 32 38 31 2C 30 3A 97 36 34 36 2C C2 28 31 0020: 36 32 29 3A 99 22 93 11 11 11 11 11 11 11 11 22 0030: 3A 99 22 20 20 20 20 20 55 53 45 20 4C 49 4E 58 0040: 20 54 4F 20 44 49 53 53 4F 4C 56 45 20 54 48 49 0050: 53 20 46 49 4C 45 22 3A 89 31 30 00 00 00 .. .. Following this is the "signature" of the archive, as well as the size of the directory (in blocks) and the number of directory entries in the archive. These are stored in CBM lower case (ASCII for the most part), it is delimited by carriage returns ($0D) after each entry (except the dir block size!), and has spaces on both sides of the numbers. Normally the signature will contain the string "LYNX" somewhere. 0050: .. .. .. .. .. .. .. .. .. .. .. .. .. .. 0D 20 0060: 31 20 20 2A 55 4C 54 52 41 2D 4C 59 4E 58 20 32 1 *ULTRA-LYNX 2 0070: 30 30 31 20 42 59 20 53 2E 42 2E 0D 20 33 20 0D 001 BY S.B. 3 So in this example, we have a directory of 1 block (254 bytes) long, the archive was created by "Ultra-Lynx 2001 by S.B.", and we have 3 entries in the directory. The total directory length is 1 block * 254 bytes=254 bytes. Therefore at byte $00FE, the program data will start. If the directory size was 3 blocks, the data would start at $02FA. I do not know what the maximum size is for either number (dir size/entry total), but it would seem to be that since the 1541 only can store 144 files, these numbers would be limited accordingly. 0080: 45 4C 45 50 2E 20 41 4E 54 49 43 2B 34 2F 47 50 ELEP. ANTIC+4/GP 0090: 0D 20 31 36 31 20 0D 50 0D 20 39 20 0D .. .. .. 161 P 9 This is the first directory entry called "ELEP. ANTIC+4/GP". The layout has the filename (in PETASCII), followed by the size of the file in blocks of 254 bytes, the file type (P, S, R, U), the size in bytes of the last block of the file plus 1. 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. 0090: .. .. .. .. .. .. .. .. .. .. .. .. .. 45 2E 41 00A0: 4E 54 49 43 20 49 4E 54 52 4F 2F 47 50 0D 20 34 00B0: 34 20 0D 50 0D 20 32 36 20 0D .. .. .. .. .. .. This is the second directory entry. It follows the same layout as the first. 00B0: .. .. .. .. .. .. .. .. .. .. 45 2E 41 4E 54 49 00C0: 43 20 50 49 43 2E 20 2F 47 50 0D 20 32 38 20 0D 00D0: 50 0D 20 33 31 20 0D This is the third (and last in this example) entry. 00D0: .. .. .. .. .. .. .. BD BD BD BD BD BD BD BD BD 00E0: BD BD BD BD BD BD BD BD BD BD BD BD BD BD BD BD 00F0: BD BD BD BD BD BD BD BD BD BD BD BD BD BD 01 08 0100: 0A 08 58 1B 9E 32 30 36 31 00 00 00 78 A9 34 85 The remaining bytes are unused, and exist simply as filler to pad the directory so as it takes up to its alloted space (recall it is one block of 254 bytes). Once the directory has ended, the real file information is stored. Since in this example the directory is only 1 block long, the file info starts at byte $00FE... 00F0: .. .. .. .. .. .. .. .. .. .. .. .. .. .. 01 08 0100: 0A 08 58 1B 9E 32 30 36 31 00 00 00 78 A9 34 85 The files are also stored so that they take up the full multiples of 254 bytes. This does result in a little dead space at the end of each file stored, but its a small price to pay for how easy it is to break up the file on a real C64. The best feature of this file is how it is constructed and gets broken up on a real C64. When a LNX is created, a new file is created on the disk containing all the necessary information about the files it is going to contain such as the BASIC program, signature and central directory. The header track/sector link is then pointed to the beginning of the first file. The last sector track/sector link of the first file is then pointed to the start of the second file and so on, until the last file is added. This method makes creating LNX's very quick! The advantage to this method is that *no* files are moved or compressed, they are simply "linked" (hence the name LNX) together by changing the t/s links to create one large file. One disadvantage to its use on the PC is the lack of a better laid out central directory, one that had the same number of bytes used for each entry, like the D64 or T64, and had a user-definable directory size, also like the D64/T64. As it is, all the emulator utilities (and perhaps the native C64 Lynxing utilities as well?) cannot add files to a LNX archive once it is created. If a LNX is created with three files in it, generally you can't add any more. There may be a little dead space (due to the directory size always being multiples of 254) if the directory entries don't take up the whole 254 bytes, but I would not muck around with it. --------------------------------------------------------------------------- *** P00/S00/U00/R00 (PC64-native files) These files are used almost exclusively by the PC64 emulator and have not been widely adopted. Each one has the same layout with the filetype being stored in the DOS extension (i.e. Pxx is a PRG, Sxx is a SEQ, Uxx is a USR and Rxx is a relative file), and the header is only 26 bytes long. This is a dump of a Pxx file (PRG)... 0000: 43 36 34 46 69 6C 65 00 43 52 49 53 49 53 20 4D C64File.CRISIS.M 0010: 4F 55 4E 54 41 49 4E 00 00 00 .. .. .. .. .. .. OUNTAIN......... Bytes: $00-06: ASCII string "C64File" 07: Always $00 08-17: Filename in PETASCII, padded with $00 (not $A0, like a D64) 18: Always $00 19: REL file record size ($00 if not a REL file) 1A-??: Program data The 'xx' in the extension of the file is usually 00, except when we have two DOS filenames which would be the same, but the C64 filenames are different! If we have two C64 filenames which are the same, they *cannot* co-exist in the same directory. If we have two files which do convert down to be the same DOS filename, the extension is incremented until an unused one is found (P01, P02, P03, etc). We can have up to 99 different C64 files with the same corresponding DOS names as that's all the extension will hold (from P00 to P99). Each PC64 file only has one entry, there are no multi-file archives allowed. This could result in a large number of these files in a directory, even for only a few programs, as each C64 file will result in a PC64 file entry. The best use for a PC64 file is a single-file program, one which needs to load nothing else. The DOS filename is generated by an algorithm used inside PC64 when a file needs to be created. It compresses the 16-byte name down to one that fits the DOS 8.3 size and character limitations. The biggest benefit to this format is that the layout and useage is fairly simple to impliment, it performs best when used on single-file games, and has *no* wasted space. However, PC64's biggest rival, C64s, doesn't support this format (as of C64s 2.0). Note that PC64 doesn't support C64s's T64 format either. In order to maintain compatibility between emulators (as some games work only on one particular emulator), it would be best to keep the files in a format common to both (D64). The biggest drawback to this format is when converting small C64 files (those of only a few blocks), you tend to waste hard disk space due to a large DOS "slack" area. When DOS stores files of any size, each one takes up a minimum of one cluster, which on small drives may only be 2 kb/cluster, but on vary large disks could be as high as 32 or 64 kb per cluster. An emulator file which is only a few Kb would, on the large disks, only use a small portion of the total cluster, and thus a lot of wasted DOS storage space results. Note, this also applies to normal DOS files as well! Another drawback (not obvious right away) is since some games are made up of a large number of files or varying sizes, it would be best to store each game in a different sub-directory. If you don't, then you might not be sure what files belong to what programs, and if you ever want to give a program to someone else, there would be difficulties re-assembling the program. This makes file management much more user intensive. The D64 format is best when using multi-file games. One other thing which most people might not know is you *can't* rename the P/S/R/U DOS files. If you do, PC64 won't be able to load them. When PC64 goes searching for files, it takes the 16-byte C64 filename, does its magic to reduce it to 8.3, then looks to see which DOS files match. Remember, there could be several files with the same 8 character name, but the extensions will be P00, P01, P02 etc. If it can't find the 16-byte C64 filename in amongst those that match, it will report a "File not found". I've tried this with the DOS version (PC64 1.20) and it does report an error locating the file, both in the File Manager and the 64 emulator window. --------------------------------------------------------------------------- *** PC64 .C64 Saved Images These are files created when you save a C64 window under PC64 (DOS only). It consists of a 64k memory dump, color ram dump, I/O ports, the name of the ROM images you were using at the time, and user-setable options. Typical size is around 69091 bytes. 00000: 43 36 34 49 6D 61 67 65 00 .. .. .. .. .. .. .. C64Image........ ... 00000: .. .. .. .. .. .. .. .. .. 2F 37 00 AA B1 91 B3 00010: 22 22 00 00 4C 00 FF 00 04 00 00 00 00 00 00 19 00020: 16 00 0A 76 A3 00 00 00 00 00 00 76 A3 B3 BD 00 00030: 00 00 00 00 01 08 03 08 03 08 03 08 00 A0 00 00 ... 10000: .. .. .. .. .. .. .. .. .. 0E 0E 0E 0E 0E 0E 0E 10010: 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 10020: 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 10030: 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E ... 10400: .. .. .. .. .. .. .. .. .. 0E 0E 0E 0E 06 06 06 10410: 06 00 00 00 00 00 00 00 00 00 00 C8 00 20 34 37 10420: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 10430: 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 10440: 20 20 20 20 20 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E 0E ... Byte: $00000-00008: The signature "C64Image" followed by a $00. Note the varying case of the text. 00009-10008: Main memory of the C64 (64K, $0000-FFFF) 10009-10408: Color memory (1k, $D800-DBFF) 10409-?????: I/O memory and user-selectable options (ROM version, etc). The ????? means the end of the file is not known, since these files can vary in length. Since these files are only supported by PC64 for DOS (*NOT* for Win95), it is not worth getting too much into the layout. I was attempting to decipher the layout of the I/O ports and options area but decided it wasn't worth it. --------------------------------------------------------------------------- *** Binary This is really a non-format, as it has no specific layout whatsoever, but instead is simply a data file. This does make it difficult to describe any general layout, as there are many possibilities. It is assumed that a binary is a single file, usually having no special extension (like LNX/SFX/SDA, etc), sitting on a PC hard disk, not inside of *any* other file (like D64/T64/LNX etc). By looking at the whole file (but especially the beginning) with a HEX editor, and a well-trained eye, you can usually learn what file type it is by the BASIC code, load address, and text strings contained within. If the file is a normal C64 program, then the first two bytes are the load address, stored in low-byte/high byte format. If it is a BASIC program (or has a BASIC header), then the first two bytes might be $01 $08 (or $01 $04 for PET, $01 $1C for C128), and the rest is line number pointers, line numbers and BASIC code. If it not a BASIC file (like part of a game loader), then it could be a machine language file, requiring a SYS call to execute. Many secondary files with multi-file games have load addresses other than $0801 (like $C000, or $8000 for cartridges), and some have none at all, where the loader program will actually specify where to load the file. Below is a typical header for a game, using a SYS call to start (in this case, SYS 2061). 0000: 01 08 0B 08 C8 07 9E 32 30 36 31 00 00 00 A9 00 0010: 8D 20 D0 8D 21 D0 8D 86 02 20 44 E5 A9 37 A0 08 The reason this non-archive format is important is because of the filename limitations imposed by the PC architecture, and some of the problems that it can cause. The PC has an 8.3 filename convention (assuming DOS, not OS/2 or Windows 95/NT long filenames), allowing for, at most, a filename of 11 characters. The PC also has character limitations on filenames, most of which don't apply to the C64. When converting a C64 file from any emulator file, you *cannot* use the C64 filename directly on the PC since, first of all its likely too long, and secondly it may contain characters illegal in the DOS world (i.e. ASCII 46, the 'extension' separator). In order to convert a filename from C64 to DOS, the use of translate tables (translating illegal characters to legal ones) as well as an algorithm to reduce the filesize down to useable size (typically only 8 characters from 16) must be used. Included within the PC64 distribution archive is a C program which does just what I have described. It contains an algorithm which will remove illegal characters, then reduce the filename down using several different rules, all to make the filename conform to the DOS 8.3 naming standards. There are some people who feel that with Windows95 (or OS/2, or Windows NT) long filename support, we should be able to have C64 files, with the full 16 character filename, on the PC hard disk. This is a fallacy as the useable character limitations of the C64 and those of any OS supporting long filenames are not the same. There is very little benefit or disadvantage to using binary. About the only thing which is important to remember is you could lose most of the original C64 filename, which *will* cause problems when you try to reconstruct them. Binaries have no way of preserving the long names. --------------------------------------------------------------------------- *** ARK These files are created by the ARKIVE program on the C64. This format bears some resemblance to LNX archives in that all the files are simply stored, one after the other, and are byte aligned to take up multiples of 254 bytes (256 on a real 1541). However, there is no BASIC program at the beginning telling you to "Use LYNX to dissolve this file". Another odd difference is that ARK files are *always* multiples of 254 bytes, since even the last block of the last file is 254 bytes long The structure of the directory is also a little different, as all entries take up 29 bytes (unlike LNX's variable size). Below is a sample of an ARK file, with a few of its directory entries... 0000: 1F C2 8A 41 4C 57 41 59 53 20 45 44 2E 56 32 2E 0010: 33 20 20 00 00 00 00 00 00 00 00 00 2A 00 C2 D3 0020: 5A 4F 4F 4D 34 20 45 44 49 54 20 56 30 2E 30 31 0030: 00 00 00 00 00 00 00 00 00 10 00 C2 BF 50 41 52 0040: 54 59 2D 44 45 4D 4F 20 20 20 20 20 20 00 00 00 0050: 00 00 00 00 00 00 08 00 C2 5C 46 49 4B 30 5C 20 0060: 4D 52 2E 20 42 55 5C 4B 49 20 00 00 00 00 00 00 0070: 00 00 00 10 00 C2 DD 53 59 4C 57 45 53 54 45 52 0080: 55 52 45 42 47 4F 20 00 00 00 00 00 00 00 00 00 0090: 0C 00 C2 D8 54 41 2D 54 41 2D 50 4C 45 2D 50 4C Byte: $00: Number of files in the ARKive ($1F = 31 files) 01-1D: First directory entry (29 bytes per entry) 01: File Attribute (same as a D64 attribute) $81=SEQ 82=PRG 83=USR 84=REL 85-8F=Not normally used (results in very *weird* types) bit 7 on, file is locked (< file, $Cx) bit 8 on, normal file ($8x). 02: Number of bytes+1 used in the last sector of the file 03-12: 16-byte filename (in PETASCII, padded with $A0) 13-1B: REL file info (side sector t/s link, record length). Note, these are also the same locations that GEOS stores its VLIR file info. ARK's couldn't possibly contain VLIR files as GEOS files are much too complex. See the entry on GEOS VLIR for more info. 1C-1D: Length of file, in blocks (low-byte, high-byte order) 1E-3A: Second directory entry 3B-57: Third directory entry 58-74: Fourth directory entry 75-91: Fifth directory entry ... The starting location of the file information takes some calculation to find out. Since we have 31 entries, total byte size of the directory is 31 * 29 + 1 = 900 (the 1 comes from the first byte of the file, the byte representing the # of entries). Now, we take the 900 and divide it by 254 to see the number of blocks. 900/254 = 3.543, which when rounded up is 4 blocks. So now we know that the file information starts at 4*254 = 1016 ($03F8) Seeing as no emulator that I know of supports ARK format, I can't see any usefulness in using it. It does have a better directory structure than LNX as each entry has a consistent byte size (versus LNX's variable size). In fact, the entries are virtually identical to the D64/1541. There are also a few utilities for UnARK'ing on the PC. It would seem that LNX is the better supported format, on both the C64 and the emulators. Star Commander does contain a utility called StarArk which will unARK these files into a D64 image. --------------------------------------------------------------------------- * GEOS VLIR Later on in the life of the C64, an OS called GEOS came out. It was a system much like the MacOS in that it used icons, windows, a mouse pointer and resource drivers. In order to contain all the information needed for the windowing system (icon, window position, creation time/date), a new filetype called VLIR was needed. While GEOS files may not be of interest to the majority of emulator users, it is possible that these files might be encountered, and some knowledge of them would be helpful. Each GEOS file or application is comprised of many separate chains (called RECORDS) for different sections of the app/file. Each RECORD can be loaded in separately, and overtop of one another. Below is a dump of the first directory sector of the GEOS 2.0 disk. Note the first entry seems normal enough, but the rest have additional information in the normally unused section of the entry. 00: 12 04 82 13 00 47 45 4F 53 20 56 32 2E 30 20 45 <- Normal entry 10: 4E 47 4C 2E A0 00 00 00 00 00 00 00 00 00 58 00 20: 00 00 83 02 02 44 45 53 4B 20 54 4F 50 A0 A0 A0 <- First GEOS file. 30: A0 A0 A0 A0 A0 02 0F 01 04 58 08 13 0D 23 78 00 <- Note extra info! 40: 00 00 83 0B 13 43 4F 4D 4D 20 31 33 35 31 28 61 50: 29 A0 A0 A0 A0 08 0F 00 0A 58 05 09 15 24 03 00 60: 00 00 83 0F 0D 4D 50 53 2D 38 30 31 A0 A0 A0 A0 70: A0 A0 A0 A0 A0 0F 05 00 09 56 07 19 01 00 04 00 80: 00 00 83 08 0D 43 4F 4E 46 49 47 55 52 45 A0 A0 90: A0 A0 A0 A0 A0 08 05 01 0E 58 05 1F 0B 31 4E 00 A0: 00 00 83 01 10 50 41 49 4E 54 20 44 52 49 56 45 B0: 52 53 A0 A0 A0 01 08 00 06 57 08 0C 0E 00 12 00 C0: 00 00 83 0B 05 70 72 65 66 65 72 65 6E 63 65 20 D0: 6D 67 72 A0 A0 0B 12 00 05 56 0A 09 13 2D 16 00 E0: 00 00 83 08 11 70 61 64 20 63 6F 6C 6F 72 20 6D F0: 67 72 A0 A0 A0 08 07 00 05 58 05 19 0C 10 16 00 Lets analyze the second entry to see whats all involved with GEOS files. Note, the offset values have been changed to 0 to make referencing easier. 00: 00 00 83 02 02 44 45 53 4B 20 54 4F 50 A0 A0 A0 10: A0 A0 A0 A0 A0 02 0F 01 04 58 08 13 0D 23 78 00 Byte: $02: C64 filetype (see the section on D64 for an explanation) 03-04: Starting track/sector (2,2 from above) of C64 file if VLIR filetype is $00. If VLIR filetype is $01, track/sector of single-sector RECORD block 05-14: Filename (in ASCII, padded with $A0, case varies) 15: Track location of info block 16: Sector location of info block 17: VLIR file structure $00 - Normal C64 file (non-VLIR) 01 - VLIR file (with info and RECORD blocks) 18: GEOS filetype $00 - Non-GEOS 01 - BASIC 02 - Assembler 03 - Data file 04 - System File 05 - Desk Accessory 06 - Application 07 - Application Data 08 - Font File 09 - Printer Driver 0A - Input Driver 0B - Disk Driver (or Disk Device) 0C - System Boot File 0D - Temporary 0E - Auto-Execute File 0F-FF - Undefined 19: Year (19xx) 1A: Month (1-12) 1B: Day (1-31) 1C: Hour (0-23, military time) 1D: Minute (0-59) 1E-1F: Filesize, in blocks (low-byte/high-byte order) Note: The bytes $02-04 and $17 need a little more description than is given above. If the C64 filetype (loc $02) is a USR, then we need to look at the VLIR file structure (loc. $17). If the VLIR byte is a $01, then the file is a GEOS VLIR, and bytes $03 and $04 will contain the t/s link to the single-sector record block. If the VLIR byte is a $00 then the file is a normal C64 file, be it USR, REL, etc. The info block is that which stores items like the ICON, size, load address, file types, description, etc, and is *always* 1 sector long. Here is a sample info block, and layout... 00: 00 FF 03 15 BF FF FF FF 92 49 01 FF FF 01 80 00 10: 1D BF FF DD A0 00 5D BF FF C1 A0 00 5D A1 C6 55 20: A0 29 5D A0 C9 41 A1 09 41 B9 D6 41 A8 00 41 BF 30: FF C1 80 00 1D 9C 00 15 9C 00 15 80 00 1D 80 00 40: 01 FF FF FF 83 04 01 56 19 55 19 75 51 64 65 73 50: 6B 54 6F 70 20 41 4D 20 20 56 32 2E 30 00 00 00 60: 00 50 65 74 65 72 20 53 63 68 65 70 65 72 73 00 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80: 00 00 00 00 00 00 00 00 00 4C 2A 5B 4C 59 5D 4C 90: A7 61 4C 3A 62 AD 8A 84 D0 01 60 20 7E 23 20 9C A0: 55 73 65 20 74 68 65 20 64 65 73 6B 54 6F 70 20 B0: 74 6F 20 6D 61 6E 61 67 65 20 61 6E 64 20 6D 61 C0: 6E 69 70 75 6C 61 74 65 20 79 6F 75 72 20 66 69 D0: 6C 65 73 2E 00 03 20 E3 5C 68 85 FB 20 4F 61 20 E0: 13 61 20 32 61 20 F2 5C A9 0C 20 CC 49 A9 2E 85 F0: 13 A9 F9 85 12 A9 2F 85 15 A9 01 85 14 A9 84 85 Byte: $00-01: Contains $00/$FF since its only 1 block long 02: Icon width 03: Icon height 04: Icon bitmap length (in bytes) 05-43: Icon bitmap 44: C64 filetype (same as that from the directory entry) 45: GEOS filetype (same as that from the directory entry) 46: VLIR file structure (same as that from the dir entry) 47-48: Program load address 49-4A: Program end address (only with accessories) 4B-4C: Program start address 4D-60: Class text 61-74: Author (with application data: name of application disk) 75-88: (with application data: name of application) 89-9F: Available for applications, unreserved. A0-FF: Description If the file is a VLIR, then the RECORD block is of interest. This single sector is made up of up to 127 track/sector pointers, each of which point to program sections (called RECORDS). VLIR files are comprised of loadable RECORDS (overlays, if you wish to use PC terminology). The first RECORD is what is always loaded first when you run that application. After that, the OS loads whatever RECORD it needs. Here is a partial sample of the RECORD sector... 00: 00 FF 08 00 09 04 09 03 0A 0A 0B 11 0F 11 00 00 10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Byte: $00-01: Contains 00,FF since its only 1 block long 02-03: Starting track/sector (8,0) for the first RECORD 04-05: Starting track/sector (9,4) for the second RECORD 06-07: Starting track/sector (9,3) for the third RECORD 08-09: fourth RECORD (10,10) 0A-0B: fifth RECORD (11,17) 0C-0D: sixth RECORD (15,17) 0E-0F: seventh RECORD (0,0) When a T/S link of $00 $00 is encountered, we are at the end of the RECORD block. Note that if you add up the blocks so far, we have only used two, one for the INFO sector and one for the RECORD sector. Obviously there are more used, and they are contained in the sector chains from the RECORD sector. Each t/s link in the RECORD sector points to a chain of sectors, the length of which is included in the block count for the GEOS file. Doing a VALIDATE on a GEOS disk when not in GEOS would de-allocate all these sector chains (and the RECORD sector as well), which would not be good!. There are also changes to the BAM sector from a normal D64 file. Looking at the D64 entry from before, we have all the normal information, plus the new information starting at $AB: Bytes:$00-01: Track/Sector location of the first directory entry block (should be 18,1, but it doesn't seem to matter) 02: Disk DOS version type $41=1541 03: Unused 04-8F: BAM entries for each track, in groups of four bytes per track, starting on track 1 90-9F: Disk Name (padded with $A0) A0-A1: Filled with $A0 A2-A3: Disk ID A4: Usually $A0 A5-A6: DOS type, usually "2A" A7-AA: Filled with $A0 AB-AC: Border sector (t/s) *see below AD-BB: GEOS ID string ("geos FORMAT V1.0") BC-DC: Unused DD-FF: Free sector info for tracks 36-70, used on double-sided 1571 disks only! GEOS disks contain something called a "border sector" which is also only one sector long, has the same layout as a normal directory block, and is used for copying files from one disk to another. When you need to copy a file, you drag the icon to the bottom of the screen and the file is moved from its normal location in the directory to the border sector. Since it is only 1 sector, it can hold only 8 entries. Seeing as this is not an emulator format, I will not comment on its relative merits. It is simply another C64 file type, one which is not too useful outside of the realm of GEOS. --------------------------------------------------------------------------- *** LHA (created on a C64/C128, not PC) These files are created with LHA on the C64 (or C128), and can present special problems to the typical PC user. The compression used is LH1, an old method used on LZH1xx (pre-version 2), so any version of LHA on the PC can uncompress them. However, LHA allows filenames of up to 18 characters long, and DOS doesn't know how to handle them. Usually, some of the files already uncompressed will be overwritten by other files just being uncompressed because the name seems the same to DOS. To LHA however, the filenames are quite different. LHA archives always have a string two bytes into the file which describe the type of compression used. Over the development life of LHA there have been several different compression algorithms used, but the C64/C128 use only the first version, LH1. The following is a sample of an LHA header. Note the string to search for: 0000: 24 93 2D 6C 68 31 2D 39 02 00 00 16 04 00 00 00 ..-lh1-......... 0010: 08 4C 14 00 00 0E 73 79 73 2E 48 6F 75 73 65 20 ................ 0020: 4D 34 00 53 DE 06 11 1C 12 C4 C8 FA 3A 5B DC CE ................ 0030: B2 FA 38 1E 46 B0 B6 9E 9B 75 7A 49 71 72 B3 53 ................ 0040: 6E 4E B4 A0 BF 5E 95 B3 05 8A 75 D5 6C E3 03 4A ................ 0050: 2C 54 F4 AF 05 18 59 E2 F4 34 4A 0A 28 D4 33 E2 ................ 0060: C4 9D 04 D7 C7 8B 91 66 0E E5 DE 98 3C 92 CC B5 ................ The format of LHA is much too complex to get into here. Understanding the layout would require knowledge of Huffman coding and sliding dictionaries. It is nowhere near as simple as ZipCode! The description given in the LHA source code goes as follows: -lh1- 4k sliding dictionary (max 60 bytes) + dynamic Huffman + fixed encoding of position There are several utilities that you can use to decompress these files, the already-mentioned LHA on the PC, or STARLHA, one of the many excellent utilities contained in the Star Commander distribution package. If you use StarLHA, keep in mind it needs the LHA program to extract. It will extract the files directly into a D64 archive, so the filenames will not be lost. To an emulator user there is no use to these files, as their only real useage on a C64 was for storage and transmission benefits. The standard compression program on the PC is PKZIP (or ZIP compatibles), so unless you have some need to send *compressed* files back the C64, there is no use in using LHA. --------------------------------------------------------------------------- *** SFX This is *almost* the same as LHA, except there is a decompressor program attached to the front of the compressed file, capable of decompressing on either a C64 or a C128, independant of the load address. The beginning of the file is a BASIC program (with an unusual load address): 00000: 01 1C 28 1C C6 07 97 32 30 2C 30 3A 8B C2 28 32 <-Note load address 00010: 30 29 B2 30 A7 FE 02 30 3A 9E C2 28 34 36 29 AC 00020: 32 35 36 AA 36 36 3A 80 00 3C 1C D0 07 9E C2 28 00030: 34 34 29 AC 32 35 36 AA 36 36 3A 80 00 00 00 .. which when decoded looks like this: 1990 POKE20,0:IFPEEK(20)=0THEN<254><2>0:SYSPEEK(46)*256+66:END 2000 SYSPEEK(44)*256+66:END This was decoded on a C64... the two codes in line 1990, the <254> and <2>0 are not decodable on the C64, but likely on the C128. The first line checks for whether it is running on a C64 or a 128. If it is a C64, it will run line 2000, else it runs line 1990. Later on in the header, we have the copyright message. Any SFX should have this message. 00D20: .. .. .. .. .. .. .. .. .. .. .. 43 36 34 2F 43 ...........C64/C 00D30: 31 32 38 20 53 45 4C 46 20 45 58 54 52 41 43 54 128 SELF EXTRACT 00D40: 49 4E 47 20 4C 48 41 52 43 48 49 56 45 0D 43 4F ING LHARCHIVE.CO 00D50: 50 59 52 49 47 48 54 20 31 39 39 30 20 2D 20 43 PYRIGHT 1990 - C 00D60: 2E 53 4D 45 45 54 53 0D 54 4F 52 4F 4E 54 4F 2C .SMEETS.TORONTO, 00D70: 43 41 4E 41 44 41 .. .. .. .. .. .. .. .. .. .. CANADA.. Approximately 3721 bytes (usually address $0E89) in the file you will find the beginning of the LHA archive, denoted by two bytes and the signature '-LH1-'. 00E80: .. .. .. .. .. .. .. .. .. 1E B6 2D 6C 68 31 2D ...........-LH1- *ALL* LHA archives have this type of signature string in them ('-lhx-', where x is the type of compression used, with C64 archives being 1, and most PC archives being 5). The actual archive starts 2 bytes before this string is found, and goes to the end of the file. If you want to convert them to LHA, you can chop the front end off the file (up to two bytes before the -lhx- string), and rename it to an LHA. It will then be a normal LHA file. Another way to handle SFX's is to extract them with StarLHA. This program handles both LHA and SFX formats, as they are basically the same. Extract them as you would an LHA file (StarLHA -x xxxxxxxx.SFX). --------------------------------------------------------------------------- *** SDA The name stands for "Self-Dissolving Archive", and thats exactly what it does. There is a decompression engine at the beginning of the file called by a BASIC SYS command. It will decompress all the files contained in the archive to whatever device you specify, or disk you select. There are several different SDA formats around, but with their own decompression engine attached, you would never know unless you analyzed the file header. The HEX dump below is from one of the more commonly seen SDA formats. 00000: 01 08 0D 08 0D 00 9E 28 32 30 36 33 29 00 00 00 which decodes to... 13 SYS(2063) Later on in the header, we have the only displayable text, showing the compression types employed, such as Store, Squeeze, Squash, Pack, Crunch. 00250: .. .. .. .. .. .. .. .. 53 54 4F D2 50 41 43 CB ........STORPACK 00260: 53 51 55 45 45 DA 43 52 55 4E 43 C8 53 51 55 41 SQUEEZCRUNCHSQUA 00270: 53 C8 43 52 55 4E 43 C8 20 64 0E 0D 55 4E 2D 00 SHCRUNCH....UN-. 00280: A0 00 AE 6C 03 CA 30 0B B9 57 0A 30 03 C8 D0 F8 ................ 00290: C8 D0 F2 B9 57 0A 48 29 7F 20 CC 09 C8 68 10 F3 ................ 002A0: 20 64 0E 49 4E 47 2E 2E 2E .. .. .. .. .. .. .. ...ING... It does not appear to be in LHA format, but uses its own compression method, and I have not analyzed the decompression program to see what type of compression is used. The only way that I know of to decompress these files is by running it on either a real C64 or an emulator window, and let the file undo itself to a disk image. --------------------------------------------------------------------------- *** ZIP (created on a C64/C128, not PC) These are PKZIP 1.1-compatible archives, using the older IMPLODE algorithm, which are decompressible on the C64/C128 using various utilities. All versions of PKUNZIP (and compatible programs) will also handle the older archives. They always start with the 'PK' string at the beginning of the file, and the first filename follows very closely. This archive is a compressed 4-file ZipCode. 00000: 50 4B 03 04 14 00 00 00 08 00 00 00 00 00 19 A1 PK.............. 00010: EB 0D C2 45 00 00 50 69 00 00 07 00 00 00 31 21 ..............1! 00020: 4D 53 48 4F 57 E4 BC 7B 5C 53 47 DA 38 3E 67 CE MSHOW........... There is not a lot of visible difference between the 1.1 files and the newer versions, as the beginning of the file looks very similar. --------------------------------------------------------------------------- *** CPK This is a format created by Andre Fachat several years ago, but was not designed for the emulators specifically. It is a very basic format using a simple compression technique (RLE) with each file following in sequential order (as Andre put it, "its similar to a TAR file"). Since there is no central directory, nothing is byte aligned, and it uses compression, every file will be different. 00000: 01 40 41 2E 41 4E 4C 2C 50 00 01 08 24 08 64 00 00010: 99 22 93 20 20 20 41 4E 4C 45 49 54 55 4E 47 20 00020: 5A 55 4D 20 40 41 53 53 45 4D 42 4C 45 52 00 4E 00030: 08 6E 00 99 22 11 40 41 53 53 20 49 53 54 20 45 00040: 49 4E 20 32 2D 50 41 53 53 2D 41 53 53 45 4D 42 The first byte of the file is the version byte. Presently, only $01 is supported. 00000: 01 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. The filename follows, stored in standard PETASCII, no padding characters ($A0) are included. 00000: .. 40 41 2E 41 4E 4C .. .. .. .. .. .. .. .. .. .@A.ANL......... The filetype is attached to the end of the filename in the form of ',x', where x is the filetype used (P,S,U), and it is in PETASCII upper case. The filename ends with a $00 (null terminated). REL files are *not* supported as there is no provision made for the RECORD size byte. 00000: .. .. .. .. .. .. .. 2C 50 00 .. .. .. .. .. .. .......,P....... Following the filename, we get program data. 00000: .. .. .. .. .. .. .. .. .. .. 01 08 24 08 64 00 00010: 99 22 93 20 20 20 41 4E 4C 45 49 54 55 4E 47 20 ... 00270: 00 83 0A E6 00 99 22 11 20 20 31 32 33 F7 08 20 <- F7 string here 00280: 2D 44 45 5A 49 4D 41 4C 00 A4 0A F0 00 99 22 11 00290: 20 20 24 33 34 35 F7 07 20 2D 48 45 58 41 44 45 <- F7 string here The data requires some explanation as it uses RLE compression. When creating the archives, data in the file to be compressed is scanned for runs of repeating bytes, and when a string of 3 or more (up to 255) is found, then the following sequence of bytes is output... $F7 $xx $yy - where F7 is the code used for "encoded sequence follows", $xx is the number of times to repeat the byte and $yy is the byte to repeat. Using the sample below, we see the F7 code, then a "repeat 7 times the number $20" 00290: .. .. .. .. .. .. F7 07 20 .. .. .. .. .. .. .. <- F7 string here Using $F7 as the encoder byte presents one problem: When encoding a file, and we encounter an $F7, what does the packer do? Simple, it gets encoded into $F7 $xx $F7 meaning repeat $F7 for as many times as is needed (if its only 1 $F7, then the value for $xx is $01). The code 'F7' was chosen because it is not a 6502 opcode, a BASIC token, or any commonly used byte, but *not* because it has the least statistical probability of occuring. The stored program ends when the string $F7 $00 is encountered, since this sequence can not occur in the file naturally. If you need to search through a CPK file for the filenames, do a hex search for all $F7 $00 sequences, since they preceed all filenames except the first. The end of the archive is known two different ways: 1. When an EOF (end of file) occurs, after an $F7 $00 byte sequence. This is the normal method. 2. When a filename of $00 occurs, meaning there is no filename, just a null termination. This is not much used anymore. Using method #1 for ending the file is more common because it makes adding files to the archive very easy. All you have to do as append the new filename/data to the archive. Using method #2 means you have to check and see if the last three characters are $F7 $00 $00, and start writing the new file into the archive starting at the second $00. In order to extract *one* specific file, you would need to read the whole archive until you find the filename you want, then output that file only. As this format has no central directory and no file location references, there is no other way to do it. This format has not been used for some time now, as when it came out D64 and T64 were also being developed and used. It is unlikely you will find *any* files in this format. --------------------------------------------------------------------------- *** CKIT This is a relatively recent program which does whole-disk archiving of disks from the various Commodore drives (1541/1571/1581). The latest version is "CKIT-94", meaning it was released in 1994, which would imply that there were earlier versions. Of the samples I have seen, I am (so far) unable to determine what compression is used. The data is so different from the original disks that it would seem to be encrypted. One possible answer is that the disks are actually compressed copies of the GCR info, but that is strictly conjecture. As the decompressor program is ~20 kbytes large, it will take some effort to disassemble the code and reverse engineer the archive files. The file extension for single disk files (i.e. when a compressed disk image only takes up one archive file) is "C4-". If an compressed image takes more than 1 file, the extension changes to "C4A" for the first and "C`A" for the second. I do not know what the extensions will be for compressed files taking more than two images. The header of the compressed file always starts with the string "MMS " (with a space and $00 null termination) which stands for "Mad Man Software", the company which makes (or made) the product. 0000: 4D 4D 53 20 00 58 58 21 90 89 A0 00 60 00 04 00 MMS............. 0010: 00 29 5A EC 94 03 40 06 20 2F BF D5 20 B4 68 81 ................ 0020: A0 8D 10 B4 10 AF 15 03 61 08 07 50 00 10 80 68 ................ 0030: 8A 88 08 91 03 32 1A 0F B7 30 1D 94 D0 7B 28 80 ................ Byte: $0000-0004 - ASCII string "MMS " with null termination. 0005-???? - Compressed data More (any?) information on this format would be greatly appreciated. --------------------------------------------------------------------------- This document was compiled by Peter Schepers in an attempt to unify all the other smaller files dealing with file types that are floating about the net, or that exist with other programs. It is by no means exhaustive (even though it looks like it!), but I will attempt to keep it up-to-date, and correct anything which is wrong. Use it, pass it around, upload it, whatever. Just be sure to leave it INTACT, don't remove bits of it. The following people have helped (in one way or another) with the details, compilation, source code, and other aspects of this document (whether they know it or not!): Immers/Neufeld - 1541 disk layout (from "Inside Commodore DOS") Joe Forster/STA - FilePacked ZipCodes, document corrections Jouko Valta - X64 header (first 64 bytes) Marko Makela - LNX format Andreas Varga - GEOS file info Miha Peternel - Basic T64 layout, FRZ (C64s version <= 2.0) file info Wolfgang Lorenz - Conversion algorithm for PC64 files, .C64 file info Andre Fachat - CPK layout A very special thank you goes to *Paul David Doherty* for his extensive (what an understatement!) contributions regarding the DiskPacked ZipCode format, both 4 and 6 file versions, much of the GEOS file layout, some ideas/samples for CKIT, and proof-reading this entire document! Without his help, all three file format areas would rather sparse, and there would be lots of silly spelling mistakes as well. Plenty of information can also be gleaned from the source code contained in the archive CBMConvert 1.1, which is on the FTP.FUNET.FI FTP site. Contained in it are the sources for UnZipCode, UnLNX, Ark, some LHA info, etc. It is an invaluable set of utilities put together by both Marko Makela and Paul Doherty. --------------------------------------------------------------------------- BINARY/HEX/DECIMAL conversion chart Binary HEX Dec Binary HEX Dec Binary HEX Dec Binary HEX Dec ---------------- ---------------- ---------------- ---------------- 00000000 $00 0 01000000 $40 64 10000000 $80 128 11000000 $C0 192 00000001 01 1 01000001 41 65 10000001 81 129 11000001 C1 193 00000010 02 2 01000010 42 66 10000010 82 130 11000010 C2 194 00000011 03 3 01000011 43 67 10000011 83 131 11000011 C3 195 00000100 04 4 01000100 44 68 10000100 84 132 11000100 C4 196 00000101 05 5 01000101 45 69 10000101 85 133 11000101 C5 197 00000110 06 6 01000110 46 70 10000110 86 134 11000110 C6 198 00000111 07 7 01000111 47 71 10000111 87 135 11000111 C7 199 00001000 08 8 01001000 48 72 10001000 88 136 11001000 C8 200 00001001 09 9 01001001 49 73 10001001 89 137 11001001 C9 201 00001010 0A 10 01001010 4A 74 10001010 8A 138 11001010 CA 202 00001011 0B 11 01001011 4B 75 10001011 8B 139 11001011 CB 203 00001100 0C 12 01001100 4C 76 10001100 8C 140 11001100 CC 204 00001101 0D 13 01001101 4D 77 10001101 8D 141 11001101 CD 205 00001110 0E 14 01001110 4E 78 10001110 8E 142 11001110 CE 206 00001111 0F 15 01001111 4F 79 10001111 8F 143 11001111 CF 207 00010000 10 16 01010000 50 80 10010000 90 144 11010000 D0 208 00010001 11 17 01010001 51 81 10010001 91 145 11010001 D1 209 00010010 12 18 01010010 52 82 10010010 92 146 11010010 D2 210 00010011 13 19 01010011 53 83 10010011 93 147 11010011 D3 211 00010100 14 20 01010100 54 84 10010100 94 148 11010100 D4 212 00010101 15 21 01010101 55 85 10010101 95 149 11010101 D5 213 00010110 16 22 01010110 56 86 10010110 96 150 11010110 D6 214 00010111 17 23 01010111 57 87 10010111 97 151 11010111 D7 215 00011000 18 24 01011000 58 88 10011000 98 152 11011000 D8 216 00011001 19 25 01011001 59 89 10011001 99 153 11011001 D9 217 00011010 1A 26 01011010 5A 90 10011010 9A 154 11011010 DA 218 00011011 1B 27 01011011 5B 91 10011011 9B 155 11011011 DB 219 00011100 1C 28 01011100 5C 92 10011100 9C 156 11011100 DC 220 00011101 1D 29 01011101 5D 93 10011101 9D 157 11011101 DD 221 00011110 1E 30 01011110 5E 94 10011110 9E 158 11011110 DE 222 00011111 1F 31 01011111 5F 95 10011111 9F 159 11011111 DF 223 00100000 20 32 01100000 60 96 10100000 A0 160 11100000 E0 224 00100001 21 33 01100001 61 97 10100001 A1 161 11100001 E1 225 00100010 22 34 01100010 62 98 10100010 A2 162 11100010 E2 226 00100011 23 35 01100011 63 99 10100011 A3 163 11100011 E3 227 00100100 24 36 01100100 64 100 10100100 A4 164 11100100 E4 228 00100101 25 37 01100101 65 101 10100101 A5 165 11100101 E5 229 00100110 26 38 01100110 66 102 10100110 A6 166 11100110 E6 230 00100111 27 39 01100111 67 103 10100111 A7 167 11100111 E7 231 00101000 28 40 01101000 68 104 10101000 A8 168 11101000 E8 232 00101001 29 41 01101001 69 105 10101001 A9 169 11101001 E9 233 00101010 2A 42 01101010 6A 106 10101010 AA 170 11101010 EA 234 00101011 2B 43 01101011 6B 107 10101011 AB 171 11101011 EB 235 00101100 2C 44 01101100 6C 108 10101100 AC 172 11101100 EC 236 00101101 2D 45 01101101 6D 109 10101101 AD 173 11101101 ED 237 00101110 2E 46 01101110 6E 110 10101110 AE 174 11101110 EE 238 00101111 2F 47 01101111 6F 111 10101111 AF 175 11101111 EF 239 00110000 30 48 01110000 70 112 10110000 B0 176 11110000 F0 240 00110001 31 49 01110001 71 113 10110001 B1 177 11110001 F1 241 00110010 32 50 01110010 72 114 10110010 B2 178 11110010 F2 242 00110011 33 51 01110011 73 115 10110011 B3 179 11110011 F3 243 00110100 34 52 01110100 74 116 10110100 B4 180 11110100 F4 244 00110101 35 52 01110101 75 117 10110101 B5 181 11110101 F5 245 00110110 36 54 01110110 76 118 10110110 B6 182 11110110 F6 246 00110111 37 55 01110111 77 119 10110111 B7 183 11110111 F7 247 00111000 38 56 01111000 78 120 10111000 B8 184 11111000 F8 248 00111001 39 57 01111001 79 121 10111001 B9 185 11111001 F9 249 00111010 3A 58 01111010 7A 122 10111010 BA 186 11111010 FA 250 00111011 3B 59 01111011 7B 123 10111011 BB 187 11111011 FB 251 00111100 3C 60 01111100 7C 124 10111100 BC 188 11111100 FC 252 00111101 3D 61 01111101 7D 125 10111101 BD 189 11111101 FD 253 00111110 3E 62 01111110 7E 126 10111110 BE 190 11111110 FE 254 00111111 3F 63 01111111 7F 127 10111111 BF 191 11111111 FF 255