diff --git a/src/emu/bus/ti99_peb/hfdc.c b/src/emu/bus/ti99_peb/hfdc.c index e67172342ac..0e3c77190f4 100644 --- a/src/emu/bus/ti99_peb/hfdc.c +++ b/src/emu/bus/ti99_peb/hfdc.c @@ -74,12 +74,12 @@ #define CLK_ADDR 0x0fe0 #define RAM_ADDR 0x1000 -#define TRACE_EMU 1 +#define TRACE_EMU 0 #define TRACE_CRU 0 #define TRACE_COMP 0 #define TRACE_RAM 0 #define TRACE_ROM 0 -#define TRACE_LINES 1 +#define TRACE_LINES 0 #define TRACE_MOTOR 0 #define TRACE_DMA 0 #define TRACE_INT 0 @@ -515,12 +515,22 @@ void myarc_hfdc_device::harddisk_index_callback(mfm_harddisk_device *harddisk, i signal_drive_status(); } +/* + This is called back from the hard disk when READY becomes asserted. +*/ +void myarc_hfdc_device::harddisk_ready_callback(mfm_harddisk_device *harddisk, int state) +{ + /* if (TRACE_LINES) */ logerror("%s: HD READY = %d\n", tag(), state); + set_bits(m_status_latch, HDC_DS_READY, (state==ASSERT_LINE)); + signal_drive_status(); +} + /* This is called back from the hard disk when seek_complete becomes asserted. */ void myarc_hfdc_device::harddisk_skcom_callback(mfm_harddisk_device *harddisk, int state) { - /* if (TRACE_LINES) */ if (state==1) logerror("%s: HD seek complete\n", tag()); + /* if (TRACE_LINES) */ logerror("%s: HD seek complete = %d\n", tag(), state); set_bits(m_status_latch, HDC_DS_SKCOM, (state==ASSERT_LINE)); signal_drive_status(); } @@ -674,6 +684,7 @@ WRITE8_MEMBER( myarc_hfdc_device::auxbus_out ) // Dir = 0 -> outward m_current_harddisk->direction_in_w((data & 0x20)? ASSERT_LINE : CLEAR_LINE); m_current_harddisk->step_w((data & 0x10)? ASSERT_LINE : CLEAR_LINE); + m_current_harddisk->headsel_w(data & 0x0f); } // We are pushing the drive status after OUTPUT2 @@ -728,6 +739,7 @@ void myarc_hfdc_device::connect_harddisk_unit(int index) if (m_current_harddisk != NULL) { m_current_harddisk->setup_index_pulse_cb(mfm_harddisk_device::index_pulse_cb(FUNC(myarc_hfdc_device::harddisk_index_callback), this)); + m_current_harddisk->setup_ready_cb(mfm_harddisk_device::ready_cb(FUNC(myarc_hfdc_device::harddisk_ready_callback), this)); m_current_harddisk->setup_seek_complete_cb(mfm_harddisk_device::seek_complete_cb(FUNC(myarc_hfdc_device::harddisk_skcom_callback), this)); } else @@ -1012,9 +1024,9 @@ MACHINE_CONFIG_FRAGMENT( ti99_hfdc ) MCFG_FLOPPY_DRIVE_ADD("f4", hfdc_floppies, NULL, myarc_hfdc_device::floppy_formats) // NB: Hard disks don't go without image (other than floppy drives) - MCFG_MFM_HARDDISK_ADD("h1", hfdc_harddisks, NULL) - MCFG_MFM_HARDDISK_ADD("h2", hfdc_harddisks, NULL) - MCFG_MFM_HARDDISK_ADD("h3", hfdc_harddisks, NULL) + MCFG_MFM_HARDDISK_ADD("h1", hfdc_harddisks, NULL, MFM_BYTE) + MCFG_MFM_HARDDISK_ADD("h2", hfdc_harddisks, NULL, MFM_BYTE) + MCFG_MFM_HARDDISK_ADD("h3", hfdc_harddisks, NULL, MFM_BYTE) MCFG_DEVICE_ADD(CLOCK_TAG, MM58274C, 0) MCFG_MM58274C_MODE24(1) // 24 hour @@ -1046,27 +1058,6 @@ ioport_constructor myarc_hfdc_device::device_input_ports() const const device_type TI99_HFDC = &device_creator; -mfm_harddisk_connector::mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock): - device_t(mconfig, MFM_HD_CONNECTOR, "MFM hard disk connector", tag, owner, clock, "mfm_hd_connector", __FILE__), - device_slot_interface(mconfig, *this) -{ -} - -mfm_harddisk_connector::~mfm_harddisk_connector() -{ -} - -mfm_harddisk_device *mfm_harddisk_connector::get_device() -{ - return dynamic_cast(get_card_device()); -} - -void mfm_harddisk_connector::device_start() -{ -} - -const device_type MFM_HD_CONNECTOR = &device_creator; - // ========================================================================= /* diff --git a/src/emu/bus/ti99_peb/hfdc.h b/src/emu/bus/ti99_peb/hfdc.h index f7d7e9053b5..19e7d0fc321 100644 --- a/src/emu/bus/ti99_peb/hfdc.h +++ b/src/emu/bus/ti99_peb/hfdc.h @@ -69,6 +69,7 @@ private: // Callbacks for the index hole and seek complete void floppy_index_callback(floppy_image_device *floppy, int state); void harddisk_index_callback(mfm_harddisk_device *harddisk, int state); + void harddisk_ready_callback(mfm_harddisk_device *harddisk, int state); void harddisk_skcom_callback(mfm_harddisk_device *harddisk, int state); // Operate the floppy motors @@ -182,26 +183,6 @@ private: int m_readyflags; }; -/* Connector for a MFM hard disk. See also floppy.c */ -class mfm_harddisk_connector : public device_t, - public device_slot_interface -{ -public: - mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - ~mfm_harddisk_connector(); - - mfm_harddisk_device *get_device(); - -protected: - void device_start(); -}; - -extern const device_type MFM_HD_CONNECTOR; - -#define MCFG_MFM_HARDDISK_ADD(_tag, _slot_intf, _def_slot) \ - MCFG_DEVICE_ADD(_tag, MFM_HD_CONNECTOR, 0) \ - MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false); - // ========================================================================= /* diff --git a/src/emu/machine/hdc9234.c b/src/emu/machine/hdc9234.c index a89c6e93184..183d1af2aaf 100644 --- a/src/emu/machine/hdc9234.c +++ b/src/emu/machine/hdc9234.c @@ -22,6 +22,7 @@ #include "emu.h" #include "hdc9234.h" +#include "formats/imageutl.h" // Per-command debugging #define TRACE_SELECT 0 @@ -335,12 +336,14 @@ enum SEARCH_IDAM, SEARCH_IDAM_FAILED, READ_TWO_MORE_A1_IDAM, + READ_IDENT, READ_ID_FIELDS_INTO_REGS, SEARCH_DAM, READ_TWO_MORE_A1_DAM, + READ_DATADEL_FLAG, SEARCH_DAM_FAILED, READ_SECTOR_DATA, - READ_SECTOR_DATA1, + READ_SECTOR_DATA_CONT, WRITE_DAM_AND_SECTOR, WRITE_SEC_SKIP_GAP2, WRITE_SEC_SKIP_GAP2_LOOP, @@ -463,6 +466,30 @@ bool hdc9234_device::on_track00() return (m_register_r[DRIVE_STATUS] & HDC_DS_TRK00)!=0; } +/* + Seek completed? +*/ +bool hdc9234_device::seek_complete() +{ + return (m_register_r[DRIVE_STATUS] & HDC_DS_SKCOM)!=0; +} + +/* + Index hole? +*/ +bool hdc9234_device::index_hole() +{ + return (m_register_r[DRIVE_STATUS] & HDC_DS_INDEX)!=0; +} + +/* + Drive ready? +*/ +bool hdc9234_device::drive_ready() +{ + return (m_register_r[DRIVE_STATUS] & HDC_DS_READY)!=0; +} + /* Accessor functions for specific parameters. */ @@ -545,11 +572,6 @@ int hdc9234_device::pulse_width() return time; } -bool hdc9234_device::rapid_steps() -{ - return (m_register_w[MODE] & MO_STEPRATE) == 0; -} - /* Delivers the sector size */ @@ -582,10 +604,41 @@ void hdc9234_device::wait_time(emu_timer *tm, const attotime &delay, int param) */ void hdc9234_device::wait_line(int line, line_state level, int substate, bool stopwrite) { - m_event_line = line; - m_line_level = level; - m_state_after_line = substate; - m_stopwrite = stopwrite; + bool line_at_level = true; + if (line == SEEKCOMP_LINE && (seek_complete() == (level==ASSERT_LINE))) + { + logerror("%s: SEEK_COMPLETE line is already %d\n", tag(), level); + } + else + { + if (line == INDEX_LINE && (index_hole() == (level==ASSERT_LINE))) + { + logerror("%s: INDEX line is already %d\n", tag(), level); + } + else + { + if (line == READY_LINE && (drive_ready() == (level==ASSERT_LINE))) + { + logerror("%s: READY line is already %d\n", tag(), level); + } + else + { + // The line is not yet at the desired level; hence, arm the trigger. + m_event_line = line; + m_line_level = level; + m_state_after_line = substate; + m_stopwrite = stopwrite; + line_at_level = false; + } + } + } + + if (line_at_level) + { + m_substate = substate; + m_state_after_line = UNDEF; + reenter_command_processing(); + } } // ================================================================== @@ -666,6 +719,7 @@ void hdc9234_device::read_id(int& cont, bool implied_seek, bool wait_seek_comple if (wait_seek_complete) { // We have to wait for SEEK COMPLETE + if (TRACE_READID && TRACE_SUBSTATES) logerror("%s: Waiting for SEEK COMPLETE\n", tag()); wait_line(SEEKCOMP_LINE, ASSERT_LINE, READ_ID_SEEK_COMPLETE, false); cont = WAIT; } @@ -1065,7 +1119,7 @@ void hdc9234_device::restore_drive() { case RESTORE_CHECK: // Track 0 has not been reached yet - if ((m_register_r[DRIVE_STATUS] & HDC_DS_READY)==0) + if (!drive_ready()) { if (TRACE_RESTORE) logerror("%s: restore command: Drive not ready\n", tag()); // Does not look like a success, but this takes into account @@ -1257,7 +1311,7 @@ void hdc9234_device::poll_drives() break; case POLL2: - if ((m_register_r[DRIVE_STATUS] & HDC_DS_SKCOM)!=0) + if (seek_complete()) { // Seek complete has been set m_substate = DONE; @@ -1404,7 +1458,7 @@ void hdc9234_device::seek_read_id() int cont = NEXT; bool step_enable = (current_command() & 0x04)==1; - bool wait_seek_comp = ((current_command() & 0x02)==1) || rapid_steps(); + bool wait_seek_comp = (current_command() & 0x02)==1; bool do_verify = (current_command() & 0x01)==1; while (cont == NEXT) @@ -1477,7 +1531,7 @@ void hdc9234_device::read_sectors() switch (m_substate & 0xf0) { case READ_ID: - read_id(cont, implied_seek, rapid_steps()); + read_id(cont, implied_seek, true); // Always check SEEK COMPLETE break; case VERIFY: verify(cont, logical); // for physical, only verify the first sector @@ -1522,14 +1576,13 @@ void hdc9234_device::read_track() switch (m_substate) { case WAITINDEX0: - // Do we happen to have an index hole right now? - if ((m_register_r[DRIVE_STATUS] & HDC_DS_INDEX)==0) + if (!index_hole()) { m_substate = WAITINDEX1; } else { - // Waiting for the index line going down + // We're above the index hole; wait for the index line going down wait_line(INDEX_LINE, ASSERT_LINE, WAITINDEX1, false); cont = WAIT; } @@ -1650,15 +1703,14 @@ void hdc9234_device::format_track() switch (m_substate) { case WAITINDEX0: - // Do we happen to have an index hole right now? - if ((m_register_r[DRIVE_STATUS] & HDC_DS_INDEX)==0) + if (!index_hole()) { m_substate = WAITINDEX1; cont = NEXT; } else { - // Waiting for the index line going down + // We're above the index hole right now, so wait for the line going down wait_line(INDEX_LINE, ASSERT_LINE, WAITINDEX1, false); cont = WAIT; } @@ -1734,7 +1786,7 @@ void hdc9234_device::write_sectors() switch (m_substate & 0xf0) { case READ_ID: - read_id(cont, implied_seek, rapid_steps()); + read_id(cont, implied_seek, true); // Always check SEEK COMPLETE break; case VERIFY: verify(cont, logical); @@ -1780,6 +1832,39 @@ std::string hdc9234_device::ttsn() return tts(machine().time()); } +bool hdc9234_device::found_mark(int state) +{ + bool ismark = false; + if (using_floppy()) + { + if (state==SEARCH_IDAM) ismark = (m_live_state.shift_reg == fm_mode()? 0xf57e : 0x4489); + else + { + // f56a 1x1x + ismark = fm_mode()? ((m_live_state.shift_reg & 0xfffa) == 0xf56a) : (m_live_state.shift_reg == 0x4489); + } + } + else + { + switch (m_hd_encoding) + { + case MFM_BITS: + case MFM_BYTE: + ismark = (m_live_state.shift_reg == 0x4489); + break; + case SEPARATED: + // 0 0 0 0 1 0 1 0 + // 1 0 1 0 0 0 0 1 + ismark = (m_live_state.data_reg == 0xa1 && m_live_state.clock_reg == 0x0a); + break; + case SEPARATED_SIMPLE: + ismark = (m_live_state.data_reg == 0xa1 && m_live_state.clock_reg == 0xff); + break; + } + } + return ismark; +} + /* The controller starts to read bits from the disk. This method takes an argument for the state machine called at the end. @@ -1799,7 +1884,7 @@ void hdc9234_device::live_start(int state) m_live_state.data_reg = 0; m_live_state.last_data_bit = false; - pll_reset(m_live_state.time, m_write); + if (using_floppy()) pll_reset(m_live_state.time, m_write); m_checkpoint_state = m_live_state; // Save checkpoint @@ -1807,6 +1892,7 @@ void hdc9234_device::live_start(int state) live_run(); m_last_live_state = UNDEF; + if (TRACE_LIVE) logerror("%s: [%s] Live start end\n", tag(), ttsn().c_str()); // delete } void hdc9234_device::live_run() @@ -1833,9 +1919,9 @@ void hdc9234_device::live_run_until(attotime limit) if (TRACE_LIVE) { if (limit == attotime::never) - logerror("%s: [%s] live_run, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); + logerror("%s: [%s live] live_run, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); else - logerror("%s: [%s] live_run until %s, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), tts(limit).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); + logerror("%s: [%s live] live_run until %s, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), tts(limit).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); } if (limit == attotime::never) @@ -1867,7 +1953,7 @@ void hdc9234_device::live_run_until(attotime limit) if (TRACE_LIVE && m_last_live_state != SEARCH_IDAM) { - logerror("%s: [%s] SEARCH_IDAM [limit %s]\n", tag(),tts(m_live_state.time).c_str(), tts(limit).c_str()); + logerror("%s: [%s live] SEARCH_IDAM [limit %s]\n", tag(),tts(m_live_state.time).c_str(), tts(limit).c_str()); m_last_live_state = m_live_state.state; } @@ -1876,11 +1962,11 @@ void hdc9234_device::live_run_until(attotime limit) if (read_one_bit(limit)) { - if (TRACE_LIVE) logerror("%s: [%s] SEARCH_IDAM limit reached\n", tag(), tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] SEARCH_IDAM limit reached\n", tag(), tts(m_live_state.time).c_str()); return; } // logerror("%s: SEARCH_IDAM\n", tts(m_live_state.time).c_str()); - if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).c_str(), m_live_state.shift_reg, + if (TRACE_SHIFT) logerror("%s: [%s live] shift = %04x data=%02x c=%d\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg, get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter); // [1] p. 9: The ID field sync mark must be found within 33,792 byte times @@ -1895,7 +1981,7 @@ void hdc9234_device::live_run_until(attotime limit) // MFM case if (m_live_state.shift_reg == 0x4489) { - if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); m_live_state.crc = 0x443b; m_live_state.data_separator_phase = false; m_live_state.bit_counter = 0; @@ -1926,14 +2012,14 @@ void hdc9234_device::live_run_until(attotime limit) if (TRACE_LIVE && m_last_live_state != READ_TWO_MORE_A1_IDAM) { - logerror("%s: [%s] READ_TWO_MORE_A1\n", tag(),tts(m_live_state.time).c_str()); + logerror("%s: [%s live] READ_TWO_MORE_A1\n", tag(),tts(m_live_state.time).c_str()); m_last_live_state = m_live_state.state; } // Beyond time limit? if (read_one_bit(limit)) return; - if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).c_str(), m_live_state.shift_reg, + if (TRACE_SHIFT) logerror("%s: [%s live] shift = %04x data=%02x c=%d\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg, get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter); if (m_live_state.bit_count_total > 33792*16) @@ -1955,12 +2041,12 @@ void hdc9234_device::live_run_until(attotime limit) m_live_state.state = SEARCH_IDAM; } else - if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); // Continue break; } - if (TRACE_LIVE) logerror("%s: [%s] Found data value %02X\n", tag(),tts(m_live_state.time).c_str(), m_live_state.data_reg); + if (TRACE_LIVE) logerror("%s: [%s live] Found data value %02X\n", tag(),tts(m_live_state.time).c_str(), m_live_state.data_reg); // Check for ident field (fe, ff, fd, fc) if ((m_live_state.data_reg & 0xfc) != 0xfc) @@ -1981,7 +2067,7 @@ void hdc9234_device::live_run_until(attotime limit) case READ_ID_FIELDS_INTO_REGS: if (TRACE_LIVE && m_last_live_state != READ_ID_FIELDS_INTO_REGS) { - logerror("%s: [%s] READ_ID_FIELDS_INTO_REGS\n", tag(),tts(m_live_state.time).c_str()); + logerror("%s: [%s live] READ_ID_FIELDS_INTO_REGS\n", tag(),tts(m_live_state.time).c_str()); m_last_live_state = m_live_state.state; } @@ -2022,7 +2108,7 @@ void hdc9234_device::live_run_until(attotime limit) case SEARCH_DAM: if (TRACE_LIVE && m_last_live_state != SEARCH_DAM) { - logerror("%s: [%s] SEARCH_DAM\n", tag(),tts(m_live_state.time).c_str()); + logerror("%s: [%s live] SEARCH_DAM\n", tag(),tts(m_live_state.time).c_str()); m_last_live_state = m_live_state.state; } @@ -2031,7 +2117,7 @@ void hdc9234_device::live_run_until(attotime limit) if(read_one_bit(limit)) return; - if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).c_str(), m_live_state.shift_reg, + if (TRACE_SHIFT) logerror("%s: [%s live] shift = %04x data=%02x c=%d\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg, get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter); if (!fm_mode()) @@ -2045,7 +2131,7 @@ void hdc9234_device::live_run_until(attotime limit) if (m_live_state.bit_counter >= 28*16 && m_live_state.shift_reg == 0x4489) { - if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); m_live_state.crc = 0x443b; m_live_state.data_separator_phase = false; m_live_state.bit_counter = 0; @@ -2079,14 +2165,14 @@ void hdc9234_device::live_run_until(attotime limit) case READ_TWO_MORE_A1_DAM: { if (TRACE_LIVE && m_last_live_state != READ_TWO_MORE_A1_DAM) { - logerror("%s: [%s] READ_TWO_MORE_A1_DAM\n", tag(),tts(m_live_state.time).c_str()); + logerror("%s: [%s live] READ_TWO_MORE_A1_DAM\n", tag(),tts(m_live_state.time).c_str()); m_last_live_state = m_live_state.state; } if(read_one_bit(limit)) return; - if (TRACE_SHIFT) logerror("%s: shift = %04x data=%02x c=%d\n", tts(m_live_state.time).c_str(), m_live_state.shift_reg, + if (TRACE_SHIFT) logerror("%s: [%s live] shift = %04x data=%02x c=%d\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg, get_data_from_encoding(m_live_state.shift_reg), m_live_state.bit_counter); // Repeat until we have collected 16 bits @@ -2103,12 +2189,12 @@ void hdc9234_device::live_run_until(attotime limit) return; } else - if (TRACE_LIVE) logerror("%s: [%s] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); // Continue break; } - if (TRACE_LIVE) logerror("%s: [%s] Found data value %02X\n", tag(),tts(m_live_state.time).c_str(), m_live_state.data_reg); + if (TRACE_LIVE) logerror("%s: [%s live] Found data value %02X\n", tag(),tts(m_live_state.time).c_str(), m_live_state.data_reg); if ((m_live_state.data_reg & 0xff) == 0xf8) { @@ -2138,7 +2224,7 @@ void hdc9234_device::live_run_until(attotime limit) { if (TRACE_LIVE && m_last_live_state != READ_SECTOR_DATA) { - logerror("%s: [%s] READ_SECTOR_DATA\n", tag(),tts(m_live_state.time).c_str()); + logerror("%s: [%s live] READ_SECTOR_DATA\n", tag(),tts(m_live_state.time).c_str()); m_last_live_state = m_live_state.state; } @@ -2163,13 +2249,13 @@ void hdc9234_device::live_run_until(attotime limit) // Repeat until we have collected 16 bits if (m_live_state.bit_counter & 15) break; - if (TRACE_LIVE) logerror("%s: [%s] Found data value %02X, CRC=%04x\n", tag(),tts(m_live_state.time).c_str(), m_live_state.data_reg, m_live_state.crc); + if (TRACE_LIVE) logerror("%s: [%s live] Found data value %02X, CRC=%04x\n", tag(),tts(m_live_state.time).c_str(), m_live_state.data_reg, m_live_state.crc); int slot = (m_live_state.bit_counter >> 4)-1; if (slot < calc_sector_size()) { // Sector data - wait_for_realtime(READ_SECTOR_DATA1); + wait_for_realtime(READ_SECTOR_DATA_CONT); return; } else if (slot < calc_sector_size()+2) @@ -2184,7 +2270,7 @@ void hdc9234_device::live_run_until(attotime limit) } else { - if (TRACE_LIVE) logerror("%s: [%s] Sector read completed\n", tag(),tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] Sector read completed\n", tag(),tts(m_live_state.time).c_str()); wait_for_realtime(IDLE); } return; @@ -2193,10 +2279,10 @@ void hdc9234_device::live_run_until(attotime limit) break; } - case READ_SECTOR_DATA1: - if (TRACE_LIVE && m_last_live_state != READ_SECTOR_DATA1) + case READ_SECTOR_DATA_CONT: + if (TRACE_LIVE && m_last_live_state != READ_SECTOR_DATA_CONT) { - logerror("%s: [%s] READ_SECTOR_DATA1\n", tag(),tts(m_live_state.time).c_str()); + logerror("%s: [%s live] READ_SECTOR_DATA_CONT\n", tag(),tts(m_live_state.time).c_str()); m_last_live_state = m_live_state.state; } @@ -2242,7 +2328,7 @@ void hdc9234_device::live_run_until(attotime limit) // 5. Write the CRC bytes if (TRACE_LIVE) - logerror("%s: [%s] WRITE_DAM_AND_SECTOR\n", tag(), tts(m_live_state.time).c_str()); + logerror("%s: [%s live] WRITE_DAM_AND_SECTOR\n", tag(), tts(m_live_state.time).c_str()); skip_on_track(m_gap2_size, WRITE_DAM_SYNC); break; @@ -2559,14 +2645,14 @@ void hdc9234_device::live_run_until(attotime limit) // The pause is implemented by doing dummy reads on the floppy if (read_one_bit(limit)) { - if (TRACE_LIVE) logerror("%s: [%s] return; limit=%s\n", tag(), tts(m_live_state.time).c_str(), tts(limit).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] return; limit=%s\n", tag(), tts(m_live_state.time).c_str(), tts(limit).c_str()); return; } // Repeat until we have collected 16 bits if ((m_live_state.bit_counter & 15)==0) { - if (TRACE_READ && TRACE_DETAIL) logerror("%s: [%s] Read byte %02x, repeat = %d\n", tag(), tts(m_live_state.time).c_str(), m_live_state.data_reg, m_live_state.repeat); + if (TRACE_READ && TRACE_DETAIL) logerror("%s: [%s live] Read byte %02x, repeat = %d\n", tag(), tts(m_live_state.time).c_str(), m_live_state.data_reg, m_live_state.repeat); wait_for_realtime(READ_TRACK_NEXT_BYTE); return; } @@ -2637,7 +2723,7 @@ void hdc9234_device::live_run_until(attotime limit) */ void hdc9234_device::live_run_hd_until(attotime limit) { -// int slot = 0; + int slot = 0; logerror("%s: live_run_hd\n", tag()); if (m_live_state.state == IDLE || m_live_state.next_state != -1) @@ -2646,19 +2732,239 @@ void hdc9234_device::live_run_hd_until(attotime limit) if (TRACE_LIVE) { if (limit == attotime::never) - logerror("%s: [%s] live_run_hd, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); + logerror("%s: [%s live] live_run_hd, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); else - logerror("%s: [%s] live_run_hd until %s, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), tts(limit).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); + logerror("%s: [%s live] live_run_hd until %s, live_state=%d, mode=%s\n", tag(), tts(m_live_state.time).c_str(), tts(limit).c_str(), m_live_state.state, fm_mode()? "FM":"MFM"); } + + // We did not specify an upper time bound, so we take the next index pulse + if (m_harddisk != NULL) limit = m_harddisk->track_end_time(); + while (true) { switch (m_live_state.state) { - case SEARCH_IDAM: - break; + case SEARCH_IDAM: + // This bit will be set when the IDAM cannot be found + set_bits(m_register_r[CHIP_STATUS], CS_SYNCERR, false); + + if (read_from_mfmhd(limit)) + { + if (TRACE_LIVE) logerror("%s: [%s live] SEARCH_IDAM limit reached\n", tag(), tts(m_live_state.time).c_str()); + return; + } + logerror("%s: [%s live] Read %04x\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg); + + // [1] p. 9: The ID field sync mark must be found within 33,792 byte times + if (m_live_state.bit_count_total > 33792*16) + { + wait_for_realtime(SEARCH_IDAM_FAILED); + return; + } + + if (found_mark(SEARCH_IDAM)) + { + if (TRACE_LIVE) logerror("%s: [%s live] Found an A1 mark\n", tag(), tts(m_live_state.time).c_str()); + m_live_state.crc = 0x443b; + m_live_state.data_separator_phase = false; + m_live_state.bit_counter = 0; + + m_live_state.state = READ_IDENT; + } + break; + + case SEARCH_IDAM_FAILED: + set_bits(m_register_r[CHIP_STATUS], CS_SYNCERR, true); + m_live_state.state = IDLE; + return; + + case READ_IDENT: + if (read_from_mfmhd(limit)) return; + + // Ident bytes are 111111xx + if ((m_live_state.data_reg & 0xfc) != 0xfc) + { + // This may happen when we accidentally locked onto the DAM. Look for the next IDAM. + if (TRACE_LIVE) logerror("%s: Missing ident byte after A1\n", tag()); + m_live_state.state = SEARCH_IDAM; + } + else + { + m_register_r[CURRENT_IDENT] = m_live_state.data_reg; + m_live_state.state = READ_ID_FIELDS_INTO_REGS; + slot = 0; + } + break; + + case READ_ID_FIELDS_INTO_REGS: + + if (TRACE_LIVE && m_last_live_state != READ_ID_FIELDS_INTO_REGS) + { + logerror("%s: [%s live] READ_ID_FIELDS_INTO_REGS\n", tag(),tts(m_live_state.time).c_str()); + m_last_live_state = m_live_state.state; + } + + if (read_from_mfmhd(limit)) return; + + if (TRACE_LIVE) logerror("%s: slot %d = %02x, crc=%04x\n", tag(), slot, m_live_state.data_reg, m_live_state.crc); + m_register_r[id_field[slot++]] = m_live_state.data_reg; + + if(slot > 5) + { + // We successfully read the ID fields; let's wait for the machine time to catch up. + m_live_state.bit_count_total = 0; + + if ((current_command() & 0xfe) == 0x5a) + // Continue if we're reading a complete track + wait_for_realtime(READ_TRACK_ID_DONE); + else + // Live run is done here; it is the main state machine's turn again. + wait_for_realtime(IDLE); + return; + } + break; + + case SEARCH_DAM: + if (TRACE_LIVE && m_last_live_state != SEARCH_DAM) + { + logerror("%s: [%s live] SEARCH_DAM\n", tag(),tts(m_live_state.time).c_str()); + m_last_live_state = m_live_state.state; + } + set_bits(m_register_r[CHIP_STATUS], CS_DELDATA, false); + + if (read_from_mfmhd(limit)) return; + + logerror("%s: [%s live] Read %04x\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg); + + if (m_live_state.bit_counter > 30*16) + { + if (TRACE_FAIL) logerror("%s: SEARCH_DAM failed\n", tag()); + wait_for_realtime(SEARCH_DAM_FAILED); + return; + } + + if (found_mark(SEARCH_DAM)) + { + if (TRACE_LIVE) logerror("%s: [%s live] Found an A1 mark\n", tag(),tts(m_live_state.time).c_str()); + m_live_state.crc = 0x443b; + m_live_state.data_separator_phase = false; + m_live_state.bit_counter = 0; + m_live_state.state = READ_DATADEL_FLAG; + } + break; + + case READ_DATADEL_FLAG: + if (read_from_mfmhd(limit)) return; + + if ((m_live_state.data_reg & 0xff) == 0xf8) + { + if (TRACE_LIVE) logerror("%s: [%s live] Found deleted data mark F8 after DAM sync\n", tag(), tts(m_live_state.time).c_str()); + set_bits(m_register_r[CHIP_STATUS], CS_DELDATA, true); + } + else + { + if ((m_live_state.data_reg & 0xff) != 0xfb) + { + if (TRACE_FAIL) logerror("%s: [%s live] Missing FB/F8 data mark after DAM sync; found %04x\n", tag(), tts(m_live_state.time).c_str(), m_live_state.shift_reg); + wait_for_realtime(SEARCH_DAM_FAILED); + return; + } + } + m_live_state.bit_counter = 0; + m_live_state.state = READ_SECTOR_DATA; + break; + + case SEARCH_DAM_FAILED: + if (TRACE_FAIL) logerror("%s: SEARCH_DAM failed\n", tag()); + m_live_state.state = IDLE; + return; + + case READ_SECTOR_DATA: + if (TRACE_LIVE && m_last_live_state != READ_SECTOR_DATA) + { + logerror("%s: [%s live] READ_SECTOR_DATA\n", tag(),tts(m_live_state.time).c_str()); + m_last_live_state = m_live_state.state; + } + + if (read_from_mfmhd(limit)) return; + + // Request bus release + // For hard disk, get it only for the first byte and then keep the bus until the last byte. + // HD: bit_counter increases by 16 for MFM_BYTE, SEPARATED(_SIMPLE) and by 1 for MFM_BIT + if (m_transfer_enabled && (m_live_state.bit_counter == 1 || m_live_state.bit_counter == 16)) + { + set_bits(m_register_r[INT_STATUS], ST_OVRUN, true); + m_out_dmarq(ASSERT_LINE); + } + + slot = (m_live_state.bit_counter >> 4)-1; + if (TRACE_LIVE) logerror("%s: [%s live] Found data value [%d/%d] = %02X, CRC=%04x\n", tag(),tts(m_live_state.time).c_str(), slot, calc_sector_size(), m_live_state.data_reg, m_live_state.crc); + + if (slot < calc_sector_size()) + { + // For the first byte, allow for the DMA acknowledge to be set. + if (slot == 0) + { + wait_for_realtime(READ_SECTOR_DATA_CONT); + return; + } + else m_live_state.state = READ_SECTOR_DATA_CONT; + } + else if (slot < calc_sector_size()+2) + { + // CRC + if (slot == calc_sector_size()+1) + { + m_out_dip(CLEAR_LINE); + m_out_dmarq(CLEAR_LINE); + checkpoint(); + + if ((current_command() & 0xfe)==0x5a) + { + // Reading a track? Continue with next ID. + wait_for_realtime(READ_TRACK_ID); + } + else + { + if (TRACE_LIVE) logerror("%s: [%s live] Sector read completed\n", tag(),tts(m_live_state.time).c_str()); + wait_for_realtime(IDLE); + } + return; + } + } + break; + + case READ_SECTOR_DATA_CONT: + + // Did the system CPU send the DMA ACK in the meantime? + if ((m_register_r[INT_STATUS] & ST_OVRUN)!=0) + { + if (TRACE_FAIL) logerror("%s: No DMA ACK - buffer overrun\n", tag()); + set_bits(m_register_r[INT_STATUS], TC_DATAERR, true); + m_live_state.state = IDLE; + return; + } + + if (m_transfer_enabled) + { + m_register_r[DATA] = m_register_w[DATA] = m_live_state.data_reg; + // See above: For hard disk do it only for the first byte / bit + if (m_live_state.bit_counter == 1 || m_live_state.bit_counter == 16) + m_out_dip(ASSERT_LINE); + + m_out_dma(0, m_register_r[DATA], 0xff); + if (TRACE_LIVE) logerror("%s: [%s live] Byte %02x sent via DMA\n", tag(),tts(m_live_state.time).c_str(), m_register_r[DATA] & 0xff); + } + m_live_state.state = READ_SECTOR_DATA; + + break; + + default: + if (TRACE_LIVE) logerror("%s: Unknown state: %d\n", tag(), m_live_state.state); + break; } - return; } + m_last_live_state = UNDEF; } /* @@ -2675,33 +2981,43 @@ void hdc9234_device::live_sync() if(m_live_state.time > machine().time()) { // If so, we must roll back to the last checkpoint - if (TRACE_SYNC) logerror("%s: [%s] Rolling back and replaying (%s)\n", tag(), ttsn().c_str(), tts(m_live_state.time).c_str()); + if (TRACE_SYNC) logerror("%s: [%s] Rolling back and replaying [%s live]\n", tag(), ttsn().c_str(), tts(m_live_state.time).c_str()); rollback(); + // and replay until we reach the machine time - live_run_until(machine().time()); - // Caught up, write on floppy image - m_pll.commit(m_floppy, m_live_state.time); + if (using_floppy()) + { + live_run_until(machine().time()); + // Caught up, commit bits from pll buffer to disk until live time (if there is something to write) + m_pll.commit(m_floppy, m_live_state.time); + } + else + { + // HD case + live_run_hd_until(machine().time()); + } } else { // We are behind machine time, so we will never get back to that // time, thus we can commit that position - if (TRACE_SYNC) logerror("%s: [%s] Committing (%s)\n", tag(), ttsn().c_str(), tts(m_live_state.time).c_str()); - // Write on floppy image - m_pll.commit(m_floppy, m_live_state.time); + if (TRACE_SYNC) logerror("%s: [%s] Committing [%s live]\n", tag(), ttsn().c_str(), tts(m_live_state.time).c_str()); + + // Commit bits from pll buffer to disk until live time (if there is something to write) + if (using_floppy()) + m_pll.commit(m_floppy, m_live_state.time); if (m_live_state.next_state != -1) - { m_live_state.state = m_live_state.next_state; - } if (m_live_state.state == IDLE) { - m_pll.stop_writing(m_floppy, m_live_state.time); + // Commit until live time and stop + if (using_floppy()) + m_pll.stop_writing(m_floppy, m_live_state.time); m_live_state.time = attotime::never; } } - m_live_state.next_state = -1; checkpoint(); } @@ -2711,12 +3027,13 @@ void hdc9234_device::live_abort() { if (!m_live_state.time.is_never() && m_live_state.time > machine().time()) { - if (TRACE_LIVE) logerror("%s: Abort; rolling back and replaying (%s)\n", ttsn().c_str(), tts(m_live_state.time).c_str()); + if (TRACE_LIVE) logerror("%s: [%s] Abort; rolling back and replaying [%s live]\n", tag(), ttsn().c_str(), tts(m_live_state.time).c_str()); rollback(); live_run_until(machine().time()); } - m_pll.stop_writing(m_floppy, m_live_state.time); + if (using_floppy()) m_pll.stop_writing(m_floppy, m_live_state.time); + m_live_state.time = attotime::never; m_live_state.state = IDLE; m_live_state.next_state = -1; @@ -2774,7 +3091,7 @@ void hdc9234_device::wait_for_realtime(int state) { m_live_state.next_state = state; m_timer->adjust(m_live_state.time - machine().time()); - if (TRACE_LIVE) logerror("%s: [%s] Waiting for real time [%s] to catch up\n", tag(), tts(m_live_state.time).c_str(), tts(machine().time()).c_str()); + if (TRACE_LIVE) logerror("%s: [%s live] Waiting for real time [%s] to catch up; next state = %d\n", tag(), tts(m_live_state.time).c_str(), ttsn().c_str(), state); } /* @@ -2897,7 +3214,7 @@ void hdc9234_device::encode_byte(UINT8 byte) m_live_state.bit_counter = 16; m_live_state.last_data_bit = raw & 1; m_live_state.shift_reg = m_live_state.shift_reg_save = raw; - if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: [%s] Write %02x (%04x)\n", tag(), tts(m_live_state.time).c_str(), byte, raw); + if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: [%s live] Write %02x (%04x)\n", tag(), tts(m_live_state.time).c_str(), byte, raw); checkpoint(); } @@ -2911,7 +3228,7 @@ void hdc9234_device::encode_raw(UINT16 raw) m_live_state.bit_counter = 16; m_live_state.shift_reg = m_live_state.shift_reg_save = raw; m_live_state.last_data_bit = raw & 1; - if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: [%s] Write %02x (%04x)\n", tag(), tts(m_live_state.time).c_str(), get_data_from_encoding(raw), raw); + if (TRACE_WRITE && TRACE_DETAIL) logerror("%s: [%s live] Write %02x (%04x)\n", tag(), tts(m_live_state.time).c_str(), get_data_from_encoding(raw), raw); checkpoint(); } @@ -2940,12 +3257,86 @@ void hdc9234_device::pll_reset(const attotime &when, bool output) void hdc9234_device::checkpoint() { - // Write on floppy image - m_pll.commit(m_floppy, m_live_state.time); + // Commit bits from pll buffer to disk until live time (if there is something to write) + if (using_floppy()) m_pll.commit(m_floppy, m_live_state.time); m_checkpoint_state = m_live_state; m_checkpoint_pll = m_pll; } +// =========================================================================== + +// HD support +/* + Read the bit or complete byte from the hard disk at the point of time + specified by the time in the live_state. + Return true: the time limit has been reached + Return false: valid return + + Updates the CRC and the shift register. Also, the time is updated. + +*/ +bool hdc9234_device::read_from_mfmhd(const attotime &limit) +{ + UINT16 data = 0; + bool offlimit = m_harddisk->read(m_live_state.time, limit, data); + + // We have reached the time limit + if (offlimit) return true; + + if (m_hd_encoding == MFM_BITS) + { + // Push bit into shift register + m_live_state.shift_reg = (m_live_state.shift_reg << 1) | data; + m_live_state.bit_counter++; + // Used for timeout handling + m_live_state.bit_count_total++; + + // Clock bit (false) or data bit (true)? + if (m_live_state.data_separator_phase==true) + { + m_live_state.data_reg = (m_live_state.data_reg << 1) | data; + // Update CRC + if ((m_live_state.crc ^ (data ? 0x8000 : 0x0000)) & 0x8000) + m_live_state.crc = (m_live_state.crc << 1) ^ 0x1021; + else + m_live_state.crc = m_live_state.crc << 1; + } + + m_live_state.data_separator_phase = !m_live_state.data_separator_phase; + } + else + { + UINT16 separated = data; + m_live_state.shift_reg = data; + + if (m_hd_encoding == MFM_BYTE) + { + for (int i=0; i < 8; i++) + { + separated <<= 1; + if (data & 0x8000) separated |= 0x0100; + data <<= 1; + if (data & 0x8000) separated |= 0x0001; + data <<= 1; + } + } + + // Push byte into data / clock register + m_live_state.clock_reg = (separated >> 8) & 0xff; + m_live_state.data_reg = separated & 0xff; + m_live_state.bit_counter += 16; + // Used for timeout handling + m_live_state.bit_count_total += 16; + + // Update CRC + m_live_state.crc = ccitt_crc16_one(m_live_state.crc, m_live_state.data_reg); + m_live_state.data_separator_phase = false; + } + + return false; +} + + // =========================================================================== /* @@ -3074,6 +3465,7 @@ void hdc9234_device::process_command() void hdc9234_device::reenter_command_processing() { + if (TRACE_DELAY) logerror("%s: Re-enter command processing; live state = %d\n", tag(), m_live_state.state); // Do we have a live run on the track? if (m_live_state.state != IDLE) { @@ -3085,6 +3477,7 @@ void hdc9234_device::reenter_command_processing() // We're here when there is no live_run anymore // Where were we last time? // Take care not to restart commands because of the index callback + if (TRACE_DELAY) logerror("%s: Continue with substate %d\n", tag(), m_substate); if (m_executing && m_substate != UNDEF) (this->*m_command)(); auxbus_out(); } @@ -3184,31 +3577,36 @@ void hdc9234_device::auxbus_in(UINT8 data) (data&HDC_DS_SKCOM)? 1:0, (data&HDC_DS_TRK00)? 1:0, (data&HDC_DS_UDEF)? 1:0, (data&HDC_DS_WRPROT)? 1:0, (data&HDC_DS_READY)? 1:0, (data&HDC_DS_WRFAULT)? 1:0); - UINT8 prev = m_register_r[DRIVE_STATUS]; + + bool previndex = index_hole(); + bool prevready = drive_ready(); + bool prevskcom = seek_complete(); + m_register_r[DRIVE_STATUS] = data; - if ((prev & HDC_DS_INDEX) != (data & HDC_DS_INDEX)) - { - // Check whether index value changed - index_callback((data & HDC_DS_INDEX)? ASSERT_LINE : CLEAR_LINE); - } - - if ((prev & HDC_DS_READY) != (data & HDC_DS_READY)) - { - // Check whether ready value changed - ready_callback((data & HDC_DS_READY)? ASSERT_LINE : CLEAR_LINE); - } - - if ((prev & HDC_DS_SKCOM) != (data & HDC_DS_SKCOM)) - { - // Check whether seek complete value changed - seek_complete_callback((data & HDC_DS_SKCOM)? ASSERT_LINE : CLEAR_LINE); - } + // Call a handler if the respective flag changed + if (previndex != index_hole()) index_handler(); + if (prevready != drive_ready()) ready_handler(); + if (prevskcom != seek_complete()) seek_complete_handler(); } -void hdc9234_device::index_callback(int level) +bool hdc9234_device::waiting_for_line(int line, int level) { - if (TRACE_LINES) logerror("%s: [%s] Index callback level=%d\n", tag(), ttsn().c_str(), level); + return (m_event_line == line && m_state_after_line != UNDEF && m_line_level == level); +} + +bool hdc9234_device::waiting_for_other_line(int line) +{ + return (m_state_after_line != UNDEF && m_event_line != line); +} + +/* + Handlers for incoming signal lines. +*/ +void hdc9234_device::index_handler() +{ + int level = index_hole()? ASSERT_LINE : CLEAR_LINE; + if (TRACE_LINES) logerror("%s: [%s] Index handler; level=%d\n", tag(), ttsn().c_str(), level); // Synchronize our position on the track live_sync(); @@ -3219,24 +3617,31 @@ void hdc9234_device::index_callback(int level) if (m_wait_for_index) m_stop_after_index = true; } - if (m_event_line == INDEX_LINE && level == m_line_level && m_state_after_line != UNDEF) + if (waiting_for_line(INDEX_LINE, level)) { if (TRACE_LINES) logerror("%s: [%s] Index pulse level=%d triggers event\n", tag(), ttsn().c_str(), level); m_substate = m_state_after_line; m_state_after_line = UNDEF; if (m_stopwrite) { - m_pll.stop_writing(m_floppy, m_live_state.time); + if (using_floppy()) m_pll.stop_writing(m_floppy, m_live_state.time); m_live_state.state = IDLE; } + reenter_command_processing(); + } + else + { + // Live processing waits for INDEX + // For harddisk we will continue processing on the falling edge + if (!waiting_for_other_line(INDEX_LINE) && (using_floppy() || level == CLEAR_LINE)) + reenter_command_processing(); } - - reenter_command_processing(); } -void hdc9234_device::ready_callback(int level) +void hdc9234_device::ready_handler() { - if (TRACE_LINES) logerror("%s: [%s] Ready callback level=%d\n", tag(), ttsn().c_str(), level); + int level = drive_ready()? ASSERT_LINE : CLEAR_LINE; + if (TRACE_LINES) logerror("%s: [%s] Ready handler; level=%d\n", tag(), ttsn().c_str(), level); // Set the interrupt status flag set_bits(m_register_r[INT_STATUS], ST_RDYCHNG, true); @@ -3251,7 +3656,8 @@ void hdc9234_device::ready_callback(int level) set_interrupt(ASSERT_LINE); } - if (m_event_line == READY_LINE && level == m_line_level && m_state_after_line != UNDEF) + // This is actually not needed, since we never wait for READY + if (waiting_for_line(READY_LINE, level)) { m_substate = m_state_after_line; m_state_after_line = UNDEF; @@ -3259,14 +3665,15 @@ void hdc9234_device::ready_callback(int level) } } -void hdc9234_device::seek_complete_callback(int level) +void hdc9234_device::seek_complete_handler() { - if (TRACE_LINES) logerror("%s: [%s] Seek complete callback level=%d\n", tag(), ttsn().c_str(), level); + int level = seek_complete()? ASSERT_LINE : CLEAR_LINE; + if (TRACE_LINES) logerror("%s: [%s] Seek complete handler; level=%d\n", tag(), ttsn().c_str(), level); // Synchronize our position on the track live_sync(); - if (m_event_line == SEEKCOMP_LINE && level == m_line_level && m_state_after_line != UNDEF) + if (waiting_for_line(SEEKCOMP_LINE, level)) { m_substate = m_state_after_line; m_state_after_line = UNDEF; @@ -3382,6 +3789,8 @@ void hdc9234_device::connect_floppy_drive(floppy_image_device* floppy) void hdc9234_device::connect_hard_drive(mfm_harddisk_device* harddisk) { m_harddisk = harddisk; + m_hd_encoding = m_harddisk->get_encoding(); + logerror("%s: HD encoding = %d\n", tag(), m_hd_encoding); } /* diff --git a/src/emu/machine/hdc9234.h b/src/emu/machine/hdc9234.h index 8317545c552..b6dad3fa4f2 100644 --- a/src/emu/machine/hdc9234.h +++ b/src/emu/machine/hdc9234.h @@ -173,10 +173,16 @@ private: // Timer callback void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); - // Callbacks - void ready_callback(int level); - void index_callback(int level); - void seek_complete_callback(int level); + // Handlers for incoming signals + void ready_handler(); + void index_handler(); + void seek_complete_handler(); + + // Wait for this line? + bool waiting_for_line(int line, int level); + + // Wait for some other line? + bool waiting_for_other_line(int line); // Wait for some time to pass or for a line to change level void wait_time(emu_timer *tm, int microsec, int next_substate); @@ -212,6 +218,7 @@ private: int byte_counter; bool data_separator_phase; bool last_data_bit; + UINT8 clock_reg; UINT8 data_reg; int state; int next_state; @@ -241,11 +248,14 @@ private: void rollback(); void checkpoint(); + // Found a mark + bool found_mark(int state); + // Delivers the data bits from the given encoding UINT8 get_data_from_encoding(UINT16 raw); // ============================================== - // PLL functions and interface to floppy + // PLL functions and interface to floppy and harddisk // ============================================== // Phase-locked loops @@ -254,6 +264,9 @@ private: // Clock divider value UINT8 m_clock_divider; + // MFM HD encoding type + mfmhd_enc_t m_hd_encoding; + // Resets the PLL to the given time void pll_reset(const attotime &when, bool write); @@ -283,6 +296,9 @@ private: // Skips bytes on the track void skip_on_track(int count, int next_state); + // Read from the MFM HD + bool read_from_mfmhd(const attotime &limit); + // ============================================== // Command state machine // ============================================== @@ -368,6 +384,18 @@ private: // Are we in FM mode? bool fm_mode(); + // Seek completed? + bool seek_complete(); + + // Are we on track 0? + bool on_track00(); + + // Are we at the index hole? + bool index_hole(); + + // Is the attached drive ready? + bool drive_ready(); + // Delivers the desired head int desired_head(); @@ -398,12 +426,6 @@ private: // Sector size as read from the track int calc_sector_size(); - // Are we on track 0? - bool on_track00(); - - // Are we using rapid steps (needed to decide to wait for seek complete) - bool rapid_steps(); - // Is the currently selected drive a floppy drive? bool using_floppy(); diff --git a/src/emu/machine/ti99_hd.c b/src/emu/machine/ti99_hd.c index 1f3029a1200..5ad8fc1a535 100644 --- a/src/emu/machine/ti99_hd.c +++ b/src/emu/machine/ti99_hd.c @@ -45,25 +45,36 @@ enum STEP_SETTLE }; +#define TRACKSLOTS 5 +#define TRACKIMAGE_SIZE 10416 // Provide the buffer for a complete track, including preambles and gaps + mfm_harddisk_device::mfm_harddisk_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : harddisk_image_device(mconfig, type, name, tag, owner, clock, shortname, source), device_slot_card_interface(mconfig, *this) { } +mfm_harddisk_device::~mfm_harddisk_device() +{ +} + void mfm_harddisk_device::device_start() { m_index_timer = timer_alloc(INDEX_TM); m_spinup_timer = timer_alloc(SPINUP_TM); m_seek_timer = timer_alloc(SEEK_TM); + m_spinup_time = attotime::from_msec(8000); + m_rev_time = attotime::from_hz(60); // MFM drives have a revolution rate of 3600 rpm (i.e. 60/sec) m_index_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60)); // Spinup may take up to 24 seconds - m_spinup_timer->adjust(attotime::from_msec(8000)); + m_spinup_timer->adjust(m_spinup_time); m_current_cylinder = 10; // for test purpose + + m_cache = global_alloc(mfmhd_trackimage_cache); } void mfm_harddisk_device::device_reset() @@ -76,16 +87,55 @@ void mfm_harddisk_device::device_reset() m_step_line = CLEAR_LINE; } +void mfm_harddisk_device::device_stop() +{ + global_free(m_cache); +} + +bool mfm_harddisk_device::call_load() +{ + logerror("call_load\n"); + bool loaded = harddisk_image_device::call_load(); + if (loaded==IMAGE_INIT_PASS) + { + m_cache->init(get_chd_file(), tag(), TRACKSLOTS, m_encoding); + } + else + { + logerror("Could not load CHD\n"); + } + return loaded; +} + void mfm_harddisk_device::setup_index_pulse_cb(index_pulse_cb cb) { m_index_pulse_cb = cb; } +void mfm_harddisk_device::setup_ready_cb(ready_cb cb) +{ + m_ready_cb = cb; +} + void mfm_harddisk_device::setup_seek_complete_cb(seek_complete_cb cb) { m_seek_complete_cb = cb; } +attotime mfm_harddisk_device::track_end_time() +{ + // We back up two microseconds before the track end to avoid the + // index pulse to appear earlier (because of rounding effects) + attotime nexttime = m_rev_time - attotime::from_nsec(2000); + if (!m_ready) + { + // estimate the next index time; during power-up we assume half the rotational speed + // Should never be relevant, though, because READY is false. + nexttime = nexttime * 2; + } + return (m_revolution_start_time.is_never())? attotime::never : (m_revolution_start_time + nexttime); +} + void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { switch (id) @@ -94,6 +144,7 @@ void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int /* Simple index hole handling. We assume that there is only a short pulse. */ if (!m_index_pulse_cb.isnull()) { + m_revolution_start_time = machine().time(); m_index_pulse_cb(this, ASSERT_LINE); m_index_pulse_cb(this, CLEAR_LINE); } @@ -101,6 +152,7 @@ void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int case SPINUP_TM: m_ready = true; logerror("%s: Spinup complete, drive is ready\n", tag()); + if (!m_ready_cb.isnull()) m_ready_cb(this, ASSERT_LINE); break; case SEEK_TM: switch (m_step_phase) @@ -128,7 +180,7 @@ void mfm_harddisk_device::device_timer(emu_timer &timer, device_timer_id id, int // Seek completed logerror("%s: Settling done at cylinder %d, seek complete\n", tag(), m_current_cylinder); m_seek_complete = true; - m_seek_complete_cb(this, ASSERT_LINE); + if (!m_seek_complete_cb.isnull()) m_seek_complete_cb(this, ASSERT_LINE); m_step_phase = STEP_COLLECT; } break; @@ -210,7 +262,7 @@ void mfm_harddisk_device::step_w(line_state line) { m_step_phase = STEP_COLLECT; m_seek_complete = false; - m_seek_complete_cb(this, CLEAR_LINE); + if (!m_seek_complete_cb.isnull()) m_seek_complete_cb(this, CLEAR_LINE); } // Counter will be adjusted according to the direction (+-1) @@ -226,6 +278,51 @@ void mfm_harddisk_device::step_w(line_state line) m_step_line = line; } +/* + Reading bytes from the hard disk. + This is the byte-level access to the hard disk. We deliver the next data byte + together with the clock byte. + + Returns true if the time limit will be exceeded before reading the complete byte. + Otherwise returns the byte at the given position together with the clock byte. +*/ +bool mfm_harddisk_device::read(attotime &from_when, const attotime &limit, UINT16 &cdata) +{ + UINT16* track = m_cache->get_trackimage(m_current_cylinder, m_current_head); + + if (track==NULL) + { + // What shall we do in this case? + throw emu_fatalerror("Cannot read CHD image"); + } + + // Calculate the position in the track, given the from_when time and the revolution_start_time. + // Each cell takes 100 ns (10 MHz) + + int cell = (from_when - m_revolution_start_time).as_ticks(10000000); + + from_when += attotime::from_nsec(1600); + if (from_when > limit) return true; + + int position = cell / 16; + + // Reached the end + if (position >= 10416) + { + m_revolution_start_time += m_rev_time; + cell = (from_when - m_revolution_start_time).as_ticks(10000000); + position = cell / 16; + } + + // TODO: fix the actual issue + if (position < 0) position = 0; + + logerror("%s: Reading track %d head %d at position %d\n", tag(), m_current_cylinder, m_current_head, position); + cdata = track[position]; + + return false; +} + mfm_hd_generic_device::mfm_hd_generic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : mfm_harddisk_device(mconfig, MFM_HD_GENERIC, "Generic MFM hard disk (byte level)", tag, owner, clock, "mfm_harddisk", __FILE__) { @@ -233,6 +330,402 @@ mfm_hd_generic_device::mfm_hd_generic_device(const machine_config &mconfig, cons const device_type MFM_HD_GENERIC = &device_creator; +// =========================================================== +// Track cache +// The cache holds track images to be read by the controller. +// This is a write-back LRU cache. +// =========================================================== + +mfmhd_trackimage_cache::mfmhd_trackimage_cache(): + m_tracks(NULL) +{ + m_current_crc = 0; +} + +mfmhd_trackimage_cache::~mfmhd_trackimage_cache() +{ + mfmhd_trackimage* current = m_tracks; + logerror("%s: MFM HD cache destroy\n", tag()); + + // Still dirty? + while (current != NULL) + { + logerror("%s: MFM HD cache: evict line cylinder=%d head=%d\n", tag(), current->cylinder, current->head); + if (current->dirty) write_back(current); + global_free_array(current->encdata); + mfmhd_trackimage* currenttmp = current->next; + global_free(current); + current = currenttmp; + } +} + +/* + Initialize the cache by loading the first tracks. +*/ +void mfmhd_trackimage_cache::init(chd_file* chdfile, const char* dtag, int trackslots, mfmhd_enc_t encoding) +{ + m_encoding = encoding; + m_tagdev = dtag; + + logerror("%s: MFM HD cache init; using encoding %d\n", m_tagdev, encoding); + chd_error state = CHDERR_NONE; + mfmhd_trackimage* previous = NULL; + mfmhd_trackimage* current = NULL; + std::string metadata; + + m_chd = chdfile; + + if (chdfile==NULL) + { + logerror("%s: chdfile is null\n", tag()); + return; + } + + // Read the hard disk metadata + state = chdfile->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata); + if (state != CHDERR_NONE) + { + throw emu_fatalerror("Failed to read CHD metadata"); + } + + // Parse the metadata + if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &m_cylinders, &m_heads, &m_sectors_per_track, &m_sectorsize) != 4) + { + throw emu_fatalerror("Invalid metadata"); + } + + // Load some tracks into the cache + for (int i=0; i < trackslots; i++) + { + logerror("%s: MFM HD allocate cache slot\n", tag()); + previous = current; + current = global_alloc(mfmhd_trackimage); + current->encdata = global_alloc_array(UINT16, TRACKIMAGE_SIZE); + + // Load the first tracks into the slots + state = load_track(current, i, 0, 32, 256, 4); + current->next = NULL; + + if (state != CHDERR_NONE) throw emu_fatalerror("Cannot load cylinder %d head %d from hard disk", i, 0); + + if (previous != NULL) + previous->next = current; + else + // Head + m_tracks = current; + } + + current = m_tracks; + + while (current != NULL) + { + logerror("%s: MFM HD cache: containing line cylinder=%d head=%d\n", tag(), current->cylinder, current->head); + mfmhd_trackimage* currenttmp = current->next; + current = currenttmp; + } +} + +/* + Returns the linear sector number, given the CHS data. + + C,H,S + | 0,0,0 | 0,0,1 | 0,0,2 | ... + | 0,1,0 | 0,1,1 | 0,1,2 | ... + ... + | 1,0,0 | ... + ... +*/ +int mfmhd_trackimage_cache::chs_to_lba(int cylinder, int head, int sector) +{ + if ((cylinder < m_cylinders) && (head < m_heads) && (sector < m_sectors_per_track)) + { + return (cylinder * m_heads + head) * m_sectors_per_track + sector; + } + else return -1; +} + +/* + Delivers the track image. + First look up the track image in the cache. If not present, load it from + the CHD, convert it, and evict the least recently used line. + The searched track will be the first in m_tracks. +*/ +UINT16* mfmhd_trackimage_cache::get_trackimage(int cylinder, int head) +{ + // Search the cached track images + mfmhd_trackimage* current = m_tracks; + mfmhd_trackimage* previous = NULL; + + chd_error state = CHDERR_NONE; + + // Repeat the search. This loop should run at most twice; once for a direct hit, + // and twice on miss, then the second iteration will be a hit. + while (state == CHDERR_NONE) + { + // A simple linear search + while (current != NULL) + { + if (current->cylinder == cylinder && current->head == head) + { + // found it + // move it to the front of the chain for LRU + if (previous != NULL) + { + previous->next = current->next; // pull out here + current->next = m_tracks; // put the previous head into the next field + m_tracks = current; // set this line as new head + } + return current->encdata; + } + else + { + // Don't lose the pointer to the next tail + if (current->next != NULL) previous = current; + current = current->next; + } + } + // If we are here, we have a cache miss. + // Evict the last line + // Load the new line into that line + // Then look it up again, which will move it to the front + + // previous points to the second to last element + current = previous->next; + logerror("%s: MFM HD cache: evict line cylinder=%d head=%d\n", tag(), current->cylinder, current->head); + + if (current->dirty) write_back(current); + state = load_track(current, cylinder, head, 32, 256, 4); + } + // If we are here we have a CHD error. + return NULL; +} + +/* + Create MFM encoding. +*/ +void mfmhd_trackimage_cache::mfm_encode(mfmhd_trackimage* slot, int& position, UINT8 byte, int count) +{ + mfm_encode_mask(slot, position, byte, count, 0x00); +} + +void mfmhd_trackimage_cache::mfm_encode_a1(mfmhd_trackimage* slot, int& position) +{ + m_current_crc = 0xffff; + mfm_encode_mask(slot, position, 0xa1, 1, 0x04); + // 0x443b; CRC for A1 +} + +void mfmhd_trackimage_cache::mfm_encode_mask(mfmhd_trackimage* slot, int& position, UINT8 byte, int count, int mask) +{ + UINT16 encclock = 0; + UINT16 encdata = 0; + UINT8 thisbyte = byte; + bool mark = (mask != 0x00); + + m_current_crc = ccitt_crc16_one(m_current_crc, byte); + + for (int i=0; i < 8; i++) + { + encdata <<= 1; + encclock <<= 1; + + if (m_encoding == MFM_BITS || m_encoding == MFM_BYTE) + { + // skip one position for later interleaving + encdata <<= 1; + encclock <<= 1; + } + + if (thisbyte & 0x80) + { + // Encoding 1 => 01 + encdata |= 1; + m_lastbit = true; + } + else + { + // Encoding 0 => x0 + // If the bit in the mask is set, suppress the clock bit + // Also, if we use the simplified encoding, don't set the clock bits + if (m_lastbit == false && m_encoding != SEPARATED_SIMPLE && (mask & 0x80) == 0) encclock |= 1; + m_lastbit = false; + } + mask <<= 1; + // For simplified encoding, set all clock bits to indicate a mark + if (m_encoding == SEPARATED_SIMPLE && mark) encclock |= 1; + thisbyte <<= 1; + } + + if (m_encoding == MFM_BITS || m_encoding == MFM_BYTE) + encclock <<= 1; + else + encclock <<= 8; + + slot->encdata[position++] = (encclock | encdata); + + // When we write the byte multiple times, check whether the next encoding + // differs from the previous because of the last bit + + if (m_encoding == MFM_BITS || m_encoding == MFM_BYTE) + { + encclock &= 0x7fff; + if ((byte & 0x80)==0 && m_lastbit==false) encclock |= 0x8000; + } + + for (int j=1; j < count; j++) + { + slot->encdata[position++] = (encclock | encdata); + m_current_crc = ccitt_crc16_one(m_current_crc, byte); + } +} + +/* + Load a track from the CHD. +*/ +chd_error mfmhd_trackimage_cache::load_track(mfmhd_trackimage* slot, int cylinder, int head, int sectorcount, int size, int interleave) +{ + chd_error state = CHDERR_NONE; + + UINT8 sector_content[1024]; + + logerror("%s: MFM HD cache: load cylinder=%d head=%d from CHD\n", tag(), cylinder, head); + + m_lastbit = false; + int position = 0; // will be incremented by each encode call + + // Gap 1 + mfm_encode(slot, position, 0x4e, 16); + + int sec_il_start = 0; + int sec_number = 0; + + // Round up + int delta = (sectorcount + interleave-1) / interleave; + + logerror("%02x %02x: ", cylinder&0xff, head&0xff); + for (int sector = 0; sector < sectorcount; sector++) + { + logerror("%02d ", sec_number); + // Sync gap + mfm_encode(slot, position, 0x00, 13); + + // Write IDAM + mfm_encode_a1(slot, position); + mfm_encode(slot, position, 0xfe); // ID field? + + // Write header + mfm_encode(slot, position, cylinder & 0xff); + mfm_encode(slot, position, head & 0xff); + mfm_encode(slot, position, sec_number); + mfm_encode(slot, position, (size >> 7)-1); + + // Write CRC for header. + int crc = m_current_crc; + mfm_encode(slot, position, (crc >> 8) & 0xff); + mfm_encode(slot, position, crc & 0xff); + + // Gap 2 + mfm_encode(slot, position, 0x4e, 3); + + // Sync + mfm_encode(slot, position, 0x00, 13); + + // Write DAM + mfm_encode_a1(slot, position); + mfm_encode(slot, position, 0xfb); + + // Get sector content from CHD + chd_error state = m_chd->read_units(chs_to_lba(cylinder, head, sec_number), sector_content); + if (state != CHDERR_NONE) + break; + + for (int i=0; i < size; i++) + mfm_encode(slot, position, sector_content[i]); + + // Write CRC for content. + crc = m_current_crc; + mfm_encode(slot, position, (crc >> 8) & 0xff); + mfm_encode(slot, position, crc & 0xff); + + // Gap 3 + mfm_encode(slot, position, 0x00, 3); + mfm_encode(slot, position, 0x4e, 19); + + // Calculate next sector number + sec_number += delta; + if (sec_number >= sectorcount) sec_number = ++sec_il_start; + } + + // Gap 4 + if (state == CHDERR_NONE) + { + // Fill the rest with 0x4e + mfm_encode(slot, position, 0x4e, TRACKIMAGE_SIZE-position); + logerror("\n"); + showtrack(slot->encdata, TRACKIMAGE_SIZE); + } + + slot->dirty = false; + slot->cylinder = cylinder; + slot->head = head; + + return state; +} + +/* + TODO: Maybe use a scheduled write-back in addition to the eviction. +*/ +void mfmhd_trackimage_cache::write_back(mfmhd_trackimage* slot) +{ + logerror("%s: MFM HD cache: write back cylinder=%d head=%d to CHD\n", tag(), slot->cylinder, slot->head); + slot->dirty = false; +} + +/* + For debugging. Outputs the byte array in a xxd-like way. +*/ +void mfmhd_trackimage_cache::showtrack(UINT16* enctrack, int length) +{ + for (int i=0; i < length; i+=16) + { + logerror("%07x: ", i); + for (int j=0; j < 16; j++) + { + logerror("%04x ", enctrack[i+j]); + } + logerror(" "); + logerror("\n"); + } +} + +// ================================================================ + +mfm_harddisk_connector::mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock): + device_t(mconfig, MFM_HD_CONNECTOR, "MFM hard disk connector", tag, owner, clock, "mfm_hd_connector", __FILE__), + device_slot_interface(mconfig, *this) +{ +} + +mfm_harddisk_connector::~mfm_harddisk_connector() +{ +} + +mfm_harddisk_device* mfm_harddisk_connector::get_device() +{ + return dynamic_cast(get_card_device()); +} + +void mfm_harddisk_connector::device_config_complete() +{ + mfm_harddisk_device *dev = get_device(); + if (dev != NULL) + dev->set_encoding(m_encoding); +} + +const device_type MFM_HD_CONNECTOR = &device_creator; + +// ================================================================ + // =========================================================================== // Legacy implementation // =========================================================================== diff --git a/src/emu/machine/ti99_hd.h b/src/emu/machine/ti99_hd.h index 7f2a5cddf7b..87de506398f 100644 --- a/src/emu/machine/ti99_hd.h +++ b/src/emu/machine/ti99_hd.h @@ -17,38 +17,112 @@ #include "emu.h" #include "imagedev/harddriv.h" +/* + Determine how data are passed from the hard disk to the controller. We + allow for different degrees of hardware emulation. +*/ +enum mfmhd_enc_t +{ + MFM_BITS, // One bit at a time + MFM_BYTE, // One data byte with interleaved clock bits + SEPARATED, // 8 clock bits (most sig byte), one data byte (least sig byte) + SEPARATED_SIMPLE // MSB: 00/FF (standard / mark) clock, LSB: one data byte +}; + +class mfmhd_trackimage +{ +public: + bool dirty; + int cylinder; + int head; + UINT16* encdata; // MFM encoding per byte + mfmhd_trackimage* next; +}; + +class mfmhd_trackimage_cache +{ +public: + mfmhd_trackimage_cache(); + ~mfmhd_trackimage_cache(); + void init(chd_file* chdfile, const char* tag, int trackslots, mfmhd_enc_t encoding); + UINT16* get_trackimage(int cylinder, int head); + +private: + void mfm_encode(mfmhd_trackimage* slot, int& position, UINT8 byte, int count=1); + void mfm_encode_a1(mfmhd_trackimage* slot, int& position); + void mfm_encode_mask(mfmhd_trackimage* slot, int& position, UINT8 byte, int count, int mask); + chd_error load_track(mfmhd_trackimage* slot, int cylinder, int head, int sectorcount, int size, int interleave); + void write_back(mfmhd_trackimage* timg); + int chs_to_lba(int cylinder, int head, int sector); + chd_file* m_chd; + + const char* m_tagdev; + mfmhd_trackimage* m_tracks; + mfmhd_enc_t m_encoding; + bool m_lastbit; + int m_current_crc; + int m_cylinders; + int m_heads; + int m_sectors_per_track; + int m_sectorsize; + void showtrack(UINT16* enctrack, int length); + const char* tag() { return m_tagdev; } +}; + class mfm_harddisk_device : public harddisk_image_device, public device_slot_card_interface { public: mfm_harddisk_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + ~mfm_harddisk_device(); typedef delegate index_pulse_cb; + typedef delegate ready_cb; typedef delegate seek_complete_cb; void setup_index_pulse_cb(index_pulse_cb cb); + void setup_ready_cb(ready_cb cb); void setup_seek_complete_cb(seek_complete_cb cb); + void set_encoding(mfmhd_enc_t encoding) { m_encoding = encoding; } + mfmhd_enc_t get_encoding() { return m_encoding; } + // Active low lines. We're using ASSERT=0 / CLEAR=1 line_state ready_r() { return m_ready? ASSERT_LINE : CLEAR_LINE; } line_state seek_complete_r() { return m_seek_complete? ASSERT_LINE : CLEAR_LINE; } ; line_state trk00_r() { return m_current_cylinder==0? ASSERT_LINE : CLEAR_LINE; } + // Data output towards controller + bool read(attotime &from_when, const attotime &limit, UINT16 &data); + // Step void step_w(line_state line); void direction_in_w(line_state line); + // Head select + void headsel_w(int head) { m_current_head = head & 0x0f; } + + bool call_load(); + + // Tells us the time when the track ends (next index pulse) + attotime track_end_time(); + protected: void device_start(); + void device_stop(); void device_reset(); - emu_timer *m_index_timer, *m_spinup_timer, *m_seek_timer; - index_pulse_cb m_index_pulse_cb; - seek_complete_cb m_seek_complete_cb; void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + emu_timer *m_index_timer, *m_spinup_timer, *m_seek_timer; + index_pulse_cb m_index_pulse_cb; + ready_cb m_ready_cb; + seek_complete_cb m_seek_complete_cb; + private: + mfmhd_enc_t m_encoding; bool m_ready; int m_current_cylinder; + int m_current_head; int m_track_delta; int m_step_phase; bool m_seek_complete; @@ -57,6 +131,13 @@ private: bool m_autotruncation; line_state m_step_line; // keep the last state + attotime m_spinup_time; + attotime m_revolution_start_time; + attotime m_rev_time; + + mfmhd_trackimage_cache* m_cache; + + void prepare_track(int cylinder, int head); void head_move(); }; @@ -68,6 +149,33 @@ public: extern const device_type MFM_HD_GENERIC; +/* Connector for a MFM hard disk. See also floppy.c */ +class mfm_harddisk_connector : public device_t, + public device_slot_interface +{ +public: + mfm_harddisk_connector(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + ~mfm_harddisk_connector(); + + mfm_harddisk_device *get_device(); + + void set_encoding(mfmhd_enc_t encoding) { m_encoding = encoding; } + +protected: + void device_start() { }; + void device_config_complete(); + +private: + mfmhd_enc_t m_encoding; +}; + +extern const device_type MFM_HD_CONNECTOR; + +#define MCFG_MFM_HARDDISK_ADD(_tag, _slot_intf, _def_slot, _enc) \ + MCFG_DEVICE_ADD(_tag, MFM_HD_CONNECTOR, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast(device)->set_encoding(_enc); + // =========================================================================== // Legacy implementation // ===========================================================================