diff options
Diffstat (limited to 'src/devices/machine/cop452.cpp')
-rw-r--r-- | src/devices/machine/cop452.cpp | 342 |
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]; +} |