May 24, 2022

MFM disk - hex dump a track

I am itching for a hex dump of an entire track. David tells me what he does is add "-quiet 0" to a command a redirect it to a file. I do this as follows:
mfm_util --quiet 0 --format WD_1006 --sectors 17,0 --heads 8 --cylinders 320 --header_crc 0xffff,0x1021,16,0 --data_crc  0xffffffff,0x140a0445,32,6 --sector_length 512  --extracted_data_file xyz.img --transitions_file callan_raw1 >read.log
This yields data for the entire disk and it is easy enough to use an editor to find the track you are interested in.

Look at the code

I am having trouble interpreting the dump, so I am looking at wd_mfm_decoder.c

To make my life easier, I run "ctags -R ." in the mfm directory.

A search on the string "Got exp" takes me to deep inside a long function "wd_process_data()". There is another function wd_decode_track() and those are the only two functions in the file. Both of these are called from mfm_decoder.c

The routine mfm_decode_track() is called in many places in analyze.c, my bet on what is most pertinent for me is the call in analyze_sectors().

The wd_process_data() function is a long if/else on different controllers by testing "drive_params->controller"

Now let's look at this top down rather than bottom up for a bit. The file mfm_util.c contains main() and main() contains a loop to read the entire transitions file. The loop calls tran_file_read_track_deltas() and mfm_decode_track() (main() calls tran_file_read_header() to open the file.)

The tran file seems to hold deltas in some kind of compressed format, but tran_file_read_track_deltas() expands it into an array of unsigned shorts, returning the size of the array generated as the function value. The routine tran_file_read_track_deltas() doesn't do any seeks as near as I can see, so it presumes the file is being read in order.

The routine mfm_decode_track() is called for each track and is passed the array of deltas. This is in mfm_decoder.c It is a giant else/if on controller type and in our case bounces is right to wd_decode_track() in wd_mfm_decoder() I had thought that some generic decoding of the deltas might have been done first, but this is not the case. Curiously the size of the deltas array does not get passed as an argument, but is fetched via a call to deltas_get_count(0).

Much to my surpise, wd_decode_track() does not call some routine to get an array of "bytes" for the entire track to then process. It works its way through the deltas "on the fly". My original goal of getting a hex dump of an entire track may simply not be possible, or at least not as easy as just adding a print statement to the existing code. My goal is to understand the information being dumped by "-quiet 0" which looks like this:

0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,
0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,
0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,
0xe5,0xe5,0x51,0x66,0x4d,0x5a,
   0  0  1:0xa1,0xfe,0x00,0x00,0x06,0xcc,0xe8,
Got exp 0,0 cyl 0 head 0 sector 6,1 size 256 bad block 0
Expected sector size 512 header says 256 cyl 0 head 0 sector 6
   0  0  2:0xa1,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

On some reflection, a person will realize that the data coming from a track on a hard disk is really a series of bits, with no byte ordering without additional work, in particular synchronization. So a controller (and we are now talking about a software controller that does not run in real time) will need to recognize certain bit patterns that allow it to synchronize.

Some of what is on a track is written when the disk is formatted, with empty areas left for sectors to reside in. The data in sectors is written at an entirely different time and will not necessarily (or even likely) be synchronized with the data written during formatting.

Once wd_decode_track() likes what it sees, it calls mfm_process_bytes (), which is back in mfm_decoder.c. This routine has some nice debug stuff commented out up front. It has a big if/else on controller type and eventually will call wd_process_data() in our case. This takes us back to wd_mfm_decoder.c.

Here we can find our first message:

    msg(MSG_DEBUG,
         "Got exp %d,%d cyl %d head %d sector %d,%d size %d bad block %d\n",
            exp_cyl, exp_head, sector_status.cyl, sector_status.head,
            sector_status.sector, *sector_index, sector_size, bad_block);

Just to focus in on one thing, on my disk "sector_size" here prints out as 256. Where does this number come from? It depends (on the controller). It looks like in our case it gets extracted from data on the disk via code like the following. I include the lines that get the cylinder number to provide some context.
int sector_size_lookup[4] = {256, 512, 1024, 128};
cyl_high = cyl_high_lookup[(bytes[1] & 0xf) ^ 0xe];
sector_status.cyl = 0;
if (cyl_high != -1) {
    sector_status.cyl = cyl_high << 8;
 }
     sector_status.cyl |= bytes[2];
sector_size = sector_size_lookup[(bytes[3] & 0x60) >> 5];
What about our next message? I am trying to decide (among other things) if my disk has 256 byte or 512 byte sectors. And I am trying to decide whether this message is actually valid, or whether it is just interpreting information in my sector headers wrong because it is using an improper format. It is too early for me to tell.
Expected sector size 512 header says 256 cyl 0 head 0 sector 0
This is from mfm_check_header_values() in mfm_decoder.c The dump also has some lines with a Ctrl-M at the end making them somewhat garbled. An example is:
At cyl 160^M                                                                               ^M 160  0  0:0xa1,0xfe,0xa0,0x00,0x00,0x11,0xb2,
I started to go through some fuss and bother to get rid of these doggone returns, changing them all to newlines. But I realize now that these act as some kind of snazzy progress indicator when the code is running in other than logging mode, so I leave them be. The stuff off to the right is only off to the right when the logging is disturbed by this progress message stuff, which only happens at the start of every tenth cylinder

I am now thinking that the best way for me to understand all of this is to write my own code. Read a single track from the transitions file, then figure out how to dump it in a way I understand. There is nothing wrong with David's code, but I just don't understand what the dump is showing me.


Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org