ns32202: new device (wip)

This commit is contained in:
Patrick Mackinlay 2020-10-10 13:28:41 +07:00
parent 29fc166c54
commit e67a06058f
4 changed files with 847 additions and 0 deletions

View File

@ -4568,3 +4568,14 @@ if (MACHINES["NS32081"]~=null) then
MAME_DIR .. "src/devices/machine/ns32081.h",
}
end
---------------------------------------------------
--
--@src/devices/machine/ns32202.h,MACHINES["NS32202"] = true
---------------------------------------------------
if (MACHINES["NS32202"]~=null) then
files {
MAME_DIR .. "src/devices/machine/ns32202.cpp",
MAME_DIR .. "src/devices/machine/ns32202.h",
}
end

View File

@ -781,6 +781,7 @@ MACHINES["WTL3132"] = true
MACHINES["CXD1185"] = true
MACHINES["BL_HANDHELDS_MENUCONTROL"] = true
MACHINES["NS32081"] = true
MACHINES["NS32202"] = true
--------------------------------------------------
-- specify available bus cores

View File

@ -0,0 +1,698 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
/*
* National Semiconductor NS32202 Interrupt Control Unit (ICU).
*
* Sources:
*
* http://bitsavers.org/components/national/_dataBooks/1989_National_Microprocessor_Databook_32000_NSC800.pdf
*
* TODO
* - timer/counter
*/
#include "emu.h"
#include "ns32202.h"
#define LOG_GENERAL (1U << 0)
#define LOG_STATE (1U << 1)
#define LOG_REGW (1U << 2)
#define LOG_REGR (1U << 3)
//#define VERBOSE (LOG_GENERAL|LOG_STATE|LOG_REGW|LOG_REGR)
#include "logmacro.h"
DEFINE_DEVICE_TYPE(NS32202, ns32202_device, "ns32202", "NS32202 Interrupt Control Unit")
enum mctl_mask : u8
{
MCTL_T16N8 = 0x01, // data bus mode
MCTL_NTAR = 0x02, // not auto-rotate mode
MCTL_FRZ = 0x08, // freeze interrupt pending
MCTL_CLKM = 0x10, // clock mode (square wave/pulsed)
MCTL_COUTM = 0x20, // cout mode (square wave/pulsed)
MCTL_COUTD = 0x40, // cout/scin input/output
MCTL_CFRZ = 0x80, // freeze counter readings
};
enum cctl_mask : u8
{
CCTL_CDCRL = 0x01, // decrement l-counter
CCTL_CDCRH = 0x02, // decrement h-counter
CCTL_CRUNL = 0x04, // l-counter running
CCTL_CRUNH = 0x08, // h-counter running
CCTL_COUT0 = 0x10, // zero detect l-counter
CCTL_COUT1 = 0x20, // zero detect h-counter
CCTL_CFNPS = 0x40, // clock not prescaled
CCTL_CCON = 0x80, // counters concatenated
};
enum cictl_mask : u8
{
CICTL_WENL = 0x01, // l-counter write enable
CICTL_CIEL = 0x02, // l-counter interrupt enable
CICTL_CIRL = 0x04, // l-counter interrupt request
CICTL_CERL = 0x08, // l-counter error flag
CICTL_WENH = 0x10, // h-counter write enable
CICTL_CIEH = 0x20, // h-counter interrupt enable
CICTL_CIRH = 0x40, // h-counter interrupt request
CICTL_CERH = 0x80, // h-counter error flag
};
ns32202_device::ns32202_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
: device_t(mconfig, NS32202, tag, owner, clock)
, m_out_int(*this)
, m_out_cout(*this)
, m_out_port(*this)
, m_line_state(0xffff)
, m_out_int_state(false)
, m_out_cout_state(false)
{
}
void ns32202_device::device_start()
{
m_out_int.resolve_safe();
m_out_cout.resolve_safe();
m_out_port.resolve_safe();
save_item(NAME(m_hvct));
save_item(NAME(m_eltg));
save_item(NAME(m_tpl));
save_item(NAME(m_ipnd));
save_item(NAME(m_isrv));
save_item(NAME(m_imsk));
save_item(NAME(m_csrc));
save_item(NAME(m_fprt));
save_item(NAME(m_mctl));
save_item(NAME(m_ocasn));
save_item(NAME(m_ciptr));
save_item(NAME(m_pdat));
save_item(NAME(m_ips));
save_item(NAME(m_pdir));
save_item(NAME(m_cctl));
save_item(NAME(m_cictl));
save_item(NAME(m_csv));
save_item(NAME(m_ccv));
save_item(NAME(m_isrv_count));
save_item(NAME(m_line_state));
save_item(NAME(m_out_int_state));
save_item(NAME(m_out_cout_state));
m_interrupt = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ns32202_device::interrupt), this));
m_counter[0] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ns32202_device::counter<0>), this));
m_counter[1] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ns32202_device::counter<1>), this));
}
void ns32202_device::device_reset()
{
m_eltg = 0xffff;
m_tpl = 0;
m_ipnd = 0;
m_isrv = 0;
m_imsk = 0xffff;
m_csrc = 0;
m_fprt = 0x0001;
m_mctl = MCTL_COUTD;
m_ocasn = 0;
m_ciptr = 0xff;
m_ips = 0xff;
m_pdir = 0xff;
m_cictl = 0;
}
void ns32202_device::set_int(bool int_state)
{
if (int_state != m_out_int_state)
{
LOGMASKED(LOG_STATE, "int %s\n", int_state ? "asserted" : "cleared");
m_out_int_state = int_state;
m_out_int(!m_out_int_state);
}
}
void ns32202_device::set_cout(bool cout_state)
{
if (cout_state != m_out_cout_state)
{
LOGMASKED(LOG_STATE, "cout %s\n", cout_state ? "asserted" : "cleared");
m_out_cout_state = cout_state;
m_out_cout(!m_out_cout_state);
}
}
template <unsigned ST1> void ns32202_device::map(address_map &map)
{
map(0x00, 0x00).r(&ns32202_device::hvct_r<ST1, true>, "ns32202_device::hvct_r");
map(0x01, 0x01).rw(&ns32202_device::hvct_r<ST1, false>, "ns32202_device::svct_r", FUNC(ns32202_device::svct_w));
map(0x02, 0x02).rw(FUNC(ns32202_device::eltgl_r), FUNC(ns32202_device::eltgl_w));
map(0x03, 0x03).rw(FUNC(ns32202_device::eltgh_r), FUNC(ns32202_device::eltgh_w));
map(0x04, 0x04).rw(FUNC(ns32202_device::tpll_r), FUNC(ns32202_device::tpll_w));
map(0x05, 0x05).rw(FUNC(ns32202_device::tplh_r), FUNC(ns32202_device::tplh_w));
map(0x06, 0x06).rw(FUNC(ns32202_device::ipndl_r), FUNC(ns32202_device::ipndl_w));
map(0x07, 0x07).rw(FUNC(ns32202_device::ipndh_r), FUNC(ns32202_device::ipndh_w));
map(0x08, 0x08).rw(FUNC(ns32202_device::isrvl_r), FUNC(ns32202_device::isrvl_w));
map(0x09, 0x09).rw(FUNC(ns32202_device::isrvh_r), FUNC(ns32202_device::isrvh_w));
map(0x0a, 0x0a).rw(FUNC(ns32202_device::imskl_r), FUNC(ns32202_device::imskl_w));
map(0x0b, 0x0b).rw(FUNC(ns32202_device::imskh_r), FUNC(ns32202_device::imskh_w));
map(0x0c, 0x0c).rw(FUNC(ns32202_device::csrcl_r), FUNC(ns32202_device::csrcl_w));
map(0x0d, 0x0d).rw(FUNC(ns32202_device::csrch_r), FUNC(ns32202_device::csrch_w));
map(0x0e, 0x0e).rw(FUNC(ns32202_device::fprtl_r), FUNC(ns32202_device::fprtl_w));
map(0x0f, 0x0f).rw(FUNC(ns32202_device::fprth_r), FUNC(ns32202_device::fprth_w));
map(0x10, 0x10).rw(FUNC(ns32202_device::mctl_r), FUNC(ns32202_device::mctl_w));
map(0x11, 0x11).rw(FUNC(ns32202_device::ocasn_r), FUNC(ns32202_device::ocasn_w));
map(0x12, 0x12).rw(FUNC(ns32202_device::ciptr_r), FUNC(ns32202_device::ciptr_w));
map(0x13, 0x13).rw(FUNC(ns32202_device::pdat_r), FUNC(ns32202_device::pdat_w));
map(0x14, 0x14).rw(FUNC(ns32202_device::ips_r), FUNC(ns32202_device::ips_w));
map(0x15, 0x15).rw(FUNC(ns32202_device::pdir_r), FUNC(ns32202_device::pdir_w));
map(0x16, 0x16).rw(FUNC(ns32202_device::cctl_r), FUNC(ns32202_device::cctl_w));
map(0x17, 0x17).rw(FUNC(ns32202_device::cictl_r), FUNC(ns32202_device::cictl_w));
map(0x18, 0x18).rw(FUNC(ns32202_device::csvl_r<0>), FUNC(ns32202_device::csvl_w<0>));
map(0x19, 0x19).rw(FUNC(ns32202_device::csvh_r<0>), FUNC(ns32202_device::csvh_w<0>));
map(0x1a, 0x1a).rw(FUNC(ns32202_device::csvl_r<1>), FUNC(ns32202_device::csvl_w<1>));
map(0x1b, 0x1b).rw(FUNC(ns32202_device::csvh_r<1>), FUNC(ns32202_device::csvh_w<1>));
map(0x1c, 0x1c).rw(FUNC(ns32202_device::lccvl_r), FUNC(ns32202_device::lccvl_w));
map(0x1d, 0x1d).rw(FUNC(ns32202_device::lccvh_r), FUNC(ns32202_device::lccvh_w));
map(0x1e, 0x1e).rw(FUNC(ns32202_device::hccvl_r), FUNC(ns32202_device::hccvl_w));
map(0x1f, 0x1f).rw(FUNC(ns32202_device::hccvh_r), FUNC(ns32202_device::hccvh_w));
}
template void ns32202_device::map<0>(address_map &map);
template void ns32202_device::map<1>(address_map &map);
/*
* Set (and clear, for level-triggered interrupts) interrupt pending state
* based on edge/level/polarity configuration and previous/current line state,
* regardless of mask.
*/
template <unsigned Number> void ns32202_device::ir_w(int state)
{
// ignore external interrupts assigned to counters
if (((m_cictl & CICTL_CIEL) && (m_ciptr & 15) == Number) ||
((m_cictl & CICTL_CIEH) && (m_ciptr >> 4) == Number))
return;
u16 const mask = 1 << Number;
if (m_eltg & mask)
{
// level triggered
if (state == BIT(m_tpl, Number))
m_ipnd |= mask;
else
m_ipnd &= ~mask;
}
else
{
// edge triggered
if (bool(state) == BIT(m_tpl, Number) && bool(state) ^ BIT(m_line_state, Number))
m_ipnd |= mask;
}
// record input line state
if (state)
m_line_state |= mask;
else
m_line_state &= ~mask;
// datasheet states maximum 800ns
m_interrupt->adjust(attotime::from_nsec(600));
}
// instantiate all valid interrupt request templates
template void ns32202_device::ir_w<0>(int state);
template void ns32202_device::ir_w<1>(int state);
template void ns32202_device::ir_w<2>(int state);
template void ns32202_device::ir_w<3>(int state);
template void ns32202_device::ir_w<4>(int state);
template void ns32202_device::ir_w<5>(int state);
template void ns32202_device::ir_w<6>(int state);
template void ns32202_device::ir_w<7>(int state);
template void ns32202_device::ir_w<8>(int state);
template void ns32202_device::ir_w<9>(int state);
template void ns32202_device::ir_w<10>(int state);
template void ns32202_device::ir_w<11>(int state);
template void ns32202_device::ir_w<12>(int state);
template void ns32202_device::ir_w<13>(int state);
template void ns32202_device::ir_w<14>(int state);
template void ns32202_device::ir_w<15>(int state);
/*
* Assert interrupt output if there are any unmasked pending interrupts; and
* - in auto-rotate mode and no interrupts are in-service; or
* - in fixed priority mode; and
* - no interrupts are in-service; or
* - unmasked pending interrupt has priority > in-service interrupt; or
* - unmasked pending cascade interrupt has priorty >= in-service interrupt
*/
void ns32202_device::interrupt(void *ptr, s32 param)
{
// check for unmasked pending interrupts
if (!(m_ipnd & m_imsk))
return;
if (m_mctl & MCTL_NTAR)
{
// fixed priority mode
bool accept = false;
// check any interrupts in-service
if (m_isrv)
{
// check interrupts in descending priority order
u16 mask = m_fprt;
for (unsigned i = 0; i < 16; i++)
{
// check interrupt in-service
if (m_isrv & mask)
{
// check equal priority unmasked pending cascade interrupt
if ((m_csrc & mask) && (m_ipnd & mask) && !(m_imsk & mask))
{
LOGMASKED(LOG_STATE, "unmasked pending cascade in-service interrupt %d\n", 31 - count_leading_zeros(mask));
accept = true;
}
break;
}
// check unmasked pending interrupt
if ((m_ipnd & mask) && !(m_imsk & mask))
{
LOGMASKED(LOG_STATE, "unmasked pending interrupt %d\n", 31 - count_leading_zeros(mask));
accept = true;
break;
}
// rotate priority mask
mask = (mask << 1) | (mask >> 15);
}
}
else
accept = true;
if (!accept)
return;
}
else if (m_isrv)
return;
set_int(true);
}
u8 ns32202_device::interrupt_acknowledge(bool side_effects)
{
side_effects &= !machine().side_effects_disabled();
u8 vector = m_hvct | 0x0f;
if ((m_ipnd & m_imsk) && m_fprt)
{
// find highest priority unmasked pending interrupt
u16 mask = m_fprt;
for (unsigned i = 0; i < 16; i++)
{
if ((m_ipnd & mask) && !(m_imsk & mask))
break;
// rotate priority mask
mask = (mask << 1) | (mask >> 15);
}
unsigned const number = 31 - count_leading_zeros(mask);
if (side_effects)
{
LOGMASKED(LOG_STATE, "acknowledge highest priority unmasked interrupt %d\n", number);
if (m_mctl & MCTL_NTAR)
{
if (m_csrc & mask)
m_isrv_count[number]++;
}
else
m_fprt = mask;
// mark interrupt in-service
m_isrv |= mask;
// clear interrupt pending
m_ipnd &= ~(mask);
// clear l-counter interrupt pending
if ((m_cictl & CICTL_CIEL) && (m_cictl & CICTL_CIRL) && BIT(mask, m_ciptr & 15))
m_cictl &= ~CICTL_CIRL;
// clear h-counter interrupt pending
if ((m_cictl & CICTL_CIEH) && (m_cictl & CICTL_CIRH) && BIT(mask, m_ciptr >> 4))
m_cictl &= ~CICTL_CIRH;
// clear interrupt output
set_int(false);
}
// compute acknowledge vector
if (m_csrc & mask)
vector = 0xf0 | number;
else
vector = m_hvct | number;
}
else if (side_effects)
{
if (m_fprt)
LOGMASKED(LOG_STATE, "acknowledge without unmasked interrupt pending\n");
else
LOGMASKED(LOG_STATE, "acknowledge with FPRT clear\n");
// clear pending edge for interrupt 15
if (!BIT(m_eltg, 15))
m_ipnd &= ~(1 << 15);
// clear first priority
if (!(m_mctl & MCTL_NTAR))
m_fprt = 0;
}
if (side_effects)
LOGMASKED(LOG_STATE, "acknowledge vector 0x%02x\n", vector);
return vector;
}
u8 ns32202_device::interrupt_return(bool side_effects)
{
side_effects &= !machine().side_effects_disabled();
u8 vector = m_hvct | 0x0f;
// find highest priority in-service interrupt
if (m_isrv && m_fprt)
{
u16 mask = m_fprt;
for (unsigned i = 0; i < 16; i++)
{
if (m_isrv & mask)
break;
// rotate priority mask
mask = (mask << 1) | (mask >> 15);
}
unsigned const number = 31 - count_leading_zeros(mask);
if (side_effects)
{
LOGMASKED(LOG_STATE, "return highest priority in-service interrupt %d\n", number);
if (m_mctl & MCTL_NTAR)
{
if (m_csrc & mask)
{
m_isrv_count[number]--;
if (!m_isrv_count[number])
m_isrv &= ~mask;
}
else
// clear interrupt in-service
m_isrv &= ~mask;
}
else
{
// clear interrupt in-service
m_isrv &= ~mask;
// rotate priority mask
m_fprt = (m_fprt << 1) | (m_fprt >> 15);
}
}
// compute return vector
if (m_csrc & mask)
vector = 0xf0 | number;
else
vector = m_hvct | number;
}
else if (side_effects)
{
if (m_fprt)
LOGMASKED(LOG_STATE, "return without in-service interrupt\n");
else
LOGMASKED(LOG_STATE, "return with FPRT clear\n");
if (!(m_mctl & MCTL_NTAR))
// rotate priority mask
m_fprt = (m_fprt << 1) | (m_fprt >> 15);
}
if (side_effects)
LOGMASKED(LOG_STATE, "return vector 0x%02x\n", vector);
return vector;
}
/*
* Check for level-triggered interrupts which become pending due to change of
* edge/level or polarity registers.
*/
void ns32202_device::interrupt_update()
{
// compute new pending state
u16 const ipnd = m_ipnd | (m_eltg & ~(m_line_state ^ m_tpl));
// update and assert if state changed
if (ipnd ^ m_ipnd)
{
m_ipnd = ipnd;
m_interrupt->adjust(attotime::zero);
}
}
// N=0 -> l-counter
template <unsigned N> void ns32202_device::counter(void *buf, s32 param)
{
u32 const scaled_clock = clock() / ((m_cctl & CCTL_CFNPS) ? 1 : 4);
// for now, assume this is the periodic timer triggered when we hit zero
// reload on cycle after zero
if (param)
{
u32 const ticks = (m_cctl & CCTL_CCON)
? ((u32(m_csv[1]) << 16) | m_csv[0]) - ((u32(m_ccv[1]) << 16) | m_ccv[0])
: m_csv[N] - m_ccv[N];
// reload current value
if (m_cctl & CCTL_CCON)
{
m_ccv[0] = m_csv[0];
m_ccv[1] = m_csv[1];
}
else
m_ccv[N] = m_csv[N];
// reschedule counter
m_counter[N]->adjust(attotime::from_ticks(ticks, scaled_clock), 0);
}
else
{
// clear current value
if (m_cctl & CCTL_CCON)
{
m_ccv[0] = 0;
m_ccv[1] = 0;
}
else
m_ccv[N] = 0;
// schedule reload cycle
m_counter[N]->adjust(attotime::from_ticks(1, scaled_clock), 1);
// update cout
if (!(m_mctl & MCTL_COUTD) && (m_cctl & (CCTL_COUT0 << N)))
{
if (m_mctl & MCTL_COUTM)
{
set_cout(true);
set_cout(false);
}
else
set_cout(!m_out_cout_state);
}
// update port
if ((N == 1) && !(m_mctl & MCTL_T16N8) && (m_ocasn & 15))
{
// TODO: trigger interrupts if IPS != 0
u8 const mask = (m_ocasn & ~m_pdir) & 15;
if (m_mctl & MCTL_CLKM)
{
m_pdat &= ~mask;
m_out_port(0, m_ocasn & 15, mask);
m_out_port(0, 0, mask);
}
else
{
m_pdat ^= mask;
m_out_port(0, m_pdat, mask);
}
}
// interrupts
unsigned const shift = N ? 4 : 0;
if (m_cictl & (CICTL_CIEL << shift))
{
// check counter interrupt error
if (m_cictl & (CICTL_CIRL << shift))
m_cictl |= (CICTL_CERL << shift);
// set counter interrupt request
m_cictl |= (CICTL_CIRL << shift);
// raise interrupt
m_ipnd |= 1 << ((m_ciptr >> shift) & 15);
m_interrupt->adjust(attotime::zero);
}
}
}
template <unsigned ST1, bool SideEffects> u8 ns32202_device::hvct_r()
{
if (!ST1)
return interrupt_acknowledge(SideEffects);
else
return interrupt_return(SideEffects);
}
void ns32202_device::eltgl_w(u8 data)
{
m_eltg = (m_eltg & 0xff00) | data;
interrupt_update();
}
void ns32202_device::eltgh_w(u8 data)
{
m_eltg = (u16(data) << 8) | u8(m_eltg);
interrupt_update();
}
void ns32202_device::tpll_w(u8 data)
{
m_tpl = (m_tpl & 0xff00) | data;
interrupt_update();
}
void ns32202_device::tplh_w(u8 data)
{
m_tpl = (u16(data) << 8) | u8(m_tpl);
interrupt_update();
}
void ns32202_device::csrcl_w(u8 data)
{
m_csrc = (m_csrc & 0xff00) | data;
// clear in-service counters
for (unsigned i = 0; i < 8; i++)
if (!BIT(m_csrc, i))
m_isrv_count[i] = 0;
}
void ns32202_device::csrch_w(u8 data)
{
m_csrc = (u16(data) << 8) | u8(m_csrc);
// clear in-service counters
for (unsigned i = 8; i < 16; i++)
if (!BIT(m_csrc, i))
m_isrv_count[i] = 0;
}
void ns32202_device::ipndl_w(u8 data)
{
if (BIT(data, 6))
// clear all pending interrupts in register
m_ipnd &= 0xff00;
else if (BIT(data, 7))
{
// set interrupt
m_ipnd |= 1 << (data & 7);
m_interrupt->adjust(attotime::zero);
}
else
// clear interrupt
m_ipnd &= ~(1 << (data & 7));
}
void ns32202_device::ipndh_w(u8 data)
{
if (BIT(data, 6))
// clear all pending interrupts in register
m_ipnd &= 0x00ff;
else if (BIT(data, 7))
{
// set interrupt
m_ipnd |= 256 << (data & 7);
m_interrupt->adjust(attotime::zero);
}
else
// clear interrupt
m_ipnd &= ~(256 << (data & 7));
}
void ns32202_device::fprtl_w(u8 data)
{
m_fprt = 1 << (data & 15);
}
void ns32202_device::cctl_w(u8 data)
{
// disable l-counter in concatenated mode
if ((data & CCTL_CCON) && m_counter[0]->enabled())
m_counter[0]->enable(false);
// compute scaled clock
u32 const scaled_clock = clock() / ((data & CCTL_CFNPS) ? 1 : 4);
// start/stop h-counter
if (!(m_cctl & CCTL_CRUNH) && (data & CCTL_CRUNH))
m_counter[1]->adjust(attotime::from_ticks(1, scaled_clock), 1);
else if ((m_cctl & CCTL_CRUNH) && !(data & CCTL_CRUNH))
m_counter[1]->enable(false);
if (!(data & CCTL_CRUNH) && (data & CCTL_CDCRH))
; // TODO: decrement h-counter
// start/stop l-counter
if (!(data & CCTL_CCON))
{
if (!(m_cctl & CCTL_CRUNH) && (data & CCTL_CRUNH))
m_counter[0]->adjust(attotime::from_ticks(1, scaled_clock), 1);
else if ((m_cctl & CCTL_CRUNH) && !(data & CCTL_CRUNH))
m_counter[0]->enable(false);
if (!(data & CCTL_CRUNL) && (data & CCTL_CDCRL))
; // TODO: decrement l-counter
}
m_cctl = data & ~(CCTL_CRUNH | CCTL_CRUNL);
}
void ns32202_device::cictl_w(u8 data)
{
u8 const mask =
((data & CICTL_WENL) ? (CICTL_CERL | CICTL_CIRL | CICTL_CIEL) : 0) |
((data & CICTL_WENH) ? (CICTL_CERH | CICTL_CIRH | CICTL_CIEH) : 0);
m_cictl = (m_cictl & ~mask) | (data & mask);
}

