upd7810: fix and export timer/event counter output

The timer/event counter outputs (CO0 and CO1) are set differently when
EOM changes and when a timer update occurs. Most EOM values are
cleared on use. The document at [0] is confusing regarding these bits.
An older document [1], easier to understand, was used instead.

[0] NEC Electronics User's Manual, April 1987
[1] NEC uCOM-87AD Family 8-Bit Microcomputers uPD78C1X Users Manual
This commit is contained in:
Ramiro Polla 2014-11-05 17:03:09 +01:00
parent 613ad8165d
commit f26bae07da
2 changed files with 38 additions and 28 deletions

View File

@ -420,6 +420,8 @@ const device_type UPD78C06 = &device_creator<upd78c06_device>;
upd7810_device::upd7810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: cpu_device(mconfig, UPD7810, "uPD7810", tag, owner, clock, "upd7810", __FILE__)
, m_to_func(*this)
, m_co0_func(*this)
, m_co1_func(*this)
, m_txd_func(*this)
, m_rxd_func(*this)
, m_an0_func(*this)
@ -446,6 +448,8 @@ upd7810_device::upd7810_device(const machine_config &mconfig, const char *tag, d
upd7810_device::upd7810_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)
: cpu_device(mconfig, type, name, tag, owner, clock, shortname, source)
, m_to_func(*this)
, m_co0_func(*this)
, m_co1_func(*this)
, m_txd_func(*this)
, m_rxd_func(*this)
, m_an0_func(*this)
@ -860,35 +864,43 @@ void upd7801_device::upd7810_take_irq()
void upd7810_device::upd7810_write_EOM()
{
if (EOM & 0x01) /* output LV0 content ? */
if (EOM & 0x01) /* output LV0 content */
{
switch (EOM & 0x0e)
{
case 0x02: /* toggle CO0 */
CO0 = (CO0 >> 1) | ((CO0 ^ 2) & 2);
CO0 ^= 1;
break;
case 0x04: /* reset CO0 */
CO0 = 0;
EOM &= 0xfb; /* LRE0 is reset t0 0 */
break;
case 0x08: /* set CO0 */
CO0 = 1;
EOM &= 0xf7; /* LRE1 is reset t0 0 */
break;
}
EOM &= 0xfe; /* LO0 is reset t0 0 */
m_co0_func(CO0);
}
if (EOM & 0x10) /* output LV0 content ? */
if (EOM & 0x10) /* output LV1 content */
{
switch (EOM & 0xe0)
{
case 0x20: /* toggle CO1 */
CO1 = (CO1 >> 1) | ((CO1 ^ 2) & 2);
CO1 ^= 1;
break;
case 0x40: /* reset CO1 */
CO1 = 0;
EOM &= 0xbf; /* LRE2 is reset t0 0 */
break;
case 0x80: /* set CO1 */
CO1 = 1;
EOM &= 0x7f; /* LRE3 is reset t0 0 */
break;
}
EOM &= 0xef; /* LO1 is reset t0 0 */
m_co1_func(CO1);
}
}
@ -1319,17 +1331,10 @@ void upd7810_device::handle_timers(int cycles)
((0x20 == (ETMM & 0x30)) && (ETM0 == ECNT)) || /* set CO0 if ECNT == ETM0 or at falling CI input */
((0x30 == (ETMM & 0x30)) && (ETM0 == ECNT || ETM1 == ECNT))) /* latch CO0 if ECNT == ETM0 or ECNT == ETM1 */
{
switch (EOM & 0x0e)
{
case 0x02: /* toggle CO0 */
CO0 = (CO0 >> 1) | ((CO0 ^ 2) & 2);
break;
case 0x04: /* reset CO0 */
CO0 = 0;
break;
case 0x08: /* set CO0 */
CO0 = 1;
break;
if (EOM & 0x02) {
/* toggle CO0 */
CO0 ^= 1;
m_co0_func(CO0);
}
}
/* Conditions When ECNT Causes a CO1 Output Change */
@ -1338,17 +1343,10 @@ void upd7810_device::handle_timers(int cycles)
((0x80 == (ETMM & 0xc0)) && (ETM1 == ECNT)) || /* set CO1 if ECNT == ETM1 or at falling CI input */
((0xc0 == (ETMM & 0xc0)) && (ETM0 == ECNT || ETM1 == ECNT))) /* latch CO1 if ECNT == ETM0 or ECNT == ETM1 */
{
switch (EOM & 0xe0)
{
case 0x20: /* toggle CO1 */
CO1 = (CO1 >> 1) | ((CO1 ^ 2) & 2);
break;
case 0x40: /* reset CO1 */
CO1 = 0;
break;
case 0x80: /* set CO1 */
CO1 = 1;
break;
if (EOM & 0x20) {
/* toggle CO1 */
CO1 ^= 1;
m_co1_func(CO1);
}
}
/* How and When ECNT is Cleared */
@ -1543,6 +1541,8 @@ void upd7810_device::base_device_start()
m_io = &space(AS_IO);
m_to_func.resolve_safe();
m_co0_func.resolve_safe();
m_co1_func.resolve_safe();
m_txd_func.resolve_safe();
m_rxd_func.resolve_safe(0);
m_an0_func.resolve_safe(0);
@ -1677,8 +1677,8 @@ void upd7810_device::device_start()
state_add( UPD7810_TI, "TI", m_ti).formatstr("%3u");
state_add( UPD7810_TO, "TO", m_to).formatstr("%3u");
state_add( UPD7810_CI, "CI", m_ci).formatstr("%3u");
state_add( UPD7810_CO0, "CO0", m_co0).mask(0x01).formatstr("%1X");
state_add( UPD7810_CO1, "CO1", m_co1).mask(0x01).formatstr("%1X");
state_add( UPD7810_CO0, "CO0", m_co0).formatstr("%3u");
state_add( UPD7810_CO1, "CO1", m_co1).formatstr("%3u");
state_add( STATE_GENPC, "GENPC", m_pc.w.l ).formatstr("%04X").noshow();
state_add( STATE_GENPCBASE, "GENPCBASE", m_ppc.w.l ).formatstr("%04X").noshow();

View File

@ -50,6 +50,12 @@ enum
#define MCFG_UPD7810_TO(_devcb) \
upd7810_device::set_to_func(*device, DEVCB_##_devcb);
#define MCFG_UPD7810_CO0(_devcb) \
upd7810_device::set_co0_func(*device, DEVCB_##_devcb);
#define MCFG_UPD7810_CO1(_devcb) \
upd7810_device::set_co1_func(*device, DEVCB_##_devcb);
#define MCFG_UPD7810_TXD(_devcb) \
upd7810_device::set_txd_func(*device, DEVCB_##_devcb);
@ -90,6 +96,8 @@ public:
// static configuration helpers
template<class _Object> static devcb_base &set_to_func(device_t &device, _Object object) { return downcast<upd7810_device &>(device).m_to_func.set_callback(object); }
template<class _Object> static devcb_base &set_co0_func(device_t &device, _Object object) { return downcast<upd7810_device &>(device).m_co0_func.set_callback(object); }
template<class _Object> static devcb_base &set_co1_func(device_t &device, _Object object) { return downcast<upd7810_device &>(device).m_co1_func.set_callback(object); }
template<class _Object> static devcb_base &set_txd_func(device_t &device, _Object object) { return downcast<upd7810_device &>(device).m_txd_func.set_callback(object); }
template<class _Object> static devcb_base &set_rxd_func(device_t &device, _Object object) { return downcast<upd7810_device &>(device).m_rxd_func.set_callback(object); }
template<class _Object> static devcb_base &set_an0_func(device_t &device, _Object object) { return downcast<upd7810_device &>(device).m_an0_func.set_callback(object); }
@ -173,6 +181,8 @@ protected:
void upd7810_handle_timer1(int cycles, int clkdiv);
devcb_write_line m_to_func;
devcb_write_line m_co0_func;
devcb_write_line m_co1_func;
devcb_write_line m_txd_func;
devcb_read_line m_rxd_func;
devcb_read8 m_an0_func;