summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/ns32202.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/ns32202.cpp')
-rw-r--r--src/devices/machine/ns32202.cpp260
1 files changed, 226 insertions, 34 deletions
diff --git a/src/devices/machine/ns32202.cpp b/src/devices/machine/ns32202.cpp
index 008c2c855dc..70d6bf080ea 100644
--- a/src/devices/machine/ns32202.cpp
+++ b/src/devices/machine/ns32202.cpp
@@ -5,25 +5,25 @@
* National Semiconductor NS32202 Interrupt Control Unit (ICU).
*
* Sources:
+ * - Microprocessor Databook, Series 32000, NSC800, 1989 Edition, National Semiconductor
*
- * http://bitsavers.org/components/national/_dataBooks/1989_National_Microprocessor_Databook_32000_NSC800.pdf
- *
- * TODO
- * - timer/counter
+ * TODO:
+ * - more testing
*/
#include "emu.h"
#include "ns32202.h"
-#define LOG_STATE (1U << 1)
-#define LOG_REGW (1U << 2)
-#define LOG_REGR (1U << 3)
-#define LOG_COUNTER (1U << 4)
+#define LOG_STATE (1U << 1)
+#define LOG_REGW (1U << 2)
+#define LOG_REGR (1U << 3)
+#define LOG_INTERRUPT (1U << 4)
+#define LOG_COUNTER (1U << 5)
-//#define VERBOSE (LOG_GENERAL|LOG_STATE|LOG_REGW|LOG_REGR|LOG_COUNTER)
+//#define VERBOSE (LOG_GENERAL|LOG_STATE|LOG_REGW|LOG_REGR|LOG_INTERRUPT)
#include "logmacro.h"
-DEFINE_DEVICE_TYPE(NS32202, ns32202_device, "ns32202", "NS32202 Interrupt Control Unit")
+DEFINE_DEVICE_TYPE(NS32202, ns32202_device, "ns32202", "National Semiconductor NS32202 Interrupt Control Unit")
enum mctl_mask : u8
{
@@ -64,10 +64,17 @@ ns32202_device::ns32202_device(machine_config const &mconfig, char const *tag, d
: device_t(mconfig, NS32202, tag, owner, clock)
, m_out_int(*this)
, m_out_cout(*this)
- , m_out_port(*this)
+ , m_out_g(*this)
+ , m_hvct(0)
+ , m_pdat(0)
+ , m_cctl(0)
+ , m_csv{}
+ , m_ccv{}
, m_line_state(0xffff)
, m_out_int_state(false)
, m_out_cout_state(false)
+ , m_pdat_in(0)
+ , m_pdat_out(0)
{
}
@@ -109,6 +116,7 @@ void ns32202_device::device_reset()
m_tpl = 0;
m_ipnd = 0;
m_isrv = 0;
+ std::fill(std::begin(m_isrv_count), std::end(m_isrv_count), 0);
m_imsk = 0xffff;
m_csrc = 0;
m_fprt = 0x0001;
@@ -122,6 +130,8 @@ void ns32202_device::device_reset()
set_int(false);
set_cout(false);
+
+ update_port();
}
void ns32202_device::set_int(bool int_state)
@@ -139,7 +149,7 @@ 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");
+ LOGMASKED(LOG_COUNTER, "cout %s\n", cout_state ? "asserted" : "cleared");
m_out_cout_state = cout_state;
m_out_cout(!m_out_cout_state);
@@ -192,6 +202,8 @@ template void ns32202_device::map<1>(address_map &map);
*/
template <unsigned Number> void ns32202_device::ir_w(int state)
{
+ LOGMASKED(LOG_INTERRUPT, "ir_w%d %d\n", Number, state);
+
// ignore external interrupts assigned to counters
if (((m_cictl & CICTL_CIEL) && (m_ciptr & 15) == Number) ||
((m_cictl & CICTL_CIEH) && (m_ciptr >> 4) == Number))
@@ -378,10 +390,9 @@ u8 ns32202_device::interrupt_acknowledge(bool side_effects)
if (side_effects)
{
- LOGMASKED(LOG_STATE, "acknowledge vector 0x%02x\n", vector);
-
// clear interrupt output
- set_int(false);
+ if (!(m_ipnd & ~m_imsk & ~m_isrv) || !m_fprt)
+ set_int(false);
}
return vector;
@@ -451,9 +462,6 @@ u8 ns32202_device::interrupt_return(bool side_effects)
m_fprt = (m_fprt << 1) | (m_fprt >> 15);
}
- if (side_effects)
- LOGMASKED(LOG_STATE, "return vector 0x%02x\n", vector);
-
return vector;
}
@@ -463,6 +471,9 @@ u8 ns32202_device::interrupt_return(bool side_effects)
*/
void ns32202_device::interrupt_update()
{
+ if (m_mctl & MCTL_FRZ)
+ return;
+
// compute new pending state
u16 const ipnd = m_ipnd | (m_eltg & ~(m_line_state ^ m_tpl));
@@ -530,19 +541,27 @@ template <unsigned N> void ns32202_device::counter(s32 param)
{
// 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);
+ for (unsigned i = 0; i < 4; i++)
+ {
+ if (BIT(m_ocasn, i) && !BIT(m_pdir, i))
+ {
+ m_out_g[i](1);
+ m_out_g[i](0);
+ }
+ }
}
else
{
- m_pdat ^= mask;
-
- m_out_port(0, m_pdat, mask);
+ for (unsigned i = 0; i < 4; i++)
+ {
+ if (BIT(m_ocasn, i) && !BIT(m_pdir, i))
+ {
+ m_pdat_out ^= 1U << i;
+ m_out_g[i](BIT(m_pdat_out, i));
+ }
+ }
}
}
@@ -560,16 +579,25 @@ template <unsigned N> void ns32202_device::counter(s32 param)
// raise interrupt
m_ipnd |= 1 << ((m_ciptr >> shift) & 15);
m_interrupt->adjust(attotime::zero);
+
+ LOGMASKED(LOG_COUNTER, "counter %d interrupt %d\n", N, (m_ciptr >> shift) & 15);
}
}
}
template <unsigned ST1, bool SideEffects> u8 ns32202_device::hvct_r()
{
- if (!ST1)
- return interrupt_acknowledge(SideEffects);
- else
- return interrupt_return(SideEffects);
+ u8 data = ST1 ? interrupt_return(SideEffects) : interrupt_acknowledge(SideEffects);
+ LOGMASKED(LOG_REGR, "%cvct_r 0x%02x%s (%s)\n",
+ SideEffects ? 'h' : 's', data, SideEffects ? (ST1 ? " interrupt return" : " interrupt acknowledge") : "", machine().describe_context());
+
+ return data;
+}
+
+void ns32202_device::svct_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "svct_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_hvct = data & 0xf0;
}
void ns32202_device::eltgl_w(u8 data)
@@ -590,6 +618,7 @@ void ns32202_device::eltgh_w(u8 data)
void ns32202_device::tpll_w(u8 data)
{
+ LOGMASKED(LOG_REGW, "tpll_w 0x%02x (%s)\n", data, machine().describe_context());
m_tpl = (m_tpl & 0xff00) | data;
interrupt_update();
@@ -597,6 +626,7 @@ void ns32202_device::tpll_w(u8 data)
void ns32202_device::tplh_w(u8 data)
{
+ LOGMASKED(LOG_REGW, "tplh_w 0x%02x (%s)\n", data, machine().describe_context());
m_tpl = (u16(data) << 8) | u8(m_tpl);
interrupt_update();
@@ -631,20 +661,22 @@ void ns32202_device::ipndl_w(u8 data)
m_ipnd &= 0xff00;
}
- else if (BIT(data, 7))
+ else if ((data & 0xf8) == 0x80)
{
// set pending interrupt
LOGMASKED(LOG_REGW, "ipndl_w 0x%02x set pending interrupt %d (%s)\n", data, data & 15, machine().describe_context());
m_ipnd |= 1 << (data & 15);
}
- else
+ else if ((data & 0xf8) == 0x00)
{
// clear pending interrupt
LOGMASKED(LOG_REGW, "ipndl_w 0x%02x clear pending interrupt %d (%s)\n", data, data & 15, machine().describe_context());
m_ipnd &= ~(1 << (data & 15));
}
+ else
+ LOGMASKED(LOG_REGW, "ipndl_w 0x%02x unknown (%s)\n", data, machine().describe_context());
m_interrupt->adjust(attotime::zero);
}
@@ -658,20 +690,62 @@ void ns32202_device::ipndh_w(u8 data)
m_ipnd &= 0x00ff;
}
- else if (BIT(data, 7))
+ else if ((data & 0xf8) == 0x88)
{
// set pending interrupt
LOGMASKED(LOG_REGW, "ipndh_w 0x%02x set pending interrupt %d (%s)\n", data, data & 15, machine().describe_context());
m_ipnd |= 1 << (data & 15);
}
- else
+ else if ((data & 0xf8) == 0x08)
{
// clear pending interrupt
LOGMASKED(LOG_REGW, "ipndh_w 0x%02x clear pending interrupt %d (%s)\n", data, data & 15, machine().describe_context());
m_ipnd &= ~(1 << (data & 15));
}
+ else
+ LOGMASKED(LOG_REGW, "ipndh_w 0x%02x unknown (%s)\n", data, machine().describe_context());
+
+ m_interrupt->adjust(attotime::zero);
+}
+
+void ns32202_device::isrvl_w(u8 data)
+{
+ if (u8(m_isrv >> 0) ^ data)
+ {
+ LOGMASKED(LOG_REGW, "isrvl_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_isrv = (m_isrv & 0xff00) | data;
+
+ m_interrupt->adjust(attotime::zero);
+ }
+}
+
+void ns32202_device::isrvh_w(u8 data)
+{
+ if (u8(m_isrv >> 8) ^ data)
+ {
+ LOGMASKED(LOG_REGW, "isrvh_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_isrv = (u16(data) << 8) | u8(m_isrv);
+
+ m_interrupt->adjust(attotime::zero);
+ }
+}
+
+void ns32202_device::imskl_w(u8 data)
+{
+ if (data ^ u8(m_imsk))
+ LOGMASKED(LOG_REGW, "imskl_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_imsk = (m_imsk & 0xff00) | data;
+
+ m_interrupt->adjust(attotime::zero);
+}
+
+void ns32202_device::imskh_w(u8 data)
+{
+ if (data ^ (m_imsk >> 8))
+ LOGMASKED(LOG_REGW, "imskh_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_imsk = (u16(data) << 8) | u8(m_imsk);
m_interrupt->adjust(attotime::zero);
}
@@ -737,14 +811,28 @@ void ns32202_device::cictl_w(u8 data)
m_cictl = (m_cictl & ~mask) | (data & mask);
}
+template <unsigned N> void ns32202_device::csvl_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "csvl%d_w 0x%02x (%s)\n", N, data, machine().describe_context());
+ m_csv[N] = (m_csv[N] & 0xff00) | data;
+}
+
+template <unsigned N> void ns32202_device::csvh_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "csvh%d_w 0x%02x (%s)\n", N, data, machine().describe_context());
+ m_csv[N] = (u16(data) << 8) | u8(m_csv[N]);
+}
+
template <unsigned N> void ns32202_device::ccvl_w(u8 data)
{
+ LOGMASKED(LOG_REGW, "ccvl%d_w 0x%02x (%s)\n", N, data, machine().describe_context());
if ((N == 0 && !(m_cctl & CCTL_CRUNL)) || ((N == 1) && !(m_cctl & CCTL_CRUNH)))
m_ccv[N] = (m_ccv[N] & 0xff00) | data;
}
template <unsigned N> void ns32202_device::ccvh_w(u8 data)
{
+ LOGMASKED(LOG_REGW, "ccvh%d_w 0x%02x (%s)\n", N, data, machine().describe_context());
if ((N == 0 && !(m_cctl & CCTL_CRUNL)) || ((N == 1) && !(m_cctl & CCTL_CRUNH)))
m_ccv[N] = (u16(data) << 8) | u8(m_ccv[N]);
}
@@ -758,6 +846,30 @@ void ns32202_device::mctl_w(u8 data)
m_mctl = data;
}
+void ns32202_device::ocasn_w(u8 data)
+{
+ if (VERBOSE & LOG_REGW)
+ {
+ std::string outputs;
+ if (data & 0x0f)
+ {
+ for (unsigned i = 0; i < 4; i++)
+ if (BIT(data, i))
+ outputs.append(util::string_format(" g%d/ir%d", i, i * 2));
+ }
+
+ LOGMASKED(LOG_REGW, "ocasn_w 0x%02x%s (%s)\n", data, outputs, machine().describe_context());
+ }
+
+ m_ocasn = data;
+}
+
+void ns32202_device::ciptr_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "ciptr_w 0x%02x h=%d l=%d (%s)\n", data, BIT(data, 4, 4), BIT(data, 0, 4), machine().describe_context());
+ m_ciptr = data;
+}
+
void ns32202_device::update_ccv()
{
u32 const scaled_clock = clock() / ((m_cctl & CCTL_CFNPS) ? 1 : 4);
@@ -781,3 +893,83 @@ void ns32202_device::update_ccv()
m_ccv[0] = m_csv[0] - m_counter[0]->elapsed().as_ticks(scaled_clock);
}
}
+
+template <unsigned Number> void ns32202_device::g_w(int state)
+{
+ if (state)
+ m_pdat_in |= 1U << Number;
+ else
+ m_pdat_in &= ~(1U << Number);
+}
+
+// instantiate all valid general purpose input templates
+template void ns32202_device::g_w<0>(int state);
+template void ns32202_device::g_w<1>(int state);
+template void ns32202_device::g_w<2>(int state);
+template void ns32202_device::g_w<3>(int state);
+template void ns32202_device::g_w<4>(int state);
+template void ns32202_device::g_w<5>(int state);
+template void ns32202_device::g_w<6>(int state);
+template void ns32202_device::g_w<7>(int state);
+
+u8 ns32202_device::pdat_r()
+{
+ u8 const data = ((m_pdat_in & m_pdir) | (m_pdat & ~m_pdir)) & ~m_ips & ~m_ocasn;
+ LOGMASKED(LOG_REGR, "pdat_r 0x%02x (%s)\n", data, machine().describe_context());
+
+ return data;
+}
+
+void ns32202_device::pdat_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "pdat_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_pdat = data;
+
+ update_port();
+}
+
+void ns32202_device::ips_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "ips_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_ips = data;
+
+ update_port();
+}
+
+void ns32202_device::pdir_w(u8 data)
+{
+ LOGMASKED(LOG_REGW, "pdir_w 0x%02x (%s)\n", data, machine().describe_context());
+ m_pdir = data;
+
+ update_port();
+}
+
+void ns32202_device::update_port()
+{
+ // find port output bits which need to be updated
+ u8 const output = ~m_ips & ~m_pdir & ~m_ocasn & (m_pdat ^ m_pdat_out);
+
+ // release interrupt/input lines (assume float high)
+ u8 const release = (m_ips | m_pdir) & ~m_ocasn & ~m_pdat_out;
+
+ for (unsigned i = 0; i < 8; i++)
+ {
+ if (BIT(output, i))
+ {
+ // record the output state
+ if (BIT(m_pdat, i))
+ m_pdat_out |= 1U << i;
+ else
+ m_pdat_out &= ~(1U << i);
+
+ // update the line
+ m_out_g[i](BIT(m_pdat, i));
+ }
+ else if (BIT(release, i))
+ {
+ m_pdat_out |= 1U << i;
+
+ m_out_g[i](1);
+ }
+ }
+}