View File

@ -0,0 +1,137 @@
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay
#ifndef MAME_MACHINE_NS32202_H
#define MAME_MACHINE_NS32202_H
#pragma once
class ns32202_device : public device_t
{
public:
auto out_int() { return m_out_int.bind(); }
auto out_cout() { return m_out_cout.bind(); }
auto out_port() { return m_out_port.bind(); }
template <unsigned Number> void ir_w(int state);
template <unsigned ST1> void map(address_map &map);
ns32202_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
protected:
// device_t overrides
virtual void device_start() override;
virtual void device_reset() override;
void set_int(bool int_state);
void set_cout(bool cout_state);
void interrupt(void *buf, s32 param);
u8 interrupt_acknowledge(bool side_effects);
u8 interrupt_return(bool side_effects);
void interrupt_update();
template <unsigned N> void counter(void *buf, s32 param);
template <unsigned ST1, bool SideEffects> u8 hvct_r();
u8 eltgl_r() { return u8(m_eltg); }
u8 eltgh_r() { return m_eltg >> 8; }
u8 tpll_r() { return u8(m_tpl); }
u8 tplh_r() { return m_tpl >> 8; }
u8 ipndl_r() { return u8(m_ipnd); }
u8 ipndh_r() { return m_ipnd >> 8; }
u8 isrvl_r() { return u8(m_isrv); }
u8 isrvh_r() { return m_isrv >> 8; }
u8 imskl_r() { return u8(m_imsk); }
u8 imskh_r() { return m_imsk >> 8; }
u8 csrcl_r() { return u8(m_csrc); }
u8 csrch_r() { return m_csrc >> 8; }
u8 fprtl_r() { return u8(m_fprt); }
u8 fprth_r() { return m_fprt >> 8; }
u8 mctl_r() { return m_mctl; }
u8 ocasn_r() { return m_ocasn; }
u8 ciptr_r() { return m_ciptr; }
u8 pdat_r() { return 0; }
u8 ips_r() { return m_ips; }
u8 pdir_r() { return m_pdir; }
u8 cctl_r() { return m_cctl; }
u8 cictl_r() { return m_cictl; }
template <unsigned N> u8 csvl_r() { return u8(m_csv[N]); }
template <unsigned N> u8 csvh_r() { return m_csv[N] >> 8; }
u8 lccvl_r() { return 0; }
u8 lccvh_r() { return 0; }
u8 hccvl_r() { return 0; }
u8 hccvh_r() { return 0; }
void svct_w(u8 data) { m_hvct = data & 0xf0; }
void eltgl_w(u8 data);
void eltgh_w(u8 data);
void tpll_w(u8 data);
void tplh_w(u8 data);
void ipndl_w(u8 data);
void ipndh_w(u8 data);
void isrvl_w(u8 data) { m_isrv = (m_isrv & 0xff00) | data; }
void isrvh_w(u8 data) { m_isrv = (u16(data) << 8) | u8(m_isrv); }
void imskl_w(u8 data) { m_imsk = (m_imsk & 0xff00) | data; m_interrupt->adjust(attotime::zero); }
void imskh_w(u8 data) { m_imsk = (u16(data) << 8) | u8(m_imsk); m_interrupt->adjust(attotime::zero); }
void csrcl_w(u8 data);
void csrch_w(u8 data);
void fprtl_w(u8 data);
void fprth_w(u8 data) {}
void mctl_w(u8 data) { m_mctl = data; }
void ocasn_w(u8 data) { m_ocasn = data; }
void ciptr_w(u8 data) { m_ciptr = data; }
void pdat_w(u8 data) {}
void ips_w(u8 data) { m_ips = data; }
void pdir_w(u8 data) { m_pdir = data; }
void cctl_w(u8 data);
void cictl_w(u8 data);
template <unsigned N> void csvl_w(u8 data) { m_csv[N] = (m_csv[N] & 0xff00) | data; }
template <unsigned N> void csvh_w(u8 data) { m_csv[N] = (u16(data) << 8) | u8(m_csv[N]); }
void lccvl_w(u8 data) {}
void lccvh_w(u8 data) {}
void hccvl_w(u8 data) {}
void hccvh_w(u8 data) {}
private:
devcb_write_line m_out_int;
devcb_write_line m_out_cout;
devcb_write8 m_out_port;
emu_timer *m_interrupt;
emu_timer *m_counter[2];
u8 m_hvct; // hardware vector
u16 m_eltg; // edge/level triggering
u16 m_tpl; // triggering polarity
u16 m_ipnd; // interrupts pending
u16 m_isrv; // interrupts in-service
u16 m_imsk; // interrupt mask
u16 m_csrc; // cascaded source
u16 m_fprt; // first priority
u8 m_mctl; // mode control
u8 m_ocasn; // output clock assignment
u8 m_ciptr; // counter interrupt pointer
u8 m_pdat; // port data
u8 m_ips; // interrupt/port select
u8 m_pdir; // port direction
u8 m_cctl; // counter control
u8 m_cictl; // counter interrupt control
u16 m_csv[2]; // counter starting value
u16 m_ccv[2]; // counter current value
unsigned m_isrv_count[16];
u16 m_line_state;
bool m_out_int_state;
bool m_out_cout_state;
};
DECLARE_DEVICE_TYPE(NS32202, ns32202_device)
#endif // MAME_MACHINE_NS32202_H