summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/upd765.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/upd765.c')
-rw-r--r--src/devices/machine/upd765.c2534
1 files changed, 2534 insertions, 0 deletions
diff --git a/src/devices/machine/upd765.c b/src/devices/machine/upd765.c
new file mode 100644
index 00000000000..e1ce769880e
--- /dev/null
+++ b/src/devices/machine/upd765.c
@@ -0,0 +1,2534 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+#include "debugger.h"
+
+#include "upd765.h"
+
+const device_type UPD765A = &device_creator<upd765a_device>;
+const device_type UPD765B = &device_creator<upd765b_device>;
+const device_type I8272A = &device_creator<i8272a_device>;
+const device_type UPD72065 = &device_creator<upd72065_device>;
+const device_type SMC37C78 = &device_creator<smc37c78_device>;
+const device_type N82077AA = &device_creator<n82077aa_device>;
+const device_type PC_FDC_SUPERIO = &device_creator<pc_fdc_superio_device>;
+const device_type DP8473 = &device_creator<dp8473_device>;
+const device_type PC8477A = &device_creator<pc8477a_device>;
+const device_type WD37C65C = &device_creator<wd37c65c_device>;
+const device_type MCS3201 = &device_creator<mcs3201_device>;
+const device_type TC8566AF = &device_creator<tc8566af_device>;
+
+DEVICE_ADDRESS_MAP_START(map, 8, upd765a_device)
+ AM_RANGE(0x0, 0x0) AM_READ(msr_r)
+ AM_RANGE(0x1, 0x1) AM_READWRITE(fifo_r, fifo_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, upd765b_device)
+ AM_RANGE(0x0, 0x0) AM_READ(msr_r)
+ AM_RANGE(0x1, 0x1) AM_READWRITE(fifo_r, fifo_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, i8272a_device)
+ AM_RANGE(0x0, 0x0) AM_READ(msr_r)
+ AM_RANGE(0x1, 0x1) AM_READWRITE(fifo_r, fifo_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, upd72065_device)
+ AM_RANGE(0x0, 0x0) AM_READ(msr_r)
+ AM_RANGE(0x1, 0x1) AM_READWRITE(fifo_r, fifo_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, smc37c78_device)
+ AM_RANGE(0x2, 0x2) AM_READWRITE(dor_r, dor_w)
+ AM_RANGE(0x3, 0x3) AM_READWRITE(tdr_r, tdr_w)
+ AM_RANGE(0x4, 0x4) AM_READWRITE(msr_r, dsr_w)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ AM_RANGE(0x7, 0x7) AM_READWRITE(dir_r, ccr_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, n82077aa_device)
+ AM_RANGE(0x0, 0x0) AM_READ(sra_r)
+ AM_RANGE(0x1, 0x1) AM_READ(srb_r)
+ AM_RANGE(0x2, 0x2) AM_READWRITE(dor_r, dor_w)
+ AM_RANGE(0x3, 0x3) AM_READWRITE(tdr_r, tdr_w)
+ AM_RANGE(0x4, 0x4) AM_READWRITE(msr_r, dsr_w)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ AM_RANGE(0x7, 0x7) AM_READWRITE(dir_r, ccr_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, pc_fdc_superio_device)
+ AM_RANGE(0x0, 0x0) AM_READ(sra_r)
+ AM_RANGE(0x1, 0x1) AM_READ(srb_r)
+ AM_RANGE(0x2, 0x2) AM_READWRITE(dor_r, dor_w)
+ AM_RANGE(0x3, 0x3) AM_READWRITE(tdr_r, tdr_w)
+ AM_RANGE(0x4, 0x4) AM_READWRITE(msr_r, dsr_w)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ AM_RANGE(0x7, 0x7) AM_READWRITE(dir_r, ccr_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, dp8473_device)
+ AM_RANGE(0x0, 0x0) AM_READ(sra_r)
+ AM_RANGE(0x1, 0x1) AM_READ(srb_r)
+ AM_RANGE(0x2, 0x2) AM_READWRITE(dor_r, dor_w)
+ AM_RANGE(0x3, 0x3) AM_READWRITE(tdr_r, tdr_w)
+ AM_RANGE(0x4, 0x4) AM_READWRITE(msr_r, dsr_w)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ AM_RANGE(0x7, 0x7) AM_READWRITE(dir_r, ccr_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, pc8477a_device)
+ AM_RANGE(0x0, 0x0) AM_READ(sra_r)
+ AM_RANGE(0x1, 0x1) AM_READ(srb_r)
+ AM_RANGE(0x2, 0x2) AM_READWRITE(dor_r, dor_w)
+ AM_RANGE(0x3, 0x3) AM_READWRITE(tdr_r, tdr_w)
+ AM_RANGE(0x4, 0x4) AM_READWRITE(msr_r, dsr_w)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ AM_RANGE(0x7, 0x7) AM_READWRITE(dir_r, ccr_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START(map, 8, wd37c65c_device)
+ AM_RANGE(0x0, 0x0) AM_READ(msr_r)
+ AM_RANGE(0x1, 0x1) AM_READWRITE(fifo_r, fifo_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START( map, 8, mcs3201_device )
+ AM_RANGE(0x0, 0x0) AM_READ(input_r)
+ AM_RANGE(0x2, 0x2) AM_WRITE(dor_w)
+ AM_RANGE(0x4, 0x4) AM_READ(msr_r)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ AM_RANGE(0x7, 0x7) AM_READWRITE(dir_r, ccr_w)
+ADDRESS_MAP_END
+
+DEVICE_ADDRESS_MAP_START( map, 8, tc8566af_device )
+ AM_RANGE(0x2, 0x2) AM_WRITE(dor_w)
+ AM_RANGE(0x3, 0x3) AM_WRITE(cr1_w)
+ AM_RANGE(0x4, 0x4) AM_READ(msr_r)
+ AM_RANGE(0x5, 0x5) AM_READWRITE(fifo_r, fifo_w)
+ADDRESS_MAP_END
+
+
+int upd765_family_device::rates[4] = { 500000, 300000, 250000, 1000000 };
+
+upd765_family_device::upd765_family_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) :
+ pc_fdc_interface(mconfig, type, name, tag, owner, clock, shortname, source),
+ intrq_cb(*this),
+ drq_cb(*this),
+ hdl_cb(*this)
+{
+ ready_polled = true;
+ ready_connected = true;
+ select_connected = true;
+ external_ready = false;
+ dor_reset = 0x00;
+ mode = MODE_AT;
+}
+
+void upd765_family_device::set_ready_line_connected(bool _ready)
+{
+ ready_connected = _ready;
+}
+
+void upd765_family_device::set_select_lines_connected(bool _select)
+{
+ select_connected = _select;
+}
+
+void upd765_family_device::set_mode(int _mode)
+{
+ mode = _mode;
+}
+
+void upd765_family_device::device_start()
+{
+ intrq_cb.resolve_safe();
+ drq_cb.resolve_safe();
+ hdl_cb.resolve_safe();
+
+ for(int i=0; i != 4; i++) {
+ char name[2];
+ flopi[i].tm = timer_alloc(i);
+ flopi[i].id = i;
+ if(select_connected) {
+ name[0] = '0'+i;
+ name[1] = 0;
+ floppy_connector *con = subdevice<floppy_connector>(name);
+ if(con) {
+ flopi[i].dev = con->get_device();
+ if (flopi[i].dev != NULL)
+ flopi[i].dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(FUNC(upd765_family_device::index_callback), this));
+ } else
+ flopi[i].dev = NULL;
+ } else
+ flopi[i].dev = NULL;
+
+ flopi[i].main_state = IDLE;
+ flopi[i].sub_state = IDLE;
+ flopi[i].dir = 0;
+ flopi[i].counter = 0;
+ flopi[i].pcn = 0;
+ flopi[i].st0 = 0;
+ flopi[i].st0_filled = false;
+ flopi[i].live = false;
+ flopi[i].index = false;
+ flopi[i].ready = false;
+ }
+ cur_rate = 250000;
+ tc = false;
+
+ // reset at upper levels may cause a write to tc ending up with
+ // live_sync, which will crash if the live structure isn't
+ // initialized enough
+
+ cur_live.tm = attotime::never;
+ cur_live.state = IDLE;
+ cur_live.next_state = -1;
+ cur_live.fi = NULL;
+
+ if(ready_polled) {
+ poll_timer = timer_alloc(TIMER_DRIVE_READY_POLLING);
+ poll_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(1024));
+ } else
+ poll_timer = NULL;
+
+ cur_irq = false;
+ locked = false;
+}
+
+void upd765_family_device::device_reset()
+{
+ dor = dor_reset;
+ locked = false;
+ soft_reset();
+}
+
+void upd765_family_device::soft_reset()
+{
+ main_phase = PHASE_CMD;
+ for(int i=0; i<4; i++) {
+ flopi[i].main_state = IDLE;
+ flopi[i].sub_state = IDLE;
+ flopi[i].live = false;
+ flopi[i].ready = !ready_polled;
+ flopi[i].st0 = i;
+ flopi[i].st0_filled = false;
+ }
+ data_irq = false;
+ other_irq = false;
+ internal_drq = false;
+ fifo_pos = 0;
+ command_pos = 0;
+ result_pos = 0;
+ if(!locked)
+ fifocfg = FIF_DIS;
+ cur_live.fi = 0;
+ drq = false;
+ cur_live.tm = attotime::never;
+ cur_live.state = IDLE;
+ cur_live.next_state = -1;
+ cur_live.fi = NULL;
+ tc_done = false;
+ st1 = st2 = st3 = 0x00;
+
+ check_irq();
+ if(ready_polled)
+ poll_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(1024));
+}
+
+void upd765_family_device::tc_w(bool _tc)
+{
+ logerror("%s: tc=%d\n", tag(), _tc);
+ if(tc != _tc && _tc) {
+ live_sync();
+ tc_done = true;
+ tc = _tc;
+ if(cur_live.fi)
+ general_continue(*cur_live.fi);
+ } else
+ tc = _tc;
+}
+
+void upd765_family_device::ready_w(bool _ready)
+{
+ external_ready = _ready;
+}
+
+bool upd765_family_device::get_ready(int fid)
+{
+ if(ready_connected)
+ return flopi[fid].dev ? !flopi[fid].dev->ready_r() : false;
+ return !external_ready;
+}
+
+void upd765_family_device::set_floppy(floppy_image_device *flop)
+{
+ for(int fid=0; fid<4; fid++) {
+ if(flopi[fid].dev)
+ flopi[fid].dev->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
+ flopi[fid].dev = flop;
+ }
+ if(flop)
+ flop->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(FUNC(upd765_family_device::index_callback), this));
+}
+
+READ8_MEMBER(upd765_family_device::sra_r)
+{
+ UINT8 sra = 0;
+ int fid = dor & 3;
+ floppy_info &fi = flopi[fid];
+ if(fi.dir)
+ sra |= 0x01;
+ if(fi.index)
+ sra |= 0x04;
+ if(cur_rate >= 500000)
+ sra |= 0x08;
+ if(fi.dev && fi.dev->trk00_r())
+ sra |= 0x10;
+ if(fi.main_state == SEEK_WAIT_STEP_SIGNAL_TIME)
+ sra |= 0x20;
+ sra |= 0x40;
+ if(cur_irq)
+ sra |= 0x80;
+ if(mode == MODE_M30)
+ sra ^= 0x1f;
+ return sra;
+}
+
+READ8_MEMBER(upd765_family_device::srb_r)
+{
+ return 0;
+}
+
+READ8_MEMBER(upd765_family_device::dor_r)
+{
+ return dor;
+}
+
+WRITE8_MEMBER(upd765_family_device::dor_w)
+{
+ logerror("%s: dor = %02x\n", tag(), data);
+ UINT8 diff = dor ^ data;
+ dor = data;
+ if(diff & 4)
+ soft_reset();
+
+ for(int i=0; i<4; i++) {
+ floppy_info &fi = flopi[i];
+ if(fi.dev)
+ fi.dev->mon_w(!(dor & (0x10 << i)));
+ }
+ check_irq();
+}
+
+READ8_MEMBER(upd765_family_device::tdr_r)
+{
+ return 0;
+}
+
+WRITE8_MEMBER(upd765_family_device::tdr_w)
+{
+}
+
+READ8_MEMBER(upd765_family_device::msr_r)
+{
+ UINT32 msr = 0;
+ switch(main_phase) {
+ case PHASE_CMD:
+ msr |= MSR_RQM;
+ if(command_pos)
+ msr |= MSR_CB;
+ break;
+ case PHASE_EXEC:
+ msr |= MSR_CB;
+ if(spec & SPEC_ND)
+ msr |= MSR_EXM;
+ if(internal_drq) {
+ msr |= MSR_RQM;
+ if(!fifo_write)
+ msr |= MSR_DIO;
+ }
+ break;
+
+ case PHASE_RESULT:
+ msr |= MSR_RQM|MSR_DIO|MSR_CB;
+ break;
+ }
+ for(int i=0; i<4; i++)
+ if(flopi[i].main_state == RECALIBRATE || flopi[i].main_state == SEEK) {
+ msr |= 1<<i;
+ //msr |= MSR_CB;
+ }
+
+ if(data_irq) {
+ data_irq = false;
+ check_irq();
+ }
+
+ return msr;
+}
+
+WRITE8_MEMBER(upd765_family_device::dsr_w)
+{
+ logerror("%s: dsr_w %02x\n", tag(), data);
+ if(data & 0x80)
+ soft_reset();
+ dsr = data & 0x7f;
+ cur_rate = rates[dsr & 3];
+}
+
+void upd765_family_device::set_rate(int rate)
+{
+ cur_rate = rate;
+}
+
+READ8_MEMBER(upd765_family_device::fifo_r)
+{
+ UINT8 r = 0xff;
+ switch(main_phase) {
+ case PHASE_EXEC:
+ if(internal_drq)
+ return fifo_pop(false);
+ logerror("%s: fifo_r in phase %d\n", tag(), main_phase);
+ break;
+
+ case PHASE_RESULT:
+ r = result[0];
+ result_pos--;
+ memmove(result, result+1, result_pos);
+ if(!result_pos)
+ main_phase = PHASE_CMD;
+ break;
+ default:
+ logerror("%s: fifo_r in phase %d\n", tag(), main_phase);
+ break;
+ }
+
+ return r;
+}
+
+WRITE8_MEMBER(upd765_family_device::fifo_w)
+{
+ switch(main_phase) {
+ case PHASE_CMD: {
+ command[command_pos++] = data;
+ other_irq = false;
+ check_irq();
+ int cmd = check_command();
+ if(cmd == C_INCOMPLETE)
+ break;
+ if(cmd == C_INVALID) {
+ logerror("%s: Invalid on %02x\n", tag(), command[0]);
+ main_phase = PHASE_RESULT;
+ result[0] = ST0_UNK;
+ result_pos = 1;
+ command_pos = 0;
+ return;
+ }
+ start_command(cmd);
+ break;
+ }
+ case PHASE_EXEC:
+ if(internal_drq) {
+ fifo_push(data, false);
+ return;
+ }
+ logerror("%s: fifo_w in phase %d\n", tag(), main_phase);
+ break;
+
+ default:
+ logerror("%s: fifo_w in phase %d\n", tag(), main_phase);
+ break;
+ }
+}
+
+UINT8 upd765_family_device::do_dir_r()
+{
+ floppy_info &fi = flopi[dor & 3];
+ if(fi.dev)
+ return fi.dev->dskchg_r() ? 0x00 : 0x80;
+ return 0x00;
+}
+
+READ8_MEMBER(upd765_family_device::dir_r)
+{
+ return do_dir_r();
+}
+
+WRITE8_MEMBER(upd765_family_device::ccr_w)
+{
+ dsr = (dsr & 0xfc) | (data & 3);
+ cur_rate = rates[data & 3];
+}
+
+void upd765_family_device::set_drq(bool state)
+{
+ if(state != drq) {
+ drq = state;
+ drq_cb(drq);
+ }
+}
+
+bool upd765_family_device::get_drq() const
+{
+ return drq;
+}
+
+void upd765_family_device::enable_transfer()
+{
+ if(spec & SPEC_ND) {
+ // PIO
+ if(!internal_drq) {
+ internal_drq = true;
+ check_irq();
+ }
+
+ } else {
+ // DMA
+ if(!drq)
+ set_drq(true);
+ }
+}
+
+void upd765_family_device::disable_transfer()
+{
+ if(spec & SPEC_ND) {
+ internal_drq = false;
+ check_irq();
+ } else
+ set_drq(false);
+}
+
+void upd765_family_device::fifo_push(UINT8 data, bool internal)
+{
+ if(fifo_pos == 16) {
+ if(internal) {
+ if(!(st1 & ST1_OR))
+ logerror("%s: Fifo overrun\n", tag());
+ st1 |= ST1_OR;
+ }
+ return;
+ }
+ fifo[fifo_pos++] = data;
+ fifo_expected--;
+
+ int thr = (fifocfg & FIF_THR)+1;
+ if(!fifo_write && (!fifo_expected || fifo_pos >= thr || (fifocfg & FIF_DIS)))
+ enable_transfer();
+ if(fifo_write && (fifo_pos == 16 || !fifo_expected))
+ disable_transfer();
+}
+
+
+UINT8 upd765_family_device::fifo_pop(bool internal)
+{
+ if(!fifo_pos) {
+ if(internal) {
+ if(!(st1 & ST1_OR))
+ logerror("%s: Fifo underrun\n", tag());
+ st1 |= ST1_OR;
+ }
+ return 0;
+ }
+ UINT8 r = fifo[0];
+ fifo_pos--;
+ memmove(fifo, fifo+1, fifo_pos);
+ if(!fifo_write && !fifo_pos)
+ disable_transfer();
+ int thr = fifocfg & 15;
+ if(fifo_write && fifo_expected && (fifo_pos <= thr || (fifocfg & 0x20)))
+ enable_transfer();
+ return r;
+}
+
+void upd765_family_device::fifo_expect(int size, bool write)
+{
+ fifo_expected = size;
+ fifo_write = write;
+ if(fifo_write)
+ enable_transfer();
+}
+
+READ8_MEMBER(upd765_family_device::mdma_r)
+{
+ return dma_r();
+}
+
+WRITE8_MEMBER(upd765_family_device::mdma_w)
+{
+ dma_w(data);
+}
+
+UINT8 upd765_family_device::dma_r()
+{
+ return fifo_pop(false);
+}
+
+void upd765_family_device::dma_w(UINT8 data)
+{
+ fifo_push(data, false);
+}
+
+void upd765_family_device::live_start(floppy_info &fi, int state)
+{
+ cur_live.tm = machine().time();
+ cur_live.state = state;
+ cur_live.next_state = -1;
+ cur_live.fi = &fi;
+ 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;
+ cur_live.previous_type = live_info::PT_NONE;
+ cur_live.data_bit_context = false;
+ cur_live.byte_counter = 0;
+ cur_live.pll.reset(cur_live.tm);
+ cur_live.pll.set_clock(attotime::from_hz(mfm ? 2*cur_rate : cur_rate));
+ checkpoint_live = cur_live;
+ fi.live = true;
+
+ live_run();
+}
+
+void upd765_family_device::checkpoint()
+{
+ if(cur_live.fi)
+ cur_live.pll.commit(cur_live.fi->dev, cur_live.tm);
+ checkpoint_live = cur_live;
+}
+
+void upd765_family_device::rollback()
+{
+ cur_live = checkpoint_live;
+}
+
+void upd765_family_device::live_delay(int state)
+{
+ cur_live.next_state = state;
+ if(cur_live.tm != machine().time())
+ cur_live.fi->tm->adjust(cur_live.tm - machine().time());
+ else
+ live_sync();
+}
+
+void upd765_family_device::live_sync()
+{
+ if(!cur_live.tm.is_never()) {
+ if(cur_live.tm > machine().time()) {
+ rollback();
+ live_run(machine().time());
+ cur_live.pll.commit(cur_live.fi->dev, cur_live.tm);
+ } else {
+ cur_live.pll.commit(cur_live.fi->dev, 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) {
+ cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
+ cur_live.tm = attotime::never;
+ cur_live.fi->live = false;
+ cur_live.fi = 0;
+ }
+ }
+ cur_live.next_state = -1;
+ checkpoint();
+ }
+}
+
+void upd765_family_device::live_abort()
+{
+ if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) {
+ rollback();
+ live_run(machine().time());
+ }
+
+ if(cur_live.fi) {
+ cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
+ cur_live.fi->live = false;
+ cur_live.fi = 0;
+ }
+
+ cur_live.tm = attotime::never;
+ cur_live.state = IDLE;
+ cur_live.next_state = -1;
+}
+
+void upd765_family_device::live_run(attotime limit)
+{
+ if(cur_live.state == IDLE || cur_live.next_state != -1)
+ return;
+
+ if(limit == attotime::never) {
+ if(cur_live.fi->dev)
+ limit = cur_live.fi->dev->time_next_index();
+ if(limit == attotime::never) {
+ // Happens when there's no disk or if the fdc 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);
+ cur_live.fi->tm->adjust(attotime::from_msec(1));
+ }
+ }
+
+ for(;;) {
+ switch(cur_live.state) {
+ case SEARCH_ADDRESS_MARK_HEADER:
+ if(read_one_bit(limit))
+ return;
+#if 0
+ fprintf(stderr, "%s: shift = %04x data=%02x c=%d\n", tts(cur_live.tm).c_str(), cur_live.shift_reg,
+ (cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
+ (cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
+ (cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
+ (cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
+ (cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
+ (cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
+ (cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
+ (cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
+ cur_live.bit_counter);
+#endif
+
+ if(mfm && cur_live.shift_reg == 0x4489) {
+ cur_live.crc = 0x443b;
+ cur_live.data_separator_phase = false;
+ cur_live.bit_counter = 0;
+ cur_live.state = READ_HEADER_BLOCK_HEADER;
+ }
+
+ if(!mfm && cur_live.shift_reg == 0xf57e) {
+ cur_live.crc = 0xef21;
+ cur_live.data_separator_phase = false;
+ cur_live.bit_counter = 0;
+ cur_live.state = READ_ID_BLOCK;
+ }
+ break;
+
+ case READ_HEADER_BLOCK_HEADER: {
+ if(read_one_bit(limit))
+ return;
+#if 0
+ fprintf(stderr, "%s: shift = %04x data=%02x counter=%d\n", tts(cur_live.tm).c_str(), cur_live.shift_reg,
+ (cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
+ (cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
+ (cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
+ (cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
+ (cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
+ (cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
+ (cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
+ (cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
+ cur_live.bit_counter);
+#endif
+ if(cur_live.bit_counter & 15)
+ break;
+
+ int slot = cur_live.bit_counter >> 4;
+
+ if(slot < 3) {
+ if(cur_live.shift_reg != 0x4489)
+ cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
+ break;
+ }
+ if(cur_live.data_reg != 0xfe) {
+ cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
+ break;
+ }
+
+ cur_live.bit_counter = 0;
+ cur_live.state = READ_ID_BLOCK;
+
+ break;
+ }
+
+ case READ_ID_BLOCK: {
+ if(read_one_bit(limit))
+ return;
+ if(cur_live.bit_counter & 15)
+ break;
+ int slot = (cur_live.bit_counter >> 4)-1;
+
+ if(0)
+ fprintf(stderr, "%s: slot=%d data=%02x crc=%04x\n", tts(cur_live.tm).c_str(), slot, cur_live.data_reg, cur_live.crc);
+ cur_live.idbuf[slot] = cur_live.data_reg;
+ if(slot == 5) {
+ live_delay(IDLE);
+ return;
+ }
+ break;
+ }
+
+ case SEARCH_ADDRESS_MARK_DATA:
+ if(read_one_bit(limit))
+ return;
+#if 0
+ fprintf(stderr, "%s: shift = %04x data=%02x c=%d.%x\n", tts(cur_live.tm).c_str(), cur_live.shift_reg,
+ (cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
+ (cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
+ (cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
+ (cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
+ (cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
+ (cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
+ (cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
+ (cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
+ cur_live.bit_counter >> 4, cur_live.bit_counter & 15);
+#endif
+
+ if(mfm) {
+ // Large tolerance due to perpendicular recording at extended density
+ if(cur_live.bit_counter > 62*16) {
+ live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
+ return;
+ }
+
+ if(cur_live.bit_counter >= 28*16 && cur_live.shift_reg == 0x4489) {
+ cur_live.crc = 0x443b;
+ cur_live.data_separator_phase = false;
+ cur_live.bit_counter = 0;
+ cur_live.state = READ_DATA_BLOCK_HEADER;
+ }
+
+ } else {
+ if(cur_live.bit_counter > 23*16) {
+ live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
+ return;
+ }
+
+ if(cur_live.bit_counter >= 11*16 && (cur_live.shift_reg == 0xf56a || cur_live.shift_reg == 0xf56f)) {
+ cur_live.crc = cur_live.shift_reg == 0xf56a ? 0x8fe7 : 0xbf84;
+ cur_live.data_separator_phase = false;
+ cur_live.bit_counter = 0;
+ cur_live.state = READ_SECTOR_DATA;
+ }
+ }
+
+ break;
+
+ case READ_DATA_BLOCK_HEADER: {
+ if(read_one_bit(limit))
+ return;
+#if 0
+ fprintf(stderr, "%s: shift = %04x data=%02x counter=%d\n", tts(cur_live.tm).c_str(), cur_live.shift_reg,
+ (cur_live.shift_reg & 0x4000 ? 0x80 : 0x00) |
+ (cur_live.shift_reg & 0x1000 ? 0x40 : 0x00) |
+ (cur_live.shift_reg & 0x0400 ? 0x20 : 0x00) |
+ (cur_live.shift_reg & 0x0100 ? 0x10 : 0x00) |
+ (cur_live.shift_reg & 0x0040 ? 0x08 : 0x00) |
+ (cur_live.shift_reg & 0x0010 ? 0x04 : 0x00) |
+ (cur_live.shift_reg & 0x0004 ? 0x02 : 0x00) |
+ (cur_live.shift_reg & 0x0001 ? 0x01 : 0x00),
+ cur_live.bit_counter);
+#endif
+ if(cur_live.bit_counter & 15)
+ break;
+
+ int slot = cur_live.bit_counter >> 4;
+
+ if(slot < 3) {
+ if(cur_live.shift_reg != 0x4489) {
+ live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
+ return;
+ }
+ break;
+ }
+ if(cur_live.data_reg != 0xfb && cur_live.data_reg != 0xf8) {
+ live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
+ return;
+ }
+
+ cur_live.bit_counter = 0;
+ cur_live.state = READ_SECTOR_DATA;
+ break;
+ }
+
+ case SEARCH_ADDRESS_MARK_DATA_FAILED:
+ st1 |= ST1_MA;
+ st2 |= ST2_MD;
+ cur_live.state = IDLE;
+ return;
+
+ case READ_SECTOR_DATA: {
+ if(read_one_bit(limit))
+ return;
+ if(cur_live.bit_counter & 15)
+ break;
+ int slot = (cur_live.bit_counter >> 4)-1;
+ if(slot < sector_size) {
+ // Sector data
+ if(cur_live.fi->main_state == SCAN_DATA)
+ live_delay(SCAN_SECTOR_DATA_BYTE);
+ else
+ live_delay(READ_SECTOR_DATA_BYTE);
+ return;
+
+ } else if(slot < sector_size+2) {
+ // CRC
+ if(slot == sector_size+1) {
+ live_delay(IDLE);
+ return;
+ }
+ }
+ break;
+ }
+
+ case READ_SECTOR_DATA_BYTE:
+ if(!tc_done)
+ fifo_push(cur_live.data_reg, true);
+ cur_live.state = READ_SECTOR_DATA;
+ checkpoint();
+ break;
+
+ case SCAN_SECTOR_DATA_BYTE:
+ if(!scan_done) // TODO: handle stp, x68000 sets it to 0xff (as it would dtl)?
+ {
+ int slot = (cur_live.bit_counter >> 4)-1;
+ UINT8 data = fifo_pop(true);
+ if(!slot)
+ st2 = (st2 & ~(ST2_SN)) | ST2_SH;
+
+ if(data != cur_live.data_reg)
+ {
+ st2 = (st2 & ~(ST2_SH)) | ST2_SN;
+ if((data < cur_live.data_reg) && ((command[0] & 0x1f) == 0x19)) // low
+ st2 &= ~ST2_SN;
+
+ if((data > cur_live.data_reg) && ((command[0] & 0x1f) == 0x1d)) // high
+ st2 &= ~ST2_SN;
+ }
+ if((slot == sector_size) && !(st2 & ST2_SN))
+ {
+ scan_done = true;
+ tc_done = true;
+ }
+ }
+ else
+ {
+ if(fifo_pos)
+ fifo_pop(true);
+ }
+ cur_live.state = READ_SECTOR_DATA;
+ checkpoint();
+ break;
+
+ case WRITE_SECTOR_SKIP_GAP2:
+ cur_live.bit_counter = 0;
+ cur_live.byte_counter = 0;
+ cur_live.state = WRITE_SECTOR_SKIP_GAP2_BYTE;
+ checkpoint();
+ break;
+
+ case WRITE_SECTOR_SKIP_GAP2_BYTE:
+ if(read_one_bit(limit))
+ return;
+ if(mfm && cur_live.bit_counter != 22*16)
+ break;
+ if(!mfm && cur_live.bit_counter != 11*16)
+ break;
+ cur_live.bit_counter = 0;
+ cur_live.byte_counter = 0;
+ live_delay(WRITE_SECTOR_DATA);
+ return;
+
+ case WRITE_SECTOR_DATA:
+ if(mfm) {
+ if(cur_live.byte_counter < 12)
+ live_write_mfm(0x00);
+ else if(cur_live.byte_counter < 15)
+ live_write_raw(0x4489);
+ else if(cur_live.byte_counter < 16) {
+ cur_live.crc = 0xcdb4;
+ live_write_mfm(command[0] & 0x08 ? 0xf8 : 0xfb);
+ } else if(cur_live.byte_counter < 16+sector_size)
+ live_write_mfm(tc_done && !fifo_pos? 0x00 : fifo_pop(true));
+ else if(cur_live.byte_counter < 16+sector_size+2)
+ live_write_mfm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter < 16+sector_size+2+command[7])
+ live_write_mfm(0x4e);
+ else {
+ cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
+ cur_live.state = IDLE;
+ return;
+ }
+
+ } else {
+ if(cur_live.byte_counter < 6)
+ live_write_fm(0x00);
+ else if(cur_live.byte_counter < 7) {
+ cur_live.crc = 0xffff;
+ live_write_raw(command[0] & 0x08 ? 0xf56a : 0xf56f);
+ } else if(cur_live.byte_counter < 7+sector_size)
+ live_write_fm(tc_done && !fifo_pos? 0x00 : fifo_pop(true));
+ else if(cur_live.byte_counter < 7+sector_size+2)
+ live_write_fm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter < 7+sector_size+2+command[7])
+ live_write_fm(0xff);
+ else {
+ cur_live.pll.stop_writing(cur_live.fi->dev, cur_live.tm);
+ cur_live.state = IDLE;
+ return;
+ }
+ }
+ cur_live.state = WRITE_SECTOR_DATA_BYTE;
+ cur_live.bit_counter = 16;
+ checkpoint();
+ break;
+
+ case WRITE_TRACK_PRE_SECTORS:
+ if(!cur_live.byte_counter && command[3])
+ fifo_expect(4, true);
+ if(mfm) {
+ if(cur_live.byte_counter < 80)
+ live_write_mfm(0x4e);
+ else if(cur_live.byte_counter < 92)
+ live_write_mfm(0x00);
+ else if(cur_live.byte_counter < 95)
+ live_write_raw(0x5224);
+ else if(cur_live.byte_counter < 96)
+ live_write_mfm(0xfc);
+ else if(cur_live.byte_counter < 146)
+ live_write_mfm(0x4e);
+ else {
+ cur_live.state = WRITE_TRACK_SECTOR;
+ cur_live.byte_counter = 0;
+ break;
+ }
+ } else {
+ if(cur_live.byte_counter < 40)
+ live_write_fm(0xff);
+ else if(cur_live.byte_counter < 46)
+ live_write_fm(0x00);
+ else if(cur_live.byte_counter < 47)
+ live_write_raw(0xf77a);
+ else if(cur_live.byte_counter < 73)
+ live_write_fm(0xff);
+ else {
+ cur_live.state = WRITE_TRACK_SECTOR;
+ cur_live.byte_counter = 0;
+ break;
+ }
+ }
+ cur_live.state = WRITE_TRACK_PRE_SECTORS_BYTE;
+ cur_live.bit_counter = 16;
+ checkpoint();
+ break;
+
+ case WRITE_TRACK_SECTOR:
+ if(!cur_live.byte_counter) {
+ command[3]--;
+ if(command[3])
+ fifo_expect(4, true);
+ }
+ if(mfm) {
+ if(cur_live.byte_counter < 12)
+ live_write_mfm(0x00);
+ else if(cur_live.byte_counter < 15)
+ live_write_raw(0x4489);
+ else if(cur_live.byte_counter < 16) {
+ cur_live.crc = 0xcdb4;
+ live_write_mfm(0xfe);
+ } else if(cur_live.byte_counter < 20) {
+ UINT8 byte = fifo_pop(true);
+ command[12+cur_live.byte_counter-16] = byte;
+ live_write_mfm(byte);
+ if(cur_live.byte_counter == 19)
+ logerror("%s: formatting sector %02x %02x %02x %02x\n",
+ tag(), command[12], command[13], command[14], command[15]);
+ } else if(cur_live.byte_counter < 22)
+ live_write_mfm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter < 44)
+ live_write_mfm(0x4e);
+ else if(cur_live.byte_counter < 56)
+ live_write_mfm(0x00);
+ else if(cur_live.byte_counter < 59)
+ live_write_raw(0x4489);
+ else if(cur_live.byte_counter < 60) {
+ cur_live.crc = 0xcdb4;
+ live_write_mfm(0xfb);
+ } else if(cur_live.byte_counter < 60+sector_size)
+ live_write_mfm(command[5]);
+ else if(cur_live.byte_counter < 62+sector_size)
+ live_write_mfm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter < 62+sector_size+command[4])
+ live_write_mfm(0x4e);
+ else {
+ cur_live.byte_counter = 0;
+ cur_live.state = command[3] ? WRITE_TRACK_SECTOR : WRITE_TRACK_POST_SECTORS;
+ break;
+ }
+
+ } else {
+ if(cur_live.byte_counter < 6)
+ live_write_fm(0x00);
+ else if(cur_live.byte_counter < 7) {
+ cur_live.crc = 0xffff;
+ live_write_raw(0xf57e);
+ } else if(cur_live.byte_counter < 11) {
+ UINT8 byte = fifo_pop(true);
+ command[12+cur_live.byte_counter-7] = byte;
+ live_write_fm(byte);
+ if(cur_live.byte_counter == 10)
+ logerror("%s: formatting sector %02x %02x %02x %02x\n",
+ tag(), command[12], command[13], command[14], command[15]);
+ } else if(cur_live.byte_counter < 13)
+ live_write_fm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter < 24)
+ live_write_fm(0xff);
+ else if(cur_live.byte_counter < 30)
+ live_write_fm(0x00);
+ else if(cur_live.byte_counter < 31) {
+ cur_live.crc = 0xffff;
+ live_write_raw(0xf56f);
+ } else if(cur_live.byte_counter < 31+sector_size)
+ live_write_fm(command[5]);
+ else if(cur_live.byte_counter < 33+sector_size)
+ live_write_fm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter < 33+sector_size+command[4])
+ live_write_fm(0xff);
+ else {
+ cur_live.byte_counter = 0;
+ cur_live.state = command[3] ? WRITE_TRACK_SECTOR : WRITE_TRACK_POST_SECTORS;
+ break;
+ }
+ }
+ cur_live.state = WRITE_TRACK_SECTOR_BYTE;
+ cur_live.bit_counter = 16;
+ checkpoint();
+ break;
+
+ case WRITE_TRACK_POST_SECTORS:
+ if(mfm)
+ live_write_mfm(0x4e);
+ else
+ live_write_fm(0xff);
+ cur_live.state = WRITE_TRACK_POST_SECTORS_BYTE;
+ cur_live.bit_counter = 16;
+ checkpoint();
+ break;
+
+ case WRITE_TRACK_PRE_SECTORS_BYTE:
+ case WRITE_TRACK_SECTOR_BYTE:
+ case WRITE_TRACK_POST_SECTORS_BYTE:
+ case WRITE_SECTOR_DATA_BYTE:
+ if(write_one_bit(limit))
+ return;
+ if(cur_live.bit_counter == 0) {
+ cur_live.byte_counter++;
+ live_delay(cur_live.state-1);
+ return;
+ }
+ break;
+
+ default:
+ logerror("%s: Unknown live state %d\n", tts(cur_live.tm).c_str(), cur_live.state);
+ return;
+ }
+ }
+}
+
+int upd765_family_device::check_command()
+{
+ // 0.000010 read track
+ // 00000011 specify
+ // 00000100 sense drive status
+ // ..000101 write data
+ // ...00110 read data
+ // 00000111 recalibrate
+ // 00001000 sense interrupt status
+ // ..001001 write deleted data
+ // 0.001010 read id
+ // ...01100 read deleted data
+ // 0.001101 format track
+ // 00001110 dumpreg
+ // 00101110 save
+ // 01001110 restore
+ // 10001110 drive specification command
+ // 00001111 seek
+ // 1.001111 relative seek
+ // 00010000 version
+ // ...10001 scan equal
+ // 00010010 perpendicular mode
+ // 00010011 configure
+ // 00110011 option
+ // .0010100 lock
+ // ...10110 verify
+ // 00010111 powerdown mode
+ // 00011000 part id
+ // ...11001 scan low or equal
+ // ...11101 scan high or equal
+
+ // MSDOS 6.22 format uses 0xcd to format a track, which makes one
+ // think only the bottom 5 bits are decoded.
+
+ switch(command[0] & 0x1f) {
+ case 0x02:
+ return command_pos == 9 ? C_READ_TRACK : C_INCOMPLETE;
+
+ case 0x03:
+ return command_pos == 3 ? C_SPECIFY : C_INCOMPLETE;
+
+ case 0x04:
+ return command_pos == 2 ? C_SENSE_DRIVE_STATUS : C_INCOMPLETE;
+
+ case 0x05:
+ case 0x09:
+ return command_pos == 9 ? C_WRITE_DATA : C_INCOMPLETE;
+
+ case 0x06:
+ case 0x0c:
+ return command_pos == 9 ? C_READ_DATA : C_INCOMPLETE;
+
+ case 0x07:
+ return command_pos == 2 ? C_RECALIBRATE : C_INCOMPLETE;
+
+ case 0x08:
+ return C_SENSE_INTERRUPT_STATUS;
+
+ case 0x0a:
+ return command_pos == 2 ? C_READ_ID : C_INCOMPLETE;
+
+ case 0x0d:
+ return command_pos == 6 ? C_FORMAT_TRACK : C_INCOMPLETE;
+
+ case 0x0e:
+ return C_DUMP_REG;
+
+ case 0x0f:
+ return command_pos == 3 ? C_SEEK : C_INCOMPLETE;
+
+ case 0x11:
+ return command_pos == 9 ? C_SCAN_EQUAL : C_INCOMPLETE;
+
+ case 0x12:
+ return command_pos == 2 ? C_PERPENDICULAR : C_INCOMPLETE;
+
+ case 0x13:
+ return command_pos == 4 ? C_CONFIGURE : C_INCOMPLETE;
+
+ case 0x14:
+ return C_LOCK;
+
+ case 0x19:
+ return command_pos == 9 ? C_SCAN_LOW : C_INCOMPLETE;
+
+ case 0x1d:
+ return command_pos == 9 ? C_SCAN_HIGH : C_INCOMPLETE;
+
+ default:
+ return C_INVALID;
+ }
+}
+
+void upd765_family_device::start_command(int cmd)
+{
+ command_pos = 0;
+ result_pos = 0;
+ main_phase = PHASE_EXEC;
+ tc_done = false;
+ switch(cmd) {
+ case C_CONFIGURE:
+ logerror("%s: command configure %02x %02x %02x\n",
+ tag(),
+ command[1], command[2], command[3]);
+ // byte 1 is ignored, byte 3 is precompensation-related
+ fifocfg = command[2];
+ precomp = command[3];
+ main_phase = PHASE_CMD;
+ break;
+
+ case C_DUMP_REG:
+ logerror("%s: command dump regs\n", tag());
+ main_phase = PHASE_RESULT;
+ result[0] = flopi[0].pcn;
+ result[1] = flopi[1].pcn;
+ result[2] = flopi[2].pcn;
+ result[3] = flopi[3].pcn;
+ result[4] = (spec & 0xff00) >> 8;
+ result[5] = (spec & 0x00ff);
+ result[6] = sector_size;
+ result[7] = locked ? 0x80 : 0x00;
+ result[7] |= (perpmode & 0x30);
+ result[8] = fifocfg;
+ result[9] = precomp;
+ result_pos = 10;
+ break;
+
+ case C_FORMAT_TRACK:
+ format_track_start(flopi[command[1] & 3]);
+ break;
+
+ case C_LOCK:
+ locked = command[0] & 0x80;
+ main_phase = PHASE_RESULT;
+ result[0] = locked ? 0x10 : 0x00;
+ result_pos = 1;
+ logerror("%s: command lock (%s)\n", tag(), locked ? "on" : "off");
+ break;
+
+ case C_PERPENDICULAR:
+ logerror("%s: command perpendicular\n", tag());
+ perpmode = command[1];
+ main_phase = PHASE_CMD;
+ break;
+
+ case C_READ_DATA:
+ read_data_start(flopi[command[1] & 3]);
+ break;
+
+ case C_READ_ID:
+ read_id_start(flopi[command[1] & 3]);
+ break;
+
+ case C_READ_TRACK:
+ read_track_start(flopi[command[1] & 3]);
+ break;
+
+ case C_SCAN_EQUAL:
+ case C_SCAN_LOW:
+ case C_SCAN_HIGH:
+ scan_start(flopi[command[1] & 3]);
+ break;
+
+ case C_RECALIBRATE:
+ recalibrate_start(flopi[command[1] & 3]);
+ main_phase = PHASE_CMD;
+ break;
+
+ case C_SEEK:
+ seek_start(flopi[command[1] & 3]);
+ main_phase = PHASE_CMD;
+ break;
+
+ case C_SENSE_DRIVE_STATUS: {
+ floppy_info &fi = flopi[command[1] & 3];
+ main_phase = PHASE_RESULT;
+ result[0] = command[1] & 7;
+ if(fi.ready)
+ result[0] |= ST3_RY;
+ if(fi.dev)
+ result[0] |=
+ (fi.dev->wpt_r() ? ST3_WP : 0x00) |
+ (fi.dev->trk00_r() ? 0x00 : ST3_T0) |
+ (fi.dev->twosid_r() ? 0x00 : ST3_TS);
+ logerror("%s: command sense drive status %d (%02x)\n", tag(), fi.id, result[0]);
+ result_pos = 1;
+ break;
+ }
+
+ case C_SENSE_INTERRUPT_STATUS: {
+ // Documentation is somewhat contradictory w.r.t polling
+ // and irq. PC bios, especially 5150, requires that only
+ // one irq happens. That's also wait the ns82077a doc
+ // says it does. OTOH, a number of docs says you need to
+ // call SIS 4 times, once per drive...
+ //
+ // There's also the interaction with the seek irq. The
+ // somewhat borderline tf20 code seems to think that
+ // essentially ignoring the polling irq should work.
+ //
+ // And the pc98 expects to be able to accumulate irq reasons
+ // for different drives and things to work.
+ //
+ // Current hypothesis:
+ // - each drive has its own st0 and irq trigger
+ // - SIS drops the irq always, but also returns the first full st0 it finds
+
+ main_phase = PHASE_RESULT;
+
+ int fid;
+ for(fid=0; fid<4 && !flopi[fid].st0_filled; fid++);
+ if(fid == 4) {
+ result[0] = ST0_UNK;
+ result_pos = 1;
+ logerror("%s: command sense interrupt status (%02x)\n", tag(), result[0]);
+ break;
+ }
+
+ floppy_info &fi = flopi[fid];
+ fi.st0_filled = false;
+
+ result[0] = fi.st0;
+ result[1] = fi.pcn;
+
+ logerror("%s: command sense interrupt status (fid=%d %02x %02x)\n", tag(), fid, result[0], result[1]);
+ result_pos = 2;
+
+ other_irq = false;
+ check_irq();
+ break;
+ }
+
+ case C_SPECIFY:
+ logerror("%s: command specify %02x %02x\n",
+ tag(),
+ command[1], command[2]);
+ spec = (command[1] << 8) | command[2];
+ main_phase = PHASE_CMD;
+ break;
+
+ case C_WRITE_DATA:
+ write_data_start(flopi[command[1] & 3]);
+ break;
+
+ default:
+ fprintf(stderr, "start command %d\n", cmd);
+ exit(1);
+ }
+}
+
+void upd765_family_device::command_end(floppy_info &fi, bool data_completion)
+{
+ logerror("%s: command done (%s) -", tag(), data_completion ? "data" : "seek");
+ for(int i=0; i != result_pos; i++)
+ logerror(" %02x", result[i]);
+ logerror("\n");
+ fi.main_state = fi.sub_state = IDLE;
+ if(data_completion)
+ data_irq = true;
+ else
+ {
+ other_irq = true;
+ fi.st0_filled = true;
+ }
+ check_irq();
+}
+
+void upd765_family_device::recalibrate_start(floppy_info &fi)
+{
+ logerror("%s: command recalibrate\n", tag());
+ fi.main_state = RECALIBRATE;
+ fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
+ fi.dir = 1;
+ fi.counter = 77;
+ seek_continue(fi);
+}
+
+void upd765_family_device::seek_start(floppy_info &fi)
+{
+ logerror("%s: command %sseek %d\n", tag(), command[0] & 0x80 ? "relative " : "", command[2]);
+ fi.main_state = SEEK;
+ fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
+ fi.dir = fi.pcn > command[2] ? 1 : 0;
+ seek_continue(fi);
+}
+
+void upd765_family_device::delay_cycles(emu_timer *tm, int cycles)
+{
+ tm->adjust(attotime::from_double(double(cycles)/cur_rate));
+}
+
+void upd765_family_device::seek_continue(floppy_info &fi)
+{
+ for(;;) {
+ switch(fi.sub_state) {
+ case SEEK_MOVE:
+ if(fi.dev) {
+ fi.dev->dir_w(fi.dir);
+ fi.dev->stp_w(0);
+ }
+ fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
+ fi.tm->adjust(attotime::from_nsec(2500));
+ return;
+
+ case SEEK_WAIT_STEP_SIGNAL_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
+ if(fi.dev)
+ fi.dev->stp_w(1);
+
+ if(fi.main_state == SEEK) {
+ if(fi.pcn > command[2])
+ fi.pcn--;
+ else
+ fi.pcn++;
+ }
+ fi.sub_state = SEEK_WAIT_STEP_TIME;
+ delay_cycles(fi.tm, 500*(16-(spec >> 12)));
+ return;
+
+ case SEEK_WAIT_STEP_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_TIME_DONE: {
+ bool done = false;
+ switch(fi.main_state) {
+ case RECALIBRATE:
+ fi.counter--;
+ done = fi.dev && !fi.dev->trk00_r();
+ if(done)
+ fi.pcn = 0;
+ else if(!fi.counter) {
+ fi.st0 = ST0_FAIL|ST0_SE|ST0_EC | fi.id;
+ command_end(fi, false);
+ return;
+ }
+ break;
+ case SEEK:
+ done = fi.pcn == command[2];
+ break;
+ }
+ if(done) {
+ fi.st0 = ST0_SE | fi.id;
+ command_end(fi, false);
+ return;
+ }
+ fi.sub_state = SEEK_MOVE;
+ break;
+ }
+ }
+ }
+}
+
+void upd765_family_device::read_data_start(floppy_info &fi)
+{
+ fi.main_state = READ_DATA;
+ fi.sub_state = HEAD_LOAD_DONE;
+ mfm = command[0] & 0x40;
+
+ logerror("%s: command read%s data%s%s%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",
+ tag(),
+ command[0] & 0x08 ? " deleted" : "",
+ command[0] & 0x80 ? " mt" : "",
+ command[0] & 0x40 ? " mfm" : "",
+ command[0] & 0x20 ? " sk" : "",
+ fifocfg & 0x40 ? " seek" : "",
+ command[0],
+ command[1],
+ command[2],
+ command[3],
+ command[4],
+ 128 << (command[5] & 7),
+ command[6],
+ command[7],
+ command[8],
+ cur_rate);
+
+ fi.st0 = command[1] & 7;
+ st1 = ST1_MA;
+ st2 = 0x00;
+ hdl_cb(1);
+ fi.ready = get_ready(command[1] & 3);
+
+ if(!fi.ready)
+ {
+ fi.st0 |= ST0_NR | ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ st1 = 0;
+ st2 = 0;
+ read_data_continue(fi);
+ return;
+ }
+
+ if(fi.dev)
+ fi.dev->ss_w(command[1] & 4 ? 1 : 0);
+ read_data_continue(fi);
+}
+
+void upd765_family_device::scan_start(floppy_info &fi)
+{
+ fi.main_state = SCAN_DATA;
+ fi.sub_state = HEAD_LOAD_DONE;
+ mfm = command[0] & 0x40;
+
+ logerror("%s: command scan%s data%s%s%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x stp=%02x rate=%d\n",
+ tag(),
+ command[0] & 0x08 ? " deleted" : "",
+ command[0] & 0x80 ? " mt" : "",
+ command[0] & 0x40 ? " mfm" : "",
+ command[0] & 0x20 ? " sk" : "",
+ fifocfg & 0x40 ? " seek" : "",
+ command[0],
+ command[1],
+ command[2],
+ command[3],
+ command[4],
+ 128 << (command[5] & 7),
+ command[6],
+ command[7],
+ command[8],
+ cur_rate);
+
+ fi.st0 = command[1] & 7;
+ st1 = ST1_MA;
+ st2 = 0x00;
+ scan_done = false;
+ hdl_cb(1);
+ fi.ready = get_ready(command[1] & 3);
+
+ if(!fi.ready)
+ {
+ fi.st0 |= ST0_NR | ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ st1 = 0;
+ st2 = 0;
+ read_data_continue(fi);
+ return;
+ }
+
+ if(fi.dev)
+ fi.dev->ss_w(command[1] & 4 ? 1 : 0);
+ read_data_continue(fi);
+}
+
+void upd765_family_device::read_data_continue(floppy_info &fi)
+{
+ for(;;) {
+ switch(fi.sub_state) {
+ case HEAD_LOAD_DONE:
+ if(fi.pcn == command[2] || !(fifocfg & 0x40)) {
+ fi.sub_state = SEEK_DONE;
+ break;
+ }
+ fi.st0 |= ST0_SE;
+ if(fi.dev) {
+ fi.dev->dir_w(fi.pcn > command[2] ? 1 : 0);
+ fi.dev->stp_w(0);
+ }
+ fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
+ fi.tm->adjust(attotime::from_nsec(2500));
+ return;
+
+ case SEEK_WAIT_STEP_SIGNAL_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
+ if(fi.dev)
+ fi.dev->stp_w(1);
+
+ fi.sub_state = SEEK_WAIT_STEP_TIME;
+ delay_cycles(fi.tm, 500*(16-(spec >> 12)));
+ return;
+
+ case SEEK_WAIT_STEP_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_TIME_DONE:
+ if(fi.pcn > command[2])
+ fi.pcn--;
+ else
+ fi.pcn++;
+ fi.sub_state = HEAD_LOAD_DONE;
+ break;
+
+ case SEEK_DONE:
+ fi.counter = 0;
+ fi.sub_state = SCAN_ID;
+ live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ if(cur_live.crc) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_DE|ST1_ND;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+ st1 &= ~ST1_MA;
+ if(!sector_matches()) {
+ if(cur_live.idbuf[0] != command[2]) {
+ if(cur_live.idbuf[0] == 0xff)
+ st2 |= ST2_WC|ST2_BC;
+ else
+ st2 |= ST2_WC;
+ fi.st0 |= ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+ live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ logerror("%s: reading sector %02x %02x %02x %02x\n",
+ tag(),
+ cur_live.idbuf[0],
+ cur_live.idbuf[1],
+ cur_live.idbuf[2],
+ cur_live.idbuf[3]);
+ sector_size = calc_sector_size(cur_live.idbuf[3]);
+ if(fi.main_state == SCAN_DATA)
+ fifo_expect(sector_size, true);
+ else
+ fifo_expect(sector_size, false);
+ fi.sub_state = SECTOR_READ;
+ live_start(fi, SEARCH_ADDRESS_MARK_DATA);
+ return;
+
+ case SCAN_ID_FAILED:
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_ND;
+ fi.sub_state = COMMAND_DONE;
+ break;
+
+ case SECTOR_READ: {
+ if(st2 & ST2_MD) {
+ fi.st0 |= ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+ if(cur_live.crc) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_DE;
+ st2 |= ST2_CM;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+ bool done = tc_done;
+ if(command[4] == command[6]) {
+ if(command[0] & 0x80) {
+ command[3] = command[3] ^ 1;
+ command[4] = 1;
+ if(fi.dev)
+ fi.dev->ss_w(command[3] & 1);
+ }
+ if(!(command[0] & 0x80) || !(command[3] & 1)) {
+ if(!tc_done) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_EN;
+ } else {
+ command[2]++;
+ command[4] = 1;
+ }
+ done = true;
+ }
+ } else
+ command[4]++;
+ if(!done) {
+ fi.sub_state = SEEK_DONE;
+ break;
+ }
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+
+ case COMMAND_DONE:
+ main_phase = PHASE_RESULT;
+ result[0] = fi.st0;
+ result[1] = st1;
+ result[2] = st2;
+ result[3] = command[2];
+ result[4] = command[3];
+ result[5] = command[4];
+ result[6] = command[5];
+ result_pos = 7;
+ command_end(fi, true);
+ return;
+
+ default:
+ logerror("%s: read sector unknown sub-state %d\n", ttsn().c_str(), fi.sub_state);
+ return;
+ }
+ }
+}
+
+void upd765_family_device::write_data_start(floppy_info &fi)
+{
+ fi.main_state = WRITE_DATA;
+ fi.sub_state = HEAD_LOAD_DONE;
+ mfm = command[0] & 0x40;
+ logerror("%s: command write%s data%s%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",
+ tag(),
+ command[0] & 0x08 ? " deleted" : "",
+ command[0] & 0x80 ? " mt" : "",
+ command[0] & 0x40 ? " mfm" : "",
+ command[0],
+ command[1],
+ command[2],
+ command[3],
+ command[4],
+ 128 << (command[5] & 7),
+ command[6],
+ command[7],
+ command[8],
+ cur_rate);
+
+ if(fi.dev)
+ fi.dev->ss_w(command[1] & 4 ? 1 : 0);
+
+ fi.st0 = command[1] & 7;
+ st1 = ST1_MA;
+ st2 = 0x00;
+ hdl_cb(1);
+ fi.ready = get_ready(command[1] & 3);
+
+ if(!fi.ready)
+ {
+ fi.st0 |= ST0_NR | ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ st1 = 0;
+ st2 = 0;
+ write_data_continue(fi);
+ return;
+ }
+
+ write_data_continue(fi);
+}
+
+void upd765_family_device::write_data_continue(floppy_info &fi)
+{
+ for(;;) {
+ switch(fi.sub_state) {
+ case HEAD_LOAD_DONE:
+ fi.counter = 0;
+ fi.sub_state = SCAN_ID;
+ live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ if(!sector_matches()) {
+ live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ if(cur_live.crc) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_DE|ST1_ND;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+ st1 &= ~ST1_MA;
+ sector_size = calc_sector_size(cur_live.idbuf[3]);
+ fifo_expect(sector_size, true);
+ fi.sub_state = SECTOR_WRITTEN;
+ live_start(fi, WRITE_SECTOR_SKIP_GAP2);
+ return;
+
+ case SCAN_ID_FAILED:
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_ND;
+ fi.sub_state = COMMAND_DONE;
+ break;
+
+ case SECTOR_WRITTEN: {
+ bool done = tc_done;
+ if(command[4] == command[6]) {
+ if(command[0] & 0x80) {
+ command[3] = command[3] ^ 1;
+ command[4] = 1;
+ if(fi.dev)
+ fi.dev->ss_w(command[3] & 1);
+ }
+ if(!(command[0] & 0x80) || !(command[3] & 1)) {
+ if(!tc_done) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_EN;
+ } else {
+ command[2]++;
+ command[4] = 1;
+ }
+ done = true;
+ }
+ } else
+ command[4]++;
+ if(!done) {
+ fi.sub_state = HEAD_LOAD_DONE;
+ break;
+ }
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+
+ case COMMAND_DONE:
+ main_phase = PHASE_RESULT;
+ result[0] = fi.st0;
+ result[1] = st1;
+ result[2] = st2;
+ result[3] = command[2];
+ result[4] = command[3];
+ result[5] = command[4];
+ result[6] = command[5];
+ result_pos = 7;
+ command_end(fi, true);
+ return;
+
+ default:
+ logerror("%s: write sector unknown sub-state %d\n", ttsn().c_str(), fi.sub_state);
+ return;
+ }
+ }
+}
+
+void upd765_family_device::read_track_start(floppy_info &fi)
+{
+ fi.main_state = READ_TRACK;
+ fi.sub_state = HEAD_LOAD_DONE;
+ mfm = command[0] & 0x40;
+ sectors_read = 0;
+
+ logerror("%s: command read track%s cmd=%02x sel=%x chrn=(%d, %d, %d, %d) eot=%02x gpl=%02x dtl=%02x rate=%d\n",
+ tag(),
+ command[0] & 0x40 ? " mfm" : "",
+ command[0],
+ command[1],
+ command[2],
+ command[3],
+ command[4],
+ 128 << (command[5] & 7),
+ command[6],
+ command[7],
+ command[8],
+ cur_rate);
+ fi.st0 = command[1] & 7;
+ st1 = ST1_MA;
+ st2 = 0x00;
+ hdl_cb(1);
+ fi.ready = get_ready(command[1] & 3);
+
+ if(!fi.ready)
+ {
+ fi.st0 |= ST0_NR | ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ st1 = 0;
+ st2 = 0;
+ read_track_continue(fi);
+ return;
+ }
+
+ if(fi.dev)
+ fi.dev->ss_w(command[1] & 4 ? 1 : 0);
+ read_track_continue(fi);
+}
+
+void upd765_family_device::read_track_continue(floppy_info &fi)
+{
+ for(;;) {
+ switch(fi.sub_state) {
+ case HEAD_LOAD_DONE:
+ if(fi.pcn == command[2] || !(fifocfg & 0x40)) {
+ fi.sub_state = SEEK_DONE;
+ break;
+ }
+ fi.st0 |= ST0_SE;
+ if(fi.dev) {
+ fi.dev->dir_w(fi.pcn > command[2] ? 1 : 0);
+ fi.dev->stp_w(0);
+ }
+ fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
+ fi.tm->adjust(attotime::from_nsec(2500));
+ return;
+
+ case SEEK_WAIT_STEP_SIGNAL_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
+ if(fi.dev)
+ fi.dev->stp_w(1);
+
+ fi.sub_state = SEEK_WAIT_STEP_TIME;
+ delay_cycles(fi.tm, 500*(16-(spec >> 12)));
+ return;
+
+ case SEEK_WAIT_STEP_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_TIME_DONE:
+ if(fi.pcn > command[2])
+ fi.pcn--;
+ else
+ fi.pcn++;
+ fi.sub_state = HEAD_LOAD_DONE;
+ break;
+
+ case SEEK_DONE:
+ fi.counter = 0;
+ fi.sub_state = WAIT_INDEX;
+ return;
+
+ case WAIT_INDEX:
+ return;
+
+ case WAIT_INDEX_DONE:
+ logerror("%s: index found, reading track\n", tag());
+ fi.sub_state = SCAN_ID;
+ live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ if(cur_live.crc) {
+ st1 |= ST1_DE;
+ }
+ st1 &= ~ST1_MA;
+ logerror("%s: reading sector %02x %02x %02x %02x\n",
+ tag(),
+ cur_live.idbuf[0],
+ cur_live.idbuf[1],
+ cur_live.idbuf[2],
+ cur_live.idbuf[3]);
+ if(!sector_matches())
+ st1 |= ST1_ND;
+ else
+ st1 &= ~ST1_ND;
+
+ sector_size = calc_sector_size(cur_live.idbuf[3]);
+ fifo_expect(sector_size, false);
+ fi.sub_state = SECTOR_READ;
+ live_start(fi, SEARCH_ADDRESS_MARK_DATA);
+ return;
+
+ case SCAN_ID_FAILED:
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_ND;
+ fi.sub_state = COMMAND_DONE;
+ break;
+
+ case SECTOR_READ: {
+ if(st2 & ST2_MD) {
+ fi.st0 |= ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+ if(cur_live.crc) {
+ st1 |= ST1_DE;
+ st2 |= ST2_CM;
+ }
+ bool done = tc_done;
+ sectors_read++;
+ if(sectors_read == command[6]) {
+ if(!tc_done) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_EN;
+ }
+ done = true;
+ }
+ if(!done) {
+ fi.sub_state = WAIT_INDEX_DONE;
+ break;
+ }
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+
+ case COMMAND_DONE:
+ main_phase = PHASE_RESULT;
+ result[0] = fi.st0;
+ result[1] = st1;
+ result[2] = st2;
+ result[3] = command[2];
+ result[4] = command[3];
+ result[5] = command[4];
+ result[6] = command[5];
+ result_pos = 7;
+ command_end(fi, true);
+ return;
+
+ default:
+ logerror("%s: read track unknown sub-state %d\n", ttsn().c_str(), fi.sub_state);
+ return;
+ }
+ }
+}
+
+int upd765_family_device::calc_sector_size(UINT8 size)
+{
+ return size > 7 ? 16384 : 128 << size;
+}
+
+void upd765_family_device::format_track_start(floppy_info &fi)
+{
+ fi.main_state = FORMAT_TRACK;
+ fi.sub_state = HEAD_LOAD_DONE;
+ mfm = command[0] & 0x40;
+
+ logerror("%s: command format track %s h=%02x n=%02x sc=%02x gpl=%02x d=%02x\n",
+ tag(),
+ command[0] & 0x40 ? "mfm" : "fm",
+ command[1], command[2], command[3], command[4], command[5]);
+
+ hdl_cb(1);
+ fi.ready = get_ready(command[1] & 3);
+
+ if(!fi.ready)
+ {
+ fi.st0 = (command[1] & 7) | ST0_NR | ST0_FAIL;
+ fi.sub_state = TRACK_DONE;
+ format_track_continue(fi);
+ return;
+ }
+ fi.st0 = command[1] & 7;
+
+ if(fi.dev)
+ fi.dev->ss_w(command[1] & 4 ? 1 : 0);
+ sector_size = calc_sector_size(command[2]);
+
+ format_track_continue(fi);
+}
+
+void upd765_family_device::format_track_continue(floppy_info &fi)
+{
+ for(;;) {
+ switch(fi.sub_state) {
+ case HEAD_LOAD_DONE:
+ fi.sub_state = WAIT_INDEX;
+ break;
+
+ case WAIT_INDEX:
+ return;
+
+ case WAIT_INDEX_DONE:
+ logerror("%s: index found, writing track\n", tag());
+ fi.sub_state = TRACK_DONE;
+ cur_live.pll.start_writing(machine().time());
+ live_start(fi, WRITE_TRACK_PRE_SECTORS);
+ return;
+
+ case TRACK_DONE:
+ main_phase = PHASE_RESULT;
+ result[0] = fi.st0;
+ result[1] = 0;
+ result[2] = 0;
+ result[3] = 0;
+ result[4] = 0;
+ result[5] = 0;
+ result[6] = 0;
+ result_pos = 7;
+ command_end(fi, true);
+ return;
+
+ default:
+ logerror("%s: format track unknown sub-state %d\n", ttsn().c_str(), fi.sub_state);
+ return;
+ }
+ }
+}
+
+void upd765_family_device::read_id_start(floppy_info &fi)
+{
+ fi.main_state = READ_ID;
+ fi.sub_state = HEAD_LOAD_DONE;
+ mfm = command[0] & 0x40;
+
+ logerror("%s: command read id%s, rate=%d\n",
+ tag(),
+ command[0] & 0x40 ? " mfm" : "",
+ cur_rate);
+
+ if(fi.dev)
+ fi.dev->ss_w(command[1] & 4 ? 1 : 0);
+
+ fi.st0 = command[1] & 7;
+ st1 = 0x00;
+ st2 = 0x00;
+
+ for(int i=0; i<4; i++)
+ cur_live.idbuf[i] = 0x00;
+
+ hdl_cb(1);
+ fi.ready = get_ready(command[1] & 3);
+
+ if(!fi.ready)
+ {
+ fi.st0 |= ST0_NR | ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ read_id_continue(fi);
+ return;
+ }
+
+ read_id_continue(fi);
+}
+
+void upd765_family_device::read_id_continue(floppy_info &fi)
+{
+ for(;;) {
+ switch(fi.sub_state) {
+ case HEAD_LOAD_DONE:
+ fi.counter = 0;
+ fi.sub_state = SCAN_ID;
+ live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ if(cur_live.crc) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_MA|ST1_DE|ST1_ND;
+ }
+ fi.sub_state = COMMAND_DONE;
+ break;
+
+ case SCAN_ID_FAILED:
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_ND|ST1_MA;
+ fi.sub_state = COMMAND_DONE;
+ break;
+
+ case COMMAND_DONE:
+ main_phase = PHASE_RESULT;
+ result[0] = fi.st0;
+ result[1] = st1;
+ result[2] = st2;
+ result[3] = cur_live.idbuf[0];
+ result[4] = cur_live.idbuf[1];
+ result[5] = cur_live.idbuf[2];
+ result[6] = cur_live.idbuf[3];
+ result_pos = 7;
+ command_end(fi, true);
+ return;
+
+ default:
+ logerror("%s: read id unknown sub-state %d\n", ttsn().c_str(), fi.sub_state);
+ return;
+ }
+ }
+}
+
+void upd765_family_device::check_irq()
+{
+ bool old_irq = cur_irq;
+ cur_irq = data_irq || other_irq || internal_drq;
+ cur_irq = cur_irq && (dor & 4) && (mode != MODE_AT || (dor & 8));
+ if(cur_irq != old_irq) {
+ logerror("%s: irq = %d\n", tag(), cur_irq);
+ intrq_cb(cur_irq);
+ }
+}
+
+bool upd765_family_device::get_irq() const
+{
+ return cur_irq;
+}
+
+std::string upd765_family_device::tts(attotime t)
+{
+ char buf[256];
+ const char *sign = "";
+ if(t.seconds() < 0) {
+ t = attotime::zero-t;
+ sign = "-";
+ }
+ int nsec = t.attoseconds() / ATTOSECONDS_PER_NANOSECOND;
+ sprintf(buf, "%s%04d.%03d,%03d,%03d", sign, int(t.seconds()), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
+ return buf;
+}
+
+std::string upd765_family_device::ttsn()
+{
+ return tts(machine().time());
+}
+
+void upd765_family_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if(id == TIMER_DRIVE_READY_POLLING) {
+ run_drive_ready_polling();
+ return;
+ }
+
+ live_sync();
+
+ floppy_info &fi = flopi[id];
+ switch(fi.sub_state) {
+ case SEEK_WAIT_STEP_SIGNAL_TIME:
+ fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME_DONE;
+ break;
+ case SEEK_WAIT_STEP_TIME:
+ fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
+ break;
+ }
+
+ general_continue(fi);
+}
+
+void upd765_family_device::run_drive_ready_polling()
+{
+ if(main_phase != PHASE_CMD || (fifocfg & FIF_POLL) || command_pos)
+ return;
+
+ for(int fid=0; fid<4; fid++) {
+ bool ready = get_ready(fid);
+ if(ready != flopi[fid].ready) {
+ logerror("%s: polled %d : %d -> %d\n", tag(), fid, flopi[fid].ready, ready);
+ flopi[fid].ready = ready;
+ if(!flopi[fid].st0_filled) {
+ flopi[fid].st0 = ST0_ABRT | fid;
+ flopi[fid].st0_filled = true;
+ other_irq = true;
+ }
+ }
+ }
+
+ check_irq();
+}
+
+void upd765_family_device::index_callback(floppy_image_device *floppy, int state)
+{
+ for(int fid=0; fid<4; fid++) {
+ floppy_info &fi = flopi[fid];
+ if(fi.dev != floppy)
+ continue;
+
+ if(fi.live)
+ live_sync();
+ fi.index = state;
+
+ if(!state) {
+ general_continue(fi);
+ continue;
+ }
+
+ switch(fi.sub_state) {
+ case IDLE:
+ case SEEK_MOVE:
+ case SEEK_WAIT_STEP_SIGNAL_TIME:
+ case SEEK_WAIT_STEP_SIGNAL_TIME_DONE:
+ case SEEK_WAIT_STEP_TIME:
+ case SEEK_WAIT_STEP_TIME_DONE:
+ case HEAD_LOAD_DONE:
+ case SCAN_ID_FAILED:
+ case SECTOR_READ:
+ break;
+
+ case WAIT_INDEX:
+ fi.sub_state = WAIT_INDEX_DONE;
+ break;
+
+ case SCAN_ID:
+ fi.counter++;
+ if(fi.counter == 2) {
+ fi.sub_state = SCAN_ID_FAILED;
+ live_abort();
+ }
+ break;
+
+ case TRACK_DONE:
+ live_abort();
+ break;
+
+ default:
+ logerror("%s: Index pulse on unknown sub-state %d\n", ttsn().c_str(), fi.sub_state);
+ break;
+ }
+
+ general_continue(fi);
+ }
+}
+
+
+void upd765_family_device::general_continue(floppy_info &fi)
+{
+ if(fi.live && cur_live.state != IDLE) {
+ live_run();
+ if(cur_live.state != IDLE)
+ return;
+ }
+
+ switch(fi.main_state) {
+ case IDLE:
+ break;
+
+ case RECALIBRATE:
+ case SEEK:
+ seek_continue(fi);
+ break;
+
+ case READ_DATA:
+ case SCAN_DATA:
+ read_data_continue(fi);
+ break;
+
+ case WRITE_DATA:
+ write_data_continue(fi);
+ break;
+
+ case READ_TRACK:
+ read_track_continue(fi);
+ break;
+
+ case FORMAT_TRACK:
+ format_track_continue(fi);
+ break;
+
+ case READ_ID:
+ read_id_continue(fi);
+ break;
+
+ default:
+ logerror("%s: general_continue on unknown main-state %d\n", ttsn().c_str(), fi.main_state);
+ break;
+ }
+}
+
+bool upd765_family_device::read_one_bit(const attotime &limit)
+{
+ int bit = cur_live.pll.get_next_bit(cur_live.tm, cur_live.fi->dev, 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 upd765_family_device::write_one_bit(const attotime &limit)
+{
+ bool bit = cur_live.shift_reg & 0x8000;
+ if(cur_live.pll.write_next_bit(bit, cur_live.tm, cur_live.fi->dev, 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 upd765_family_device::live_write_raw(UINT16 raw)
+{
+ // logerror("write %04x %04x\n", raw, cur_live.crc);
+ cur_live.shift_reg = raw;
+ cur_live.data_bit_context = raw & 1;
+}
+
+void upd765_family_device::live_write_mfm(UINT8 mfm)
+{
+ bool context = cur_live.data_bit_context;
+ UINT16 raw = 0;
+ for(int i=0; i<8; i++) {
+ bool bit = mfm & (0x80 >> i);
+ if(!(bit || context))
+ raw |= 0x8000 >> (2*i);
+ if(bit)
+ raw |= 0x4000 >> (2*i);
+ context = bit;
+ }
+ cur_live.data_reg = mfm;
+ cur_live.shift_reg = raw;
+ cur_live.data_bit_context = context;
+ // logerror("write %02x %04x %04x\n", mfm, cur_live.crc, raw);
+}
+
+void upd765_family_device::live_write_fm(UINT8 fm)
+{
+ UINT16 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;
+ cur_live.data_bit_context = fm & 1;
+ // logerror("write %02x %04x %04x\n", fm, cur_live.crc, raw);
+}
+
+bool upd765_family_device::sector_matches() const
+{
+ if(0)
+ logerror("%s: matching %02x %02x %02x %02x - %02x %02x %02x %02x\n", tag(),
+ cur_live.idbuf[0], cur_live.idbuf[1], cur_live.idbuf[2], cur_live.idbuf[3],
+ command[2], command[3], command[4], command[5]);
+ return
+ cur_live.idbuf[0] == command[2] &&
+ cur_live.idbuf[1] == command[3] &&
+ cur_live.idbuf[2] == command[4] &&
+ cur_live.idbuf[3] == command[5];
+}
+
+upd765a_device::upd765a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, UPD765A, "UPD765A", tag, owner, clock, "upd765a", __FILE__)
+{
+ dor_reset = 0x0c;
+}
+
+upd765b_device::upd765b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, UPD765B, "UPD765B", tag, owner, clock, "upd765b", __FILE__)
+{
+ dor_reset = 0x0c;
+}
+
+i8272a_device::i8272a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, I8272A, "I8272A", tag, owner, clock, "i8272a", __FILE__)
+{
+ dor_reset = 0x0c;
+}
+
+upd72065_device::upd72065_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, UPD72065, "UPD72065", tag, owner, clock, "upd72065", __FILE__)
+{
+ dor_reset = 0x0c;
+}
+
+smc37c78_device::smc37c78_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, SMC37C78, "SMC37C78", tag, owner, clock, "smc37c78", __FILE__)
+{
+ ready_connected = false;
+ select_connected = true;
+}
+
+n82077aa_device::n82077aa_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, N82077AA, "N82077AA", tag, owner, clock, "n82077aa", __FILE__)
+{
+ ready_connected = false;
+ select_connected = true;
+}
+
+pc_fdc_superio_device::pc_fdc_superio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, PC_FDC_SUPERIO, "PC FDC SUPERIO", tag, owner, clock, "pc_fdc_superio", __FILE__)
+{
+ ready_polled = false;
+ ready_connected = false;
+ select_connected = true;
+}
+
+dp8473_device::dp8473_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, PC_FDC_SUPERIO, "DP8473", tag, owner, clock, "dp8473", __FILE__)
+{
+ ready_polled = false;
+ ready_connected = false;
+ select_connected = true;
+}
+
+pc8477a_device::pc8477a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, PC_FDC_SUPERIO, "PC8477A", tag, owner, clock, "pc8477a", __FILE__)
+{
+ ready_polled = true;
+ ready_connected = false;
+ select_connected = true;
+}
+
+wd37c65c_device::wd37c65c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : upd765_family_device(mconfig, PC_FDC_SUPERIO, "WD37C65C", tag, owner, clock, "wd37c65c", __FILE__)
+{
+ ready_polled = true;
+ ready_connected = false;
+ select_connected = true;
+}
+
+mcs3201_device::mcs3201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ upd765_family_device(mconfig, MCS3201, "Motorola MCS3201", tag, owner, clock, "mcs3201", __FILE__),
+ m_input_handler(*this)
+{
+ dor_reset = 0x0c;
+ ready_polled = false;
+ ready_connected = false;
+ select_connected = true;
+}
+
+void mcs3201_device::device_start()
+{
+ upd765_family_device::device_start();
+ m_input_handler.resolve_safe(0);
+}
+
+READ8_MEMBER( mcs3201_device::input_r )
+{
+ return m_input_handler();
+}
+
+tc8566af_device::tc8566af_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : upd765_family_device(mconfig, TC8566AF, "TC8566AF", tag, owner, clock, "tc8566af", __FILE__)
+ , m_cr1(0)
+{
+ ready_polled = true;
+ ready_connected = true;
+ select_connected = true;
+}
+
+void tc8566af_device::device_start()
+{
+ upd765_family_device::device_start();
+ save_item(NAME(m_cr1));
+}
+
+WRITE8_MEMBER(tc8566af_device::cr1_w)
+{
+ m_cr1 = data;
+
+ if (m_cr1 & 0x02)
+ {
+ // Not sure if this inverted or not
+ tc_w((m_cr1 & 0x01) ? true : false);
+ }
+}