diff options
Diffstat (limited to 'src/devices/machine/ucb1200.cpp')
-rw-r--r-- | src/devices/machine/ucb1200.cpp | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/src/devices/machine/ucb1200.cpp b/src/devices/machine/ucb1200.cpp new file mode 100644 index 00000000000..636e223ea1d --- /dev/null +++ b/src/devices/machine/ucb1200.cpp @@ -0,0 +1,370 @@ +// license:BSD-3-Clause +// copyright-holders:Ryan Holtz +/*************************************************************************** + + Philips UCB1200 Advanced modem/audio analog front-end skeleton + +***************************************************************************/ + +#include "emu.h" +#include "ucb1200.h" + +#define LOG_UNKNOWN (1U << 1) +#define LOG_READS (1U << 2) +#define LOG_WRITES (1U << 3) +#define LOG_ALL (LOG_UNKNOWN | LOG_READS | LOG_WRITES) + +#define VERBOSE (LOG_ALL) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(UCB1200, ucb1200_device, "ucb1200", "Philips UCB1200 modem/audio codec") + +ucb1200_device::ucb1200_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, UCB1200, tag, owner, clock) + , m_gpio_out(*this) + , m_audio_out(*this) + , m_telecom_out(*this) + , m_irq_out(*this) + , m_adc_in(*this, 0xffff) +{ +} + +void ucb1200_device::device_start() +{ + save_item(NAME(m_gpio_out_latch)); + save_item(NAME(m_gpio_in_latch)); + save_item(NAME(m_gpio_dir)); + save_item(NAME(m_rising_int_en)); + save_item(NAME(m_falling_int_en)); + save_item(NAME(m_int_status)); + save_item(NAME(m_telecom_ctrl_a)); + save_item(NAME(m_telecom_ctrl_b)); + save_item(NAME(m_audio_ctrl_a)); + save_item(NAME(m_audio_ctrl_b)); + save_item(NAME(m_touch_ctrl)); + save_item(NAME(m_adc_ctrl)); + save_item(NAME(m_adc_data)); + save_item(NAME(m_id)); + save_item(NAME(m_mode)); +} + +void ucb1200_device::device_reset() +{ + m_gpio_out_latch = 0; + m_gpio_in_latch = 0; + m_gpio_dir = 0; + m_rising_int_en = 0; + m_falling_int_en = 0; + m_int_status = 0; + m_telecom_ctrl_a = 0x10 << TEL_DIV_BIT; + m_telecom_ctrl_b = 0; + m_audio_ctrl_a = 0x06 << AUD_DIV_BIT; + m_audio_ctrl_b = 0; + m_touch_ctrl = 0; + m_adc_ctrl = 0; + m_adc_data = 0; + m_id = 0x1004; + m_mode = 0; +} + +void ucb1200_device::gpio_in(const uint16_t line, const int state) +{ + const uint16_t mask = (1 << line); + const uint16_t old_latch = m_gpio_in_latch; + m_gpio_in_latch &= ~mask; + m_gpio_in_latch |= (state << line); + + const bool old_irq = (m_int_status != 0); + if (old_latch != m_gpio_in_latch && !BIT(m_gpio_dir, line)) + { + if (state && BIT(m_rising_int_en, line)) + m_int_status |= mask; + else if (!state && BIT(m_falling_int_en, line)) + m_int_status |= mask; + } + + if (!old_irq && m_int_status != 0) + m_irq_out(1); +} + +void ucb1200_device::update_gpio_direction(const uint16_t old_dir) +{ + const uint16_t new_outputs = ~old_dir & m_gpio_dir; + if (new_outputs) + { + for (uint32_t line = 0; line < 10; line++) + { + if (BIT(new_outputs, line)) + { + m_gpio_out[line](BIT(m_gpio_out_latch, line)); + } + } + } + + // TODO: Do we need to check rising/falling edges based on the transition from output to input? +} + +void ucb1200_device::update_gpio_outputs(const uint16_t old_latch, const uint16_t changed) +{ + uint16_t remaining_changed = changed; + + for (uint32_t line = 0; line < 10 && remaining_changed != 0; line++) + { + if (BIT(remaining_changed, line)) + { + m_gpio_out[line](BIT(m_gpio_out_latch, line)); + remaining_changed &= ~(1 << line); + } + } +} + +void ucb1200_device::audio_sample_in(const uint16_t sample) +{ + // TODO: Handle incoming audio samples +} + +void ucb1200_device::telecom_sample_in(const uint16_t sample) +{ + // TODO: Handle incoming telecom samples +} + +void ucb1200_device::adc_begin_conversion() +{ + const uint16_t adc_input = (m_adc_ctrl & ADC_INPUT_MASK) >> ADC_INPUT_BIT; + switch (adc_input) + { + case ADC_INPUT_TSPX: + case ADC_INPUT_TSMX: + case ADC_INPUT_TSPY: + case ADC_INPUT_TSMY: + default: + m_adc_data = 0; + break; + case ADC_INPUT_AD0: + case ADC_INPUT_AD1: + case ADC_INPUT_AD2: + case ADC_INPUT_AD3: + m_adc_data = m_adc_in[adc_input - ADC_INPUT_AD0](); + m_adc_data <<= ADC_DATA_BIT; + m_adc_data &= ADC_DATA_MASK; + break; + } + + m_adc_data |= (1 << ADC_DAT_VAL_BIT); +} + +uint16_t ucb1200_device::read(offs_t offset) +{ + switch (offset) + { + case 0: + { + const uint16_t data = (m_gpio_out_latch & m_gpio_dir) | (m_gpio_in_latch & ~m_gpio_dir); + LOGMASKED(LOG_READS, "%s: read: GPIO Data Register: %04x\n", machine().describe_context(), data); + return data; + } + case 1: + LOGMASKED(LOG_READS, "%s: read: GPIO Direction Register: %04x\n", machine().describe_context(), m_gpio_dir); + return m_gpio_dir; + case 2: + LOGMASKED(LOG_READS, "%s: read: GPIO Rising-Edge Interrupt Enable Register: %04x\n", machine().describe_context(), m_rising_int_en); + return m_rising_int_en; + case 3: + LOGMASKED(LOG_READS, "%s: read: GPIO Falling-Edge Interrupt Enable Register: %04x\n", machine().describe_context(), m_falling_int_en); + return m_falling_int_en; + case 4: + LOGMASKED(LOG_READS, "%s: read: Interrupt Clear/Status Register: %04x\n", machine().describe_context(), m_int_status); + return m_int_status; + case 5: + LOGMASKED(LOG_READS, "%s: read: Telecom Control Register A: %04x\n", machine().describe_context(), m_telecom_ctrl_a); + return m_telecom_ctrl_a; + case 6: + LOGMASKED(LOG_READS, "%s: read: Telecom Control Register B: %04x\n", machine().describe_context(), m_telecom_ctrl_b); + return m_telecom_ctrl_b; + case 7: + LOGMASKED(LOG_READS, "%s: read: Audio Control Register A: %04x\n", machine().describe_context(), m_audio_ctrl_a); + return m_audio_ctrl_a; + case 8: + LOGMASKED(LOG_READS, "%s: read: Audio Control Register B: %04x\n", machine().describe_context(), m_audio_ctrl_b); + return m_audio_ctrl_b; + case 9: + LOGMASKED(LOG_READS, "%s: read: Touchscreen Control Register: %04x\n", machine().describe_context(), m_touch_ctrl); + return m_touch_ctrl; + case 10: + LOGMASKED(LOG_READS, "%s: read: ADC Control Register: %04x\n", machine().describe_context(), m_adc_ctrl); + return m_adc_ctrl; + case 11: + LOGMASKED(LOG_READS, "%s: read: ADC Data Register: %04x\n", machine().describe_context(), m_adc_data); + return m_adc_data; + case 12: + LOGMASKED(LOG_READS, "%s: read: ID Register: %04x\n", machine().describe_context(), m_id); + return m_id; + case 13: + LOGMASKED(LOG_READS, "%s: read: Mode Register: %04x\n", machine().describe_context(), m_mode); + return m_mode; + case 14: + LOGMASKED(LOG_READS, "%s: read: Reserved Register: %04x\n", machine().describe_context(), 0); + return 0; + case 15: + LOGMASKED(LOG_READS, "%s: read: NULL Register: %04x\n", machine().describe_context(), 0xffff); + return 0xffff; + default: + LOGMASKED(LOG_READS | LOG_UNKNOWN, "%s: read: Unknown Register: %d\n", machine().describe_context(), offset); + return 0; + } +} + +void ucb1200_device::write(offs_t offset, uint16_t data) +{ + switch (offset) + { + case 0: + { + LOGMASKED(LOG_WRITES, "%s: write: GPIO Data Register: %04x\n", machine().describe_context(), data); + const uint16_t old = m_gpio_out_latch; + m_gpio_out_latch = data; + const uint16_t changed = (old ^ m_gpio_out_latch) & m_gpio_dir; + if (changed) + update_gpio_outputs(old, changed); + break; + } + case 1: + { + LOGMASKED(LOG_WRITES, "%s: write: GPIO Direction Register: %04x\n", machine().describe_context(), data); + const uint16_t old = m_gpio_dir; + m_gpio_dir = data; + if (old != m_gpio_dir) + update_gpio_direction(old); + break; + } + case 2: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Rising-Edge Interrupt Enable Register: %04x\n", machine().describe_context(), data); + m_rising_int_en = data; + break; + case 3: + LOGMASKED(LOG_WRITES, "%s: write: GPIO Falling-Edge Interrupt Enable Register: %04x\n", machine().describe_context(), data); + m_falling_int_en = data; + break; + case 4: + { + LOGMASKED(LOG_WRITES, "%s: write: Interrupt Clear/Status Register: %04x\n", machine().describe_context(), data); + const uint16_t old = m_int_status; + m_int_status &= ~data; + if (old != 0 && m_int_status == 0) + m_irq_out(0); + break; + } + case 5: + LOGMASKED(LOG_WRITES, "%s: write: Telecom Control Register A: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: Telecom Codec Sample Rate Divisor: %02x\n", machine().describe_context(), (data & TEL_DIV_MASK) >> TEL_DIV_BIT); + LOGMASKED(LOG_WRITES, "%s: Telecom Codec Loopback: %d\n", machine().describe_context(), BIT(data, TEL_LOOP_BIT)); + m_telecom_ctrl_a = data; + break; + case 6: + { + LOGMASKED(LOG_WRITES, "%s: write: Telecom Control Register B: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: Telecom Voice Band Filter: %d\n", machine().describe_context(), BIT(data, TEL_VOICE_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: Telecom Clip Detect Clear: %d\n", machine().describe_context(), BIT(data, TEL_CLIP_BIT)); + LOGMASKED(LOG_WRITES, "%s: Telecom Input Attenuation: %d\n", machine().describe_context(), BIT(data, TEL_ATT_BIT)); + LOGMASKED(LOG_WRITES, "%s: Telecom Sidetone Suppression: %d\n", machine().describe_context(), BIT(data, TEL_SIDE_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: Telecom Output Mute: %d\n", machine().describe_context(), BIT(data, TEL_MUTE_BIT)); + LOGMASKED(LOG_WRITES, "%s: Telecom Input Enable: %d\n", machine().describe_context(), BIT(data, TEL_IN_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: Telecom Output Enable: %d\n", machine().describe_context(), BIT(data, TEL_OUT_ENA_BIT)); + + const uint16_t old_clip = m_telecom_ctrl_b & (1 << TEL_CLIP_BIT); + const uint16_t new_clip = data & (1 << TEL_CLIP_BIT); + + m_telecom_ctrl_b = data; + + if (new_clip) + m_telecom_ctrl_b &= ~(1 << TEL_CLIP_BIT); + else if (old_clip) + m_telecom_ctrl_b |= (1 << TEL_CLIP_BIT); + break; + } + case 7: + LOGMASKED(LOG_WRITES, "%s: write: Audio Control Register A: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: Audio Codec Sample Rate Divisor: %02x\n", machine().describe_context(), (data & AUD_DIV_MASK) >> AUD_DIV_BIT); + LOGMASKED(LOG_WRITES, "%s: Audio Input Gain: %02x\n", machine().describe_context(), (data & AUD_GAIN_MASK) >> AUD_GAIN_BIT); + m_audio_ctrl_a = data; + break; + case 8: + { + LOGMASKED(LOG_WRITES, "%s: write: Audio Control Register B: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: Audio Output Attenuation: %02x\n", machine().describe_context(), (data & AUD_ATT_MASK) >> AUD_ATT_BIT); + LOGMASKED(LOG_WRITES, "%s: Audio Clip Detect Clear: %d\n", machine().describe_context(), BIT(data, AUD_CLIP_BIT)); + LOGMASKED(LOG_WRITES, "%s: Audio Codec Loopback: %d\n", machine().describe_context(), BIT(data, AUD_LOOP_BIT)); + LOGMASKED(LOG_WRITES, "%s: Audio Output Mute: %d\n", machine().describe_context(), BIT(data, AUD_MUTE_BIT)); + LOGMASKED(LOG_WRITES, "%s: Audio Input Enable: %d\n", machine().describe_context(), BIT(data, AUD_IN_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: Audio Output Enable: %d\n", machine().describe_context(), BIT(data, AUD_OUT_ENA_BIT)); + + const uint16_t old_clip = m_audio_ctrl_b & (1 << AUD_CLIP_BIT); + const uint16_t new_clip = data & (1 << AUD_CLIP_BIT); + + m_audio_ctrl_b = data; + + if (new_clip) + m_audio_ctrl_b &= ~(1 << AUD_CLIP_BIT); + else if (old_clip) + m_audio_ctrl_b |= (1 << AUD_CLIP_BIT); + break; + } + case 9: + { + static const char *const s_tsc_modes[4] = { "Interrupt", "Pressure", "Position [2]", "Position [3]" }; + LOGMASKED(LOG_WRITES, "%s: write: Touchscreen Control Register: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: TSMX Pin Powered: %d\n", machine().describe_context(), BIT(data, TSMX_POW_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSPX Pin Powered: %d\n", machine().describe_context(), BIT(data, TSPX_POW_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSMY Pin Powered: %d\n", machine().describe_context(), BIT(data, TSMY_POW_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSPY Pin Powered: %d\n", machine().describe_context(), BIT(data, TSPY_POW_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSMX Pin Grounded: %d\n", machine().describe_context(), BIT(data, TSMX_GND_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSPX Pin Grounded: %d\n", machine().describe_context(), BIT(data, TSPX_GND_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSMY Pin Grounded: %d\n", machine().describe_context(), BIT(data, TSMY_GND_BIT)); + LOGMASKED(LOG_WRITES, "%s: TSPY Pin Grounded: %d\n", machine().describe_context(), BIT(data, TSPY_GND_BIT)); + LOGMASKED(LOG_WRITES, "%s: Touch Screen Mode: %s\n", machine().describe_context(), s_tsc_modes[(data & TSC_MODE_MASK) >> TSC_MODE_BIT]); + LOGMASKED(LOG_WRITES, "%s: Touch Screen Bias Circuit Active: %d\n", machine().describe_context(), BIT(data, TSC_BIAS_ENA_BIT)); + m_touch_ctrl &= ~TOUCH_WRITE_MASK; + m_touch_ctrl |= data & TOUCH_WRITE_MASK; + break; + } + case 10: + { + static const char *const s_adc_inputs[8] = { "TSPX", "TSMX", "TSPY", "TSMY", "AD0", "AD1", "AD2", "AD3" }; + LOGMASKED(LOG_WRITES, "%s: write: ADC Control Register: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: ADC Sync Mode: %d\n", machine().describe_context(), BIT(data, ADC_SYNC_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: Connect Internal Vref to VREFBYP Pin: %d\n", machine().describe_context(), BIT(data, VREFBYP_CON_BIT)); + LOGMASKED(LOG_WRITES, "%s: ADC Input Select: %s\n", machine().describe_context(), s_adc_inputs[(data & ADC_INPUT_MASK) >> ADC_INPUT_BIT]); + LOGMASKED(LOG_WRITES, "%s: Apply External Voltage to VREFBYP Pin: %d\n", machine().describe_context(), BIT(data, EXT_REF_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: ADC Conversion Start: %d\n", machine().describe_context(), BIT(data, ADC_START_BIT)); + LOGMASKED(LOG_WRITES, "%s: ADC Enabled: %d\n", machine().describe_context(), BIT(data, ADC_ENA_BIT)); + const uint16_t old = m_adc_ctrl; + m_adc_ctrl = data; + if (!BIT(old, ADC_START_BIT) && BIT(data, ADC_START_BIT) && BIT(data, ADC_ENA_BIT)) + adc_begin_conversion(); + break; + } + case 11: + LOGMASKED(LOG_WRITES, "%s: write: ADC Data Register (ignored): %04x\n", machine().describe_context(), data); + break; + case 12: + LOGMASKED(LOG_WRITES, "%s: write: ID Register (ignored): %04x\n", machine().describe_context(), data); + break; + case 13: + LOGMASKED(LOG_WRITES, "%s: write: Mode Register: %04x\n", machine().describe_context(), data); + LOGMASKED(LOG_WRITES, "%s: Analog Audio Test Mode: %d\n", machine().describe_context(), BIT(data, AUD_TEST_BIT)); + LOGMASKED(LOG_WRITES, "%s: Analog Telecom Test Mode: %d\n", machine().describe_context(), BIT(data, TEL_TEST_BIT)); + LOGMASKED(LOG_WRITES, "%s: Production Test Mode: %02x\n", machine().describe_context(), (data & PROD_TEST_MODE_MASK) >> PROD_TEST_MODE_BIT); + LOGMASKED(LOG_WRITES, "%s: Dynamic Data Valid Flag Mode: %d\n", machine().describe_context(), BIT(data, DYN_VFLAG_ENA_BIT)); + LOGMASKED(LOG_WRITES, "%s: Audio Offset-Cancelling: %d\n", machine().describe_context(), BIT(data, AUD_OFF_CAN_BIT)); + break; + case 14: + LOGMASKED(LOG_WRITES, "%s: write: Reserved Register (ignored): %04x\n", machine().describe_context(), data); + break; + case 15: + LOGMASKED(LOG_WRITES, "%s: write: NULL Register (ignored): %04x\n", machine().describe_context(), data); + break; + default: + LOGMASKED(LOG_WRITES | LOG_UNKNOWN, "%s: write: Unknown Register: %d = %04x\n", machine().describe_context(), offset, data); + break; + } +} |