mirror of
https://github.com/holub/mame
synced 2025-04-23 08:49:55 +03:00
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).
This commit is contained in:
parent
b46091aaa5
commit
dfa9e1a22f
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user