Some Apple II 5.25" floppies used a special pattern of bytes that could not be automatically detected by disk copying software. How could the pattern be invisible to disk copiers but detectable by the program itself?
The Apple II reads disk tracks as a continuous stream of bits. To make sense of the data, it's necessary to figure out where individual bytes start. This is done with self-sync bytes.
Standard self-sync bytes are
FF, followed by two "invisible" zeroes:
byte 0 ** byte 1 ** byte 2 ** 11111111 00 11111111 00 11111111 00
The Apple II will read bits from the bit stream, sliding them in from right to left, until a
1 lands in the high bit of the 8-bit register. If you start reading from a
1, you will read 7 more bits. If you start reading from a
0, you will read 8 or 9 more bits, because the initial zeroes just slide off the left end of the register. With just a few self-sync bytes, we can ensure that we fall into sync with the byte boundaries.
For example, suppose you started reading at an offset of 4 bits into a self-sync pattern:
The code would read
11110011 for the first byte,
11111100 for the second, and
11111111 for the third -- synchronization achieved. The extra zeroes are read, but just slide off the left end of the register, because we're waiting for a
1 to appear in the high bit. The only way to tell the difference between a self-sync
FF, and a regular
FF, is by how long it takes to read. With one bit arriving every 4 CPU cycles, detecting the difference is tricky.
The trick exploited by the bit-slip technique is to follow the self-sync bytes with a pattern that includes additional zeroes between bytes. For example, consider this stream:
If we latch 8-bit bytes as usual, we'll read it like this:
byte 0 * byte 1 ** byte 2 byte 3 11100111 0 11100111 00 11100111 11100111
Because the "extra"
0 bits "between" bytes are ignored, this will be read as
E7 E7 E7 E7. But what happens if we deliberately stall for 12 cycles, ignoring the first three bits?
xxx ** byte 0 * byte 1 ** byte 2 ... 111 00 11101110 0 11100111 00 11111100 111
We get a different pattern:
EE E7 FC. By deliberately desynchronizing the stream, the program can detect the shifted pattern. And because the pattern starts with zeroes, the delay doesn't have to be perfectly cycle-accurate.
Copy programs can pretty reliably detect the difference between "normal" and "long" bytes with a carefully timed loop -- either the byte is ready after 32 cycles, or it isn't. The pattern above uses both 9-bit and 10-bit bytes, so to reproduce it correctly you'd need to accurately detect the difference between a 32-cycle read, a 36-cycle read, and a 40-cycle read. Very difficult to do on a 1MHz 6502.