diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 185b1a8164b..ac96f03e4cc 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -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 diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 82f6bfc26ea..1401795fd94 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -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 diff --git a/src/devices/machine/ns32202.cpp b/src/devices/machine/ns32202.cpp new file mode 100644 index 00000000000..947bb6e38cd --- /dev/null +++ b/src/devices/machine/ns32202.cpp @@ -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 void ns32202_device::map(address_map &map) +{ + map(0x00, 0x00).r(&ns32202_device::hvct_r, "ns32202_device::hvct_r"); + map(0x01, 0x01).rw(&ns32202_device::hvct_r, "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 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 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 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); +} diff --git a/src/devices/machine/ns32202.h b/src/devices/machine/ns32202.h new file mode 100644 index 00000000000..c6328e5fa38 --- /dev/null +++ b/src/devices/machine/ns32202.h @@ -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 void ir_w(int state); + template 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 void counter(void *buf, s32 param); + + template 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 u8 csvl_r() { return u8(m_csv[N]); } + template 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 void csvl_w(u8 data) { m_csv[N] = (m_csv[N] & 0xff00) | data; } + template 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