This document provides a detailed description of the copy protection sector found on the Dungeon Master for Apple IIGS floppy disks.
You may get an overview of the copy protection, including on other platforms, on Copy Protection.
All versions of Dungeon Master for Apple IIGS were studied:
Note that Dungeon Master Demo version 1.4 is not copy protected.
This page is based on the study of some .a2r disk images created with Applesauce that are available online:
Because of the duplicate images, only the 3 disk images marked with an * were used for this document.
Please read Basics of data storage on floppy disks if you are not already familiar with the way data is stored on floppy disks because this is required to understand how the copy protection works.
Floppy disks on Apple IIGS do not use the MFM encoding that most computers of that time were using. Instead it uses an encoding named ‘Group Coded Recording 6 and 2’ (GCR). This encoding ensures that there are never more than two ‘0’ bits in a row stored on the floppy disk. The encoding and decoding processes are called nibblizing and denibblizing, and they both rely on a translation table between 6 bits of user data and 8 bits of disk data. Both are explained in detail in chapters 1.3.1.3 and 1.3.1.4 of the uPD72070 Floppy Disk Controller datasheet (note that this is not the FDC used in the Apple IIGS, but it is compatible with the GCR encoding).
A reference 6-and-2 encoding table is also available here
The FDC needs a clock so that it knows the rate at which it should output data bits interpreted from the electrical signal it receives from the floppy disk drive. For this purpose, it uses an ‘inspection window’ which is a time frame during which the FDC waits for an electrical pulse (a flux reversal) to occur.
In the ideal situation, each bit of data represents a 2µs time frame. Because of the ’no more than two consecutive 0 bits’ rule, only three patterns can occur after a ‘1’ bit (a flux reversal): ‘1’, ‘01’ and ‘001’, which respectively represent 2µs, 4µs and 6µs time frames. The FDC resets its inspection window after each flux reversal and then waits for the next one. When the next flux reversal occurs, the FDC outputs the data bits corresponding to the time elapsed since the previous reversal (with some tolerance to account for speed variations between different disk drives and also between each rotation on the same drive).
When the FDC starts reading bits from the disk, it will start at the random location on the track that the head is above, which may be right in the middle of a byte. Here is how byte boundaries are found in the large stream of bits that is read from the disk:
These four special ten bit bytes force the FDC to align itself on byte boundaries because of the rule above: if 8 bits are read from the disk but a ‘0’ bit is in the most significant bit of the read register, then reading continues and that ‘0’ bit is ignored, which causes the FDC to skip a bit in the stream. After four such special self-synchronization bytes, the FDC is guaranteed to be synchronized. For more information about self-synchronization bytes, you can read pages 3-8 to 3-11 of ‘Beneath Apple ProDOS’
All sector fields start with self-synchronization bytes to make sure the address and data prologues (and the following data) are read correctly. When a sector is written to the disk, the sector address field is not overwritten, but the whole sector data field is overwritten, including the preceding self-synchronization bytes.
When a floppy disk is formatted, each track is written at once. The design ensures that the last bytes written on a track will wrap around and overwrite some the first bytes that were written on the same track, so that no part of the track is left unformatted. For that reason, the first bytes written are not user data bytes: they form what is called a gap and gaps only contain self-synchronization bytes.
Each track is structured as a series of sectors separated by gaps. The first gap (Gap 1) is longer than the others (Gap 3) for the reason above. Each sector has two parts, an address field and a data field which are also separated by a gap (Gap 2).
Gaps provide some tolerance for the start and end locations of individual sectors: when an individual sector is written to the disk after it was formatted, the start and end locations of the written sector cannot be perfectly aligned with the previous sector content because of imprecision and small rotation speed differences. Gaps are also useful to give enough time to the FDC and CPU to process a sector address field before reading the following sector data field starts.
Gap 1 consists of up to 128 self-synchronization bytes (between first and last sector stored on the track). The purpose of this long gap is to make sure that the end of the last sector data field on the track will overwrite the beginning of this gap, ensuring the track is fully used and taking into account speed variations from drive to drive.
A regular sector has the following structure (these details are useful for comparison with the copy protection sector). This example is sector 5 on track 0 side 1 which is physically located right before the copy protected sector:
On this screenshot, the sector data is highlighted on the scatter graph on the top and shows only regular timing values of 2µs, 4µs and 6µs.$00 00tttttt
Low order 6 bits of track number = 0$05 00ssssss
Sector number = 5 (= logical block 17 because it is on side 1 and track 0 on side 0 contains 12 sectors, 12 + 5 = 17)$20 00nttttt
n is side 1 (0=side 0, 1=side 1). ttttt are the high-order 5 bits of track number = 0$22 00h0iiii
h is number of sides = double-sided (0=single, 1=double). iiii is the interleave = 2$07 00cccccc
Checksum of 4 previous bytes using exclusive OR: ((($00 XOR $05) XOR $20) XOR $22) = $07Note: Sector prologues use $D5 and $AA bytes to mark their beginning. These are reserved codes that are not part of the GCR 6 and 2 encoding table.
There are many possible floppy disk formats that use varying numbers of tracks, sectors per track and sector sizes. The format of a floppy disk is defined when the disk is formatted. When a floppy disk is formatted, each track is written at once (not sector by sector), thus defining the number of sectors and their size, the size of gaps, etc. This operation is called low-level formatting. High-level formatting means installing a file system on the floppy disk. Usually low and high level formatting are performed together.
The standard Apple IIGS floppy disk format is the Apple double-density, double-sided 3.5" floppy disk format that was also used on Macintosh computers. The file system is different though: ProDOS on Apple IIGS, MFS or HFS on Macintosh.
The format has 80 tracks per side (numbered 0 to 79), 8 to 12 sectors per track (5 zones of 16 tracks each) and 512 bytes of data per sector.
In order to maximize capacity, there are more sectors on the outer tracks than on the inner tracks, and the drive has a different rotation speed in each zone:
Tracks | Sectors Per Track | Revolutions Per Minute |
---|---|---|
00 to 15 | 12 | 394 |
16 to 31 | 11 | 429 |
32 to 47 | 10 | 472 |
48 to 63 | 9 | 525 |
64 to 79 | 8 | 590 |
The storage capacity is: 2 sides x 16 tracks x (8 + 9 + 10 + 11 + 12) sectors per track x 512 bytes per sector = 819200 Bytes = 800 KB.
Sectors are not physically stored in their logical order, they are ‘interleaved’. For example on track 0, sectors 0 to 11 are physically stored in the following order on the circular track: 0 6 1 7 2 8 3 9 4 10 5 11.
All versions of Dungeon Master for Apple IIGS use the standard floppy disk format.
Track 0 on side 0 contains a standard Apple IIGS boot sector and file system.
There is one special sector for the copy protection: logical block $17 = 23, physical sector 11 on track 0, side 1. This is the last on the track, meaning that Gap1 (the largest) follows it and then there is sector 0. This sector is part of the file named BAD.BLOCKS in the file system.
The remaining sectors in track 0 and all sectors in other tracks are used to store the game’s files. These other sectors will not be described further in this document as they are completely standard and just contain the game’s program and data files.
The copy protection sector contains fuzzy bits: bits of raw data that have a random value (0 or 1) when read multiple times.
This sector cannot be read as a normal sector: The game contains a special routine to read the raw GCR encoded data from the disk and never tries to decode this data. The copy protection checks are performed directly on the raw data.
This sector cannot be copied with the Apple IIGS hardware because:
$00 00tttttt
Low order 6 bits of track number = 0$0B 00ssssss
Sector number = 11 (= logical block 23 because it is on side 1 and track 0 on side 0 contains 12 sectors, 12 + 11 = 23)$20 00nttttt
n is side 1 (0=side 0, 1=side 1). ttttt are the high-order 5 bits of track number = 0$22 00h0iiii
h is number of sides = double-sided (0=single, 1=double). iiii is the interleave = 2$09 00cccccc
Checksum of 4 previous bytes using exclusive OR: ((($00 XOR $0B) XOR $20) XOR $22) = $09Sector data field prologue (3 bytes): $D5 $AA $AD. These values are checked by the copy protection code to identify the beginning of the sector data.
6 bytes: Date and serial number. These bytes are never checked by the game. They have different values on each copy of the game so this is similar to the serial number used in the Atari ST / Amiga copy protected sector (see Detailed analysis of Atari ST Floppy Disks of Dungeon Master and Chaos Strikes Back).
Here are the values of these bytes in the copies that were studied:
If the sector was a regular sector, the first byte should contain the sector number (11 = $0B encoded as $AD using GCR in 6 and 2). As its value does not match the sector number in the preceding sector address field, any read attempt using the sector number will fail.
4 bytes $FF $FF $FF $FF. Checked by the copy protection code.
690 bytes: 345 pairs of $B2 $EF bytes, divided in 11 series of 32 bytes, except the first that is only 31 bytes and the last that is only 26 bytes (The same series are found in all of the three copies that were studied): in each series, each $B2 byte contains one flux reversal that is slightly shifted compared to the preceding $B2 byte. The timing of this reversal goes from one normal timing to another normal timing but some of the intermediary values are guaranteed to be ambiguous and cause the fuzzy bits effect. The resulting value read by the FDC is either $B2, $A9(9) or $AA. The values of all these bytes are checked by the copy protection code (alternating values of $EF and one of the three possible values of $B2, $A9, and $AA).
The animation below (from Dungeon Master - Disk 1 - sdancer_a2rs.a2r) shows the flux reversal shifting from left to right (32 images from the second series, captured from Applesauce software). Other reversals also move slightly on the animation but only within the margin of tolerance so the corresponding decoded data does not change.
The reversal in the center of the animation slowly moves from left to right, and the byte value above shows how the FDC would decode it:
Because of small fluctuations in the disk rotation speed, and the spreading of the flux reversal timings, there are always some ambiguous flux reversals that cause two readings to contain different bit values, causing the fuzzy bits effect that is checked by the copy protection code.
Each byte containing a fuzzy bit produces one of three possible values after being read by the FDC. Note that the first three bits and the last three bits are always the same:
Byte | Bits | Description |
---|---|---|
$B2 | 101 10 010 | This is the starting value in each series, the byte value when a normal timing is used (within the tolerance margin) between the two flux reversals for the two adjacent ‘1’ bits. |
$A9(9) | 101 010 010 | When the time between the two ‘1’ bits is large enough, an additional ‘0’ bit is detected by the FDC between the two ‘1’ bits, yet the following two ‘0’ bits are still detected, so that 9 bits are actually read by the FDC, giving the value A9(9). The last 0 bit is ignored and skipped by the FDC when reading the next byte, because the most significant bit of a byte cannot be a ‘0’. So this additional bit has no effect on the FDC synchronization. |
$AA | 101 01 010 | When the time between the two ‘1’ bits has increased even more, there is not enough time to detect the following two ‘0’ bits anymore. Only one is detected and the read value then becomes $AA. |
4 bytes $FF $FF $FF $FF. Checked by the copy protection code.
Sector data field epilogue (2 bytes): $DE $AA. These values are checked by the copy protection code to identify the end of the sector data.
The following screenshot compares the Gap 2 content of the copy protection sector 11 (between the sector address field and the sector data field):
Note that each copy of the game has a different gap content. Also note that the same flux reversal pattern is written on all three copies, but starting at different locations.Sector | Gap 2 length |
---|---|
Regular sector | 6 |
Copy protection sector in Dungeon Master - Disk 1 - sdancer_a2rs.a2r | 8 |
Copy protection sector in Dungeon Master - Disk 1 - copy 2 - sdancer_a2rs.a2r | 8 |
Copy protection sector in Dungeon Master - Disk 1 - copy 2 - Antoine Vignau.a2r | 9 |
Similar differences are visible at the end of the sectors where the write stopped right after the $DE $AA epilogue. The first few bytes afterwards are not decoded as $FF because the write stopped in the middle of what was previously written on the disk. After at most four $FF(10) synchronization bytes, the decoding is fine again (and this is exactly the purpose of the synchronization bytes).
All other gaps in the studied disk images are very ‘clean’ (and identical for all sectors of all copies) except the two gaps before and after the copy protection sector data field, which are also a little different on each copy. This suggests that the disks were produced in two steps: