// This routine decodes NorthStar formated disks and other with similar format.
//
// TODO: Too much code is being duplicated adding new formats.
//
// 10/13/23 DJG Added CONTROLLER_ND100_3041
// 12/08/22 DJG Changed error message
// 10/02/22 DJG Suppress false reporting of needing --begin_time
// 03/17/22 DJG Handle large deltas and improved error message
// 09/03/21 DJG Added CONTROLLER_SUPERBRAIN
// 07/05/19 DJG Improved 3 bit head field handling
// 04/22/18 DJG Added support for non 10 MHz bit rate
// 04/21/17 DJG Added parameter to mfm_check_header_values and added
// determining --begin_time if needed
// 05/21/16 DJG Parameter change to mfm_mark_*
// 12/31/15 DJG Parameter change to mfm_mark_*
// 12/24/15 DJG Comment cleanup
// 11/01/15 DJG Use new drive_params field and comment changes
//
// Copyright 2022 David Gesswein.
// This file is part of MFM disk utilities.
//
// MFM disk utilities is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MFM disk utilities is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MFM disk utilities. If not, see .
#define DEBUG 0
#define PRINT_ERR 1
#define WINDOW_FILTER 0
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "crc_ecc.h"
#include "emu_tran_file.h"
#include "mfm_decoder.h"
#include "msg.h"
#include "deltas_read.h"
// Type II PLL. Here so it will inline. Converted from continuous time
// by bilinear transformation. Coefficients adjusted to work best with
// my data. Could use some more work.
static inline float filter(float v, float *delay)
{
float in, out;
in = v + *delay;
out = in * 0.034446428576716f + *delay * -0.034124999994713f;
*delay = in;
return out;
}
// Compare two uint16_t values
int cmp_uint16_t(const void *p1, const void *p2)
{
return memcmp(p1, p2, 2);
}
// Decode bytes into header or sector data for the various formats we know about.
// The decoded data will be written to a file if one was specified.
// Since processing a header with errors can overwrite other good sectors this
// routine shouldn't be called if data has a CRC error.
//
// The format names are arbitrarily assigned to the first controller found
// writing that format.
// The format is
// CONTROLLER_NORTHSTAR_ADVANTAGE,
// 7 byte header + 2 checksum bytes
// byte 0 sector number, upper cyl in upper nibble
// byte 1 cylinder
// byte 2 head in low 4 bits.
// byte 3-6 Unknown
// byte 7 checksum of header
// byte 8 complement of checksum of header
// Data immediately follows header
// 512 bytes data
// 16 bit checksum of data
// 16 bit complement of checksum of data
// Track format information
// http://www.classiccmp.org/dunfield/miscpm/advtech.pdf (pg 3-47)
// The format is 187 0xff, 3 0x55, 40 0xff at start of track.
// Each sector starts with 67 0x00, 0x01, 9 byte header, 512 data bytes,
// 4 byte crc, 45 unspecified bytes
// The 3 0x55 is used to syncronize finding the sector locations. The
// controller uses fixed delays from where it finds that pattern to start
// looking for the sector headers.
// It appears the controller uses the time from index to the 0x55 pattern
// as an estimate of drive RPM and ajusts the time it looks for the
// sector based on it. This code does not implement that method.
//
// The timing doesn't match real captures with the above formatting. The
// data in mfm_decoder.h has been adjusted to match captured data.
//
// CONTROLLER_SUPERBRAIN
// superbrain2.trans
// Not Northstar but seemed to match this logic the best of the decoders.
// SuperFive disk system. No documentation on format.
// Sector start by time and mfm patttern 0xa89
// 4 byte header + 2 checksum bytes
// byte 0 0xfe
// byte 1 cylinder
// byte 2 head
// byte 3 sector
// bytes 4-5 CRC
// Data
// byte 0 0xf8
// 256 bytes data
// 2 byte crc
//
// CONTROLLER_ND100_3041
// micropolis1325_nd100
// Not Northstar but seemed to match this logic the best of the decoders.
// ND-11.015.01-Winchester Disk Controller
// 4 byte header + 2 checksum bytes
// byte 0 0
// byte 1 head low 4 bits. Bit 4 is 1 if bad track or spare track.
// For bad track data had cylinder and head remapped to repeated
// in the sector data as lower 5 bits head, Upper 11 bits cylinder
// byte 2 upper bits of cylinder
// byte 3 low 5 bits sector, upper 3 bits low bits cylinder
// bytes 4-5 CRC
// Data
// 1024 bytes data
// 2 byte crc
// Both header and data are marked with strings of 0's and then a 1 bit.
SECTOR_DECODE_STATUS northstar_process_data(STATE_TYPE *state, uint8_t bytes[],
int total_bytes,
uint64_t crc, int exp_cyl, int exp_head, int *sector_index,
DRIVE_PARAMS *drive_params, int *seek_difference,
SECTOR_STATUS sector_status_list[], int ecc_span,
SECTOR_DECODE_STATUS init_status)
{
static int sector_size;
static int bad_block;
static SECTOR_STATUS sector_status;
// Set if track has flag set that it may have an alternate assigned. Need
// to process sector data to get remapping info
static int alt_assigned = 0;
if (*state == PROCESS_HEADER) {
alt_assigned = 0;
memset(§or_status, 0, sizeof(sector_status));
sector_status.status |= init_status | SECT_HEADER_FOUND;
sector_status.ecc_span_corrected_header = ecc_span;
if (ecc_span != 0) {
sector_status.status |= SECT_ECC_RECOVERED;
}
if (drive_params->controller == CONTROLLER_NORTHSTAR_ADVANTAGE) {
sector_status.cyl = bytes[1] | (((int) bytes[0] & 0xf0) << 4);
sector_status.head = mfm_fix_head(drive_params, exp_head, bytes[2] & 0xf);
sector_status.sector = bytes[0] & 0xf;
// Don't know how/if these are encoded in header
sector_size = drive_params->sector_size;
bad_block = 0;
msg(MSG_DEBUG,
"Got exp %d,%d cyl %d head %d sector %d size %d bad block %d\n",
exp_cyl, exp_head, sector_status.cyl, sector_status.head,
sector_status.sector, sector_size, bad_block);
mfm_check_header_values(exp_cyl, exp_head, sector_index, sector_size,
seek_difference, §or_status, drive_params, sector_status_list);
*state = DATA_SYNC;
} else if (drive_params->controller == CONTROLLER_SUPERBRAIN) {
sector_status.cyl = bytes[1];
sector_status.head = mfm_fix_head(drive_params, exp_head, bytes[2]);
sector_status.sector = bytes[3];
// Don't know how/if these are encoded in header
sector_size = drive_params->sector_size;
bad_block = 0;
if (bytes[0] != 0xfe) {
msg(MSG_INFO, "Invalid header id byte %02x on cyl %d head %d sector %d\n",
bytes[0], sector_status.cyl, sector_status.head, sector_status.sector);
sector_status.status |= SECT_BAD_HEADER;
}
msg(MSG_DEBUG,
"Got exp %d,%d cyl %d head %d sector %d size %d bad block %d\n",
exp_cyl, exp_head, sector_status.cyl, sector_status.head,
sector_status.sector, sector_size, bad_block);
mfm_check_header_values(exp_cyl, exp_head, sector_index, sector_size,
seek_difference, §or_status, drive_params, sector_status_list);
*state = DATA_SYNC2;
} else if (drive_params->controller == CONTROLLER_ND100_3041) {
sector_status.cyl = (bytes[2] << 3) | (bytes[3] >> 5);
sector_status.head = mfm_fix_head(drive_params, exp_head, bytes[1] & 0xf);
sector_status.sector = bytes[3] & 0x1f;
int spare = bytes[1] & 0xf0;
if (!(spare == 0 || spare == 0x10)) {
msg(MSG_INFO, "Spare flag %02x on cyl %d head %d sector %d\n",
spare, sector_status.cyl, sector_status.head, sector_status.sector);
}
// Same flag used for bad track and spare track. Bad track has
// spare track cylinder and head repeated through the sector data.
if (spare == 0x10) {
alt_assigned = 1;
}
// Not encoded in header
sector_size = drive_params->sector_size;
bad_block = 0;
if (bytes[0] != 0x0) {
msg(MSG_INFO, "Invalid header id byte %02x on cyl %d head %d sector %d\n",
bytes[0], sector_status.cyl, sector_status.head, sector_status.sector);
sector_status.status |= SECT_BAD_HEADER;
}
// This drive uses CRC initial value of 0 so all zeros data is valid
// CRC. Reject if all zero data and head or cylinder is non zero
if ((exp_cyl != 0 || exp_head != 0) && bytes[1] == 0 && bytes[2] == 0
&& bytes[3] == 0) {
*state = HEADER_SYNC;
} else {
msg(MSG_DEBUG,
"Got exp %d,%d cyl %d head %d sector %d size %d bad block %d\n",
exp_cyl, exp_head, sector_status.cyl, sector_status.head,
sector_status.sector, sector_size, bad_block);
mfm_check_header_values(exp_cyl, exp_head, sector_index, sector_size,
seek_difference, §or_status, drive_params, sector_status_list);
*state = DATA_SYNC2;
}
}
} else if (*state == PROCESS_DATA) {
if (crc != 0) {
sector_status.status |= SECT_BAD_DATA;
}
if (ecc_span != 0) {
sector_status.status |= SECT_ECC_RECOVERED;
}
sector_status.ecc_span_corrected_data = ecc_span;
if (drive_params->controller == CONTROLLER_SUPERBRAIN) {
if (bytes[0] != 0xf8) {
msg(MSG_INFO, "Invalid data id byte %02x on cyl %d head %d sector %d\n",
bytes[0], sector_status.cyl, sector_status.head, sector_status.sector);
sector_status.status |= SECT_BAD_DATA;
}
}
// Try to determine if this is a bad track that was remapped or is a
// spare cylinder
if (drive_params->controller == CONTROLLER_ND100_3041 && alt_assigned &&
!(sector_status.status & SECT_BAD_HEADER)) {
uint16_t remap[sector_size/2];
uint16_t last;
int count;
uint16_t new_map = 0;
static int last_remap = 0;
for (int i = 0; i < sector_size/2; i++) {
remap[i] = (bytes[i*2] << 8) | bytes[i*2+1];
}
qsort(&remap, sector_size/2, sizeof(uint16_t), cmp_uint16_t);
last = remap[0];
count = 1;
// Real controller knows where alternate cylinders start
// For one sample they started at 1011. Since that may be
// different on different drives we say if we find all except
// max_different repeated values its the remapping data otherwise
// its a spare cylinder. Not guaranteed to get it correct.
int max_different = 30;
for (int i = 1; i < sector_size/2; i++) {
if (remap[i] == last) {
count++;
} else {
if (count >= (sector_size/2 - max_different)) {
new_map = last;
break;
} else {
count = 1;
last = remap[i];
}
}
}
if (count >= sector_size/2 - max_different) {
new_map = last;
}
if (new_map != 0) {
int new_head = new_map & 0x1f;
int new_cyl = new_map >> 5;
if (new_cyl > sector_status.cyl &&
new_head < drive_params->num_head) {
mfm_handle_alt_track_ch(drive_params, sector_status.cyl,
sector_status.head, new_cyl, new_head);
if (new_map != last_remap) {
msg(MSG_INFO, "Remapping cyl,track %d,%d to %d,%d\n",
sector_status.cyl, sector_status.head, new_cyl, new_head);
}
last_remap = new_map;
} else {
msg(MSG_ERR, "Ignored invalid bad track remap of %d,%d to %d,%d\n",
sector_status.cyl, sector_status.head, new_cyl, new_head);
}
}
}
if (!(sector_status.status & SECT_BAD_HEADER)) {
if (mfm_write_sector(&bytes[0], drive_params, §or_status,
sector_status_list, &bytes[0], total_bytes) == -1) {
sector_status.status |= SECT_BAD_HEADER;
}
}
*state = MARK_ID;
}
return sector_status.status;
}
// Decode a track's worth of deltas.
//
//
// drive_params: Drive parameters
// cyl,head: Physical Track data from
// deltas: MFM delta data to decode
// seek_difference: Return of difference between expected cyl and header
// sector_status_list: Return of status of decoded sector
// return: Or together of the status of each sector decoded
SECTOR_DECODE_STATUS northstar_decode_track(DRIVE_PARAMS *drive_params, int cyl,
int head, uint16_t deltas[], int *seek_difference,
SECTOR_STATUS sector_status_list[])
{
// This is which MFM clock and data bits are valid codes. So far haven't
// found a good way to use this.
//int valid_code[16] = { 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 };
// This converts the MFM clock and data bits into data bits.
int code_bits[16] = { 0, 1, 0, 0, 2, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
// This is the raw MFM data decoded with above
unsigned int raw_word = 0;
// Counter to know when to decode the next two bits.
int raw_bit_cntr = 0;
// The decoded bits
unsigned int decoded_word = 0;
// Counter to know when we have a bytes worth
int decoded_bit_cntr = 0;
// loop counter
int i;
// These are variables for the PLL filter. avg_bit_sep_time is the
// "VCO" frequency
float avg_bit_sep_time; // 200 MHz clocks
float nominal_bit_sep_time; // 200 MHz clocks
// Clock time is the clock edge time from the VCO.
float clock_time = 0;
// How many bits the last delta corresponded to
int int_bit_pos;
// PLL filter state. Static works better since next track bit timing
// similar to previous though a bad track can put it off enough that
// the next track has errors. Retry should fix. TODO: Look at
static float filter_state = 0;
// Time in track for debugging
int track_time = 0;
// Counter for debugging
int tot_raw_bit_cntr = 0;
// Where we are in decoding a sector, Start looking for header ID mark
STATE_TYPE state = MARK_ID;
//STATE_TYPE state = PROCESS_HEADER;
// Status of decoding returned
int sector_status = SECT_NO_STATUS;
// How many zeros we need to see before we will look for the mark byte.
// When write turns on and off can cause codes that look like the mark
// so this avoids them.
#define MARK_NUM_ZEROS 30
int sync_count = 0;
// Number of deltas available so far to process
int num_deltas;
// And number from last time
int last_deltas = 0;
// If we get too large a delta we need to process it in less than 32 bit
// word number of bits. This holds remaining number to process
int remaining_delta = 0;
// Maximum delta to process in one pass
int max_delta;
// Intermediate value
int tmp_raw_word;
// Collect bytes to further process here
uint8_t bytes[MAX_SECTOR_SIZE + 50];
// How many we need before passing them to the next routine
int bytes_needed = 0;
// Length to perform CRC over
int bytes_crc_len = 0;
// how many we have so far
int byte_cntr = 0;
// Sequential counter for counting sectors
int sector_index = 0;
// Count all the raw bits for emulation file
int all_raw_bits_count = 0;
// Time to look for next header. This should be in beginning of 0 words
int next_header_time = 0;
// First address mark time in ns
int first_addr_mark_ns = 0;
if (drive_params->controller == CONTROLLER_NORTHSTAR_ADVANTAGE) {
next_header_time = 74000;
} else if (drive_params->controller == CONTROLLER_SUPERBRAIN) {
next_header_time = 55000;
} else if (drive_params->controller == CONTROLLER_ND100_3041) {
next_header_time = 4230;
} else {
msg(MSG_FATAL,"northstart_mfm_decoder got unknwon controller %d\n",
drive_params->controller);
}
// Adjust time for when data capture started
next_header_time -= drive_params->start_time_ns / CLOCKS_TO_NS;
num_deltas = deltas_get_count(0);
raw_word = 0;
nominal_bit_sep_time = 200e6 /
mfm_controller_info[drive_params->controller].clk_rate_hz;
max_delta = nominal_bit_sep_time * 22;
avg_bit_sep_time = nominal_bit_sep_time;
i = 1;
while (num_deltas >= 0) {
// We process what we have then check for more.
for (; i < num_deltas;) {
int delta_process;
// If no remaining delta process next else finish remaining
if (remaining_delta == 0) {
delta_process = deltas[i++];
remaining_delta = delta_process;
} else {
delta_process = remaining_delta;
}
// Don't overflow our 32 bit word
if (delta_process > max_delta) {
delta_process = max_delta;
}
track_time += delta_process;
// This is simulating a PLL/VCO clock sampling the data.
clock_time += delta_process;
remaining_delta -= delta_process;
// Move the clock in current frequency steps and count how many bits
// the delta time corresponds to
for (int_bit_pos = 0; clock_time > avg_bit_sep_time / 2;
clock_time -= avg_bit_sep_time, int_bit_pos++) {
}
// And then filter based on the time difference between the delta and
// the clock. Don't update PLL if this is a long burst without
// transitions
if (remaining_delta == 0) {
avg_bit_sep_time = nominal_bit_sep_time + filter(clock_time, &filter_state);
}
#if DEBUG
//printf("track %d clock %f\n", track_time, clock_time);
//if (cyl == 70 & head == 5 && track_time > next_header_time)
if (cyl == 0 && head == 0)
printf
(" delta %d %.2f int %d avg_bit %.2f time %d dec %08x raw %08x\n",
deltas[i], clock_time,
int_bit_pos, avg_bit_sep_time, track_time, decoded_word,
raw_word);
#endif
if (all_raw_bits_count + int_bit_pos >= 32) {
all_raw_bits_count = mfm_save_raw_word(drive_params,
all_raw_bits_count, int_bit_pos, raw_word);
} else {
all_raw_bits_count += int_bit_pos;
}
// Shift based on number of bit times then put in the 1 from the
// delta. If we had a delta greater than the size of raw word we
// will lose the unprocessed bits in raw_word. This is unlikely
// to matter since this is invalid MFM data so the disk had a long
// drop out so many more bits are lost.
if (int_bit_pos >= sizeof(raw_word)*8) {
raw_word = 1;
} else {
raw_word = (raw_word << int_bit_pos) | 1;
}
tot_raw_bit_cntr += int_bit_pos;
raw_bit_cntr += int_bit_pos;
//printf("Raw %08x %d %d %d %d %d\n", raw_word, state, sync_count, tot_raw_bit_cntr, track_time, next_header_time);
// Are we looking for a mark code?
if (state == MARK_ID) {
// These patterns are MFM encoded all zeros or all ones.
// We are looking for zeros so we assume they are zeros.
if (track_time > next_header_time &&
(raw_word == 0x55555555 || raw_word == 0xaaaaaaaa)) {
sync_count++;
} else {
if (sync_count < MARK_NUM_ZEROS) {
sync_count = 0;
}
}
// If we found enough zeros start looking for a 1
if (sync_count >= MARK_NUM_ZEROS) {
//printf("Mark %d cyl %d head %d sect %d\n",tot_raw_bit_cntr, cyl, head, sector_index);
sync_count = 0;
state = HEADER_SYNC;
raw_bit_cntr = 0;
decoded_word = 0;
decoded_bit_cntr = 0;
}
// We need to wait for the one bit to resynchronize.
} else if (state == HEADER_SYNC) {
int found = 0;
if (drive_params->controller == CONTROLLER_NORTHSTAR_ADVANTAGE && (raw_word & 0xf) == 0x9) {
raw_bit_cntr = 0;
found = 1;
} else if (drive_params->controller == CONTROLLER_SUPERBRAIN) {
//Better preventing false syncs but misses some with data
//errors
//if ((raw_word & 0xfff) == 0xa89) {
if ((raw_word & 0xf) == 0x9) {
sync_count = 0;
raw_bit_cntr = 2;
found = 1;
}
} else if (drive_params->controller == CONTROLLER_ND100_3041) {
if ((raw_word & 0xf) == 0x9) {
sync_count = 0;
raw_bit_cntr = 0;
found = 1;
}
}
if (found) {
//printf("Sync %d,%d cyl %d head %d sect %d\n",tot_raw_bit_cntr, (track_time * 5 + drive_params->start_time_ns)/101, cyl, head, sector_index);
if (first_addr_mark_ns == 0) {
first_addr_mark_ns = track_time * CLOCKS_TO_NS;
}
// Time next header should start at
decoded_word = 0;
decoded_bit_cntr = 0;
state = PROCESS_HEADER;
mfm_mark_header_location(all_raw_bits_count, raw_bit_cntr,
tot_raw_bit_cntr);
// Figure out the length of data we should look for
bytes_crc_len = mfm_controller_info[drive_params->controller].header_bytes +
drive_params->header_crc.length / 8;
bytes_needed = bytes_crc_len;
if (bytes_needed >= sizeof(bytes)) {
printf("Too many bytes needed %d\n",bytes_needed);
exit(1);
}
byte_cntr = 0;
}
} else if (state == DATA_SYNC) {
state = PROCESS_DATA;
//printf("Start data %d\n",tot_raw_bit_cntr);
mfm_mark_data_location(all_raw_bits_count, 0, tot_raw_bit_cntr);
// Figure out the length of data we should look for
bytes_crc_len = mfm_controller_info[drive_params->controller].data_header_bytes +
mfm_controller_info[drive_params->controller].data_trailer_bytes +
drive_params->sector_size +
drive_params->data_crc.length / 8;
bytes_needed = bytes_crc_len;
// Must read enough extra bytes to ensure we send last 32
// bit word to mfm_save_raw_word
bytes_needed += 2;
if (bytes_needed >= sizeof(bytes)) {
printf("Too many bytes needed %d\n",bytes_needed);
exit(1);
}
byte_cntr = 0;
} else if (state == DATA_SYNC2) {
int found = 0;
if (drive_params->controller == CONTROLLER_SUPERBRAIN) {
if (raw_word == 0x55555555 || raw_word == 0xaaaaaaaa) {
sync_count++;
}
//if ((raw_word & 0xfff) == 0xa89 && sync_count > 80) {
if ((raw_word & 0xf) == 0x9 && sync_count > 80) {
raw_bit_cntr = 2;
found = 1;
}
} else if (drive_params->controller == CONTROLLER_ND100_3041) {
if (raw_word == 0x55555555 || raw_word == 0xaaaaaaaa) {
sync_count++;
}
if ((raw_word & 0xf) == 0x9 && sync_count > 20) {
raw_bit_cntr = 0;
found = 1;
}
}
if (found) {
sync_count = 0;
decoded_word = 0;
decoded_bit_cntr = 0;
state = PROCESS_DATA;
//printf("Start data %d\n",tot_raw_bit_cntr);
mfm_mark_data_location(all_raw_bits_count, 0, tot_raw_bit_cntr);
// Figure out the length of data we should look for
bytes_crc_len = mfm_controller_info[drive_params->controller].data_header_bytes +
mfm_controller_info[drive_params->controller].data_trailer_bytes +
drive_params->sector_size +
drive_params->data_crc.length / 8;
bytes_needed = bytes_crc_len;
// Must read enough extra bytes to ensure we send last 32
// bit word to mfm_save_raw_word
bytes_needed += 2;
if (bytes_needed >= sizeof(bytes)) {
printf("Too many bytes needed %d\n",bytes_needed);
exit(1);
}
byte_cntr = 0;
}
} else { // PROCESS_HEADER or PROCESS_DATA
int entry_state = state;
// If we have enough bits to decode do so. Stop if state changes
while (raw_bit_cntr >= 4 && entry_state == state) {
// If we have more than 4 only process 4 this time
raw_bit_cntr -= 4;
tmp_raw_word = raw_word >> raw_bit_cntr;
decoded_word =
(decoded_word << 2) | code_bits[tmp_raw_word & 0xf];
decoded_bit_cntr += 2;
//printf("Decoded %x %d %d\n",decoded_word, byte_cntr, tot_raw_bit_cntr);
// And if we have a bytes worth store it
if (decoded_bit_cntr >= 8) {
// Do we have enough to further process?
bytes[byte_cntr++] = decoded_word;
if (byte_cntr >= bytes_needed) {
mfm_mark_end_data(all_raw_bits_count, drive_params, cyl, head);
//printf("End data %d,%d state %d cyl %d head %d sect %d\n",tot_raw_bit_cntr, 166666 - (track_time * 5 + drive_params->start_time_ns)/100, state, cyl, head, sector_index);
sector_status |= mfm_process_bytes(drive_params, bytes,
bytes_crc_len, bytes_needed, &state, cyl, head,
§or_index, seek_difference, sector_status_list, 0);
// Look after the fill bytes. 8 is byte to bits, 40 is
// 200 MHz clocks per data bit (5 MHz for data bit,
// 10 for clock and data bit)
if (drive_params->controller == CONTROLLER_NORTHSTAR_ADVANTAGE) {
// 45 bytes plus extra to get past any junk from overwriting
next_header_time = track_time + 55 * 8 * 40;
} else if (drive_params->controller == CONTROLLER_SUPERBRAIN) {
next_header_time = track_time + 5 * 8 * 40;
} else if (drive_params->controller == CONTROLLER_ND100_3041) {
next_header_time = track_time + 20 * 40;
}
}
decoded_bit_cntr = 0;
}
}
}
}
// Finished what we had, any more?
// If we didn't get too many last time sleep so delta reader can run.
// Thread priorities might be better.
if (num_deltas - last_deltas <= 2000) {
usleep(500);
}
last_deltas = num_deltas;
num_deltas = deltas_get_count(i);
}
if (state == PROCESS_DATA && sector_index <= drive_params->num_sectors) {
float begin_time =
((bytes_needed - byte_cntr) * 16.0 *
1e9/mfm_controller_info[drive_params->controller].clk_rate_hz
+ first_addr_mark_ns) / 2 + drive_params->start_time_ns;
msg(MSG_ERR, "Ran out of data on sector index %d, try adding --begin_time %.0f to mfm_read command line\n",
sector_index, round(begin_time / 1000.0) * 1000.0);
}
// Force last partial word to be saved
mfm_save_raw_word(drive_params, all_raw_bits_count, 32-all_raw_bits_count,
raw_word);
// If we didn't find anything to decode return header error
if (sector_status == SECT_NO_STATUS) {
sector_status = SECT_BAD_HEADER;
}
return sector_status;
}