upd765: i82072 fixes (nw) (#3729)

Mostly tiying up and simplifying the i82072 behaviour; the only change that could potentially impact other upd765 users is marking the st0_filled after every interrupt-generating command ends, rather than only the non-data ones. This appears to be correct behaviour, and will only affect systems which actually do a "sense interrupt status" after a command-completion interrupt (like the InterPro does).
* report valid interrupt sense data after all interrupt-generating commands
* improve i82072 drive busy handling and motor control
This commit is contained in:
Patrick Mackinlay 2018-07-09 16:57:52 +08:00 committed by Olivier Galibert
parent 7427664e00
commit 9eae436524
2 changed files with 118 additions and 58 deletions

View File

@ -268,6 +268,7 @@ void upd765_family_device::soft_reset()
flopi[i].st0 = i;
flopi[i].st0_filled = false;
}
clr_drive_busy();
data_irq = false;
other_irq = false;
internal_drq = false;
@ -433,6 +434,7 @@ uint8_t upd765_family_device::read_msr()
msr |= 1<<i;
//msr |= MSR_CB;
}
msr |= get_drive_busy();
if(data_irq) {
data_irq = false;
@ -472,12 +474,9 @@ uint8_t upd765_family_device::read_fifo()
memmove(result, result+1, result_pos);
if(!result_pos)
main_phase = PHASE_CMD;
else if(result_pos == 1) {
else if(result_pos == 1)
// clear drive busy bit after the first sense interrupt status result byte is read
for(floppy_info &fi : flopi)
if((fi.main_state == RECALIBRATE || fi.main_state == SEEK) && fi.sub_state == IDLE && fi.st0_filled == false)
fi.main_state = IDLE;
}
clr_drive_busy();
break;
default:
LOGFIFO("read_fifo in phase %d\n", main_phase);
@ -1469,11 +1468,11 @@ void upd765_family_device::command_end(floppy_info &fi, bool data_completion)
{
LOGCOMMAND("command done (%s) - %s\n", data_completion ? "data" : "seek", results());
fi.main_state = fi.sub_state = IDLE;
fi.st0_filled = true;
if(data_completion)
data_irq = true;
else {
other_irq = true;
fi.st0_filled = true;
}
check_irq();
}
@ -2639,6 +2638,8 @@ void i82072_device::device_start()
save_item(NAME(motor_off_counter));
save_item(NAME(motor_on_counter));
save_item(NAME(drive_busy));
save_item(NAME(delayed_command));
}
void i82072_device::soft_reset()
@ -2650,13 +2651,68 @@ void i82072_device::soft_reset()
int i82072_device::check_command()
{
// ...00110 read data
// ...01100 read deleted data
// ..000101 write data
// ..001001 write deleted data
// 0.000010 read track
// 0.001010 read id
// 0.001101 format track
// 00000111 recalibrate
// 00001000 sense interrupt status
// 00000011 specify
// 00000100 sense drive status
// 00001111 seek
// 00010011 configure
// ...01011 motor on/off
// 1.001111 relative seek
// 00001110 dumpreg
switch(command[0] & 0x1f) {
case 0x02:
return command_pos == 9 ? C_READ_TRACK : C_INCOMPLETE;
case 0x03:
return command_pos == 3 ? C_SPECIFY : C_INCOMPLETE;
case 0x04:
return command_pos == 2 ? C_SENSE_DRIVE_STATUS : C_INCOMPLETE;
case 0x05:
case 0x09:
return command_pos == 9 ? C_WRITE_DATA : C_INCOMPLETE;
case 0x06:
case 0x0c:
return command_pos == 9 ? C_READ_DATA : C_INCOMPLETE;
case 0x07:
return command_pos == 2 ? C_RECALIBRATE : C_INCOMPLETE;
case 0x08:
return C_SENSE_INTERRUPT_STATUS;
case 0x0a:
return command_pos == 2 ? C_READ_ID : C_INCOMPLETE;
case 0x0b:
return C_MOTOR_ONOFF;
}
return upd765_family_device::check_command();
case 0x0d:
return command_pos == 6 ? C_FORMAT_TRACK : C_INCOMPLETE;
case 0x0e:
return C_DUMP_REG;
case 0x0f:
return command_pos == 3 ? C_SEEK : C_INCOMPLETE;
case 0x13:
return command_pos == 4 ? C_CONFIGURE : C_INCOMPLETE;
default:
return C_INVALID;
}
}
void i82072_device::start_command(int cmd)
@ -2675,13 +2731,18 @@ void i82072_device::start_command(int cmd)
case C_SEEK:
// start the motor
motor_control(command[1] & 0x3, true);
// TODO: motor on delay
//if(motor_on_counter > 0)
break;
}
upd765_family_device::start_command(cmd);
// execute the command immediately if there's no motor on delay
if(motor_on_counter == 0) {
upd765_family_device::start_command(cmd);
// set motor off counter if command execution has completed
if (main_phase != PHASE_EXEC && motorcfg)
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
} else
delayed_command = cmd;
}
void i82072_device::execute_command(int cmd)
@ -2700,6 +2761,10 @@ void i82072_device::execute_command(int cmd)
LOGCOMMAND("command motor %s drive %d\n", motor_on ? "on" : "off", fi.id);
// if we are selecting a different drive, stop the motor on the previously selected drive
if (selected_drive != fi.id && flopi[selected_drive].dev && flopi[selected_drive].dev->mon_r() == 0)
flopi[selected_drive].dev->mon_w(1);
// select the drive
if(motor_on)
set_ds(fi.id);
@ -2712,32 +2777,6 @@ void i82072_device::execute_command(int cmd)
break;
}
case C_SPECIFY:
/*
* The InterPro 2000 expects the sense interrupt status command to return
* the status of a completed recalibrate or seek operation instead of a
* pending drive poll status after a soft reset. This behaviour does not
* seem to match any of the other upd765 devices, and without hardware,
* it's difficult to know exactly how it works at this point.
*
* For now, reproduce the expected behaviour by clearing pending drive
* poll results in the specify command, giving two different result
* pathways (both present in InterPro boot code):
*
* 1. reset, poll, sense interrupt status -> drive poll status
* 2. reset, poll, specify, recalibrate, sense interrupt status -> recalibrate status
*
* Possible alternatives include:
*
* 1. Clearing pending status during all command execution.
* 2. Returning results in LIFO order.
* 3. Returning results in priority order (where recalibrate/seek is "higher" priority than poll)
*/
// clear pending interrupts and fall through
for(floppy_info &fi : flopi)
fi.st0_filled = false;
default:
upd765_family_device::execute_command(cmd);
break;
@ -2759,16 +2798,18 @@ void i82072_device::execute_command(int cmd)
*/
void i82072_device::command_end(floppy_info &fi, bool data_completion)
{
LOGCOMMAND("command done (%s) - %s\n", data_completion ? "data" : "seek", results());
fi.sub_state = IDLE;
if(data_completion) {
fi.main_state = IDLE;
data_irq = true;
} else {
other_irq = true;
fi.st0_filled = true;
}
check_irq();
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)
fi.st0_filled = false;
upd765_family_device::command_end(fi, data_completion);
}
void i82072_device::motor_control(int fid, bool start_motor)
@ -2795,9 +2836,6 @@ void i82072_device::motor_control(int fid, bool start_motor)
// set motor on counter
motor_on_counter = (motorcfg & MON) << (motorcfg & HSDA ? 1 : 0);
}
// FIXME: reset motor off timer on command end, not start
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
} else {
// motor off timer only applies to the selected drive
if(selected_drive != fid)
@ -2807,6 +2845,19 @@ void i82072_device::motor_control(int fid, bool start_motor)
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);
// set motor off counter if command execution has completed
if(main_phase != PHASE_EXEC && motorcfg)
motor_off_counter = (2 + ((motorcfg & MOFF) >> 4)) << (motorcfg & HSDA ? 1 : 0);
delayed_command = 0;
return;
}
// ignore motor off timer while drive is busy
if(fi.main_state == SEEK || fi.main_state == RECALIBRATE)
return;
@ -2828,13 +2879,14 @@ void i82072_device::motor_control(int fid, bool start_motor)
void i82072_device::index_callback(floppy_image_device *floppy, int state)
{
for(floppy_info &fi : flopi) {
if(fi.dev != floppy)
continue;
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);
}
// update motor on/off counters and stop motor if necessary
motor_control(fi.id, false);
}
upd765_family_device::index_callback(floppy, state);
}

