From a8015550ce9ddec3e3d261b9d3f9dbad045a477a Mon Sep 17 00:00:00 2001 From: Devin Acker Date: Sat, 18 Nov 2023 10:41:19 -0500 Subject: sound/upd933.cpp: Use a timer to activate interrupt output. (#11750) * This decouples the interrupt output from the sound update cycle so it's timed correctly. * Also improved interrupt priority handling. --- src/devices/sound/upd933.cpp | 141 +++++++++++++++++++++++++++++++++---------- src/devices/sound/upd933.h | 17 ++++-- 2 files changed, 121 insertions(+), 37 deletions(-) diff --git a/src/devices/sound/upd933.cpp b/src/devices/sound/upd933.cpp index 763caf45a4d..8b9ae286b1b 100644 --- a/src/devices/sound/upd933.cpp +++ b/src/devices/sound/upd933.cpp @@ -9,6 +9,7 @@ #include "upd933.h" #include +#include #include DEFINE_DEVICE_TYPE(UPD933, upd933_device, "upd933", "NEC uPD933") @@ -25,6 +26,8 @@ void upd933_device::device_start() { m_stream = stream_alloc(0, 1, clock() / CLOCKS_PER_SAMPLE); + m_irq_timer = timer_alloc(FUNC(upd933_device::timer_tick), this); + for (int i = 0; i < 0x800; i++) m_cosine[i] = 0xfff * (1 - cos(2.0 * M_PI * i / 0x7ff)) / 2; @@ -44,16 +47,17 @@ void upd933_device::device_start() m_volume[i] = pow(2 << VOLUME_SHIFT, (double)i / 0x1ff); m_volume[0] = 0; - m_cs = 1; + m_cs = m_id = 1; + save_item(NAME(m_irq_pending)); save_item(NAME(m_irq_state)); save_item(NAME(m_cs)); + save_item(NAME(m_id)); save_item(NAME(m_sound_data)); save_item(NAME(m_sound_data_pos)); save_item(NAME(m_sound_regs)); save_item(NAME(m_sample_count)); save_item(NAME(m_last_sample)); - save_item(NAME(m_irq_data)); save_item(STRUCT_MEMBER(m_voice, m_wave)); save_item(STRUCT_MEMBER(m_voice, m_window)); @@ -88,10 +92,17 @@ void upd933_device::device_start() save_item(STRUCT_MEMBER(m_dco, m_current)); } +/**************************************************************************/ +TIMER_CALLBACK_MEMBER(upd933_device::timer_tick) +{ + m_irq_pending = 1; + update_irq(); +} + /**************************************************************************/ void upd933_device::device_reset() { - m_irq_state = 0; + m_irq_pending = m_irq_state = 0; m_sound_data[0] = m_sound_data[1] = 0; m_sound_data_pos = 0; @@ -104,7 +115,6 @@ void upd933_device::device_reset() m_sample_count = 0; m_last_sample = 0; - m_irq_data = 0; } /**************************************************************************/ @@ -113,40 +123,86 @@ void upd933_device::device_clock_changed() m_stream->set_sample_rate(clock() / CLOCKS_PER_SAMPLE); } +/**************************************************************************/ +int upd933_device::rq_r() +{ + if (!machine().side_effects_disabled()) + m_stream->update(); + + return m_irq_state; +} + /**************************************************************************/ void upd933_device::cs_w(int state) { m_stream->update(); if (!m_cs && state) - check_irq(); + update_pending_irq(); m_cs = state; + update_irq(); +} + +/**************************************************************************/ +void upd933_device::id_w(int state) +{ + m_stream->update(); + + m_id = state; + update_irq(); } /**************************************************************************/ -void upd933_device::check_irq() +u8 upd933_device::irq_data() const { + // TODO: do these have the correct priority? for (int i = 0; i < 8; i++) { - if (m_dca[i].m_irq) - { - m_irq_data = 1 | (i << 1); - break; - } - if (m_dcw[i].m_irq) - { - m_irq_data = 2 | (i << 2); - break; - } if (m_dco[i].m_irq) - { - m_irq_data = 4 | (i << 3); - break; - } + return 4 | (i << 3); + } + for (int i = 0; i < 8; i++) + { + if (m_dcw[i].m_irq) + return 2 | (i << 2); } + for (int i = 0; i < 8; i++) + { + if (m_dca[i].m_irq) + return 1 | (i << 1); + } + return 0; +} - if (m_irq_data) - m_irq_cb(m_irq_state = 1); +/**************************************************************************/ +void upd933_device::update_pending_irq() +{ + m_irq_pending = 0; + bool env_active = false; + unsigned new_time = UINT_MAX; + + for (int i = 0; i < 8; i++) + { + env_active |= (m_dca[i].calc_timeout(new_time) + | m_dco[i].calc_timeout(new_time) + | m_dcw[i].calc_timeout(new_time)); + } + + if (!new_time) + m_irq_pending = 1; + else if (env_active) + m_irq_timer->adjust(clocks_to_attotime((u64)new_time * CLOCKS_PER_SAMPLE)); +} + +/**************************************************************************/ +void upd933_device::update_irq() +{ + u8 const irq_state = m_cs & m_id & m_irq_pending; + if (irq_state != m_irq_state) + { + m_irq_state = irq_state; + m_irq_cb(m_irq_state); + } } /**************************************************************************/ @@ -155,7 +211,7 @@ u8 upd933_device::read() if (!machine().side_effects_disabled()) m_stream->update(); - return m_cs ? 0xff : m_irq_data; + return m_cs ? 0xff : irq_data(); } /**************************************************************************/ @@ -167,9 +223,6 @@ void upd933_device::write(u8 data) { m_stream->update(); - m_irq_data = 0; - m_irq_cb(m_irq_state = 0); - const u8 reg = m_sound_data[0]; const u16 value = m_sound_regs[reg] = (m_sound_data[1] << 8) | data; @@ -263,9 +316,12 @@ void upd933_device::write(u8 data) mod_voice.m_mute_other = BIT(value, 2); break; - case 0x13: // 98-9f - // unknown, but cz101 sets these to zero when starting a note, probably to reset the oscillator - voice.m_position = value << PITCH_SHIFT; + case 0x13: // 98-9f: phase counter + /* + cz101 sets these to zero when starting a note to reset the oscillator. + cz1 writes 0x0000, 0x0080, 0x0100, or 0x0180 for up to four voices of a tone instead + */ + voice.m_position = value << (PITCH_SHIFT - 4); break; default: @@ -303,9 +359,6 @@ void upd933_device::sound_stream_update(sound_stream &stream, std::vector m_voice; std::array m_dca, m_dco, m_dcw; -- cgit v1.2.3