mirror of
https://github.com/holub/mame
synced 2025-07-03 09:06:08 +03:00
Merge pull request #2482 from Bavarese/patch-29
Improve WD2010 emulation
This commit is contained in:
commit
0b1375f218
@ -4,23 +4,25 @@
|
|||||||
|
|
||||||
Western Digital WD2010 Winchester Disk Controller
|
Western Digital WD2010 Winchester Disk Controller
|
||||||
|
|
||||||
Portions (2015) : Karl-Ludwig Deisenhofer
|
Portions (2015, 2017) : Karl-Ludwig Deisenhofer
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
|
|
||||||
Implements WD2010 / WD1010 controller basics.
|
Implements WD2010 / WD1010 controller basics for a single hard disk.
|
||||||
|
|
||||||
Provides IRQ / (B)DRQ signals needed for early MFM cards.
|
Provides IRQ / (B)DRQ signals needed for early MFM cards.
|
||||||
Honors DRIVE_READY and WRITE FAULT (DRDY / WF).
|
Honors DRIVE_READY and WRITE FAULT (DRDY / WF).
|
||||||
|
|
||||||
Single sector read / write (format) confirmed to work with
|
Single sector read / write (format) confirmed to work with
|
||||||
Rainbow-100 controller (WD1010, largely compatible to WD2010, see **)
|
Rainbow-100 controller (WD1010, quite compatible to WD2010, see **)
|
||||||
|
|
||||||
LIST OF UNIMPLEMENTED FEATURES :
|
UNIMPLEMENTED FEATURES :
|
||||||
- MULTI SECTOR TRANSFERS (M = 1); MULTIPLE DRIVES
|
- more than 1 drive (untested)
|
||||||
- AUTO_SCAN_ID / SEEK + INDEX TIMERS / ID NOT FOUND
|
- multi sector transfers (M = 1)
|
||||||
- IMPLIED SEEKS / IMPLIED WRITES / RETRIES
|
- seek and index timers / ID not found.
|
||||||
- EDGE or LEVEL TRIGGERED SEEK_COMPLETE (SC)
|
- implied seeks / implied writes / retries
|
||||||
- SET_PARAMETER / COMPUTE_CORRECTION (DWC flag!)
|
- edge or level triggered seek complete (SC)
|
||||||
|
- set_parameter / compute_correction
|
||||||
|
(the DWC flag is not usable in this context).
|
||||||
|
|
||||||
Pseudo code (from datasheet) left in to illustrate
|
Pseudo code (from datasheet) left in to illustrate
|
||||||
the intended instruction flow. Some loops were omitted!
|
the intended instruction flow. Some loops were omitted!
|
||||||
@ -447,7 +449,7 @@ void wd2010_device::restore(uint8_t data)
|
|||||||
// seek -
|
// seek -
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
// FIXME : step rate, drive change (!)
|
// FIXME : step rate, drive change (untested)
|
||||||
|
|
||||||
// NOT IMPLEMENTED: IMPLIED SEEK ("wait until rising edge of SC signal")
|
// NOT IMPLEMENTED: IMPLIED SEEK ("wait until rising edge of SC signal")
|
||||||
void wd2010_device::seek(uint8_t data)
|
void wd2010_device::seek(uint8_t data)
|
||||||
@ -459,7 +461,6 @@ void wd2010_device::seek(uint8_t data)
|
|||||||
m_status = STATUS_BSY | STATUS_CIP;
|
m_status = STATUS_BSY | STATUS_CIP;
|
||||||
|
|
||||||
// TODO : store STEP RATE.
|
// TODO : store STEP RATE.
|
||||||
|
|
||||||
auto_scan_id(data); // has drive number changed?
|
auto_scan_id(data); // has drive number changed?
|
||||||
|
|
||||||
int direction; // 0 = towards 0
|
int direction; // 0 = towards 0
|
||||||
@ -525,7 +526,11 @@ void wd2010_device::seek(uint8_t data)
|
|||||||
// UPDATE INTERNAL CYLINDER POSITION REGISTER (from WD1010 spec -> "SEEK COMMAND")
|
// UPDATE INTERNAL CYLINDER POSITION REGISTER (from WD1010 spec -> "SEEK COMMAND")
|
||||||
m_present_cylinder = cylinder_registers;
|
m_present_cylinder = cylinder_registers;
|
||||||
|
|
||||||
logerror("SEEK (END) - m_present_cylinder = %u\n", m_present_cylinder);
|
// ...update CYLINDER registers with cylinder found -
|
||||||
|
m_task_file[TASK_FILE_CYLINDER_HIGH] = (m_present_cylinder >> 8) & 0xff;
|
||||||
|
m_task_file[TASK_FILE_CYLINDER_LOW] = (m_present_cylinder - ((m_task_file[TASK_FILE_CYLINDER_HIGH] << 8) )) & 0xff;
|
||||||
|
|
||||||
|
logerror("SEEK (END) - m_present_cylinder = %u SDH CYL L/H %02x / %02x\n", m_present_cylinder,m_task_file[TASK_FILE_CYLINDER_LOW],m_task_file[TASK_FILE_CYLINDER_HIGH]);
|
||||||
|
|
||||||
cmd_timer->adjust(attotime::from_msec(35), newstatus); // 35 msecs makes "SEEK_TIMING" test happy.
|
cmd_timer->adjust(attotime::from_msec(35), newstatus); // 35 msecs makes "SEEK_TIMING" test happy.
|
||||||
}
|
}
|
||||||
@ -585,6 +590,7 @@ void wd2010_device::read_sector(uint8_t data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LOOP OVER 10 INDEXES : SCAN_ID / GET CYL.# (not implemented: ID NOT FOUND)
|
// LOOP OVER 10 INDEXES : SCAN_ID / GET CYL.# (not implemented: ID NOT FOUND)
|
||||||
|
m_present_cylinder = CYLINDER;
|
||||||
|
|
||||||
// CYL / HEAD / SEC.SIZE MATCH ? => (ID FOUND)
|
// CYL / HEAD / SEC.SIZE MATCH ? => (ID FOUND)
|
||||||
//
|
//
|
||||||
@ -654,6 +660,13 @@ void wd2010_device::write_sector(uint8_t data)
|
|||||||
|
|
||||||
m_status = STATUS_BSY | STATUS_CIP; // Assert BUSY + CIP
|
m_status = STATUS_BSY | STATUS_CIP; // Assert BUSY + CIP
|
||||||
|
|
||||||
|
// (When drive changed) : SCAN_ID / GET CYL#
|
||||||
|
auto_scan_id(data); // has drive number changed?
|
||||||
|
|
||||||
|
// Assume YES : CYL.register + internal CYL.register SAME? (if NO => SEEK!)
|
||||||
|
// Assume : SEEK_COMPLETE = YES
|
||||||
|
m_present_cylinder = CYLINDER;
|
||||||
|
|
||||||
m_status |= STATUS_DRQ; // Assert BDRQ + DRQ (= status bit 3)
|
m_status |= STATUS_DRQ; // Assert BDRQ + DRQ (= status bit 3)
|
||||||
m_out_bdrq_cb(1);
|
m_out_bdrq_cb(1);
|
||||||
|
|
||||||
@ -672,12 +685,6 @@ void wd2010_device::complete_write_sector(uint8_t data)
|
|||||||
m_out_bdrq_cb(0); // DE-Assert BDRQ (...and DRQ !)
|
m_out_bdrq_cb(0); // DE-Assert BDRQ (...and DRQ !)
|
||||||
m_status &= ~(STATUS_DRQ);
|
m_status &= ~(STATUS_DRQ);
|
||||||
|
|
||||||
// (When drive changed) : SCAN_ID / GET CYL#
|
|
||||||
auto_scan_id(data); // has drive number changed? (*** UNIMPLEMENTED ***)
|
|
||||||
|
|
||||||
// Assume YES : CYL.register + internal CYL.register SAME? (if NO => SEEK!)
|
|
||||||
// Assume : SEEK_COMPLETE = YES
|
|
||||||
|
|
||||||
if (!m_in_drdy_cb() || m_in_wf_cb()) // DRIVE IS READY / NO WF?
|
if (!m_in_drdy_cb() || m_in_wf_cb()) // DRIVE IS READY / NO WF?
|
||||||
{
|
{
|
||||||
m_error = ERROR_AC; // ABORTED_COMMAND
|
m_error = ERROR_AC; // ABORTED_COMMAND
|
||||||
@ -726,41 +733,35 @@ void wd2010_device::complete_write_sector(uint8_t data)
|
|||||||
|
|
||||||
// ******************************************************
|
// ******************************************************
|
||||||
// AUTO SCAN-ID (whenever DRIVE # changes):
|
// AUTO SCAN-ID (whenever DRIVE # changes):
|
||||||
|
|
||||||
// * does nothing right now *
|
|
||||||
// ******************************************************
|
// ******************************************************
|
||||||
void wd2010_device::auto_scan_id(uint8_t data)
|
void wd2010_device::auto_scan_id(uint8_t data)
|
||||||
{
|
{
|
||||||
static int last_drive;
|
static int last_drive;
|
||||||
|
|
||||||
if (DRIVE != last_drive)
|
if (DRIVE != last_drive)
|
||||||
{
|
{
|
||||||
printf("\n(WD2010) : UNSUPPORTED DRIVE CHANGE !\n");
|
// FIXME: geometry of disk not available here. Assume sector size already set (?)
|
||||||
logerror("\n(WD2010) : UNSUPPORTED DRIVE CHANGE !\n");
|
update_sdh( SECTOR_SIZE, 0, 0, 1 ); // new sector_size, head, cylinder, sector
|
||||||
|
|
||||||
//update_sdh(new_sector_size, new_head, new_cylinder, new_sectornr);
|
logerror("\n(WD2010) : UNSUPPORTED DRIVE CHANGE (old = %02x, new = %02x) Sector size assumed: %d !\n", last_drive, DRIVE, SECTOR_SIZES[SECTOR_SIZE]);
|
||||||
}
|
}
|
||||||
last_drive = DRIVE;
|
last_drive = DRIVE;
|
||||||
|
return; // (see NOTES)
|
||||||
return; // AUTO-SCAN CURRENTLY DISABLED (see NOTES)
|
|
||||||
}
|
}
|
||||||
// ******************************************************
|
// ******************************************************
|
||||||
|
|
||||||
// What to do here (just update present_cylinder with CYLINDER)...?
|
// Update SDH register / update present_cylinder.
|
||||||
void wd2010_device::update_sdh(uint8_t new_sector_size, uint8_t new_head, uint16_t new_cylinder, uint8_t new_sectornr)
|
void wd2010_device::update_sdh(uint8_t new_sector_size, uint8_t new_head, uint16_t new_cylinder, uint8_t new_sectornr)
|
||||||
{
|
{
|
||||||
// "Update SDH"
|
|
||||||
/*
|
|
||||||
// Update SECTOR_SIZE, HEAD in SDH with the ID found -
|
// Update SECTOR_SIZE, HEAD in SDH with the ID found -
|
||||||
m_task_file[TASK_FILE_SDH_REGISTER] = ???
|
m_task_file[TASK_FILE_SDH_REGISTER] &= 0x98; // mask 10011000 (size | head)
|
||||||
|
m_task_file[TASK_FILE_SDH_REGISTER] = ((new_sector_size & 3) << 5) | (new_head & 7);
|
||||||
|
|
||||||
// ...update CYLINDER registers with cylinder found -
|
// ...update CYLINDER registers with cylinder given -
|
||||||
m_task_file[TASK_FILE_CYLINDER_LOW] = (new_cylinder >> 4) & 0x0f;
|
m_task_file[TASK_FILE_CYLINDER_HIGH] = (new_cylinder >> 8) & 0xff;
|
||||||
m_task_file[TASK_FILE_CYLINDER_HIGH] = (new_cylinder - ((new_cylinder >> 4) << 4)) & 0x0f;
|
m_task_file[TASK_FILE_CYLINDER_LOW] = (new_cylinder - ((m_task_file[TASK_FILE_CYLINDER_HIGH] << 8) )) & 0xff;
|
||||||
|
|
||||||
// ...update SECTOR_NUMBER with sector nr. found -
|
// ...update SECTOR_NUMBER with sector nr. given -
|
||||||
m_task_file[TASK_FILE_SECTOR_NUMBER] = new_sectornr;
|
m_task_file[TASK_FILE_SECTOR_NUMBER] = new_sectornr;
|
||||||
*/
|
|
||||||
|
|
||||||
m_present_cylinder = CYLINDER;
|
m_present_cylinder = CYLINDER;
|
||||||
logerror("UPDATE_SDH - m_present_cylinder = %u\n", m_present_cylinder);
|
logerror("UPDATE_SDH - m_present_cylinder = %u\n", m_present_cylinder);
|
||||||
@ -770,8 +771,7 @@ void wd2010_device::update_sdh(uint8_t new_sector_size, uint8_t new_head, uint16
|
|||||||
// scan_id -
|
// scan_id -
|
||||||
//-------------------------------------------------
|
//-------------------------------------------------
|
||||||
|
|
||||||
// Reads the cylinder number from the track on which the heads are PRESENTLY located,
|
// Reads the cylinder number from the track on which the heads are presently located
|
||||||
// and writes this into the Present Cylinder Position Register.
|
|
||||||
|
|
||||||
// FIXME: NO ID HANDLING (ID FOUND / NOT FOUND), NO BAD BLOCK; NO CRC
|
// FIXME: NO ID HANDLING (ID FOUND / NOT FOUND), NO BAD BLOCK; NO CRC
|
||||||
void wd2010_device::scan_id(uint8_t data)
|
void wd2010_device::scan_id(uint8_t data)
|
||||||
@ -786,7 +786,8 @@ void wd2010_device::scan_id(uint8_t data)
|
|||||||
// < TODO: Search for ANY ID FIELD. >
|
// < TODO: Search for ANY ID FIELD. >
|
||||||
|
|
||||||
// Assume ID FOUND :
|
// Assume ID FOUND :
|
||||||
update_sdh( 32, 0, 0, 1 ); // (NEW:) SECTOR_SIZE, HEAD, CYLINDER, SECTOR_NR
|
m_task_file[TASK_FILE_CYLINDER_HIGH] = (m_present_cylinder >> 8) & 0xff;
|
||||||
|
m_task_file[TASK_FILE_CYLINDER_LOW] = (m_present_cylinder - ((m_task_file[TASK_FILE_CYLINDER_HIGH] << 8) )) & 0xff;
|
||||||
|
|
||||||
// NO BAD BLOCK.
|
// NO BAD BLOCK.
|
||||||
// NO CRC ERROR.
|
// NO CRC ERROR.
|
||||||
@ -798,10 +799,10 @@ void wd2010_device::scan_id(uint8_t data)
|
|||||||
// FORMAT ENTIRE TRACK using the task file + sector buffer
|
// FORMAT ENTIRE TRACK using the task file + sector buffer
|
||||||
|
|
||||||
// On real hardware, data fields are filled with FF.
|
// On real hardware, data fields are filled with FF.
|
||||||
// Sector buffer is used for track layout (see datasheet).
|
// Sector buffer is used for track layout (- datasheet).
|
||||||
|
|
||||||
// Routine simulates one single write on each track
|
// This routine does just enough to keep formatter
|
||||||
// - just enough to keep formatter programs happy -
|
// programs happy (no need to low level format a CHD).
|
||||||
|
|
||||||
// < UNIMPLEMENTED: (IMPLIED) SEEKs, INDEX, CRC and GAPs >
|
// < UNIMPLEMENTED: (IMPLIED) SEEKs, INDEX, CRC and GAPs >
|
||||||
//--------------------------------------------------------
|
//--------------------------------------------------------
|
||||||
@ -851,36 +852,14 @@ void wd2010_device::format(uint8_t data)
|
|||||||
|
|
||||||
m_out_wg_cb(1); // Have Index, activate WRITE GATE
|
m_out_wg_cb(1); // Have Index, activate WRITE GATE
|
||||||
|
|
||||||
// Check for WRITE FAULT (WF)
|
if (m_in_wf_cb()) // Check for WRITE FAULT (WF)
|
||||||
if (m_in_wf_cb())
|
|
||||||
{
|
{
|
||||||
m_error = ERROR_AC; // ABORTED_COMMAND
|
m_error = ERROR_AC; // ABORTED_COMMAND
|
||||||
complete_cmd(newstatus | STATUS_ERR);
|
complete_cmd(newstatus | STATUS_ERR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint8_t format_sector_count = m_task_file[TASK_FILE_SECTOR_COUNT];
|
m_out_wg_cb(0); // (transition from WG 1 -> 0). Actual write.
|
||||||
// do
|
|
||||||
// {
|
|
||||||
// < WRITE GAP 1 or GAP 3 >
|
|
||||||
|
|
||||||
// < Wait for SEEK_COMPLETE=1 (extend GAP if SEEK_COMPLETE = 0) >
|
|
||||||
// < Assume SEEK COMPLETE >
|
|
||||||
|
|
||||||
// format_sector_count--;
|
|
||||||
// if (format_sector_count != 0)
|
|
||||||
{
|
|
||||||
// The Rainbow 100 driver does ignore multiple sector
|
|
||||||
// transfers so WRITE FORMAT does not actually write -
|
|
||||||
|
|
||||||
m_out_wg_cb(0); // (transition from WG 1 -> 0)
|
|
||||||
|
|
||||||
// NOTE: decrementing TASK_FILE_SECTOR_COUNT does * NOT WORK *
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
// { // < Write 4Es until INDEX (*** UNIMPLEMENTED ****) >
|
|
||||||
// }
|
|
||||||
// } while (format_sector_count > 0);
|
|
||||||
|
|
||||||
// ** DELAY INTRQ UNTIL WRITE IS COMPLETE :
|
// ** DELAY INTRQ UNTIL WRITE IS COMPLETE :
|
||||||
complete_write_when_buffer_ready_high->adjust(attotime::from_usec(1), newstatus | STATUS_DRQ); // 1 USECs
|
complete_write_when_buffer_ready_high->adjust(attotime::from_usec(1), newstatus | STATUS_DRQ); // 1 USECs
|
||||||
|
Loading…
Reference in New Issue
Block a user