View File

@ -34,7 +34,7 @@
downcast<upd72065_device *>(device)->set_select_lines_connected(_select);
#define MCFG_I82072_ADD(_tag, _ready) \
MCFG_DEVICE_ADD(_tag, I82072, 0) \
MCFG_DEVICE_ADD(_tag, I82072, 24_MHz_XTAL) \
downcast<i82072_device *>(device)->set_ready_line_connected(_ready);
#define MCFG_SMC37C78_ADD(_tag) \
@ -434,6 +434,9 @@ protected:
bool read_one_bit(const attotime &limit);
bool write_one_bit(const attotime &limit);
virtual u8 get_drive_busy() const { return 0; }
virtual void clr_drive_busy() { };
};
class upd765a_device : public upd765_family_device {
@ -483,11 +486,16 @@ protected:
virtual void execute_command(int cmd) override;
virtual void command_end(floppy_info &fi, bool data_completion) override;
virtual void index_callback(floppy_image_device *floppy, int state) override;
virtual u8 get_drive_busy() const override { return drive_busy; };
virtual void clr_drive_busy() override { drive_busy = 0; };
void motor_control(int fid, bool start_motor);
private:
u8 motor_off_counter;
u8 motor_on_counter;
u8 drive_busy;
int delayed_command;
};
class smc37c78_device : public upd765_family_device {