summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/wd2010.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/wd2010.c')
-rw-r--r--src/emu/machine/wd2010.c743
1 files changed, 665 insertions, 78 deletions
diff --git a/src/emu/machine/wd2010.c b/src/emu/machine/wd2010.c
index 6adc888ca2c..909b08b0243 100644
--- a/src/emu/machine/wd2010.c
+++ b/src/emu/machine/wd2010.c
@@ -2,16 +2,56 @@
// copyright-holders:Curt Coder
/**********************************************************************
- Western Digital WD2010 Winchester Disk Controller
+Western Digital WD2010 Winchester Disk Controller
- Copyright MESS Team.
- Visit http://mamedev.org for licensing and usage restrictions.
+Copyright MESS Team.
+Visit http://mamedev.org for licensing and usage restrictions.
-**********************************************************************/
+Portions (2015) : Karl-Ludwig Deisenhofer
+**********************************************************************
-#include "machine/wd2010.h"
+Implements WD2010 / WD1010 controller basics.
+
+ 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 **)
+
+ 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!)
+
+ Pseudo code (from datasheet) left in to illustrate
+ the intended instruction flow. Some loops were omitted!
+
+ USAGE: tie WF (write fault) to ground if not needed:
+ MCFG_WD2010_IN_WF_CB(GND)
+
+ Other signals should be set to VCC if not serviced:
+ MCFG_WD2010_IN_DRDY_CB(VCC) // DRIVE READY = VCC
+ MCFG_WD2010_IN_SC_CB(VCC) // SEEK COMPLETE = VCC
+ **********************************************************************/
+
+// WD 2010 CONFIGURATION (2048 cylinder limit)
+#define STEP_LIMIT 2048
+#define CYLINDER_HIGH_MASK 0x07
+
+// DEC RD51 chip; different STEP / CYLINDER LIMIT (**):
+
+// WD 1010 CONFIGURATION (1024 cylinder limit)
+// #define STEP_LIMIT 1024
+// #define CYLINDER_HIGH_MASK 0x03
+
+// --------------------------------------------------------
+#define MAX_MFM_SECTORS 17 // STANDARD MFM SECTORS/TRACK
+// --------------------------------------------------------
+#include "machine/wd2010.h"
//**************************************************************************
// MACROS / CONSTANTS
@@ -19,7 +59,6 @@
#define LOG 1
-
// task file
enum
{
@@ -44,7 +83,7 @@ enum
(m_task_file[TASK_FILE_SECTOR_NUMBER])
#define CYLINDER \
- (((m_task_file[TASK_FILE_CYLINDER_HIGH] & 0x07) << 8) | m_task_file[TASK_FILE_CYLINDER_LOW])
+ (((m_task_file[TASK_FILE_CYLINDER_HIGH] & CYLINDER_HIGH_MASK) << 8) | m_task_file[TASK_FILE_CYLINDER_LOW])
#define HEAD \
(m_task_file[TASK_FILE_SDH_REGISTER] & 0x07)
@@ -57,7 +96,6 @@ static const int SECTOR_SIZES[4] = { 256, 512, 1024, 128 };
#define SECTOR_SIZE \
SECTOR_SIZES[(m_task_file[TASK_FILE_SDH_REGISTER] >> 5) & 0x03]
-
// status register
#define STATUS_BSY 0x80
#define STATUS_RDY 0x40
@@ -108,22 +146,24 @@ const device_type WD2010 = &device_creator<wd2010_device>;
//-------------------------------------------------
wd2010_device::wd2010_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
- : device_t(mconfig, WD2010, "Western Digital WD2010", tag, owner, clock, "wd2010", __FILE__),
- m_out_intrq_cb(*this),
- m_out_bdrq_cb(*this),
- m_out_bcr_cb(*this),
- m_in_bcs_cb(*this),
- m_out_bcs_cb(*this),
- m_out_dirin_cb(*this),
- m_out_step_cb(*this),
- m_out_rwc_cb(*this),
- m_in_drdy_cb(*this),
- m_in_index_cb(*this),
- m_in_wf_cb(*this),
- m_in_tk000_cb(*this),
- m_in_sc_cb(*this),
- m_status(0),
- m_error(0)
+: device_t(mconfig, WD2010, "Western Digital WD2010", tag, owner, clock, "wd2010", __FILE__),
+m_out_intrq_cb(*this),
+m_out_bdrq_cb(*this),
+m_out_bcr_cb(*this),
+m_in_bcs_cb(*this),
+m_in_brdy_cb(*this),
+m_out_bcs_cb(*this),
+m_out_dirin_cb(*this),
+m_out_step_cb(*this),
+m_out_rwc_cb(*this),
+m_out_wg_cb(*this),
+m_in_drdy_cb(*this),
+m_in_index_cb(*this),
+m_in_wf_cb(*this),
+m_in_tk000_cb(*this),
+m_in_sc_cb(*this),
+m_status(0),
+m_error(0)
{
}
@@ -139,17 +179,33 @@ void wd2010_device::device_start()
m_out_bdrq_cb.resolve_safe();
m_out_bcr_cb.resolve_safe();
m_in_bcs_cb.resolve_safe(0);
+
+ m_in_brdy_cb.resolve_safe(0);
+
m_out_bcs_cb.resolve_safe();
m_out_dirin_cb.resolve_safe();
m_out_step_cb.resolve_safe();
m_out_rwc_cb.resolve_safe();
+ m_out_wg_cb.resolve_safe();
m_in_drdy_cb.resolve_safe(0);
m_in_index_cb.resolve_safe(0);
m_in_wf_cb.resolve_safe(0);
m_in_tk000_cb.resolve_safe(0);
m_in_sc_cb.resolve_safe(0);
+
+ /* allocate a timer for commands */
+ cmd_timer = timer_alloc(0);
+ complete_write_when_buffer_ready_high = timer_alloc(1);
+ deassert_write_when_buffer_ready_low = timer_alloc(2);
+ deassert_read_when_buffer_ready_high = timer_alloc(3);
}
+// timers
+#define COMMAND_TIMER 0
+#define COMPLETE_WRITE_SECTOR 1
+#define DE_ASSERT_WRITE 2
+#define DE_ASSERT_READ 3
+
//-------------------------------------------------
// device_reset - device-specific reset
@@ -157,6 +213,11 @@ void wd2010_device::device_start()
void wd2010_device::device_reset()
{
+ m_out_intrq_cb(CLEAR_LINE);
+
+ buffer_ready(false);
+
+ m_present_cylinder = 0; // start somewhere
}
@@ -164,23 +225,36 @@ void wd2010_device::device_reset()
// read -
//-------------------------------------------------
-READ8_MEMBER( wd2010_device::read )
+READ8_MEMBER(wd2010_device::read)
{
UINT8 data = 0;
switch (offset)
{
case TASK_FILE_ERROR:
- data = m_error;
+ if (m_status & STATUS_CIP) // "if other registers are read while CIP, the status register contents are returned."
+ data = (m_in_drdy_cb() ? 0x40 : 0) | (m_in_wf_cb() ? 0x20 : 0) | (m_in_sc_cb() ? 0x10 : 0) | m_status;// see STATUS register
+ else
+ data = m_error;
break;
case TASK_FILE_STATUS:
- m_out_intrq_cb(CLEAR_LINE);
- data = m_status | STATUS_RDY | STATUS_SC;
+ m_out_intrq_cb(CLEAR_LINE); // "reading the status register clears INTRQ" (-> datasheet)
+ data = (m_in_drdy_cb() ? 0x40 : 0) | (m_in_wf_cb() ? 0x20 : 0) | (m_in_sc_cb() ? 0x10 : 0) | m_status;// see ERROR register
break;
default:
data = m_task_file[offset];
+
+ if (offset == TASK_FILE_SDH_REGISTER)
+ {
+
+ logerror("(READ) %s WD2010 '%s' SDH: %u\n", machine().describe_context(), tag(), data);
+ logerror("(READ) %s WD2010 '%s' Head: %u\n", machine().describe_context(), tag(), HEAD);
+ logerror("(READ) %s WD2010 '%s' Drive: %u\n", machine().describe_context(), tag(), DRIVE);
+ logerror("(READ) %s WD2010 '%s' Sector Size: %u\n", machine().describe_context(), tag(), SECTOR_SIZE);
+ }
+
break;
}
@@ -192,7 +266,7 @@ READ8_MEMBER( wd2010_device::read )
// write -
//-------------------------------------------------
-WRITE8_MEMBER( wd2010_device::write )
+WRITE8_MEMBER(wd2010_device::write)
{
m_task_file[offset] = data;
@@ -211,23 +285,28 @@ WRITE8_MEMBER( wd2010_device::write )
break;
case TASK_FILE_CYLINDER_LOW:
- if (LOG) logerror("%s WD2010 '%s' Cylinder Low: %u\n", machine().describe_context(), tag(), CYLINDER);
+ if (LOG) logerror("%s WD2010 '%s' Cylinder (lower bits set): %u\n", machine().describe_context(), tag(), CYLINDER);
break;
case TASK_FILE_CYLINDER_HIGH:
- if (LOG) logerror("%s WD2010 '%s' Cylinder Low: %u\n", machine().describe_context(), tag(), CYLINDER);
+ if (LOG) logerror("%s WD2010 '%s' Cylinder (MSB bits set): %u\n", machine().describe_context(), tag(), CYLINDER);
break;
case TASK_FILE_SDH_REGISTER:
if (LOG)
{
- logerror("%s WD2010 '%s' Head: %u\n", machine().describe_context(), tag(), HEAD);
- logerror("%s WD2010 '%s' Drive: %u\n", machine().describe_context(), tag(), DRIVE);
- logerror("%s WD2010 '%s' Sector Size: %u\n", machine().describe_context(), tag(), SECTOR_SIZE);
+ logerror("(WRITE) %s WD2010 '%s' SDH: %u\n", machine().describe_context(), tag(), data);
+ logerror("(WRITE) %s WD2010 '%s' Head: %u\n", machine().describe_context(), tag(), HEAD);
+ logerror("(WRITE) %s WD2010 '%s' Drive: %u\n", machine().describe_context(), tag(), DRIVE);
+ logerror("(WRITE) %s WD2010 '%s' Sector Size: %u\n", machine().describe_context(), tag(), SECTOR_SIZE);
}
break;
case TASK_FILE_COMMAND:
+ m_out_intrq_cb(CLEAR_LINE); // "either reading the status register or writing a new command clears INTRQ"
+ m_status &= ~(STATUS_ERR | STATUS_BSY | STATUS_CIP); // "Reset ERR bit in STATUS upon new cmd" (see datasheet)
+ m_error = 0;
+
if (data == COMMAND_COMPUTE_CORRECTION)
{
if (LOG) logerror("%s WD2010 '%s' COMPUTE CORRECTION\n", machine().describe_context(), tag());
@@ -253,12 +332,12 @@ WRITE8_MEMBER( wd2010_device::write )
break;
case COMMAND_READ_SECTOR:
- if (LOG) logerror("%s WD2010 '%s' READ SECTOR\n", machine().describe_context(), tag());
+ if (LOG) logerror("%s WD2010 '%s' READ SECTOR (I = %u) (M = %u)\n", machine().describe_context(), tag(), ((data & 8)>0), ((data & 4)>0));
read_sector(data);
break;
case COMMAND_WRITE_SECTOR:
- if (LOG) logerror("%s WD2010 '%s' WRITE SECTOR\n", machine().describe_context(), tag());
+ if (LOG) logerror("%s WD2010 '%s' WRITE SECTOR (M = %u)\n", machine().describe_context(), tag(), ((data & 4) > 0));
write_sector(data);
break;
@@ -272,136 +351,644 @@ WRITE8_MEMBER( wd2010_device::write )
format(data);
break;
}
+
}
+
break;
- }
+ } // switch
}
//-------------------------------------------------
// compute_correction -
//-------------------------------------------------
-
void wd2010_device::compute_correction(UINT8 data)
{
+ UINT8 newstatus = STATUS_RDY | STATUS_SC;
+ complete_cmd(newstatus);
}
//-------------------------------------------------
// set_parameter -
//-------------------------------------------------
-
void wd2010_device::set_parameter(UINT8 data)
{
+ UINT8 newstatus = STATUS_RDY | STATUS_SC;
+ complete_cmd(newstatus);
}
//-------------------------------------------------
// restore -
//-------------------------------------------------
-
void wd2010_device::restore(UINT8 data)
{
- // reset INTRQ, errors, set BUSY, CIP
- m_out_intrq_cb(CLEAR_LINE);
+ UINT8 newstatus = STATUS_RDY | STATUS_SC;
+
+ m_out_intrq_cb(CLEAR_LINE); // reset INTRQ, errors, set BUSY, CIP
m_error = 0;
m_status = STATUS_BSY | STATUS_CIP;
- // reset RWC, set direction=OUT, store step rate
- m_out_rwc_cb(0);
- m_out_dirin_cb(0);
+ m_out_rwc_cb(0); // reset RWC, set direction = OUT
- int step_pulses = 0;
+ // datasheet: DIRIN HIGH = in ; LOW = out
+ m_out_dirin_cb(0); // 0 = heads move away from the spindle, towards track O.
+
+ // TODO: store step rate
- while (step_pulses < 2048)
+ m_present_cylinder = 0; // (sse WD2010-05 datasheet)
+ m_task_file[TASK_FILE_CYLINDER_HIGH] = 0;
+ m_task_file[TASK_FILE_CYLINDER_LOW] = 0;
+
+ int step_pulses = 0;
+ while (step_pulses < STEP_LIMIT)
{
while (!m_in_sc_cb())
{
- // drive not ready or write fault?
- if (!m_in_drdy_cb() || m_in_wf_cb())
+ if (!m_in_drdy_cb() || m_in_wf_cb()) // drive not ready or write fault?
{
- // pulse BCR, set AC, INTRQ, reset BSY, CIP
- m_out_bcr_cb(0);
+ m_out_bcr_cb(0); // pulse BCR
m_out_bcr_cb(1);
- m_error = ERROR_AC;
- m_status = (m_in_drdy_cb() << 6) | (m_in_wf_cb() << 5) | STATUS_ERR;
- m_out_intrq_cb(ASSERT_LINE);
+
+ m_error = ERROR_AC; // ERROR : ABORTED COMMAND
+ complete_cmd(newstatus | STATUS_ERR);
return;
}
}
- if (m_in_tk000_cb())
+ //if (m_in_tk000_cb())
+ if (step_pulses == STEP_LIMIT - 2) // Simulate TRACK 00 signal (normally from DRIVE)
{
- // pulse BCR, set INTRQ, reset BSY, CIP
- m_out_bcr_cb(0);
+ m_out_bcr_cb(0); // pulse BCR
m_out_bcr_cb(1);
- m_status &= ~(STATUS_BSY | STATUS_CIP);
- m_out_intrq_cb(ASSERT_LINE);
+ newstatus &= ~(STATUS_BSY | STATUS_CIP); // prepare new status; (INTRQ later) reset BSY, CIP
+ complete_cmd(newstatus);
return;
}
- if (step_pulses == 2047)
+ if (step_pulses == STEP_LIMIT - 1) // NOTE: STEP_LIMIT - differs - between WD2010 and WD1010
{
- // set TK000 error
- m_error = ERROR_TK;
- m_status |= STATUS_ERR;
+ m_error = ERROR_TK; // ERROR: track 0 not reached within limit
+ newstatus = newstatus | STATUS_ERR;
- // pulse BCR, set INTRQ, reset BSY, CIP
- m_out_bcr_cb(0);
+ m_out_bcr_cb(0); // pulse BCR
m_out_bcr_cb(1);
- m_status &= ~(STATUS_BSY | STATUS_CIP);
- m_out_intrq_cb(ASSERT_LINE);
+ newstatus &= ~(STATUS_BSY | STATUS_CIP); // prepare new status; (INTRQ later) reset BSY, CIP
+ complete_cmd(newstatus);
return;
}
- // issue a step pulse
- m_out_step_cb(1);
+ m_out_step_cb(1); // issue a step pulse
m_out_step_cb(0);
step_pulses++;
}
-}
+ assert(1);
+}
//-------------------------------------------------
// seek -
//-------------------------------------------------
+// FIXME : step rate, drive change (!)
+
+// NOT IMPLEMENTED: IMPLIED SEEK ("wait until rising edge of SC signal")
void wd2010_device::seek(UINT8 data)
-{
-}
+{
+ UINT8 newstatus = STATUS_RDY | STATUS_SC;
+
+ m_out_intrq_cb(CLEAR_LINE); // reset INTRQ, errors, set BUSY, CIP
+ m_error = 0;
+ m_status = STATUS_BSY | STATUS_CIP;
+
+ // TODO : store STEP RATE.
+
+ auto_scan_id(data); // has drive number changed?
+
+ int direction = 0; // 0 = towards 0
+ int step_pulses = 0;
+
+ // Calculate number of steps by comparing the cylinder registers
+ // HI/LO with the internally stored position.
+ UINT32 cylinder_registers = CYLINDER;
+ if (m_present_cylinder > cylinder_registers)
+ {
+ step_pulses = m_present_cylinder - cylinder_registers;
+ direction = 0;
+ }
+ else
+ {
+ step_pulses = cylinder_registers - m_present_cylinder;
+ direction = 1;
+ }
+ logerror("SEEK - direction = %u, step_pulses = %u\n", direction, step_pulses);
+ m_out_dirin_cb(direction);
+
+ if (!m_in_drdy_cb() || m_in_wf_cb()) // DRDY de-asserted or WF asserted?
+ {
+ m_error = ERROR_AC;
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+ else
+ {
+ while (step_pulses > 0) // issue STEP PULSES
+ {
+ if (direction == 0)
+ {
+ m_out_step_cb(1); // issue a step pulse
+ m_out_step_cb(0);
+
+ if (m_present_cylinder > 0)
+ m_present_cylinder--;
+ }
+ else
+ {
+ m_out_step_cb(0);
+ m_out_step_cb(1);
+
+ m_present_cylinder++;
+ }
+ step_pulses--;
+ // TODO: delay according to rate field
+ }
+
+ // ALL STEPS ISSUED NOW
+
+ if (!m_in_drdy_cb()) // DRDY not asserted = > ABORTED COMMAND
+ {
+ m_error = ERROR_AC;
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+ }
+
+ // AFTER ALL STEPS ARE ISSUED ...
+ // 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);
+
+ cmd_timer->adjust(attotime::from_msec(35), newstatus); // 35 msecs makes "SEEK_TIMING" test happy.
+}
//-------------------------------------------------
// read_sector -
//-------------------------------------------------
-
+// FIXME: multiple sector transfers, ID / CYL / HEAD / SIZE match
+// + ERROR HANDLING (...)
void wd2010_device::read_sector(UINT8 data)
{
+ UINT8 newstatus = STATUS_RDY | STATUS_SC;
+ int intrq_at_end = 0; // (default) : (I = 1 INTRQ occurs when the command
+
+ m_out_intrq_cb(CLEAR_LINE); // reset INTRQ, errors, set BUSY, CIP
+ m_error = 0;
+ m_status = STATUS_BSY | STATUS_CIP;
+
+ // Assume: drive NO # has not changed... (else: SCAN_ID; GET CYL#)
+ auto_scan_id(data); // has drive number changed?
+
+ // CYL REGISTERS and INTERNAL CYL. SAME ?
+ // TODO: < NOT SAME? THEN _SEEK_ >
+
+ // DRIVE NOT READY? OR WF?
+ if ( (!m_in_drdy_cb()) || m_in_wf_cb() )
+ {
+ m_error = ERROR_AC; // ABORTED_COMMAND
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+ else
+ {
+ m_out_bcs_cb(1); // activate BCS (!)
+
+ m_out_bcr_cb(0); // strobe BCR
+ m_out_bcr_cb(1);
+
+ if (!m_in_drdy_cb()) // DRIVE NOT READY?
+ {
+ m_error = ERROR_AC; // ABORTED_COMMAND
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+ else
+ {
+ // < SEARCH FOR ID FIELD >
+ // < CYL / HEAD / SEC.SIZE MATCH ? >
+
+ // < ID NOT FOUND >
+ if (SECTOR_NUMBER > MAX_MFM_SECTORS)
+ {
+ // prepare new status; (later IRQ +) reset BSY, CIP
+ m_error = ERROR_ID;
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+
+ // LOOP OVER 10 INDEXES : SCAN_ID / GET CYL.# (not implemented: ID NOT FOUND)
+
+ // CYL / HEAD / SEC.SIZE MATCH ? => (ID FOUND)
+ //
+ // NO "BAD BLOCK DETECT" (** NOT IMPLEMENTED **)
+ // NO "CRC ERROR" (** NOT IMPLEMENTED **)
+ // AND "DAM FOUND" (** NOT IMPLEMENTED **)
+
+ // ====> THEN "TRANSFER SECTOR TO BUFFER" <====
+
+ m_out_bcr_cb(0); // strobe BCR
+ m_out_bcr_cb(1);
+
+ // NO "CRC ERROR"
+
+ // FLAG "M" SET? (MULTIPLE SECTOR TRANSFERS)
+ if (data & 4)
+ logerror("WD2010 (READ): MULTIPLE SECTOR READ (M = 1).\n");
+
+ // Assume: NO "M" (MULTIPLE SECTOR TRANSFERS)
+
+ m_out_bcs_cb(0); // deactivate BCS (!)
+
+ m_out_bcr_cb(0); // strobe BCR
+ m_out_bcr_cb(1);
+
+ // set BDRQ (NOTE: DRQ status bit 3 reflects state of BDRQ)
+ m_status |= STATUS_DRQ;
+ m_out_bdrq_cb(1);
+
+ // reset BUSY (* after * TRANSFER OF SECTOR in READ)
+ m_status &= ~(STATUS_BSY);
+
+ // FLAG "I" SET?
+ if (!(data & 8)) // (I = 0 INTRQ occurs with BDRQ/DRQ indicating the Sector Buffer is full...)
+ {
+ m_out_intrq_cb(ASSERT_LINE);
+ if (!(data & 4)) // (...valid only when M = 0)
+ intrq_at_end = STATUS_DWC; // 'reuse' unused DWC bit!
+ }
+ else
+ {
+ intrq_at_end = 0; // (default): (I = 1 INTRQ occurs when the command is completed and the Host has read the Sector Buffer)
+ }
+
+ // (WAIT FOR): BRDY LOW TO HIGH? (see -> TIMER)
+
+ } // DRIVE_READY ? (inner)
+
+ } // DRIVE_READY ? (outer)
+
+ // NOTE : (intrq_at_end = 0) - INTRQ occurs when the command is completed
+ newstatus |= (m_status & ~(STATUS_CIP | STATUS_DRQ)) | intrq_at_end; // de-assert CIP + DRQ (BSY already reset)
+
+ deassert_read_when_buffer_ready_high->adjust(attotime::from_usec(1), newstatus); // complete command ON *RISING EDGE * OF BUFFER_READY
}
//-------------------------------------------------
-// write_sector -
+// write_sector (stage I)
//-------------------------------------------------
-
+// FIXME: SEEK, SEEK_COMPLETE, Drive # change (!)
+// as well as CYL.register + internal CYL.register comparisons
void wd2010_device::write_sector(UINT8 data)
{
+ m_error = 0; // De-assert ERROR + DRQ
+ m_status &= ~(STATUS_DRQ);
+
+ m_status = STATUS_BSY | STATUS_CIP; // Assert BUSY + CIP
+
+ m_status |= STATUS_DRQ; // Assert BDRQ + DRQ (= status bit 3)
+ m_out_bdrq_cb(1);
+
+ // WAIT UNTIL BRDY ASSERTED (-> timer):
+ complete_write_when_buffer_ready_high->adjust(attotime::from_usec(1), data); // 1 usec
}
//-------------------------------------------------
-// scan_id -
+// write_sector (stage II)
//-------------------------------------------------
+void wd2010_device::complete_write_sector(UINT8 data)
+{
+ UINT8 newstatus = STATUS_RDY | STATUS_SC;
-void wd2010_device::scan_id(UINT8 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
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+ else
+ { // --------------------------------------------------------
+ // (*** UNIMPLEMENTED ***) Search for ID field...
+
+ // < Correct ID found >
+
+ // (*** UNIMPLEMENTED ***) : 'ID NOT FOUND' - set bit 4 error register
+ // ........................: => SCAN_ID => RE-SEEK (2-10 INDEX PULSES) / Set ERR bit 0 status register ..
+
+ m_status &= ~(STATUS_SC); // "WRITE_GATE valid when SEEK_COMPLETE = 0" (see Rainbow 100 Addendum!)
+
+ m_out_bcs_cb(1);
+ m_out_wg_cb(1); // (!)
+
+ m_out_bcr_cb(0); // strobe BCR
+ m_out_bcr_cb(1);
+
+ // Assume: DRIVE IS READY / NO WF
+
+ if (!m_in_drdy_cb() || m_in_wf_cb()) // DRDY de-asserted or WF asserted?
+ {
+ m_error = ERROR_AC; // ABORTED_COMMAND
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+ else
+ {
+ // ====> WRITE DATA TO SECTOR <====
+
+ m_out_wg_cb(0); // (!)
+
+ // Assume: (single sector transfer; M = 0)
+
+ } // (INNER IF): No WF and DRIVE IS READY.
+ } // --------------------------------------------------------
+
+ // 'complete_cmd' ON THE FALLING EDGE OF _BUFFER_READY_ ( set by WRITE_SECTOR ) !
+ deassert_write_when_buffer_ready_low->adjust(attotime::from_usec(1), newstatus);
+}
+
+// ******************************************************
+// AUTO SCAN-ID (whenever DRIVE # changes):
+
+ // * does nothing right now *
+// ******************************************************
+void wd2010_device::auto_scan_id(UINT8 data)
{
+ static int last_drive;
+
+ if (DRIVE != last_drive)
+ {
+ printf("\n(WD2010) : UNSUPPORTED DRIVE CHANGE !\n");
+ logerror("\n(WD2010) : UNSUPPORTED DRIVE CHANGE !\n");
+
+ //update_sdh(new_sector_size, new_head, new_cylinder, new_sectornr);
+ }
+ last_drive = DRIVE;
+
+ return; // AUTO-SCAN CURRENTLY DISABLED (see NOTES)
}
+// ******************************************************
+
+// What to do here (just update present_cylinder with CYLINDER)...?
+void wd2010_device::update_sdh(UINT8 new_sector_size, UINT8 new_head, UINT16 new_cylinder, UINT8 new_sectornr)
+{
+ // "Update SDH"
+ /*
+ // Update SECTOR_SIZE, HEAD in SDH with the ID found -
+ m_task_file[TASK_FILE_SDH_REGISTER] = ???
+
+ // ...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 SECTOR_NUMBER with sector nr. found -
+ m_task_file[TASK_FILE_SECTOR_NUMBER] = new_sectornr;
+ */
+
+ m_present_cylinder = CYLINDER;
+ logerror("UPDATE_SDH - m_present_cylinder = %u\n", m_present_cylinder);
+}
//-------------------------------------------------
-// format -
+// 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.
+
+// FIXME: NO ID HANDLING (ID FOUND / NOT FOUND), NO BAD BLOCK; NO CRC
+void wd2010_device::scan_id(UINT8 data)
+{
+ UINT8 newstatus = STATUS_RDY;
+
+ m_out_intrq_cb(CLEAR_LINE);
+ m_error = 0;
+ m_status = STATUS_BSY | STATUS_CIP;
+
+ // Assume DRIVE READY.
+ // < TODO: Search for ANY ID FIELD. >
+
+ // Assume ID FOUND :
+ update_sdh( 32, 0, 0, 1 ); // (NEW:) SECTOR_SIZE, HEAD, CYLINDER, SECTOR_NR
+
+ // NO BAD BLOCK.
+ // NO CRC ERROR.
+
+ complete_cmd(newstatus);
+}
+
+//--------------------------------------------------------
+// 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).
+
+// Routine simulates one single write on each track
+// - just enough to keep formatter programs happy -
+
+// < UNIMPLEMENTED: (IMPLIED) SEEKs, INDEX, CRC and GAPs >
+//--------------------------------------------------------
+// SECTOR_COUNT REG.= 'total # of sectors to be formatted'
+// (raw number; no multiplication) = 16 decimal on RD51
+
+// SECTOR NUMBER REG.= number of bytes - 3 (for GAP 1 + 3)
+// = 40 decimal on DEC RD51 with WUTIL 3.2
+//--------------------------------------------------------
void wd2010_device::format(UINT8 data)
{
+ UINT8 newstatus = STATUS_RDY;
+
+ m_out_intrq_cb(CLEAR_LINE);
+ m_error = 0;
+ m_status = STATUS_BSY | STATUS_CIP;
+
+ m_status |= STATUS_DRQ;
+ m_out_bdrq_cb(1);
+
+ // < WAIT UNTIL BRDY ASSERTED >
+
+ // Datasheet says [DRQ] must go LOW...
+ // ...delayed here _until BRDY goes high_ (=> TIMER EVENT <=):
+
+ // m_out_bdrq_cb(0);
+ // m_status &= ~(STATUS_DRQ);
+
+ auto_scan_id(data); // has drive number changed?
+
+ // TODO: Seek to desired cylinder
+ // Assume : SEEK COMPLETE.
+
+ m_out_bcr_cb(0); // strobe BCR
+ m_out_bcr_cb(1);
+
+ m_out_bcs_cb(1); // activate BCS (!)
+
+ if (!m_in_drdy_cb() || m_in_wf_cb())
+ {
+ m_error = ERROR_AC; // ABORTED_COMMAND
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+
+ // WAIT FOR INDEX
+
+ m_out_wg_cb(1); // Have Index, activate WRITE GATE
+
+ // Check for WRITE FAULT (WF)
+ if (m_in_wf_cb())
+ {
+ m_error = ERROR_AC; // ABORTED_COMMAND
+ complete_cmd(newstatus | STATUS_ERR);
+ return;
+ }
+
+ // UINT8 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);
+
+ // ** DELAY INTRQ UNTIL WRITE IS COMPLETE :
+ complete_write_when_buffer_ready_high->adjust(attotime::from_usec(1), newstatus | STATUS_DRQ); // 1 USECs
+}
+
+
+// *************************************
+// INTERNAL
+// *************************************
+void wd2010_device::buffer_ready(bool state)
+{
+ is_buffer_ready = state;
}
+
+
+void wd2010_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ switch (tid)
+ {
+ case COMMAND_TIMER:
+ cmd_timer->adjust(attotime::never);
+ complete_immediate(param);
+ break;
+
+ case COMPLETE_WRITE_SECTOR: // when BUFFER_READY -> HIGH
+ if (is_buffer_ready)
+ {
+ complete_write_when_buffer_ready_high->adjust(attotime::never);
+ complete_write_sector(param);
+ }
+ else
+ {
+ complete_write_when_buffer_ready_high->reset();
+ complete_write_when_buffer_ready_high->adjust(attotime::from_usec(1), param); // DELAY ANOTHER 1 USEC (!)
+ }
+ break;
+
+ case DE_ASSERT_WRITE: // waiting for BUFFER_READY -> LOW
+ if (!(is_buffer_ready))
+ {
+ deassert_write_when_buffer_ready_low->adjust(attotime::never);
+ complete_immediate(param);
+ }
+ else
+ {
+ deassert_write_when_buffer_ready_low->reset();
+ deassert_write_when_buffer_ready_low->adjust(attotime::from_usec(1), param); // DELAY ANOTHER 1 USEC (!)
+ }
+ break;
+
+ case DE_ASSERT_READ: // when BUFFER_READY -> HIGH
+ if (is_buffer_ready)
+ {
+ deassert_read_when_buffer_ready_high->adjust(attotime::never);
+
+ m_error &= ~ERROR_ID;
+ param &= ~STATUS_ERR;
+
+ m_out_bdrq_cb(0);
+ complete_immediate(param);
+ }
+ else
+ {
+ deassert_read_when_buffer_ready_high->reset();
+ deassert_read_when_buffer_ready_high->adjust(attotime::from_usec(1), param); // DELAY ANOTHER 1 USEC (!)
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+// Called by 'device_timer' -
+void wd2010_device::complete_immediate(UINT8 status)
+{
+ // re-evaluate external signals at end of command
+ status &= ~(STATUS_RDY | STATUS_WF | STATUS_SC); // RDY 0x40 / WF 0x20 / SC 0x10
+ status |= (m_in_drdy_cb() ? 0x40 : 0) | (m_in_wf_cb() ? 0x20 : 0) | (m_in_sc_cb() ? 0x10 : 0);
+
+ if (status & STATUS_DRQ) // if DRQ was set, reset
+ {
+ status &= ~(STATUS_DRQ);
+ m_out_bdrq_cb(0);
+ }
+
+ // Set current status (M_STATUS)
+ m_status = status & (255 - STATUS_DWC); // minus "unused" bit 2 (DWC)
+
+ m_status &= ~(STATUS_BSY | STATUS_CIP); // de-assert BUSY + CIP
+
+ // "IRQ AT END OF COMMAND" when BIT 2 set (DWC 'data was corrected' - unused in this context!)
+ if (!(status & STATUS_DWC)) // interrupt at END OF COMMAND ?
+ m_out_intrq_cb(ASSERT_LINE); // Assert INTRQ (callback).
+
+ m_out_bcs_cb(0); // de-assert BCS (needed)
+ m_out_wg_cb(0); // deactivate WG (required by write / format)
+
+ m_out_bcr_cb(0); // strobe BCR
+ m_out_bcr_cb(1);
+}
+
+void wd2010_device::complete_cmd(UINT8 status)
+{
+ cmd_timer->adjust(attotime::from_msec(1), status);
+}
+