mirror of
https://github.com/holub/mame
synced 2025-04-23 00:39:36 +03:00
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:
parent
7427664e00
commit
9eae436524
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user