From dfa9e1a22f4479a1b88129d2123f343c9b23714c Mon Sep 17 00:00:00 2001 From: Bavarese Date: Sun, 16 Jul 2017 12:27:16 +0200 Subject: [PATCH] Improve WD2010 emulation Better feedback on cylinder positions. Almost all checks of the winchester test pass (option 16 @ RX50DIAG.TD0 diskette), except low level tests expecting manufacturer or diagnostic tracks (write test #1). --- src/devices/machine/wd2010.cpp | 109 +++++++++++++-------------------- 1 file changed, 44 insertions(+), 65 deletions(-) diff --git a/src/devices/machine/wd2010.cpp b/src/devices/machine/wd2010.cpp index 52bdd98905a..7c1fa28fbfc 100644 --- a/src/devices/machine/wd2010.cpp +++ b/src/devices/machine/wd2010.cpp @@ -4,23 +4,25 @@ 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. Honors DRIVE_READY and WRITE FAULT (DRDY / WF). 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 : - - MULTI SECTOR TRANSFERS (M = 1); MULTIPLE DRIVES - - AUTO_SCAN_ID / SEEK + INDEX TIMERS / ID NOT FOUND - - IMPLIED SEEKS / IMPLIED WRITES / RETRIES - - EDGE or LEVEL TRIGGERED SEEK_COMPLETE (SC) - - SET_PARAMETER / COMPUTE_CORRECTION (DWC flag!) +UNIMPLEMENTED FEATURES : + - more than 1 drive (untested) + - multi sector transfers (M = 1) + - seek and index timers / ID not found. + - implied seeks / implied writes / retries + - 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 the intended instruction flow. Some loops were omitted! @@ -447,7 +449,7 @@ void wd2010_device::restore(uint8_t data) // seek - //------------------------------------------------- -// FIXME : step rate, drive change (!) +// FIXME : step rate, drive change (untested) // NOT IMPLEMENTED: IMPLIED SEEK ("wait until rising edge of SC signal") void wd2010_device::seek(uint8_t data) @@ -459,7 +461,6 @@ void wd2010_device::seek(uint8_t data) m_status = STATUS_BSY | STATUS_CIP; // TODO : store STEP RATE. - auto_scan_id(data); // has drive number changed? 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") 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. } @@ -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) + m_present_cylinder = CYLINDER; // 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 + // (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_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_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? { 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): - - // * does nothing right now * // ****************************************************** void wd2010_device::auto_scan_id(uint8_t data) { static int last_drive; - if (DRIVE != last_drive) { - printf("\n(WD2010) : UNSUPPORTED DRIVE CHANGE !\n"); - logerror("\n(WD2010) : UNSUPPORTED DRIVE CHANGE !\n"); + // FIXME: geometry of disk not available here. Assume sector size already set (?) + 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; - - return; // AUTO-SCAN CURRENTLY DISABLED (see NOTES) + return; // (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) { - // "Update SDH" - /* // 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 - - m_task_file[TASK_FILE_CYLINDER_LOW] = (new_cylinder >> 4) & 0x0f; - m_task_file[TASK_FILE_CYLINDER_HIGH] = (new_cylinder - ((new_cylinder >> 4) << 4)) & 0x0f; + // ...update CYLINDER registers with cylinder given - + m_task_file[TASK_FILE_CYLINDER_HIGH] = (new_cylinder >> 8) & 0xff; + 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_present_cylinder = 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 - //------------------------------------------------- -// Reads the cylinder number from the track on which the heads are PRESENTLY located, -// and writes this into the Present Cylinder Position Register. +// Reads the cylinder number from the track on which the heads are presently located // FIXME: NO ID HANDLING (ID FOUND / NOT FOUND), NO BAD BLOCK; NO CRC 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. > // 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 CRC ERROR. @@ -798,10 +799,10 @@ void wd2010_device::scan_id(uint8_t data) // FORMAT ENTIRE TRACK using the task file + sector buffer // 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 -// - just enough to keep formatter programs happy - +// This routine does just enough to keep formatter +// programs happy (no need to low level format a CHD). // < 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 - // Check for WRITE FAULT (WF) - if (m_in_wf_cb()) + if (m_in_wf_cb()) // Check for WRITE FAULT (WF) { m_error = ERROR_AC; // ABORTED_COMMAND complete_cmd(newstatus | STATUS_ERR); return; } - // uint8_t format_sector_count = m_task_file[TASK_FILE_SECTOR_COUNT]; - // 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); + m_out_wg_cb(0); // (transition from WG 1 -> 0). Actual write. // ** DELAY INTRQ UNTIL WRITE IS COMPLETE : complete_write_when_buffer_ready_high->adjust(attotime::from_usec(1), newstatus | STATUS_DRQ); // 1 USECs