// 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 to develop Z8000 peripherals. No company is known to have second-sourced the device, however.) Clock source, edge selection, gating and retriggering are programmable for each channel. There is also a frequency divider which can take any of the 15 normal counter inputs and divide it by any number between 1 and 16. 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) #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_tc), std::end(m_tc), false); std::fill(std::begin(m_toggle), std::end(m_toggle), false); // Unused SRC and GATE inputs are typically grounded std::fill(std::begin(m_src), std::end(m_src), false); std::fill(std::begin(m_gate), std::end(m_gate), false); std::fill(std::begin(m_gate_active), std::end(m_gate_active), false); // Alternate gate inputs should be tied high if not used std::fill(std::begin(m_gate_alt), std::end(m_gate_alt), true); // 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() ? 0x3e : 0x3f) : 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 set_master_mode(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, load and hold registers for (int c = 0; c < 5; c++) { set_counter_mode(c, 0x0b00); m_counter_load[c] = 0; m_counter_hold[c] = 0; } } //************************************************************************** // 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 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; } //------------------------------------------------- // compare_count - determine comparator output // for Counter 1 or Counter 2 //------------------------------------------------- bool am9513_device::compare_count(int c) const { assert(c == 0 || c == 1); // TOD special case: Comparator 2 does 32-bit comparison when Comparator 1 is also activated if (c == 1 && BIT(m_mmr, 2) && (m_mmr & 0x0003) != 0) return m_count[0] == m_alarm[0] && m_count[1] == m_alarm[1]; else return m_count[c] == m_alarm[c]; } //------------------------------------------------- // 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)) { // CM15-CM13, CM7-CM5: Mode selection and gating control int mode = ((data >> 5) & 7) * 3; switch (data & 0xe000) { case 0x0000: mode += 'A'; LOGMASKED(LOG_MODE, "Counter %d: Mode %c selected (no gating)\n", c + 1, mode); break; case 0x2000: mode += 'B'; LOGMASKED(LOG_MODE, "Counter %d: Mode %c selected (active high TC%d)\n", c + 1, mode, c); break; case 0x4000: case 0x6000: mode += 'B'; LOGMASKED(LOG_MODE, "Counter %d: Mode %c selected (active high GATE %d)\n", c + 1, mode, BIT(data, 13) ? c + 2 : c); break; case 0x8000: case 0xa000: mode += 'B'; LOGMASKED(LOG_MODE, "Counter %d: Mode %c selected (active %s GATE %d)\n", c + 1, mode, BIT(data, 13) ? "low" : "high", c + 1); break; case 0xc000: case 0xe000: mode += 'C'; LOGMASKED(LOG_MODE, "Counter %d: Mode %c selected (%s edge GATE %d)\n", c + 1, mode, BIT(data, 13) ? "falling" : "rising", c + 1); break; } } if ((data & 0x1f00) != (m_counter_mode[c] & 0x1f00)) { // CM11-CM8: Source selection // CM12: Source 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"); if ((data & 0x0007) != (m_counter_mode[c] & 0x0007)) { // CM2-CM0: Output form 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, compare_count(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, !compare_count(c)); else { LOGMASKED(LOG_MODE, "Counter %d: Output active low TC pulse\n", c + 1); set_output(c, !m_tc[c]); } break; case 0x0002: case 0x0003: // SCP-300F sets this up; why? if (c < 2 && BIT(m_mmr, c + 2)) set_output(c, compare_count(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] & 0x0006) == 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: case 0x0003: // SCP-300F sets this up; why? // TC toggled output if (!state) set_toggle(c, !m_toggle[c]); break; case 0x0005: // Active low TC pulse set_output(c, !state); break; } } // TCn-1 = TC5 for Counter 1 int d = (c + 1) % 5; // TC cascading if ((m_counter_mode[d] & 0x1f00) == (state ? 0x0000 : 0x1000)) count_edge(d); // TC gating if ((m_counter_mode[d] & 0xe000) == 0x2000) gate_count(d, state && (bus_is_16_bit() || m_gate_alt[d])); } //************************************************************************** // 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 + 1, 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 (c == 0 && (m_mmr & 0x0003) == 0x0001) { // TOD: 50Hz if ((m_count[c] & 0x00f0) >= 0x0050) m_count[c] += 0x00b0; } else if (c == 0 && (m_mmr & 0x0003) == 0x0002) { // TOD: 60Hz if ((m_count[c] & 0x00f0) >= 0x0060) m_count[c] += 0x00a0; } else if (c == 1 && (m_mmr & 0x0003) != 0) { // TOD: minutes if ((m_count[c] & 0x00f0) >= 0x0060) m_count[c] += 0x00a0; } else { if ((m_count[c] & 0x00f0) >= 0x00a0) m_count[c] += 0x0060; } if ((m_count[c] & 0x0f00) >= 0x0a00) m_count[c] += 0x0600; if (c == 0 && (m_mmr & 0x0003) != 0) { // TOD: seconds if ((m_count[c] & 0xf000) >= 0x6000) m_count[c] += 0xa000; } else if (c == 1 && (m_mmr & 0x0003) != 0) { // TOD: hours if ((m_count[c] & 0xff00) >= 0x2400) m_count[c] += 0xdc00; } else { 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 (c == 0 && (m_mmr & 0x0003) == 0x0001) { // TOD: 50Hz if ((m_count[c] & 0x00f0) >= 0x0050) m_count[c] -= 0x00b0; } else if (c == 0 && (m_mmr & 0x0003) == 0x0002) { // TOD: 60Hz if ((m_count[c] & 0x00f0) >= 0x0060) m_count[c] -= 0x00a0; } else if (c == 1 && (m_mmr & 0x0003) != 0) { // TOD: minutes if ((m_count[c] & 0x00f0) >= 0x0060) m_count[c] -= 0x00a0; } else { if ((m_count[c] & 0x00f0) >= 0x00a0) m_count[c] -= 0x0060; } if ((m_count[c] & 0x0f00) >= 0x0a00) m_count[c] -= 0x0600; if (c == 0 && (m_mmr & 0x0003) != 0) { // TOD: seconds if ((m_count[c] & 0xf000) >= 0x6000) m_count[c] -= 0xa000; } else if (c == 1 && (m_mmr & 0x0003) != 0) { // TOD: hours if ((m_count[c] & 0xff00) >= 0x2400) m_count[c] -= 0xdc00; } else { if ((m_count[c] & 0xf000) >= 0xa000) m_count[c] -= 0x6000; } } } if (m_count[c] == 0) { 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]; LOGMASKED(LOG_TC, "Counter %d: Terminal count (%u reloaded)\n", c + 1, m_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 { // Drive counter out of TC if (m_tc[c]) set_tc(c, false); // Load if requested if (force_load) { m_count[c] = reload_from_hold(c) ? m_counter_hold[c] : m_counter_load[c]; LOGMASKED(LOG_GENERAL, "Counter %d: %u loaded\n", c + 1, m_count[c]); } } // Active comparators if (c < 2 && BIT(m_mmr, c + 2)) { switch (m_counter_mode[c] & 0x0007) { case 0x0001: case 0x0002: // Active high comparator output set_output(c, compare_count(c)); break; case 0x0005: // Active low comparator output set_output(c, !compare_count(c)); break; } } } //************************************************************************** // 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; // Check for selection of GATE n as source for any counter 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); } // Check for selection of GATE n as control for same-numbered counter if (BIT(m_counter_mode[g], 15)) gate_count(g, level && (bus_is_16_bit() || m_gate_alt[g])); // Check for selection of GATE n as control for previous counter if ((m_counter_mode[(g + 4) % 5] & 0xe000) == 0x4000) gate_count((g + 4) % 5, level && (bus_is_16_bit() || m_gate_alt[(g + 4) % 5])); // Check for selection of GATE n as control for next counter if ((m_counter_mode[(g + 1) % 5] & 0xe000) == 0x6000) gate_count((g + 1) % 5, level && (bus_is_16_bit() || m_gate_alt[(g + 1) % 5])); // Check for selection of GATE n as source for FOUT 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: gate_count(c, level && m_tc[(c + 4) % 5]); break; case 0x4000: gate_count((c + 1) % 5, level && m_gate[(c + 1) % 5]); break; case 0x6000: gate_count((c + 4) % 5, level && m_gate[(c + 4) % 5]); 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 if (m_alarm[BIT(m_dpr, 3)] != data) 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 if (m_counter_mode[(m_dpr & 7) - 1] != data) 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 if (m_counter_load[(m_dpr & 7) - 1] != data) 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 if (m_counter_hold[(m_dpr & 7) - 1] != data) 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 (machine().side_effects_disabled()) return; if (bus_is_16_bit() || !BIT(m_status, 0)) { // Least significant byte (or 16-bit word) transferred next m_status |= 0x01; if (BIT(m_mmr, 14)) return; } 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 //------------------------------------------------- u8 am9513_device::read8(offs_t offset) { if (BIT(offset, 0)) return status_read(); else return data_read() & 0x00ff; } //------------------------------------------------- // write8 - 8-bit write access //------------------------------------------------- void am9513_device::write8(offs_t offset, u8 data) { 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 //------------------------------------------------- u16 am9513_device::read16(offs_t offset) { 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 //------------------------------------------------- void am9513_device::write16(offs_t offset, u16 data) { 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; }