Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upd933: rework irq to use a timer #11750

Merged
merged 3 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 109 additions & 32 deletions src/devices/sound/upd933.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "upd933.h"

#include <algorithm>
#include <climits>
#include <cmath>

DEFINE_DEVICE_TYPE(UPD933, upd933_device, "upd933", "NEC uPD933")
Expand All @@ -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;

Expand All @@ -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));
Expand Down Expand Up @@ -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;
Expand All @@ -104,7 +115,6 @@ void upd933_device::device_reset()

m_sample_count = 0;
m_last_sample = 0;
m_irq_data = 0;
}

/**************************************************************************/
Expand All @@ -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);
}
}

/**************************************************************************/
Expand All @@ -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();
}

/**************************************************************************/
Expand All @@ -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;

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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();
}
}

Expand Down Expand Up @@ -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)
{
Expand Down
17 changes: 12 additions & 5 deletions src/devices/sound/upd933.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ class upd933_device : public device_t, public device_sound_interface

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();
Expand Down Expand Up @@ -48,6 +49,8 @@ class upd933_device : public device_t, public device_sound_interface
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
Expand All @@ -61,8 +64,12 @@ class upd933_device : public device_t, public device_sound_interface
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);
Expand All @@ -71,8 +78,9 @@ class upd933_device : public device_t, public device_sound_interface
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];
Expand All @@ -85,7 +93,6 @@ class upd933_device : public device_t, public device_sound_interface

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;
Expand Down