summaryrefslogtreecommitdiffstatshomepage
path: root/src
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2017-09-22 00:22:12 -0400
committer AJR <ajrhacker@users.noreply.github.com>2017-09-22 01:35:51 -0400
commit83d5618b34b2b0c07a93ff88784081635af55595 (patch)
tree8a0edaa4445b4c1e1db7cc0fd7045ca7a0e6d52e /src
parent0a8e4698a06375561448f1c2e027974d90c8988c (diff)
am9513: New device
Diffstat (limited to 'src')
-rw-r--r--src/devices/machine/am9513.cpp1318
-rw-r--r--src/devices/machine/am9513.h209
-rw-r--r--src/mame/drivers/pm68k.cpp9
3 files changed, 1532 insertions, 4 deletions
diff --git a/src/devices/machine/am9513.cpp b/src/devices/machine/am9513.cpp
new file mode 100644
index 00000000000..1432c879ca9
--- /dev/null
+++ b/src/devices/machine/am9513.cpp
@@ -0,0 +1,1318 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ Am9513A/Am9513 System Timing Controller (STC)
+
+ The Am9513 is a five-channel counter/timer circuit introduced by
+ AMD around 1980. (It was also sold as the AmZ8073, apparently due
+ to a licensing deal with Zilog.) Clock source, edge selection,
+ gating and retriggering are programmable for each channel.
+
+ All internal counters are 16 bits wide (except the internal 4-bit
+ counter for the FOUT divider, which is not externally accessible).
+ The device defaults to an 8-bit external interface after being
+ powered on or a programmed master reset, but can be configured to
+ work more efficiently with an 16-bit data bus.
+
+ There is no reset line, though the device does reset itself when
+ it powers on, and there is a "master reset" software command.
+
+ For a full description of each counter mode, see the datasheet.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "machine/am9513.h"
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_MODE (1U << 1)
+#define LOG_INPUT (1U << 2)
+#define LOG_TC (1U << 3)
+//#define VERBOSE (LOG_GENERAL | LOG_MODE | LOG_INPUT)
+
+#include "logmacro.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(AM9513, am9513_device, "am9513", "Am9513 STC")
+DEFINE_DEVICE_TYPE(AM9513A, am9513a_device, "am9513a", "Am9513A STC")
+
+
+//**************************************************************************
+// DEVICE CONSTRUCTION AND INITIALIZATION
+//**************************************************************************
+
+//-------------------------------------------------
+// am9513_device - constructor
+//-------------------------------------------------
+
+am9513_device::am9513_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, bool is_am9513a)
+ : device_t(mconfig, type, tag, owner, clock),
+ m_out_cb{{*this}, {*this}, {*this}, {*this}, {*this}},
+ m_fout_cb(*this),
+ m_is_am9513a(is_am9513a)
+{
+}
+
+am9513_device::am9513_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : am9513_device(mconfig, AM9513, tag, owner, clock, false)
+{
+}
+
+am9513a_device::am9513a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : am9513_device(mconfig, AM9513A, tag, owner, clock, true)
+{
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void am9513_device::device_start()
+{
+ // Resolve callbacks
+ for (auto &cb : m_out_cb)
+ cb.resolve_safe();
+ m_fout_cb.resolve();
+
+ // Power-on reset
+ m_dpr = 0x1f;
+ m_mmr = 0;
+ m_write_prefetch = true;
+ m_status = 0;
+ m_f = 0;
+ m_fout = true;
+ m_fout_counter = 16;
+ std::fill(std::begin(m_count), std::end(m_count), 0);
+ std::fill(std::begin(m_counter_load), std::end(m_counter_load), 0);
+ std::fill(std::begin(m_counter_hold), std::end(m_counter_hold), 0);
+ std::fill(std::begin(m_counter_mode), std::end(m_counter_mode), 0x0b00);
+ std::fill(std::begin(m_alarm), std::end(m_alarm), 0);
+ std::fill(std::begin(m_counter_armed), std::end(m_counter_armed), false);
+ std::fill(std::begin(m_counter_running), std::end(m_counter_running), false);
+ std::fill(std::begin(m_alternate_count), std::end(m_alternate_count), false);
+ std::fill(std::begin(m_src), std::end(m_src), true);
+ std::fill(std::begin(m_gate), std::end(m_gate), true);
+ std::fill(std::begin(m_gate_alt), std::end(m_gate_alt), true);
+ std::fill(std::begin(m_gate_active), std::end(m_gate_active), true);
+ std::fill(std::begin(m_tc), std::end(m_tc), false);
+ std::fill(std::begin(m_toggle), std::end(m_toggle), false);
+
+ // Set up frequency timers
+ for (int f = 0; f < 5; f++)
+ {
+ m_freq_timer[f] = timer_alloc(TIMER_F1 + f);
+ m_freq_timer_selected[f] = (f == 0) ? (m_fout_cb.isnull() ? 0x1e : 0x1f) : 0;
+ m_freq_timer_cycle[f] = 0;
+ }
+
+ // Save device state
+ save_item(NAME(m_dpr));
+ save_item(NAME(m_mmr));
+ save_item(NAME(m_status));
+ save_item(NAME(m_write_prefetch));
+ save_item(NAME(m_count));
+ save_item(NAME(m_counter_load));
+ save_item(NAME(m_counter_hold));
+ save_item(NAME(m_counter_mode));
+ save_item(NAME(m_alarm));
+ save_item(NAME(m_counter_armed));
+ save_item(NAME(m_counter_running));
+ save_item(NAME(m_alternate_count));
+ save_item(NAME(m_src));
+ save_item(NAME(m_gate));
+ save_item(NAME(m_gate_alt));
+ save_item(NAME(m_gate_active));
+ save_item(NAME(m_tc));
+ save_item(NAME(m_toggle));
+ save_item(NAME(m_f));
+ save_item(NAME(m_freq_timer_selected));
+ save_item(NAME(m_freq_timer_cycle));
+ save_item(NAME(m_fout));
+ save_item(NAME(m_fout_counter));
+
+ // Synchronize clearing of OUT n
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(am9513_device::clear_outputs), this));
+}
+
+
+//-------------------------------------------------
+// clear_outputs - output initial clear state
+// (delayed until all devices have started)
+//-------------------------------------------------
+
+TIMER_CALLBACK_MEMBER(am9513_device::clear_outputs)
+{
+ for (int c = 0; c < 5; c++)
+ m_out_cb[c](0);
+}
+
+
+//-------------------------------------------------
+// master_reset - software-controlled reset
+//-------------------------------------------------
+
+void am9513_device::master_reset()
+{
+ LOGMASKED(LOG_MODE, "Master reset\n");
+
+ // Clear master mode register to all zeroes
+ m_mmr = 0;
+
+ // Enable prefetch for write
+ m_write_prefetch = true;
+
+ // Clear TC state
+ std::fill(std::begin(m_tc), std::end(m_tc), false);
+ std::fill(std::begin(m_toggle), std::end(m_toggle), false);
+
+ // Initialize counter mode registers
+ for (int c = 0; c < 5; c++)
+ set_counter_mode(c, 0x0b00);
+}
+
+
+//**************************************************************************
+// FREQUENCY SCALER
+//**************************************************************************
+
+//-------------------------------------------------
+// init_freq_timer - set up one of the F1-F5
+// frequency timers
+//-------------------------------------------------
+
+void am9513_device::init_freq_timer(int f)
+{
+ u32 scale = 1;
+ for (int n = 0; n < f; n++)
+ scale *= BIT(m_mmr, 15) ? 10 : 16;
+
+ attotime freq = clocks_to_attotime(scale);
+ if (m_freq_timer_cycle[f] == 0)
+ m_freq_timer[f]->adjust(freq, 0, freq);
+ else
+ m_freq_timer[f]->adjust(freq / 2, 0, freq / 2);
+ m_freq_timer[f]->enable(m_freq_timer_selected[f] != 0);
+
+ LOGMASKED(LOG_GENERAL, "F%d = %f Hz (%s cycle emulation)\n", f + 1, double(clock()) / scale,
+ m_freq_timer_selected[f] == 0 ? "no" : m_freq_timer_cycle[f] == 0 ? "partial" : "full");
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called when the
+// device clock is altered in any way
+//-------------------------------------------------
+
+void am9513_device::device_clock_changed()
+{
+ for (int f = 0; f < 5; f++)
+ init_freq_timer(f);
+}
+
+
+//-------------------------------------------------
+// device_timer - called whenever a device timer
+// fires
+//-------------------------------------------------
+
+void am9513_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ assert(id >= TIMER_F1 && id <= TIMER_F5);
+
+ int cycle = m_freq_timer_cycle[id - TIMER_F1] == 0 ? 2 : 1;
+ while (cycle-- > 0)
+ {
+ m_f ^= 1 << (id - TIMER_F1);
+ bool level = BIT(m_f, id - TIMER_F1);
+
+ int source = id - TIMER_F1 + 11;
+ for (int c = 0; c < 5; c++)
+ {
+ if ((m_counter_mode[c] & 0x0f00) >> 8 == source && BIT(m_counter_mode[c], 12) == !level)
+ count_edge(c);
+ }
+
+ // FOUT Source = Fn
+ if ((m_mmr & 0x00f0) >> 4 == source || ((m_mmr & 0x00f0) == 0 && id == TIMER_F1))
+ fout_tick();
+ }
+}
+
+
+//-------------------------------------------------
+// select_freq_timer - keep track of which of the
+// F1-F5 timers are actually in use
+//-------------------------------------------------
+
+void am9513_device::select_freq_timer(int f, int c, bool selected, bool cycle)
+{
+ assert(f >= 0 && f < 5);
+
+ if (selected)
+ m_freq_timer_selected[f] |= 1 << c;
+ else
+ m_freq_timer_selected[f] &= ~(1 << c);
+
+ if (selected && cycle)
+ m_freq_timer_cycle[f] |= 1 << c;
+ else
+ m_freq_timer_cycle[f] &= ~(1 << c);
+
+ init_freq_timer(f);
+}
+
+
+//**************************************************************************
+// MASTER MODE SELECTION
+//**************************************************************************
+
+//-------------------------------------------------
+// set_master_mode - master mode control
+//-------------------------------------------------
+
+void am9513_device::set_master_mode(u16 data)
+{
+ u16 old_mmr = m_mmr;
+ m_mmr = data;
+
+ // Time-of-Day options (TODO: not implemented yet)
+ if ((m_mmr & 0x0003) != (old_mmr & 0x0003))
+ {
+ switch (m_mmr & 0x0003)
+ {
+ case 0: LOGMASKED(LOG_MODE, "TOD Disabled\n"); break;
+ case 1: LOGMASKED(LOG_MODE, "TOD Enabled (/5 Input)\n"); break;
+ case 2: LOGMASKED(LOG_MODE, "TOD Enabled (/6 Input)\n"); break;
+ case 3: LOGMASKED(LOG_MODE, "TOD Enabled (/10 Input)\n"); break;
+ }
+ }
+
+ // Comparator enable
+ if (BIT(m_mmr, 2) != BIT(old_mmr, 2))
+ LOGMASKED(LOG_MODE, "Compare 1 %s\n", BIT(m_mmr, 2) ? "Enabled" : "Disabled");
+ if (BIT(m_mmr, 3) != BIT(old_mmr, 3))
+ LOGMASKED(LOG_MODE, "Compare 2 %s\n", BIT(m_mmr, 3) ? "Enabled" : "Disabled");
+
+ // FOUT Source/Divider selection
+ if ((m_mmr & 0x0ff0) != (old_mmr & 0x0ff0))
+ {
+ int source = (m_mmr >> 4) & 15;
+ int divider = (m_mmr >> 8) & 15;
+ if (source == 0)
+ source = 11;
+ if (divider == 0)
+ divider = 16;
+
+ int old_source = (old_mmr >> 4) & 15;
+ if (old_source == 0)
+ old_source = 11;
+ if (old_source >= 11 && old_source <= 15 && source != old_source)
+ select_freq_timer(old_source - 11, 0, false, false);
+
+ if (source >= 11 && source <= 15)
+ {
+ LOGMASKED(LOG_MODE, "FOUT = F%d / %d\n", source - 10, divider);
+ select_freq_timer(source - 11, 0, !m_fout_cb.isnull(), BIT(divider, 0));
+ }
+ else if (source >= 6 && source <= 10)
+ LOGMASKED(LOG_MODE, "FOUT = GATE %d / %d\n", source - 5, divider);
+ else
+ LOGMASKED(LOG_MODE, "FOUT = SRC %d / %d\n", source, divider);
+ }
+
+ // MM12: FOUT gate control
+ if (BIT(m_mmr, 12) != BIT(old_mmr, 12))
+ LOGMASKED(LOG_MODE, "FOUT: Gate %s\n", BIT(m_mmr, 12) ? "Off" : "On");
+
+ // MM13: Data bus width
+ if (BIT(m_mmr, 13) != BIT(old_mmr, 13))
+ LOGMASKED(LOG_MODE, "Data Bus Width = %d-Bit\n", BIT(m_mmr, 13) ? 16 : 8);
+
+ // MM14: Data pointer sequencing
+ if (BIT(m_mmr, 14) != BIT(old_mmr, 14))
+ LOGMASKED(LOG_MODE, "%s Data Pointer Increment\n", BIT(m_mmr, 14) ? "Disable" : "Enable");
+
+ // MM15: Scaler ratio control
+ if (BIT(m_mmr, 15) != BIT(old_mmr, 15))
+ {
+ LOGMASKED(LOG_MODE, "%s Frequency Division\n", BIT(m_mmr, 15) ? "BCD" : "Binary");
+ for (int f = 1; f < 5; f++)
+ init_freq_timer(f);
+ }
+}
+
+
+//**************************************************************************
+// COUNTER CONTROL
+//**************************************************************************
+
+//-------------------------------------------------
+// counter_is_mode_x - return true if the counter
+// is configured for the special Mode X
+//-------------------------------------------------
+
+bool am9513_device::counter_is_mode_x(int c) const
+{
+ // Am9513A only: CM7-CM5 = 1, CM15-CM13 = EDGE
+ return m_is_am9513a && (m_counter_mode[c] & 0xc0e0) == 0xc0e0;
+}
+
+
+//-------------------------------------------------
+// set_counter_mode - handle counter mode changes
+//-------------------------------------------------
+
+void am9513_device::set_counter_mode(int c, u16 data)
+{
+ if ((data & 0xe0e0) != (m_counter_mode[c] & 0xe0e0))
+ {
+ // Mode selection and gating control
+ int mode = ((data >> 5) & 7) * 3;
+ if ((data & 0xc000) == 0xc000)
+ mode += 2; // Edge gating
+ else if ((data & 0xe000) != 0)
+ mode += 1; // Level gating
+ LOGMASKED(LOG_MODE, "Counter %d: Mode %c selected\n", c + 1, mode + 'A');
+ }
+
+ if ((data & 0x1f00) != (m_counter_mode[c] & 0x1f00))
+ {
+ // Source selection and edge control
+ int source = (data >> 8) & 15;
+ int old_source = (m_counter_mode[c] >> 8) & 15;
+ if (old_source >= 11 && old_source <= 15 && source != old_source)
+ select_freq_timer(old_source - 11, c + 1, false, false);
+ if (source >= 11 && source <= 15)
+ {
+ select_freq_timer(source - 11, c + 1, true, BIT(data, 12));
+ LOGMASKED(LOG_MODE, "Counter %d: Count on %s edge of F%d\n", c + 1, BIT(data, 12) ? "falling" : "rising", source - 10);
+ }
+ else if (source >= 6 && source <= 10)
+ LOGMASKED(LOG_MODE, "Counter %d: Count on %s edge of GATE %d\n", c + 1, BIT(data, 12) ? "falling" : "rising", source - 5);
+ else if (source == 0)
+ LOGMASKED(LOG_MODE, "Counter %d: Count on %s edge of TC%d\n", c + 1, BIT(data, 12) ? "falling" : "rising", c - 1);
+ else
+ LOGMASKED(LOG_MODE, "Counter %d: Count on %s edge of SRC %d\n", c + 1, BIT(data, 12) ? "falling" : "rising", source);
+ }
+
+ if ((data & 0x0018) != (m_counter_mode[c] & 0x0018))
+ LOGMASKED(LOG_MODE, "Counter %d: %s %s count\n", c + 1, BIT(data, 4) ? "BCD" : "Binary", BIT(data, 3) ? "up" : "down");
+
+ // Output forms
+ if ((data & 0x0007) != (m_counter_mode[c] & 0x0007))
+ {
+ switch (data & 0x0007)
+ {
+ case 0x0000:
+ LOGMASKED(LOG_MODE, "Counter %d: Output low (inactive)\n", c + 1);
+ set_output(c, false);
+ break;
+
+ case 0x0004:
+ LOGMASKED(LOG_MODE, "Counter %d: Output high impedance (inactive)\n", c + 1);
+ break;
+
+ case 0x0001:
+ if (c < 2 && BIT(m_mmr, c + 2))
+ set_output(c, m_count[c] == m_alarm[c]);
+ else
+ {
+ LOGMASKED(LOG_MODE, "Counter %d: Output active high TC pulse\n", c + 1);
+ set_output(c, m_tc[c]);
+ }
+ break;
+
+ case 0x0005:
+ if (c < 2 && BIT(m_mmr, c + 2))
+ set_output(c, m_count[c] != m_alarm[c]);
+ else
+ {
+ LOGMASKED(LOG_MODE, "Counter %d: Output active low TC pulse\n", c + 1);
+ set_output(c, !m_tc[c]);
+ }
+ break;
+
+ case 0x0002:
+ if (c < 2 && BIT(m_mmr, c + 2))
+ set_output(c, m_count[c] == m_alarm[c]);
+ else
+ {
+ LOGMASKED(LOG_MODE, "Counter %d: Output toggle on TC\n", c + 1);
+ set_output(c, m_toggle[c]);
+ }
+ break;
+
+ default:
+ LOGMASKED(LOG_MODE, "Counter %d: Output mode %d (illegal)\n", c + 1, data & 0x0007);
+ break;
+ }
+ }
+
+ m_counter_mode[c] = data;
+}
+
+
+//-------------------------------------------------
+// arm_counter - arm a particular counter
+//-------------------------------------------------
+
+void am9513_device::arm_counter(int c)
+{
+ if (!m_counter_armed[c])
+ {
+ LOGMASKED(LOG_GENERAL, "Counter %d: Arming counter\n", c + 1);
+ m_counter_armed[c] = true;
+
+ // Count starts upon first active gate edge after arming in Modes C, F, I, L, O, R, X
+ m_counter_running[c] = (m_counter_mode[c] & 0xc000) != 0xc000;
+ }
+ m_alternate_count[c] = false;
+}
+
+
+//-------------------------------------------------
+// disarm_counter - disarm a particular counter
+//-------------------------------------------------
+
+void am9513_device::disarm_counter(int c)
+{
+ if (m_counter_armed[c])
+ {
+ LOGMASKED(LOG_GENERAL, "Counter %d: Disarming counter\n", c + 1);
+ m_counter_armed[c] = false;
+ m_counter_running[c] = false;
+ }
+}
+
+
+//-------------------------------------------------
+// save_counter - capture the current count
+//-------------------------------------------------
+
+void am9513_device::save_counter(int c)
+{
+ m_counter_hold[c] = m_count[c];
+ LOGMASKED(LOG_GENERAL, "Counter %d: Count %u saved\n", c + 1, m_count[c]);
+}
+
+
+//**************************************************************************
+// OUTPUT CONTROL
+//**************************************************************************
+
+
+//-------------------------------------------------
+// set_output - set one of the 5 main outputs
+//-------------------------------------------------
+
+void am9513_device::set_output(int c, bool state)
+{
+ // SR1-SR5 track the output state
+ if (BIT(m_status, c + 1) == state)
+ return;
+
+ if (state)
+ m_status |= 1 << (c + 1);
+ else
+ m_status &= ~(1 << (c + 1));
+ m_out_cb[c](state);
+}
+
+
+//-------------------------------------------------
+// set_toggle - output level control for TC
+// toggle mode
+//-------------------------------------------------
+
+void am9513_device::set_toggle(int c, bool state)
+{
+ m_toggle[c] = state;
+ if ((m_counter_mode[c] & 0x0007) == 0x0002)
+ set_output(c, state);
+}
+
+
+//-------------------------------------------------
+// set_tc - register terminal count status
+//-------------------------------------------------
+
+void am9513_device::set_tc(int c, bool state)
+{
+ m_tc[c] = state;
+
+ // TC output is disabled when comparator is enabled
+ if (c >= 2 || !BIT(m_mmr, 2 + c))
+ {
+ switch (m_counter_mode[c] & 0x0007)
+ {
+ case 0x0001:
+ // Active high TC pulse
+ set_output(c, state);
+ break;
+ case 0x0002:
+ // TC toggled output
+ if (!state)
+ set_toggle(c, !m_toggle[c]);
+ break;
+ case 0x0005:
+ // Active low TC pulse
+ set_output(c, !state);
+ break;
+ }
+ }
+
+ if (c < 4)
+ {
+ // TC cascading
+ if ((m_counter_mode[c + 1] & 0x1f00) == (state ? 0x0000 : 0x1000))
+ count_edge(c + 1);
+
+ // TC gating
+ if ((m_counter_mode[c + 1] & 0xe000) == 0x2000)
+ gate_count(c + 1, state && (bus_is_16_bit() || m_gate_alt[c + 1]));
+ }
+}
+
+
+//**************************************************************************
+// SOURCE INPUTS
+//**************************************************************************
+
+//-------------------------------------------------
+// write_source - register state changes on SRC
+// input lines
+//-------------------------------------------------
+
+void am9513_device::write_source(int s, bool level)
+{
+ if (level == m_src[s])
+ return;
+
+ m_src[s] = level;
+ LOGMASKED(LOG_INPUT, "Source %d: %s edge\n", s, level ? "Rising" : "Falling");
+
+ for (int c = 0; c < 5; c++)
+ {
+ if ((m_counter_mode[c] & 0x0f00) >> 8 == (s + 1) && BIT(m_counter_mode[c], 12) == !level)
+ count_edge(c);
+ }
+
+ // FOUT Source = SRC n
+ if ((m_mmr & 0x00f0) >> 4 == (s + 1))
+ fout_tick();
+}
+
+
+//**************************************************************************
+// COUNTER OPERATION
+//**************************************************************************
+
+//-------------------------------------------------
+// count_edge - gate and count active edges
+//-------------------------------------------------
+
+void am9513_device::count_edge(int c)
+{
+ // Counting cannot be disabled or gated during TC
+ if (!m_tc[c])
+ {
+ if (!m_counter_running[c])
+ return;
+
+ if (!m_gate_active[c])
+ {
+ // Modes B, E, H, K, N & Q: Count only during active gate level
+ int gating = (m_counter_mode[c] >> 13) & 7;
+ if (gating >= 1 && gating <= 5)
+ return;
+ }
+ }
+
+ step_counter(c, false);
+}
+
+
+//-------------------------------------------------
+// reload_from_hold - return true if the current
+// reload source of the counter is the hold
+// register rather than the load register
+//-------------------------------------------------
+
+bool am9513_device::reload_from_hold(int c) const
+{
+ if (counter_is_mode_x(c))
+ return false;
+
+ // Modes S & V: Reload from hold register when gate is high
+ else if ((m_counter_mode[c] & 0x00c0) == 0x00c0)
+ return m_gate_active[c];
+
+ // Modes G, H, I, J, K & L: Alternating reload
+ else if ((m_counter_mode[c] & 0x00c0) == 0x0040)
+ return m_alternate_count[c];
+
+ else
+ return false;
+}
+
+
+//-------------------------------------------------
+// step_counter - advance the counter by one step
+// and/or reload it from the specified register
+// N.B. The "load counter" command works by
+// causing an internal step and so can affect TC
+//-------------------------------------------------
+
+void am9513_device::step_counter(int c, bool force_load)
+{
+ if (BIT(m_counter_mode[c], 3))
+ {
+ // CM3 = 1: Count up
+ ++m_count[c];
+
+ // CM4 = 1: BCD adjustment
+ if (BIT(m_counter_mode[c], 4))
+ {
+ if ((m_count[c] & 0x000f) >= 0x000a)
+ m_count[c] += 0x0006;
+ if ((m_count[c] & 0x00f0) >= 0x00a0)
+ m_count[c] += 0x0060;
+ if ((m_count[c] & 0x0f00) >= 0x0a00)
+ m_count[c] += 0x0600;
+ if ((m_count[c] & 0xf000) >= 0xa000)
+ m_count[c] += 0x6000;
+ }
+ }
+ else
+ {
+ // CM3 = 0: Count down
+ --m_count[c];
+
+ // CM4 = 1: BCD adjustment
+ if (BIT(m_counter_mode[c], 4))
+ {
+ if ((m_count[c] & 0x000f) >= 0x000a)
+ m_count[c] -= 0x0006;
+ if ((m_count[c] & 0x00f0) >= 0x00a0)
+ m_count[c] -= 0x0060;
+ if ((m_count[c] & 0x0f00) >= 0x0a00)
+ m_count[c] -= 0x0600;
+ if ((m_count[c] & 0xf000) >= 0xa000)
+ m_count[c] -= 0x6000;
+ }
+ }
+
+ if (m_count[c] == 0)
+ {
+ LOGMASKED(LOG_TC, "Counter %d: Terminal count\n", c + 1);
+ set_tc(c, true);
+ m_count[c] = reload_from_hold(c) ? m_counter_hold[c] : m_counter_load[c];
+ m_alternate_count[c] = !m_alternate_count[c];
+
+ // Modes A, B, C, N & O: disarm counter after counting to TC once
+ if ((m_counter_mode[c] & 0x0060) == 0x0000)
+ disarm_counter(c);
+
+ // Modes G, H, I & S: disarm counter after counting to TC twice
+ if ((m_counter_mode[c] & 0x0060) == 0x0040 && !m_alternate_count[c])
+ disarm_counter(c);
+
+ // Modes C, F, O, R & X: retriggering required after first TC
+ // Modes I & L: retriggering required after second TC
+ if ((m_counter_mode[c] & 0xc000) == 0xc000 && ((m_counter_mode[c] & 0x00c0) != 0x0040 || !m_alternate_count[c]))
+ m_counter_running[c] = false;
+ }
+ else if (m_tc[c])
+ {
+ set_tc(c, false);
+ if (force_load)
+ m_count[c] = reload_from_hold(c) ? m_counter_hold[c] : m_counter_load[c];
+ }
+
+ if (force_load)
+ LOGMASKED(LOG_GENERAL, "Counter %d: %u loaded\n", c + 1, m_count[c]);
+
+ // Active comparators
+ if (c < 2 && BIT(m_mmr, c + 2))
+ {
+ if (BIT(m_counter_mode[c], 2))
+ set_output(c, m_count[c] == m_alarm[c]);
+ else
+ set_output(c, m_count[c] != m_alarm[c]);
+ }
+}
+
+
+//**************************************************************************
+// HARDWARE GATING
+//**************************************************************************
+
+//-------------------------------------------------
+// gate_count - track active gate state for each
+// counter
+//-------------------------------------------------
+
+void am9513_device::gate_count(int c, bool state)
+{
+ // Active low gating when CM15 = CM13 = 1
+ if ((m_counter_mode[c] & 0xa000) == 0xa000)
+ state = !state;
+
+ if (m_gate_active[c] == state)
+ return;
+
+ m_gate_active[c] = state;
+ LOGMASKED(LOG_INPUT, "Counter %d: Gate %sactive\n", c + 1, state ? "" : "in");
+
+ // Active gate edge handling for armed counters
+ if (state && m_counter_armed[c])
+ {
+ // Mode X: transfer counter into Hold register but continue counting
+ if (counter_is_mode_x(c))
+ m_counter_hold[c] = m_count[c];
+
+ // Modes N, O, Q, R: hardware retriggering
+ if ((m_counter_mode[c] & 0x00c0) == 0x0080)
+ {
+ m_counter_hold[c] = m_count[c];
+ m_count[c] = m_counter_load[c];
+ }
+
+ // Modes C, F, I, L, O, R, X: start count on active gate edge
+ if ((m_counter_mode[c] & 0xc000) == 0xc000)
+ m_counter_running[c] = true;
+ }
+}
+
+
+//-------------------------------------------------
+// write_gate - register state changes on the
+// GATE n inputs
+//-------------------------------------------------
+
+void am9513_device::write_gate(int g, bool level)
+{
+ if (level == m_gate[g])
+ return;
+
+ m_gate[g] = level;
+
+ for (int c = 0; c < 5; c++)
+ {
+ if ((m_counter_mode[c] & 0x0f00) >> 8 == (g + 6) && BIT(m_counter_mode[c], 12) == !level)
+ count_edge(c);
+ }
+
+ if (BIT(m_counter_mode[g], 15))
+ gate_count(g, level && (bus_is_16_bit() || m_gate_alt[g]));
+ if (g > 0 && (m_counter_mode[g - 1] & 0xe000) == 0x4000)
+ gate_count(g - 1, level && (bus_is_16_bit() || m_gate_alt[g - 1]));
+ if (g < 4 && (m_counter_mode[g + 1] & 0xe000) == 0x6000)
+ gate_count(g + 1, level && (bus_is_16_bit() || m_gate_alt[g + 1]));
+
+ // FOUT Source = GATE n
+ if ((m_mmr & 0x00f0) >> 4 == (g + 6))
+ fout_tick();
+}
+
+
+//-------------------------------------------------
+// write_gate_alt - register state on GATE nA
+// inputs (only available in 8-bit mode)
+//-------------------------------------------------
+
+void am9513_device::write_gate_alt(int c, bool level)
+{
+ if (bus_is_16_bit())
+ {
+ logerror("Gate %dA written when configured as DB%d\n", c + 1, c + 8);
+ return;
+ }
+
+ m_gate_alt[c] = level;
+
+ switch (m_counter_mode[c] & 0xe000)
+ {
+ case 0x2000:
+ if (c > 0)
+ gate_count(c, level && m_tc[c - 1]);
+ break;
+
+ case 0x4000:
+ if (c < 4)
+ gate_count(c + 1, level && m_gate[c + 1]);
+ break;
+
+ case 0x6000:
+ if (c > 0)
+ gate_count(c - 1, level && m_gate[c - 1]);
+ break;
+
+ case 0x8000:
+ case 0xa000:
+ case 0xc000:
+ case 0xe000:
+ gate_count(c, level && m_gate[c]);
+ break;
+ }
+}
+
+
+//**************************************************************************
+// DATA PORT MUX
+//**************************************************************************
+
+//-------------------------------------------------
+// describe_register - diagnostic helper
+//-------------------------------------------------
+
+std::string am9513_device::describe_register() const
+{
+ switch (m_dpr)
+ {
+ case 0x17:
+ return std::string("Master Mode");
+ case 0x1f:
+ return std::string("Status");
+ case 0x07:
+ case 0x0f:
+ return string_format("Counter %d Alarm", BIT(m_dpr, 3) ? 2 : 1);
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ return string_format("Counter %d Mode", m_dpr & 7);
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0c:
+ case 0x0d:
+ return string_format("Counter %d Load", m_dpr & 7);
+ case 0x11: case 0x19:
+ case 0x12: case 0x1a:
+ case 0x13: case 0x1b:
+ case 0x14: case 0x1c:
+ case 0x15: case 0x1d:
+ return string_format("Counter %d Hold", m_dpr & 7);
+ default:
+ return string_format("Illegal %02X", m_dpr);
+ }
+}
+
+
+//-------------------------------------------------
+// internal_read - read from register selected by
+// the data pointer
+//-------------------------------------------------
+
+u16 am9513_device::internal_read() const
+{
+ switch (m_dpr)
+ {
+ case 0x17: // Master mode register
+ return m_mmr;
+ case 0x1f: // Status register
+ return m_status | 0xff00;
+ case 0x07: // Alarm 1 register
+ case 0x0f: // Alarm 2 register
+ return m_alarm[BIT(m_dpr, 3)];
+ case 0x01: // Counter 1 mode register
+ case 0x02: // Counter 2 mode register
+ case 0x03: // Counter 3 mode register
+ case 0x04: // Counter 4 mode register
+ case 0x05: // Counter 5 mode register
+ return m_counter_mode[(m_dpr & 7) - 1];
+ case 0x09: // Counter 1 load register
+ case 0x0a: // Counter 2 load register
+ case 0x0b: // Counter 3 load register
+ case 0x0c: // Counter 4 load register
+ case 0x0d: // Counter 5 load register
+ return m_counter_load[(m_dpr & 7) - 1];
+ case 0x11: case 0x19: // Counter 1 hold register
+ case 0x12: case 0x1a: // Counter 2 hold register
+ case 0x13: case 0x1b: // Counter 3 hold register
+ case 0x14: case 0x1c: // Counter 4 hold register
+ case 0x15: case 0x1d: // Counter 5 hold register
+ return m_counter_hold[(m_dpr & 7) - 1];
+ default: // Invalid register
+ return 0xffff;
+ }
+}
+
+
+//-------------------------------------------------
+// internal_read - write to register selected by
+// the data pointer
+//-------------------------------------------------
+
+void am9513_device::internal_write(u16 data)
+{
+ switch (m_dpr)
+ {
+ case 0x17: // Master mode register
+ set_master_mode(data);
+ break;
+ case 0x1f: // Status register (read only?)
+ logerror("Writing %04X to status register\n", data);
+ break;
+ case 0x07: // Alarm 1 register
+ case 0x0f: // Alarm 2 register
+ LOGMASKED(LOG_GENERAL, "Counter %d: Alarm = %u\n", BIT(m_dpr, 3) ? 2 : 1, data);
+ m_alarm[BIT(m_dpr, 3)] = data;
+ break;
+ case 0x01: // Counter 1 mode register
+ case 0x02: // Counter 2 mode register
+ case 0x03: // Counter 3 mode register
+ case 0x04: // Counter 4 mode register
+ case 0x05: // Counter 5 mode register
+ LOGMASKED(LOG_GENERAL, "Counter %d: Mode = %04X\n", m_dpr & 7, data);
+ set_counter_mode((m_dpr & 7) - 1, data);
+ break;
+ case 0x09: // Counter 1 load register
+ case 0x0a: // Counter 2 load register
+ case 0x0b: // Counter 3 load register
+ case 0x0c: // Counter 4 load register
+ case 0x0d: // Counter 5 load register
+ LOGMASKED(LOG_GENERAL, "Counter %d: Load = %u\n", m_dpr & 7, data);
+ m_counter_load[(m_dpr & 7) - 1] = data;
+ break;
+ case 0x11: case 0x19: // Counter 1 hold register
+ case 0x12: case 0x1a: // Counter 2 hold register
+ case 0x13: case 0x1b: // Counter 3 hold register
+ case 0x14: case 0x1c: // Counter 4 hold register
+ case 0x15: case 0x1d: // Counter 5 hold register
+ LOGMASKED(LOG_GENERAL, "Counter %d: Hold = %u\n", m_dpr & 7, data);
+ m_counter_hold[(m_dpr & 7) - 1] = data;
+ break;
+ default: // Invalid register
+ logerror("Writing %04X to register %02X\n", data, m_dpr);
+ break;
+ }
+}
+
+
+//**************************************************************************
+// PREFETCH LATCH
+//**************************************************************************
+
+//-------------------------------------------------
+// advance_dpr - advance the data pointer through
+// one of four cycles after a data read or write
+//-------------------------------------------------
+
+void am9513_device::advance_dpr()
+{
+ if (BIT(m_mmr, 14) || machine().side_effect_disabled())
+ return;
+
+ if (bus_is_16_bit() || !BIT(m_status, 0))
+ {
+ // Least significant byte (or 16-bit word) transferred next
+ m_status |= 0x01;
+ }
+ else
+ {
+ // Most significant byte transferred next
+ m_status &= 0xfe;
+ return;
+ }
+
+ // Cycle within group
+ switch (m_dpr & 0x18)
+ {
+ case 0x00: // Mode/Alarm 1 -> Load/Alarm 2
+ case 0x08: // Load/Alarm 2 -> Hold/Master Mode
+ m_dpr += 0x08;
+ return;
+ case 0x10: // Hold/Master Mode -> Mode (next counter)/Alarm 1
+ m_dpr -= 0x10;
+ break;
+ case 0x18:
+ break;
+ }
+
+ // Cycle through groups
+ switch (m_dpr & 0x07)
+ {
+ case 0x05: // Counter 5 -> Counter 1
+ m_dpr -= 0x04;
+ break;
+ case 0x07: // In control group/status cycle
+ break;
+ default:
+ m_dpr += 0x01;
+ break;
+ }
+}
+
+
+//**************************************************************************
+// CONTROL PORT
+//**************************************************************************
+
+//-------------------------------------------------
+// command_write - decode writes to control
+// register
+//-------------------------------------------------
+
+void am9513_device::command_write(u8 data)
+{
+ switch (data & 0xe0)
+ {
+ case 0x00:
+ if ((data & 0x07) == 0x00 || (data & 0x07) == 0x06)
+ {
+ logerror("Invalid register selected: %02X\n", data);
+ break;
+ }
+
+ // Load data pointer
+ m_dpr = data;
+
+ // Automatic prefetch
+ m_status |= 0x01;
+ break;
+
+ case 0x20: // Arm
+ case 0x40: // Load
+ case 0x60: // Load and Arm
+ for (int c = 0; c < 5; c++)
+ {
+ if (BIT(data, c))
+ {
+ if (BIT(data, 6))
+ step_counter(c, true);
+ if (BIT(data, 5))
+ arm_counter(c);
+ }
+ }
+ break;
+
+ case 0x80: // Disarm and Save
+ case 0xa0: // Save
+ case 0xc0: // Disarm
+ for (int c = 0; c < 5; c++)
+ {
+ if (BIT(data, c))
+ {
+ if (!BIT(data, 5))
+ disarm_counter(c);
+ if (!BIT(data, 6))
+ save_counter(c);
+ }
+ }
+ break;
+
+ default:
+ switch (data)
+ {
+ case 0xe0: case 0xe8: // Clear/set MM14 (Enable/Disable data pointer sequencing)
+ LOGMASKED(LOG_MODE, "%s Data Pointer Increment\n", BIT(data, 3) ? "Disable" : "Enable");
+ m_mmr = ((m_mmr & ~(1 << 14)) | BIT(data, 3) << 14);
+ break;
+ case 0xe1: case 0xe9: // Clear/set toggle out for counter 1
+ case 0xe2: case 0xea: // Clear/set toggle out for counter 2
+ case 0xe3: case 0xeb: // Clear/set toggle out for counter 3
+ case 0xe4: case 0xec: // Clear/set toggle out for counter 4
+ case 0xe5: case 0xed: // Clear/set toggle out for counter 5
+ set_toggle((data & 7) - 1, BIT(data, 3));
+ break;
+ case 0xe6: case 0xee: // Clear/set MM12 (FOUT gate on/FOUT gate off)
+ LOGMASKED(LOG_MODE, "FOUT: Gate %s\n", BIT(data, 3) ? "Off" : "On");
+ m_mmr = ((m_mmr & ~(1 << 12)) | BIT(data, 3) << 12);
+ break;
+ case 0xe7: case 0xef: // Clear/set MM13 (8-bit bus/16-bit bus)
+ m_mmr = ((m_mmr & ~(1 << 13)) | BIT(data, 3) << 13);
+ break;
+ case 0xf1: // Step counter 1
+ case 0xf2: // Step counter 2
+ case 0xf3: // Step counter 3
+ case 0xf4: // Step counter 4
+ case 0xf5: // Step counter 5
+ step_counter((data & 7) - 1, false);
+ break;
+ case 0xff: // Master reset
+ master_reset();
+ break;
+ case 0xf8: case 0xf9: // Enable/disable prefetch for write (Am9513A only)
+ if (m_is_am9513a)
+ {
+ LOGMASKED(LOG_MODE, "Prefetch %s for write operations\n", BIT(data, 0) ? "disabled" : "enabled");
+ m_write_prefetch = !BIT(data, 0);
+ break;
+ }
+ // else fall through
+ default:
+ logerror("Invalid command: %02X\n", data);
+ break;
+ }
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// status_read - return the status register
+//-------------------------------------------------
+
+u8 am9513_device::status_read() const
+{
+ return m_status;
+}
+
+
+//**************************************************************************
+// DATA BUS MUX
+//**************************************************************************
+
+//-------------------------------------------------
+// bus_is_16_bit - determine whether or not the
+// device is configured for a 16-bit bus
+//-------------------------------------------------
+
+bool am9513_device::bus_is_16_bit() const
+{
+ return BIT(m_mmr, 13);
+}
+
+
+//-------------------------------------------------
+// data_read - generic data read access handler
+//-------------------------------------------------
+
+u16 am9513_device::data_read()
+{
+ u16 result = internal_read();
+ if (!bus_is_16_bit() && !BIT(m_status, 0))
+ result = (result >> 8) | 0xff00;
+
+ // update the data pointer
+ advance_dpr();
+ return result;
+}
+
+
+//-------------------------------------------------
+// data_write - generic data write access handler
+//-------------------------------------------------
+
+void am9513_device::data_write(u16 data)
+{
+ if (!bus_is_16_bit())
+ {
+ data &= 0x00ff;
+ if (BIT(m_status, 0))
+ data |= internal_read() & 0xff00;
+ else
+ data = (data << 8) | (internal_read() & 0x00ff);
+ }
+ internal_write(data);
+
+ // update the data pointer
+ if (m_write_prefetch)
+ advance_dpr();
+}
+
+
+//-------------------------------------------------
+// read8 - 8-bit read access
+//-------------------------------------------------
+
+READ8_MEMBER(am9513_device::read8)
+{
+ if (BIT(offset, 0))
+ return status_read();
+ else
+ return data_read() & 0x00ff;
+}
+
+
+//-------------------------------------------------
+// write8 - 8-bit write access
+//-------------------------------------------------
+
+WRITE8_MEMBER(am9513_device::write8)
+{
+ if (BIT(offset, 0))
+ {
+ if (data == 0xef)
+ logerror("16-bit data bus selected with 8-bit write\n");
+ command_write(data);
+ }
+ else
+ data_write(data | 0xff00);
+}
+
+
+//-------------------------------------------------
+// read16 - 16-bit read access
+//-------------------------------------------------
+
+READ16_MEMBER(am9513_device::read16)
+{
+ if (BIT(offset, 0))
+ return status_read() | 0xff00;
+ else
+ {
+ if (!bus_is_16_bit())
+ logerror("16-bit data read in 8-bit bus mode\n");
+ return data_read();
+ }
+}
+
+
+//-------------------------------------------------
+// write16 - 16-bit write access
+//-------------------------------------------------
+
+WRITE16_MEMBER(am9513_device::write16)
+{
+ if ((!bus_is_16_bit() || BIT(offset, 0)) && (data & 0xff00) != 0xff00)
+ logerror("Errant write of %02X to upper byte of %s register in %d-bit bus mode\n",
+ (data & 0xff00) >> 8,
+ BIT(offset, 0) ? "control" : "data",
+ bus_is_16_bit() ? 16 : 8);
+
+ if (BIT(offset, 0))
+ {
+ if ((data & 0x00ff) == 0x00e7)
+ logerror("8-bit data bus selected with 16-bit write\n");
+ else if ((data & 0x00ff) == 0x00ef && !bus_is_16_bit())
+ logerror("16-bit data bus selected\n");
+
+ command_write(data & 0x00ff);
+ }
+ else
+ data_write(data);
+}
+
+
+//**************************************************************************
+// DIVIDED FREQUENCY OUTPUT
+//**************************************************************************
+
+void am9513_device::fout_tick()
+{
+ --m_fout_counter;
+ if (m_fout_counter > 0)
+ return;
+
+ // Toggle the output
+ m_fout = !m_fout;
+
+ // Check whether the FOUT gate is on
+ if (!BIT(m_mmr, 12) && !m_fout_cb.isnull())
+ m_fout_cb(m_fout);
+
+ // Reload the counter
+ m_fout_counter = (m_mmr >> 8) & 15;
+ if (m_fout_counter == 0)
+ m_fout_counter = 16;
+}
diff --git a/src/devices/machine/am9513.h b/src/devices/machine/am9513.h
new file mode 100644
index 00000000000..1c3cc587c53
--- /dev/null
+++ b/src/devices/machine/am9513.h
@@ -0,0 +1,209 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ Am9513A/Am9513 System Timing Controller
+
+***********************************************************************
+ _____ _____
+ VCC 1 |* \_/ | 40 OUT 3
+ OUT 2 2 | | 39 GATE 2
+ OUT 1 3 | | 38 OUT 4
+ GATE 1 4 | | 37 OUT 5
+ X1 5 | | 36 GATE 3
+ X2 6 | | 35 GATE 4
+ FOUT 7 | | 34 GATE 5
+ C/_D 8 | | 33 SOURCE 1
+ _WR 9 | | 32 SOURCE 2
+ _CS 10 | Am9513A | 31 SOURCE 3
+ _RD 11 | Am9513 | 30 SOURCE 4
+ DB0 12 | | 29 SOURCE 5
+ DB1 13 | | 28 DB15
+ DB2 14 | | 27 DB14
+ DB3 15 | | 26 DB13
+ DB4 16 | | 25 DB12/GATE 5A
+ DB5 17 | | 24 DB11/GATE 4A
+ DB6 18 | | 23 DB10/GATE 3A
+ DB7 19 | | 22 DB9/GATE 2A
+ GATE 1A/DB8 20 |_____________| 21 VSS
+
+**********************************************************************/
+
+#ifndef MAME_MACHINE_AM9513_H
+#define MAME_MACHINE_AM9513_H
+
+#pragma once
+
+//**************************************************************************
+// CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_AM9513_OUT1_CALLBACK(_devcb) \
+ devcb = &am9513_device::set_out_cb(*device, 0, DEVCB_##_devcb);
+#define MCFG_AM9513_OUT2_CALLBACK(_devcb) \
+ devcb = &am9513_device::set_out_cb(*device, 1, DEVCB_##_devcb);
+#define MCFG_AM9513_OUT3_CALLBACK(_devcb) \
+ devcb = &am9513_device::set_out_cb(*device, 2, DEVCB_##_devcb);
+#define MCFG_AM9513_OUT4_CALLBACK(_devcb) \
+ devcb = &am9513_device::set_out_cb(*device, 3, DEVCB_##_devcb);
+#define MCFG_AM9513_OUT5_CALLBACK(_devcb) \
+ devcb = &am9513_device::set_out_cb(*device, 4, DEVCB_##_devcb);
+#define MCFG_AM9513_FOUT_CALLBACK(_devcb) \
+ devcb = &am9513_device::set_fout_cb(*device, DEVCB_##_devcb);
+
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> am9513_device
+
+class am9513_device : public device_t
+{
+public:
+ // device type constructor
+ am9513_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ // static configuration
+ template<class Object> static devcb_base &set_out_cb(device_t &device, int c, Object &&cb)
+ { assert(c >= 0 && c < 5); return downcast<am9513_device &>(device).m_out_cb[c].set_callback(std::forward<Object>(cb)); }
+ template<class Object> static devcb_base &set_fout_cb(device_t &device, Object &&cb)
+ { return downcast<am9513_device &>(device).m_fout_cb.set_callback(std::forward<Object>(cb)); }
+
+ // 8-bit data bus interface
+ DECLARE_READ8_MEMBER(read8);
+ DECLARE_WRITE8_MEMBER(write8);
+
+ // 16-bit data bus interface
+ DECLARE_READ16_MEMBER(read16);
+ DECLARE_WRITE16_MEMBER(write16);
+
+ // Source N inputs
+ DECLARE_WRITE_LINE_MEMBER(source1_w) { write_source(0, state); }
+ DECLARE_WRITE_LINE_MEMBER(source2_w) { write_source(1, state); }
+ DECLARE_WRITE_LINE_MEMBER(source3_w) { write_source(2, state); }
+ DECLARE_WRITE_LINE_MEMBER(source4_w) { write_source(3, state); }
+ DECLARE_WRITE_LINE_MEMBER(source5_w) { write_source(4, state); }
+
+ // Gate N inputs
+ DECLARE_WRITE_LINE_MEMBER(gate1_w) { write_gate(0, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate2_w) { write_gate(1, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate3_w) { write_gate(2, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate4_w) { write_gate(3, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate5_w) { write_gate(4, state); }
+
+ // Gate N alternate inputs (8-bit mode only; multiplexed with DB8-DB12)
+ DECLARE_WRITE_LINE_MEMBER(gate1a_w) { write_gate_alt(0, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate2a_w) { write_gate_alt(1, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate3a_w) { write_gate_alt(2, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate4a_w) { write_gate_alt(3, state); }
+ DECLARE_WRITE_LINE_MEMBER(gate5a_w) { write_gate_alt(4, state); }
+
+ // diagnostic helper
+ std::string describe_register() const;
+
+protected:
+ // base constructor
+ am9513_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, bool is_am9513a);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_clock_changed() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ // internal helpers
+ TIMER_CALLBACK_MEMBER(clear_outputs);
+ void master_reset();
+ void init_freq_timer(int f);
+ void select_freq_timer(int f, int c, bool selected, bool cycle);
+ void set_master_mode(u16 data);
+ bool counter_is_mode_x(int c) const;
+ void set_counter_mode(int c, u16 data);
+ void arm_counter(int c);
+ void disarm_counter(int c);
+ void save_counter(int c);
+ void set_output(int c, bool state);
+ void set_toggle(int c, bool state);
+ void set_tc(int c, bool state);
+ void write_source(int s, bool level);
+ void count_edge(int c);
+ bool reload_from_hold(int c) const;
+ void step_counter(int c, bool force_load);
+ void gate_count(int c, bool state);
+ void write_gate(int g, bool level);
+ void write_gate_alt(int c, bool level);
+ u16 internal_read() const;
+ void internal_write(u16 data);
+ void advance_dpr();
+ void command_write(u8 data);
+ u8 status_read() const;
+ bool bus_is_16_bit() const;
+ u16 data_read();
+ void data_write(u16 data);
+ void fout_tick();
+
+ // output callbacks
+ devcb_write_line m_out_cb[5];
+ devcb_write_line m_fout_cb;
+
+ const bool m_is_am9513a;
+
+ u8 m_dpr;
+ u16 m_mmr;
+ u8 m_status;
+ bool m_write_prefetch;
+
+ // counter-specific registers
+ u16 m_count[5];
+ u16 m_counter_load[5];
+ u16 m_counter_hold[5];
+ u16 m_counter_mode[5];
+ u16 m_alarm[2];
+ bool m_counter_armed[5];
+ bool m_counter_running[5];
+ bool m_alternate_count[5];
+
+ // input state
+ bool m_src[5];
+ bool m_gate[5];
+ bool m_gate_alt[5];
+ bool m_gate_active[5];
+
+ // internal outputs
+ bool m_tc[5];
+ bool m_toggle[5];
+
+ // frequency timer
+ u8 m_f;
+ enum
+ {
+ TIMER_F1,
+ TIMER_F2,
+ TIMER_F3,
+ TIMER_F4,
+ TIMER_F5
+ };
+ emu_timer *m_freq_timer[5];
+ u8 m_freq_timer_selected[5];
+ u8 m_freq_timer_cycle[5];
+
+ bool m_fout;
+ int m_fout_counter;
+};
+
+// ======================> am9513a_device
+
+class am9513a_device : public am9513_device
+{
+public:
+ // device type constructor
+ am9513a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+};
+
+// device type declarations
+DECLARE_DEVICE_TYPE(AM9513, am9513_device)
+DECLARE_DEVICE_TYPE(AM9513A, am9513a_device)
+
+#endif // MAME_MACHINE_AM9513_H
diff --git a/src/mame/drivers/pm68k.cpp b/src/mame/drivers/pm68k.cpp
index 5a707d94204..be471162a26 100644
--- a/src/mame/drivers/pm68k.cpp
+++ b/src/mame/drivers/pm68k.cpp
@@ -13,7 +13,7 @@ Status: Boots into monitor, some commands work, some freeze.
#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "bus/rs232/rs232.h"
-#include "machine/clock.h"
+#include "machine/am9513.h"
#include "machine/z80sio.h"
@@ -39,6 +39,7 @@ static ADDRESS_MAP_START(pm68k_mem, AS_PROGRAM, 16, pm68k_state)
AM_RANGE(0x000000, 0x1fffff) AM_RAM AM_SHARE("rambase")
AM_RANGE(0x200000, 0x205fff) AM_ROM AM_REGION("roms", 0)
AM_RANGE(0x600000, 0x600007) AM_DEVREADWRITE8("mpsc", i8274_new_device, ba_cd_r, ba_cd_w, 0xff00)
+ AM_RANGE(0x800000, 0x800003) AM_DEVREADWRITE("stc", am9513_device, read16, write16)
ADDRESS_MAP_END
@@ -67,10 +68,10 @@ static MACHINE_CONFIG_START( pm68k )
MCFG_Z80SIO_OUT_DTRB_CB(DEVWRITELINE("rs232b", rs232_port_device, write_dtr))
MCFG_Z80SIO_OUT_RTSB_CB(DEVWRITELINE("rs232b", rs232_port_device, write_rts))
- MCFG_DEVICE_ADD("baudclock", CLOCK, 153600)
- MCFG_CLOCK_SIGNAL_HANDLER(DEVWRITELINE("mpsc", i8274_new_device, rxca_w))
- MCFG_DEVCB_CHAIN_OUTPUT(DEVWRITELINE("mpsc", i8274_new_device, rxcb_w))
+ MCFG_DEVICE_ADD("stc", AM9513, 3993600)
+ MCFG_AM9513_OUT4_CALLBACK(DEVWRITELINE("mpsc", i8274_new_device, rxca_w))
MCFG_DEVCB_CHAIN_OUTPUT(DEVWRITELINE("mpsc", i8274_new_device, txca_w))
+ MCFG_AM9513_OUT5_CALLBACK(DEVWRITELINE("mpsc", i8274_new_device, rxcb_w))
MCFG_DEVCB_CHAIN_OUTPUT(DEVWRITELINE("mpsc", i8274_new_device, txcb_w))
MCFG_RS232_PORT_ADD("rs232a", default_rs232_devices, "terminal")