-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
490 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,350 @@ | ||
// 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) | ||
{ | ||
} | ||
|
||
WRITE_LINE_MEMBER(cop452_device::cs_w) | ||
{ | ||
m_cs = state; | ||
if (m_cs) { | ||
// CS removed | ||
m_spi_state = 0; | ||
m_sr = 0; | ||
} | ||
} | ||
|
||
WRITE_LINE_MEMBER(cop452_device::sk_w) | ||
{ | ||
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); | ||
char reg = idx ? 'B' : 'A'; | ||
m_reg[ idx ] = (m_reg[ idx ] << 1) | m_di; | ||
m_spi_state++; | ||
if (m_spi_state == 22) { | ||
LOG("REG%c = %04x\n" , reg , m_reg[ idx ]); | ||
} | ||
} | ||
} | ||
m_sk = state; | ||
} | ||
|
||
WRITE_LINE_MEMBER(cop452_device::di_w) | ||
{ | ||
m_di = state; | ||
} | ||
|
||
READ_LINE_MEMBER(cop452_device::do_r) | ||
{ | ||
// TODO: | ||
return 0; | ||
} | ||
|
||
void cop452_device::device_start() | ||
{ | ||
m_out_handlers.resolve_all_safe(); | ||
|
||
m_timers[ 0 ] = timer_alloc(0); | ||
m_timers[ 1 ] = timer_alloc(1); | ||
|
||
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(); | ||
} | ||
|
||
void cop452_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) | ||
{ | ||
attotime target = attotime::never; | ||
|
||
switch (m_mode) { | ||
case MODE_DUAL_FREQ: | ||
toggle_n_reload(id); | ||
target = counts_to_attotime(m_cnt[ id ]); | ||
break; | ||
|
||
case MODE_TRIG_PULSE: | ||
// TODO: NOT IMPLEMENTED | ||
break; | ||
|
||
case MODE_NUMBER_PULSES: | ||
if (id == 0) { | ||
toggle_n_reload(0); | ||
target = counts_to_attotime(m_cnt[ 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; | ||
target = attotime::never; | ||
} | ||
} | ||
} | ||
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 (id == 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; | ||
} | ||
|
||
timer.adjust(target); | ||
} | ||
|
||
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); | ||
} | ||
|
||
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 ]; | ||
} |
Oops, something went wrong.