mirror of
https://github.com/holub/mame
synced 2025-06-07 13:23:50 +03:00
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.
This commit is contained in:
parent
61fe61537a
commit
a8015550ce
@ -9,6 +9,7 @@
|
||||
#include "upd933.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
|
||||
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::check_irq()
|
||||
void upd933_device::id_w(int state)
|
||||
{
|
||||
m_stream->update();
|
||||
|
||||
m_id = state;
|
||||
update_irq();
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
u8 upd933_device::irq_data() const
|
||||
{
|
||||
// TODO: do these have the correct priority?
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (m_dco[i].m_irq)
|
||||
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)
|
||||
{
|
||||
m_irq_data = 1 | (i << 1);
|
||||
break;
|
||||
return 1 | (i << 1);
|
||||
}
|
||||
if (m_dcw[i].m_irq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
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++)
|
||||
{
|
||||
m_irq_data = 2 | (i << 2);
|
||||
break;
|
||||
}
|
||||
if (m_dco[i].m_irq)
|
||||
{
|
||||
m_irq_data = 4 | (i << 3);
|
||||
break;
|
||||
}
|
||||
env_active |= (m_dca[i].calc_timeout(new_time)
|
||||
| m_dco[i].calc_timeout(new_time)
|
||||
| m_dcw[i].calc_timeout(new_time));
|
||||
}
|
||||
|
||||
if (m_irq_data)
|
||||
m_irq_cb(m_irq_state = 1);
|
||||
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<read_s
|
||||
|
||||
outputs[0].put_int_clamp(i, sample, 1 << 15);
|
||||
m_sample_count++;
|
||||
|
||||
if (!m_irq_data && m_cs)
|
||||
check_irq();
|
||||
}
|
||||
}
|
||||
|
||||
@ -504,6 +557,30 @@ void upd933_device::env_t::update()
|
||||
m_irq = true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
bool upd933_device::env_t::calc_timeout(unsigned &samples)
|
||||
{
|
||||
if (m_sustain || !m_rate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (m_irq)
|
||||
{
|
||||
samples = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned remaining = m_direction ? (m_current - m_target) : (m_target - m_current);
|
||||
unsigned new_time = remaining / m_rate;
|
||||
if (remaining % m_rate)
|
||||
new_time++;
|
||||
if (new_time < samples)
|
||||
samples = new_time;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
void upd933_device::update_pitch_step(int vnum)
|
||||
{
|
||||
|
@ -19,8 +19,9 @@ public:
|
||||
|
||||
auto irq_cb() { return m_irq_cb.bind(); }
|
||||
|
||||
int rq_r() const { return m_irq_state; }
|
||||
int rq_r();
|
||||
void cs_w(int state); // chip select, active low
|
||||
void id_w(int state); // irq disable, active low
|
||||
|
||||
void write(u8 data);
|
||||
u8 read();
|
||||
@ -48,6 +49,8 @@ private:
|
||||
u32 m_rate = 0, m_target = 0, m_current = 0;
|
||||
|
||||
void update();
|
||||
// calculate the next time this envelope generates an interrupt
|
||||
bool calc_timeout(unsigned &samples);
|
||||
};
|
||||
|
||||
struct voice_t
|
||||
@ -61,8 +64,12 @@ private:
|
||||
s16 m_pm_level = 0;
|
||||
};
|
||||
|
||||
TIMER_CALLBACK_MEMBER(timer_tick);
|
||||
|
||||
s16 update(int vnum);
|
||||
void check_irq();
|
||||
u8 irq_data() const;
|
||||
void update_pending_irq();
|
||||
void update_irq();
|
||||
|
||||
u32 env_rate(u8 data) const;
|
||||
void update_pitch_step(int vnum);
|
||||
@ -71,8 +78,9 @@ private:
|
||||
static constexpr unsigned CLOCKS_PER_SAMPLE = 112;
|
||||
|
||||
devcb_write_line m_irq_cb;
|
||||
u8 m_irq_state;
|
||||
u8 m_cs;
|
||||
u8 m_irq_pending, m_irq_state;
|
||||
u8 m_cs, m_id;
|
||||
emu_timer *m_irq_timer;
|
||||
|
||||
u16 m_cosine[0x800];
|
||||
u32 m_pitch[0x80];
|
||||
@ -85,7 +93,6 @@ private:
|
||||
|
||||
u32 m_sample_count;
|
||||
s16 m_last_sample;
|
||||
u8 m_irq_data;
|
||||
|
||||
std::array<voice_t, 8> m_voice;
|
||||
std::array<env_t, 8> m_dca, m_dco, m_dcw;
|
||||
|
Loading…
Reference in New Issue
Block a user