[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
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 "coreutil.h"
@ -47,12 +58,11 @@ void ds1315_device::device_start()
void ds1315_device::device_reset()
{
memset(m_raw_data, 0, sizeof(m_raw_data));
m_count = 0;
m_mode = DS_SEEK_MATCHING;
chip_reset();
}
/***************************************************************************
LOCAL VARIABLES
***************************************************************************/
@ -75,7 +85,7 @@ static const UINT8 ds1315_pattern[] =
***************************************************************************/
/*-------------------------------------------------
read_0
read_0 (actual data)
-------------------------------------------------*/
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 )
@ -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
-------------------------------------------------*/
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.
*/
@ -181,7 +168,7 @@ void ds1315_device::fill_raw_data()
/* get the current date/time from the core */
machine().current_datetime(systime);
raw[0] = 0; /* tenths and hundreths of seconds are always zero */
raw[1] = dec_2_bcd(systime.local_time.second);
raw[2] = dec_2_bcd(systime.local_time.minute);
@ -203,16 +190,86 @@ void ds1315_device::fill_raw_data()
}
/*-------------------------------------------------
ds1315_input_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
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()
{
/* This 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.
int raw[8], i, j=0;
raw[0] = raw[1] = raw[2] = raw[3] = raw[4] = raw[5] = raw[6] = raw[7] = 0;
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_1);
DECLARE_READ8_MEMBER(read_data);
DECLARE_WRITE8_MEMBER(write_data);
DECLARE_READ8_MEMBER(write_data);
bool chip_enable();
void chip_reset();
protected:
// device-level overrides
@ -45,12 +48,12 @@ protected:
private:
// internal state
ds1315_mode_t m_mode;
void fill_raw_data();
void input_raw_data();
int m_count;
ds1315_mode_t m_mode;
UINT8 m_raw_data[8*8];
};

View File

@ -2,24 +2,63 @@
// 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
**********************************************************************
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
//**************************************************************************
#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
// 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;
while (step_pulses < 2048)
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 -
//-------------------------------------------------
void wd2010_device::seek(UINT8 data)
{
}
// 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
}
//-------------------------------------------------
// 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 -
//-------------------------------------------------
// 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
//-------------------------------------------------
// format -
//-------------------------------------------------
// 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);
}

View File

@ -32,6 +32,9 @@
#define MCFG_WD2010_OUT_BCR_CB(_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) \
devcb = &wd2010_device::set_in_bcs_callback(*device, DEVCB_##_devcb);
@ -47,6 +50,9 @@
#define MCFG_WD2010_OUT_RWC_CB(_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) \
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_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_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_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_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_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_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); }
@ -91,11 +99,15 @@ public:
DECLARE_READ8_MEMBER( read );
DECLARE_WRITE8_MEMBER( write );
void buffer_ready(bool state);
protected:
// device-level overrides
virtual void device_start();
virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
private:
void compute_correction(UINT8 data);
void set_parameter(UINT8 data);
@ -104,16 +116,20 @@ private:
void read_sector(UINT8 data);
void write_sector(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);
devcb_write_line m_out_intrq_cb;
devcb_write_line m_out_bdrq_cb;
devcb_write_line m_out_bcr_cb;
devcb_read8 m_in_bcs_cb;
devcb_read_line m_in_brdy_cb;
devcb_write8 m_out_bcs_cb;
devcb_write_line m_out_dirin_cb;
devcb_write_line m_out_step_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_index_cb;
devcb_read_line m_in_wf_cb;
@ -123,8 +139,20 @@ private:
UINT8 m_status;
UINT8 m_error;
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
extern const device_type WD2010;

File diff suppressed because it is too large Load Diff

View File

@ -106,7 +106,12 @@
</text>
</element>
<element name="digit" defstate="0">
<led7seg>
<color red="0.75" green="0.0" blue="0.0" />
</led7seg>
</element>
<view name="Default Layout">
<screen index="0">
<bounds x="30" y="0" width="640" height="480" />
@ -206,6 +211,13 @@
<bezel name="label11" element="l11_wait">
<bounds x="0" y="282" width="15" height="16" />
</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 name="Screen Only">

View File

@ -230,14 +230,14 @@ const rom_entry *lk201_device::device_rom_region() const
DEC omitted terms like 'Interrupt', 'Break' and 'Data / Talk' on some keyboards,
so Fn numbers are definitely important for end users.
=== CURRENT SPECIAL KEYS ===
[PC-AT] ......=> [DEC]
LEFT CONTROL..=> Control
LEFT ALT .....=> Compose
RIGHT ALT ....=> Help
RIGHT CONTROL => Do
[PC-AT] ......=> [DEC]
LEFT CONTROL..=> Control
LEFT ALT .....=> Compose
RIGHT ALT ....=> Help
RIGHT CONTROL => Do
==============================================================================================
=== (PC - AT ) keys above cursor block ===
* KEYCODE_INSERT * KEYCODE_HOME * KEYCODE_PGUP
@ -249,9 +249,9 @@ const rom_entry *lk201_device::device_rom_region() const
==============================================================================================
=== CURRENT NUM PAD ASSIGNMENTS ===
[PF1] to [PF4] are mapped to NUM LOCK, SLASH etc. (=> 4 keys on top on num pad).
Num pad '+' gives ',' on the DEC.
Num pad '+' gives ',' on the DEC.
',' translates to '.' (=> more or less the layout of model 'LK-201-AG')
Switch between 'full' and 'partial keyboard emulation' with Scroll Lock.
*/
@ -276,7 +276,7 @@ INPUT_PORTS_START( lk201 )
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Compose") PORT_CODE(KEYCODE_LALT)
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Caps Lock") PORT_CODE(KEYCODE_CAPSLOCK)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Ctrl") PORT_CODE(KEYCODE_LCONTROL)
PORT_START("KBD2")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Z") PORT_CODE(KEYCODE_Z)
@ -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( 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( 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( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Print Screen (F2)") PORT_CODE(KEYCODE_F2)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
@ -363,20 +363,20 @@ INPUT_PORTS_START( lk201 )
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( 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( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("BS (F12)") PORT_CODE(KEYCODE_F12)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_START("KBD11")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("/") PORT_CODE(KEYCODE_SLASH)
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME(";") PORT_CODE(KEYCODE_COLON)
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED ) // FIXME - duplicate "Return"
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("P") PORT_CODE(KEYCODE_P)
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( 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_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("\\") PORT_CODE(KEYCODE_BACKSLASH)
@ -394,7 +394,7 @@ INPUT_PORTS_START( lk201 )
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("'") PORT_CODE(KEYCODE_QUOTE)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("[") PORT_CODE(KEYCODE_OPENBRACE)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Previous [^]") PORT_CODE(KEYCODE_END)
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Do (F16) [Ausfuehren]") PORT_CODE(KEYCODE_RCONTROL)
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Do (F16) [Ausfuehren]") PORT_CODE(KEYCODE_RCONTROL)
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("-") PORT_CODE(KEYCODE_MINUS)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Insert Here") PORT_CODE(KEYCODE_HOME)
@ -433,7 +433,7 @@ INPUT_PORTS_START( lk201 )
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Num ,") PORT_CODE(KEYCODE_PLUS_PAD)
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) // PORT_NAME("Num -") = duplicate...see KBD13
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("PF4") PORT_CODE(KEYCODE_MINUS_PAD)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F20")
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F20")
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED )
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F19")
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )
@ -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[2] & 0x1) kbd_data = m_kbd16->read();
if (ports[2] & 0x2) kbd_data = m_kbd17->read();
#endif
}
// Check for LED update strobe
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_lock" , (led_data & 0x8) == 0);
}
#endif
break;
}
}
@ -755,3 +756,4 @@ WRITE8_MEMBER( lk201_device::spi_w )
// 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.
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
---------------------------------
@ -21,27 +21,27 @@ FIXME: work out the differences and identify common code between VT and Rainbow.
- REQUIRED TODOS / TESTS :
* do line and character attributes (plus combinations) match real hardware?
* how does the AVO fit in?
- SCROLLING REGIONS / SPLIT SCREEN SCROLLING UNTESTED (if you open > 1 file with the VAX editor EDT)
See VT100 Technical Manual: 4.7.4 Address Shuffling to 4.7.9 Split Screen Smooth Scrolling.
More on scrolling regions: Rainbow 100 B technical documentation (QV069-GZ) April 1985 page 22
- NEW - INTERLACED MODE (Rainbow only):
Vertical resolution increases from 240 to 480, while the refresh rate halves (flickers on CRTs).
To accomplish this, the display controller repeats even lines in odd scans.
VTVIDEO activates line doubling in 24 line, interlaced mode only.
Although the DC12 has the ability to display 48 lines, most units are low on screen RAM and
won't even show 80 x 48. -> REASON: (83 x 48 = 3984 Byte) > (screen RAM) minus 'scratch area'
won't even show 80 x 48. -> REASON: (83 x 48 = 3984 Byte) > (screen RAM) minus 'scratch area'
On a VT-180, BIOS scratch requires up to 700 bytes used for SETUP, flags, SILO, keyboard.
- 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:
Position of the first visible scanline (relative to the vertical reset) depends on
content of fill bytes at the beginning of screen RAM.
Position of the first visible scanline (relative to the vertical reset) depends on
content of fill bytes at the beginning of screen RAM.
Six invisible, linked lines are initially provided (at location $EE000+ on a Rainbow).
Real-world DC hardware parses the (circular) chain until interrupted by blanking.
@ -65,7 +65,7 @@ FIXME: work out the differences and identify common code between VT and Rainbow.
PARAMETERS
***************************************************************************/
#define VERBOSE 0
#define VERBOSE 1
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
@ -185,10 +185,10 @@ void rainbow_video_device::device_reset()
m_basic_attribute = 0;
m_columns = 80;
m_frequency = 60;
m_frequency = 60;
m_interlaced = 1;
m_interlaced = 1;
m_fill_lines = 2; // for 60Hz (not in use any longer -> detected)
recompute_parameters();
}
@ -210,8 +210,8 @@ void vt100_video_device::recompute_parameters()
int vert_pix_total = ((m_linedoubler == false) ? m_height : m_height_MAX) * 10;
if (m_columns == 132)
horiz_pix_total = m_columns * 9; // display 1 less filler pixel in 132 char. mode
if (m_columns == 132)
horiz_pix_total = m_columns * 9; // display 1 less filler pixel in 132 char. mode
else
horiz_pix_total = m_columns * 10; // normal 80 character mode.
@ -239,30 +239,57 @@ READ8_MEMBER(vt100_video_device::lba7_r)
// Also used by Rainbow-100 ************
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)
if (data == 0) // MHFU is disabled by writing 00 to port 010C.
{
MHFU_FLAG = false;
MHFU_counter = 0;
if (VERBOSE)
{
if (MHFU_FLAG == true)
printf("MHFU *** DISABLED *** %ul \n", offset);
}
}
// MHFU is disabled by writing 00 to port 010C.
// Code recognition is abysmal - sorry for that.
if (data == 0)
{
UINT8 *rom = machine().root_device().memregion("maincpu")->base();
if (rom != NULL)
{
UINT32 PC = space.device().safe_pc();
if ((rom[ PC - 1] == 0xe6) &&
(rom[ PC ] == 0x0c)
)
{
// OUT 0C,al < DO NOTHING >
}
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
{
// RESET
MHFU_FLAG = true;
MHFU_counter = 0;
//if (VERBOSE)
if (MHFU_FLAG == false)
printf("MHFU ___ENABLED___ %05x \n", space.device().safe_pc());
if (VERBOSE)
{
if (MHFU_FLAG == false)
printf("MHFU ___ENABLED___ %ul \n", offset);
}
// RESET
MHFU_FLAG = true;
MHFU_counter = 0;
}
if (!(data & 0x08))
@ -284,57 +311,57 @@ WRITE8_MEMBER(vt100_video_device::dc012_w)
{
switch (data & 0x0f)
{
case 0x08:
// toggle blink flip flop
m_blink_flip_flop = !(m_blink_flip_flop) ? 1 : 0;
break;
case 0x09:
// clear vertical frequency interrupt;
m_write_clear_video_interrupt(0);
break;
case 0x0a:
// set reverse field on
m_reverse_field = 1;
break;
case 0x0b:
// set reverse field off
m_reverse_field = 0;
break;
case 0x08:
// toggle blink flip flop
m_blink_flip_flop = !(m_blink_flip_flop) ? 1 : 0;
break;
case 0x09:
// clear vertical frequency interrupt;
m_write_clear_video_interrupt(0);
break;
case 0x0a:
// set reverse field on
m_reverse_field = 1;
break;
case 0x0b:
// set reverse field off
m_reverse_field = 0;
break;
// Writing a 11XX bit combination clears the blink-flip flop (valid for 0x0C - 0x0F):
case 0x0c:
// set basic attribute to underline / blink flip-flop off
m_blink_flip_flop = 0;
m_basic_attribute = 0; // (VT-100 without AVO): reverse video is interpreted as underline (basic_attribute 0)
break;
case 0x0c:
// set basic attribute to underline / blink flip-flop off
m_blink_flip_flop = 0;
m_basic_attribute = 0; // (VT-100 without AVO): reverse video is interpreted as underline (basic_attribute 0)
break;
case 0x0d:
// (DEC Rainbow 100 DEFAULT) : reverse video with 24 lines / blink flip-flop off
m_blink_flip_flop = 0;
m_basic_attribute = 1; // (VT-100 without AVO): reverse video is interpreted as reverse (basic_attribute 1)
case 0x0d:
// (DEC Rainbow 100 DEFAULT) : reverse video with 24 lines / blink flip-flop off
m_blink_flip_flop = 0;
m_basic_attribute = 1; // (VT-100 without AVO): reverse video is interpreted as reverse (basic_attribute 1)
if (m_height_MAX == 25) break; // Abort on VT-100 for now.
if (m_height_MAX == 25) break; // Abort on VT-100 for now.
m_height = 24; // (DEC Rainbow 100) : 24 line display
recompute_parameters();
break;
m_height = 24; // (DEC Rainbow 100) : 24 line display
recompute_parameters();
break;
case 0x0e:
case 0x0e:
m_blink_flip_flop = 0; // 'unsupported' DC012 command. Turns blink flip-flop off (11XX).
break;
break;
case 0x0f:
// (DEC Rainbow 100): reverse video with 48 lines / blink flip-flop off
m_blink_flip_flop = 0;
m_basic_attribute = 1;
case 0x0f:
// (DEC Rainbow 100): reverse video with 48 lines / blink flip-flop off
m_blink_flip_flop = 0;
m_basic_attribute = 1;
// 0x0f = 'reserved' on VT 100
// Abort on VT-100 for now.
if (m_height_MAX == 25) break;
// 0x0f = 'reserved' on VT 100
// Abort on VT-100 for now.
if (m_height_MAX == 25) break;
m_height = 48; // (DEC Rainbow 100) : 48 line display
recompute_parameters();
break;
m_height = 48; // (DEC Rainbow 100) : 48 line display
recompute_parameters();
break;
}
}
}
@ -342,15 +369,15 @@ WRITE8_MEMBER(vt100_video_device::dc012_w)
// Writing to DC011 resets internal counters (& disturbs display) on real hardware.
WRITE8_MEMBER(vt100_video_device::dc011_w)
{
if (!BIT(data, 5))
if (!BIT(data, 5))
{
m_interlaced = 1;
m_interlaced = 1;
if (!BIT(data, 4))
m_columns = 80;
else
m_columns = 132;
}
}
else
{
m_interlaced = 0;
@ -358,16 +385,16 @@ WRITE8_MEMBER(vt100_video_device::dc011_w)
if (!BIT(data, 4))
{
m_frequency = 60;
m_fill_lines = 2;
m_fill_lines = 2;
}
else
{
m_frequency = 50;
m_fill_lines = 5;
m_fill_lines = 5;
}
}
recompute_parameters();
recompute_parameters();
}
WRITE8_MEMBER(vt100_video_device::brightness_w)
@ -388,12 +415,12 @@ void vt100_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x, i
switch (display_type)
{
case 0: // bottom half, double height
j = (i >> 1) + 5; break;
j = (i >> 1) + 5; break;
case 1: // top half, double height
j = (i >> 1); break;
j = (i >> 1); break;
case 2: // double width
case 3: // normal
j = i; break;
j = i; break;
default: j = 0; break;
}
// modify line since that is how it is stored in rom
@ -415,27 +442,27 @@ void vt100_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x, i
bit = BIT((line << b), 7);
if (double_width)
{
bitmap.pix16(y * 10 + i, x * 20 + b * 2) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + b * 2 + 1) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + b * 2) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + b * 2 + 1) = bit ^ invert;
}
else
{
bitmap.pix16(y * 10 + i, x * 10 + b) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 10 + b) = (bit | prevbit) ^ invert;
}
}
prevbit = bit;
// char interleave is filled with last bit
if (double_width)
{
bitmap.pix16(y * 10 + i, x * 20 + 16) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 17) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 18) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 19) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 16) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 17) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 18) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 20 + 19) = bit ^ invert;
}
else
{
bitmap.pix16(y * 10 + i, x * 10 + 8) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 10 + 9) = bit ^ invert;
bitmap.pix16(y * 10 + i, x * 10 + 8) = (bit | prevbit) ^ invert;
bitmap.pix16(y * 10 + i, x * 10 + 9) = bit ^ invert;
}
}
}
@ -474,7 +501,7 @@ void vt100_video_device::video_update(bitmap_ind16 &bitmap, const rectangle &cli
// if A12 is 1 then it is 0x2000 block, if 0 then 0x4000 (AVO)
if (addr & 0x1000) addr &= 0xfff; else addr |= 0x2000;
scroll_region = (temp >> 15) & 1;
display_type = (temp >> 13) & 3;
display_type = (temp >> 13) & 3;
if (line >= m_fill_lines)
{
ypos++;
@ -533,7 +560,7 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
UINT16 y_preset;
UINT16 CHARPOS_y_preset = y << 3; // CHARPOS_y_preset = y * 10;
UINT16 CHARPOS_y_preset = y << 3; // CHARPOS_y_preset = y * 10;
CHARPOS_y_preset += y;
CHARPOS_y_preset += y;
@ -545,10 +572,10 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
int back_intensity, back_default_intensity;
int invert = (display_type & 8) ? 1 : 0; // REVERSE
int bold = (display_type & 16) ? 0 : 1; // BIT 4
int bold = (display_type & 16) ? 0 : 1; // BIT 4
int blink = (display_type & 32) ? 0 : 1; // BIT 5
int underline = (display_type & 64) ? 0 : 1; // BIT 6
bool blank = (display_type & 128) ? true : false; // BIT 7
int underline = (display_type & 64) ? 0 : 1; // BIT 6
bool blank = (display_type & 128) ? true : false; // BIT 7
display_type = display_type & 3;
@ -580,8 +607,8 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
// BG: DEFAULT for entire character (underline overrides this for 1 line) -
back_default_intensity = back_intensity;
bool double_width = (display_type != 3) ? true : false; // all except normal: double width
bool double_height = (display_type & 1) ? false : true; // 0,2 = double height
bool double_width = (display_type != 3) ? true : false; // all except normal: double width
bool double_height = (display_type & 1) ? false : true; // 0,2 = double height
int smooth_offset = 0;
if (scroll_region != 0)
@ -610,24 +637,24 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
else
{
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)
}
}
switch (display_type)
{
case 0: // bottom half of 'double height, double width' char.
j = (i >> 1) + 5;
break;
j = (i >> 1) + 5;
break;
case 2: // top half of 'double height, double width' char.
j = (i >> 1);
break;
j = (i >> 1);
break;
default: // 1: double width
// 3: normal
j = i;
break;
// 3: normal
j = i;
break;
}
// modify line since that is how it is stored in rom
@ -641,13 +668,13 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
{
if (i == 8)
{
if (invert == 0)
line = 0xff; // CASE 5 A)
else
if (invert == 0)
line = 0xff; // CASE 5 A)
else
{
line = 0x00; // CASE 5 B)
back_intensity = 0; // OVERRIDE: BLACK BACKGROUND
}
back_intensity = 0; // OVERRIDE: BLACK BACKGROUND
}
}
}
@ -659,12 +686,12 @@ void rainbow_video_device::display_char(bitmap_ind16 &bitmap, UINT8 code, int x,
}
else
{
bit = BIT((line << b), 7);
bit = BIT((line << b), 7);
if (bit > 0)
bit = fg_intensity;
else
bit = back_intensity;
if (bit > 0)
bit = fg_intensity;
else
bit = back_intensity;
}
// Double, 'double_height + double_width', then normal.
@ -729,7 +756,7 @@ void rainbow_video_device::video_update(bitmap_ind16 &bitmap, const rectangle &c
// Skip fill (0xFF) lines and put result in ADDR.
for (int xp = 1; xp <= 6; xp += 1) // beware of circular references
{
// Fetch LINE ATTRIBUTE before it is gone
// Fetch LINE ATTRIBUTE before it is gone
attr_addr = 0x1000 | ((addr + 1) & 0x0fff);
temp = m_read_ram(addr + 2) * 256 + m_read_ram(addr + 1);
@ -749,9 +776,9 @@ void rainbow_video_device::video_update(bitmap_ind16 &bitmap, const rectangle &c
code = m_read_ram(addr + xpos);
if (code == 0x00) // TODO: investigate side effect on regular zero character!
display_type |= 0x80; // DEFAULT: filler chars (till end of line) and empty lines (00) will be blanked
display_type |= 0x80; // DEFAULT: filler chars (till end of line) and empty lines (00) will be blanked
else
display_type &= 0x7f; // else activate display.
display_type &= 0x7f; // else activate display.
if (code == 0xff) // end of line, fill empty till end of line
{
@ -759,7 +786,7 @@ void rainbow_video_device::video_update(bitmap_ind16 &bitmap, const rectangle &c
for (x = xpos; x < ((display_type != 3) ? (m_columns / 2) : m_columns); x++)
{
display_char(bitmap, code, x, ypos, scroll_region, display_type | 0x80);
}
}
// LINE ATTRIBUTE - valid for all chars on next line ** DO NOT SHUFFLE **
attr_addr = 0x1000 | ((addr + xpos + 1) & 0x0fff);
@ -770,7 +797,7 @@ void rainbow_video_device::video_update(bitmap_ind16 &bitmap, const rectangle &c
temp = m_read_ram(attr_addr);
scroll_region = (temp)& 1;
display_type = (temp >> 1) & 3;
display_type = (temp >> 1) & 3;
ypos++; // Y + 1 in non-interlaced mode
if (m_linedoubler)
@ -821,24 +848,24 @@ void rainbow_video_device::palette_select(int choice)
{
switch (choice)
{
default:
case 0x01:
default:
case 0x01:
m_palette->set_pen_color(1, 0xff - 100, 0xff - 100, 0xff - 100); // WHITE (dim)
m_palette->set_pen_color(2, 0xff - 50, 0xff - 50, 0xff - 50); // WHITE NORMAL
m_palette->set_pen_color(3, 0xff, 0xff, 0xff); // WHITE (brighter)
break;
m_palette->set_pen_color(3, 0xff, 0xff, 0xff); // WHITE (brighter)
break;
case 0x02:
case 0x02:
m_palette->set_pen_color(1, 35, 200 - 55, 75); // GREEN (dim)
m_palette->set_pen_color(2, 35 + 55, 200, 75 + 55); // GREEN (NORMAL)
m_palette->set_pen_color(3, 35 + 110, 200 + 55, 75 + 110); // GREEN (brighter)
break;
break;
case 0x03:
m_palette->set_pen_color(1, 213 - 47, 146 - 47, 82 - 47); // AMBER (dim)
case 0x03:
m_palette->set_pen_color(1, 213 - 47, 146 - 47, 82 - 47); // AMBER (dim)
m_palette->set_pen_color(2, 213, 146, 82); // AMBER (NORMAL)
m_palette->set_pen_color(3, 255, 193, 129); // AMBER (brighter)
break;
break;
}
}
@ -856,31 +883,43 @@ int rainbow_video_device::MHFU(int ASK)
{
switch (ASK)
{
case 1: // "true": RETURN BOOLEAN (MHFU disabled or enabled?)
return MHFU_FLAG;
case 1: // "true": RETURN BOOLEAN (MHFU disabled or enabled?)
return MHFU_FLAG;
case -1: // -1: increment, return counter value (=> Rainbow.c)
if (MHFU_FLAG == true)
MHFU_counter++;
return MHFU_counter;
case -1: // -1: increment IF ENABLED, return counter value (=> Rainbow.c)
if (MHFU_FLAG == true)
MHFU_counter++;
return MHFU_counter;
case -100: // -100 : RESET and ENABLE MHFU counter
if (VERBOSE)
case -100: // -100 : RESET and ENABLE MHFU counter
MHFU_counter = 0;
if(1) //if (VERBOSE)
printf("-100 MHFU * reset and ENABLE * \n");
MHFU_counter = 0;
if (VERBOSE)
if(1) // if (VERBOSE)
{
if (MHFU_FLAG == false)
printf("-100 MHFU ___ENABLED___\n");
}
MHFU_FLAG = true;
MHFU_FLAG = true;
return -100;
return -100;
case -200: // -200 : RESET and DISABLE MHFU
MHFU_counter = 0;
default:
assert(1);
return -255;
if(1) //if (VERBOSE)
{
if (MHFU_FLAG == true)
printf("MHFU *** DISABLED *** \n");
}
MHFU_FLAG = false;
return -200;
default:
assert(1);
return -255;
} // switch
}

View File

@ -1,12 +1,12 @@
/**********************************************************************
DEC VT Terminal video emulation
[ DC012 and DC011 emulation ]
DEC VT Terminal video emulation
[ DC012 and DC011 emulation ]
01/05/2009 Initial implementation [Miodrag Milanovic]
01/05/2009 Initial implementation [Miodrag Milanovic]
Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions.
Copyright MESS Team.
Visit http://mamedev.org for licensing and usage restrictions.
**********************************************************************/
@ -16,7 +16,7 @@
#include "emu.h"
class vt100_video_device : public device_t,
public device_video_interface
public device_video_interface
{
public:
vt100_video_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);