mirror of
https://github.com/holub/mame
synced 2025-04-26 10:13:37 +03:00
2982 lines
70 KiB
C++
2982 lines
70 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Olivier Galibert
|
|
#include "emu.h"
|
|
#include "wd_fdc.h"
|
|
|
|
#include "imagedev/floppy.h"
|
|
|
|
#include "debugger.h"
|
|
|
|
//#define LOG_GENERAL (1U << 0) //defined in logmacro.h already
|
|
#define LOG_SETUP (1U << 1) // Shows register setup
|
|
#define LOG_SHIFT (1U << 2) // Shows shift register contents
|
|
#define LOG_COMP (1U << 3) // Shows operations on the CPU side
|
|
#define LOG_COMMAND (1U << 4) // Shows command invocation
|
|
#define LOG_SYNC (1U << 5) // Shows sync actions
|
|
#define LOG_LINES (1U << 6) // Show control lines
|
|
#define LOG_EVENT (1U << 7) // Show events
|
|
#define LOG_MATCH (1U << 8) // Show sector match operation
|
|
#define LOG_DESC (1U << 9) // Show track description
|
|
#define LOG_WRITE (1U << 10) // Show write operation on image
|
|
#define LOG_TRANSITION (1U << 11) // Show transitions
|
|
#define LOG_STATE (1U << 12) // Show state machine
|
|
#define LOG_LIVE (1U << 13) // Live states
|
|
#define LOG_FUNC (1U << 14) // Function calls
|
|
|
|
#define VERBOSE (LOG_GENERAL | LOG_COMMAND | LOG_MATCH)
|
|
//#define LOG_OUTPUT_STREAM std::cout
|
|
|
|
#include "logmacro.h"
|
|
|
|
#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)
|
|
#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__)
|
|
#define LOGCOMP(...) LOGMASKED(LOG_COMP, __VA_ARGS__)
|
|
#define LOGCOMMAND(...) LOGMASKED(LOG_COMMAND, __VA_ARGS__)
|
|
#define LOGSYNC(...) LOGMASKED(LOG_SYNC, __VA_ARGS__)
|
|
#define LOGLINES(...) LOGMASKED(LOG_LINES, __VA_ARGS__)
|
|
#define LOGEVENT(...) LOGMASKED(LOG_EVENT, __VA_ARGS__)
|
|
#define LOGMATCH(...) LOGMASKED(LOG_MATCH, __VA_ARGS__)
|
|
#define LOGDESC(...) LOGMASKED(LOG_DESC, __VA_ARGS__)
|
|
#define LOGWRITE(...) LOGMASKED(LOG_WRITE, __VA_ARGS__)
|
|
#define LOGTRANSITION(...) LOGMASKED(LOG_TRANSITION, __VA_ARGS__)
|
|
#define LOGSTATE(...) LOGMASKED(LOG_STATE, __VA_ARGS__)
|
|
#define LOGLIVE(...) LOGMASKED(LOG_LIVE, __VA_ARGS__)
|
|
#define LOGFUNC(...) LOGMASKED(LOG_FUNC, __VA_ARGS__)
|
|
|
|
#ifdef _MSC_VER
|
|
#define FUNCNAME __func__
|
|
#else
|
|
#define FUNCNAME __PRETTY_FUNCTION__
|
|
#endif
|
|
|
|
DEFINE_DEVICE_TYPE(FD1771, fd1771_device, "fd1771", "FD1771 FDC")
|
|
|
|
DEFINE_DEVICE_TYPE(FD1781, fd1781_device, "fd1781", "FD1781 FDC")
|
|
|
|
DEFINE_DEVICE_TYPE(FD1791, fd1791_device, "fd1791", "FD1791 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1792, fd1792_device, "fd1792", "FD1792 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1793, fd1793_device, "fd1793", "FD1793 FDC")
|
|
DEFINE_DEVICE_TYPE(KR1818VG93, kr1818vg93_device, "kr1818vg93", "KR1818VG93 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1794, fd1794_device, "fd1794", "FD1794 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1795, fd1795_device, "fd1795", "FD1795 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1797, fd1797_device, "fd1797", "FD1797 FDC")
|
|
|
|
DEFINE_DEVICE_TYPE(MB8866, mb8866_device, "mb8866", "Fujitsu MB8866 FDC")
|
|
DEFINE_DEVICE_TYPE(MB8876, mb8876_device, "mb8876", "Fujitsu MB8876 FDC")
|
|
DEFINE_DEVICE_TYPE(MB8877, mb8877_device, "mb8877", "Fujitsu MB8877 FDC")
|
|
|
|
DEFINE_DEVICE_TYPE(FD1761, fd1761_device, "fd1761", "FD1761 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1763, fd1763_device, "fd1763", "FD1763 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1765, fd1765_device, "fd1765", "FD1765 FDC")
|
|
DEFINE_DEVICE_TYPE(FD1767, fd1767_device, "fd1767", "FD1767 FDC")
|
|
|
|
DEFINE_DEVICE_TYPE(WD2791, wd2791_device, "wd2791", "Western Digital WD2791 FDC")
|
|
DEFINE_DEVICE_TYPE(WD2793, wd2793_device, "wd2793", "Western Digital WD2793 FDC")
|
|
DEFINE_DEVICE_TYPE(WD2795, wd2795_device, "wd2795", "Western Digital WD2795 FDC")
|
|
DEFINE_DEVICE_TYPE(WD2797, wd2797_device, "wd2797", "Western Digital WD2797 FDC")
|
|
|
|
DEFINE_DEVICE_TYPE(WD1770, wd1770_device, "wd1770", "Western Digital WD1770 FDC")
|
|
DEFINE_DEVICE_TYPE(WD1772, wd1772_device, "wd1772", "Western Digital WD1772 FDC")
|
|
DEFINE_DEVICE_TYPE(WD1773, wd1773_device, "wd1773", "Western Digital WD1773 FDC")
|
|
|
|
wd_fdc_device_base::wd_fdc_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
|
|
device_t(mconfig, type, tag, owner, clock),
|
|
intrq_cb(*this),
|
|
drq_cb(*this),
|
|
hld_cb(*this),
|
|
enp_cb(*this),
|
|
sso_cb(*this),
|
|
ready_cb(*this), // actually output by the drive, not by the FDC
|
|
enmf_cb(*this),
|
|
mon_cb(*this)
|
|
{
|
|
force_ready = false;
|
|
disable_motor_control = false;
|
|
spinup_on_interrupt = false;
|
|
}
|
|
|
|
void wd_fdc_device_base::set_force_ready(bool _force_ready)
|
|
{
|
|
force_ready = _force_ready;
|
|
}
|
|
|
|
void wd_fdc_device_base::set_disable_motor_control(bool _disable_motor_control)
|
|
{
|
|
disable_motor_control = _disable_motor_control;
|
|
}
|
|
|
|
void wd_fdc_device_base::device_start()
|
|
{
|
|
intrq_cb.resolve();
|
|
drq_cb.resolve();
|
|
hld_cb.resolve();
|
|
enp_cb.resolve();
|
|
sso_cb.resolve();
|
|
ready_cb.resolve();
|
|
enmf_cb.resolve();
|
|
mon_cb.resolve_safe();
|
|
|
|
if (!has_enmf && !enmf_cb.isnull())
|
|
logerror("Warning, this chip doesn't have an ENMF line.\n");
|
|
|
|
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 = disable_mfm;
|
|
enmf = false;
|
|
floppy = nullptr;
|
|
status = 0x00;
|
|
mr = true;
|
|
|
|
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));
|
|
if (!disable_mfm)
|
|
save_item(NAME(dden));
|
|
save_item(NAME(mr));
|
|
save_item(NAME(intrq));
|
|
save_item(NAME(drq));
|
|
if (head_control)
|
|
save_item(NAME(hld));
|
|
}
|
|
|
|
void wd_fdc_device_base::device_reset()
|
|
{
|
|
soft_reset();
|
|
}
|
|
|
|
void wd_fdc_device_base::soft_reset()
|
|
{
|
|
if(mr) {
|
|
mr_w(0);
|
|
mr_w(1);
|
|
}
|
|
}
|
|
|
|
WRITE_LINE_MEMBER(wd_fdc_device_base::mr_w)
|
|
{
|
|
if(mr && !state) {
|
|
command = 0x00;
|
|
main_state = IDLE;
|
|
sub_state = IDLE;
|
|
cur_live.state = IDLE;
|
|
track = 0x00;
|
|
sector = 0x01;
|
|
status = 0x00;
|
|
data = 0x00;
|
|
cmd_buffer = track_buffer = sector_buffer = -1;
|
|
counter = 0;
|
|
status_type_1 = true;
|
|
last_dir = 1;
|
|
mr = false;
|
|
|
|
// gnd == enmf enabled, otherwise disabled (default)
|
|
if (!enmf_cb.isnull() && has_enmf)
|
|
enmf = enmf_cb() ? false : true;
|
|
|
|
intrq = false;
|
|
if (!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
drq = false;
|
|
if (!drq_cb.isnull())
|
|
drq_cb(drq);
|
|
if(head_control) {
|
|
hld = false;
|
|
if(!hld_cb.isnull())
|
|
hld_cb(hld);
|
|
}
|
|
|
|
mon_cb(1); // Clear the MON* line
|
|
|
|
intrq_cond = 0;
|
|
live_abort();
|
|
} else if(state && !mr) {
|
|
// trigger a restore after everything else is reset too, in particular the floppy device itself
|
|
sub_state = INITIAL_RESTORE;
|
|
t_gen->adjust(attotime::zero);
|
|
mr = true;
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::set_floppy(floppy_image_device *_floppy)
|
|
{
|
|
if(floppy == _floppy)
|
|
return;
|
|
|
|
int prev_ready = floppy ? floppy->ready_r() : 1;
|
|
|
|
if(floppy) {
|
|
// Warning: deselecting a drive does *not* stop its motor if it was running
|
|
floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb());
|
|
floppy->setup_ready_cb(floppy_image_device::ready_cb());
|
|
}
|
|
|
|
floppy = _floppy;
|
|
|
|
int next_ready = floppy ? floppy->ready_r() : 1;
|
|
|
|
if (motor_control)
|
|
mon_cb(status & S_MON ? 0 : 1);
|
|
|
|
if(floppy) {
|
|
if(motor_control && !disable_motor_control)
|
|
floppy->mon_w(status & S_MON ? 0 : 1);
|
|
floppy->setup_index_pulse_cb(floppy_image_device::index_pulse_cb(&wd_fdc_device_base::index_callback, this));
|
|
floppy->setup_ready_cb(floppy_image_device::ready_cb(&wd_fdc_device_base::ready_callback, this));
|
|
}
|
|
|
|
if(prev_ready != next_ready)
|
|
ready_callback(floppy, next_ready);
|
|
}
|
|
|
|
WRITE_LINE_MEMBER(wd_fdc_device_base::dden_w)
|
|
{
|
|
if(disable_mfm) {
|
|
logerror("Error, this chip does not have a dden line\n");
|
|
return;
|
|
}
|
|
|
|
if(dden != bool(state)) {
|
|
dden = bool(state);
|
|
LOGLINES("select %s\n", dden ? "fm" : "mfm");
|
|
}
|
|
}
|
|
|
|
std::string wd_fdc_device_base::tts(const 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;
|
|
}
|
|
|
|
std::string wd_fdc_device_base::ttsn()
|
|
{
|
|
return tts(machine().time());
|
|
}
|
|
|
|
void wd_fdc_device_base::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
|
{
|
|
LOGEVENT("Event fired for timer %s\n", (id==TM_GEN)? "TM_GEN" : (id==TM_CMD)? "TM_CMD" : (id==TM_TRACK)? "TM_TRACK" : "TM_SECTOR");
|
|
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 wd_fdc_device_base::command_end()
|
|
{
|
|
LOGFUNC("%s\n", FUNCNAME);
|
|
main_state = sub_state = IDLE;
|
|
motor_timeout = 0;
|
|
|
|
if(!drq && (status & S_BUSY)) {
|
|
status &= ~S_BUSY;
|
|
intrq = true;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::seek_start(int state)
|
|
{
|
|
LOGCOMMAND("cmd: seek %d %x (track=%d)\n", state, data, track);
|
|
main_state = state;
|
|
status &= ~(S_CRC|S_RNF|S_SPIN);
|
|
if(head_control) {
|
|
if(BIT(command, 3))
|
|
set_hld();
|
|
else
|
|
drop_hld();
|
|
|
|
// TODO get value from HLT callback
|
|
if(hld)
|
|
status |= S_HLD;
|
|
else
|
|
status &= ~S_HLD;
|
|
}
|
|
sub_state = motor_control ? SPINUP : SPINUP_DONE;
|
|
status_type_1 = true;
|
|
seek_continue();
|
|
}
|
|
|
|
void wd_fdc_device_base::seek_continue()
|
|
{
|
|
for(;;) {
|
|
switch(sub_state) {
|
|
case SPINUP:
|
|
LOGSTATE("SPINUP\n");
|
|
if(!(status & S_MON)) {
|
|
spinup();
|
|
return;
|
|
}
|
|
if(!(command & 0x08))
|
|
status |= S_SPIN;
|
|
sub_state = SPINUP_DONE;
|
|
break;
|
|
|
|
case SPINUP_WAIT:
|
|
LOGSTATE("SPINUP_WAIT\n");
|
|
return;
|
|
|
|
case SPINUP_DONE:
|
|
LOGSTATE("SPINUP_DONE\n");
|
|
if(main_state == RESTORE && floppy && !floppy->trk00_r()) {
|
|
sub_state = SEEK_WAIT_STEP_TIME;
|
|
delay_cycles(t_gen, step_times[command & 3]);
|
|
}
|
|
|
|
if(main_state == SEEK && track == data) {
|
|
sub_state = SEEK_WAIT_STABILIZATION_TIME;
|
|
delay_cycles(t_gen, 30000);
|
|
}
|
|
|
|
if(sub_state == SPINUP_DONE) {
|
|
counter = 0;
|
|
sub_state = SEEK_MOVE;
|
|
}
|
|
break;
|
|
|
|
case SEEK_MOVE:
|
|
LOGSTATE("SEEK_MOVE\n");
|
|
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_times[command & 3]);
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME:
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STEP_TIME_DONE: {
|
|
LOGSTATE("SEEK_WAIT_STEP_TIME_DONE\n");
|
|
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) {
|
|
set_hld();
|
|
sub_state = SEEK_WAIT_STABILIZATION_TIME;
|
|
delay_cycles(t_gen, 30000);
|
|
return;
|
|
} else
|
|
sub_state = SEEK_DONE;
|
|
|
|
} else
|
|
sub_state = SEEK_MOVE;
|
|
|
|
break;
|
|
}
|
|
|
|
case SEEK_WAIT_STABILIZATION_TIME:
|
|
LOGSTATE("SEEK_WAIT_STABILIZATION_TIME\n");
|
|
return;
|
|
|
|
case SEEK_WAIT_STABILIZATION_TIME_DONE:
|
|
LOGSTATE("SEEK_WAIT_STABILIZATION_TIME_DONE\n");
|
|
if(hld)
|
|
status |= S_HLD;
|
|
sub_state = SEEK_DONE;
|
|
break;
|
|
|
|
case SEEK_DONE:
|
|
LOGSTATE("SEEK_DONE\n");
|
|
if(command & 0x04) {
|
|
if(!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:
|
|
LOGSTATE("SCAN_ID\n");
|
|
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:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
status |= S_RNF;
|
|
command_end();
|
|
return;
|
|
|
|
default:
|
|
logerror("seek unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool wd_fdc_device_base::sector_matches() const
|
|
{
|
|
LOGMATCH("matching read T=%02x H=%02x S=%02x L=%02x - searched T=%02x S=%02x\n",
|
|
cur_live.idbuf[0], cur_live.idbuf[1], cur_live.idbuf[2], cur_live.idbuf[3],
|
|
track, sector);
|
|
|
|
if(cur_live.idbuf[0] != track || cur_live.idbuf[2] != sector)
|
|
return false;
|
|
if(!side_compare || ((command & 2)==0))
|
|
return true;
|
|
if(command & 8)
|
|
return cur_live.idbuf[1] & 1;
|
|
else
|
|
return !(cur_live.idbuf[1] & 1);
|
|
}
|
|
|
|
bool wd_fdc_device_base::is_ready()
|
|
{
|
|
return !ready_hooked || force_ready || (floppy && !floppy->ready_r());
|
|
}
|
|
|
|
void wd_fdc_device_base::read_sector_start()
|
|
{
|
|
LOGCOMMAND("cmd: read sector%s (c=%02x) t=%d, s=%d\n", command & 0x10 ? " multiple" : "", command, track, sector);
|
|
if(!is_ready()) {
|
|
command_end();
|
|
return;
|
|
}
|
|
|
|
main_state = READ_SECTOR;
|
|
status &= ~(S_CRC|S_LOST|S_RNF|S_WP|S_DDM);
|
|
drop_drq();
|
|
update_sso();
|
|
set_hld();
|
|
sub_state = motor_control ? SPINUP : SPINUP_DONE;
|
|
status_type_1 = false;
|
|
read_sector_continue();
|
|
}
|
|
|
|
void wd_fdc_device_base::read_sector_continue()
|
|
{
|
|
for(;;) {
|
|
switch(sub_state) {
|
|
case SPINUP:
|
|
LOGSTATE("SPINUP\n");
|
|
if(!(status & S_MON)) {
|
|
spinup();
|
|
return;
|
|
}
|
|
sub_state = SPINUP_DONE;
|
|
break;
|
|
|
|
case SPINUP_WAIT:
|
|
LOGSTATE("SPINUP_WAIT\n");
|
|
return;
|
|
|
|
case SPINUP_DONE:
|
|
LOGSTATE("SPINUP_DONE\n");
|
|
if(command & 4) {
|
|
sub_state = SETTLE_WAIT;
|
|
delay_cycles(t_gen, settle_time());
|
|
return;
|
|
} else {
|
|
sub_state = SETTLE_DONE;
|
|
break;
|
|
}
|
|
|
|
case SETTLE_WAIT:
|
|
LOGSTATE("SETTLE_WAIT\n");
|
|
return;
|
|
|
|
case SETTLE_DONE:
|
|
LOGSTATE("SETTLE_DONE\n");
|
|
sub_state = SCAN_ID;
|
|
counter = 0;
|
|
live_start(SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
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 = calc_sector_size(cur_live.idbuf[3], command);
|
|
sub_state = SECTOR_READ;
|
|
live_start(SEARCH_ADDRESS_MARK_DATA);
|
|
return;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
status |= S_RNF;
|
|
command_end();
|
|
return;
|
|
|
|
case SECTOR_READ:
|
|
LOGSTATE("SECTOR_READ\n");
|
|
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("read sector unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::read_track_start()
|
|
{
|
|
LOGCOMMAND("cmd: read track (c=%02x) t=%d\n", command, track);
|
|
|
|
if(!is_ready()) {
|
|
command_end();
|
|
return;
|
|
}
|
|
|
|
main_state = READ_TRACK;
|
|
status &= ~(S_LOST|S_RNF);
|
|
drop_drq();
|
|
update_sso();
|
|
set_hld();
|
|
sub_state = motor_control ? SPINUP : SPINUP_DONE;
|
|
status_type_1 = false;
|
|
read_track_continue();
|
|
}
|
|
|
|
void wd_fdc_device_base::read_track_continue()
|
|
{
|
|
for(;;) {
|
|
switch(sub_state) {
|
|
case SPINUP:
|
|
LOGSTATE("SPINUP\n");
|
|
if(!(status & S_MON)) {
|
|
spinup();
|
|
return;
|
|
}
|
|
sub_state = SPINUP_DONE;
|
|
break;
|
|
|
|
case SPINUP_WAIT:
|
|
LOGSTATE("SPINUP_WAIT\n");
|
|
return;
|
|
|
|
case SPINUP_DONE:
|
|
LOGSTATE("SPINUP_DONE\n");
|
|
if(command & 4) {
|
|
sub_state = SETTLE_WAIT;
|
|
delay_cycles(t_gen, settle_time());
|
|
return;
|
|
|
|
} else {
|
|
sub_state = SETTLE_DONE;
|
|
break;
|
|
}
|
|
|
|
case SETTLE_WAIT:
|
|
LOGSTATE("SETTLE_WAIT\n");
|
|
return;
|
|
|
|
case SETTLE_DONE:
|
|
LOGSTATE("SETTLE_DONE\n");
|
|
sub_state = WAIT_INDEX;
|
|
return;
|
|
|
|
case WAIT_INDEX:
|
|
LOGSTATE("WAIT_INDEX\n");
|
|
return;
|
|
|
|
case WAIT_INDEX_DONE:
|
|
LOGSTATE("WAIT_INDEX_DONE\n");
|
|
sub_state = TRACK_DONE;
|
|
live_start(READ_TRACK_DATA);
|
|
return;
|
|
|
|
case TRACK_DONE:
|
|
LOGSTATE("TRACK_DONE\n");
|
|
command_end();
|
|
return;
|
|
|
|
default:
|
|
logerror("read track unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::read_id_start()
|
|
{
|
|
LOGCOMMAND("cmd: read id (c=%02x)\n", command);
|
|
if(!is_ready()) {
|
|
LOGCOMMAND("cmd: - not ready!");
|
|
command_end();
|
|
return;
|
|
}
|
|
|
|
main_state = READ_ID;
|
|
status &= ~(S_WP|S_DDM|S_LOST|S_RNF);
|
|
drop_drq();
|
|
update_sso();
|
|
set_hld();
|
|
sub_state = motor_control ? SPINUP : SPINUP_DONE;
|
|
status_type_1 = false;
|
|
read_id_continue();
|
|
}
|
|
|
|
void wd_fdc_device_base::read_id_continue()
|
|
{
|
|
LOGFUNC("%s\n", FUNCNAME);
|
|
for(;;) {
|
|
switch(sub_state) {
|
|
case SPINUP:
|
|
LOGSTATE("SPINUP\n");
|
|
if(!(status & S_MON)) {
|
|
spinup();
|
|
return;
|
|
}
|
|
sub_state = SPINUP_DONE;
|
|
break;
|
|
|
|
case SPINUP_WAIT:
|
|
LOGSTATE("SPINUP_WAIT\n");
|
|
return;
|
|
|
|
case SPINUP_DONE:
|
|
LOGSTATE("SPINUP_DONE\n");
|
|
if(command & 4) {
|
|
sub_state = SETTLE_WAIT;
|
|
delay_cycles(t_gen, settle_time());
|
|
return;
|
|
} else {
|
|
sub_state = SETTLE_DONE;
|
|
break;
|
|
}
|
|
|
|
case SETTLE_WAIT:
|
|
LOGSTATE("SETTLE_WAIT\n");
|
|
return;
|
|
|
|
case SETTLE_DONE:
|
|
LOGSTATE("SETTLE_DONE\n");
|
|
sub_state = SCAN_ID;
|
|
counter = 0;
|
|
live_start(SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
command_end();
|
|
return;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
status |= S_RNF;
|
|
command_end();
|
|
return;
|
|
|
|
default:
|
|
logerror("read id unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::write_track_start()
|
|
{
|
|
LOGCOMMAND("cmd: write track (c=%02x) t=%d\n", command, track);
|
|
|
|
if(!is_ready()) {
|
|
command_end();
|
|
return;
|
|
}
|
|
|
|
main_state = WRITE_TRACK;
|
|
status &= ~(S_WP|S_DDM|S_LOST|S_RNF);
|
|
drop_drq();
|
|
update_sso();
|
|
set_hld();
|
|
sub_state = motor_control ? SPINUP : SPINUP_DONE;
|
|
status_type_1 = false;
|
|
|
|
format_last_byte = 0;
|
|
format_last_byte_count = 0;
|
|
format_description_string = "";
|
|
|
|
write_track_continue();
|
|
}
|
|
|
|
void wd_fdc_device_base::write_track_continue()
|
|
{
|
|
for(;;) {
|
|
switch(sub_state) {
|
|
case SPINUP:
|
|
LOGSTATE("SPINUP\n");
|
|
if(!(status & S_MON)) {
|
|
spinup();
|
|
return;
|
|
}
|
|
sub_state = SPINUP_DONE;
|
|
break;
|
|
|
|
case SPINUP_WAIT:
|
|
LOGSTATE("SPINUP_WAIT\n");
|
|
return;
|
|
|
|
case SPINUP_DONE:
|
|
LOGSTATE("SPINUP_DONE\n");
|
|
if(command & 4) {
|
|
sub_state = SETTLE_WAIT;
|
|
delay_cycles(t_gen, settle_time());
|
|
return;
|
|
} else {
|
|
sub_state = SETTLE_DONE;
|
|
break;
|
|
}
|
|
|
|
case SETTLE_WAIT:
|
|
LOGSTATE("SETTLE_WAIT\n");
|
|
return;
|
|
|
|
case SETTLE_DONE:
|
|
LOGSTATE("SETTLE_DONE\n");
|
|
set_drq();
|
|
sub_state = DATA_LOAD_WAIT;
|
|
delay_cycles(t_gen, 192);
|
|
return;
|
|
|
|
case DATA_LOAD_WAIT:
|
|
LOGSTATE("DATA_LOAD_WAIT\n");
|
|
return;
|
|
|
|
case DATA_LOAD_WAIT_DONE:
|
|
LOGSTATE("DATA_LOAD_WAIT_DONE\n");
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
drop_drq();
|
|
command_end();
|
|
return;
|
|
}
|
|
sub_state = WAIT_INDEX;
|
|
break;
|
|
|
|
case WAIT_INDEX:
|
|
LOGSTATE("WAIT_INDEX\n");
|
|
return;
|
|
|
|
case WAIT_INDEX_DONE:
|
|
LOGSTATE("WAIT_INDEX_DONE\n");
|
|
sub_state = TRACK_DONE;
|
|
live_start(WRITE_TRACK_DATA);
|
|
pll_start_writing(machine().time());
|
|
return;
|
|
|
|
case TRACK_DONE:
|
|
LOGSTATE("TRACK_DONE\n");
|
|
if(format_last_byte_count) {
|
|
char buf[32];
|
|
if(format_last_byte_count > 1)
|
|
sprintf(buf, "%dx%02x", format_last_byte_count, format_last_byte);
|
|
else
|
|
sprintf(buf, "%02x", format_last_byte);
|
|
format_description_string += buf;
|
|
}
|
|
LOGDESC("track description %s\n", format_description_string.c_str());
|
|
command_end();
|
|
return;
|
|
|
|
default:
|
|
logerror("write track unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void wd_fdc_device_base::write_sector_start()
|
|
{
|
|
LOGCOMMAND("cmd: write sector%s (c=%02x) t=%d, s=%d\n", command & 0x10 ? " multiple" : "", command, track, sector);
|
|
|
|
if(!is_ready()) {
|
|
command_end();
|
|
return;
|
|
}
|
|
|
|
main_state = WRITE_SECTOR;
|
|
status &= ~(S_CRC|S_LOST|S_RNF|S_WP|S_DDM);
|
|
drop_drq();
|
|
update_sso();
|
|
set_hld();
|
|
sub_state = motor_control ? SPINUP : SPINUP_DONE;
|
|
status_type_1 = false;
|
|
write_sector_continue();
|
|
}
|
|
|
|
void wd_fdc_device_base::write_sector_continue()
|
|
{
|
|
for(;;) {
|
|
switch(sub_state) {
|
|
case SPINUP:
|
|
LOGSTATE("SPINUP\n");
|
|
if(!(status & S_MON)) {
|
|
spinup();
|
|
return;
|
|
}
|
|
sub_state = SPINUP_DONE;
|
|
break;
|
|
|
|
case SPINUP_WAIT:
|
|
LOGSTATE("SPINUP_WAIT\n");
|
|
return;
|
|
|
|
case SPINUP_DONE:
|
|
LOGSTATE("SPINUP_DONE\n");
|
|
if(command & 4) {
|
|
sub_state = SETTLE_WAIT;
|
|
delay_cycles(t_gen, settle_time());
|
|
return;
|
|
} else {
|
|
sub_state = SETTLE_DONE;
|
|
break;
|
|
}
|
|
|
|
case SETTLE_WAIT:
|
|
LOGSTATE("SETTLE_WAIT\n");
|
|
return;
|
|
|
|
case SETTLE_DONE:
|
|
LOGSTATE("SETTLE_DONE\n");
|
|
sub_state = SCAN_ID;
|
|
counter = 0;
|
|
live_start(SEARCH_ADDRESS_MARK_HEADER);
|
|
return;
|
|
|
|
case SCAN_ID:
|
|
LOGSTATE("SCAN_ID\n");
|
|
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 = calc_sector_size(cur_live.idbuf[3], command);
|
|
sub_state = SECTOR_WRITE;
|
|
live_start(WRITE_SECTOR_PRE);
|
|
return;
|
|
|
|
case SCAN_ID_FAILED:
|
|
LOGSTATE("SCAN_ID_FAILED\n");
|
|
status |= S_RNF;
|
|
command_end();
|
|
return;
|
|
|
|
case SECTOR_WRITE:
|
|
LOGSTATE("SECTOR_WRITE\n");
|
|
if(command & 0x10) {
|
|
sector++;
|
|
sub_state = SPINUP_DONE;
|
|
} else {
|
|
command_end();
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
logerror("write sector unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::interrupt_start()
|
|
{
|
|
// technically we should re-execute this (at chip-specific rate) all the time while interrupt command code is in command register
|
|
|
|
LOGCOMMAND("cmd: forced interrupt (c=%02x)\n", command);
|
|
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
// when a force interrupt command is issued and there is no
|
|
// currently running command, return the status type 1 bits
|
|
status_type_1 = true;
|
|
}
|
|
|
|
intrq_cond = command & 0x0f;
|
|
|
|
if(!intrq && (command & I_IMM)) {
|
|
intrq = true;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
|
|
if(command & 0x03) {
|
|
logerror("%s: unhandled interrupt generation (%02x)\n", ttsn().c_str(), command);
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::general_continue()
|
|
{
|
|
LOGFUNC("%s\n", FUNCNAME);
|
|
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().c_str(), main_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::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;
|
|
|
|
case INITIAL_RESTORE:
|
|
last_dir = 1;
|
|
seek_start(RESTORE);
|
|
break;
|
|
|
|
default:
|
|
if(cur_live.tm.is_never())
|
|
logerror("%s: do_generic on unknown sub-state %d\n", ttsn().c_str(), sub_state);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::do_cmd_w()
|
|
{
|
|
// 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;
|
|
|
|
LOGCOMMAND("%s %02x: %s\n", FUNCNAME, cmd_buffer, std::array<char const *, 16>
|
|
{{"RESTORE", "SEEK", "STEP", "STEP", "STEP", "STEP", "STEP", "STEP",
|
|
"READ sector start", "READ sector start", "WRITE sector start", "WRITE sector start",
|
|
"READ ID start", "INTERRUPT start", "READ track start", "WRITE track start"}}[(command >> 4) & 0x0f]);
|
|
switch(command & 0xf0) {
|
|
case 0x00:
|
|
last_dir = 1;
|
|
seek_start(RESTORE);
|
|
break;
|
|
case 0x10:
|
|
last_dir = data > track ? 0 : 1;
|
|
seek_start(SEEK);
|
|
break;
|
|
case 0x20:
|
|
case 0x30:
|
|
seek_start(STEP);
|
|
break;
|
|
case 0x40:
|
|
case 0x50:
|
|
last_dir = 0;
|
|
seek_start(STEP);
|
|
break;
|
|
case 0x60:
|
|
case 0x70:
|
|
last_dir = 1;
|
|
seek_start(STEP);
|
|
break;
|
|
case 0x80:
|
|
case 0x90:
|
|
read_sector_start();
|
|
break;
|
|
case 0xa0:
|
|
case 0xb0:
|
|
write_sector_start();
|
|
break;
|
|
case 0xc0:
|
|
read_id_start();
|
|
break;
|
|
case 0xd0:
|
|
interrupt_start();
|
|
break;
|
|
case 0xe0:
|
|
read_track_start();
|
|
break;
|
|
case 0xf0:
|
|
write_track_start();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::cmd_w(uint8_t val)
|
|
{
|
|
if (inverted_bus) val ^= 0xff;
|
|
if (!mr) {
|
|
logerror("Not initiating command %02x during master reset\n", val);
|
|
return;
|
|
}
|
|
|
|
LOGCOMP("Initiating command %02x\n", val);
|
|
|
|
if (intrq) {
|
|
intrq = false;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
|
|
// No more than one write in flight, but interrupts take priority
|
|
if(cmd_buffer != -1 && ((val & 0xf0) != 0xd0))
|
|
return;
|
|
|
|
cmd_buffer = val;
|
|
|
|
if ((val & 0xf0) == 0xd0)
|
|
{
|
|
// checkme
|
|
delay_cycles(t_cmd, dden ? delay_register_commit * 2 : delay_register_commit);
|
|
if (spinup_on_interrupt) // see note in WD1772 constructor
|
|
spinup();
|
|
}
|
|
else
|
|
{
|
|
intrq_cond = 0;
|
|
// set busy, then set a timer to process the command
|
|
status |= S_BUSY;
|
|
delay_cycles(t_cmd, dden ? delay_command_commit*2 : delay_command_commit);
|
|
}
|
|
}
|
|
|
|
uint8_t wd_fdc_device_base::status_r()
|
|
{
|
|
if(intrq && !(intrq_cond & I_IMM) && !machine().side_effects_disabled()) {
|
|
intrq = false;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
|
|
if(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(ready_hooked) {
|
|
if(!is_ready())
|
|
status |= S_NRDY;
|
|
else
|
|
status &= ~S_NRDY;
|
|
}
|
|
|
|
uint8_t val = status;
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
return val;
|
|
}
|
|
|
|
void wd_fdc_device_base::do_track_w()
|
|
{
|
|
track = track_buffer;
|
|
track_buffer = -1;
|
|
}
|
|
|
|
void wd_fdc_device_base::track_w(uint8_t val)
|
|
{
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
// No more than one write in flight
|
|
if(track_buffer != -1 || !mr)
|
|
return;
|
|
|
|
track_buffer = val;
|
|
delay_cycles(t_track, dden ? delay_register_commit*2 : delay_register_commit);
|
|
}
|
|
|
|
uint8_t wd_fdc_device_base::track_r()
|
|
{
|
|
uint8_t val = track;
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
return val;
|
|
}
|
|
|
|
void wd_fdc_device_base::do_sector_w()
|
|
{
|
|
sector = sector_buffer;
|
|
sector_buffer = -1;
|
|
}
|
|
|
|
void wd_fdc_device_base::sector_w(uint8_t val)
|
|
{
|
|
if (!mr) return;
|
|
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
// No more than one write in flight
|
|
// C1581 accesses this register with an INC opcode,
|
|
// i.e. write old value, write new value, and the new value gets ignored by this
|
|
//if(sector_buffer != -1)
|
|
// return;
|
|
|
|
sector_buffer = val;
|
|
|
|
// set a timer to write the new value to the register, but only if we aren't in
|
|
// the middle of an already occurring update
|
|
if (!t_sector->enabled())
|
|
delay_cycles(t_sector, dden ? delay_register_commit*2 : delay_register_commit);
|
|
}
|
|
|
|
uint8_t wd_fdc_device_base::sector_r()
|
|
{
|
|
uint8_t val = sector;
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
return val;
|
|
}
|
|
|
|
void wd_fdc_device_base::data_w(uint8_t val)
|
|
{
|
|
if (!mr) return;
|
|
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
data = val;
|
|
drop_drq();
|
|
}
|
|
|
|
uint8_t wd_fdc_device_base::data_r()
|
|
{
|
|
if (!machine().side_effects_disabled())
|
|
drop_drq();
|
|
|
|
uint8_t val = data;
|
|
if (inverted_bus) val ^= 0xff;
|
|
|
|
return val;
|
|
}
|
|
|
|
void wd_fdc_device_base::write(offs_t reg, uint8_t val)
|
|
{
|
|
LOGFUNC("%s %02x: %02x\n", FUNCNAME, reg, 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_t wd_fdc_device_base::read(offs_t reg)
|
|
{
|
|
switch(reg) {
|
|
case 0: return status_r();
|
|
case 1: return track_r();
|
|
case 2: return sector_r();
|
|
case 3: return data_r();
|
|
}
|
|
return 0xff;
|
|
}
|
|
|
|
void wd_fdc_device_base::delay_cycles(emu_timer *tm, int cycles)
|
|
{
|
|
tm->adjust(clocks_to_attotime(cycles*clock_ratio));
|
|
}
|
|
|
|
void wd_fdc_device_base::spinup()
|
|
{
|
|
if(command & 0x08)
|
|
sub_state = SPINUP_DONE;
|
|
else {
|
|
sub_state = SPINUP_WAIT;
|
|
counter = 0;
|
|
}
|
|
|
|
status |= S_MON|S_SPIN;
|
|
|
|
mon_cb(0);
|
|
if(floppy && !disable_motor_control)
|
|
floppy->mon_w(0);
|
|
}
|
|
|
|
void wd_fdc_device_base::ready_callback(floppy_image_device *floppy, int state)
|
|
{
|
|
if(!ready_cb.isnull())
|
|
ready_cb(state);
|
|
|
|
// why is this even possible?
|
|
if (!floppy)
|
|
return;
|
|
|
|
live_sync();
|
|
if(!ready_hooked)
|
|
return;
|
|
|
|
if(!intrq && (((intrq_cond & I_RDY) && !state) || ((intrq_cond & I_NRDY) && state))) {
|
|
intrq = true;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::index_callback(floppy_image_device *floppy, int state)
|
|
{
|
|
live_sync();
|
|
|
|
if(!state) {
|
|
general_continue();
|
|
return;
|
|
}
|
|
|
|
switch(sub_state) {
|
|
case IDLE:
|
|
if(motor_control || head_control) {
|
|
motor_timeout ++;
|
|
// Spindown delay is 9 revs according to spec
|
|
if(motor_control && motor_timeout >= 8) {
|
|
status &= ~S_MON;
|
|
mon_cb(1);
|
|
if(floppy && !disable_motor_control)
|
|
floppy->mon_w(1);
|
|
}
|
|
|
|
if(head_control && motor_timeout >= hld_timeout) {
|
|
drop_hld();
|
|
status &= ~S_HLD; // todo: should get this value from the drive
|
|
}
|
|
}
|
|
|
|
if(!intrq && (intrq_cond & I_IDX)) {
|
|
intrq = true;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
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().c_str(), sub_state);
|
|
break;
|
|
}
|
|
|
|
general_continue();
|
|
}
|
|
|
|
READ_LINE_MEMBER(wd_fdc_device_base::intrq_r)
|
|
{
|
|
return intrq;
|
|
}
|
|
|
|
READ_LINE_MEMBER(wd_fdc_device_base::drq_r)
|
|
{
|
|
return drq;
|
|
}
|
|
|
|
READ_LINE_MEMBER(wd_fdc_device_base::hld_r)
|
|
{
|
|
return hld;
|
|
}
|
|
|
|
WRITE_LINE_MEMBER(wd_fdc_device_base::hlt_w)
|
|
{
|
|
hlt = bool(state);
|
|
}
|
|
|
|
READ_LINE_MEMBER(wd_fdc_device_base::enp_r)
|
|
{
|
|
return enp;
|
|
}
|
|
|
|
void wd_fdc_device_base::live_start(int state)
|
|
{
|
|
LOGFUNC("%s\n", FUNCNAME);
|
|
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;
|
|
|
|
if (!enmf_cb.isnull() && has_enmf)
|
|
enmf = enmf_cb() ? false : true;
|
|
|
|
pll_reset(dden, enmf, cur_live.tm);
|
|
checkpoint_live = cur_live;
|
|
pll_save_checkpoint();
|
|
|
|
live_run();
|
|
}
|
|
|
|
void wd_fdc_device_base::checkpoint()
|
|
{
|
|
LOGFUNC("%s\n", FUNCNAME);
|
|
pll_commit(floppy, cur_live.tm);
|
|
checkpoint_live = cur_live;
|
|
pll_save_checkpoint();
|
|
}
|
|
|
|
void wd_fdc_device_base::rollback()
|
|
{
|
|
cur_live = checkpoint_live;
|
|
pll_retrieve_checkpoint();
|
|
}
|
|
|
|
void wd_fdc_device_base::live_delay(int state)
|
|
{
|
|
cur_live.next_state = state;
|
|
t_gen->adjust(cur_live.tm - machine().time());
|
|
}
|
|
|
|
void wd_fdc_device_base::live_sync()
|
|
{
|
|
if(!cur_live.tm.is_never()) {
|
|
if(cur_live.tm > machine().time()) {
|
|
LOGSYNC("%s: Rolling back and replaying (%s)\n", ttsn().c_str(), tts(cur_live.tm).c_str());
|
|
rollback();
|
|
live_run(machine().time());
|
|
pll_commit(floppy, cur_live.tm);
|
|
} else {
|
|
LOGSYNC("%s: Committing (%s)\n", ttsn().c_str(), tts(cur_live.tm).c_str());
|
|
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) {
|
|
pll_stop_writing(floppy, cur_live.tm);
|
|
cur_live.tm = attotime::never;
|
|
}
|
|
}
|
|
cur_live.next_state = -1;
|
|
checkpoint();
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::live_abort()
|
|
{
|
|
if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) {
|
|
rollback();
|
|
live_run(machine().time());
|
|
}
|
|
|
|
pll_stop_writing(floppy, cur_live.tm);
|
|
cur_live.tm = attotime::never;
|
|
cur_live.state = IDLE;
|
|
cur_live.next_state = -1;
|
|
}
|
|
|
|
bool wd_fdc_device_base::read_one_bit(const attotime &limit)
|
|
{
|
|
int bit = 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 wd_fdc_device_base::write_one_bit(const attotime &limit)
|
|
{
|
|
bool bit = cur_live.shift_reg & 0x8000;
|
|
if(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 wd_fdc_device_base::live_write_raw(uint16_t raw)
|
|
{
|
|
LOGWRITE("write raw %04x, CRC=%04x\n", raw, cur_live.crc);
|
|
cur_live.shift_reg = raw;
|
|
cur_live.data_bit_context = raw & 1;
|
|
}
|
|
|
|
void wd_fdc_device_base::live_write_mfm(uint8_t mfm)
|
|
{
|
|
bool context = cur_live.data_bit_context;
|
|
uint16_t 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;
|
|
LOGWRITE("live_write_mfm byte=%02x, raw=%04x, CRC=%04x\n", mfm, raw, cur_live.crc);
|
|
}
|
|
|
|
|
|
void wd_fdc_device_base::live_write_fm(uint8_t fm)
|
|
{
|
|
uint16_t raw = 0xaaaa;
|
|
for(int i=0; i<8; i++)
|
|
if(fm & (0x80 >> i))
|
|
raw |= 0x4000 >> (2*i);
|
|
cur_live.data_reg = fm;
|
|
cur_live.shift_reg = raw;
|
|
cur_live.data_bit_context = fm & 1;
|
|
LOGWRITE("live_write_fm byte=%02x, raw=%04x, CRC=%04x\n", fm, raw, cur_live.crc);
|
|
}
|
|
|
|
void wd_fdc_device_base::live_run(attotime limit)
|
|
{
|
|
// LOG("%s\n", FUNCNAME);
|
|
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().c_str(), tts(limit).c_str());
|
|
|
|
for(;;) {
|
|
switch(cur_live.state) {
|
|
case SEARCH_ADDRESS_MARK_HEADER:
|
|
LOGLIVE("%s - SEARCH_ADDRESS_MARK_HEADER\n", FUNCNAME);
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%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);
|
|
|
|
if(!dden && 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(dden && cur_live.shift_reg == 0xf57e) {
|
|
cur_live.crc = 0xef21;
|
|
cur_live.data_separator_phase = false;
|
|
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_HEADER_BLOCK_HEADER: {
|
|
LOGLIVE("%s - READ_HEADER_BLOCK_HEADER\n", FUNCNAME);
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%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);
|
|
|
|
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: {
|
|
LOGLIVE("%s - READ_ID_BLOCK_TO_LOCAL\n", FUNCNAME);
|
|
if(read_one_bit(limit))
|
|
return;
|
|
if(cur_live.bit_counter & 15)
|
|
break;
|
|
int slot = (cur_live.bit_counter >> 4)-1;
|
|
// fprintf(stderr, "%s: slot[%d] = %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 READ_ID_BLOCK_TO_DMA:
|
|
LOGLIVE("%s - READ_ID_BLOCK_TO_DMA\n", FUNCNAME);
|
|
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:
|
|
LOGLIVE("%s - READ_ID_BLOCK_TO_DMA_BYTE\n", FUNCNAME);
|
|
data = cur_live.data_reg;
|
|
if(cur_live.bit_counter == 16)
|
|
sector = data;
|
|
set_drq();
|
|
|
|
if(cur_live.bit_counter == 16*6) {
|
|
if(cur_live.crc) {
|
|
status |= S_CRC;
|
|
}
|
|
|
|
// Already synchronous
|
|
cur_live.state = IDLE;
|
|
return;
|
|
}
|
|
|
|
cur_live.state = READ_ID_BLOCK_TO_DMA;
|
|
checkpoint();
|
|
break;
|
|
|
|
case SEARCH_ADDRESS_MARK_DATA:
|
|
LOGLIVE("%s - SEARCH_ADDRESS_MARK_DATA\n", FUNCNAME);
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%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);
|
|
|
|
if(!dden) {
|
|
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;
|
|
}
|
|
} 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 == 0xf56b ||
|
|
cur_live.shift_reg == 0xf56e || cur_live.shift_reg == 0xf56f)) {
|
|
cur_live.crc =
|
|
cur_live.shift_reg == 0xf56a ? 0x8fe7 :
|
|
cur_live.shift_reg == 0xf56b ? 0x9fc6 :
|
|
cur_live.shift_reg == 0xf56e ? 0xafa5 :
|
|
0xbf84;
|
|
|
|
if((cur_live.data_reg & 0xfe) == 0xf8)
|
|
status |= S_DDM;
|
|
|
|
cur_live.data_separator_phase = false;
|
|
cur_live.bit_counter = 0;
|
|
cur_live.state = READ_SECTOR_DATA;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case READ_DATA_BLOCK_HEADER: {
|
|
LOGLIVE("%s - READ_DATA_BLOCK_HEADER\n", FUNCNAME);
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
LOGSHIFT("%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);
|
|
|
|
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) != 0xf8) {
|
|
live_delay(SEARCH_ADDRESS_MARK_DATA_FAILED);
|
|
return;
|
|
}
|
|
|
|
cur_live.bit_counter = 0;
|
|
if((cur_live.data_reg & 0xfe) == 0xf8)
|
|
status |= S_DDM;
|
|
live_delay(READ_SECTOR_DATA);
|
|
return;
|
|
}
|
|
|
|
case SEARCH_ADDRESS_MARK_DATA_FAILED:
|
|
LOGLIVE("%s - SEARCH_ADDRESS_MARK_DATA_FAILED\n", FUNCNAME);
|
|
status |= S_RNF;
|
|
cur_live.state = IDLE;
|
|
return;
|
|
|
|
case READ_SECTOR_DATA: {
|
|
LOGLIVE("%s - READ_SECTOR_DATA\n", FUNCNAME);
|
|
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:
|
|
LOGLIVE("%s - READ_SECTOR_DATA_BYTE\n", FUNCNAME);
|
|
data = cur_live.data_reg;
|
|
set_drq();
|
|
cur_live.state = READ_SECTOR_DATA;
|
|
checkpoint();
|
|
break;
|
|
|
|
case READ_TRACK_DATA: {
|
|
LOGLIVE("%s - READ_TRACK_DATA\n", FUNCNAME);
|
|
if(read_one_bit(limit))
|
|
return;
|
|
|
|
if(cur_live.bit_counter != 16
|
|
// MFM resyncs
|
|
&& !(!dden && (cur_live.shift_reg == 0x4489
|
|
|| cur_live.shift_reg == 0x5224))
|
|
// FM resyncs
|
|
&& !(dden && (cur_live.shift_reg == 0xf57e // FM IDAM
|
|
|| cur_live.shift_reg == 0xf56f // FM DAM
|
|
|| cur_live.shift_reg == 0xf56a)) // FM DDAM
|
|
)
|
|
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
|
|
|
|
// MZ: TI99 "DISkASSEMBLER" copy protection requires a threshold of 8
|
|
bool output_byte = cur_live.bit_counter > 8;
|
|
|
|
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:
|
|
LOGLIVE("%s - READ_TRACK_DATA_BYTE\n", FUNCNAME);
|
|
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;
|
|
}
|
|
if(data != format_last_byte) {
|
|
if(format_last_byte_count) {
|
|
char buf[32];
|
|
if(format_last_byte_count > 1)
|
|
sprintf(buf, "%dx%02x ", format_last_byte_count, format_last_byte);
|
|
else
|
|
sprintf(buf, "%02x ", format_last_byte);
|
|
format_description_string += buf;
|
|
}
|
|
format_last_byte = data;
|
|
format_last_byte_count = 1;
|
|
} else
|
|
format_last_byte_count++;
|
|
|
|
if(dden) {
|
|
switch(data) {
|
|
case 0xf7:
|
|
if(cur_live.previous_type == live_info::PT_CRC_2) {
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
live_write_fm(0xf7);
|
|
} else {
|
|
cur_live.previous_type = live_info::PT_CRC_1;
|
|
live_write_fm(cur_live.crc >> 8);
|
|
}
|
|
break;
|
|
case 0xf8:
|
|
live_write_raw(0xf56a);
|
|
cur_live.crc = 0xffff;
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
break;
|
|
case 0xf9:
|
|
live_write_raw(0xf56b);
|
|
cur_live.crc = 0xffff;
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
break;
|
|
case 0xfa:
|
|
live_write_raw(0xf56e);
|
|
cur_live.crc = 0xffff;
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
break;
|
|
case 0xfb:
|
|
live_write_raw(0xf56f);
|
|
cur_live.crc = 0xffff;
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
break;
|
|
case 0xfc:
|
|
live_write_raw(0xf77a);
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
break;
|
|
case 0xfe:
|
|
live_write_raw(0xf57e);
|
|
cur_live.crc = 0xffff;
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
break;
|
|
default:
|
|
cur_live.previous_type = live_info::PT_NONE;
|
|
live_write_fm(data);
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
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;
|
|
if(dden)
|
|
live_write_fm(cur_live.crc >> 8);
|
|
else
|
|
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(dden) {
|
|
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 & 1 ? 0xf56a : 0xf56f);
|
|
} else if(cur_live.byte_counter < sector_size + 7-1) {
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
data = 0;
|
|
}
|
|
live_write_fm(data);
|
|
set_drq();
|
|
} else if(cur_live.byte_counter < sector_size + 7) {
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
data = 0;
|
|
}
|
|
live_write_fm(data);
|
|
} else if(cur_live.byte_counter < sector_size + 7+2)
|
|
live_write_fm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < sector_size + 7+3)
|
|
live_write_fm(0xff);
|
|
else {
|
|
pll_stop_writing(floppy, cur_live.tm);
|
|
cur_live.state = IDLE;
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
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 & 1 ? 0xf8 : 0xfb);
|
|
|
|
} else if(cur_live.byte_counter < sector_size + 16-1) {
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
data = 0;
|
|
}
|
|
live_write_mfm(data);
|
|
set_drq();
|
|
} else if(cur_live.byte_counter < sector_size + 16) {
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
data = 0;
|
|
}
|
|
live_write_mfm(data);
|
|
} else if(cur_live.byte_counter < sector_size + 16+2)
|
|
live_write_mfm(cur_live.crc >> 8);
|
|
else if(cur_live.byte_counter < sector_size + 16+3)
|
|
live_write_mfm(0xff);
|
|
else {
|
|
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).c_str(), 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;
|
|
|
|
// MZ: There is an inconsistency in the wd177x specs; compare
|
|
// the flow chart and the text of the section "Write sector" (1-9) and
|
|
// pages 1-17 and 1-18.
|
|
//
|
|
// I suppose the sum of the delays in the flow chart should be
|
|
// 11 and 22, so we shorten the 9-byte delay to 8 bytes.
|
|
|
|
// case 11:
|
|
case 10:
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
cur_live.state = IDLE;
|
|
return;
|
|
}
|
|
break;
|
|
// case 12:
|
|
case 11:
|
|
if(dden) {
|
|
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;
|
|
pll_start_writing(cur_live.tm);
|
|
live_write_fm(0x00);
|
|
}
|
|
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;
|
|
pll_start_writing(cur_live.tm);
|
|
live_write_mfm(0x00);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
logerror("%s: Unknown live state %d\n", tts(cur_live.tm).c_str(), cur_live.state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::set_drq()
|
|
{
|
|
if(drq) {
|
|
status |= S_LOST;
|
|
drq = false;
|
|
if(!drq_cb.isnull())
|
|
drq_cb(false);
|
|
} else if(!(status & S_LOST)) {
|
|
drq = true;
|
|
if(!drq_cb.isnull())
|
|
drq_cb(true);
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::drop_drq()
|
|
{
|
|
if(drq) {
|
|
drq = false;
|
|
if(!drq_cb.isnull())
|
|
drq_cb(false);
|
|
if(main_state == IDLE && (status & S_BUSY)) {
|
|
status &= ~S_BUSY;
|
|
intrq = true;
|
|
if(!intrq_cb.isnull())
|
|
intrq_cb(intrq);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::set_hld()
|
|
{
|
|
if(head_control && !hld) {
|
|
hld = true;
|
|
if(!hld_cb.isnull())
|
|
hld_cb(hld);
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::drop_hld()
|
|
{
|
|
if(head_control && hld) {
|
|
hld = false;
|
|
if(!hld_cb.isnull())
|
|
hld_cb(hld);
|
|
}
|
|
}
|
|
|
|
void wd_fdc_device_base::update_sso()
|
|
{
|
|
// The 'side_control' flag is interpreted as meaning that the FDC has
|
|
// a SSO output feature, not that it necessarily controls the floppy.
|
|
if(!side_control)
|
|
return;
|
|
|
|
uint8_t side = (command & 0x02) ? 1 : 0;
|
|
|
|
// If a SSO callback is defined then it is assumed that this callback
|
|
// will update the floppy side if that is the connection. There are
|
|
// some machines that use the SSO output for other purposes.
|
|
if(!sso_cb.isnull()) {
|
|
sso_cb(side);
|
|
return;
|
|
}
|
|
|
|
// If a SSO callback is not defined then assume that the machine
|
|
// intended the driver to update the floppy side which appears to be
|
|
// the case in most cases.
|
|
if(floppy) {
|
|
floppy->ss_w((command & 0x02) ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
int wd_fdc_device_base::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
return 128 << (size & 3);
|
|
}
|
|
|
|
int wd_fdc_device_base::settle_time() const
|
|
{
|
|
return 60000;
|
|
}
|
|
|
|
wd_fdc_analog_device_base::wd_fdc_analog_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
|
|
wd_fdc_device_base(mconfig, type, tag, owner, clock)
|
|
{
|
|
clock_ratio = 1;
|
|
}
|
|
|
|
void wd_fdc_analog_device_base::pll_reset(bool fm, bool enmf, const attotime &when)
|
|
{
|
|
int clocks = 2;
|
|
|
|
if (fm) clocks *= 2;
|
|
if (enmf) clocks *= 2;
|
|
|
|
cur_pll.reset(when);
|
|
cur_pll.set_clock(clocks_to_attotime(clocks));
|
|
}
|
|
|
|
void wd_fdc_analog_device_base::pll_start_writing(const attotime &tm)
|
|
{
|
|
cur_pll.start_writing(tm);
|
|
}
|
|
|
|
void wd_fdc_analog_device_base::pll_commit(floppy_image_device *floppy, const attotime &tm)
|
|
{
|
|
cur_pll.commit(floppy, tm);
|
|
}
|
|
|
|
void wd_fdc_analog_device_base::pll_stop_writing(floppy_image_device *floppy, const attotime &tm)
|
|
{
|
|
cur_pll.stop_writing(floppy, tm);
|
|
}
|
|
|
|
void wd_fdc_analog_device_base::pll_save_checkpoint()
|
|
{
|
|
checkpoint_pll = cur_pll;
|
|
}
|
|
|
|
void wd_fdc_analog_device_base::pll_retrieve_checkpoint()
|
|
{
|
|
cur_pll = checkpoint_pll;
|
|
}
|
|
|
|
int wd_fdc_analog_device_base::pll_get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit)
|
|
{
|
|
return cur_pll.get_next_bit(tm, floppy, limit);
|
|
}
|
|
|
|
bool wd_fdc_analog_device_base::pll_write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit)
|
|
{
|
|
return cur_pll.write_next_bit(bit, tm, floppy, limit);
|
|
}
|
|
|
|
wd_fdc_digital_device_base::wd_fdc_digital_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
|
|
wd_fdc_device_base(mconfig, type, tag, owner, clock)
|
|
{
|
|
clock_ratio = 4;
|
|
}
|
|
|
|
constexpr int wd_fdc_digital_device_base::wd_digital_step_times[4];
|
|
|
|
void wd_fdc_digital_device_base::pll_reset(bool fm, bool enmf, const attotime &when)
|
|
{
|
|
int clocks = 1;
|
|
|
|
if (fm) clocks *= 2;
|
|
if (enmf) clocks *= 2;
|
|
|
|
cur_pll.reset(when);
|
|
cur_pll.set_clock(clocks_to_attotime(clocks));
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::pll_start_writing(const attotime &tm)
|
|
{
|
|
cur_pll.start_writing(tm);
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::pll_commit(floppy_image_device *floppy, const attotime &tm)
|
|
{
|
|
cur_pll.commit(floppy, tm);
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::pll_stop_writing(floppy_image_device *floppy, const attotime &tm)
|
|
{
|
|
cur_pll.stop_writing(floppy, tm);
|
|
}
|
|
|
|
int wd_fdc_digital_device_base::pll_get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit)
|
|
{
|
|
return cur_pll.get_next_bit(tm, floppy, limit);
|
|
}
|
|
|
|
bool wd_fdc_digital_device_base::pll_write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const attotime &limit)
|
|
{
|
|
return cur_pll.write_next_bit(bit, tm, floppy, limit);
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::pll_save_checkpoint()
|
|
{
|
|
checkpoint_pll = cur_pll;
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::pll_retrieve_checkpoint()
|
|
{
|
|
cur_pll = checkpoint_pll;
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::digital_pll_t::set_clock(const attotime &period)
|
|
{
|
|
for(int i=0; i<42; i++)
|
|
delays[i] = period*(i+1);
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::digital_pll_t::reset(const 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 wd_fdc_digital_device_base::digital_pll_t::get_next_bit(attotime &tm, floppy_image_device *floppy, const attotime &limit)
|
|
{
|
|
attotime when = floppy ? floppy->get_next_transition(ctime) : attotime::never;
|
|
|
|
/*
|
|
if(!when.is_never())
|
|
LOGTRANSITION("transition_time=%s\n", tts(when).c_str());
|
|
*/
|
|
for(;;) {
|
|
// LOGTRANSITION("slot=%2d, counter=%03x\n", slot, counter);
|
|
attotime etime = ctime+delays[slot];
|
|
// LOGTRANSITION("etime=%s\n", tts(etime).c_str());
|
|
if(etime > limit)
|
|
return -1;
|
|
if(transition_time == 0xffff && !when.is_never() && etime >= when)
|
|
transition_time = counter;
|
|
if(slot < 8) {
|
|
uint8_t 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;
|
|
}
|
|
//LOGTRANSITION("first transition, time=%03x, inc=%3d\n", transition_time, increment);
|
|
int bit = transition_time != 0xffff;
|
|
|
|
if(transition_time != 0xffff) {
|
|
static const uint8_t pha[8] = { 0xf, 0x7, 0x3, 0x1, 0, 0, 0, 0 };
|
|
static const uint8_t phs[8] = { 0, 0, 0, 0, 0x1, 0x3, 0x7, 0xf };
|
|
static const uint8_t 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_t 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 wd_fdc_digital_device_base::digital_pll_t::start_writing(const attotime &tm)
|
|
{
|
|
write_start_time = tm;
|
|
write_position = 0;
|
|
}
|
|
|
|
void wd_fdc_digital_device_base::digital_pll_t::stop_writing(floppy_image_device *floppy, const attotime &tm)
|
|
{
|
|
commit(floppy, tm);
|
|
write_start_time = attotime::never;
|
|
}
|
|
|
|
bool wd_fdc_digital_device_base::digital_pll_t::write_next_bit(bool bit, attotime &tm, floppy_image_device *floppy, const 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_t 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 wd_fdc_digital_device_base::digital_pll_t::commit(floppy_image_device *floppy, const 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;
|
|
}
|
|
|
|
fd1771_device::fd1771_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1771, tag, owner, clock)
|
|
{
|
|
constexpr static int fd1771_step_times[4] = { 12000, 12000, 20000, 40000 };
|
|
|
|
step_times = fd1771_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 20; // x2 due to fm
|
|
disable_mfm = true;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 3;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int fd1771_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return size ? size << 4 : 4096;
|
|
}
|
|
|
|
fd1781_device::fd1781_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1781, tag, owner, clock)
|
|
{
|
|
constexpr static int fd1781_step_times[4] = { 6000, 12000, 20000, 40000 };
|
|
|
|
step_times = fd1781_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 3;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int fd1781_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return size ? size << 4 : 4096;
|
|
}
|
|
|
|
constexpr int wd_fdc_device_base::fd179x_step_times[4];
|
|
constexpr int wd_fdc_device_base::fd176x_step_times[4];
|
|
|
|
fd1791_device::fd1791_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1791, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1792_device::fd1792_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1792, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = true;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1793_device::fd1793_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1793, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
kr1818vg93_device::kr1818vg93_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, KR1818VG93, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1794_device::fd1794_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1794, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = true;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1795_device::fd1795_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1795, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = true;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int fd1795_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return 128 << ((size + 1) & 3);
|
|
}
|
|
|
|
fd1797_device::fd1797_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1797, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = true;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int fd1797_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return 128 << ((size + 1) & 3);
|
|
}
|
|
|
|
mb8866_device::mb8866_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, MB8866, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
mb8876_device::mb8876_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, MB8876, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
mb8877_device::mb8877_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, MB8877, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 4;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1761_device::fd1761_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1761, tag, owner, clock)
|
|
{
|
|
step_times = fd176x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1763_device::fd1763_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1763, tag, owner, clock)
|
|
{
|
|
step_times = fd176x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
fd1765_device::fd1765_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1765, tag, owner, clock)
|
|
{
|
|
step_times = fd176x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = true;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int fd1765_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return 128 << ((size + 1) & 3);
|
|
}
|
|
|
|
fd1767_device::fd1767_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, FD1767, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = true;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int fd1767_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return 128 << ((size + 1) & 3);
|
|
}
|
|
|
|
wd2791_device::wd2791_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, WD2791, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = true;
|
|
inverted_bus = true;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
wd2793_device::wd2793_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, WD2793, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = true;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
wd2795_device::wd2795_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, WD2795, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = true;
|
|
side_control = true;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int wd2795_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return 128 << ((size + 1) & 3);
|
|
}
|
|
|
|
wd2797_device::wd2797_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_analog_device_base(mconfig, WD2797, tag, owner, clock)
|
|
{
|
|
step_times = fd179x_step_times;
|
|
delay_register_commit = 16;
|
|
delay_command_commit = 12;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = true;
|
|
side_compare = false;
|
|
head_control = true;
|
|
hld_timeout = 15;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|
|
|
|
int wd2797_device::calc_sector_size(uint8_t size, uint8_t command) const
|
|
{
|
|
if(command & 0x08)
|
|
return 128 << (size & 3);
|
|
else
|
|
return 128 << ((size + 1) & 3);
|
|
}
|
|
|
|
wd1770_device::wd1770_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_digital_device_base(mconfig, WD1770, tag, owner, clock)
|
|
{
|
|
step_times = wd_digital_step_times;
|
|
delay_register_commit = 32;
|
|
delay_command_commit = 36; // official 48 is too high for oric jasmin boot
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = false;
|
|
head_control = false;
|
|
hld_timeout = 0;
|
|
motor_control = true;
|
|
ready_hooked = false;
|
|
}
|
|
|
|
wd1772_device::wd1772_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_digital_device_base(mconfig, WD1772, tag, owner, clock)
|
|
{
|
|
const static int wd1772_step_times[4] = { 12000, 24000, 4000, 6000 };
|
|
|
|
step_times = wd1772_step_times;
|
|
delay_register_commit = 32;
|
|
delay_command_commit = 48;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = false;
|
|
head_control = false;
|
|
hld_timeout = 0;
|
|
motor_control = true;
|
|
ready_hooked = false;
|
|
|
|
/* Sam Coupe/+D/Disciple expect a 0xd0 force interrupt command to cause a spin-up.
|
|
eg. +D issues 2x 0xd0, then waits for index pulses to start, bails out with no disk error if that doesn't happen.
|
|
Not sure if other chips should do this too? */
|
|
spinup_on_interrupt = true;
|
|
}
|
|
|
|
int wd1772_device::settle_time() const
|
|
{
|
|
return 30000;
|
|
}
|
|
|
|
wd1773_device::wd1773_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : wd_fdc_digital_device_base(mconfig, WD1773, tag, owner, clock)
|
|
{
|
|
step_times = wd_digital_step_times;
|
|
delay_register_commit = 32;
|
|
delay_command_commit = 48;
|
|
disable_mfm = false;
|
|
has_enmf = false;
|
|
inverted_bus = false;
|
|
side_control = false;
|
|
side_compare = true;
|
|
head_control = false;
|
|
hld_timeout = 0;
|
|
motor_control = false;
|
|
ready_hooked = true;
|
|
}
|