summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/machine/wd1772.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/machine/wd1772.c')
-rw-r--r--src/mess/machine/wd1772.c1779
1 files changed, 1779 insertions, 0 deletions
diff --git a/src/mess/machine/wd1772.c b/src/mess/machine/wd1772.c
new file mode 100644
index 00000000000..43db7961c4d
--- /dev/null
+++ b/src/mess/machine/wd1772.c
@@ -0,0 +1,1779 @@
+/***************************************************************************
+
+ Copyright Olivier Galibert
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+****************************************************************************/
+
+#include "wd1772.h"
+
+#include "debugger.h"
+
+const device_type WD1770x = &device_creator<wd1770_t>;
+const device_type WD1772x = &device_creator<wd1772_t>;
+const device_type WD1773x = &device_creator<wd1773_t>;
+
+wd177x_t::wd177x_t(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, type, name, tag, owner, clock)
+{
+}
+
+void wd177x_t::device_start()
+{
+ t_gen = timer_alloc(TM_GEN);
+ t_cmd = timer_alloc(TM_CMD);
+ t_track = timer_alloc(TM_TRACK);
+ t_sector = timer_alloc(TM_SECTOR);
+ dden = false;
+ floppy = 0;
+
+ save_item(NAME(status));
+ save_item(NAME(command));
+ save_item(NAME(main_state));
+ save_item(NAME(sub_state));
+ save_item(NAME(track));
+ save_item(NAME(sector));
+ save_item(NAME(cmd_buffer));
+ save_item(NAME(track_buffer));
+ save_item(NAME(sector_buffer));
+ save_item(NAME(counter));
+ save_item(NAME(status_type_1));
+ save_item(NAME(last_dir));
+}
+
+void wd177x_t::device_reset()
+{
+ command = 0x00;
+ main_state = IDLE;
+ sub_state = IDLE;
+ cur_live.state = IDLE;
+ track = 0x00;
+ sector = 0x00;
+ status = 0x00;
+ data = 0x00;
+ cmd_buffer = track_buffer = sector_buffer = -1;
+ counter = 0;
+ status_type_1 = true;
+ last_dir = 1;
+ intrq = false;
+ drq = false;
+ live_abort();
+}
+
+void wd177x_t::set_floppy(floppy_image_device *_floppy)
+{
+ if(floppy == _floppy)
+ return;
+
+ if(floppy) {
+ floppy->mon_w(1);
+ floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
+ }
+
+ floppy = _floppy;
+
+ if(floppy) {
+ if(has_motor())
+ floppy->mon_w(status & S_MON ? 0 : 1);
+ floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(FUNC(wd177x_t::index_callback), this));
+ }
+}
+
+void wd177x_t::setup_intrq_cb(line_cb cb)
+{
+ intrq_cb = cb;
+}
+
+void wd177x_t::setup_drq_cb(line_cb cb)
+{
+ drq_cb = cb;
+}
+
+void wd177x_t::dden_w(bool _dden)
+{
+ dden = _dden;
+}
+
+astring wd177x_t::tts(attotime t)
+{
+ char buf[256];
+ int nsec = t.attoseconds / ATTOSECONDS_PER_NANOSECOND;
+ sprintf(buf, "%4d.%03d,%03d,%03d", int(t.seconds), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
+ return buf;
+}
+
+astring wd177x_t::ttsn()
+{
+ return tts(machine().time());
+}
+
+void wd177x_t::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ live_sync();
+
+ switch(id) {
+ case TM_GEN: do_generic(); break;
+ case TM_CMD: do_cmd_w(); break;
+ case TM_TRACK: do_track_w(); break;
+ case TM_SECTOR: do_sector_w(); break;
+ }
+
+ general_continue();
+}
+
+void wd177x_t::command_end()
+{
+ main_state = sub_state = IDLE;
+ status &= ~S_BUSY;
+ intrq = true;
+ motor_timeout = 0;
+ if(!intrq_cb.isnull())
+ intrq_cb(intrq);
+}
+
+void wd177x_t::seek_start(int state)
+{
+ main_state = state;
+ status = (status & ~(S_CRC|S_RNF|S_SPIN)) | S_BUSY;
+ sub_state = has_motor() ? SPINUP : SPINUP_DONE;
+ status_type_1 = true;
+ seek_continue();
+}
+
+void wd177x_t::seek_continue()
+{
+ for(;;) {
+ switch(sub_state) {
+ case SPINUP:
+ if(!(status & S_MON)) {
+ spinup();
+ return;
+ }
+ if(!(command & 0x08))
+ status |= S_SPIN;
+ sub_state = SPINUP_DONE;
+ break;
+
+ case SPINUP_WAIT:
+ return;
+
+ case SPINUP_DONE:
+ if(main_state == RESTORE && floppy && !floppy->trk00_r())
+ sub_state = SEEK_DONE;
+
+ if(main_state == SEEK && track == data)
+ sub_state = SEEK_DONE;
+
+ if(sub_state == SPINUP_DONE) {
+ counter = 0;
+ sub_state = SEEK_MOVE;
+ }
+ break;
+
+ case SEEK_MOVE:
+ if(floppy) {
+ floppy->dir_w(last_dir);
+ floppy->stp_w(0);
+ floppy->stp_w(1);
+
+ // When stepping with update, the track register is updated before seeking.
+ // Important for the sam coupe format code.
+ if(main_state == STEP && (command & 0x10))
+ track += last_dir ? -1 : 1;
+ }
+ counter++;
+ sub_state = SEEK_WAIT_STEP_TIME;
+ delay_cycles(t_gen, step_time(command & 3));
+ return;
+
+ case SEEK_WAIT_STEP_TIME:
+ return;
+
+ case SEEK_WAIT_STEP_TIME_DONE: {
+ bool done = false;
+ switch(main_state) {
+ case RESTORE:
+ done = !floppy || !floppy->trk00_r();
+ break;
+ case SEEK:
+ track += last_dir ? -1 : 1;
+ done = track == data;
+ break;
+ case STEP:
+ done = true;
+ break;
+ }
+
+ if(done || counter == 255) {
+ if(main_state == RESTORE)
+ track = 0;
+
+ if(command & 0x04) {
+ sub_state = SEEK_WAIT_STABILIZATION_TIME;
+ delay_cycles(t_gen, 120000);
+ return;
+ } else
+ sub_state = SEEK_DONE;
+
+ } else
+ sub_state = SEEK_MOVE;
+
+ break;
+ }
+
+ case SEEK_WAIT_STABILIZATION_TIME:
+ return;
+
+ case SEEK_WAIT_STABILIZATION_TIME_DONE:
+ sub_state = SEEK_DONE;
+ break;
+
+ case SEEK_DONE:
+ if(command & 0x04) {
+ sub_state = SCAN_ID;
+ counter = 0;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ command_end();
+ return;
+
+ case SCAN_ID:
+ if(cur_live.idbuf[0] != track) {
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ if(cur_live.crc) {
+ status |= S_CRC;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ command_end();
+ return;
+
+ case SCAN_ID_FAILED:
+ status |= S_RNF;
+ command_end();
+ return;
+
+ default:
+ logerror("%s: seek unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ return;
+ }
+ }
+}
+
+bool wd177x_t::sector_matches() const
+{
+ if(cur_live.idbuf[0] != track || cur_live.idbuf[2] != sector)
+ return false;
+ if(!has_side_check())
+ return true;
+ if(command & 8)
+ return cur_live.idbuf[1] & 1;
+ else
+ return !(cur_live.idbuf[1] & 1);
+}
+
+void wd177x_t::read_sector_start()
+{
+ main_state = READ_SECTOR;
+ status = (status & ~(S_CRC|S_LOST|S_RNF|S_WP|S_DDM)) | S_BUSY;
+ drop_drq();
+ sub_state = has_motor() ? SPINUP : SPINUP_DONE;
+ status_type_1 = false;
+ read_sector_continue();
+}
+
+void wd177x_t::read_sector_continue()
+{
+ for(;;) {
+ switch(sub_state) {
+ case SPINUP:
+ if(!(status & S_MON)) {
+ spinup();
+ return;
+ }
+ sub_state = SPINUP_DONE;
+ break;
+
+ case SPINUP_WAIT:
+ return;
+
+ case SPINUP_DONE:
+ if(command & 4) {
+ sub_state = SETTLE_WAIT;
+ delay_cycles(t_gen, settle_time());
+ return;
+ } else {
+ sub_state = SETTLE_DONE;
+ break;
+ }
+
+ case SETTLE_WAIT:
+ return;
+
+ case SETTLE_DONE:
+ sub_state = SCAN_ID;
+ counter = 0;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ if(!sector_matches()) {
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ if(cur_live.crc) {
+ status |= S_CRC;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ sector_size = 128 << (cur_live.idbuf[3] & 3);
+ sub_state = SECTOR_READ;
+ live_start(SEARCH_ADDRESS_MARK_DATA);
+ return;
+
+ case SCAN_ID_FAILED:
+ status |= S_RNF;
+ command_end();
+ return;
+
+ case SECTOR_READ:
+ if(cur_live.crc)
+ status |= S_CRC;
+
+ if(command & 0x10 && !(status & S_RNF)) {
+ sector++;
+ sub_state = SETTLE_DONE;
+ } else {
+ command_end();
+ return;
+ }
+ break;
+
+ default:
+ logerror("%s: read sector unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ return;
+ }
+ }
+}
+
+void wd177x_t::read_track_start()
+{
+ main_state = READ_TRACK;
+ status = (status & ~(S_LOST|S_RNF)) | S_BUSY;
+ drop_drq();
+ sub_state = has_motor() ? SPINUP : SPINUP_DONE;
+ status_type_1 = false;
+ read_track_continue();
+}
+
+void wd177x_t::read_track_continue()
+{
+ for(;;) {
+ switch(sub_state) {
+ case SPINUP:
+ if(!(status & S_MON)) {
+ spinup();
+ return;
+ }
+ sub_state = SPINUP_DONE;
+ break;
+
+ case SPINUP_WAIT:
+ return;
+
+ case SPINUP_DONE:
+ if(command & 4) {
+ sub_state = SETTLE_WAIT;
+ delay_cycles(t_gen, settle_time());
+ return;
+
+ } else {
+ sub_state = SETTLE_DONE;
+ break;
+ }
+
+ case SETTLE_WAIT:
+ return;
+
+ case SETTLE_DONE:
+ sub_state = WAIT_INDEX;
+ return;
+
+ case WAIT_INDEX:
+ return;
+
+ case WAIT_INDEX_DONE:
+ sub_state = TRACK_DONE;
+ live_start(READ_TRACK_DATA);
+ return;
+
+ case TRACK_DONE:
+ command_end();
+ return;
+
+ default:
+ logerror("%s: read track unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ return;
+ }
+ }
+}
+
+void wd177x_t::read_id_start()
+{
+ main_state = READ_ID;
+ status = (status & ~(S_WP|S_DDM|S_LOST|S_RNF)) | S_BUSY;
+ drop_drq();
+ sub_state = has_motor() ? SPINUP : SPINUP_DONE;
+ status_type_1 = false;
+ read_id_continue();
+}
+
+void wd177x_t::read_id_continue()
+{
+ for(;;) {
+ switch(sub_state) {
+ case SPINUP:
+ if(!(status & S_MON)) {
+ spinup();
+ return;
+ }
+ sub_state = SPINUP_DONE;
+ break;
+
+ case SPINUP_WAIT:
+ return;
+
+ case SPINUP_DONE:
+ if(command & 4) {
+ sub_state = SETTLE_WAIT;
+ delay_cycles(t_gen, settle_time());
+ return;
+ } else {
+ sub_state = SETTLE_DONE;
+ break;
+ }
+
+ case SETTLE_WAIT:
+ return;
+
+ case SETTLE_DONE:
+ sub_state = SCAN_ID;
+ counter = 0;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ command_end();
+ return;
+
+ case SCAN_ID_FAILED:
+ status |= S_RNF;
+ command_end();
+ return;
+
+ default:
+ logerror("%s: read id unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ return;
+ }
+ }
+}
+
+void wd177x_t::write_track_start()
+{
+ main_state = WRITE_TRACK;
+ status = (status & ~(S_WP|S_DDM|S_LOST|S_RNF)) | S_BUSY;
+ drop_drq();
+ sub_state = has_motor() ? SPINUP : SPINUP_DONE;
+ status_type_1 = false;
+ write_track_continue();
+}
+
+void wd177x_t::write_track_continue()
+{
+ for(;;) {
+ switch(sub_state) {
+ case SPINUP:
+ if(!(status & S_MON)) {
+ spinup();
+ return;
+ }
+ sub_state = SPINUP_DONE;
+ break;
+
+ case SPINUP_WAIT:
+ return;
+
+ case SPINUP_DONE:
+ if(command & 4) {
+ sub_state = SETTLE_WAIT;
+ delay_cycles(t_gen, settle_time());
+ return;
+ } else {
+ sub_state = SETTLE_DONE;
+ break;
+ }
+
+ case SETTLE_WAIT:
+ return;
+
+ case SETTLE_DONE:
+ set_drq();
+ sub_state = DATA_LOAD_WAIT;
+ delay_cycles(t_gen, 768);
+ return;
+
+ case DATA_LOAD_WAIT:
+ return;
+
+ case DATA_LOAD_WAIT_DONE:
+ if(drq) {
+ status |= S_LOST;
+ command_end();
+ return;
+ }
+ sub_state = WAIT_INDEX;
+ break;
+
+ case WAIT_INDEX:
+ return;
+
+ case WAIT_INDEX_DONE:
+ sub_state = TRACK_DONE;
+ live_start(WRITE_TRACK_DATA);
+ cur_live.pll.start_writing(machine().time());
+ return;
+
+ case TRACK_DONE:
+ command_end();
+ return;
+
+ default:
+ logerror("%s: write track unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ return;
+ }
+ }
+}
+
+
+void wd177x_t::write_sector_start()
+{
+ main_state = WRITE_SECTOR;
+ status = (status & ~(S_CRC|S_LOST|S_RNF|S_WP|S_DDM)) | S_BUSY;
+ drop_drq();
+ sub_state = has_motor() ? SPINUP : SPINUP_DONE;
+ status_type_1 = false;
+ write_sector_continue();
+}
+
+void wd177x_t::write_sector_continue()
+{
+ for(;;) {
+ switch(sub_state) {
+ case SPINUP:
+ if(!(status & S_MON)) {
+ spinup();
+ return;
+ }
+ sub_state = SPINUP_DONE;
+ break;
+
+ case SPINUP_WAIT:
+ return;
+
+ case SPINUP_DONE:
+ if(command & 4) {
+ sub_state = SETTLE_WAIT;
+ delay_cycles(t_gen, settle_time());
+ return;
+ } else {
+ sub_state = SETTLE_DONE;
+ break;
+ }
+
+ case SETTLE_WAIT:
+ return;
+
+ case SETTLE_DONE:
+ sub_state = SCAN_ID;
+ counter = 0;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+
+ case SCAN_ID:
+ if(!sector_matches()) {
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ if(cur_live.crc) {
+ status |= S_CRC;
+ live_start(SEARCH_ADDRESS_MARK_HEADER);
+ return;
+ }
+ sector_size = 128 << (cur_live.idbuf[3] & 3);
+ sub_state = SECTOR_WRITE;
+ live_start(WRITE_SECTOR_PRE);
+ return;
+
+ case SCAN_ID_FAILED:
+ status |= S_RNF;
+ command_end();
+ return;
+
+ case SECTOR_WRITE:
+ if(command & 0x10) {
+ sector++;
+ sub_state = SPINUP_DONE;
+ } else {
+ command_end();
+ return;
+ }
+ break;
+
+ default:
+ logerror("%s: write sector unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ return;
+ }
+ }
+}
+
+void wd177x_t::interrupt_start()
+{
+ if(status & S_BUSY) {
+ main_state = sub_state = cur_live.state = IDLE;
+ cur_live.tm = attotime::never;
+ status &= ~S_BUSY;
+ drop_drq();
+ motor_timeout = 0;
+ }
+ if(command & 0x0f) {
+ logerror("%s: unhandled interrupt generation (%02x)\n", ttsn().cstr(), command);
+ }
+}
+
+void wd177x_t::general_continue()
+{
+ if(cur_live.state != IDLE) {
+ live_run();
+ if(cur_live.state != IDLE)
+ return;
+ }
+
+ switch(main_state) {
+ case IDLE:
+ break;
+ case RESTORE: case SEEK: case STEP:
+ seek_continue();
+ break;
+ case READ_SECTOR:
+ read_sector_continue();
+ break;
+ case READ_TRACK:
+ read_track_continue();
+ break;
+ case READ_ID:
+ read_id_continue();
+ break;
+ case WRITE_TRACK:
+ write_track_continue();
+ break;
+ case WRITE_SECTOR:
+ write_sector_continue();
+ break;
+ default:
+ logerror("%s: general_continue on unknown main-state %d\n", ttsn().cstr(), main_state);
+ break;
+ }
+}
+
+void wd177x_t::do_generic()
+{
+ switch(sub_state) {
+ case IDLE:
+ case SCAN_ID:
+ case SECTOR_READ:
+ break;
+
+ case SETTLE_WAIT:
+ sub_state = SETTLE_DONE;
+ break;
+
+ case SEEK_WAIT_STEP_TIME:
+ sub_state = SEEK_WAIT_STEP_TIME_DONE;
+ break;
+
+ case SEEK_WAIT_STABILIZATION_TIME:
+ sub_state = SEEK_WAIT_STABILIZATION_TIME_DONE;
+ break;
+
+ case DATA_LOAD_WAIT:
+ sub_state = DATA_LOAD_WAIT_DONE;
+ break;
+
+ default:
+ if(cur_live.tm.is_never())
+ logerror("%s: do_generic on unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ break;
+ }
+}
+
+void wd177x_t::do_cmd_w()
+{
+ // fprintf(stderr, "%s: command %02x\n", ttsn().cstr(), cmd_buffer);
+
+ // Only available command when busy is interrupt
+ if(main_state != IDLE && (cmd_buffer & 0xf0) != 0xd0) {
+ cmd_buffer = -1;
+ return;
+ }
+ command = cmd_buffer;
+ cmd_buffer = -1;
+
+ switch(command & 0xf0) {
+ case 0x00: logerror("wd1772: restore\n"); last_dir = 1; seek_start(RESTORE); break;
+ case 0x10: logerror("wd1772: seek %d\n", data); last_dir = data > track ? 0 : 1; seek_start(SEEK); break;
+ case 0x20: case 0x30: logerror("wd1772: step\n"); seek_start(STEP); break;
+ case 0x40: case 0x50: logerror("wd1772: step +\n"); last_dir = 0; seek_start(STEP); break;
+ case 0x60: case 0x70: logerror("wd1772: step -\n"); last_dir = 1; seek_start(STEP); break;
+ case 0x80: case 0x90: logerror("wd1772: read sector%s %d, %d\n", command & 0x10 ? " multiple" : "", track, sector); read_sector_start(); break;
+ case 0xa0: case 0xb0: logerror("wd1772: write sector%s %d, %d\n", command & 0x10 ? " multiple" : "", track, sector); write_sector_start(); break;
+ case 0xc0: logerror("wd1772: read id\n"); read_id_start(); break;
+ case 0xd0: logerror("wd1772: interrupt\n"); interrupt_start(); break;
+ case 0xe0: logerror("wd1772: read track %d\n", track); read_track_start(); break;
+ case 0xf0: logerror("wd1772: write track %d\n", track); write_track_start(); break;
+ }
+}
+
+void wd177x_t::cmd_w(UINT8 val)
+{
+ if(intrq && ((command & 0xf8) != 0xd8)) {
+ intrq = false;
+ if(!intrq_cb.isnull())
+ intrq_cb(intrq);
+ }
+
+ // No more than one write in flight
+ if(cmd_buffer != -1)
+ return;
+
+ cmd_buffer = val;
+
+ delay_cycles(t_cmd, dden ? 384 : 184);
+}
+
+UINT8 wd177x_t::status_r()
+{
+ if(intrq && ((command & 0xf8) != 0xd8)) {
+ intrq = false;
+ if(!intrq_cb.isnull())
+ intrq_cb(intrq);
+ }
+
+ if(status_type_1) {
+ status &= ~(S_TR00|S_WP);
+ if(floppy) {
+ if(floppy->wpt_r())
+ status |= S_WP;
+ if(!floppy->trk00_r())
+ status |= S_TR00;
+ }
+ }
+
+ if(main_state == IDLE || status_type_1) {
+ if(floppy && floppy->idx_r())
+ status |= S_IP;
+ else
+ status &= ~S_IP;
+ } else {
+ if(drq)
+ status |= S_DRQ;
+ else
+ status &= ~S_DRQ;
+ }
+
+ return status;
+}
+
+void wd177x_t::do_track_w()
+{
+ track = track_buffer;
+ track_buffer = -1;
+}
+
+void wd177x_t::track_w(UINT8 val)
+{
+ // No more than one write in flight
+ if(track_buffer != -1)
+ return;
+
+ track_buffer = val;
+ delay_cycles(t_track, dden ? 256 : 128);
+}
+
+UINT8 wd177x_t::track_r()
+{
+ return track;
+}
+
+void wd177x_t::do_sector_w()
+{
+ sector = sector_buffer;
+ sector_buffer = -1;
+}
+
+void wd177x_t::sector_w(UINT8 val)
+{
+ // No more than one write in flight
+ if(sector_buffer != -1)
+ return;
+
+ sector_buffer = val;
+ delay_cycles(t_sector, dden ? 256 : 128);
+}
+
+UINT8 wd177x_t::sector_r()
+{
+ return sector;
+}
+
+void wd177x_t::data_w(UINT8 val)
+{
+ data = val;
+ drop_drq();
+}
+
+UINT8 wd177x_t::data_r()
+{
+ drop_drq();
+ return data;
+}
+
+void wd177x_t::gen_w(int reg, UINT8 val)
+{
+ switch(reg) {
+ case 0: cmd_w(val); break;
+ case 1: track_w(val); break;
+ case 2: sector_w(val); break;
+ case 3: data_w(val); break;
+ }
+}
+
+UINT8 wd177x_t::gen_r(int reg)
+{
+ switch(reg) {
+ case 0: return status_r(); break;
+ case 1: return track_r(); break;
+ case 2: return sector_r(); break;
+ case 3: return data_r(); break;
+ }
+ return 0xff;
+}
+
+void wd177x_t::delay_cycles(emu_timer *tm, int cycles)
+{
+ tm->adjust(clocks_to_attotime(cycles));
+}
+
+void wd177x_t::spinup()
+{
+ if(command & 0x08)
+ sub_state = SPINUP_DONE;
+ else {
+ sub_state = SPINUP_WAIT;
+ counter = 0;
+ }
+
+ status |= S_MON;
+ if(floppy)
+ floppy->mon_w(0);
+
+}
+
+void wd177x_t::index_callback(floppy_image_device *floppy, int state)
+{
+ live_sync();
+
+ if(!state) {
+ general_continue();
+ return;
+ }
+
+ switch(sub_state) {
+ case IDLE:
+ if(has_motor()) {
+ motor_timeout ++;
+ if(motor_timeout >= 5) {
+ status &= ~S_MON;
+ if(floppy)
+ floppy->mon_w(1);
+ }
+ }
+ break;
+
+ case SPINUP:
+ break;
+
+ case SPINUP_WAIT:
+ counter++;
+ if(counter == 6) {
+ sub_state = SPINUP_DONE;
+ if(status_type_1)
+ status |= S_SPIN;
+ }
+ break;
+
+ case SPINUP_DONE:
+ case SETTLE_WAIT:
+ case SETTLE_DONE:
+ case DATA_LOAD_WAIT:
+ case DATA_LOAD_WAIT_DONE:
+ case SEEK_MOVE:
+ case SEEK_WAIT_STEP_TIME:
+ case SEEK_WAIT_STEP_TIME_DONE:
+ case SEEK_WAIT_STABILIZATION_TIME:
+ case SEEK_WAIT_STABILIZATION_TIME_DONE:
+ case SEEK_DONE:
+ case WAIT_INDEX_DONE:
+ case SCAN_ID_FAILED:
+ case SECTOR_READ:
+ case SECTOR_WRITE:
+ break;
+
+ case SCAN_ID:
+ counter++;
+ if(counter == 5) {
+ sub_state = SCAN_ID_FAILED;
+ live_abort();
+ }
+ break;
+
+ case WAIT_INDEX:
+ sub_state = WAIT_INDEX_DONE;
+ break;
+
+ case TRACK_DONE:
+ live_abort();
+ break;
+
+ default:
+ logerror("%s: Index pulse on unknown sub-state %d\n", ttsn().cstr(), sub_state);
+ break;
+ }
+
+ general_continue();
+}
+
+bool wd177x_t::intrq_r()
+{
+ return intrq;
+}
+
+bool wd177x_t::drq_r()
+{
+ return drq;
+}
+
+void wd177x_t::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;
+ 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(clocks_to_attotime(1));
+ checkpoint_live = cur_live;
+
+ live_run();
+}
+
+void wd177x_t::checkpoint()
+{
+ cur_live.pll.commit(floppy, cur_live.tm);
+ checkpoint_live = cur_live;
+}
+
+void wd177x_t::rollback()
+{
+ cur_live = checkpoint_live;
+}
+
+void wd177x_t::live_delay(int state)
+{
+ cur_live.next_state = state;
+ t_gen->adjust(cur_live.tm - machine().time());
+}
+
+void wd177x_t::live_sync()
+{
+ if(!cur_live.tm.is_never()) {
+ if(cur_live.tm > machine().time()) {
+ // fprintf(stderr, "%s: Rolling back and replaying (%s)\n", ttsn().cstr(), tts(cur_live.tm).cstr());
+ rollback();
+ live_run(machine().time());
+ cur_live.pll.commit(floppy, cur_live.tm);
+ } else {
+ // fprintf(stderr, "%s: Committing (%s)\n", ttsn().cstr(), tts(cur_live.tm).cstr());
+ cur_live.pll.commit(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) {
+ cur_live.pll.stop_writing(floppy, cur_live.tm);
+ cur_live.tm = attotime::never;
+ }
+ }
+ cur_live.next_state = -1;
+ checkpoint();
+ }
+}
+
+void wd177x_t::live_abort()
+{
+ if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) {
+ rollback();
+ live_run(machine().time());
+ }
+
+ cur_live.pll.stop_writing(floppy, cur_live.tm);
+ cur_live.tm = attotime::never;
+ cur_live.state = IDLE;
+ cur_live.next_state = -1;
+}
+
+bool wd177x_t::read_one_bit(attotime limit)
+{
+ int bit = cur_live.pll.get_next_bit(cur_live.tm, 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 wd177x_t::write_one_bit(attotime limit)
+{
+ bool bit = cur_live.shift_reg & 0x8000;
+ if(cur_live.pll.write_next_bit(bit, cur_live.tm, 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 wd177x_t::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 wd177x_t::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.shift_reg = raw;
+ cur_live.data_bit_context = context;
+ // logerror("write %02x %04x %04x\n", mfm, cur_live.crc, raw);
+}
+
+void wd177x_t::live_run(attotime limit)
+{
+ if(cur_live.state == IDLE || cur_live.next_state != -1)
+ return;
+
+ if(limit == attotime::never) {
+ if(floppy)
+ limit = 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));
+ }
+ }
+
+ // fprintf(stderr, "%s: live_run(%s)\n", ttsn().cstr(), tts(limit).cstr());
+
+ 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).cstr(), 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.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;
+ }
+ 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).cstr(), 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.data_reg != 0xff) {
+ cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
+ break;
+ }
+
+ cur_live.bit_counter = 0;
+
+ if(main_state == READ_ID)
+ cur_live.state = READ_ID_BLOCK_TO_DMA;
+ else
+ cur_live.state = READ_ID_BLOCK_TO_LOCAL;
+
+ break;
+ }
+
+ case READ_ID_BLOCK_TO_LOCAL: {
+ if(read_one_bit(limit))
+ return;
+ if(cur_live.bit_counter & 15)
+ break;
+ int slot = (cur_live.bit_counter >> 4)-1;
+ cur_live.idbuf[slot] = cur_live.data_reg;
+ if(slot == 5) {
+ live_delay(IDLE);
+ return;
+ }
+ break;
+ }
+
+ case READ_ID_BLOCK_TO_DMA: {
+ if(read_one_bit(limit))
+ return;
+ if(cur_live.bit_counter & 15)
+ break;
+ live_delay(READ_ID_BLOCK_TO_DMA_BYTE);
+ return;
+ }
+
+ case READ_ID_BLOCK_TO_DMA_BYTE:
+ data = cur_live.data_reg;
+ if(cur_live.bit_counter == 16)
+ sector = data;
+ set_drq();
+
+ if(cur_live.bit_counter == 16*6) {
+ // Already synchronous
+ cur_live.state = IDLE;
+ return;
+ }
+
+ cur_live.state = READ_ID_BLOCK_TO_DMA;
+ checkpoint();
+ 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).cstr(), 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(cur_live.bit_counter > 43*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;
+ }
+ 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).cstr(), 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 & 0xfe) != 0xfa && (cur_live.data_reg & 0xfe) != 0xfc) {
+ 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:
+ status |= S_RNF;
+ 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
+ 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:
+ data = cur_live.data_reg;
+ set_drq();
+ cur_live.state = READ_SECTOR_DATA;
+ checkpoint();
+ break;
+
+ case READ_TRACK_DATA: {
+ if(read_one_bit(limit))
+ return;
+ if(cur_live.bit_counter != 16
+ && cur_live.shift_reg != 0x4489
+ && cur_live.shift_reg != 0x5224)
+ break;
+
+ // Incorrect, hmmm
+ // Probably >2 + not just after a sync if <16
+
+ // Transitions 00..00 -> 4489.4489.4489 at varied syncs:
+ // 0: 00.00.14.a1 1: ff.fe.c2.a1 2: 00.01.14.a1 3: ff.fc.c2.a1
+ // 4: 00.02.14.a1 5: ff.f8.c2.a1 6: 00.05.14.a1 7: ff.f0.c2.a1
+ // 8: 00.00.0a.a1 9: ff.ff.e1.a1 10: 00.00.14.a1 11: ff.ff.ce.a1
+ // 12: 00.00.14.a1 13: ff.ff.c2.a1 14: 00.00.14.a1 15: ff.ff.c2.a1
+
+ bool output_byte = cur_live.bit_counter > 5;
+
+ cur_live.data_separator_phase = false;
+ cur_live.bit_counter = 0;
+
+ if(output_byte) {
+ live_delay(READ_TRACK_DATA_BYTE);
+ return;
+ }
+
+ break;
+ }
+
+ case READ_TRACK_DATA_BYTE:
+ data = cur_live.data_reg;
+ set_drq();
+ cur_live.state = READ_TRACK_DATA;
+ checkpoint();
+ break;
+
+ case WRITE_TRACK_DATA:
+ if(drq) {
+ status |= S_LOST;
+ data = 0;
+ }
+
+ switch(data) {
+ case 0xf5:
+ live_write_raw(0x4489);
+ cur_live.crc = 0x968b; // Ensures that the crc is cdb4 after writing the byte
+ cur_live.previous_type = live_info::PT_NONE;
+ break;
+ case 0xf6:
+ cur_live.previous_type = live_info::PT_NONE;
+ live_write_raw(0x5224);
+ break;
+ case 0xf7:
+ if(cur_live.previous_type == live_info::PT_CRC_2) {
+ cur_live.previous_type = live_info::PT_NONE;
+ live_write_mfm(0xf7);
+ } else {
+ cur_live.previous_type = live_info::PT_CRC_1;
+ live_write_mfm(cur_live.crc >> 8);
+ }
+ break;
+ default:
+ cur_live.previous_type = live_info::PT_NONE;
+ live_write_mfm(data);
+ break;
+ }
+ set_drq();
+ cur_live.state = WRITE_BYTE;
+ cur_live.bit_counter = 16;
+ checkpoint();
+ break;
+
+ case WRITE_BYTE:
+ if(write_one_bit(limit))
+ return;
+ if(cur_live.bit_counter == 0) {
+ live_delay(WRITE_BYTE_DONE);
+ return;
+ }
+ break;
+
+ case WRITE_BYTE_DONE:
+ switch(sub_state) {
+ case TRACK_DONE:
+ if(cur_live.previous_type == live_info::PT_CRC_1) {
+ cur_live.previous_type = live_info::PT_CRC_2;
+ live_write_mfm(cur_live.crc >> 8);
+ cur_live.state = WRITE_BYTE;
+ cur_live.bit_counter = 16;
+ checkpoint();
+ } else
+ cur_live.state = WRITE_TRACK_DATA;
+ break;
+
+ case SECTOR_WRITE:
+ cur_live.state = WRITE_BYTE;
+ cur_live.bit_counter = 16;
+ cur_live.byte_counter++;
+ if(cur_live.byte_counter <= 11)
+ live_write_mfm(0x00);
+ else if(cur_live.byte_counter == 12) {
+ cur_live.crc = 0xffff;
+ live_write_raw(0x4489);
+ } else if(cur_live.byte_counter <= 14)
+ live_write_raw(0x4489);
+ else if(cur_live.byte_counter == 15)
+ live_write_mfm(command & 1 ? 0xf8 : 0xfb);
+ else if(cur_live.byte_counter <= sector_size + 16-2) {
+ if(drq) {
+ status |= S_LOST;
+ data = 0;
+ }
+ live_write_mfm(data);
+ set_drq();
+ } else if(cur_live.byte_counter == sector_size + 16-1) {
+ if(drq) {
+ status |= S_LOST;
+ data = 0;
+ }
+ live_write_mfm(data);
+ } else if(cur_live.byte_counter <= sector_size + 16+1)
+ live_write_mfm(cur_live.crc >> 8);
+ else if(cur_live.byte_counter == sector_size + 16+2)
+ // Is that correct? It seems required (try ST formatting)
+ live_write_mfm(0xff);
+ else {
+ cur_live.pll.stop_writing(floppy, cur_live.tm);
+ cur_live.state = IDLE;
+ return;
+ }
+
+ checkpoint();
+ break;
+
+ default:
+ logerror("%s: Unknown sub state %d in WRITE_BYTE_DONE\n", tts(cur_live.tm).cstr(), sub_state);
+ live_abort();
+ return;
+ }
+ break;
+
+ case WRITE_SECTOR_PRE:
+ if(read_one_bit(limit))
+ return;
+ if(cur_live.bit_counter != 16)
+ break;
+ live_delay(WRITE_SECTOR_PRE_BYTE);
+ return;
+
+ case WRITE_SECTOR_PRE_BYTE:
+ cur_live.state = WRITE_SECTOR_PRE;
+ cur_live.byte_counter++;
+ cur_live.bit_counter = 0;
+ switch(cur_live.byte_counter) {
+ case 2:
+ set_drq();
+ checkpoint();
+ break;
+ case 11:
+ if(drq) {
+ status |= S_LOST;
+ cur_live.state = IDLE;
+ return;
+ }
+ break;
+ case 22:
+ cur_live.state = WRITE_BYTE;
+ cur_live.bit_counter = 16;
+ cur_live.byte_counter = 0;
+ cur_live.data_bit_context = cur_live.data_reg & 1;
+ cur_live.pll.start_writing(cur_live.tm);
+ live_write_mfm(0x00);
+ break;
+ }
+ break;
+
+ default:
+ logerror("%s: Unknown live state %d\n", tts(cur_live.tm).cstr(), cur_live.state);
+ return;
+ }
+ }
+}
+
+void wd177x_t::set_drq()
+{
+ if(drq)
+ status |= S_LOST;
+ else {
+ drq = true;
+ if(!drq_cb.isnull())
+ drq_cb(true);
+ }
+}
+
+void wd177x_t::drop_drq()
+{
+ if(drq) {
+ drq = false;
+ if(!drq_cb.isnull())
+ drq_cb(false);
+ }
+}
+
+void wd177x_t::pll_t::set_clock(attotime period)
+{
+ for(int i=0; i<42; i++)
+ delays[i] = period*(i+1);
+}
+
+void wd177x_t::pll_t::reset(attotime when)
+{
+ counter = 0;
+ increment = 128;
+ transition_time = 0xffff;
+ history = 0x80;
+ slot = 0;
+ ctime = when;
+ phase_add = 0x00;
+ phase_sub = 0x00;
+ freq_add = 0x00;
+ freq_sub = 0x00;
+ write_position = 0;
+ write_start_time = attotime::never;
+}
+
+int wd177x_t::pll_t::get_next_bit(attotime &tm, floppy_image_device *floppy, attotime limit)
+{
+ attotime when = floppy ? floppy->get_next_transition(ctime) : attotime::never;
+#if 0
+ if(!when.is_never())
+ fprintf(stderr, "transition_time=%s\n", tts(when).cstr());
+#endif
+
+ for(;;) {
+ // fprintf(stderr, "slot=%2d, counter=%03x\n", slot, counter);
+ attotime etime = ctime+delays[slot];
+ // fprintf(stderr, "etime=%s\n", tts(etime).cstr());
+ if(etime > limit)
+ return -1;
+ if(transition_time == 0xffff && !when.is_never() && etime >= when)
+ transition_time = counter;
+ if(slot < 8) {
+ UINT8 mask = 1 << slot;
+ if(phase_add & mask)
+ counter += 226;
+ else if(phase_sub & mask)
+ counter += 30;
+ else
+ counter += increment;
+
+ if((freq_add & mask) && increment < 140)
+ increment++;
+ else if((freq_sub & mask) && increment > 117)
+ increment--;
+ } else
+ counter += increment;
+
+ slot++;
+ tm = etime;
+ if(counter & 0x800)
+ break;
+ }
+ // fprintf(stderr, "first transition, time=%03x, inc=%3d\n", transition_time, increment);
+ int bit = transition_time != 0xffff;
+
+ if(transition_time != 0xffff) {
+ static const UINT8 pha[8] = { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 };
+ static const UINT8 phs[8] = { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf };
+ static const UINT8 freqa[4][8] = {
+ { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 },
+ { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 },
+ { 0x7, 0x3, 0x1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+ };
+ static const UINT8 freqs[4][8] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 },
+ { 0, 0, 0, 0, 0, 0x1, 0x3, 0x7 },
+ { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf },
+ };
+
+ int cslot = transition_time >> 8;
+ phase_add = pha[cslot];
+ phase_sub = phs[cslot];
+ int way = transition_time & 0x400 ? 1 : 0;
+ if(history & 0x80)
+ history = way ? 0x80 : 0x83;
+ else if(history & 0x40)
+ history = way ? history & 2 : (history & 2) | 1;
+ freq_add = freqa[history & 3][cslot];
+ freq_sub = freqs[history & 3][cslot];
+ history = way ? (history >> 1) | 2 : history >> 1;
+
+ } else
+ phase_add = phase_sub = freq_add = freq_sub = 0;
+
+ counter &= 0x7ff;
+
+ ctime = tm;
+ transition_time = 0xffff;
+ slot = 0;
+
+ return bit;
+}
+
+void wd177x_t::pll_t::start_writing(attotime tm)
+{
+ write_start_time = tm;
+ write_position = 0;
+}
+
+void wd177x_t::pll_t::stop_writing(floppy_image_device *floppy, attotime tm)
+{
+ commit(floppy, tm);
+ write_start_time = attotime::never;
+}
+
+bool wd177x_t::pll_t::write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, attotime limit)
+{
+ if(write_start_time.is_never()) {
+ write_start_time = ctime;
+ write_position = 0;
+ }
+
+ for(;;) {
+ attotime etime = ctime+delays[slot];
+ if(etime > limit)
+ return true;
+ UINT16 pre_counter = counter;
+ counter += increment;
+ if(bit && !(pre_counter & 0x400) && (counter & 0x400))
+ if(write_position < ARRAY_LENGTH(write_buffer))
+ write_buffer[write_position++] = etime;
+ slot++;
+ tm = etime;
+ if(counter & 0x800)
+ break;
+ }
+
+ counter &= 0x7ff;
+
+ ctime = tm;
+ slot = 0;
+
+ return false;
+}
+
+void wd177x_t::pll_t::commit(floppy_image_device *floppy, attotime tm)
+{
+ if(write_start_time.is_never() || tm == write_start_time)
+ return;
+
+ if(floppy)
+ floppy->write_flux(write_start_time, tm, write_position, write_buffer);
+ write_start_time = tm;
+ write_position = 0;
+}
+
+int wd177x_t::step_time(int mode) const
+{
+ const static int step_times[4] = { 48000, 96000, 160000, 240000 };
+ return step_times[mode];
+}
+
+int wd177x_t::settle_time() const
+{
+ return 240000;
+}
+
+bool wd177x_t::has_side_check() const
+{
+ return false;
+}
+
+wd1770_t::wd1770_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD1770x, "WD1770", tag, owner, clock)
+{
+}
+
+bool wd1770_t::has_motor() const
+{
+ return true;
+}
+
+wd1772_t::wd1772_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD1772x, "WD1772", tag, owner, clock)
+{
+}
+
+bool wd1772_t::has_motor() const
+{
+ return true;
+}
+
+int wd1772_t::step_time(int mode) const
+{
+ const static int step_times[4] = { 48000, 96000, 16000, 24000 };
+ return step_times[mode];
+}
+
+int wd1772_t::settle_time() const
+{
+ return 120000;
+}
+
+wd1773_t::wd1773_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD1773x, "WD1773", tag, owner, clock)
+{
+}
+
+bool wd1773_t::has_motor() const
+{
+ return false;
+}
+
+bool wd1773_t::has_side_check() const
+{
+ return false;
+}