MFM Encoding
There have been many encoding methods devised over the years, some can be applied to floppy disks and help reduce the magnetic flux transitions within this type of media. Common methods include FM, MFM, and MMFM. Each with its own pros and cons. Here we'll talk specifically about MFM encoding and how it pertains to your average 3.5" Floppy Disk.
Some facts:
- Every even bit is a true bit of original data.
- Every odd bit is a clock signal. Although the odd bits are optimized (more on this later).
- The resulting signal doubles in size (a u8 byte will need a u16 to encode).
- Although the signal may be double the size, the pulses it generates are fewer.
Table of Contents
Pulses
When we talk about pulses, we're referring specifically to the timing of a flux transition. Floppy disks work by magnetizing a section of a physically spinning disk and the time between magnetic transitions indicate how many zeros there are in the signal. For example
- A short transition (S) will nominally have 2us between bits, and represents 0b10
- A medium transition (M) will nominally have 3us between bits, and represents 0b100
- A long transition (L) will nominally have 4us between bits, and represents 0b1000
# 2.5 microseconds (in clock cycles) T_25 = 2.5 * F_CPU_HZ / 1000000 # 3.5 microseconds (in clock cycles) T_35 = 3.5 * F_CPU_HZ / 1000000 def read_symbol: counter = 0 while data_pin is low: counter++ while data_pin is high: counter++ if counter < T_25: return 0 else if counter < T_35: return 1 else return 2
In this example, we are counting clock cycles. The timing needs to be very precise and you'll have to be very familiar with the clock speed of your particular device.
A cautionary tale: check the generated assembly (yeah, I know that's a bit intense) and make sure the compiler didn't inject 10,000 lines of boundary checks. Simple things like that can be enough to ruin your timing. When I wrote my original controller, I had to bust out some basic assembly to get it perfect.
The Algorithm
At it's core, the algorithm to produce an MFM encoded signal
works as follows:
to encode x, y, z the resulting signal would be generated
like (x, x nor y, y, y nor z, z, ...)
Implementing this may seem easy, but there's a catch! The last bit of the signal will depend on the subsequent bit of the next signal. What this means in practice is that you can't really encode one byte at a time. Instead, you must stream the bits in a sequence, letting all of them flow together for your entire payload.
I think this is why most (all?) floppy disk controllers require you to write an entire sector at once. You can't just change the 3rd byte in place and call it good because that might have a cascading effect, ruining the timing of downstream bits. Instead, you must rewrite the whole sector at once to keep the timing information consistent.
MFM Pulse Encoder
Enter a comma delimmited list of hexadecimal values. For example: 0xA1, 0x13, 0x37. The encoded signal and accompanying pulses will be rendered below.
MFM Pulse Decoder
Enter a sequence of pulses in the form of S, M, or L. For example: LMSSMMSMMMSMMSLSSS. The value parsed from the signal will be rendered below.
Further Reading
Next, let's check out some FUNCTIONS that can be implemented for your floppy drive.