summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/wd2010.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/wd2010.cpp')
-rw-r--r--src/devices/machine/wd2010.cpp109
1 files 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