summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/m68sfdc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/m68sfdc.cpp')
-rw-r--r--src/devices/machine/m68sfdc.cpp869
1 files changed, 869 insertions, 0 deletions
diff --git a/src/devices/machine/m68sfdc.cpp b/src/devices/machine/m68sfdc.cpp
new file mode 100644
index 00000000000..471952cf1a6
--- /dev/null
+++ b/src/devices/machine/m68sfdc.cpp
@@ -0,0 +1,869 @@
+// license:BSD-3-Clause
+// copyright-holders:68bit
+//
+// Motorola M68SFDC floppy disk controller
+//
+// References:
+//
+// "M68SFDC2(D) EXORdisk II Floppy disk controller module - Users's guide.",
+// Motorola, June 1978.
+//
+// "AN-764: A floppy disk controller using the MC6852 SSDA and other M6800
+// microprocessor family parts", Motorola 1976.
+
+#include "emu.h"
+#include "m68sfdc.h"
+
+m68sfdc_device::m68sfdc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
+ device_t(mconfig, M68SFDC, tag, owner, clock),
+ m_pia(*this, "pia"),
+ m_ssda(*this, "ssda"),
+ m_timer_head_load(nullptr),
+ m_timer_timeout(nullptr),
+ m_irq_handler(*this),
+ m_nmi_handler(*this),
+ m_select2_mode(*this, "SELECT2_MODE"),
+ m_select3_mode(*this, "SELECT3_MODE"),
+ m_disk_sides(*this, "DISK_SIDES"),
+ m_write_protect_mode(*this, "WRITE_PROTECT_MODE"),
+ m_stepper_mode(*this, "STEPPER_MODE")
+{
+}
+
+INPUT_PORTS_START(m68sfdc)
+
+ PORT_START("SELECT2_MODE")
+ PORT_CONFNAME(0x01, 0x00, "Select 2 line mode")
+ PORT_CONFSETTING(0, "Not connected")
+ PORT_CONFSETTING(1, "Selects drives 2 and 3")
+
+ PORT_START("SELECT3_MODE")
+ PORT_CONFNAME(0x01, 0x00, "Select 3 line mode")
+ PORT_CONFSETTING(0, "Not connected")
+ PORT_CONFSETTING(1, "Selects drive head")
+
+ PORT_START("DISK_SIDES")
+ PORT_CONFNAME(0x20, 0x20, "Disk sides switch")
+ PORT_CONFSETTING(0x20, "Single sided")
+ PORT_CONFSETTING(0x00, "Double sided")
+
+ PORT_START("WRITE_PROTECT_MODE")
+ PORT_CONFNAME(0x1, 0x1, "Write-enabled line mode")
+ PORT_CONFSETTING(0x0, "Active low write protect")
+ PORT_CONFSETTING(0x1, "Active low write enabled")
+
+ PORT_START("STEPPER_MODE")
+ PORT_CONFNAME(0x1, 0x0, "Stepper control lines mode")
+ PORT_CONFSETTING(0x0, "Conventional")
+ PORT_CONFSETTING(0x1, "'Step' line steps in, 'direction' line steps out")
+
+INPUT_PORTS_END
+
+ioport_constructor m68sfdc_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(m68sfdc);
+}
+
+void m68sfdc_device::device_resolve_objects()
+{
+}
+
+void m68sfdc_device::device_start()
+{
+ m_irq_handler.resolve_safe();
+ m_nmi_handler.resolve_safe();
+
+ m_timer_head_load = timer_alloc(TM_HEAD_LOAD);
+ m_timer_timeout = timer_alloc(TM_TIMEOUT);
+ save_item(NAME(m_select_0));
+ save_item(NAME(m_select_1));
+ save_item(NAME(m_select_2));
+ save_item(NAME(m_select_3));
+ save_item(NAME(m_step));
+ save_item(NAME(m_direction));
+ save_item(NAME(m_head_load1));
+ save_item(NAME(m_head_load2));
+ save_item(NAME(m_head_load));
+ save_item(NAME(m_crc));
+ save_item(NAME(m_last_crc));
+ save_item(NAME(m_pia_ca1));
+ save_item(NAME(m_pia_cb2));
+ save_item(NAME(m_reset));
+ save_item(NAME(m_enable_drive_write));
+ save_item(NAME(m_enable_read));
+ save_item(NAME(m_shift_crc));
+ save_item(NAME(m_shift_crc_count));
+ save_item(NAME(m_tuf_count));
+ save_item(NAME(m_ssda_reg));
+
+ m_floppy = nullptr;
+
+ t_gen = timer_alloc(TM_GEN);
+}
+
+void m68sfdc_device::device_reset()
+{
+ m_select_0 = 0;
+ m_select_1 = 0;
+ m_select_2 = 0;
+ m_select_3 = 0;
+ m_step = 1;
+ m_direction = 0;
+ m_head_load1 = 0;
+ m_head_load2 = 0;
+ m_head_load = 0;
+ m_crc = 0;
+ m_last_crc = 0;
+ m_pia_ca1 = 0;
+ m_pia_cb2 = 0;
+ m_reset = 1;
+ m_enable_drive_write = 0;
+ m_enable_read = 0;
+ m_shift_crc = 0;
+ m_shift_crc_count = 0;
+ m_tuf_count = 0;
+
+ m_irq_handler(false);
+ m_nmi_handler(false);
+}
+
+void m68sfdc_device::set_floppies_4(floppy_connector *f0, floppy_connector *f1, floppy_connector *f2, floppy_connector *f3)
+{
+ m_floppy0 = f0;
+ m_floppy1 = f1;
+ m_floppy2 = f2;
+ m_floppy3 = f3;
+
+ if (m_floppy0)
+ {
+ m_floppy = m_floppy0->get_device();
+ }
+}
+
+WRITE_LINE_MEMBER(m68sfdc_device::handle_irq)
+{
+ m_irq_handler(state);
+}
+
+WRITE_LINE_MEMBER(m68sfdc_device::handle_nmi)
+{
+ m_nmi_handler(state);
+}
+
+void m68sfdc_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TM_HEAD_LOAD:
+ {
+ live_sync();
+ m_head_load2 = 0;
+ u8 head_load = m_head_load1 && m_head_load2;
+ if (head_load != m_head_load)
+ {
+ // TODO sound?
+ m_head_load = head_load;
+ }
+ break;
+ }
+ case TM_TIMEOUT:
+ {
+ live_sync();
+ m_pia->ca1_w(0);
+ m_pia_ca1 = 0;
+ break;
+ }
+ case TM_GEN:
+ live_sync();
+ live_run();
+ break;
+ default:
+ throw emu_fatalerror("Unknown id in m68sfdc_device::device_timer");
+ }
+}
+
+
+
+uint8_t m68sfdc_device::flip_bits(uint8_t data)
+{
+ data = (data & 0b11110000) >> 4 | (data & 0b00001111) << 4;
+ data = (data & 0b11001100) >> 2 | (data & 0b00110011) << 2;
+ data = (data & 0b10101010) >> 1 | (data & 0b01010101) << 1;
+ return data;
+}
+
+
+u8 m68sfdc_device::read(offs_t offset)
+{
+ if (!machine().side_effects_disabled())
+ {
+ live_sync();
+ // Triggers the 0.8 second head-load timer.
+ m_timer_head_load->reset(attotime::from_msec(800));
+ }
+
+ if (offset > 3)
+ {
+ u8 data = m_ssda->read(offset - 4);
+ // The data bits are connected in reverse.
+ data = (data & 0b11110000) >> 4 | (data & 0b00001111) << 4;
+ data = (data & 0b11001100) >> 2 | (data & 0b00110011) << 2;
+ data = (data & 0b10101010) >> 1 | (data & 0b01010101) << 1;
+ return data;
+
+ }
+
+ // The 6821 address lines are swapped.
+ offset = ((offset & 1) << 1) | (offset >> 1);
+ return m_pia->read(offset);
+}
+
+#define C1_RX_RS 0x01
+#define C1_AC_MASK 0xc0
+#define C1_AC_C2 0x00
+#define C2_PC_MASK 0x03
+#define C2_PC1 0x01
+
+
+void m68sfdc_device::write(offs_t offset, u8 data)
+{
+ live_sync();
+
+ // Triggers the 0.8 second head-load timer.
+ m_head_load2 = 1;
+ m_timer_head_load->reset(attotime::from_msec(800));
+
+ if (offset > 3)
+ {
+ // Address line A1 is not decoded for the SSDA
+ offset = (offset - 4) & 0x0001;
+
+ // The data bits are connected in reverse.
+ data = (data & 0b11110000) >> 4 | (data & 0b00001111) << 4;
+ data = (data & 0b11001100) >> 2 | (data & 0b00110011) << 2;
+ data = (data & 0b10101010) >> 1 | (data & 0b01010101) << 1;
+ m_ssda->write(offset, data);
+
+ // Maintain shadow copies of the 6852 register writes.
+ if (offset == 0)
+ m_ssda_reg[0] = data;
+ else
+ m_ssda_reg[(m_ssda_reg[0] >> 6) + 1] = data;
+
+ if (offset == 1 && (m_ssda_reg[0] & C1_AC_MASK) == C1_AC_C2 &&
+ (data & C2_PC_MASK) == C2_PC1 && m_enable_read)
+ {
+ // This a write to the 6852 CR2 register which enables
+ // the SM output (PC2 = 0, PC1 = 1), while the read
+ // logic is enabled. At this point all is setup to
+ // search for a sync code.
+ if (m_reset == 0 && m_enable_read)
+ {
+ live_start(SYNC1);
+ }
+ }
+
+ if (offset == 0 && m_enable_read && (data & C1_RX_RS) != 0)
+ {
+ live_abort();
+ }
+
+ return;
+ }
+
+ // The 6821 address lines are swapped.
+ offset = ((offset & 1) << 1) | (offset >> 1);
+ m_pia->write(offset, data);
+}
+
+
+uint8_t m68sfdc_device::pia_pa_r()
+{
+ int ready = 1;
+ int track0 = 1;
+ if (m_floppy)
+ {
+ ready = m_floppy->ready_r();
+ track0 = m_floppy->trk00_r();
+ }
+
+ // While this is not connected in the schematic, the MDOS 3 format
+ // command probes this input to determine if a disk is to be formatted
+ // singled sided (1) or double sided (0), and it is assumed to be a
+ // later revision.
+ int sides = m_disk_sides->read();
+
+ return (track0 ? 0 : 0x80) | (ready << 6) | sides;
+}
+
+void m68sfdc_device::update_floppy_selection()
+{
+ floppy_image_device *floppy = nullptr;
+ u8 select2_mode = m_select2_mode->read();
+
+ if (select2_mode == 0 || m_select_2 == 0)
+ {
+ if (!m_select_1 && m_select_0)
+ floppy = m_floppy0->get_device();
+ else if (m_select_1 && !m_select_0)
+ floppy = m_floppy1->get_device();
+ }
+ else
+ {
+ if (!m_select_1 && m_select_0)
+ floppy = m_floppy2->get_device();
+ else if (m_select_1 && !m_select_0)
+ floppy = m_floppy3->get_device();
+ }
+
+ if (floppy != m_floppy)
+ {
+ if (m_floppy)
+ {
+ m_floppy->mon_w(1); // Active low
+ m_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
+ }
+ m_floppy = floppy;
+ if (m_floppy)
+ {
+ // Assume the motors are always on?
+ m_floppy->mon_w(0); // Active low
+ if (m_stepper_mode->read())
+ {
+ m_floppy->dir_w(0);
+ m_floppy->stp_w(0);
+ }
+ else
+ {
+ m_floppy->dir_w(m_direction);
+ m_floppy->stp_w(m_step);
+ }
+ m_floppy->ss_w(m_select3_mode->read() ? m_select_3 : 0);
+ m_floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&m68sfdc_device::fdc_index_callback, this));
+ }
+ }
+}
+
+void m68sfdc_device::pia_pa_w(u8 data)
+{
+ // Select 0 and select 1 are used for drive selection. When 0x02 these
+ // select drive 0 or 2, and when 0x01 select drive 1 or 3. These are
+ // used in conjuction with select 2 to decode four drives.
+ m_select_0 = !BIT(data, 0);
+ m_select_1 = !BIT(data, 1);
+ // u8 m_gt_trk43 = !BIT(data, 2);
+ u8 direction = !BIT(data, 3);
+ m_head_load1 = !BIT(data, 4);
+
+ if (m_floppy)
+ {
+ if (m_stepper_mode->read())
+ {
+ if (m_direction == 0 && direction == 1)
+ {
+ m_floppy->dir_w(0);
+ m_floppy->stp_w(1);
+ m_floppy->stp_w(0);
+ }
+ }
+ else
+ {
+ m_floppy->dir_w(m_direction);
+ m_floppy->stp_w(m_step);
+ }
+
+ m_floppy->ss_w(m_select3_mode->read() ? m_select_3 : 0);
+ }
+ m_direction = direction;
+
+ update_floppy_selection();
+
+ u8 head_load = m_head_load1 && m_head_load2;
+ if (head_load != m_head_load)
+ {
+ // TODO sound?
+ m_head_load = head_load;
+ }
+}
+
+int m68sfdc_device::pia_ca1_r()
+{
+ return m_pia_ca1;
+}
+
+void m68sfdc_device::pia_ca2_w(int state)
+{
+ if (m_floppy)
+ {
+ if (m_stepper_mode->read())
+ {
+ if (m_step == 1 && state == 0)
+ {
+ m_floppy->dir_w(1);
+ m_floppy->stp_w(1);
+ m_floppy->stp_w(0);
+ }
+ }
+ else
+ {
+ m_floppy->dir_w(m_direction);
+ m_floppy->stp_w(state);
+ }
+ }
+ m_step = state;
+}
+
+uint8_t m68sfdc_device::pia_pb_r()
+{
+ int wpt = m_floppy ? m_floppy->wpt_r() : 1;
+
+ if (m_write_protect_mode->read())
+ wpt = !wpt;
+
+ return (wpt << 4) | (m_crc << 7);
+}
+
+void m68sfdc_device::pia_pb_w(u8 data)
+{
+ u8 reset = BIT(data, 0);
+ u8 enable_drive_write = !BIT(data, 1);
+ m_enable_read = BIT(data, 2);
+ u8 shift_crc = BIT(data, 3);
+ // Select 2 is used for drive selection in MDOS, expanding the
+ // capability from 2 to 4 drives. A port value of 1 selects drives 0
+ // and 1, and a port value of 0 selects drives 2 and 3.
+ m_select_2 = !BIT(data, 5);
+ // Select 3 is used for head selection in MDOS 3. A port value of 1
+ // selects head 0, and a port value of 0 selects head 1.
+ m_select_3 = !BIT(data, 6);
+
+ int reset_edge = m_reset == 0 && reset == 1;
+ int disable_write_edge = m_enable_drive_write == 1 && enable_drive_write == 0;
+ int enable_write_edge = m_enable_drive_write == 0 && enable_drive_write == 1;
+ int shift_crc_edge = m_shift_crc == 0 && shift_crc == 1;
+
+ m_reset = reset;
+ m_enable_drive_write = enable_drive_write;
+ m_shift_crc = shift_crc;
+
+ if (m_floppy)
+ m_floppy->ss_w(m_select3_mode->read() ? m_select_3 : 0);
+
+ update_floppy_selection();
+
+ if (shift_crc_edge)
+ m_shift_crc_count = 2;
+
+ if (reset_edge)
+ m_shift_crc_count = 0;
+
+ // When reset goes high the read circuit switches to using a 500kHz
+ // clock to search for the sync byte. It also resets the CRC
+ // calculation. A reset may occur during a write, in a format
+ // operation, so don't idle if still writing.
+ if ((reset_edge && !enable_drive_write) || disable_write_edge)
+ {
+ // End of read or write operations.
+ // typically m_enable_read will be low here too.
+ live_abort();
+ }
+
+ if (enable_write_edge && m_floppy && !(m_select_0 && m_select_1))
+ {
+ // Start of write operations, even if the logic is in reset.
+ m_tuf_count = 0;
+ live_start(WRITE);
+ }
+}
+
+int m68sfdc_device::pia_cb1_r()
+{
+ // Index pulse, active high at CB1.
+ if (m_floppy)
+ {
+ int index = m_floppy->idx_r() ? 0 : 1;
+ return index;
+ }
+ return 0;
+}
+
+void m68sfdc_device::pia_cb2_w(int state)
+{
+ if (m_pia_cb2 == 1 && state == 0)
+ {
+ // Trigger the timeout timer on a high to low transition of CB2
+ m_pia->ca1_w(1);
+ m_pia_ca1 = 1;
+ m_timer_timeout->reset(attotime::from_msec(800));
+ }
+ m_pia_cb2 = state;
+}
+
+void m68sfdc_device::fdc_index_callback(floppy_image_device *floppy, int state)
+{
+ live_sync();
+ m_pia->cb1_w(state ? 0 : 1);
+ live_run();
+}
+
+
+void m68sfdc_device::live_start(int state)
+{
+ cur_live.tm = machine().time();
+ cur_live.state = state;
+ cur_live.next_state = -1;
+ cur_live.shift_reg = 0;
+ cur_live.crc = 0xffff;
+ cur_live.bit_counter = 0;
+ cur_live.data_separator_phase = false;
+ cur_live.data_reg = 0;
+
+ pll_reset(cur_live.tm);
+ checkpoint_live = cur_live;
+ pll_save_checkpoint();
+
+ live_run();
+}
+
+void m68sfdc_device::checkpoint()
+{
+ pll_commit(m_floppy, cur_live.tm);
+ checkpoint_live = cur_live;
+ pll_save_checkpoint();
+}
+
+void m68sfdc_device::rollback()
+{
+ cur_live = checkpoint_live;
+ pll_retrieve_checkpoint();
+}
+
+
+void m68sfdc_device::pll_reset(const attotime &when)
+{
+ cur_pll.reset(when);
+ // 500kHz
+ cur_pll.set_clock(attotime::from_nsec(2000));
+}
+
+void m68sfdc_device::live_delay(int state)
+{
+ cur_live.next_state = state;
+ t_gen->adjust(cur_live.tm - machine().time());
+}
+
+void m68sfdc_device::live_sync()
+{
+ if(!cur_live.tm.is_never()) {
+ if(cur_live.tm > machine().time()) {
+ rollback();
+ live_run(machine().time());
+ pll_commit(m_floppy, cur_live.tm);
+ } else {
+ pll_commit(m_floppy, cur_live.tm);
+ if(cur_live.next_state != -1) {
+ cur_live.state = cur_live.next_state;
+ cur_live.next_state = -1;
+ }
+ if(cur_live.state == IDLE) {
+ pll_stop_writing(m_floppy, cur_live.tm);
+ cur_live.tm = attotime::never;
+ }
+ }
+ cur_live.next_state = -1;
+ checkpoint();
+ }
+}
+
+void m68sfdc_device::live_abort()
+{
+ if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) {
+ rollback();
+ live_run(machine().time());
+ }
+
+ pll_stop_writing(m_floppy, cur_live.tm);
+ cur_live.tm = attotime::never;
+ cur_live.state = IDLE;
+ cur_live.next_state = -1;
+}
+
+bool m68sfdc_device::read_one_bit(const attotime &limit)
+{
+ int bit = pll_get_next_bit(cur_live.tm, m_floppy, limit);
+ if(bit < 0)
+ return true;
+ cur_live.shift_reg = (cur_live.shift_reg << 1) | bit;
+ cur_live.bit_counter++;
+ if(cur_live.data_separator_phase) {
+ cur_live.data_reg = (cur_live.data_reg << 1) | bit;
+ if((cur_live.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000)
+ cur_live.crc = (cur_live.crc << 1) ^ 0x1021;
+ else
+ cur_live.crc = cur_live.crc << 1;
+ }
+ cur_live.data_separator_phase = !cur_live.data_separator_phase;
+ return false;
+}
+
+bool m68sfdc_device::write_one_bit(const attotime &limit)
+{
+ bool bit = cur_live.shift_reg & 0x8000;
+ if(pll_write_next_bit(bit, cur_live.tm, m_floppy, limit))
+ return true;
+ if(cur_live.bit_counter & 1) {
+ if((cur_live.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000)
+ cur_live.crc = (cur_live.crc << 1) ^ 0x1021;
+ else
+ cur_live.crc = cur_live.crc << 1;
+ }
+ cur_live.shift_reg = cur_live.shift_reg << 1;
+ cur_live.bit_counter--;
+ return false;
+}
+
+void m68sfdc_device::live_write_fm(uint8_t fm)
+{
+ uint16_t raw = 0xaaaa;
+ for(int i=0; i<8; i++)
+ if(fm & (0x80 >> i))
+ raw |= 0x4000 >> (2*i);
+ cur_live.data_reg = fm;
+ cur_live.shift_reg = raw;
+}
+
+void m68sfdc_device::live_run(attotime limit)
+{
+ if(cur_live.state == IDLE || cur_live.next_state != -1)
+ return;
+
+ if(limit == attotime::never) {
+ if(m_floppy)
+ limit = m_floppy->time_next_index();
+ if(limit == attotime::never) {
+ // Happens when there's no disk or if the wd is not
+ // connected to a drive, hence no index pulse. Force a
+ // sync from time to time in that case, so that the main
+ // cpu timeout isn't too painful. Avoids looping into
+ // infinity looking for data too.
+
+ limit = machine().time() + attotime::from_msec(1);
+ t_gen->adjust(attotime::from_msec(1));
+ }
+ }
+
+ for(;;) {
+ switch(cur_live.state) {
+ case SYNC1: {
+ if(read_one_bit(limit))
+ return;
+
+ // The SSDA performs the sync code search, and the code
+ // will have been loaded into the SSDA sync code
+ // register. This is emulated here, and the code loaded
+ // from a copy of SSDA register writes.
+ int sync = flip_bits(m_ssda_reg[3]);
+
+ // The SSDA searches for only the 8-bit 0xf5 code, and
+ // the CPU loads and checks the subsequent code. The
+ // 0xaa prefix check is an emulator hack for now to
+ // improve detection reliability.
+ if ((cur_live.shift_reg & 0xff) == sync &&
+ (cur_live.shift_reg >> 8) == 0xaa)
+ {
+ // Initialize the CRC. The hardware has an 8
+ // bit shift register to delay the bit stream
+ // so that it can reset the CRC on this sync
+ // event and then feed it the delayed sync
+ // code.
+ cur_live.crc = 0xffff;
+ cur_live.data_separator_phase = false;
+ cur_live.bit_counter = 0;
+ for (int i = 6; i >= 0; i-=2)
+ {
+ int bit = BIT(cur_live.shift_reg, i);
+ if((cur_live.crc ^ (bit ? 0x8000 : 0x0000)) & 0x8000)
+ cur_live.crc = (cur_live.crc << 1) ^ 0x1021;
+ else
+ cur_live.crc = cur_live.crc << 1;
+ }
+ live_delay(SYNC_BYTE1);
+ return;
+ }
+
+ break;
+ }
+ case SYNC_BYTE1:
+ m_ssda->receive_byte(flip_bits(cur_live.shift_reg & 0xff));
+ cur_live.state = SYNC2;
+ checkpoint();
+ break;
+
+ case SYNC2: {
+ if(read_one_bit(limit))
+ return;
+
+ if(cur_live.bit_counter == 8)
+ {
+ live_delay(SYNC_BYTE2);
+ return;
+ }
+
+ break;
+ }
+ case SYNC_BYTE2:
+ m_ssda->receive_byte(flip_bits(cur_live.shift_reg & 0xff));
+ cur_live.bit_counter = 0;
+ cur_live.state = READ;
+ checkpoint();
+ break;
+
+ case READ: {
+ if(read_one_bit(limit))
+ return;
+
+ if(cur_live.bit_counter & 15)
+ break;
+
+ live_delay(READ_BYTE);
+ return;
+ }
+
+ case READ_BYTE:
+ m_ssda->receive_byte(flip_bits(cur_live.data_reg));
+ cur_live.state = READ;
+
+ // The data to the CRC generator is delayed 8 bits behind
+ // the SSDA data input delaying the CRC line.
+ m_crc = m_last_crc;
+ m_last_crc = cur_live.crc != 0;
+
+ // Unfortunately the emulated system can at times read
+ // the CRC line early, the timing needs work, so as a
+ // workaround for now the CRC line is asserted early at
+ // expected CRC end positions: address marks, and 128
+ // and 256 byte data sectors.
+ if (cur_live.bit_counter == (4 + 2) * 16 ||
+ cur_live.bit_counter == (128 + 2) * 16 ||
+ cur_live.bit_counter == (256 + 2) * 16)
+ {
+ m_crc = m_last_crc;
+ }
+
+ checkpoint();
+ break;
+
+ case WRITE:
+ {
+ int tuf;
+ u8 data = flip_bits(m_ssda->get_tx_byte(&tuf));
+
+ if (tuf)
+ {
+ m_tuf_count = 3;
+ }
+ else if (m_tuf_count > 0)
+ {
+ if (m_tuf_count == 2)
+ {
+ // Start of the sync code,
+ // initialize the CRC.
+ cur_live.crc = 0xffff;
+ }
+
+ }
+
+ if (m_tuf_count > 0)
+ {
+ // Data clocked at 500kHz
+ cur_live.shift_reg = data << 8;
+ cur_live.bit_counter = 8;
+ m_tuf_count--;
+ }
+ else
+ {
+ // Data clocked at 250kHz
+
+ // If the 'shift crc' line has been asserted
+ // then write the CRC code rather than the SSDA
+ // data, and for two bytes.
+ if (m_shift_crc_count > 0)
+ {
+ // Two CRC bytes
+ data = cur_live.crc >> 8;
+ m_shift_crc_count--;
+ }
+
+ live_write_fm(data);
+ cur_live.bit_counter = 16;
+ }
+
+ cur_live.state = WRITE_BITS;
+ checkpoint();
+ break;
+ }
+
+ case WRITE_BITS:
+ if(write_one_bit(limit))
+ return;
+ if(cur_live.bit_counter == 0) {
+ live_delay(WRITE);
+ return;
+ }
+ break;
+
+ default:
+ logerror("%s: Unknown live state %d\n", cur_live.tm.to_string(), cur_live.state);
+ return;
+ }
+ }
+}
+
+void m68sfdc_device::pll_commit(floppy_image_device *floppy, const attotime &tm)
+{
+ cur_pll.commit(floppy, tm);
+}
+
+void m68sfdc_device::pll_stop_writing(floppy_image_device *floppy, const attotime &tm)
+{
+ cur_pll.stop_writing(floppy, tm);
+}
+
+void m68sfdc_device::pll_save_checkpoint()
+{
+ checkpoint_pll = cur_pll;
+}
+
+void m68sfdc_device::pll_retrieve_checkpoint()
+{
+ cur_pll = checkpoint_pll;
+}
+
+int m68sfdc_device::pll_get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit)
+{
+ return cur_pll.get_next_bit(tm, m_floppy, limit);
+}
+
+bool m68sfdc_device::pll_write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit)
+{
+ return cur_pll.write_next_bit(bit, tm, m_floppy, limit);
+}
+
+void m68sfdc_device::device_add_mconfig(machine_config &config)
+{
+ PIA6821(config, m_pia, 0);
+ m_pia->readpa_handler().set(FUNC(m68sfdc_device::pia_pa_r));
+ m_pia->writepa_handler().set(FUNC(m68sfdc_device::pia_pa_w));
+ m_pia->readca1_handler().set(FUNC(m68sfdc_device::pia_ca1_r));
+ m_pia->ca2_handler().set(FUNC(m68sfdc_device::pia_ca2_w));
+ m_pia->readpb_handler().set(FUNC(m68sfdc_device::pia_pb_r));
+ m_pia->writepb_handler().set(FUNC(m68sfdc_device::pia_pb_w));
+ m_pia->readcb1_handler().set(FUNC(m68sfdc_device::pia_cb1_r));
+ m_pia->cb2_handler().set(FUNC(m68sfdc_device::pia_cb2_w));
+ m_pia->irqa_handler().set(FUNC(m68sfdc_device::handle_nmi));
+ m_pia->irqb_handler().set(FUNC(m68sfdc_device::handle_irq));
+
+ MC6852(config, m_ssda, 0);
+}
+
+DEFINE_DEVICE_TYPE(M68SFDC, m68sfdc_device, "m68sfdc", "M68SFDC")