floppy.cafe

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:

Table of Contents

Pulses
The Algorithm
The Clock Signal
MFM Pulse Encoder
MFM Pulse Decoder
Further Reading

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

The specific logic for timing can be represented with the following psuedocode.

# 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.

The Clock Signal

Here's a fun observation! If we we are encoding, say, 0x1 the resulting signal will be 0101010101010010. The first bit is a 0! This can't translate nicely into a S/M/L pulse and so the resulting data will be shifted by 1 bit, ultimately offsetting your clock signal and ruining the entire payload.

The leading bit is important and I think this is why the floppy disk format require your first byte to be either 0xFA or 0xFB because it will ensure your clock signal doesn't get messed up.

If you find the data is not being parsed properly, try leading with either 0xFA or 0xFB (or really anything that causes the signal to begin with a 1) and see if that fixes your payload.

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.



Encoded Signal

Flux Pulses

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.



Decoded data

Further Reading

Next, let's check out some FUNCTIONS that can be implemented for your floppy drive.