[MESS] DEC Rainbow updates: [Karl-Ludwig Deisenhofer]

- Hard disk R/W support and real-time clock support emulating ClikClok card.
-wd2010: provides IRQ / (B)DRQ signals. Honors DRIVE_READY and WRITE FAULT
 (DRDY / WF) now. Set WF to GND and DRDY to VCC in your driver if signals are
 not serviced.
-ds1315 : Handle chip enable / chip reset / phantom writes to RTC.
This commit is contained in:
arbee 2015-05-02 21:42:25 -04:00
parent b702e6bd3e
commit 0026618a68
9 changed files with 2771 additions and 969 deletions

View File

@ -1,12 +1,23 @@
/********************************************************************* /*****************************************************************************************
ds1315.c ds1315.c
Dallas Semiconductor's Phantom Time Chip DS1315. Dallas Semiconductor's Phantom Time Chip DS1315.
NOTE: writes are decoded, but the host's time will always be returned when asked.
by tim lindner, November 2001. April 2015: chip enable / chip reset / phantom writes by Karl-Ludwig Deisenhofer
*********************************************************************/ November 2001: implementation by Tim Lindner
HOW DOES IT WORK?
READS: pattern recognition (64 bits in correct order). When RTC finally enables
64 bits of data can be read. Chance of accidential pattern recognition is minimal.
WRITES: two different locations (bits 0 and 1) are used to transfer data to the
DS1315. 64 bit with time/date info are transmitted directly after recognition
of the magic 64 bit pattern (see read above).
**************************************************************************************/
#include "ds1315.h" #include "ds1315.h"
#include "coreutil.h" #include "coreutil.h"
@ -47,12 +58,11 @@ void ds1315_device::device_start()
void ds1315_device::device_reset() void ds1315_device::device_reset()
{ {
memset(m_raw_data, 0, sizeof(m_raw_data)); chip_reset();
m_count = 0;
m_mode = DS_SEEK_MATCHING;
} }
/*************************************************************************** /***************************************************************************
LOCAL VARIABLES LOCAL VARIABLES
***************************************************************************/ ***************************************************************************/
@ -75,7 +85,7 @@ static const UINT8 ds1315_pattern[] =
***************************************************************************/ ***************************************************************************/
/*------------------------------------------------- /*-------------------------------------------------
read_0 read_0 (actual data)
-------------------------------------------------*/ -------------------------------------------------*/
READ8_MEMBER( ds1315_device::read_0 ) READ8_MEMBER( ds1315_device::read_0 )
@ -100,7 +110,7 @@ READ8_MEMBER( ds1315_device::read_0 )
/*------------------------------------------------- /*-------------------------------------------------
read_1 read_1 (actual data)
-------------------------------------------------*/ -------------------------------------------------*/
READ8_MEMBER( ds1315_device::read_1 ) READ8_MEMBER( ds1315_device::read_1 )
@ -143,36 +153,13 @@ READ8_MEMBER( ds1315_device::read_data )
} }
/*-------------------------------------------------
write_data
-------------------------------------------------*/
WRITE8_MEMBER( ds1315_device::write_data )
{
if (m_mode == DS_CALENDAR_IO)
{
m_raw_data[m_count++] = data & 0x01;
if (m_count == 64)
{
m_mode = DS_SEEK_MATCHING;
m_count = 0;
input_raw_data();
}
return;
}
m_count = 0;
}
/*------------------------------------------------- /*-------------------------------------------------
fill_raw_data fill_raw_data
-------------------------------------------------*/ -------------------------------------------------*/
void ds1315_device::fill_raw_data() void ds1315_device::fill_raw_data()
{ {
/* This routine will (hopefully) call a standard 'C' library routine to get the current /* This routine calls a standard 'C' library routine to get the current
date and time and then fill in the raw data struct. date and time and then fill in the raw data struct.
*/ */
@ -203,16 +190,86 @@ void ds1315_device::fill_raw_data()
} }
/*-------------------------------------------------
write_data
-------------------------------------------------*/
READ8_MEMBER(ds1315_device::write_data)
{
static int write_count;
if (write_count >= 64)
write_count = 0;
if (m_mode == DS_CALENDAR_IO)
{
m_raw_data[write_count++] = offset & 0x01;
if (write_count == 64)
{
write_count = 0;
m_mode = DS_SEEK_MATCHING;
m_count = 0;
input_raw_data();
}
}
return 0; // ignore
}
/*------------------------------------------------- /*-------------------------------------------------
ds1315_input_raw_data ds1315_input_raw_data
Routine is called when new date and time has
been written to the clock chip. Currently we
ignore setting the date and time in the clock
chip.
-------------------------------------------------*/ -------------------------------------------------*/
void ds1315_device::input_raw_data() void ds1315_device::input_raw_data()
{ {
/* This routine is called when new date and time has been written to the int raw[8], i, j=0;
clock chip. Currently we ignore setting the date and time in the clock raw[0] = raw[1] = raw[2] = raw[3] = raw[4] = raw[5] = raw[6] = raw[7] = 0;
chip. UINT8 flag = 1;
We always return the host's time when asked. for (i = 0; i < 64; i++)
*/ {
j = i / 8;
if ((i % 8) == 0)
flag = 1;
if (m_raw_data[i] & 1)
raw[j] |= flag;
flag <<= 1;
}
raw[0] = bcd_2_dec(raw[0]); // hundreds of seconds
raw[1] = bcd_2_dec(raw[1]); // seconds (often set to zero)
raw[2] = bcd_2_dec(raw[2]); // minute
raw[3] = bcd_2_dec(raw[3]); // hour
raw[4] = bcd_2_dec(raw[4]); // weekday (10 for Friday ?!)
raw[5] = bcd_2_dec(raw[5]); // mday
raw[6] = bcd_2_dec(raw[6]); // month
raw[7] = bcd_2_dec(raw[7]); // year (two digits)
printf("\nDS1315 RTC INPUT (WILL BE IGNORED) mm/dd/yy hh:mm:ss - %02d/%02d/%02d %02d/%02d/%02d",
raw[6], raw[5], raw[7], raw[3], raw[2], raw[1]
);
}
/*-------------------------------------------------
query and reset chip status
-------------------------------------------------*/
bool ds1315_device::chip_enable()
{
return (m_mode == DS_CALENDAR_IO);
}
// Set a defined state (important for pattern detection)
void ds1315_device::chip_reset()
{
memset(m_raw_data, 0, sizeof(m_raw_data));
m_count = 0;
m_mode = DS_SEEK_MATCHING;
} }

