geneve/ti99: Fixed HFDC hangs on not connected drives; allows timed steps for sector i/o instead of buffered steps only

This commit is contained in:
Michael Zapf 2021-06-03 01:35:35 +02:00
parent a9170dccb8
commit 3c775ba8e5
4 changed files with 82 additions and 27 deletions

View File

@ -71,7 +71,7 @@
#define LOG_CRU (1U<<10)
#define LOG_CONFIG (1U<<15) // Configuration
#define VERBOSE ( LOG_CONFIG | LOG_WARN )
#define VERBOSE ( LOG_GENERAL | LOG_CONFIG | LOG_WARN )
#include "logmacro.h"
DEFINE_DEVICE_TYPE_NS(TI99_HFDC, bus::ti99::peb, myarc_hfdc_device, "ti99_hfdc", "Myarc Hard and Floppy Disk Controller")
@ -82,6 +82,8 @@ namespace bus::ti99::peb {
#define FDC_TAG "hdc9234"
#define CLOCK_TAG "mm58274c"
#define NONE -1
#define MOTOR_TIMER 1
#define TAPE_ADDR 0x0fc0
@ -100,8 +102,12 @@ myarc_hfdc_device::myarc_hfdc_device(const machine_config &mconfig, const char *
device_ti99_peribox_card_interface(mconfig, *this),
m_motor_on_timer(nullptr),
m_hdc9234(*this, FDC_TAG),
m_clock(*this, CLOCK_TAG), m_current_floppy(nullptr),
m_current_harddisk(nullptr), m_see_switches(false),
m_clock(*this, CLOCK_TAG),
m_current_floppy(nullptr),
m_current_harddisk(nullptr),
m_current_floppy_index(NONE),
m_current_hd_index(NONE),
m_see_switches(false),
m_irq(), m_dip(), m_motor_running(false),
m_inDsrArea(false), m_HDCsel(false), m_RTCsel(false),
m_tapesel(false), m_RAMsel(false), m_ROMsel(false), m_address(0),
@ -714,7 +720,7 @@ enum
void myarc_hfdc_device::connect_floppy_unit(int index)
{
// Check if we have a new floppy
if (m_floppy_unit[index] != m_current_floppy)
if (index != m_current_floppy_index)
{
// Clear all latched flags from other drives
m_status_latch = 0;
@ -723,6 +729,7 @@ void myarc_hfdc_device::connect_floppy_unit(int index)
// Connect new drive
m_current_floppy = m_floppy_unit[index];
m_current_floppy_index = index;
// We don't use the READY line of floppy drives.
// READY is asserted when DSKx = 1
@ -732,7 +739,9 @@ void myarc_hfdc_device::connect_floppy_unit(int index)
m_current_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&myarc_hfdc_device::floppy_index_callback, this));
else
LOGMASKED(LOG_WARN, "Connection to DSK%d failed because no drive is connected\n", index+1);
m_hdc9234->connect_floppy_drive(m_floppy_unit[index]);
// Connect to HDC (or disconnect)
m_hdc9234->connect_floppy_drive(m_current_floppy);
}
// We can only run a floppy or a harddisk at a time, not both
@ -741,15 +750,16 @@ void myarc_hfdc_device::connect_floppy_unit(int index)
void myarc_hfdc_device::connect_harddisk_unit(int index)
{
if (m_harddisk_unit[index] != m_current_harddisk)
if (index != m_current_hd_index)
{
// Clear all latched flags form other drives
// Clear all latched flags from other drives
m_status_latch = 0;
disconnect_hard_drives();
LOGMASKED(LOG_LINES, "Select hard disk WDS%d\n", index+1);
// Connect new drive
m_current_harddisk = m_harddisk_unit[index];
m_current_hd_index = index;
LOGMASKED(LOG_LINES, "Connect index callback WDS%d\n", index+1);
if (m_current_harddisk != nullptr)
@ -760,6 +770,8 @@ void myarc_hfdc_device::connect_harddisk_unit(int index)
}
else
LOGMASKED(LOG_WARN, "Connection to WDS%d failed because no drive is connected\n", index+1);
// Connect to HDC (or disconnect)
m_hdc9234->connect_hard_drive(m_current_harddisk);
}
@ -776,6 +788,7 @@ void myarc_hfdc_device::disconnect_floppy_drives()
m_current_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
m_current_floppy = nullptr;
}
m_current_floppy_index = NONE;
}
void myarc_hfdc_device::disconnect_hard_drives()
@ -787,6 +800,7 @@ void myarc_hfdc_device::disconnect_hard_drives()
m_current_harddisk->setup_seek_complete_cb(mfm_harddisk_device::seek_complete_cb());
m_current_harddisk = nullptr;
}
m_current_hd_index = NONE;
}
/*
@ -875,6 +889,8 @@ void myarc_hfdc_device::device_start()
// The HFDC does not use READY; it has on-board RAM for DMA
m_current_floppy = nullptr;
m_current_harddisk = nullptr;
m_current_floppy_index = NONE;
m_current_hd_index = NONE;
// Parent class members
save_item(NAME(m_senila));

View File

@ -111,6 +111,12 @@ private:
// Currently selected hard drive
mfm_harddisk_device* m_current_harddisk;
// Currently selected floppy disk index
int m_current_floppy_index;
// Currently selected hard disk index
int m_current_hd_index;
// True: Access to DIP switch settings, false: access to line states
bool m_see_switches;

View File

@ -516,6 +516,14 @@ bool hdc92x4_device::fm_mode()
return ((m_register_w[MODE]&MO_DENSITY)!=0);
}
/*
Tell whether we have timed steps or buffered steps.
*/
bool hdc92x4_device::timed_steps()
{
return ((m_register_w[MODE]&MO_STEPRATE)!=0);
}
/*
Are we back on track 0?
*/
@ -913,7 +921,14 @@ void hdc92x4_device::read_id(int& cont, bool implied_seek, bool wait_seek_comple
// zero -> we're already there
if (m_substate == VERIFY) cont = NEXT;
else m_track_delta = desired_cylinder() - current_cylinder();
else
{
m_track_delta = desired_cylinder() - current_cylinder();
if (m_track_delta > 0)
LOGMASKED(LOG_STEP, "Implied seek %d tracks inwards\n", m_track_delta);
if (m_track_delta < 0)
LOGMASKED(LOG_STEP, "Implied seek %d tracks outwards\n", -m_track_delta);
}
break;
@ -1162,6 +1177,7 @@ void hdc92x4_device::data_transfer(int& cont)
case DATA_TRANSFER_READ:
// OK, sector has been read.
// Check CRC
LOGMASKED(LOG_SUBSTATES, "substate DATA_TRANSFER_READ\n");
if (m_live_state.crc != 0)
{
// Set Retry Required flag
@ -1368,7 +1384,7 @@ void hdc92x4_device::reset_controller()
*/
void hdc92x4_device::drive_deselect()
{
LOGMASKED(LOG_SELECT, "DESELECT command\n");
LOGMASKED(LOG_COMMAND, "DESELECT command\n");
m_selected_drive_number = NODRIVE;
m_output1 = 0x00;
set_command_done(TC_SUCCESS);
@ -1397,7 +1413,7 @@ void hdc92x4_device::restore_drive()
if (m_substate == UNDEF)
{
LOGMASKED(LOG_RESTORE, "RESTORE command %02x\n", current_command());
LOGMASKED(LOG_COMMAND, "RESTORE command %02x\n", current_command());
m_seek_count = 0;
m_substate = RESTORE_CHECK;
}
@ -1496,7 +1512,7 @@ void hdc92x4_device::step_drive()
if (m_substate == UNDEF)
{
LOGMASKED(LOG_STEP, "STEP IN/OUT command %02x\n", current_command());
LOGMASKED(LOG_COMMAND, "STEP IN/OUT command %02x\n", current_command());
m_substate = STEP_ON;
}
@ -1671,7 +1687,7 @@ void hdc92x4_device::drive_select()
// Calculate the head load delays
head_load_delay = head_load_delay_enable? m_register_w[DATA] * head_load_timer_increment[m_selected_drive_type] : 0;
LOGMASKED(LOG_SELECT, "DRIVE SELECT command (%02x): head load delay=%d, type=%d, drive=%d, pout=%02x, step_rate=%d\n", current_command(), head_load_delay, m_selected_drive_type, driveparm&3, m_register_w[RETRY_COUNT]&0x0f, pulse_width() + step_time());
LOGMASKED(LOG_COMMAND, "DRIVE SELECT command (%02x): head load delay=%d, type=%d, drive=%d, pout=%02x, step_rate=%d\n", current_command(), head_load_delay, m_selected_drive_type, driveparm&3, m_register_w[RETRY_COUNT]&0x0f, pulse_width() + step_time());
// Copy the DMA registers to registers CURRENT_HEAD, CURRENT_CYLINDER,
// and CURRENT_IDENT. This is required during formatting [1,2]
@ -1722,7 +1738,7 @@ void hdc92x4_device::drive_select()
void hdc92x4_device::set_register_pointer()
{
m_register_pointer = current_command() & 0xf;
LOGMASKED(LOG_SETPTR, "SET REGISTER POINTER command; start reg=%d\n", m_register_pointer);
LOGMASKED(LOG_COMMAND, "SET REGISTER POINTER command; start reg=%d\n", m_register_pointer);
// The specification does not say anything about the effect of setting an
// invalid value (only "care should be taken")
if (m_register_pointer > 10)
@ -1753,7 +1769,7 @@ void hdc92x4_device::seek_read_id()
if (m_substate == UNDEF)
{
// Command init
LOGMASKED(LOG_READ, "SEEK / READ ID command %02x, CHS=(%d,%d,%d)\n", current_command(), desired_cylinder(), desired_head(), desired_sector());
LOGMASKED(LOG_COMMAND, "SEEK / READ ID command %02x, CHS=(%d,%d,%d)\n", current_command(), desired_cylinder(), desired_head(), desired_sector());
m_substate = READ_ID;
}
@ -1827,7 +1843,7 @@ void hdc92x4_device::read_sectors()
{
// Command init
m_logical = (current_command() & 0x04)!=0; // used in VERIFY and DATA TRANSFER substate
LOGMASKED(LOG_READ, "READ SECTORS %s command %02x, CHS=(%d,%d,%d)\n", m_logical? "LOGICAL": "PHYSICAL", current_command(), desired_cylinder(), desired_head(), desired_sector());
LOGMASKED(LOG_COMMAND, "READ SECTORS %s command %02x, CHS=(%d,%d,%d)\n", m_logical? "LOGICAL": "PHYSICAL", current_command(), desired_cylinder(), desired_head(), desired_sector());
m_bypass = !m_is_hdc9234 && (current_command() & 0x02)!=0;
m_transfer_enabled = (current_command() & 0x01)!=0;
@ -1843,7 +1859,7 @@ void hdc92x4_device::read_sectors()
switch (m_substate & 0xf0)
{
case READ_ID:
read_id(cont, implied_seek, true); // Always check SEEK COMPLETE
read_id(cont, implied_seek, !timed_steps()); // Check SEEK COMPLETE when time bits are 000
break;
case VERIFY:
verify(cont); // for physical, only verify the first sector
@ -1879,7 +1895,7 @@ void hdc92x4_device::read_track()
{
if (m_substate == UNDEF)
{
LOGMASKED(LOG_READTRACK, "READ TRACK command %02x, head = %d\n", current_command(), desired_head());
LOGMASKED(LOG_COMMAND, "READ TRACK command %02x, head = %d\n", current_command(), desired_head());
dma_address_out(m_register_w[DMA23_16], m_register_w[DMA15_8], m_register_w[DMA7_0]);
m_transfer_enabled = (current_command() & 1)!=0;
}
@ -2003,7 +2019,7 @@ void hdc92x4_device::format_track()
{
if (m_substate == UNDEF)
{
LOGMASKED(LOG_FORMAT, "FORMAT TRACK command %02x, head = %d\n", current_command(), desired_head());
LOGMASKED(LOG_COMMAND, "FORMAT TRACK command %02x, head = %d\n", current_command(), desired_head());
m_substate = WAITINDEX0;
m_deleted = (current_command() & 0x10)!=0;
m_reduced_write_current = (current_command() & 0x08)!=0;
@ -2118,7 +2134,7 @@ void hdc92x4_device::write_sectors()
{
// Command init
m_logical = (current_command() & 0x20)!=0;
LOGMASKED(LOG_WRITE, "WRITE SECTORS %s command %02x, CHS=(%d,%d,%d)\n", m_logical? "LOGICAL" : "PHYSICAL", current_command(), desired_cylinder(), desired_head(), desired_sector());
LOGMASKED(LOG_COMMAND, "WRITE SECTORS %s command %02x, CHS=(%d,%d,%d)\n", m_logical? "LOGICAL" : "PHYSICAL", current_command(), desired_cylinder(), desired_head(), desired_sector());
m_multi_sector = (m_register_w[SECTOR_COUNT] != 1);
m_substate = READ_ID;
@ -2156,7 +2172,7 @@ void hdc92x4_device::write_sectors()
switch (m_substate & 0xf0)
{
case READ_ID:
read_id(cont, implied_seek, true); // Always check SEEK COMPLETE
read_id(cont, implied_seek, !timed_steps()); // Check SEEK COMPLETE when time bits are 000
break;
case VERIFY:
verify(cont);
@ -3195,6 +3211,7 @@ void hdc92x4_device::live_run_hd_until(attotime limit)
wait_for_realtime(VERIFY_FAILED);
else
wait_for_realtime(SEARCH_IDAM_FAILED);
return;
}
@ -3874,6 +3891,11 @@ uint8_t hdc92x4_device::get_data_from_encoding(uint16_t raw)
return (value >> 14) & 0xff;
}
/*
TODO: Check whether the whole rollback concept makes sense in this
controller. Unlike the wd17xx, this controller contains a lot more state,
including the external RAM connected via DMA.
*/
void hdc92x4_device::rollback()
{
m_live_state = m_checkpoint_state;
@ -4302,7 +4324,7 @@ void hdc92x4_device::write(offs_t offset, uint8_t data)
{
if ((offset & 1) == 0)
{
LOGMASKED(LOG_COMMAND, "New register write access %02x\n", data & 0xff);
LOGMASKED(LOG_DETAIL, "New register write access %02x\n", data & 0xff);
if (m_executing) LOGMASKED(LOG_WARN, "Error - previous command %02x not completed; register access ignored\n", current_command());
else
{
@ -4312,7 +4334,7 @@ void hdc92x4_device::write(offs_t offset, uint8_t data)
}
else
{
LOGMASKED(LOG_COMMAND, "New incoming command %02x\n", data);
LOGMASKED(LOG_DETAIL, "New incoming command %02x\n", data);
if (m_executing) LOGMASKED(LOG_WARN, "Error - previous command %02x not completed; new command %02x ignored\n", current_command(), data);
else
{
@ -4599,15 +4621,20 @@ void hdc92x4_device::seek_complete_handler()
int level = seek_complete()? ASSERT_LINE : CLEAR_LINE;
LOGMASKED(LOG_LINES, "[%s] Seek complete handler; level=%d\n", ttsn(), level);
// Synchronize our position on the track
live_sync();
// Some commands may wait for SEEK_COMPLETE regardless of the step rate
if (waiting_for_line(SEEKCOMP_LINE, level))
{
// Synchronize our position on the track
live_sync();
m_substate = m_state_after_line;
m_state_after_line = UNDEF;
reenter_command_processing();
}
else
{
if (level==ASSERT_LINE) LOGMASKED(LOG_LINES, "[%s] Ignoring seek complete signal\n", ttsn());
}
}
/*
@ -4718,8 +4745,11 @@ void hdc92x4_device::connect_floppy_drive(floppy_image_device* floppy)
void hdc92x4_device::connect_hard_drive(mfm_harddisk_device* harddisk)
{
m_harddisk = harddisk;
m_hd_encoding = m_harddisk->get_encoding();
LOGMASKED(LOG_DETAIL, "HD encoding = %d\n", m_hd_encoding);
if (harddisk != nullptr)
{
m_hd_encoding = m_harddisk->get_encoding();
LOGMASKED(LOG_DETAIL, "HD encoding = %d\n", m_hd_encoding);
}
}
/*

View File

@ -364,6 +364,9 @@ protected:
// Are we in FM mode?
bool fm_mode();
// Do we have timed steps?
bool timed_steps();
// Seek completed?
bool seek_complete();