summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/upd765.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/upd765.cpp')
-rw-r--r--src/devices/machine/upd765.cpp925
1 files changed, 673 insertions, 252 deletions
diff --git a/src/devices/machine/upd765.cpp b/src/devices/machine/upd765.cpp
index 3d763187bd8..3788ae3b4d0 100644
--- a/src/devices/machine/upd765.cpp
+++ b/src/devices/machine/upd765.cpp
@@ -20,7 +20,7 @@
#define LOG_LIVE (1U << 12) // Live states
#define LOG_DONE (1U << 13) // Command done
-#define VERBOSE (LOG_GENERAL | LOG_WARN )
+#define VERBOSE (LOG_GENERAL | LOG_WARN)
#include "logmacro.h"
@@ -45,16 +45,19 @@ DEFINE_DEVICE_TYPE(UPD765A, upd765a_device, "upd765a", "NEC
DEFINE_DEVICE_TYPE(UPD765B, upd765b_device, "upd765b", "NEC uPD765B FDC")
DEFINE_DEVICE_TYPE(I8272A, i8272a_device, "i8272a", "Intel 8272A FDC")
DEFINE_DEVICE_TYPE(UPD72065, upd72065_device, "upd72065", "NEC uPD72065 FDC")
+DEFINE_DEVICE_TYPE(UPD72067, upd72067_device, "upd72067", "NEC uPD72067 FDC")
DEFINE_DEVICE_TYPE(UPD72069, upd72069_device, "upd72069", "NEC uPD72069 FDC")
DEFINE_DEVICE_TYPE(I82072, i82072_device, "i82072", "Intel 82072 FDC")
-DEFINE_DEVICE_TYPE(SMC37C78, smc37c78_device, "smc37c78", "SMC FDC73C78 FDC")
+DEFINE_DEVICE_TYPE(SMC37C78, smc37c78_device, "smc37c78", "SMC FDC37C78 FDC")
DEFINE_DEVICE_TYPE(N82077AA, n82077aa_device, "n82077aa", "Intel N82077AA FDC")
DEFINE_DEVICE_TYPE(PC_FDC_SUPERIO, pc_fdc_superio_device, "pc_fdc_superio", "Winbond PC FDC Super I/O")
DEFINE_DEVICE_TYPE(DP8473, dp8473_device, "dp8473", "National Semiconductor DP8473 FDC")
DEFINE_DEVICE_TYPE(PC8477A, pc8477a_device, "pc8477a", "National Semiconductor PC8477A FDC")
+DEFINE_DEVICE_TYPE(PC8477B, pc8477b_device, "pc8477b", "National Semiconductor PC8477B FDC")
DEFINE_DEVICE_TYPE(WD37C65C, wd37c65c_device, "wd37c65c", "Western Digital WD37C65C FDC")
DEFINE_DEVICE_TYPE(MCS3201, mcs3201_device, "mcs3201", "Motorola MCS3201 FDC")
DEFINE_DEVICE_TYPE(TC8566AF, tc8566af_device, "tc8566af", "Toshiba TC8566AF FDC")
+DEFINE_DEVICE_TYPE(HD63266F, hd63266f_device, "hd63266f", "Hitachi HD63266F FDC")
void upd765a_device::map(address_map &map)
{
@@ -97,8 +100,10 @@ void smc37c78_device::map(address_map &map)
void n82077aa_device::map(address_map &map)
{
- map(0x0, 0x0).r(FUNC(n82077aa_device::sra_r));
- map(0x1, 0x1).r(FUNC(n82077aa_device::srb_r));
+ if(mode != mode_t::AT) {
+ map(0x0, 0x0).r(FUNC(n82077aa_device::sra_r));
+ map(0x1, 0x1).r(FUNC(n82077aa_device::srb_r));
+ }
map(0x2, 0x2).rw(FUNC(n82077aa_device::dor_r), FUNC(n82077aa_device::dor_w));
map(0x3, 0x3).rw(FUNC(n82077aa_device::tdr_r), FUNC(n82077aa_device::tdr_w));
map(0x4, 0x4).rw(FUNC(n82077aa_device::msr_r), FUNC(n82077aa_device::dsr_w));
@@ -108,30 +113,27 @@ void n82077aa_device::map(address_map &map)
void pc_fdc_superio_device::map(address_map &map)
{
- map(0x0, 0x0).r(FUNC(pc_fdc_superio_device::sra_r));
- map(0x1, 0x1).r(FUNC(pc_fdc_superio_device::srb_r));
- map(0x2, 0x2).rw(FUNC(pc_fdc_superio_device::dor_r), FUNC(pc_fdc_superio_device::dor_w));
- map(0x3, 0x3).rw(FUNC(pc_fdc_superio_device::tdr_r), FUNC(pc_fdc_superio_device::tdr_w));
- map(0x4, 0x4).rw(FUNC(pc_fdc_superio_device::msr_r), FUNC(pc_fdc_superio_device::dsr_w));
+ map(0x2, 0x2).w(FUNC(pc_fdc_superio_device::dor_w));
+ map(0x3, 0x3).w(FUNC(pc_fdc_superio_device::tdr_w));
+ map(0x4, 0x4).r(FUNC(pc_fdc_superio_device::msr_r));
map(0x5, 0x5).rw(FUNC(pc_fdc_superio_device::fifo_r), FUNC(pc_fdc_superio_device::fifo_w));
map(0x7, 0x7).rw(FUNC(pc_fdc_superio_device::dir_r), FUNC(pc_fdc_superio_device::ccr_w));
}
void dp8473_device::map(address_map &map)
{
- map(0x0, 0x0).r(FUNC(dp8473_device::sra_r));
- map(0x1, 0x1).r(FUNC(dp8473_device::srb_r));
- map(0x2, 0x2).rw(FUNC(dp8473_device::dor_r), FUNC(dp8473_device::dor_w));
- map(0x3, 0x3).rw(FUNC(dp8473_device::tdr_r), FUNC(dp8473_device::tdr_w));
- map(0x4, 0x4).rw(FUNC(dp8473_device::msr_r), FUNC(dp8473_device::dsr_w));
+ map(0x2, 0x2).w(FUNC(dp8473_device::dor_w));
+ map(0x4, 0x4).r(FUNC(dp8473_device::msr_r));
map(0x5, 0x5).rw(FUNC(dp8473_device::fifo_r), FUNC(dp8473_device::fifo_w));
map(0x7, 0x7).rw(FUNC(dp8473_device::dir_r), FUNC(dp8473_device::ccr_w));
}
void pc8477a_device::map(address_map &map)
{
- map(0x0, 0x0).r(FUNC(pc8477a_device::sra_r));
- map(0x1, 0x1).r(FUNC(pc8477a_device::srb_r));
+ if(mode != mode_t::AT) {
+ map(0x0, 0x0).r(FUNC(pc8477a_device::sra_r));
+ map(0x1, 0x1).r(FUNC(pc8477a_device::srb_r));
+ }
map(0x2, 0x2).rw(FUNC(pc8477a_device::dor_r), FUNC(pc8477a_device::dor_w));
map(0x3, 0x3).rw(FUNC(pc8477a_device::tdr_r), FUNC(pc8477a_device::tdr_w));
map(0x4, 0x4).rw(FUNC(pc8477a_device::msr_r), FUNC(pc8477a_device::dsr_w));
@@ -139,8 +141,24 @@ void pc8477a_device::map(address_map &map)
map(0x7, 0x7).rw(FUNC(pc8477a_device::dir_r), FUNC(pc8477a_device::ccr_w));
}
+void pc8477b_device::map(address_map &map)
+{
+ if(mode != mode_t::AT) {
+ map(0x0, 0x0).r(FUNC(pc8477b_device::sra_r));
+ map(0x1, 0x1).r(FUNC(pc8477b_device::srb_r));
+ }
+ map(0x2, 0x2).rw(FUNC(pc8477b_device::dor_r), FUNC(pc8477b_device::dor_w));
+ map(0x3, 0x3).rw(FUNC(pc8477b_device::tdr_r), FUNC(pc8477b_device::tdr_w));
+ map(0x4, 0x4).rw(FUNC(pc8477b_device::msr_r), FUNC(pc8477b_device::dsr_w));
+ map(0x5, 0x5).rw(FUNC(pc8477b_device::fifo_r), FUNC(pc8477b_device::fifo_w));
+ map(0x7, 0x7).rw(FUNC(pc8477b_device::dir_r), FUNC(pc8477b_device::ccr_w));
+}
+
void wd37c65c_device::map(address_map &map)
{
+ // NOTE: this map only covers registers defined by CS.
+ // LDOR and LDCR must be mapped separately, since their addresses are
+ // defined only by external decoding circuits. LDIR (optional) is also separate.
map(0x0, 0x0).r(FUNC(wd37c65c_device::msr_r));
map(0x1, 0x1).rw(FUNC(wd37c65c_device::fifo_r), FUNC(wd37c65c_device::fifo_w));
}
@@ -166,20 +184,21 @@ void tc8566af_device::map(address_map &map)
constexpr int upd765_family_device::rates[4];
upd765_family_device::upd765_family_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
- pc_fdc_interface(mconfig, type, tag, owner, clock),
+ device_t(mconfig, type, tag, owner, clock),
+ ready_connected(true),
+ ready_polled(true),
+ select_connected(true),
+ select_multiplexed(true),
+ has_dor(true),
+ external_ready(false),
+ recalibrate_steps(77),
+ mode(mode_t::AT),
intrq_cb(*this),
drq_cb(*this),
hdl_cb(*this),
idx_cb(*this),
us_cb(*this)
{
- ready_polled = true;
- ready_connected = true;
- select_connected = true;
- select_multiplexed = true;
- external_ready = false;
- dor_reset = 0x00;
- mode = MODE_AT;
}
void upd765_family_device::set_ready_line_connected(bool _ready)
@@ -192,28 +211,19 @@ void upd765_family_device::set_select_lines_connected(bool _select)
select_connected = _select;
}
-void upd765_family_device::set_mode(int _mode)
+void ps2_fdc_device::set_mode(mode_t _mode)
{
mode = _mode;
}
-void upd765_family_device::device_resolve_objects()
-{
- intrq_cb.resolve_safe();
- drq_cb.resolve_safe();
- hdl_cb.resolve_safe();
- idx_cb.resolve_safe();
- us_cb.resolve_safe();
-}
-
void upd765_family_device::device_start()
{
- save_item(NAME(motorcfg));
save_item(NAME(selected_drive));
+ save_item(NAME(drive_busy));
for(int i=0; i != 4; i++) {
char name[2];
- flopi[i].tm = timer_alloc(i);
+ flopi[i].tm = timer_alloc(FUNC(upd765_family_device::update_floppy), this);
flopi[i].id = i;
if(select_connected) {
name[0] = '0'+i;
@@ -242,6 +252,7 @@ void upd765_family_device::device_start()
cur_rate = 250000;
tc = false;
selected_drive = -1;
+ dor = 0x0c;
// reset at upper levels may cause a write to tc ending up with
// live_sync, which will crash if the live structure isn't
@@ -253,7 +264,7 @@ void upd765_family_device::device_start()
cur_live.fi = nullptr;
if(ready_polled) {
- poll_timer = timer_alloc(TIMER_DRIVE_READY_POLLING);
+ poll_timer = timer_alloc(FUNC(upd765_family_device::run_drive_ready_polling), this);
poll_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(1024));
} else
poll_timer = nullptr;
@@ -264,7 +275,8 @@ void upd765_family_device::device_start()
void upd765_family_device::device_reset()
{
- dor = dor_reset;
+ if(has_dor)
+ dor = 0x00;
locked = false;
soft_reset();
}
@@ -281,8 +293,7 @@ void upd765_family_device::soft_reset()
flopi[i].st0_filled = false;
}
clr_drive_busy();
- data_irq = false;
- other_irq = false;
+ irq = false;
internal_drq = false;
fifo_pos = 0;
command_pos = 0;
@@ -301,6 +312,14 @@ void upd765_family_device::soft_reset()
set_ds(select_multiplexed ? 0 : -1);
check_irq();
+ if(BIT(dor, 2))
+ end_reset();
+ else if(ready_polled)
+ poll_timer->adjust(attotime::never);
+}
+
+void upd765_family_device::end_reset()
+{
if(ready_polled)
poll_timer->adjust(attotime::from_usec(100), 0, attotime::from_usec(1024));
}
@@ -330,6 +349,25 @@ bool upd765_family_device::get_ready(int fid)
return !external_ready;
}
+void upd765_family_device::reset_w(int state)
+{
+ // This implementation is not valid for devices with DOR and possibly other extra registers.
+ // The working assumption is that no need to manipulate the RESET line directly when software can use DOR instead.
+ assert(!has_dor);
+ if(bool(state) == !BIT(dor, 2))
+ return;
+
+ LOGREGS("reset = %d\n", state);
+ if(state) {
+ dor &= 0xfb;
+ soft_reset();
+ }
+ else {
+ dor |= 0x04;
+ end_reset();
+ }
+}
+
void upd765_family_device::set_ds(int fid)
{
if(selected_drive == fid)
@@ -361,11 +399,10 @@ void upd765_family_device::set_floppy(floppy_image_device *flop)
idx_cb(0);
}
-uint8_t upd765_family_device::sra_r()
+uint8_t ps2_fdc_device::sra_r()
{
uint8_t sra = 0;
- int fid = dor & 3;
- floppy_info &fi = flopi[fid];
+ floppy_info &fi = flopi[dor & 3];
if(fi.dir)
sra |= 0x01;
if(fi.index)
@@ -376,17 +413,38 @@ uint8_t upd765_family_device::sra_r()
sra |= 0x10;
if(fi.main_state == SEEK_WAIT_STEP_SIGNAL_TIME)
sra |= 0x20;
- sra |= 0x40;
if(cur_irq)
sra |= 0x80;
- if(mode == MODE_M30)
+ if(mode == mode_t::M30)
+ {
sra ^= 0x1f;
+ if(drq)
+ sra |= 0x40;
+ }
+ else
+ {
+ if(!flopi[1].dev)
+ sra |= 0x40;
+ }
return sra;
}
-uint8_t upd765_family_device::srb_r()
+uint8_t ps2_fdc_device::srb_r()
{
- return 0;
+ uint8_t srb = 0;
+ // TODO: rddata, wrdata, write enable bits
+ if(mode == mode_t::M30)
+ {
+ const uint8_t ds[4] = { 0x43, 0x23, 0x62, 0x61 };
+ srb = ds[dor & 3];
+ if(!flopi[1].dev)
+ srb |= 0x80;
+ }
+ else
+ {
+ srb = 0xc0 | ((dor & 1) << 6) | ((dor & 0x30) >> 4);
+ }
+ return srb;
}
uint8_t upd765_family_device::dor_r()
@@ -396,11 +454,16 @@ uint8_t upd765_family_device::dor_r()
void upd765_family_device::dor_w(uint8_t data)
{
+ assert(has_dor);
LOGREGS("dor = %02x\n", data);
uint8_t diff = dor ^ data;
dor = data;
- if(diff & 4)
- soft_reset();
+ if(BIT(diff, 2)) {
+ if(BIT(data, 2))
+ end_reset();
+ else
+ soft_reset();
+ }
for(int i=0; i<4; i++) {
floppy_info &fi = flopi[i];
@@ -421,7 +484,7 @@ void upd765_family_device::tdr_w(uint8_t data)
uint8_t upd765_family_device::msr_r()
{
- uint32_t msr = 0;
+ uint8_t msr = 0;
switch(main_phase) {
case PHASE_CMD:
msr |= MSR_RQM;
@@ -443,17 +506,13 @@ uint8_t upd765_family_device::msr_r()
msr |= MSR_RQM|MSR_DIO|MSR_CB;
break;
}
- for(int i=0; i<4; i++)
+ for(int i=0; i<4; i++) {
if(flopi[i].main_state == RECALIBRATE || flopi[i].main_state == SEEK) {
msr |= 1<<i;
//msr |= MSR_CB;
}
- msr |= get_drive_busy();
-
- if(data_irq && !machine().side_effects_disabled()) {
- data_irq = false;
- check_irq();
}
+ msr |= get_drive_busy();
return msr;
}
@@ -475,13 +534,25 @@ void upd765_family_device::set_rate(int rate)
uint8_t upd765_family_device::fifo_r()
{
uint8_t r = 0xff;
+ if(!machine().side_effects_disabled())
+ {
+ irq = false;
+ check_irq();
+ }
switch(main_phase) {
+ case PHASE_CMD:
+ if(machine().side_effects_disabled())
+ return 0xff;
+ if(command_pos)
+ fifo_w(0xff);
+ LOGFIFO("fifo_r in command phase\n");
+ break;
case PHASE_EXEC:
if(machine().side_effects_disabled())
return fifo[0];
if(internal_drq)
return fifo_pop(false);
- LOGFIFO("fifo_r in phase %d\n", main_phase);
+ LOGFIFO("fifo_r in execution phase\n");
break;
case PHASE_RESULT:
@@ -510,11 +581,15 @@ uint8_t upd765_family_device::fifo_r()
void upd765_family_device::fifo_w(uint8_t data)
{
+ if(!BIT(dor, 2))
+ LOGWARN("%s: fifo_w(%02x) in reset\n", machine().describe_context(), data);
+
+ irq = false;
+ check_irq();
+
switch(main_phase) {
case PHASE_CMD: {
command[command_pos++] = data;
- other_irq = false;
- check_irq();
int cmd = check_command();
if(cmd == C_INCOMPLETE)
break;
@@ -534,7 +609,7 @@ void upd765_family_device::fifo_w(uint8_t data)
fifo_push(data, false);
return;
}
- LOGFIFO("fifo_w in phase %d\n", main_phase);
+ LOGFIFO("fifo_w in execution phase\n");
break;
default:
@@ -579,11 +654,10 @@ void upd765_family_device::enable_transfer()
check_irq();
}
- } else {
- // DMA
- if(!drq)
- set_drq(true);
}
+ // DMA
+ if(!drq)
+ set_drq(true);
}
void upd765_family_device::disable_transfer()
@@ -591,8 +665,8 @@ void upd765_family_device::disable_transfer()
if(spec & SPEC_ND) {
internal_drq = false;
check_irq();
- } else
- set_drq(false);
+ }
+ set_drq(false);
}
void upd765_family_device::fifo_push(uint8_t data, bool internal)
@@ -639,8 +713,8 @@ uint8_t upd765_family_device::fifo_pop(bool internal)
memmove(fifo, fifo+1, fifo_pos);
if(!fifo_write && !fifo_pos)
disable_transfer();
- int thr = fifocfg & 15;
- if(fifo_write && fifo_expected && (fifo_pos <= thr || (fifocfg & 0x20)))
+ int thr = fifocfg & FIF_THR;
+ if(fifo_write && fifo_expected && (fifo_pos <= thr || (fifocfg & FIF_DIS)) && !tc_done)
enable_transfer();
return r;
}
@@ -703,7 +777,7 @@ void upd765_family_device::live_delay(int state)
{
cur_live.next_state = state;
if(cur_live.tm != machine().time())
- cur_live.fi->tm->adjust(cur_live.tm - machine().time());
+ cur_live.fi->tm->adjust(cur_live.tm - machine().time(), cur_live.fi->id);
else
live_sync();
}
@@ -767,7 +841,7 @@ void upd765_family_device::live_run(attotime limit)
// infinity looking for data too.
limit = machine().time() + attotime::from_msec(1);
- cur_live.fi->tm->adjust(attotime::from_msec(1));
+ cur_live.fi->tm->adjust(attotime::from_msec(1), cur_live.fi->id);
}
}
@@ -777,7 +851,7 @@ void upd765_family_device::live_run(attotime limit)
if(read_one_bit(limit))
return;
- LOGSHIFT("%s: shift = %04x data=%02x c=%d\n", tts(cur_live.tm), cur_live.shift_reg,
+ LOGSHIFT("%s: shift = %04x data=%02x c=%d\n", cur_live.tm.to_string(), 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) |
@@ -793,7 +867,7 @@ void upd765_family_device::live_run(attotime limit)
cur_live.data_separator_phase = false;
cur_live.bit_counter = 0;
cur_live.state = READ_HEADER_BLOCK_HEADER;
- LOGLIVE("%s: Found A1\n", tts(cur_live.tm));
+ LOGLIVE("%s: Found A1\n", cur_live.tm.to_string());
}
if(!mfm && cur_live.shift_reg == 0xf57e) {
@@ -801,7 +875,7 @@ void upd765_family_device::live_run(attotime limit)
cur_live.data_separator_phase = false;
cur_live.bit_counter = 0;
cur_live.state = READ_ID_BLOCK;
- LOGLIVE("%s: Found IDAM\n", tts(cur_live.tm));
+ LOGLIVE("%s: Found IDAM\n", cur_live.tm.to_string());
}
break;
@@ -809,7 +883,7 @@ void upd765_family_device::live_run(attotime limit)
if(read_one_bit(limit))
return;
- LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n", tts(cur_live.tm), cur_live.shift_reg,
+ LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n", cur_live.tm.to_string(), 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) |
@@ -829,11 +903,11 @@ void upd765_family_device::live_run(attotime limit)
if(cur_live.shift_reg != 0x4489)
cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
else
- LOGLIVE("%s: Found A1\n", tts(cur_live.tm));
+ LOGLIVE("%s: Found A1\n", cur_live.tm.to_string());
break;
}
if(cur_live.data_reg != 0xfe) {
- LOGLIVE("%s: No ident byte found after triple-A1, continue search\n", tts(cur_live.tm));
+ LOGLIVE("%s: No ident byte found after triple-A1, continue search\n", cur_live.tm.to_string());
cur_live.state = SEARCH_ADDRESS_MARK_HEADER;
break;
}
@@ -851,7 +925,7 @@ void upd765_family_device::live_run(attotime limit)
break;
int slot = (cur_live.bit_counter >> 4)-1;
- LOGLIVE("%s: slot=%d data=%02x crc=%04x\n", tts(cur_live.tm), slot, cur_live.data_reg, cur_live.crc);
+ LOGLIVE("%s: slot=%d data=%02x crc=%04x\n", cur_live.tm.to_string(), slot, cur_live.data_reg, cur_live.crc);
cur_live.idbuf[slot] = cur_live.data_reg;
if(slot == 5) {
live_delay(IDLE);
@@ -864,7 +938,7 @@ void upd765_family_device::live_run(attotime limit)
if(read_one_bit(limit))
return;
- LOGSHIFT("%s: shift = %04x data=%02x c=%d.%x\n", tts(cur_live.tm), cur_live.shift_reg,
+ LOGSHIFT("%s: shift = %04x data=%02x c=%d.%x\n", cur_live.tm.to_string(), 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) |
@@ -909,7 +983,7 @@ void upd765_family_device::live_run(attotime limit)
if(read_one_bit(limit))
return;
- LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n", tts(cur_live.tm), cur_live.shift_reg,
+ LOGSHIFT("%s: shift = %04x data=%02x counter=%d\n", cur_live.tm.to_string(), 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) |
@@ -937,6 +1011,13 @@ void upd765_family_device::live_run(attotime limit)
return;
}
+ if (
+ ((command[0] & 0x08) == 0 && cur_live.data_reg == 0xf8) // Encountered deleted sector during read data
+ || ((command[0] & 0x08) != 0 && cur_live.data_reg == 0xfb) // Encountered normal sector during read deleted data
+ ) {
+ st2 |= ST2_CM;
+ }
+
cur_live.bit_counter = 0;
cur_live.state = READ_SECTOR_DATA;
break;
@@ -1215,7 +1296,7 @@ void upd765_family_device::live_run(attotime limit)
break;
default:
- LOGWARN("%s: Unknown live state %d\n", tts(cur_live.tm), cur_live.state);
+ LOGWARN("%s: Unknown live state %d\n", cur_live.tm.to_string(), cur_live.state);
return;
}
}
@@ -1254,17 +1335,31 @@ int upd765_family_device::check_command()
// MSDOS 6.22 format uses 0xcd to format a track, which makes one
// think only the bottom 5 bits are decoded.
+ // The real 765 completely decodes the commands that don't have
+ // variable upper bits. Some later superio chips don't.
+ // The MCS Powerview depends on this.
- switch(command[0] & 0x1f) {
- case 0x02:
- return command_pos == 9 ? C_READ_TRACK : C_INCOMPLETE;
-
+ switch(command[0]) {
case 0x03:
return command_pos == 3 ? C_SPECIFY : C_INCOMPLETE;
case 0x04:
return command_pos == 2 ? C_SENSE_DRIVE_STATUS : C_INCOMPLETE;
+ case 0x07:
+ return command_pos == 2 ? C_RECALIBRATE : C_INCOMPLETE;
+
+ case 0x08:
+ return C_SENSE_INTERRUPT_STATUS;
+
+ case 0x0f:
+ return command_pos == 3 ? C_SEEK : C_INCOMPLETE;
+ }
+
+ switch(command[0] & 0x1f) {
+ case 0x02:
+ return command_pos == 9 ? C_READ_TRACK : C_INCOMPLETE;
+
case 0x05:
case 0x09:
return command_pos == 9 ? C_WRITE_DATA : C_INCOMPLETE;
@@ -1273,26 +1368,34 @@ int upd765_family_device::check_command()
case 0x0c:
return command_pos == 9 ? C_READ_DATA : C_INCOMPLETE;
- case 0x07:
- return command_pos == 2 ? C_RECALIBRATE : C_INCOMPLETE;
-
- case 0x08:
- return C_SENSE_INTERRUPT_STATUS;
-
case 0x0a:
return command_pos == 2 ? C_READ_ID : C_INCOMPLETE;
case 0x0d:
return command_pos == 6 ? C_FORMAT_TRACK : C_INCOMPLETE;
+ case 0x11:
+ return command_pos == 9 ? C_SCAN_EQUAL : C_INCOMPLETE;
+
+ case 0x19:
+ return command_pos == 9 ? C_SCAN_LOW : C_INCOMPLETE;
+
+ case 0x1d:
+ return command_pos == 9 ? C_SCAN_HIGH : C_INCOMPLETE;
+
+ default:
+ return C_INVALID;
+ }
+}
+
+int ps2_fdc_device::check_command()
+{
+ switch(command[0] & 0x1f) {
case 0x0e:
return C_DUMP_REG;
- case 0x0f:
- return command_pos == 3 ? C_SEEK : C_INCOMPLETE;
-
- case 0x11:
- return command_pos == 9 ? C_SCAN_EQUAL : C_INCOMPLETE;
+ case 0x10:
+ return C_VERSION;
case 0x12:
return command_pos == 2 ? C_PERPENDICULAR : C_INCOMPLETE;
@@ -1303,14 +1406,8 @@ int upd765_family_device::check_command()
case 0x14:
return C_LOCK;
- case 0x19:
- return command_pos == 9 ? C_SCAN_LOW : C_INCOMPLETE;
-
- case 0x1d:
- return command_pos == 9 ? C_SCAN_HIGH : C_INCOMPLETE;
-
default:
- return C_INVALID;
+ return upd765_family_device::check_command();
}
}
@@ -1327,51 +1424,10 @@ void upd765_family_device::start_command(int cmd)
void upd765_family_device::execute_command(int cmd)
{
switch(cmd) {
- case C_CONFIGURE:
- LOGCOMMAND("command configure %02x %02x %02x\n",
- command[1], command[2], command[3]);
- // byte 1 is ignored, byte 3 is precompensation-related
- motorcfg = command[1];
- fifocfg = command[2];
- precomp = command[3];
- main_phase = PHASE_CMD;
- break;
-
- case C_DUMP_REG:
- LOGCOMMAND("command dump regs\n");
- main_phase = PHASE_RESULT;
- result[0] = flopi[0].pcn;
- result[1] = flopi[1].pcn;
- result[2] = flopi[2].pcn;
- result[3] = flopi[3].pcn;
- result[4] = (spec & 0xff00) >> 8;
- result[5] = (spec & 0x00ff);
- result[6] = sector_size;
- result[7] = locked ? 0x80 : 0x00;
- result[7] |= (perpmode & 0x30);
- result[8] = fifocfg;
- result[9] = precomp;
- result_pos = 10;
- break;
-
case C_FORMAT_TRACK:
format_track_start(flopi[command[1] & 3]);
break;
- case C_LOCK:
- locked = command[0] & 0x80;
- main_phase = PHASE_RESULT;
- result[0] = locked ? 0x10 : 0x00;
- result_pos = 1;
- LOGCOMMAND("command lock (%s)\n", locked ? "on" : "off");
- break;
-
- case C_PERPENDICULAR:
- LOGCOMMAND("command perpendicular\n");
- perpmode = command[1];
- main_phase = PHASE_CMD;
- break;
-
case C_READ_DATA:
read_data_start(flopi[command[1] & 3]);
break;
@@ -1403,14 +1459,7 @@ void upd765_family_device::execute_command(int cmd)
case C_SENSE_DRIVE_STATUS: {
floppy_info &fi = flopi[command[1] & 3];
main_phase = PHASE_RESULT;
- result[0] = command[1] & 7;
- if(fi.ready)
- result[0] |= ST3_RY;
- if(fi.dev)
- result[0] |=
- (fi.dev->wpt_r() ? ST3_WP : 0x00) |
- (fi.dev->trk00_r() ? 0x00 : ST3_T0) |
- (fi.dev->twosid_r() ? 0x00 : ST3_TS);
+ result[0] = get_st3(fi);
LOGCOMMAND("command sense drive status %d (%02x)\n", fi.id, result[0]);
result_pos = 1;
break;
@@ -1454,8 +1503,6 @@ void upd765_family_device::execute_command(int cmd)
LOGCOMMAND("command sense interrupt status (fid=%d %02x %02x) (%s)\n", fid, result[0], result[1], machine().describe_context());
result_pos = 2;
- other_irq = false;
- check_irq();
break;
}
@@ -1476,29 +1523,105 @@ void upd765_family_device::execute_command(int cmd)
}
}
+void ps2_fdc_device::execute_command(int cmd)
+{
+ switch(cmd) {
+ case C_CONFIGURE:
+ LOGCOMMAND("command configure %02x %02x %02x\n",
+ command[1], command[2], command[3]);
+ // byte 1 is ignored, byte 3 is precompensation-related
+ fifocfg = command[2];
+ precomp = command[3];
+ main_phase = PHASE_CMD;
+ break;
+
+ case C_DUMP_REG:
+ LOGCOMMAND("command dump regs\n");
+ main_phase = PHASE_RESULT;
+ result[0] = flopi[0].pcn;
+ result[1] = flopi[1].pcn;
+ result[2] = flopi[2].pcn;
+ result[3] = flopi[3].pcn;
+ result[4] = (spec & 0xff00) >> 8;
+ result[5] = (spec & 0x00ff);
+ result[6] = sector_size;
+ result[7] = locked ? 0x80 : 0x00;
+ result[7] |= (perpmode & 0x3f);
+ result[8] = fifocfg;
+ result[9] = precomp;
+ result_pos = 10;
+ break;
+
+ case C_VERSION:
+ LOGCOMMAND("command version\n");
+ main_phase = PHASE_RESULT;
+ result[0] = 0x90;
+ result_pos = 1;
+ break;
+
+ case C_LOCK:
+ locked = command[0] & 0x80;
+ main_phase = PHASE_RESULT;
+ result[0] = locked ? 0x10 : 0x00;
+ result_pos = 1;
+ LOGCOMMAND("command lock (%s)\n", locked ? "on" : "off");
+ break;
+
+ case C_PERPENDICULAR:
+ LOGCOMMAND("command perpendicular\n");
+ if(BIT(command[1], 7))
+ perpmode = command[1] & 0x3f;
+ else
+ perpmode = (perpmode & 0x3c) | (command[1] & 3);
+ main_phase = PHASE_CMD;
+ break;
+
+ default:
+ upd765_family_device::execute_command(cmd);
+ break;
+ }
+}
+
void upd765_family_device::command_end(floppy_info &fi, bool data_completion)
{
LOGDONE("command done (%s) - %s\n", data_completion ? "data" : "seek", results());
fi.main_state = fi.sub_state = IDLE;
- if(data_completion)
- data_irq = true;
- else {
- other_irq = true;
+ irq = true;
+ if(!data_completion) {
fi.st0_filled = true;
+ drive_busy |= (1 << fi.id);
}
check_irq();
}
+uint8_t upd765_family_device::get_st3(floppy_info &fi)
+{
+ uint8_t st3 = command[1] & 7;
+ if(fi.ready)
+ st3 |= ST3_RY;
+ if(fi.dev)
+ st3 |=
+ (fi.dev->wpt_r() ? ST3_WP : 0x00) |
+ (fi.dev->trk00_r() ? 0x00 : ST3_T0) |
+ (fi.dev->twosid_r() ? 0x00 : ST3_TS);
+ return st3;
+}
+
void upd765_family_device::recalibrate_start(floppy_info &fi)
{
LOGCOMMAND("command recalibrate %d\n", command[1] & 3);
fi.main_state = RECALIBRATE;
fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
fi.dir = 1;
- fi.counter = 77;
+ fi.counter = recalibrate_steps;
fi.ready = get_ready(command[1] & 3);
- fi.st0 = (fi.ready ? 0 : ST0_NR);
- seek_continue(fi);
+ fi.st0 = command[1] & 7;
+ if(fi.ready) {
+ seek_continue(fi);
+ } else {
+ fi.st0 |= ST0_NR | ST0_FAIL | ST0_SE;
+ command_end(fi, false);
+ }
}
void upd765_family_device::seek_start(floppy_info &fi)
@@ -1508,13 +1631,18 @@ void upd765_family_device::seek_start(floppy_info &fi)
fi.sub_state = SEEK_WAIT_STEP_TIME_DONE;
fi.dir = fi.pcn > command[2] ? 1 : 0;
fi.ready = get_ready(command[1] & 3);
- fi.st0 = (fi.ready ? 0 : ST0_NR);
- seek_continue(fi);
+ fi.st0 = command[1] & 7;
+ if(fi.ready) {
+ seek_continue(fi);
+ } else {
+ fi.st0 |= ST0_NR | ST0_FAIL | ST0_SE;
+ command_end(fi, false);
+ }
}
-void upd765_family_device::delay_cycles(emu_timer *tm, int cycles)
+void upd765_family_device::delay_cycles(floppy_info &fi, int cycles)
{
- tm->adjust(attotime::from_double(double(cycles)/cur_rate));
+ fi.tm->adjust(attotime::from_double(double(cycles)/cur_rate), fi.id);
}
void upd765_family_device::seek_continue(floppy_info &fi)
@@ -1528,7 +1656,7 @@ void upd765_family_device::seek_continue(floppy_info &fi)
fi.dev->stp_w(0);
}
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
- fi.tm->adjust(attotime::from_nsec(2500));
+ fi.tm->adjust(attotime::from_nsec(2500), fi.id);
return;
case SEEK_WAIT_STEP_SIGNAL_TIME:
@@ -1547,7 +1675,7 @@ void upd765_family_device::seek_continue(floppy_info &fi)
fi.pcn++;
}
fi.sub_state = SEEK_WAIT_STEP_TIME;
- delay_cycles(fi.tm, 500*(16-(spec >> 12)));
+ delay_cycles(fi, 500*(16-(spec >> 12)));
return;
case SEEK_WAIT_STEP_TIME:
@@ -1578,7 +1706,7 @@ void upd765_family_device::seek_continue(floppy_info &fi)
if(done) {
fi.sub_state = SEEK_WAIT_DONE;
// recalibrate and seek takes some time, even if we don't move
- fi.tm->adjust(attotime::from_nsec((fi.main_state == RECALIBRATE) ? 20000 : 10000));
+ fi.tm->adjust(attotime::from_nsec((fi.main_state == RECALIBRATE) ? 20000 : 10000), fi.id);
return;
}
fi.sub_state = SEEK_MOVE;
@@ -1690,12 +1818,12 @@ void upd765_family_device::read_data_continue(floppy_info &fi)
switch(fi.sub_state) {
case HEAD_LOAD:
LOGSTATE("HEAD_LOAD\n");
- delay_cycles(fi.tm, 500*(spec & 0x00fe));
+ delay_cycles(fi, 500*(spec & 0x00fe));
fi.sub_state = HEAD_LOAD_DONE;
break;
case HEAD_LOAD_DONE:
LOGSTATE("HEAD_LOAD_DONE\n");
- if(fi.pcn == command[2] || !(fifocfg & 0x40)) {
+ if(fi.pcn == command[2] || !(fifocfg & FIF_EIS)) {
fi.sub_state = SEEK_DONE;
break;
}
@@ -1705,7 +1833,7 @@ void upd765_family_device::read_data_continue(floppy_info &fi)
fi.dev->stp_w(0);
}
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
- fi.tm->adjust(attotime::from_nsec(2500));
+ fi.tm->adjust(attotime::from_nsec(2500), fi.id);
return;
case SEEK_WAIT_STEP_SIGNAL_TIME:
@@ -1718,7 +1846,7 @@ void upd765_family_device::read_data_continue(floppy_info &fi)
fi.dev->stp_w(1);
fi.sub_state = SEEK_WAIT_STEP_TIME;
- delay_cycles(fi.tm, 500*(16-(spec >> 12)));
+ delay_cycles(fi, 500*(16-(spec >> 12)));
return;
case SEEK_WAIT_STEP_TIME:
@@ -1744,12 +1872,6 @@ void upd765_family_device::read_data_continue(floppy_info &fi)
case SCAN_ID:
LOGSTATE("SCAN_ID\n");
- if(cur_live.crc) {
- fi.st0 |= ST0_FAIL;
- st1 |= ST1_DE|ST1_ND;
- fi.sub_state = COMMAND_DONE;
- break;
- }
// MZ: This st1 handling ensures that both HX5102 floppy and the
// Speedlock protection scheme are properly working.
// a) HX5102 requires that the ND flag not be set when no address
@@ -1771,7 +1893,15 @@ void upd765_family_device::read_data_continue(floppy_info &fi)
live_start(fi, SEARCH_ADDRESS_MARK_HEADER);
return;
}
+ if(cur_live.crc) {
+ fi.st0 |= ST0_FAIL;
+ st1 |= ST1_DE;
+ st1 &= ~ST1_ND;
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
st1 &= ~ST1_ND;
+ st2 &= ~ST2_WC;
LOGRW("reading sector %02x %02x %02x %02x\n",
cur_live.idbuf[0],
cur_live.idbuf[1],
@@ -1803,10 +1933,19 @@ void upd765_family_device::read_data_continue(floppy_info &fi)
if(cur_live.crc) {
fi.st0 |= ST0_FAIL;
st1 |= ST1_DE;
- st2 |= ST2_CM;
+ st2 |= ST2_DD;
fi.sub_state = COMMAND_DONE;
break;
}
+
+ if ((st2 & ST2_CM) && !(command[0] & 0x20)) {
+ // Encountered terminating sector while in non-skip mode.
+ // This will stop reading when a normal data sector is encountered during read deleted data,
+ // or when a deleted sector is encountered during a read data command.
+ fi.sub_state = COMMAND_DONE;
+ break;
+ }
+
bool done = tc_done;
if(command[4] == command[6]) {
if(command[0] & 0x80) {
@@ -1894,6 +2033,14 @@ void upd765_family_device::write_data_start(floppy_info &fi)
write_data_continue(fi);
return;
}
+ else if(fi.dev && fi.dev->wpt_r()) {
+ fi.st0 |= ST0_FAIL;
+ fi.sub_state = COMMAND_DONE;
+ st1 = ST1_NW;
+ st2 = 0;
+ write_data_continue(fi);
+ return;
+ }
write_data_continue(fi);
}
@@ -1904,7 +2051,7 @@ void upd765_family_device::write_data_continue(floppy_info &fi)
switch(fi.sub_state) {
case HEAD_LOAD:
LOGSTATE("HEAD_LOAD\n");
- delay_cycles(fi.tm, 500*(spec & 0x00fe));
+ delay_cycles(fi, 500*(spec & 0x00fe));
fi.sub_state = HEAD_LOAD_DONE;
break;
case HEAD_LOAD_DONE:
@@ -2045,12 +2192,12 @@ void upd765_family_device::read_track_continue(floppy_info &fi)
switch(fi.sub_state) {
case HEAD_LOAD:
LOGSTATE("HEAD_LOAD\n");
- delay_cycles(fi.tm, 500*(spec & 0x00fe));
+ delay_cycles(fi, 500*(spec & 0x00fe));
fi.sub_state = HEAD_LOAD_DONE;
break;
case HEAD_LOAD_DONE:
LOGSTATE("HEAD_LOAD_DONE\n");
- if(fi.pcn == command[2] || !(fifocfg & 0x40)) {
+ if(fi.pcn == command[2] || !(fifocfg & FIF_EIS)) {
fi.sub_state = SEEK_DONE;
break;
}
@@ -2060,7 +2207,7 @@ void upd765_family_device::read_track_continue(floppy_info &fi)
fi.dev->stp_w(0);
}
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME;
- fi.tm->adjust(attotime::from_nsec(2500));
+ fi.tm->adjust(attotime::from_nsec(2500), fi.id);
return;
case SEEK_WAIT_STEP_SIGNAL_TIME:
@@ -2073,7 +2220,7 @@ void upd765_family_device::read_track_continue(floppy_info &fi)
fi.dev->stp_w(1);
fi.sub_state = SEEK_WAIT_STEP_TIME;
- delay_cycles(fi.tm, 500*(16-(spec >> 12)));
+ delay_cycles(fi, 500*(16-(spec >> 12)));
return;
case SEEK_WAIT_STEP_TIME:
@@ -2122,7 +2269,7 @@ void upd765_family_device::read_track_continue(floppy_info &fi)
else
st1 &= ~ST1_ND;
- sector_size = calc_sector_size(cur_live.idbuf[3]);
+ sector_size = calc_sector_size(command[5]);
fifo_expect(sector_size, false);
fi.sub_state = SECTOR_READ;
LOGSTATE("SEARCH_ADDRESS_MARK_DATA\n");
@@ -2145,7 +2292,7 @@ void upd765_family_device::read_track_continue(floppy_info &fi)
}
if(cur_live.crc) {
st1 |= ST1_DE;
- st2 |= ST2_CM;
+ st2 |= ST2_DD;
}
bool done = tc_done;
sectors_read++;
@@ -2204,12 +2351,21 @@ void upd765_family_device::format_track_start(floppy_info &fi)
set_ds(command[1] & 3);
fi.ready = get_ready(command[1] & 3);
+ st1 = 0;
+ st2 = 0;
if(!fi.ready) {
fi.st0 = (command[1] & 7) | ST0_NR | ST0_FAIL;
fi.sub_state = TRACK_DONE;
format_track_continue(fi);
return;
}
+ else if(fi.dev && fi.dev->wpt_r()) {
+ fi.st0 = (command[1] & 7) | ST0_FAIL;
+ fi.sub_state = TRACK_DONE;
+ st1 = ST1_NW;
+ format_track_continue(fi);
+ return;
+ }
fi.st0 = command[1] & 7;
if(fi.dev)
@@ -2225,7 +2381,7 @@ void upd765_family_device::format_track_continue(floppy_info &fi)
switch(fi.sub_state) {
case HEAD_LOAD:
LOGSTATE("HEAD_LOAD\n");
- delay_cycles(fi.tm, 500*(spec & 0x00fe));
+ delay_cycles(fi, 500*(spec & 0x00fe));
fi.sub_state = HEAD_LOAD_DONE;
break;
case HEAD_LOAD_DONE:
@@ -2249,8 +2405,8 @@ void upd765_family_device::format_track_continue(floppy_info &fi)
LOGSTATE("TRACK_DONE\n");
main_phase = PHASE_RESULT;
result[0] = fi.st0;
- result[1] = 0;
- result[2] = 0;
+ result[1] = st1;
+ result[2] = st2;
result[3] = 0;
result[4] = 0;
result[5] = 0;
@@ -2310,7 +2466,7 @@ void upd765_family_device::read_id_continue(floppy_info &fi)
switch(fi.sub_state) {
case HEAD_LOAD:
LOGSTATE("HEAD_LOAD\n");
- delay_cycles(fi.tm, 500*(spec & 0x00fe));
+ delay_cycles(fi, 500*(spec & 0x00fe));
fi.sub_state = HEAD_LOAD_DONE;
break;
case HEAD_LOAD_DONE:
@@ -2361,8 +2517,8 @@ void upd765_family_device::read_id_continue(floppy_info &fi)
void upd765_family_device::check_irq()
{
bool old_irq = cur_irq;
- cur_irq = data_irq || other_irq || internal_drq;
- cur_irq = cur_irq && (dor & 4) && (mode != MODE_AT || (dor & 8));
+ cur_irq = irq || internal_drq;
+ cur_irq = cur_irq && (dor & 4) && (mode != mode_t::AT || (dor & 8));
if(cur_irq != old_irq) {
LOGTCIRQ("irq = %d\n", cur_irq);
intrq_cb(cur_irq);
@@ -2374,17 +2530,6 @@ bool upd765_family_device::get_irq() const
return cur_irq;
}
-std::string upd765_family_device::tts(attotime t)
-{
- const char *sign = "";
- if(t.seconds() < 0) {
- t = attotime::zero - t;
- sign = "-";
- }
- int const nsec = t.attoseconds() / ATTOSECONDS_PER_NANOSECOND;
- return util::string_format("%s%04d.%03d,%03d,%03d", sign, int(t.seconds()), nsec/1000000, (nsec/1000)%1000, nsec % 1000);
-}
-
std::string upd765_family_device::results() const
{
std::ostringstream stream;
@@ -2402,19 +2547,14 @@ std::string upd765_family_device::results() const
std::string upd765_family_device::ttsn() const
{
- return tts(machine().time());
+ return machine().time().to_string();
}
-void upd765_family_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(upd765_family_device::update_floppy)
{
- if(id == TIMER_DRIVE_READY_POLLING) {
- run_drive_ready_polling();
- return;
- }
-
live_sync();
- floppy_info &fi = flopi[id];
+ floppy_info &fi = flopi[param];
switch(fi.sub_state) {
case SEEK_WAIT_STEP_SIGNAL_TIME:
fi.sub_state = SEEK_WAIT_STEP_SIGNAL_TIME_DONE;
@@ -2427,7 +2567,7 @@ void upd765_family_device::device_timer(emu_timer &timer, device_timer_id id, in
general_continue(fi);
}
-void upd765_family_device::run_drive_ready_polling()
+TIMER_CALLBACK_MEMBER(upd765_family_device::run_drive_ready_polling)
{
if(main_phase != PHASE_CMD || (fifocfg & FIF_POLL) || command_pos)
return;
@@ -2438,9 +2578,9 @@ void upd765_family_device::run_drive_ready_polling()
LOGCOMMAND("polled %d : %d -> %d\n", fid, flopi[fid].ready, ready);
flopi[fid].ready = ready;
if(!flopi[fid].st0_filled) {
- flopi[fid].st0 = ST0_ABRT | fid;
+ flopi[fid].st0 = ST0_ABRT | fid | (ready ? 0 : ST0_NR);
flopi[fid].st0_filled = true;
- other_irq = true;
+ irq = true;
}
}
}
@@ -2585,7 +2725,7 @@ bool upd765_family_device::write_one_bit(const attotime &limit)
void upd765_family_device::live_write_raw(uint16_t raw)
{
- LOGLIVE("%s: write %04x %04x\n", tts(cur_live.tm), raw, cur_live.crc);
+ LOGLIVE("%s: write %04x %04x\n", cur_live.tm.to_string(), raw, cur_live.crc);
cur_live.shift_reg = raw;
cur_live.data_bit_context = raw & 1;
}
@@ -2605,7 +2745,7 @@ void upd765_family_device::live_write_mfm(uint8_t mfm)
cur_live.data_reg = mfm;
cur_live.shift_reg = raw;
cur_live.data_bit_context = context;
- LOGLIVE("%s: write %02x %04x %04x\n", tts(cur_live.tm), mfm, cur_live.crc, raw);
+ LOGLIVE("%s: write %02x %04x %04x\n", cur_live.tm.to_string(), mfm, cur_live.crc, raw);
}
void upd765_family_device::live_write_fm(uint8_t fm)
@@ -2617,7 +2757,7 @@ void upd765_family_device::live_write_fm(uint8_t fm)
cur_live.data_reg = fm;
cur_live.shift_reg = raw;
cur_live.data_bit_context = fm & 1;
- LOGLIVE("%s: write %02x %04x %04x\n", tts(cur_live.tm), fm, cur_live.crc, raw);
+ LOGLIVE("%s: write %02x %04x %04x\n", cur_live.tm.to_string(), fm, cur_live.crc, raw);
}
bool upd765_family_device::sector_matches() const
@@ -2634,17 +2774,17 @@ bool upd765_family_device::sector_matches() const
upd765a_device::upd765a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, UPD765A, tag, owner, clock)
{
- dor_reset = 0x0c;
+ has_dor = false;
}
upd765b_device::upd765b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, UPD765B, tag, owner, clock)
{
- dor_reset = 0x0c;
+ has_dor = false;
}
i8272a_device::i8272a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, I8272A, tag, owner, clock)
{
- dor_reset = 0x0c;
+ has_dor = false;
}
upd72065_device::upd72065_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd72065_device(mconfig, UPD72065, tag, owner, clock)
@@ -2653,7 +2793,16 @@ upd72065_device::upd72065_device(const machine_config &mconfig, const char *tag,
upd72065_device::upd72065_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, type, tag, owner, clock)
{
- dor_reset = 0x0c;
+ has_dor = false;
+ recalibrate_steps = 255;
+}
+
+upd72067_device::upd72067_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd72065_device(mconfig, UPD72067, tag, owner, clock)
+{
+ ready_polled = true;
+ ready_connected = true;
+ select_connected = true;
+ select_multiplexed = false;
}
upd72069_device::upd72069_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd72065_device(mconfig, UPD72069, tag, owner, clock)
@@ -2666,16 +2815,17 @@ upd72069_device::upd72069_device(const machine_config &mconfig, const char *tag,
i82072_device::i82072_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, I82072, tag, owner, clock)
{
- dor_reset = 0x0c;
+ has_dor = false;
+ recalibrate_steps = 255;
}
void i82072_device::device_start()
{
upd765_family_device::device_start();
+ save_item(NAME(motorcfg));
save_item(NAME(motor_off_counter));
save_item(NAME(motor_on_counter));
- save_item(NAME(drive_busy));
save_item(NAME(delayed_command));
}
@@ -2785,11 +2935,30 @@ void i82072_device::start_command(int cmd)
void i82072_device::execute_command(int cmd)
{
switch(cmd) {
- case C_DUMP_REG:
- upd765_family_device::execute_command(cmd);
+ case C_CONFIGURE:
+ LOGCOMMAND("command configure %02x %02x %02x\n",
+ command[1], command[2], command[3]);
+ motorcfg = command[1];
+ fifocfg = command[2];
+ precomp = command[3];
+ main_phase = PHASE_CMD;
+ break;
+ case C_DUMP_REG:
+ LOGCOMMAND("command dump regs\n");
+ main_phase = PHASE_RESULT;
+ result[0] = flopi[0].pcn;
+ result[1] = flopi[1].pcn;
+ result[2] = flopi[2].pcn;
+ result[3] = flopi[3].pcn;
+ result[4] = (spec & 0xff00) >> 8;
+ result[5] = (spec & 0x00ff);
+ result[6] = sector_size;
// i82072 dumps motor configuration at offset 7
result[7] = motorcfg;
+ result[8] = fifocfg;
+ result[9] = precomp;
+ result_pos = 10;
break;
case C_MOTOR_ONOFF: {
@@ -2820,31 +2989,16 @@ void i82072_device::execute_command(int cmd)
}
}
-/*
- * The Intel datasheet says that the drive busy bits in the MSR are supposed to remain
- * set after a seek or recalibrate until a sense interrupt status status command is
- * executed. The InterPro 2000 diagnostic routine goes further, and tests the drive
- * status bits before and after the first sense interrupt status result byte is read,
- * and expects the drive busy bit to clear only after.
- *
- * The Amstrad CPC6128 uses a upd765a and seems to expect the busy bits to be cleared
- * immediately after the seek/recalibrate interrupt is generated.
- *
- * Special casing the i82072 here seems the only way to reconcile this apparently
- * different behaviour for now.
- */
void i82072_device::command_end(floppy_info &fi, bool data_completion)
{
- if(!data_completion)
- drive_busy |= (1 << fi.id);
-
// set motor off counter
if(motorcfg)
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
// clear existing interrupt sense data
- for(floppy_info &fi : flopi)
+ for(floppy_info &fi : flopi) {
fi.st0_filled = false;
+ }
upd765_family_device::command_end(fi, data_completion);
}
@@ -2928,14 +3082,40 @@ void i82072_device::index_callback(floppy_image_device *floppy, int state)
upd765_family_device::index_callback(floppy, state);
}
-smc37c78_device::smc37c78_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, SMC37C78, tag, owner, clock)
+ps2_fdc_device::ps2_fdc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, type, tag, owner, clock)
+{
+}
+
+void ps2_fdc_device::device_start()
+{
+ upd765_family_device::device_start();
+
+ save_item(NAME(perpmode));
+}
+
+void ps2_fdc_device::device_reset()
+{
+ upd765_family_device::device_reset();
+
+ perpmode = 0;
+}
+
+void ps2_fdc_device::soft_reset()
+{
+ upd765_family_device::soft_reset();
+
+ perpmode &= 0x3c;
+}
+
+smc37c78_device::smc37c78_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ps2_fdc_device(mconfig, SMC37C78, tag, owner, clock)
{
ready_connected = false;
select_connected = true;
select_multiplexed = false;
+ recalibrate_steps = 80;
}
-n82077aa_device::n82077aa_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, N82077AA, tag, owner, clock)
+n82077aa_device::n82077aa_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ps2_fdc_device(mconfig, N82077AA, tag, owner, clock)
{
ready_connected = false;
select_connected = true;
@@ -2956,14 +3136,33 @@ dp8473_device::dp8473_device(const machine_config &mconfig, const char *tag, dev
ready_connected = false;
select_connected = true;
select_multiplexed = false;
+ recalibrate_steps = 77; // TODO: 3917 in extended track range mode
+}
+
+void dp8473_device::soft_reset()
+{
+ upd765_family_device::soft_reset();
+
+ // "interrupt is generated when ... Internal Ready signal changes state immediately after a hardware or software reset"
+ irq = true;
+}
+
+pc8477a_device::pc8477a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ps2_fdc_device(mconfig, PC8477A, tag, owner, clock)
+{
+ ready_polled = true;
+ ready_connected = false;
+ select_connected = true;
+ select_multiplexed = false;
+ recalibrate_steps = 85; // TODO: may also be programmed as 255, 3925 or 4095 by (unemulated) mode command
}
-pc8477a_device::pc8477a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : upd765_family_device(mconfig, PC8477A, tag, owner, clock)
+pc8477b_device::pc8477b_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ps2_fdc_device(mconfig, PC8477B, tag, owner, clock)
{
ready_polled = true;
ready_connected = false;
select_connected = true;
select_multiplexed = false;
+ recalibrate_steps = 85; // TODO: may also be programmed as 255, 3925 or 4095 by (unemulated) mode command
}
wd37c65c_device::wd37c65c_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
@@ -2978,23 +3177,28 @@ wd37c65c_device::wd37c65c_device(const machine_config &mconfig, const char *tag,
(void)m_clock2; // TODO
}
+uint8_t wd37c65c_device::get_st3(floppy_info &fi)
+{
+ uint8_t st3 = command[1] & 7;
+ st3 |= 0x20;
+ if(fi.dev)
+ st3 |=
+ (fi.dev->wpt_r() ? 0x48 : 0x00) |
+ (fi.dev->trk00_r() ? 0x00 : 0x10);
+ return st3;
+}
+
mcs3201_device::mcs3201_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
upd765_family_device(mconfig, MCS3201, tag, owner, clock),
- m_input_handler(*this)
+ m_input_handler(*this, 0)
{
- dor_reset = 0x0c;
+ has_dor = true;
ready_polled = false;
ready_connected = false;
select_connected = true;
select_multiplexed = false;
}
-void mcs3201_device::device_start()
-{
- upd765_family_device::device_start();
- m_input_handler.resolve_safe(0);
-}
-
uint8_t mcs3201_device::input_r()
{
return m_input_handler();
@@ -3008,6 +3212,7 @@ tc8566af_device::tc8566af_device(const machine_config &mconfig, const char *tag,
ready_connected = true;
select_connected = true;
select_multiplexed = false;
+ recalibrate_steps = 255;
}
void tc8566af_device::device_start()
@@ -3038,3 +3243,219 @@ void upd72065_device::auxcmd_w(uint8_t data)
break;
}
}
+
+void upd72067_device::auxcmd_w(uint8_t data)
+{
+ /*
+ * Auxiliary commands:
+ *
+ * ???? ???? start clock
+ * 0011 0011 enable external mode
+ * 0x00 1011 control internal mode
+ * xxxx 1110 enable motors
+ * 010x 1111 select IBM or ECMA/ISO format
+ *
+ * The bare minimum needed to satisfy the news_r3k diagnostic has been
+ * implemented here.
+ */
+ switch(data & 0x0f) {
+ case 0x0e:
+ // motor on/off - no idea about timeout
+ for(unsigned i = 0; i < 4; i++)
+ if(flopi[i].dev)
+ flopi[i].dev->mon_w(!BIT(data, i + 4));
+ [[fallthrough]];
+ case 0x03: // enable external mode
+ case 0x0b: // control internal mode
+ case 0x0f: // select format
+ // report a successful status
+ main_phase = PHASE_RESULT;
+ result[0] = ST0_UNK;
+ result_pos = 1;
+ break;
+ default:
+ upd72065_device::auxcmd_w(data);
+ break;
+ }
+}
+
+void upd72069_device::auxcmd_w(uint8_t data)
+{
+ switch(data) {
+ case 0x36: // reset
+ soft_reset();
+ break;
+ case 0x1e: // motor on, probably
+ for(unsigned i = 0; i < 4; i++)
+ if(flopi[i].dev)
+ flopi[i].dev->mon_w(!BIT(data, i + 4));
+ main_phase = PHASE_RESULT;
+ result[0] = ST0_UNK;
+ result_pos = 1;
+ break;
+ }
+}
+
+
+hd63266f_device::hd63266f_device(const machine_config& mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : upd765_family_device(mconfig, HD63266F, tag, owner, clock)
+ , inp_cb(*this, 0)
+{
+ has_dor = false;
+}
+
+void hd63266f_device::map(address_map &map)
+{
+ map(0x0, 0x0).rw(FUNC(hd63266f_device::msr_r), FUNC(hd63266f_device::abort_w));
+ map(0x1, 0x1).rw(FUNC(hd63266f_device::fifo_r), FUNC(hd63266f_device::fifo_w));
+ map(0x2, 0x2).r(FUNC(hd63266f_device::extstat_r));
+}
+
+void hd63266f_device::soft_reset()
+{
+ upd765_family_device::soft_reset();
+ delayed_command = 0;
+ motor_state = 0;
+ for(int i = 0; i < 4; i++)
+ if(flopi[i].dev) flopi[i].dev->mon_w(1);
+}
+
+void hd63266f_device::abort_w(u8 data)
+{
+ if(data == 0xff) {
+ soft_reset();
+ LOGCOMMAND("abort\n");
+ }
+}
+
+int hd63266f_device::check_command()
+{
+ switch(command[0]) {
+ case 0x0e:
+ return C_SLEEP;
+ case 0x0b:
+ case 0x2b:
+ return command_pos == 4 ? C_SPECIFY : C_INCOMPLETE;
+ case 0x4b:
+ case 0x6b:
+ return command_pos == 7 ? C_SPECIFY2 : C_INCOMPLETE;
+ }
+ return upd765_family_device::check_command();
+}
+
+void hd63266f_device::execute_command(int cmd)
+{
+ switch(cmd)
+ {
+ case C_SLEEP:
+ for(int i = 0; i < 4; i++) {
+ if(flopi[i].dev) flopi[i].dev->mon_w(1);
+ }
+ main_phase = PHASE_CMD;
+ motor_state = 0;
+ LOGCOMMAND("sleep\n");
+ break;
+ case C_SPECIFY2:
+ spec = (command[1] << 8) | command[2];
+ LOGCOMMAND("command specify2 %02x %02x: step_rate=%d ms, head_unload=%d ms, head_load=%d ms, non_dma=%s\n",
+ command[1], command[2], 16-(command[1]>>4), (command[1]&0x0f)<<4, command[2]&0xfe, ((command[2]&1)==1)? "true":"false");
+ main_phase = PHASE_CMD;
+ break;
+ case C_SENSE_DRIVE_STATUS:
+ upd765_family_device::execute_command(cmd);
+ if(!inp_cb.isunset())
+ result[0] = (result[0] & ~ST3_TS) | (inp_cb() ? 0 : 8);
+ break;
+ default:
+ upd765_family_device::execute_command(cmd);
+ break;
+ }
+}
+
+u8 hd63266f_device::extstat_r()
+{
+ return (irq << 6) | motor_state;
+}
+
+// no documentation for motor control so borrow some of 82072
+void hd63266f_device::start_command(int cmd)
+{
+ // check if the command specifies a target drive
+ switch(cmd) {
+ case C_READ_TRACK:
+ case C_WRITE_DATA:
+ case C_READ_DATA:
+ case C_RECALIBRATE:
+ //case C_WRITE_DELETED_DATA:
+ case C_READ_ID:
+ //case C_READ_DELETED_DATA:
+ case C_FORMAT_TRACK:
+ case C_SEEK:
+ // start the motor
+ motor_control(command[1] & 0x3, true);
+ break;
+ default:
+ motor_on_counter = 0;
+ break;
+ }
+
+ // execute the command immediately if there's no motor on delay
+ if(motor_on_counter == 0) {
+ upd765_family_device::start_command(cmd);
+ } else
+ delayed_command = cmd;
+}
+
+void hd63266f_device::motor_control(int fid, bool start_motor)
+{
+ floppy_info &fi = flopi[fid];
+
+ if(start_motor) {
+ // if we are selecting a different drive, stop the motor on the previously selected drive
+ if(selected_drive != fid && flopi[selected_drive].dev && flopi[selected_drive].dev->mon_r() == 0)
+ flopi[selected_drive].dev->mon_w(1);
+
+ // start the motor on the selected drive
+ if(fi.dev && fi.dev->mon_r() == 1) {
+ LOGCOMMAND("motor_control: switching on motor for drive %d\n", fid);
+
+ // select the drive and enable the motor
+ set_ds(fid);
+ fi.dev->mon_w(0);
+ motor_on_counter = 3;
+ motor_state |= 1 << fid;
+ }
+ } else {
+ // motor off timer only applies to the selected drive
+ if(selected_drive != fid)
+ return;
+
+ // decrement motor on counter
+ if(motor_on_counter)
+ motor_on_counter--;
+
+ // execute the command if the motor on counter has expired
+ if(motor_on_counter == 0 && main_phase == PHASE_CMD && delayed_command) {
+ upd765_family_device::start_command(delayed_command);
+
+ delayed_command = 0;
+
+ return;
+ }
+ }
+}
+
+void hd63266f_device::index_callback(floppy_image_device *floppy, int state)
+{
+ if(state) {
+ for(floppy_info &fi : flopi) {
+ if(fi.dev != floppy)
+ continue;
+
+ // update motor on/off counters and stop motor if necessary
+ motor_control(fi.id, false);
+ }
+ }
+ upd765_family_device::index_callback(floppy, state);
+}
+