View File

@ -35,7 +35,10 @@ public:
DECLARE_READ8_MEMBER(read_0); DECLARE_READ8_MEMBER(read_0);
DECLARE_READ8_MEMBER(read_1); DECLARE_READ8_MEMBER(read_1);
DECLARE_READ8_MEMBER(read_data); DECLARE_READ8_MEMBER(read_data);
DECLARE_WRITE8_MEMBER(write_data); DECLARE_READ8_MEMBER(write_data);
bool chip_enable();
void chip_reset();
protected: protected:
// device-level overrides // device-level overrides
@ -45,12 +48,12 @@ protected:
private: private:
// internal state // internal state
ds1315_mode_t m_mode;
void fill_raw_data(); void fill_raw_data();
void input_raw_data(); void input_raw_data();
int m_count; int m_count;
ds1315_mode_t m_mode;
UINT8 m_raw_data[8*8]; UINT8 m_raw_data[8*8];
}; };

View File

@ -2,24 +2,63 @@
// copyright-holders:Curt Coder // copyright-holders:Curt Coder
/********************************************************************** /**********************************************************************
Western Digital WD2010 Winchester Disk Controller Western Digital WD2010 Winchester Disk Controller
Copyright MESS Team. Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions. Visit http://mamedev.org for licensing and usage restrictions.
Portions (2015) : Karl-Ludwig Deisenhofer
**********************************************************************
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" #include "machine/wd2010.h"
//************************************************************************** //**************************************************************************
// MACROS / CONSTANTS // MACROS / CONSTANTS
//************************************************************************** //**************************************************************************
#define LOG 1 #define LOG 1
// task file // task file
enum enum
{ {
@ -44,7 +83,7 @@ enum
(m_task_file[TASK_FILE_SECTOR_NUMBER]) (m_task_file[TASK_FILE_SECTOR_NUMBER])
#define CYLINDER \ #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 \ #define HEAD \
(m_task_file[TASK_FILE_SDH_REGISTER] & 0x07) (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 \ #define SECTOR_SIZE \
SECTOR_SIZES[(m_task_file[TASK_FILE_SDH_REGISTER] >> 5) & 0x03] SECTOR_SIZES[(m_task_file[TASK_FILE_SDH_REGISTER] >> 5) & 0x03]
// status register // status register
#define STATUS_BSY 0x80 #define STATUS_BSY 0x80
#define STATUS_RDY 0x40 #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) 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__), : device_t(mconfig, WD2010, "Western Digital WD2010", tag, owner, clock, "wd2010", __FILE__),
m_out_intrq_cb(*this), m_out_intrq_cb(*this),
m_out_bdrq_cb(*this), m_out_bdrq_cb(*this),
m_out_bcr_cb(*this), m_out_bcr_cb(*this),
m_in_bcs_cb(*this), m_in_bcs_cb(*this),
m_out_bcs_cb(*this), m_in_brdy_cb(*this),
m_out_dirin_cb(*this), m_out_bcs_cb(*this),
m_out_step_cb(*this), m_out_dirin_cb(*this),
m_out_rwc_cb(*this), m_out_step_cb(*this),
m_in_drdy_cb(*this), m_out_rwc_cb(*this),
m_in_index_cb(*this), m_out_wg_cb(*this),
m_in_wf_cb(*this), m_in_drdy_cb(*this),
m_in_tk000_cb(*this), m_in_index_cb(*this),
m_in_sc_cb(*this), m_in_wf_cb(*this),
m_status(0), m_in_tk000_cb(*this),
m_error(0) 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_bdrq_cb.resolve_safe();
m_out_bcr_cb.resolve_safe(); m_out_bcr_cb.resolve_safe();
m_in_bcs_cb.resolve_safe(0); m_in_bcs_cb.resolve_safe(0);
m_in_brdy_cb.resolve_safe(0);
m_out_bcs_cb.resolve_safe(); m_out_bcs_cb.resolve_safe();
m_out_dirin_cb.resolve_safe(); m_out_dirin_cb.resolve_safe();
m_out_step_cb.resolve_safe(); m_out_step_cb.resolve_safe();
m_out_rwc_cb.resolve_safe(); m_out_rwc_cb.resolve_safe();
m_out_wg_cb.resolve_safe();
m_in_drdy_cb.resolve_safe(0); m_in_drdy_cb.resolve_safe(0);
m_in_index_cb.resolve_safe(0); m_in_index_cb.resolve_safe(0);
m_in_wf_cb.resolve_safe(0); m_in_wf_cb.resolve_safe(0);
m_in_tk000_cb.resolve_safe(0); m_in_tk000_cb.resolve_safe(0);
m_in_sc_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 // device_reset - device-specific reset
@ -157,6 +213,11 @@ void wd2010_device::device_start()
void wd2010_device::device_reset() 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 - // read -
//------------------------------------------------- //-------------------------------------------------
READ8_MEMBER( wd2010_device::read ) READ8_MEMBER(wd2010_device::read)
{ {
UINT8 data = 0; UINT8 data = 0;
switch (offset) switch (offset)
{ {
case TASK_FILE_ERROR: case TASK_FILE_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; data = m_error;
break; break;
case TASK_FILE_STATUS: case TASK_FILE_STATUS:
m_out_intrq_cb(CLEAR_LINE); m_out_intrq_cb(CLEAR_LINE); // "reading the status register clears INTRQ" (-> datasheet)
data = m_status | STATUS_RDY | STATUS_SC; 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; break;
default: default:
data = m_task_file[offset]; 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; break;
} }
@ -192,7 +266,7 @@ READ8_MEMBER( wd2010_device::read )
// write - // write -
//------------------------------------------------- //-------------------------------------------------
WRITE8_MEMBER( wd2010_device::write ) WRITE8_MEMBER(wd2010_device::write)
{ {
m_task_file[offset] = data; m_task_file[offset] = data;
@ -211,23 +285,28 @@ WRITE8_MEMBER( wd2010_device::write )
break; break;
case TASK_FILE_CYLINDER_LOW: 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; break;
case TASK_FILE_CYLINDER_HIGH: 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; break;
case TASK_FILE_SDH_REGISTER: case TASK_FILE_SDH_REGISTER:
if (LOG) if (LOG)
{ {
logerror("%s WD2010 '%s' Head: %u\n", machine().describe_context(), tag(), HEAD); logerror("(WRITE) %s WD2010 '%s' SDH: %u\n", machine().describe_context(), tag(), data);
logerror("%s WD2010 '%s' Drive: %u\n", machine().describe_context(), tag(), DRIVE); logerror("(WRITE) %s WD2010 '%s' Head: %u\n", machine().describe_context(), tag(), HEAD);
logerror("%s WD2010 '%s' Sector Size: %u\n", machine().describe_context(), tag(), SECTOR_SIZE); 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; break;
case TASK_FILE_COMMAND: 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 (data == COMMAND_COMPUTE_CORRECTION)
{ {
if (LOG) logerror("%s WD2010 '%s' COMPUTE CORRECTION\n", machine().describe_context(), tag()); if (LOG) logerror("%s WD2010 '%s' COMPUTE CORRECTION\n", machine().describe_context(), tag());
@ -253,12 +332,12 @@ WRITE8_MEMBER( wd2010_device::write )
break; break;
case COMMAND_READ_SECTOR: 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); read_sector(data);
break; break;
case COMMAND_WRITE_SECTOR: 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); write_sector(data);
break; break;
@ -272,136 +351,644 @@ WRITE8_MEMBER( wd2010_device::write )
format(data); format(data);
break; break;
} }
} }
break; break;
} } // switch
} }
//------------------------------------------------- //-------------------------------------------------
// compute_correction - // compute_correction -
//------------------------------------------------- //-------------------------------------------------
void wd2010_device::compute_correction(UINT8 data) void wd2010_device::compute_correction(UINT8 data)
{ {
UINT8 newstatus = STATUS_RDY | STATUS_SC;
complete_cmd(newstatus);
} }
//------------------------------------------------- //-------------------------------------------------
// set_parameter - // set_parameter -
//------------------------------------------------- //-------------------------------------------------
void wd2010_device::set_parameter(UINT8 data) void wd2010_device::set_parameter(UINT8 data)
{ {
UINT8 newstatus = STATUS_RDY | STATUS_SC;
complete_cmd(newstatus);
} }
//------------------------------------------------- //-------------------------------------------------
// restore - // restore -
//------------------------------------------------- //-------------------------------------------------
void wd2010_device::restore(UINT8 data) void wd2010_device::restore(UINT8 data)
{ {
// reset INTRQ, errors, set BUSY, CIP UINT8 newstatus = STATUS_RDY | STATUS_SC;
m_out_intrq_cb(CLEAR_LINE);
m_out_intrq_cb(CLEAR_LINE); // reset INTRQ, errors, set BUSY, CIP
m_error = 0; m_error = 0;
m_status = STATUS_BSY | STATUS_CIP; m_status = STATUS_BSY | STATUS_CIP;
// reset RWC, set direction=OUT, store step rate m_out_rwc_cb(0); // reset RWC, set direction = OUT
m_out_rwc_cb(0);
m_out_dirin_cb(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
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; int step_pulses = 0;
while (step_pulses < STEP_LIMIT)
while (step_pulses < 2048)
{ {
while (!m_in_sc_cb()) while (!m_in_sc_cb())
{ {
// drive not ready or write fault? if (!m_in_drdy_cb() || m_in_wf_cb()) // drive not ready or write fault?
if (!m_in_drdy_cb() || m_in_wf_cb())
{ {
// pulse BCR, set AC, INTRQ, reset BSY, CIP m_out_bcr_cb(0); // pulse BCR
m_out_bcr_cb(0);
m_out_bcr_cb(1); m_out_bcr_cb(1);
m_error = ERROR_AC;
m_status = (m_in_drdy_cb() << 6) | (m_in_wf_cb() << 5) | STATUS_ERR; m_error = ERROR_AC; // ERROR : ABORTED COMMAND
m_out_intrq_cb(ASSERT_LINE); complete_cmd(newstatus | STATUS_ERR);
return; 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); // pulse BCR
m_out_bcr_cb(0);
m_out_bcr_cb(1); m_out_bcr_cb(1);
m_status &= ~(STATUS_BSY | STATUS_CIP); newstatus &= ~(STATUS_BSY | STATUS_CIP); // prepare new status; (INTRQ later) reset BSY, CIP
m_out_intrq_cb(ASSERT_LINE); complete_cmd(newstatus);
return; 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; // ERROR: track 0 not reached within limit
m_error = ERROR_TK; newstatus = newstatus | STATUS_ERR;
m_status |= STATUS_ERR;
// pulse BCR, set INTRQ, reset BSY, CIP m_out_bcr_cb(0); // pulse BCR
m_out_bcr_cb(0);
m_out_bcr_cb(1); m_out_bcr_cb(1);
m_status &= ~(STATUS_BSY | STATUS_CIP); newstatus &= ~(STATUS_BSY | STATUS_CIP); // prepare new status; (INTRQ later) reset BSY, CIP
m_out_intrq_cb(ASSERT_LINE); complete_cmd(newstatus);
return; return;
} }
// issue a step pulse m_out_step_cb(1); // issue a step pulse
m_out_step_cb(1);
m_out_step_cb(0); m_out_step_cb(0);
step_pulses++; step_pulses++;
} }
}
assert(1);
}
//------------------------------------------------- //-------------------------------------------------
// seek - // seek -
//------------------------------------------------- //-------------------------------------------------
// FIXME : step rate, drive change (!)
// NOT IMPLEMENTED: IMPLIED SEEK ("wait until rising edge of SC signal")
void wd2010_device::seek(UINT8 data) 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 - // read_sector -
//------------------------------------------------- //-------------------------------------------------
// FIXME: multiple sector transfers, ID / CYL / HEAD / SIZE match
// + ERROR HANDLING (...)
void wd2010_device::read_sector(UINT8 data) 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) 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
} }
//-------------------------------------------------
// write_sector (stage II)
//-------------------------------------------------
void wd2010_device::complete_write_sector(UINT8 data)
{
UINT8 newstatus = STATUS_RDY | STATUS_SC;
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);
}
//------------------------------------------------- //-------------------------------------------------
// scan_id - // 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) 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.
// format - // 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) 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);
}

View File

@ -32,6 +32,9 @@
#define MCFG_WD2010_OUT_BCR_CB(_devcb) \ #define MCFG_WD2010_OUT_BCR_CB(_devcb) \
devcb = &wd2010_device::set_out_bcr_callback(*device, DEVCB_##_devcb); devcb = &wd2010_device::set_out_bcr_callback(*device, DEVCB_##_devcb);
#define MCFG_WD2010_IN_BRDY_CB(_devcb) \
devcb = &wd2010_device::set_in_brdy_callback(*device, DEVCB_##_devcb);
#define MCFG_WD2010_IN_BCS_CB(_devcb) \ #define MCFG_WD2010_IN_BCS_CB(_devcb) \
devcb = &wd2010_device::set_in_bcs_callback(*device, DEVCB_##_devcb); devcb = &wd2010_device::set_in_bcs_callback(*device, DEVCB_##_devcb);
@ -47,6 +50,9 @@
#define MCFG_WD2010_OUT_RWC_CB(_devcb) \ #define MCFG_WD2010_OUT_RWC_CB(_devcb) \
devcb = &wd2010_device::set_out_rwc_callback(*device, DEVCB_##_devcb); devcb = &wd2010_device::set_out_rwc_callback(*device, DEVCB_##_devcb);
#define MCFG_WD2010_OUT_WG_CB(_devcb) \
devcb = &wd2010_device::set_out_wg_callback(*device, DEVCB_##_devcb);
#define MCFG_WD2010_IN_DRDY_CB(_devcb) \ #define MCFG_WD2010_IN_DRDY_CB(_devcb) \
devcb = &wd2010_device::set_in_drdy_callback(*device, DEVCB_##_devcb); devcb = &wd2010_device::set_in_drdy_callback(*device, DEVCB_##_devcb);
@ -77,11 +83,13 @@ public:
template<class _Object> static devcb_base &set_out_intrq_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_intrq_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_intrq_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_intrq_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_bdrq_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_bdrq_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_bdrq_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_bdrq_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_bcr_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_bcr_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_bcr_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_bcr_cb.set_callback(object); }
template<class _Object> static devcb_base &set_in_brdy_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_brdy_cb.set_callback(object); }
template<class _Object> static devcb_base &set_in_bcs_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_bcs_cb.set_callback(object); } template<class _Object> static devcb_base &set_in_bcs_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_bcs_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_bcs_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_bcs_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_bcs_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_bcs_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_dirin_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_dirin_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_dirin_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_dirin_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_step_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_step_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_step_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_step_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_rwc_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_rwc_cb.set_callback(object); } template<class _Object> static devcb_base &set_out_rwc_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_rwc_cb.set_callback(object); }
template<class _Object> static devcb_base &set_out_wg_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_out_wg_cb.set_callback(object); }
template<class _Object> static devcb_base &set_in_drdy_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_drdy_cb.set_callback(object); } template<class _Object> static devcb_base &set_in_drdy_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_drdy_cb.set_callback(object); }
template<class _Object> static devcb_base &set_in_index_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_index_cb.set_callback(object); } template<class _Object> static devcb_base &set_in_index_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_index_cb.set_callback(object); }
template<class _Object> static devcb_base &set_in_wf_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_wf_cb.set_callback(object); } template<class _Object> static devcb_base &set_in_wf_callback(device_t &device, _Object object) { return downcast<wd2010_device &>(device).m_in_wf_cb.set_callback(object); }
@ -91,11 +99,15 @@ public:
DECLARE_READ8_MEMBER( read ); DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write ); DECLARE_WRITE8_MEMBER( write );
void buffer_ready(bool state);
protected: protected:
// device-level overrides // device-level overrides
virtual void device_start(); virtual void device_start();
virtual void device_reset(); virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
private: private:
void compute_correction(UINT8 data); void compute_correction(UINT8 data);
void set_parameter(UINT8 data); void set_parameter(UINT8 data);
@ -104,16 +116,20 @@ private:
void read_sector(UINT8 data); void read_sector(UINT8 data);
void write_sector(UINT8 data); void write_sector(UINT8 data);
void scan_id(UINT8 data); void scan_id(UINT8 data);
void update_sdh(UINT8 new_sector_size, UINT8 new_head, UINT16 new_cylinder, UINT8 new_sectornr);
void auto_scan_id(UINT8 data);
void format(UINT8 data); void format(UINT8 data);
devcb_write_line m_out_intrq_cb; devcb_write_line m_out_intrq_cb;
devcb_write_line m_out_bdrq_cb; devcb_write_line m_out_bdrq_cb;
devcb_write_line m_out_bcr_cb; devcb_write_line m_out_bcr_cb;
devcb_read8 m_in_bcs_cb; devcb_read8 m_in_bcs_cb;
devcb_read_line m_in_brdy_cb;
devcb_write8 m_out_bcs_cb; devcb_write8 m_out_bcs_cb;
devcb_write_line m_out_dirin_cb; devcb_write_line m_out_dirin_cb;
devcb_write_line m_out_step_cb; devcb_write_line m_out_step_cb;
devcb_write_line m_out_rwc_cb; devcb_write_line m_out_rwc_cb;
devcb_write_line m_out_wg_cb;
devcb_read_line m_in_drdy_cb; devcb_read_line m_in_drdy_cb;
devcb_read_line m_in_index_cb; devcb_read_line m_in_index_cb;
devcb_read_line m_in_wf_cb; devcb_read_line m_in_wf_cb;
@ -123,8 +139,20 @@ private:
UINT8 m_status; UINT8 m_status;
UINT8 m_error; UINT8 m_error;
UINT8 m_task_file[8]; UINT8 m_task_file[8];
};
emu_timer *cmd_timer;
emu_timer *complete_write_when_buffer_ready_high;
emu_timer *deassert_write_when_buffer_ready_low;
emu_timer *deassert_read_when_buffer_ready_high;
void complete_write_sector(UINT8 status);
void complete_cmd(UINT8 status);
void complete_immediate(UINT8 status);
bool is_buffer_ready;
UINT32 m_present_cylinder; // Present Cylinder Position Register
};
// device type definition // device type definition
extern const device_type WD2010; extern const device_type WD2010;

File diff suppressed because it is too large Load Diff

View File

@ -106,6 +106,11 @@
</text> </text>
</element> </element>
<element name="digit" defstate="0">
<led7seg>
<color red="0.75" green="0.0" blue="0.0" />
</led7seg>
</element>
<view name="Default Layout"> <view name="Default Layout">
<screen index="0"> <screen index="0">
@ -206,6 +211,13 @@
<bezel name="label11" element="l11_wait"> <bezel name="label11" element="l11_wait">
<bounds x="0" y="282" width="15" height="16" /> <bounds x="0" y="282" width="15" height="16" />
</bezel> </bezel>
<bezel name="digit0" element="digit">
<bounds x="0" y="320" width="14" height="20" />
</bezel>
<bezel name="digit1" element="digit">
<bounds x="15" y="320" width="14" height="20" />
</bezel>
</view> </view>
<view name="Screen Only"> <view name="Screen Only">

View File

@ -303,7 +303,7 @@ INPUT_PORTS_START( lk201 )
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D") PORT_CODE(KEYCODE_D)
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E") PORT_CODE(KEYCODE_E)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3") PORT_CODE(KEYCODE_3)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Setup (F3)") PORT_CODE(KEYCODE_F3) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Setup (F3)") PORT_CODE(KEYCODE_PAUSE) // SET UP = Pause on PC
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Print Screen (F2)") PORT_CODE(KEYCODE_F2) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Print Screen (F2)") PORT_CODE(KEYCODE_F2)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
@ -363,7 +363,7 @@ INPUT_PORTS_START( lk201 )
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("L") PORT_CODE(KEYCODE_L)
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O") PORT_CODE(KEYCODE_O)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9") PORT_CODE(KEYCODE_9)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LF (F13)") PORT_CODE(KEYCODE_PRTSCR) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("LF (F13)") PORT_CODE(KEYCODE_F13)
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BS (F12)") PORT_CODE(KEYCODE_F12) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BS (F12)") PORT_CODE(KEYCODE_F12)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
@ -376,7 +376,7 @@ INPUT_PORTS_START( lk201 )
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0") PORT_CODE(KEYCODE_0)
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Delete <X") PORT_CODE(KEYCODE_BACKSPACE) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Delete <X") PORT_CODE(KEYCODE_BACKSPACE)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Additional Options (F14) [Zusaetze]") PORT_CODE(KEYCODE_PAUSE) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Additional Options (F14) [Zusaetze]") PORT_CODE(KEYCODE_PRTSCR)
PORT_START("KBD12") PORT_START("KBD12")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH) PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
@ -623,7 +623,6 @@ void lk201_device::send_port(address_space &space, UINT8 offset, UINT8 data)
if (ports[1] & 0x80) kbd_data = m_kbd15->read(); if (ports[1] & 0x80) kbd_data = m_kbd15->read();
if (ports[2] & 0x1) kbd_data = m_kbd16->read(); if (ports[2] & 0x1) kbd_data = m_kbd16->read();
if (ports[2] & 0x2) kbd_data = m_kbd17->read(); if (ports[2] & 0x2) kbd_data = m_kbd17->read();
#endif
} }
// Check for LED update strobe // Check for LED update strobe
if (((data & 0x80) == 0) && (ports[offset] & 0x80)) if (((data & 0x80) == 0) && (ports[offset] & 0x80))
@ -634,6 +633,8 @@ void lk201_device::send_port(address_space &space, UINT8 offset, UINT8 data)
output_set_value("led_hold" , (led_data & 0x4) == 0); output_set_value("led_hold" , (led_data & 0x4) == 0);
output_set_value("led_lock" , (led_data & 0x8) == 0); output_set_value("led_lock" , (led_data & 0x8) == 0);
} }
#endif
break; break;
} }
} }
@ -755,3 +756,4 @@ WRITE8_MEMBER( lk201_device::spi_w )
// printf("SPI %02x to %x (PC=%x)\n", data, offset, m_maincpu->pc()); // printf("SPI %02x to %x (PC=%x)\n", data, offset, m_maincpu->pc());
} }

View File

@ -6,7 +6,7 @@ Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions. Visit http://mamedev.org for licensing and usage restrictions.
01/05/2009 Initial implementation [Miodrag Milanovic] 01/05/2009 Initial implementation [Miodrag Milanovic]
Portions (2013, 2014) by Karl-Ludwig Deisenhofer. Enhancements (2013 - 2015) by Karl-Ludwig Deisenhofer.
DEC VIDEO : STATE AS OF JULY 2014 DEC VIDEO : STATE AS OF JULY 2014
--------------------------------- ---------------------------------
@ -37,7 +37,7 @@ FIXME: work out the differences and identify common code between VT and Rainbow.
- POSSIBLE IMPROVEMENTS: - POSSIBLE IMPROVEMENTS:
* exact colors for different VR201 monitors ('paper white', green and amber) * exact colors for different VR201 monitors (for white, green and amber)
* ACCURATE VIDEO DELAYS: * ACCURATE VIDEO DELAYS:
Position of the first visible scanline (relative to the vertical reset) depends on Position of the first visible scanline (relative to the vertical reset) depends on
@ -65,7 +65,7 @@ FIXME: work out the differences and identify common code between VT and Rainbow.
PARAMETERS PARAMETERS
***************************************************************************/ ***************************************************************************/
#define VERBOSE 0 #define VERBOSE 1
#define LOG(x) do { if (VERBOSE) logerror x; } while (0) #define LOG(x) do { if (VERBOSE) logerror x; } while (0)
@ -239,30 +239,57 @@ READ8_MEMBER(vt100_video_device::lba7_r)
// Also used by Rainbow-100 ************ // Also used by Rainbow-100 ************
WRITE8_MEMBER(vt100_video_device::dc012_w) WRITE8_MEMBER(vt100_video_device::dc012_w)
{ {
// TODO: writes to 10C/0C should be treated differently (emulation disables the watchdog too often). // Writes to [10C] and [0C] are treated differently
// - see 3.1.3.9.5 DC012 Programming Information (PC-100 spec) // - see 3.1.3.9.5 DC012 Programming Information (PC-100 spec)
if (data == 0) // MHFU is disabled by writing 00 to port 010C.
{
MHFU_FLAG = false;
MHFU_counter = 0;
if (VERBOSE) // MHFU is disabled by writing 00 to port 010C.
// Code recognition is abysmal - sorry for that.
if (data == 0)
{ {
if (MHFU_FLAG == true) UINT8 *rom = machine().root_device().memregion("maincpu")->base();
printf("MHFU *** DISABLED *** %ul \n", offset); if (rom != NULL)
} {
UINT32 PC = space.device().safe_pc();
if ((rom[ PC - 1] == 0xe6) &&
(rom[ PC ] == 0x0c)
)
{
// OUT 0C,al < DO NOTHING >
} }
else else
{ {
//UINT8 magic1= rom[PC - 1];
//printf("\n PC %05x - MHFU MAGIC -1 %02x\n", PC, magic1);
//UINT8 magic2 = rom[PC - 2];
//printf("\n PC %05x - MHFU MAGIC -2 %02x\n", PC, magic2);
//if (VERBOSE)
//if(1 )
if ((rom[PC - 2] == 0x0C) &&
(rom[PC - 1] == 0x01)
)
{
if (MHFU_FLAG == true)
printf("MHFU *** DISABLED *** %05x \n", PC);
MHFU_FLAG = false;
MHFU_counter = 0;
}
}
} // DATA == 0 ONLY ....
}
else
{
//if (VERBOSE)
if (MHFU_FLAG == false)
printf("MHFU ___ENABLED___ %05x \n", space.device().safe_pc());
// RESET // RESET
MHFU_FLAG = true; MHFU_FLAG = true;
MHFU_counter = 0; MHFU_counter = 0;
if (VERBOSE)
{
if (MHFU_FLAG == false)
printf("MHFU ___ENABLED___ %ul \n", offset);
}
} }
if (!(data & 0x08)) if (!(data & 0x08))
@ -610,7 +637,7 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
else else
{ {
y_preset = (m_linedoubler ? 480 : 240) - extra_scan_line; y_preset = (m_linedoubler ? 480 : 240) - extra_scan_line;
i = 0; // (should be always empty) i = 0; // blank line. Might not work with TCS or other charsets (FIXME)
} }
} }
@ -859,17 +886,17 @@ int rainbow_video_device::MHFU(int ASK)
case 1: // "true": RETURN BOOLEAN (MHFU disabled or enabled?) case 1: // "true": RETURN BOOLEAN (MHFU disabled or enabled?)
return MHFU_FLAG; return MHFU_FLAG;
case -1: // -1: increment, return counter value (=> Rainbow.c) case -1: // -1: increment IF ENABLED, return counter value (=> Rainbow.c)
if (MHFU_FLAG == true) if (MHFU_FLAG == true)
MHFU_counter++; MHFU_counter++;
return MHFU_counter; return MHFU_counter;
case -100: // -100 : RESET and ENABLE MHFU counter case -100: // -100 : RESET and ENABLE MHFU counter
if (VERBOSE)
printf("-100 MHFU * reset and ENABLE * \n");
MHFU_counter = 0; MHFU_counter = 0;
if(1) //if (VERBOSE)
printf("-100 MHFU * reset and ENABLE * \n");
if (VERBOSE) if(1) // if (VERBOSE)
{ {
if (MHFU_FLAG == false) if (MHFU_FLAG == false)
printf("-100 MHFU ___ENABLED___\n"); printf("-100 MHFU ___ENABLED___\n");
@ -878,6 +905,18 @@ int rainbow_video_device::MHFU(int ASK)
return -100; return -100;
case -200: // -200 : RESET and DISABLE MHFU
MHFU_counter = 0;
if(1) //if (VERBOSE)
{
if (MHFU_FLAG == true)
printf("MHFU *** DISABLED *** \n");
}
MHFU_FLAG = false;
return -200;
default: default:
assert(1); assert(1);
return -255; return -255;

View File

@ -1,12 +1,12 @@
/********************************************************************** /**********************************************************************
DEC VT Terminal video emulation DEC VT Terminal video emulation
[ DC012 and DC011 emulation ] [ DC012 and DC011 emulation ]
01/05/2009 Initial implementation [Miodrag Milanovic] 01/05/2009 Initial implementation [Miodrag Milanovic]
Copyright MESS Team. Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions. Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************/ **********************************************************************/