mame/src/mess/machine/wd1772.c

1908 lines
41 KiB
C

/***************************************************************************
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 FD1771x = &device_creator<fd1771_t>;
const device_type FD1793x = &device_creator<fd1793_t>;
const device_type FD1797x = &device_creator<fd1797_t>;
const device_type WD2793x = &device_creator<wd2793_t>;
const device_type WD2797x = &device_creator<wd2797_t>;
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(intrq_cond));
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;
hld = 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::setup_hld_cb(line_cb cb)
{
hld_cb = cb;
}
void wd177x_t::setup_enp_cb(line_cb cb)
{
enp_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;
if(has_head_load()) {
// TODO get value from HLT callback
if (command & 8)
status |= S_HLD;
else
status &= ~S_HLD;
}
sub_state = has_motor() && !has_head_load() ? 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) {
if(has_ready() && !is_ready()) {
status |= S_RNF;
command_end();
return;
}
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() || (command & 2))
return true;
if(command & 8)
return cur_live.idbuf[1] & 1;
else
return !(cur_live.idbuf[1] & 1);
}
bool wd177x_t::is_ready()
{
return (floppy && !floppy->ready_r());
}
void wd177x_t::read_sector_start()
{
if(has_ready() && !is_ready())
command_end();
main_state = READ_SECTOR;
status = (status & ~(S_CRC|S_LOST|S_RNF|S_WP|S_DDM)) | S_BUSY;
drop_drq();
if (has_side_select() && floppy)
floppy->ss_w(BIT(command, 1));
sub_state = has_motor() && !has_head_load() ? 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;
}
// TODO WD2795/7 alternate sector size
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()
{
if(has_ready() && !is_ready())
command_end();
main_state = READ_TRACK;
status = (status & ~(S_LOST|S_RNF)) | S_BUSY;
drop_drq();
if (has_side_select() && floppy)
floppy->ss_w(BIT(command, 1));
sub_state = has_motor() && !has_head_load() ? 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()
{
if(has_ready() && !is_ready())
command_end();
main_state = READ_ID;
status = (status & ~(S_WP|S_DDM|S_LOST|S_RNF)) | S_BUSY;
drop_drq();
if (has_side_select() && floppy)
floppy->ss_w(BIT(command, 1));
sub_state = has_motor() && !has_head_load() ? 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()
{
if(has_ready() && !is_ready())
command_end();
main_state = WRITE_TRACK;
status = (status & ~(S_WP|S_DDM|S_LOST|S_RNF)) | S_BUSY;
drop_drq();
if (has_side_select() && floppy)
floppy->ss_w(BIT(command, 1));
sub_state = has_motor() && !has_head_load() ? 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()
{
if(has_ready() && !is_ready())
command_end();
main_state = WRITE_SECTOR;
status = (status & ~(S_CRC|S_LOST|S_RNF|S_WP|S_DDM)) | S_BUSY;
drop_drq();
if (has_side_select() && floppy)
floppy->ss_w(BIT(command, 1));
sub_state = has_motor() && !has_head_load() ? 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;
}
// TODO WD2795/7 alternate sector size
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)) {
intrq_cond = 0;
} else {
intrq_cond = (intrq_cond & I_IMM) | (command & 0x07);
}
if(intrq_cond & I_IMM) {
intrq = true;
if(!intrq_cb.isnull())
intrq_cb(intrq);
}
if(command & 0x03) {
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)
{
logerror("wd1772 cmd: %02x\n", val);
if(intrq && !(intrq_cond & I_IMM)) {
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 && !(intrq_cond & I_IMM)) {
intrq = false;
if(!intrq_cb.isnull())
intrq_cb(intrq);
}
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;
}
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(has_ready()) {
if(!is_ready())
status |= S_NRDY;
else
status &= ~S_NRDY;
}
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;
}
if(intrq_cond & I_IDX) {
intrq = true;
if(!intrq_cb.isnull())
intrq_cb(intrq);
}
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;
}
bool wd177x_t::hld_r()
{
return hld;
}
void wd177x_t::hlt_w(bool state)
{
hlt = state;
}
bool wd177x_t::enp_r()
{
return enp;
}
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_ready() const
{
return false;
}
bool wd177x_t::has_head_load() const
{
return false;
}
bool wd177x_t::has_side_check() const
{
return false;
}
bool wd177x_t::has_side_select() const
{
return false;
}
bool wd177x_t::has_sector_length_select() const
{
return false;
}
bool wd177x_t::has_precompensation() const
{
return false;
}
fd1771_t::fd1771_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, FD1771x, "FD1771", tag, owner, clock)
{
}
fd1793_t::fd1793_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, FD1793x, "FD1793", tag, owner, clock)
{
}
fd1797_t::fd1797_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, FD1797x, "FD1797", tag, owner, clock)
{
}
wd2793_t::wd2793_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD2793x, "WD2793", tag, owner, clock)
{
}
wd2797_t::wd2797_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD2797x, "WD2797", tag, owner, clock)
{
}
wd1770_t::wd1770_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD1770x, "WD1770", tag, owner, clock)
{
}
wd1772_t::wd1772_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : wd177x_t(mconfig, WD1772x, "WD1772", tag, owner, clock)
{
}
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)
{
}