// This module is the routines needed to read a MFM disk. It can analyze the
// disk to determine format, read the disk and store raw delta transition data
// and decoded sector data.
// TODO Make handle more complex interleave like RD53 (cyl to cyl is 8, track
// to track is -1 or 16)
// TODO Use recovery line on Seagates to microstep instead of big seeks
//
// 10/09/23 Remove interleave as option so ext2emu can be parsed better
// 09/17/23 Changed to calling pru_exec_program to set correct path for file
// to load and board_set_restore_max_cpu_speed to have one copy
// 01/20/21 DJG Fixed call
// 07/07/19 DJG Turn off recovery line when exiting
// 03/22/19 DJG Added REV C support
// 04/20/18 DJG Fixed previous change to work properly with analyze
// 03/09/18 DJG Added ability to request reading more heads or cylinders
// than analyze detects
// 09/07/16 DJG Report possible reversal of 20 pin cable
// 12/31/15 DJG Parameter change to parse_print_cmdline
// 08/02/15 DJG Added support for rev B board
// 05/17/15 DJG Added analyze of specified cylinder and head.
// 01/04/15 DJG Added support for Corvus_H and NorthStar Advantage
// These had a sector straddle the index pulse so the start_time_ns
// logic added to allow read to start with start of physical first sector.
// Corvus_H also uses 11 MHz for clock/data bit cell so various changes to
// support other rates.
// Fixed off by 1 allocating command line storage
// 11/09/14 DJG Changes for note option
//
// Copyright 2019 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 .
#include
#include
#include
#include
#include
#include
#include
#include
#include
// MFM program include files
#include
#include "msg.h"
#include "pru_setup.h"
#include "crc_ecc.h"
#define DEF_DATA
#include "emu_tran_file.h"
#include "mfm_decoder.h"
#include "parse_cmdline.h"
#include "analyze.h"
#include "deltas_read.h"
#include "drive.h"
#include "board.h"
#include "cmd.h"
// This routine is for cleaning up when shutting down. It is installed as an
// atexit routine and called by SIGINT handler.
void shutdown(void)
{
static int called = 0;
if (called)
return;
called = 1;
// Turn off recovery mode
drive_enable_recovery(0);
board_set_restore_max_cpu_speed(1);
// Turn off selected light on drive
drive_select(0);
pru_restart(0);
deltas_stop_thread();
pru_exec_cmd(CMD_EXIT, 0);
pru_shutdown();
}
// SIGINT/ control-c handler. Call our shutdown and exit
void shutdown_signal(void)
{
shutdown();
exit(1);
}
// Main routine. If specified analyze disk format and read disk contents
int main(int argc, char *argv[])
{
// Drive parameters either from command line or analyzing the disk
DRIVE_PARAMS drive_params;
// What we wish to do
int read = 0;
// Memory deltas from PRU are put in
uint32_t *deltas;
// Size of shared memory/deltas in bytes
int ddr_mem_size;
char *cmdline;
int max_deltas;
board_initialize();
// Find out what we should do
// M is only for ext2emu. i no longer used by mfm_read/util
parse_cmdline(argc, argv, &drive_params, "Mi", 1, 0, 0, 0);
parse_validate_options(&drive_params, 1);
// If they specified a file name then we read the disk
if (drive_params.extract_filename != NULL ||
drive_params.emulation_filename != NULL ||
drive_params.transitions_filename != NULL) {
read = 1;
}
if (!read && !drive_params.analyze) {
msg(MSG_FATAL, "Analyze and/or output filenames must be specified\n");
exit(1);
}
// Initialize PRU
ddr_mem_size = pru_setup(1);
if (ddr_mem_size == -1) {
exit(1);
}
// And start our code
if (pru_exec_program(0, "prucode0.bin") != 0) {
msg(MSG_FATAL, "Unable to execute prucode0.bin\n");
exit(1);
}
pru_write_word(MEM_PRU0_DATA, PRU0_START_TIME_CLOCKS,
drive_params.start_time_ns / CLOCKS_TO_NS);
pru_write_word(MEM_PRU0_DATA, PRU0_BOARD_REVISION, board_get_revision());
deltas = deltas_setup(ddr_mem_size);
max_deltas = ddr_mem_size / sizeof(deltas[0]);
// Cleanup when we exit
signal(SIGINT,(__sighandler_t) shutdown_signal);
atexit(shutdown);
// This is needed to keep up with the data. Without it we will take
// more than 2 revolutions per track due to the default governor not
// increasing the CPU speed enough. We switch frequently between busy and
// sleeping.
if (board_set_restore_max_cpu_speed(0)) {
msg(MSG_ERR, "Unable to set CPU to maximum speed\n");
}
if (!(drive_get_drive_status() & BIT_MASK(R31_DRIVE_SEL))) {
msg(MSG_ERR,"** Drive selected without select, is J3 20 pin cable reversed? **\n");
}
drive_params.noretry_head = drive_params.num_head;
drive_params.noretry_cyl = drive_params.num_cyl;
if (drive_params.analyze) {
int head_cmdline = drive_params.num_head;
int cyl_cmdline = drive_params.num_cyl;
analyze_disk(&drive_params, deltas, max_deltas, 0);
msg(MSG_INFO,"\n");
// Print analysis results
parse_print_cmdline(&drive_params, 1, 0);
drive_params.noretry_head = drive_params.num_head;
if (head_cmdline > drive_params.num_head) {
drive_params.num_head = head_cmdline;
}
drive_params.noretry_cyl = drive_params.num_cyl;
if (cyl_cmdline > drive_params.num_cyl) {
drive_params.num_cyl = cyl_cmdline;
}
}
cmdline = parse_print_cmdline(&drive_params, 0, 0);
drive_params.cmdline = msg_malloc(strlen(cmdline)+1,"main cmdline");
strcpy(drive_params.cmdline, cmdline);
if (read) {
drive_setup(&drive_params);
drive_read_disk(&drive_params, deltas, max_deltas);
}
pru_exec_cmd(CMD_EXIT, 0);
drive_select(0);
return (0);
}