Merge pull request #2482 from Bavarese/patch-29

Improve WD2010 emulation
This commit is contained in:
ajrhacker 2017-07-16 12:38:14 -04:00 committed by GitHub
commit 0b1375f218

View File

@ -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