summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/cop452.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/cop452.cpp')
-rw-r--r--src/devices/machine/cop452.cpp342
1 files changed, 342 insertions, 0 deletions
diff --git a/src/devices/machine/cop452.cpp b/src/devices/machine/cop452.cpp
new file mode 100644
index 00000000000..f55c055e301
--- /dev/null
+++ b/src/devices/machine/cop452.cpp
@@ -0,0 +1,342 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/***************************************************************************
+
+ cop452.h
+
+ Frequency generator & counter
+
+***************************************************************************/
+
+#include "emu.h"
+#include "cop452.h"
+
+// Debugging
+//#define VERBOSE LOG_GENERAL
+#define VERBOSE 0
+#include "logmacro.h"
+
+// Device type definition
+DEFINE_DEVICE_TYPE(COP452, cop452_device, "cop452", "National Semiconductor COP452 frequency generator")
+
+cop452_device::cop452_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, COP452, tag, owner, clock)
+ , m_out_handlers(*this)
+{
+}
+
+void cop452_device::cs_w(int state)
+{
+ m_cs = state;
+ if (m_cs) {
+ // CS removed
+ m_spi_state = 0;
+ m_sr = 0;
+ }
+}
+
+void cop452_device::sk_w(int state)
+{
+ if (!m_cs && !m_sk && state) {
+ // Rising edge on SK
+ LOG("bit %d %u\n", m_di, m_spi_state);
+ if (m_spi_state == 0 && m_di) {
+ // Got start bit
+ m_spi_state = 1;
+ } else if (m_spi_state >= 1 && m_spi_state < 6) {
+ // Shifting instruction in
+ m_sr = (m_sr << 1) | m_di;
+ m_spi_state++;
+ if (m_spi_state == 6) {
+ LOG("Inst = %x\n", m_sr);
+ m_spi_state = 22;
+ unsigned idx = !BIT(m_sr, 0);
+ char reg = idx ? 'B' : 'A';
+ switch (m_sr) {
+ case 0b00000:
+ case 0b00001:
+ // LDRA/B
+ LOG("LDR%c\n", reg);
+ m_spi_state = 6;
+ m_reg[idx] = 0;
+ break;
+
+ case 0b00010:
+ case 0b00011:
+ // RDRA/B
+ // TODO: not implemented ATM
+ LOG("RDR%c\n", reg);
+ break;
+
+ case 0b00100:
+ case 0b00101:
+ // TRCA/B
+ LOG("TRC%c\n", reg);
+ m_cnt[idx] = m_reg[idx];
+ set_timer(idx);
+ break;
+
+ case 0b00110:
+ case 0b00111:
+ // TCRA/B
+ // TODO:
+ LOG("TCR%c\n", reg);
+ break;
+
+ case 0b01000:
+ // CK1
+ LOG("CK1\n");
+ m_clk_div_4 = false;
+ break;
+
+ case 0b01001:
+ // CK4
+ LOG("CK4\n");
+ m_clk_div_4 = true;
+ break;
+
+ default:
+ if (m_sr & 0b10000) {
+ // LDM
+ m_mode = m_sr & 0b01111;
+ LOG("LDM %x\n", m_mode);
+ set_timer(0);
+ set_timer(1);
+ if (m_mode == MODE_NUMBER_PULSES) {
+ // Always start with OA = 1
+ set_output(0, true);
+ } else if (m_mode == MODE_WHITE_NOISE ||
+ m_mode == MODE_GATED_WHITE) {
+ // Preset bit 15 & 16 of register A when entering
+ // white noise modes
+ m_reg[0] |= 0x8000;
+ m_regA_b16 = true;
+ }
+ } else {
+ // Unknown instruction
+ LOG("Unknown instruction\n");
+ }
+ break;
+ }
+ }
+ } else if (m_spi_state >= 6 && m_spi_state < 22) {
+ // Loading A/B register
+ unsigned idx = !BIT(m_sr, 0);
+ m_reg[idx] = (m_reg[idx] << 1) | m_di;
+ m_spi_state++;
+ if (m_spi_state == 22) {
+ LOG("REG%c = %04x\n", idx ? 'B' : 'A', m_reg[idx]);
+ }
+ }
+ }
+ m_sk = state;
+}
+
+void cop452_device::di_w(int state)
+{
+ m_di = state;
+}
+
+int cop452_device::do_r()
+{
+ // TODO:
+ return 0;
+}
+
+void cop452_device::device_start()
+{
+ m_timers[0] = timer_alloc(FUNC(cop452_device::timer_tick), this);
+ m_timers[1] = timer_alloc(FUNC(cop452_device::timer_tick), this);
+
+ save_item(NAME(m_mode));
+ save_item(NAME(m_clk_div_4));
+ save_item(NAME(m_cs));
+ save_item(NAME(m_sk));
+ save_item(NAME(m_di));
+ save_item(NAME(m_out));
+ save_item(NAME(m_regA_b16));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_cnt));
+ save_item(NAME(m_spi_state));
+ save_item(NAME(m_sr));
+}
+
+void cop452_device::device_reset()
+{
+ // Set reset mode
+ m_mode = MODE_RESET;
+ m_clk_div_4 = true;
+ m_out[0] = m_out[1] = true;
+ set_output(0, false);
+ set_output(1, false);
+ m_spi_state = 0;
+ m_sr = 0;
+ m_timers[0]->reset();
+ m_timers[1]->reset();
+}
+
+TIMER_CALLBACK_MEMBER(cop452_device::timer_tick)
+{
+ switch (m_mode) {
+ case MODE_DUAL_FREQ:
+ toggle_n_reload(param);
+ break;
+
+ case MODE_TRIG_PULSE:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_NUMBER_PULSES:
+ if (param == 0) {
+ toggle_n_reload(0);
+ if (!m_out[0]) {
+ // It seems that cnt B decrements each time OA goes low
+ if (m_cnt[1] != 0) {
+ m_cnt[1]--;
+ } else {
+ // End of pulse train
+ toggle_n_reload(1);
+ m_mode = MODE_RESET;
+ }
+ }
+ }
+ break;
+
+ case MODE_DUTY_CYCLE:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_FREQ_COUNT:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_DUAL_COUNT:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_WAVE_MEAS:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_TRIG_COUNT:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_WHITE_NOISE:
+ case MODE_GATED_WHITE:
+ {
+ if (param == 0) {
+ // Reg A & its 17th bit (m_regA_b16) form a 17-bit LFSR
+ // LFSR uses X^17+X^14+1 polynomial to generate a pseudo-random
+ // maximal-length sequence
+ bool feedback = m_regA_b16 ^ BIT(m_reg[0], 13);
+ m_regA_b16 = BIT(m_reg[0], 15);
+ m_reg[0] <<= 1;
+ m_reg[0] |= feedback;
+ } else {
+ toggle_n_reload(1);
+ }
+ bool new_out_0 = m_regA_b16;
+ if (m_mode == MODE_GATED_WHITE) {
+ new_out_0 &= m_out[1];
+ }
+ set_output(0, new_out_0);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ set_timer(param);
+}
+
+attotime cop452_device::counts_to_attotime(unsigned counts) const
+{
+ if (m_clk_div_4) {
+ return clocks_to_attotime((counts + 1) * 4);
+ } else {
+ return clocks_to_attotime(counts + 1);
+ }
+}
+
+void cop452_device::set_timer(unsigned idx)
+{
+ attotime target = attotime::never;
+
+ switch (m_mode) {
+ case MODE_DUAL_FREQ:
+ // Cnt A & B count independently
+ target = counts_to_attotime(m_cnt[idx]);
+ break;
+
+ case MODE_TRIG_PULSE:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_NUMBER_PULSES:
+ // Cnt A generates OA frequency
+ // Cnt B counts the periods to output
+ if (idx == 0) {
+ target = counts_to_attotime(m_cnt[0]);
+ }
+ break;
+
+ case MODE_DUTY_CYCLE:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_FREQ_COUNT:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_DUAL_COUNT:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_WAVE_MEAS:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_TRIG_COUNT:
+ // TODO: NOT IMPLEMENTED
+ break;
+
+ case MODE_WHITE_NOISE:
+ case MODE_GATED_WHITE:
+ // Cnt A is not used. Timer 0 expires once per internal clock period.
+ // Cnt B generates squarewave signal on OB
+ if (idx == 0) {
+ target = counts_to_attotime(0);
+ } else {
+ target = counts_to_attotime(m_cnt[1]);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ m_timers[idx]->adjust(target, idx);
+}
+
+void cop452_device::set_output(unsigned idx, bool state)
+{
+ if (m_out[idx] != state) {
+ m_out[idx] = state;
+ LOG("OUT %u=%d @%s\n", idx, state, machine().time().as_string());
+ m_out_handlers[idx](state);
+ }
+}
+
+void cop452_device::toggle_output(unsigned idx)
+{
+ set_output(idx, !m_out[idx]);
+}
+
+void cop452_device::toggle_n_reload(unsigned idx)
+{
+ // Toggle output OA/OB and reload its associated counter
+ toggle_output(idx);
+ m_cnt[idx] = m_reg[idx];
+}