mirror of
https://github.com/aaru-dps/docs.git
synced 2025-12-16 19:24:38 +00:00
474 lines
26 KiB
Plaintext
474 lines
26 KiB
Plaintext
|
||
*** G64 (raw GCR binary representation of a 1541 diskette)
|
||
*** Document revision: 1.9
|
||
*** Last updated: Feb 19, 2008
|
||
*** Compiler/Editor: Peter Schepers
|
||
*** Contributors/sources: Markus Brenner,
|
||
Immers/Neufeld "Inside Commodore DOS",
|
||
Wolfgang Moser
|
||
|
||
This format was defined in 1998 as a cooperative effort between several
|
||
emulator people, mainly Per Hakan Sundell (author of the CCS64 C64
|
||
emulator), Andreas Boose (of the VICE CBM emulator team) and Joe
|
||
Forster/STA (the author of Star Commander). It was the first real
|
||
cooperative attempt to create a format for the emulator community which
|
||
removed almost all of the drawbacks of the other existing image formats,
|
||
primarily D64. The G64 format is not specifically designed to hold only
|
||
1541 images, but they are presently the only G64 images in existance and
|
||
why this document only refers to the 1541 and D64's.
|
||
|
||
The intention behind G64 is not to replace the widely used D64 format, as
|
||
D64 works fine with the vast majority of disks in existence. It is intended
|
||
for those small percentage of programs which demand to work with the 1541
|
||
drive in a non-standard way, such as reading or writing data in a custom
|
||
format. The best example is with speeder software such as Action Cartridge
|
||
in "warp save" mode or Vorpal and V-MAX which write track/sector data in
|
||
another format other than standard GCR. The other obvious example is
|
||
copy-protected software which looks for some specific data on a track, like
|
||
the disk ID, which is not stored in a standard D64 image.
|
||
|
||
One protection method that G64 has trouble emulating is data alignment
|
||
between tracks. Some protection methods rely on data being in exact
|
||
positions when the head is stepped from one track to another. Imagine two
|
||
concentric circles representing the data tracks, with a drive head reading
|
||
data from one track, stepping over to the other track and expecting to find
|
||
some specific data where it is now. Unless you can read track data from a
|
||
1541 so it is aligned with the previous track, write it into the G64
|
||
appropriately, and also read the resulting G64 data with this alignment in
|
||
mind, the protection check will likely fail. Other methods like weak bits
|
||
are also hard to emulate.
|
||
|
||
G64 has a deceptively simply layout for what it is capable of doing. We
|
||
have a signature, version byte, some predefined size values, and a series
|
||
of offsets to the track data and speed zones. It is what's contained in the
|
||
track data areas and speed zones which is really at the heart of this
|
||
format.
|
||
|
||
Each track data area is simply the raw stream of GCR data, just what the
|
||
read head would see when a diskette is rotating past it. How the data gets
|
||
interpreted is up to the program trying to access the disk. Because the
|
||
data is stored in such a low-level manner, just about anything can be done.
|
||
Most tracks will be in the standard format with with SYNC markers, GAP,
|
||
header, data blocks and checksums. The arrangement of the data when it is
|
||
in a standard GCR sector layout is covered at the end of this document. It
|
||
is the tracks that don't follow the standard which are the reason for G64's
|
||
existance and the hardest to decode.
|
||
|
||
Below is a dump of the header, broken down into its various parts.
|
||
Following that is a breakdown of the track offset and speed zone offset
|
||
areas, as they demand much more explanation.
|
||
|
||
|
||
Addr 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
|
||
---- ----------------------------------------------- ----------------
|
||
0000: 47 43 52 2D 31 35 34 31 00 54 F8 1E .. .. .. .. GCR-1541<34>T<EFBFBD><54>....
|
||
|
||
Bytes: $0000-0007: File signature "GCR-1541"
|
||
0008: G64 version (presently only $00 defined)
|
||
0009: Number of tracks in image (usually $54, decimal 84)
|
||
000A-000B: Maximum track size in bytes in LO/HI format
|
||
|
||
Now, why are there 84 tracks defined when a normal D64 disk only has 35
|
||
tracks? By definition, an image of a 1541 must include all the tracks that
|
||
a real 1541 can access, which is at most 42 tracks and 42 half tracks. Even
|
||
though using more than 35 tracks is not typical, it was important to define
|
||
this format from the start with what the 1541 is capable of doing, and not
|
||
just what it typically does. Some 1541 drives may have problems reading
|
||
past track 40, and pushing the head past track 42 might be somewhat
|
||
hazardous to the health of the drive as the head could get stuck.
|
||
|
||
The typical value seen for the maximum track size is 7928. This is the
|
||
value used for 1541 images which use standard GCR encoding. This value is
|
||
determined by the fastest write speed possible (speed zone 0), coupled with
|
||
the average rotation speed of the disk (300 rpm), and assuming normal
|
||
Commodore GCR data formatting. After some math, the answer that actually
|
||
comes up is 7692 bytes. Allowing for a slower disk rotation of -3%, , which
|
||
would allow more data to be written, and some rounding, 7928 bytes per
|
||
track was arrived at.
|
||
|
||
Even though it might appear so, it is very important to know that this
|
||
maximum track size value is not a fixed or hard-coded value. This value
|
||
depends on the what the original disk was and the GCR encoding used.
|
||
Non-1541 images such as SFD1001 or 8050 will result in different, likely
|
||
larger, track sizes. Also, disks with non-standard GCR encoding like those
|
||
using V-MAX can result in tracks exceeding 8000 bytes.
|
||
|
||
Since it is a flexible format in both track count and track byte size,
|
||
file sizes can vary greatly. However, given a few constants like 42 tracks
|
||
with no halftracks, a consistent track size of 7928 bytes and no speed
|
||
offset entries, the typical file size will be 333744 bytes.
|
||
|
||
In my investigation using MNIB (a utility by Markus Brenner that allows
|
||
you to nibble a 1541 diskette to the PC in G64 format) on a cleanly
|
||
formatted 1541 disk (using the built-in 1541 format command), I saw the
|
||
following numbers, compared with the defaults that MNIB uses:
|
||
|
||
Track Range Avg Size MNIB
|
||
(bytes) Size
|
||
----------- -------- ----
|
||
1-17 ~7720 7692
|
||
18-24 ~7165 7142
|
||
25-30 ~6690 6666
|
||
31- ~6270 6250
|
||
|
||
Note that the first size number (7720) is larger than previously
|
||
mentioned track size of 7692. Why? Likely the drive that I used to create
|
||
and nibble the clean disk was rotating a little bit slower than 300 RPM
|
||
(~299 RPM), so more data than normal was stored on each track. I calculated
|
||
the percentage difference between my numbers and the established benchmark
|
||
of 7692, multiplied all my numbers by this factor, and arrived at the
|
||
following chart:
|
||
|
||
Track Range Size MNIB
|
||
(bytes) Size
|
||
----------- ------- ----
|
||
1-17 7692 7692
|
||
18-24 7139 7142
|
||
25-30 6666 6666
|
||
31- 6247 6250
|
||
|
||
See how close the real numbers come to what MNIB uses? I can attribute
|
||
the differences of a few bytes to my own rounding errors. Therefore I
|
||
conclude that the numbers MNIB uses can be taken as the standard that all
|
||
1541-compatible G64 tracks should be created with.
|
||
|
||
All of the above calculations are shown here to establish a safe
|
||
benchmark to create G64 images in the event that someday we can copy them
|
||
back to a real 1541 disk. If the G64 track size was too large, it might
|
||
happen that the track cannot be written back out. By using the above MNIB
|
||
track size numbers, this problem should be alleviated.
|
||
|
||
Below is a dump of the first section of a G64 file, showing the offsets
|
||
to the data portion for each track and half-track entry.
|
||
|
||
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
|
||
----------------------------------------------- ----------------
|
||
0000: .. .. .. .. .. .. .. .. .. .. .. .. AC 02 00 00 ............<2E><><EFBFBD><EFBFBD>
|
||
0010: 00 00 00 00 A6 21 00 00 00 00 00 00 A0 40 00 00 <20><><EFBFBD><EFBFBD><EFBFBD>!<21><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@<40><>
|
||
0020: 00 00 00 00 9A 5F 00 00 00 00 00 00 94 7E 00 00 <20><><EFBFBD><EFBFBD><EFBFBD>_<EFBFBD><5F><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>~<7E><>
|
||
0030: 00 00 00 00 8E 9D 00 00 00 00 00 00 88 BC 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0040: 00 00 00 00 82 DB 00 00 00 00 00 00 7C FA 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>|<7C><><EFBFBD>
|
||
0050: 00 00 00 00 76 19 01 00 00 00 00 00 70 38 01 00 <20><><EFBFBD><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>p8<70><38>
|
||
0060: 00 00 00 00 6A 57 01 00 00 00 00 00 64 76 01 00 <20><><EFBFBD><EFBFBD>jW<6A><57><EFBFBD><EFBFBD><EFBFBD><EFBFBD>dv<64><76>
|
||
0070: 00 00 00 00 5E 95 01 00 00 00 00 00 58 B4 01 00 <20><><EFBFBD><EFBFBD>^<5E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>X<EFBFBD><58><EFBFBD>
|
||
0080: 00 00 00 00 52 D3 01 00 00 00 00 00 4C F2 01 00 <20><><EFBFBD><EFBFBD>R<EFBFBD><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>L<EFBFBD><4C><EFBFBD>
|
||
0090: 00 00 00 00 46 11 02 00 00 00 00 00 40 30 02 00 <20><><EFBFBD><EFBFBD>F<EFBFBD><46><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>@0<><30>
|
||
00A0: 00 00 00 00 3A 4F 02 00 00 00 00 00 34 6E 02 00 <20><><EFBFBD><EFBFBD>:O<><4F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>4n<34><6E>
|
||
00B0: 00 00 00 00 2E 8D 02 00 00 00 00 00 28 AC 02 00 <20><><EFBFBD><EFBFBD>.<2E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><><EFBFBD>
|
||
00C0: 00 00 00 00 22 CB 02 00 00 00 00 00 1C EA 02 00 <20><><EFBFBD><EFBFBD>"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
00D0: 00 00 00 00 16 09 03 00 00 00 00 00 10 28 03 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(<28><>
|
||
00E0: 00 00 00 00 0A 47 03 00 00 00 00 00 04 66 03 00 <20><><EFBFBD><EFBFBD><EFBFBD>G<EFBFBD><47><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>f<EFBFBD><66>
|
||
00F0: 00 00 00 00 FE 84 03 00 00 00 00 00 F8 A3 03 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0100: 00 00 00 00 F2 C2 03 00 00 00 00 00 EC E1 03 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0110: 00 00 00 00 E6 00 04 00 00 00 00 00 E0 1F 04 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0120: 00 00 00 00 DA 3E 04 00 00 00 00 00 D4 5D 04 00 <20><><EFBFBD><EFBFBD><EFBFBD>><3E><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>]<5D><>
|
||
0130: 00 00 00 00 CE 7C 04 00 00 00 00 00 C8 9B 04 00 <20><><EFBFBD><EFBFBD><EFBFBD>|<7C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ț<EFBFBD><C89B>
|
||
0140: 00 00 00 00 C2 BA 04 00 00 00 00 00 BC D9 04 00 <20><><EFBFBD><EFBFBD>º<EFBFBD><C2BA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0150: 00 00 00 00 B6 F8 04 00 00 00 00 00 .. .. .. .. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.....
|
||
|
||
Bytes: $000C-000F: Offset to stored track 1.0 ($000002AC, in LO/HI
|
||
format, see below for more)
|
||
0010-0013: Offset to stored track 1.5 ($00000000)
|
||
0014-0017: Offset to stored track 2.0 ($000021A6)
|
||
...
|
||
0154-0157: Offset to stored track 42.0 ($0004F8B6)
|
||
0158-015B: Offset to stored track 42.5 ($00000000)
|
||
|
||
The track offsets rquire some explanation. When one is set to all 0's, no
|
||
track data exists for this entry. If there is a value, it is an absolute
|
||
reference into the file (starting from the beginning of the file).
|
||
|
||
If an image stored here only contains 35 tracks (e.g. a standard 1541
|
||
disk), then all the offset values for track 35.5 and higher will be set to
|
||
0. This can be used to detect the maximum track count when converting to a
|
||
D64 image. Since D64's cannot hold over 40 tracks, and typically only have
|
||
35, some information will be lost when converting a G64.
|
||
|
||
From the track 1.0 entry we see it is set for $000002AC. Going to that
|
||
file offset, here is what we see...
|
||
|
||
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
|
||
----------------------------------------------- ----------------
|
||
02A0: .. .. .. .. .. .. .. .. .. .. .. .. 0C 1E FF FF ............<2E><><EFBFBD><EFBFBD>
|
||
02B0: FF FF FF 52 54 B5 29 4B 7A 5E 95 55 55 55 55 55 <20><><EFBFBD>RT<52>)Kz^<5E>UUUUU
|
||
02C0: 55 55 55 55 55 55 FF FF FF FF FF 55 D4 A5 29 4A UUUUUU<55><55><EFBFBD><EFBFBD><EFBFBD>Uԥ)J
|
||
02D0: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
|
||
Bytes: $02AC-02AD: Actual size of stored track (7692 or $1E0C, in LO/HI
|
||
format)
|
||
02AE-02AE+$1E0C: Track data
|
||
|
||
Following the track data is filler bytes. In this case, there are 368
|
||
bytes of unused space. This space can contain anything, but for the sake of
|
||
those wishing to compress these images for storage, they should all be set
|
||
to the same value. In the sample I used, these are all set to $FF.
|
||
|
||
Below is a dump of the end of the track 1.0 data area. Note the actual
|
||
track data ends at address $20B9, with the rest of the block being unused,
|
||
and set to $FF.
|
||
|
||
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
|
||
----------------------------------------------- ----------------
|
||
1FE0: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
1FF0: 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 <20><>)JR<4A><52>)JR<4A><52>)JR<4A>
|
||
2000: A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 <20>)JR<4A><52>)JR<4A><52>)JR<4A><52>
|
||
2010: 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 )JR<4A><52>)JR<4A><52>)JR<4A><52>)
|
||
2020: 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A JR<4A><52>)JR<4A><52>)JR<4A><52>)J
|
||
2030: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
2040: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
2050: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
2060: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
2070: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
2080: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
2090: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
20A0: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 UUUUUUUUUUUUUUUU
|
||
20B0: 55 55 55 55 55 55 55 55 55 55 FF FF FF FF FF FF UUUUUUUUUU<55><55><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
20C0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
20D0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
20E0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
20F0: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2100: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2110: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2120: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2130: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2140: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2150: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2160: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2170: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2180: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
2190: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
21A0: FF FF FF FF FF FF .. .. .. .. .. .. .. .. .. .. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>..........
|
||
|
||
|
||
Now we can look at the speed zone area. Below is a dump of the speed zone
|
||
offsets.
|
||
|
||
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
|
||
----------------------------------------------- ----------------
|
||
0150: .. .. .. .. .. .. .. .. .. .. .. .. 03 00 00 00 ............<2E><><EFBFBD><EFBFBD>
|
||
0160: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0170: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0180: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0190: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
01A0: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
01B0: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
01C0: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
01D0: 00 00 00 00 03 00 00 00 00 00 00 00 03 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
01E0: 00 00 00 00 02 00 00 00 00 00 00 00 02 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
01F0: 00 00 00 00 02 00 00 00 00 00 00 00 02 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0200: 00 00 00 00 02 00 00 00 00 00 00 00 02 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0210: 00 00 00 00 02 00 00 00 00 00 00 00 01 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0220: 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0230: 00 00 00 00 01 00 00 00 00 00 00 00 01 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0240: 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
0290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
02A0: 00 00 00 00 00 00 00 00 00 00 00 00 .. .. .. .. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>....
|
||
|
||
Bytes: $015C-015F: Speed zone entry for track 1 ($03, in LO/HI format,
|
||
see below for more)
|
||
0160-0163: Speed zone entry for track 1.5 ($03)
|
||
...
|
||
02A4-02A7: Speed zone entry for track 42 ($00)
|
||
02A8-02AB: Speed zone entry for track 42.5 ($00)
|
||
|
||
Starting at $02AC is the first track entry (from above, it is the first
|
||
entry for track 1.0)
|
||
|
||
The speed offset entries can be a little more complex. The 1541 has four
|
||
speed zones defined, which means the drive can write data at four distinct
|
||
speeds. On a normal 1541 disk, these zones are as follows:
|
||
|
||
Track Range Storage in Bytes Speed Zone
|
||
----------- ---------------- ----------
|
||
1-17 7820 3 (slowest writing speed)
|
||
18-24 7170 2
|
||
25-30 6300 1
|
||
31-4x 6020 0 (fastest writing speed)
|
||
|
||
|
||
Note that you can, through custom programming of the 1541, change the
|
||
speed zone of any track to something different (change the 3 to a 0) and
|
||
write data differently.
|
||
|
||
From the above speed zone sample, all the zones use 4-byte entries in
|
||
lo-hi format. If the value of the entry is less than 4, then there is no
|
||
speed offset block for the track and the value is applied to the whole
|
||
track. If the value is greater than 4 then we have an actual file offset
|
||
referencing a speed zone block for the track.
|
||
|
||
In the above example shown, there were no offsets defined, so no speed
|
||
zone block dump can be shown. However, I can define what should be there.
|
||
You will have a block of data, 1982 bytes long. Each byte is encoded to
|
||
represent the speed of 4 bytes in the track offset area, and is broken down
|
||
as follows:
|
||
|
||
Speed entry $FF: in binary %11111111
|
||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
<20> <20> <20> <20>
|
||
<20> <20> <20> <20><> 4'th byte speed (binary 11, 3 dec)
|
||
<20> <20> <20><><EFBFBD><EFBFBD> 3'rd byte speed (binary 11, 3 dec)
|
||
<20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 2'nd byte speed (binary 11, 3 dec)
|
||
<20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 1'st byte speed (binary 11, 3 dec)
|
||
|
||
It was very smart of the designers of the G64 format to allow for two
|
||
speed zone settings, one in the offset block and another defining the speed
|
||
on a per-byte basis. If you are working with a normal disk, where each
|
||
track is one constant speed, then you don't need the extra blocks of
|
||
information hanging around the image, wasting space.
|
||
|
||
|
||
What may not be obvious is the flexibility of this format to add tracks
|
||
and speed offset zones at will. If a program decides to write a track out
|
||
with varying speeds, and no speed offset exist, a new block will be created
|
||
by appending it to the end of the image, and the offset pointer for that
|
||
track set to point to the new block. If a track has no offset yet, meaning
|
||
it doesn't exist (like a half-track), and one needs to be added, the same
|
||
procedure applies. The location of the actual track or speed zone data is
|
||
not important, meaning they do not have to be in linear order since they
|
||
are all referenced by the offsets at the beginning of the image.
|
||
|
||
|
||
|
||
|
||
Analysing the GCR data stream
|
||
-----------------------------
|
||
|
||
Since the information stored in the track data area is in GCR format, it
|
||
is not as simple to analyse as a normal 256-byte sector would be. Here is a
|
||
dump of a portion of the GCR data, and what to look for...
|
||
|
||
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII
|
||
----------------------------------------------- ----------------
|
||
0000: 0C 1E FF FF FF FF FF 52 54 B5 29 4B 7A 5E 95 55 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>RT<52>)Kz^<5E>U
|
||
0010: 55 55 55 55 55 55 55 55 55 55 FF FF FF FF FF 55 UUUUUUUUUU<55><55><EFBFBD><EFBFBD><EFBFBD>U
|
||
0020: D4 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 ԥ)JR<4A><52>)JR<4A><52>)JR<4A>
|
||
0030: A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 <20>)JR<4A><52>)JR<4A><52>)JR<4A><52>
|
||
0040: 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 )JR<4A><52>)JR<4A><52>)JR<4A><52>)
|
||
0050: 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A JR<4A><52>)JR<4A><52>)JR<4A><52>)J
|
||
0060: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
0070: 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 <20><>)JR<4A><52>)JR<4A><52>)JR<4A>
|
||
0080: A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 <20>)JR<4A><52>)JR<4A><52>)JR<4A><52>
|
||
0090: 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 )JR<4A><52>)JR<4A><52>)JR<4A><52>)
|
||
00A0: 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A JR<4A><52>)JR<4A><52>)JR<4A><52>)J
|
||
00B0: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
00C0: 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 <20><>)JR<4A><52>)JR<4A><52>)JR<4A>
|
||
00D0: A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 <20>)JR<4A><52>)JR<4A><52>)JR<4A><52>
|
||
00E0: 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 )JR<4A><52>)JR<4A><52>)JR<4A><52>)
|
||
00F0: 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A JR<4A><52>)JR<4A><52>)JR<4A><52>)J
|
||
0100: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
0110: 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 <20><>)JR<4A><52>)JR<4A><52>)JR<4A>
|
||
0120: A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 <20>)JR<4A><52>)JR<4A><52>)JR<4A><52>
|
||
0130: 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 )JR<4A><52>)JR<4A><52>)JR<4A><52>)
|
||
0140: 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A JR<4A><52>)JR<4A><52>)JR<4A><52>)J
|
||
0150: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
0160: 94 A5 29 4A 55 55 55 55 55 55 FF FF FF FF FF 52 <20><>)JUUUUUU<55><55><EFBFBD><EFBFBD><EFBFBD>R
|
||
0170: 54 A5 2D 4B 7A 5E 95 55 55 55 55 55 55 55 55 55 T<>-Kz^<5E>UUUUUUUUU
|
||
0180: 55 55 FF FF FF FF FF 55 D4 A5 29 4A 52 94 A5 29 UU<55><55><EFBFBD><EFBFBD><EFBFBD>Uԥ)JR<4A><52>)
|
||
0190: 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A JR<4A><52>)JR<4A><52>)JR<4A><52>)J
|
||
01A0: 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 R<><52>)JR<4A><52>)JR<4A><52>)JR
|
||
01B0: 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 <20><>)JR<4A><52>)JR<4A><52>)JR<4A>
|
||
01C0: A5 29 4A 52 94 A5 29 4A 52 94 A5 29 4A 52 94 A5 <20>)JR<4A><52>)JR<4A><52>)JR<4A><52>
|
||
|
||
We need to establish a marker by which one can start to interpret the
|
||
data. Always look for a group of at least 10 1-bits (two 'F's in a row and
|
||
a bit more), as they establish the SYNC mark. The 1541 actually writes out
|
||
a SYNC mark of 40 'on' bits (10 'F's in a row). Note that there are 2
|
||
groups of SYNC marks quite close together, one for the sector header and
|
||
one for the sector data. In the above example, there is 2 groups of "FF FF
|
||
FF FF FF". The first one is the header SYNC and the second one is the data
|
||
SYNC.
|
||
|
||
An important point here: some documentation refers to the minimum SYNC
|
||
mark as being at least 12 bits wide, and claims that one of that size is
|
||
still not entirely reliable. Thus Commodore chose to use 40 bits for the
|
||
SYNC mark, making it impossible for the drive read electronics to miss.
|
||
|
||
If the GCR data is not in the standard sector layout, then anything goes
|
||
for interpreting the data. If no standard SYNC mark can be found, then
|
||
there is no simple way to extract any useful data.
|
||
|
||
|
||
Here's the layout of a standard low-level pattern on a 1541 disk. Use the
|
||
above example to follow along.
|
||
|
||
1. Header sync FF FF FF FF FF (40 'on' bits, not GCR encoded)
|
||
2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes)
|
||
3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read)
|
||
4. Data sync FF FF FF FF FF (40 'on' bits, not GCR encoded)
|
||
5. Data bloc 55...4A (325 GCR bytes)
|
||
6. Tail gap 55 55 55 55...55 55 (4 to 19 bytes, never read)
|
||
1. Header sync (SYNC for the next sector)
|
||
|
||
|
||
The 10 header info bytes (#2) are GCR encoded and must be decoded down to
|
||
it's normal 8 bytes to be understood. Once decoded, its breakdown is as
|
||
follows:
|
||
|
||
Byte $00 - header block ID ($08)
|
||
01 - header block checksum (EOR of $02-$05)
|
||
02 - Sector# of data block
|
||
03 - Track# of data block
|
||
04 - Format ID byte #
|
||
05 - Format ID byte #1
|
||
06-07 - $0F ("off" bytes)
|
||
|
||
The header gap (#3) is 8 bytes on an early model 1540/1541, but 9 bytes
|
||
on a later model 1541 and 4040. The 1541 doesn't read the header gap, but
|
||
simply waits it out to write out the sector data. When sector data is
|
||
written, the SYNC mark is re-written as well.
|
||
|
||
There is some controversy over the header gap (#3). Most people assume it
|
||
to be 9 bytes of 0x55 characters, but the early 1540/1541 drives used only
|
||
8. This caused an write incompatability with the existing 4040 disks of the
|
||
day. In 1541 ROM revision 901225-3 this error was fixed, and now all drives
|
||
write out 9 of the 0x55 characters for the gap. The book "Inside Commodore
|
||
DOS" by Immers/Neufeld documents the write incompatibilty and what
|
||
corruption happens at a low level when writing to a disk with a header gap
|
||
of 8 bytes on a disk that normally expects a gap of 9 bytes.
|
||
|
||
The tail gap (#6) is the unused space between the end of one data block
|
||
and the start of the next. It will vary in size depending on what track you
|
||
are on, how fast the drive that created the disk was rotating at, and what
|
||
program was used to format the disk. The stock 1541 format code is supposed
|
||
to determine how big a track is and divide up the extra unused space into
|
||
each tail gap. However, many disks will show a much larger tail gap between
|
||
the last sector and sector 0. In tests that the author conducted on a real
|
||
1541 disk, gap sizes of 8 to 19 bytes were seen.
|
||
|
||
|
||
The 325 byte data block (#5) is GCR encoded and must be decoded to its
|
||
normal 260 bytes to be understood. For comparison, ZipCode Sixpack uses a
|
||
326 byte GCR sector (why?), but the last byte (when properly rearranged) is
|
||
not used. The data block is made up of the following:
|
||
|
||
Byte $00 - data block ID ($07)
|
||
01-100 - 256 bytes sector data
|
||
101 - data block checksum (EOR of $01-100)
|
||
102-103 - $00 ("off" bytes, to make the sector size a multiple of 5)
|
||
|
||
|
||
The most reliable way to read G64 track data is to read it as bits, not
|
||
bytes as there is no way to be sure that all the data is byte-aligned. This
|
||
simulates the way a 1541 drive reads data as well as the head only reads
|
||
bits as well. The starting location of the track data is know, as well as
|
||
the track size so the boundaries of the track limits (start and end) are
|
||
obtainable.
|
||
|
||
What follows is a very simply point-form list of how to read data,
|
||
finding sync marks, header blocks and sector blocks.
|
||
|
||
1. Search for SYNC (at least 10 or more 1 bits)
|
||
2. Check for header id after SYNC (GCR 0x52)
|
||
3. If header, read the remaining 9 header bytes
|
||
4. Decode header and get sector value
|
||
5. Search for SYNC again
|
||
6. Check for data id after SYNC (GCR 0x55).
|
||
7. If data, read and store with previous header.
|
||
8. Have we finished reading the track... stop
|
||
9. Start over
|
||
|