#include "ymfm_ssg.h"
namespace ymfm
{
void ssg_registers::reset()
{
std::fill_n(&m_regdata[0], REGISTERS, 0);
}
void ssg_registers::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_regdata);
}
ssg_engine::ssg_engine(ymfm_interface &intf) :
m_intf(intf),
m_tone_count{ 0,0,0 },
m_tone_state{ 0,0,0 },
m_envelope_count(0),
m_envelope_state(0),
m_noise_count(0),
m_noise_state(1),
m_override(nullptr)
{
}
void ssg_engine::reset()
{
if (m_override != nullptr)
return m_override->ssg_reset();
m_regs.reset();
for (int chan = 0; chan < 3; chan++)
{
m_tone_count[chan] = 0;
m_tone_state[chan] = 0;
}
m_envelope_count = 0;
m_envelope_state = 0;
m_noise_count = 0;
m_noise_state = 1;
}
void ssg_engine::save_restore(ymfm_saved_state &state)
{
m_regs.save_restore(state);
state.save_restore(m_tone_count);
state.save_restore(m_tone_state);
state.save_restore(m_envelope_count);
state.save_restore(m_envelope_state);
state.save_restore(m_noise_count);
state.save_restore(m_noise_state);
}
void ssg_engine::clock()
{
for (int chan = 0; chan < 3; chan++)
{
m_tone_count[chan]++;
if (m_tone_count[chan] >= m_regs.ch_tone_period(chan))
{
m_tone_state[chan] ^= 1;
m_tone_count[chan] = 0;
}
}
m_noise_count++;
if ((m_noise_count >> 1) >= m_regs.noise_period() && m_noise_count != 1)
{
m_noise_state ^= (bitfield(m_noise_state, 0) ^ bitfield(m_noise_state, 3)) << 17;
m_noise_state >>= 1;
m_noise_count = 0;
}
m_envelope_count++;
if (m_envelope_count >= m_regs.envelope_period())
{
m_envelope_state++;
m_envelope_count = 0;
}
}
void ssg_engine::output(output_data &output)
{
static int16_t const s_amplitudes[32] =
{
0, 32, 78, 141, 178, 222, 262, 306,
369, 441, 509, 585, 701, 836, 965, 1112,
1334, 1595, 1853, 2146, 2576, 3081, 3576, 4135,
5000, 6006, 7023, 8155, 9963,11976,14132,16382
};
uint32_t envelope_volume;
if ((m_regs.envelope_hold() | (m_regs.envelope_continue() ^ 1)) && m_envelope_state >= 32)
{
m_envelope_state = 32;
envelope_volume = ((m_regs.envelope_attack() ^ m_regs.envelope_alternate()) & m_regs.envelope_continue()) ? 31 : 0;
}
else
{
uint32_t attack = m_regs.envelope_attack();
if (m_regs.envelope_alternate())
attack ^= bitfield(m_envelope_state, 5);
envelope_volume = (m_envelope_state & 31) ^ (attack ? 0 : 31);
}
for (int chan = 0; chan < 3; chan++)
{
uint32_t noise_on = m_regs.ch_noise_enable_n(chan) | m_noise_state;
uint32_t tone_on = m_regs.ch_tone_enable_n(chan) | m_tone_state[chan];
uint32_t volume;
if ((noise_on & tone_on) == 0)
volume = 0;
else if (m_regs.ch_envelope_enable(chan))
volume = envelope_volume;
else
{
volume = m_regs.ch_amplitude(chan) * 2;
if (volume != 0)
volume |= 1;
}
output.data[chan] = s_amplitudes[volume];
}
}
uint8_t ssg_engine::read(uint32_t regnum)
{
if (m_override != nullptr)
return m_override->ssg_read(regnum);
if (regnum == 0x0e && !m_regs.io_a_out())
return m_intf.ymfm_external_read(ACCESS_IO, 0);
else if (regnum == 0x0f && !m_regs.io_b_out())
return m_intf.ymfm_external_read(ACCESS_IO, 1);
return m_regs.read(regnum);
}
void ssg_engine::write(uint32_t regnum, uint8_t data)
{
if (m_override != nullptr)
return m_override->ssg_write(regnum, data);
m_regs.write(regnum, data);
if (regnum == 0x0d)
m_envelope_state = 0;
else if (regnum == 0x0e && m_regs.io_a_out())
m_intf.ymfm_external_write(ACCESS_IO, 0, data);
else if (regnum == 0x0f && m_regs.io_b_out())
m_intf.ymfm_external_write(ACCESS_IO, 1, data);
}
}