#include "ymfm_opn.h"
#include "ymfm_fm.ipp"
namespace ymfm
{
template<bool IsOpnA>
opn_registers_base<IsOpnA>::opn_registers_base() :
m_lfo_counter(0),
m_lfo_am(0)
{
for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
}
template<bool IsOpnA>
void opn_registers_base<IsOpnA>::reset()
{
std::fill_n(&m_regdata[0], REGISTERS, 0);
if (IsOpnA)
{
m_regdata[0xb4] = m_regdata[0xb5] = m_regdata[0xb6] = 0xc0;
m_regdata[0x1b4] = m_regdata[0x1b5] = m_regdata[0x1b6] = 0xc0;
}
}
template<bool IsOpnA>
void opn_registers_base<IsOpnA>::save_restore(ymfm_saved_state &state)
{
if (IsOpnA)
{
state.save_restore(m_lfo_counter);
state.save_restore(m_lfo_am);
}
state.save_restore(m_regdata);
}
template<>
void opn_registers_base<false>::operator_map(operator_mapping &dest) const
{
static const operator_mapping s_fixed_map =
{ {
operator_list( 0, 6, 3, 9 ), operator_list( 1, 7, 4, 10 ), operator_list( 2, 8, 5, 11 ), } };
dest = s_fixed_map;
}
template<>
void opn_registers_base<true>::operator_map(operator_mapping &dest) const
{
static const operator_mapping s_fixed_map =
{ {
operator_list( 0, 6, 3, 9 ), operator_list( 1, 7, 4, 10 ), operator_list( 2, 8, 5, 11 ), operator_list( 12, 18, 15, 21 ), operator_list( 13, 19, 16, 22 ), operator_list( 14, 20, 17, 23 ), } };
dest = s_fixed_map;
}
template<bool IsOpnA>
bool opn_registers_base<IsOpnA>::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
{
assert(index < REGISTERS);
if ((index & 0xf0) == 0xa0)
{
if (bitfield(index, 0, 2) == 3)
return false;
uint32_t latchindex = 0xb8 | bitfield(index, 3);
if (bitfield(index, 2))
m_regdata[latchindex] = data & 0x3f;
else
{
m_regdata[index] = data;
m_regdata[index | 4] = m_regdata[latchindex];
}
return false;
}
else if ((index & 0xf8) == 0xb8)
{
return false;
}
m_regdata[index] = data;
if (index == 0x28)
{
channel = bitfield(data, 0, 2);
if (channel == 3)
return false;
if (IsOpnA)
channel += bitfield(data, 2, 1) * 3;
opmask = bitfield(data, 4, 4);
return true;
}
return false;
}
template<bool IsOpnA>
int32_t opn_registers_base<IsOpnA>::clock_noise_and_lfo()
{
if (!IsOpnA || !lfo_enable())
{
m_lfo_counter = 0;
m_lfo_am = IsOpnA ? 0x3f : 0x00;
return 0;
}
static uint8_t const lfo_max_count[8] = { 109, 78, 72, 68, 63, 45, 9, 6 };
uint32_t subcount = uint8_t(m_lfo_counter++);
if (subcount >= lfo_max_count[lfo_rate()])
{
m_lfo_counter += 0x101 - subcount;
}
m_lfo_am = bitfield(m_lfo_counter, 8, 6);
if (bitfield(m_lfo_counter, 8+6) == 0)
m_lfo_am ^= 0x3f;
int32_t pm = bitfield(m_lfo_counter, 10, 3);
if (bitfield(m_lfo_counter, 10+3))
pm ^= 7;
return bitfield(m_lfo_counter, 10+4) ? -pm : pm;
}
template<bool IsOpnA>
uint32_t opn_registers_base<IsOpnA>::lfo_am_offset(uint32_t choffs) const
{
uint32_t am_shift = (1 << (ch_lfo_am_sens(choffs) ^ 3)) - 1;
return (m_lfo_am << 1) >> am_shift;
}
template<bool IsOpnA>
void opn_registers_base<IsOpnA>::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
{
cache.waveform = &m_waveform[0][0];
uint32_t block_freq = cache.block_freq = ch_block_freq(choffs);
if (multi_freq() && choffs == 2)
{
if (opoffs == 2)
block_freq = cache.block_freq = multi_block_freq(1);
else if (opoffs == 10)
block_freq = cache.block_freq = multi_block_freq(2);
else if (opoffs == 6)
block_freq = cache.block_freq = multi_block_freq(0);
}
uint32_t keycode = bitfield(block_freq, 10, 4) << 1;
keycode |= bitfield(0xfe80, bitfield(block_freq, 7, 4));
cache.detune = detune_adjustment(op_detune(opoffs), keycode);
cache.multiple = op_multiple(opoffs) * 2;
if (cache.multiple == 0)
cache.multiple = 1;
if (!IsOpnA || lfo_enable() == 0 || ch_lfo_pm_sens(choffs) == 0)
cache.phase_step = compute_phase_step(choffs, opoffs, cache, 0);
else
cache.phase_step = opdata_cache::PHASE_STEP_DYNAMIC;
cache.total_level = op_total_level(opoffs) << 3;
cache.eg_sustain = op_sustain_level(opoffs);
cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
cache.eg_sustain <<= 5;
uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3);
cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
}
template<bool IsOpnA>
uint32_t opn_registers_base<IsOpnA>::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
{
uint32_t fnum = bitfield(cache.block_freq, 0, 11) << 1;
uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
if (pm_sensitivity != 0)
{
fnum += opn_lfo_pm_phase_adjustment(bitfield(cache.block_freq, 4, 7), pm_sensitivity, lfo_raw_pm);
fnum &= 0xfff;
}
uint32_t block = bitfield(cache.block_freq, 11, 3);
uint32_t phase_step = (fnum << block) >> 2;
phase_step += cache.detune;
phase_step &= 0x1ffff;
return (phase_step * cache.multiple) >> 1;
}
template<bool IsOpnA>
std::string opn_registers_base<IsOpnA>::log_keyon(uint32_t choffs, uint32_t opoffs)
{
uint32_t chnum = (choffs & 3) + 3 * bitfield(choffs, 8);
uint32_t opnum = (opoffs & 15) - ((opoffs & 15) / 4) + 12 * bitfield(opoffs, 8);
uint32_t block_freq = ch_block_freq(choffs);
if (multi_freq() && choffs == 2)
{
if (opoffs == 2)
block_freq = multi_block_freq(1);
else if (opoffs == 10)
block_freq = multi_block_freq(2);
else if (opoffs == 6)
block_freq = multi_block_freq(0);
}
char buffer[256];
int end = 0;
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X",
chnum, opnum,
block_freq,
op_detune(opoffs),
ch_feedback(choffs),
ch_algorithm(choffs),
op_multiple(opoffs),
op_total_level(opoffs),
op_ksr(opoffs),
op_attack_rate(opoffs),
op_decay_rate(opoffs),
op_sustain_rate(opoffs),
op_release_rate(opoffs),
op_sustain_level(opoffs));
if (OUTPUTS > 1)
end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c",
ch_output_0(choffs) ? 'L' : '-',
ch_output_1(choffs) ? 'R' : '-');
if (op_ssg_eg_enable(opoffs))
end += snprintf(&buffer[end], sizeof(buffer) - end, " ssg=%X", op_ssg_eg_mode(opoffs));
bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0);
if (am)
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs));
bool pm = (ch_lfo_pm_sens(choffs) != 0);
if (pm)
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs));
if (am || pm)
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate());
if (multi_freq() && choffs == 2)
end += snprintf(&buffer[end], sizeof(buffer) - end, " multi=1");
return buffer;
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::add_last(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale)
{
sum0 += m_last.data[0] * scale;
sum1 += m_last.data[1] * scale;
sum2 += m_last.data[2] * scale;
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::clock_and_add(int32_t &sum0, int32_t &sum1, int32_t &sum2, int32_t scale)
{
m_ssg.clock();
m_ssg.output(m_last);
add_last(sum0, sum1, sum2, scale);
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::write_to_output(OutputType *output, int32_t sum0, int32_t sum1, int32_t sum2, int32_t divisor)
{
if (MixTo1)
{
output->data[FirstOutput] = (sum0 + sum1 + sum2) * 2 / (3 * divisor);
}
else
{
output->data[FirstOutput + 0] = sum0 / divisor;
output->data[FirstOutput + 1] = sum1 / divisor;
output->data[FirstOutput + 2] = sum2 / divisor;
}
m_sampindex++;
}
template<typename OutputType, int FirstOutput, bool MixTo1>
ssg_resampler<OutputType, FirstOutput, MixTo1>::ssg_resampler(ssg_engine &ssg) :
m_ssg(ssg),
m_sampindex(0),
m_resampler(&ssg_resampler::resample_nop)
{
m_last.clear();
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_sampindex);
state.save_restore(m_last.data);
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::configure(uint8_t outsamples, uint8_t srcsamples)
{
switch (outsamples * 10 + srcsamples)
{
case 4*10 + 1: m_resampler = &ssg_resampler::resample_n_1<4>; break;
case 2*10 + 1: m_resampler = &ssg_resampler::resample_n_1<2>; break;
case 4*10 + 3: m_resampler = &ssg_resampler::resample_4_3; break;
case 1*10 + 1: m_resampler = &ssg_resampler::resample_n_1<1>; break;
case 2*10 + 3: m_resampler = &ssg_resampler::resample_2_3; break;
case 1*10 + 3: m_resampler = &ssg_resampler::resample_1_n<3>; break;
case 2*10 + 9: m_resampler = &ssg_resampler::resample_2_9; break;
case 1*10 + 6: m_resampler = &ssg_resampler::resample_1_n<6>; break;
case 0*10 + 0: m_resampler = &ssg_resampler::resample_nop; break;
default: assert(false); break;
}
}
template<typename OutputType, int FirstOutput, bool MixTo1>
template<int Multiplier>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_n_1(OutputType *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
if (m_sampindex % Multiplier == 0)
{
m_ssg.clock();
m_ssg.output(m_last);
}
write_to_output(output, m_last.data[0], m_last.data[1], m_last.data[2]);
}
}
template<typename OutputType, int FirstOutput, bool MixTo1>
template<int Divisor>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_1_n(OutputType *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
int32_t sum0 = 0, sum1 = 0, sum2 = 0;
for (int rep = 0; rep < Divisor; rep++)
clock_and_add(sum0, sum1, sum2);
write_to_output(output, sum0, sum1, sum2, Divisor);
}
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_2_9(OutputType *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
int32_t sum0 = 0, sum1 = 0, sum2 = 0;
if (bitfield(m_sampindex, 0) != 0)
add_last(sum0, sum1, sum2, 1);
clock_and_add(sum0, sum1, sum2, 2);
clock_and_add(sum0, sum1, sum2, 2);
clock_and_add(sum0, sum1, sum2, 2);
clock_and_add(sum0, sum1, sum2, 2);
if (bitfield(m_sampindex, 0) == 0)
clock_and_add(sum0, sum1, sum2, 1);
write_to_output(output, sum0, sum1, sum2, 9);
}
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_2_3(OutputType *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
int32_t sum0 = 0, sum1 = 0, sum2 = 0;
if (bitfield(m_sampindex, 0) == 0)
{
clock_and_add(sum0, sum1, sum2, 2);
clock_and_add(sum0, sum1, sum2, 1);
}
else
{
add_last(sum0, sum1, sum2, 1);
clock_and_add(sum0, sum1, sum2, 2);
}
write_to_output(output, sum0, sum1, sum2, 3);
}
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_4_3(OutputType *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
int32_t sum0 = 0, sum1 = 0, sum2 = 0;
int32_t step = bitfield(m_sampindex, 0, 2);
add_last(sum0, sum1, sum2, step);
if (step != 3)
clock_and_add(sum0, sum1, sum2, 3 - step);
write_to_output(output, sum0, sum1, sum2, 3);
}
}
template<typename OutputType, int FirstOutput, bool MixTo1>
void ssg_resampler<OutputType, FirstOutput, MixTo1>::resample_nop(OutputType *output, uint32_t numsamples)
{
m_sampindex += numsamples;
}
ym2203::ym2203(ymfm_interface &intf) :
m_fidelity(OPN_FIDELITY_MAX),
m_address(0),
m_fm(intf),
m_ssg(intf),
m_ssg_resampler(m_ssg)
{
m_last_fm.clear();
update_prescale(m_fm.clock_prescale());
}
void ym2203::reset()
{
m_fm.reset();
m_ssg.reset();
}
void ym2203::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_address);
state.save_restore(m_last_fm.data);
m_fm.save_restore(state);
m_ssg.save_restore(state);
m_ssg_resampler.save_restore(state);
update_prescale(m_fm.clock_prescale());
}
uint8_t ym2203::read_status()
{
uint8_t result = m_fm.status();
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ym2203::read_data()
{
uint8_t result = 0;
if (m_address < 0x10)
{
result = m_ssg.read(m_address & 0x0f);
}
return result;
}
uint8_t ym2203::read(uint32_t offset)
{
uint8_t result = 0xff;
switch (offset & 1)
{
case 0: result = read_status();
break;
case 1: result = read_data();
break;
}
return result;
}
void ym2203::write_address(uint8_t data)
{
m_address = data;
if (m_address >= 0x2d && m_address <= 0x2f)
{
if (m_address == 0x2d)
update_prescale(6);
else if (m_address == 0x2e && m_fm.clock_prescale() == 6)
update_prescale(3);
else if (m_address == 0x2f)
update_prescale(2);
}
}
void ym2203::write_data(uint8_t data)
{
if (m_address < 0x10)
{
m_ssg.write(m_address & 0x0f, data);
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2203::write(uint32_t offset, uint8_t data)
{
switch (offset & 1)
{
case 0: write_address(data);
break;
case 1: write_data(data);
break;
}
}
void ym2203::generate(output_data *output, uint32_t numsamples)
{
if (m_fm_samples_per_output != 0)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
clock_fm();
output->data[0] = m_last_fm.data[0];
}
}
else
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3;
if (step == 0)
clock_fm();
output->data[0] = m_last_fm.data[0];
if (step == 1)
{
clock_fm();
output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2;
}
}
}
m_ssg_resampler.resample(output - numsamples, numsamples);
}
void ym2203::update_prescale(uint8_t prescale)
{
m_fm.set_clock_prescale(prescale);
m_ssg.prescale_changed();
if (m_fidelity == OPN_FIDELITY_MIN)
{
switch (prescale)
{
default:
case 6: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
case 3: m_fm_samples_per_output = 0; m_ssg_resampler.configure(1, 3); break;
case 2: m_fm_samples_per_output = 1; m_ssg_resampler.configure(1, 6); break;
}
}
else if (m_fidelity == OPN_FIDELITY_MED)
{
switch (prescale)
{
default:
case 6: m_fm_samples_per_output = 6; m_ssg_resampler.configure(4, 3); break;
case 3: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
case 2: m_fm_samples_per_output = 2; m_ssg_resampler.configure(1, 3); break;
}
}
else
{
switch (prescale)
{
default:
case 6: m_fm_samples_per_output = 18; m_ssg_resampler.configure(4, 1); break;
case 3: m_fm_samples_per_output = 9; m_ssg_resampler.configure(2, 1); break;
case 2: m_fm_samples_per_output = 6; m_ssg_resampler.configure(1, 1); break;
}
}
if (m_ssg.overridden())
m_ssg_resampler.configure(0, 0);
}
void ym2203::clock_fm()
{
m_fm.clock(fm_engine::ALL_CHANNELS);
m_fm.output(m_last_fm.clear(), 0, 32767, fm_engine::ALL_CHANNELS);
m_last_fm.roundtrip_fp();
}
ym2608::ym2608(ymfm_interface &intf) :
m_fidelity(OPN_FIDELITY_MAX),
m_address(0),
m_irq_enable(0x1f),
m_flag_control(0x1c),
m_fm(intf),
m_ssg(intf),
m_ssg_resampler(m_ssg),
m_adpcm_a(intf, 0),
m_adpcm_b(intf)
{
m_last_fm.clear();
update_prescale(m_fm.clock_prescale());
}
void ym2608::reset()
{
m_fm.reset();
m_ssg.reset();
m_adpcm_a.reset();
m_adpcm_b.reset();
m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff);
m_irq_enable = 0x1f;
m_flag_control = 0x1c;
read_status_hi();
}
void ym2608::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_address);
state.save_restore(m_irq_enable);
state.save_restore(m_flag_control);
state.save_restore(m_last_fm.data);
m_fm.save_restore(state);
m_ssg.save_restore(state);
m_ssg_resampler.save_restore(state);
m_adpcm_a.save_restore(state);
m_adpcm_b.save_restore(state);
}
uint8_t ym2608::read_status()
{
uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ym2608::read_data()
{
uint8_t result = 0;
if (m_address < 0x10)
{
result = m_ssg.read(m_address & 0x0f);
}
else if (m_address == 0xff)
{
result = 1;
}
return result;
}
uint8_t ym2608::read_status_hi()
{
uint8_t status = m_fm.status() & ~(STATUS_ADPCM_B_EOS | STATUS_ADPCM_B_BRDY | STATUS_ADPCM_B_PLAYING);
uint8_t adpcm_status = m_adpcm_b.status();
if ((adpcm_status & adpcm_b_channel::STATUS_EOS) != 0)
status |= STATUS_ADPCM_B_EOS;
if ((adpcm_status & adpcm_b_channel::STATUS_BRDY) != 0)
status |= STATUS_ADPCM_B_BRDY;
if ((adpcm_status & adpcm_b_channel::STATUS_PLAYING) != 0)
status |= STATUS_ADPCM_B_PLAYING;
status &= ~(m_flag_control & 0x1f);
m_fm.set_reset_status(status, ~status);
if (m_fm.intf().ymfm_is_busy())
status |= fm_engine::STATUS_BUSY;
return status;
}
uint8_t ym2608::read_data_hi()
{
uint8_t result = 0;
if ((m_address & 0xff) < 0x10)
{
result = m_adpcm_b.read(m_address & 0x0f);
}
return result;
}
uint8_t ym2608::read(uint32_t offset)
{
uint8_t result = 0;
switch (offset & 3)
{
case 0: result = read_status();
break;
case 1: result = read_data();
break;
case 2: result = read_status_hi();
break;
case 3: result = read_data_hi();
break;
}
return result;
}
void ym2608::write_address(uint8_t data)
{
m_address = data;
if (m_address >= 0x2d && m_address <= 0x2f)
{
if (m_address == 0x2d)
update_prescale(6);
else if (m_address == 0x2e && m_fm.clock_prescale() == 6)
update_prescale(3);
else if (m_address == 0x2f)
update_prescale(2);
}
}
void ym2608::write_data(uint8_t data)
{
if (bitfield(m_address, 8))
return;
if (m_address < 0x10)
{
m_ssg.write(m_address & 0x0f, data);
}
else if (m_address < 0x20)
{
m_adpcm_a.write(m_address & 0x0f, data);
}
else if (m_address == 0x29)
{
m_irq_enable = data;
m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f);
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2608::write_address_hi(uint8_t data)
{
m_address = 0x100 | data;
}
void ym2608::write_data_hi(uint8_t data)
{
if (!bitfield(m_address, 8))
return;
if (m_address < 0x110)
{
m_adpcm_b.write(m_address & 0x0f, data);
}
else if (m_address == 0x110)
{
if (bitfield(data, 7))
m_fm.set_reset_status(0, 0xff);
else
{
m_flag_control = data;
m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x1f);
}
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2608::write(uint32_t offset, uint8_t data)
{
switch (offset & 3)
{
case 0: write_address(data);
break;
case 1: write_data(data);
break;
case 2: write_address_hi(data);
break;
case 3: write_data_hi(data);
break;
}
}
void ym2608::generate(output_data *output, uint32_t numsamples)
{
if (m_fm_samples_per_output != 0)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
clock_fm_and_adpcm();
output->data[0] = m_last_fm.data[0];
output->data[1] = m_last_fm.data[1];
}
}
else
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3;
if (step == 0)
clock_fm_and_adpcm();
output->data[0] = m_last_fm.data[0];
output->data[1] = m_last_fm.data[1];
if (step == 1)
{
clock_fm_and_adpcm();
output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2;
output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2;
}
}
}
m_ssg_resampler.resample(output - numsamples, numsamples);
}
void ym2608::update_prescale(uint8_t prescale)
{
m_fm.set_clock_prescale(prescale);
m_ssg.prescale_changed();
if (m_fidelity == OPN_FIDELITY_MIN)
{
switch (prescale)
{
default:
case 6: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
case 3: m_fm_samples_per_output = 0; m_ssg_resampler.configure(1, 3); break;
case 2: m_fm_samples_per_output = 1; m_ssg_resampler.configure(1, 6); break;
}
}
else if (m_fidelity == OPN_FIDELITY_MED)
{
switch (prescale)
{
default:
case 6: m_fm_samples_per_output = 6; m_ssg_resampler.configure(4, 3); break;
case 3: m_fm_samples_per_output = 3; m_ssg_resampler.configure(2, 3); break;
case 2: m_fm_samples_per_output = 2; m_ssg_resampler.configure(1, 3); break;
}
}
else
{
switch (prescale)
{
default:
case 6: m_fm_samples_per_output = 18; m_ssg_resampler.configure(4, 1); break;
case 3: m_fm_samples_per_output = 9; m_ssg_resampler.configure(2, 1); break;
case 2: m_fm_samples_per_output = 6; m_ssg_resampler.configure(1, 1); break;
}
}
if (m_ssg.overridden())
m_ssg_resampler.configure(0, 0);
}
void ym2608::clock_fm_and_adpcm()
{
uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07;
uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS);
if (bitfield(env_counter, 0, 2) == 0)
m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f);
m_adpcm_b.clock();
m_fm.output(m_last_fm.clear(), 1, 32767, fmmask);
m_adpcm_a.output(m_last_fm, 0x3f);
m_adpcm_b.output(m_last_fm, 1);
m_last_fm.clamp16();
}
ymf288::ymf288(ymfm_interface &intf) :
m_fidelity(OPN_FIDELITY_MAX),
m_address(0),
m_irq_enable(0x03),
m_flag_control(0x03),
m_fm(intf),
m_ssg(intf),
m_ssg_resampler(m_ssg),
m_adpcm_a(intf, 0)
{
m_last_fm.clear();
update_prescale();
}
void ymf288::reset()
{
m_fm.reset();
m_ssg.reset();
m_adpcm_a.reset();
m_adpcm_a.set_start_end(0, 0x0000, 0x01bf); m_adpcm_a.set_start_end(1, 0x01c0, 0x043f); m_adpcm_a.set_start_end(2, 0x0440, 0x1b7f); m_adpcm_a.set_start_end(3, 0x1b80, 0x1cff); m_adpcm_a.set_start_end(4, 0x1d00, 0x1f7f); m_adpcm_a.set_start_end(5, 0x1f80, 0x1fff);
m_irq_enable = 0x03;
m_flag_control = 0x00;
read_status_hi();
}
void ymf288::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_address);
state.save_restore(m_irq_enable);
state.save_restore(m_flag_control);
state.save_restore(m_last_fm.data);
m_fm.save_restore(state);
m_ssg.save_restore(state);
m_ssg_resampler.save_restore(state);
m_adpcm_a.save_restore(state);
}
uint8_t ymf288::read_status()
{
uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ymf288::read_data()
{
uint8_t result = 0;
if (m_address < 0x0e)
{
result = m_ssg.read(m_address & 0x0f);
}
else if (m_address < 0x10)
{
result = 0xff;
}
else if (m_address == 0xff)
{
result = 2;
}
else if (ymf288_mode())
{
result = m_fm.regs().read(m_address);
}
return result;
}
uint8_t ymf288::read_status_hi()
{
uint8_t status = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
status &= ~(m_flag_control & 0x03);
m_fm.set_reset_status(status, ~status);
if (m_fm.intf().ymfm_is_busy())
status |= fm_engine::STATUS_BUSY;
return status;
}
uint8_t ymf288::read(uint32_t offset)
{
uint8_t result = 0;
switch (offset & 3)
{
case 0: result = read_status();
break;
case 1: result = read_data();
break;
case 2: result = read_status_hi();
break;
case 3: debug::log_unexpected_read_write("Unexpected read from YMF288 offset %d\n", offset & 3);
break;
}
return result;
}
void ymf288::write_address(uint8_t data)
{
m_address = data;
if (ymf288_mode())
m_fm.intf().ymfm_set_busy_end(16);
}
void ymf288::write_data(uint8_t data)
{
if (bitfield(m_address, 8))
return;
int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale();
if (m_address < 0x0e)
{
m_ssg.write(m_address & 0x0f, data);
}
else if (m_address < 0x10)
{
}
else if (m_address < 0x20)
{
m_adpcm_a.write(m_address & 0x0f, data);
busy_cycles = 32 * m_fm.clock_prescale();
}
else if (m_address == 0x27)
{
data &= 0x7f;
m_fm.write(m_address, data);
}
else if (m_address == 0x29)
{
m_irq_enable = data;
m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03);
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(busy_cycles);
}
void ymf288::write_address_hi(uint8_t data)
{
m_address = 0x100 | data;
if (ymf288_mode())
m_fm.intf().ymfm_set_busy_end(16);
}
void ymf288::write_data_hi(uint8_t data)
{
if (!bitfield(m_address, 8))
return;
int busy_cycles = ymf288_mode() ? 16 : 32 * m_fm.clock_prescale();
if (m_address == 0x110)
{
if (bitfield(data, 7))
m_fm.set_reset_status(0, 0xff);
else
{
m_flag_control = data;
m_fm.set_irq_mask(m_irq_enable & ~m_flag_control & 0x03);
}
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(busy_cycles);
}
void ymf288::write(uint32_t offset, uint8_t data)
{
switch (offset & 3)
{
case 0: write_address(data);
break;
case 1: write_data(data);
break;
case 2: write_address_hi(data);
break;
case 3: write_data_hi(data);
break;
}
}
void ymf288::generate(output_data *output, uint32_t numsamples)
{
if (m_fm_samples_per_output != 0)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
clock_fm_and_adpcm();
output->data[0] = m_last_fm.data[0];
output->data[1] = m_last_fm.data[1];
}
}
else
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
uint32_t step = (m_ssg_resampler.sampindex() + samp) % 3;
if (step == 0)
clock_fm_and_adpcm();
output->data[0] = m_last_fm.data[0];
output->data[1] = m_last_fm.data[1];
if (step == 1)
{
clock_fm_and_adpcm();
output->data[0] = (output->data[0] + m_last_fm.data[0]) / 2;
output->data[1] = (output->data[1] + m_last_fm.data[1]) / 2;
}
}
}
m_ssg_resampler.resample(output - numsamples, numsamples);
}
void ymf288::update_prescale()
{
if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED)
{
m_fm_samples_per_output = 1;
m_ssg_resampler.configure(2, 9);
}
else
{
m_fm_samples_per_output = 9;
m_ssg_resampler.configure(2, 1);
}
if (m_ssg.overridden())
m_ssg_resampler.configure(0, 0);
}
void ymf288::clock_fm_and_adpcm()
{
uint32_t fmmask = bitfield(m_irq_enable, 7) ? 0x3f : 0x07;
uint32_t env_counter = m_fm.clock(fm_engine::ALL_CHANNELS);
if (bitfield(env_counter, 0, 2) == 0)
m_adpcm_a.clock(bitfield(env_counter, 2) ? 0x0f : 0x3f);
m_fm.output(m_last_fm.clear(), 1, 32767, fmmask);
m_adpcm_a.output(m_last_fm, 0x3f);
}
ym2610::ym2610(ymfm_interface &intf, uint8_t channel_mask) :
m_fidelity(OPN_FIDELITY_MAX),
m_address(0),
m_fm_mask(channel_mask),
m_eos_status(0x00),
m_flag_mask(EOS_FLAGS_MASK),
m_fm(intf),
m_ssg(intf),
m_ssg_resampler(m_ssg),
m_adpcm_a(intf, 8),
m_adpcm_b(intf, 8)
{
update_prescale();
}
void ym2610::reset()
{
m_fm.reset();
m_ssg.reset();
m_adpcm_a.reset();
m_adpcm_b.reset();
m_eos_status = 0x00;
m_flag_mask = EOS_FLAGS_MASK;
}
void ym2610::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_address);
state.save_restore(m_eos_status);
state.save_restore(m_flag_mask);
m_fm.save_restore(state);
m_ssg.save_restore(state);
m_ssg_resampler.save_restore(state);
m_adpcm_a.save_restore(state);
m_adpcm_b.save_restore(state);
}
uint8_t ym2610::read_status()
{
uint8_t result = m_fm.status() & (fm_engine::STATUS_TIMERA | fm_engine::STATUS_TIMERB);
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ym2610::read_data()
{
uint8_t result = 0;
if (m_address < 0x0e)
{
result = m_ssg.read(m_address & 0x0f);
}
else if (m_address < 0x10)
{
result = 0xff;
}
else if (m_address == 0xff)
{
result = 1;
}
return result;
}
uint8_t ym2610::read_status_hi()
{
return m_eos_status & m_flag_mask;
}
uint8_t ym2610::read_data_hi()
{
uint8_t result = 0;
return result;
}
uint8_t ym2610::read(uint32_t offset)
{
uint8_t result = 0;
switch (offset & 3)
{
case 0: result = read_status();
break;
case 1: result = read_data();
break;
case 2: result = read_status_hi();
break;
case 3: result = read_data_hi();
break;
}
return result;
}
void ym2610::write_address(uint8_t data)
{
m_address = data;
}
void ym2610::write_data(uint8_t data)
{
if (bitfield(m_address, 8))
return;
if (m_address < 0x0e)
{
m_ssg.write(m_address & 0x0f, data);
}
else if (m_address < 0x10)
{
}
else if (m_address < 0x1c)
{
if (m_address == 0x10)
data = (data | 0x20) & ~0x40;
m_adpcm_b.write(m_address & 0x0f, data);
}
else if (m_address == 0x1c)
{
m_flag_mask = ~data & EOS_FLAGS_MASK;
m_eos_status &= ~(data & EOS_FLAGS_MASK);
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2610::write_address_hi(uint8_t data)
{
m_address = 0x100 | data;
}
void ym2610::write_data_hi(uint8_t data)
{
if (!bitfield(m_address, 8))
return;
if (m_address < 0x130)
{
m_adpcm_a.write(m_address & 0x3f, data);
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2610::write(uint32_t offset, uint8_t data)
{
switch (offset & 3)
{
case 0: write_address(data);
break;
case 1: write_data(data);
break;
case 2: write_address_hi(data);
break;
case 3: write_data_hi(data);
break;
}
}
void ym2610::generate(output_data *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
if ((m_ssg_resampler.sampindex() + samp) % m_fm_samples_per_output == 0)
clock_fm_and_adpcm();
output->data[0] = m_last_fm.data[0];
output->data[1] = m_last_fm.data[1];
}
m_ssg_resampler.resample(output - numsamples, numsamples);
}
void ym2610::update_prescale()
{
if (m_fidelity == OPN_FIDELITY_MIN || m_fidelity == OPN_FIDELITY_MED)
{
m_fm_samples_per_output = 1;
m_ssg_resampler.configure(2, 9);
}
else
{
m_fm_samples_per_output = 9;
m_ssg_resampler.configure(2, 1);
}
if (m_ssg.overridden())
m_ssg_resampler.configure(0, 0);
}
void ym2610::clock_fm_and_adpcm()
{
uint32_t env_counter = m_fm.clock(m_fm_mask);
if (bitfield(env_counter, 0, 2) == 0)
m_eos_status |= m_adpcm_a.clock(0x3f);
m_adpcm_b.clock();
uint8_t live_eos = ((m_adpcm_b.status() & adpcm_b_channel::STATUS_EOS) != 0) ? 0x40 : 0x00;
if (((live_eos ^ m_eos_status) & 0x40) != 0)
m_eos_status = (m_eos_status & ~0xc0) | live_eos | (live_eos << 1);
m_fm.output(m_last_fm.clear(), 1, 32767, m_fm_mask);
m_adpcm_a.output(m_last_fm, 0x3f);
m_adpcm_b.output(m_last_fm, 1);
m_last_fm.clamp16();
}
ym2612::ym2612(ymfm_interface &intf) :
m_address(0),
m_dac_data(0),
m_dac_enable(0),
m_fm(intf)
{
}
void ym2612::reset()
{
m_fm.reset();
}
void ym2612::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_address);
state.save_restore(m_dac_data);
state.save_restore(m_dac_enable);
m_fm.save_restore(state);
}
uint8_t ym2612::read_status()
{
uint8_t result = m_fm.status();
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ym2612::read(uint32_t offset)
{
uint8_t result = 0;
switch (offset & 3)
{
case 0: result = read_status();
break;
case 1: case 2: case 3: debug::log_unexpected_read_write("Unexpected read from YM2612 offset %d\n", offset & 3);
break;
}
return result;
}
void ym2612::write_address(uint8_t data)
{
m_address = data;
}
void ym2612::write_data(uint8_t data)
{
if (bitfield(m_address, 8))
return;
if (m_address == 0x2a)
{
m_dac_data = (m_dac_data & ~0x1fe) | ((data ^ 0x80) << 1);
}
else if (m_address == 0x2b)
{
m_dac_enable = bitfield(data, 7);
}
else if (m_address == 0x2c)
{
m_dac_data = (m_dac_data & ~1) | bitfield(data, 3);
}
else
{
m_fm.write(m_address, data);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2612::write_address_hi(uint8_t data)
{
m_address = 0x100 | data;
}
void ym2612::write_data_hi(uint8_t data)
{
if (!bitfield(m_address, 8))
return;
m_fm.write(m_address, data);
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2612::write(uint32_t offset, uint8_t data)
{
switch (offset & 3)
{
case 0: write_address(data);
break;
case 1: write_data(data);
break;
case 2: write_address_hi(data);
break;
case 3: write_data_hi(data);
break;
}
}
void ym2612::generate(output_data *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
m_fm.clock(fm_engine::ALL_CHANNELS);
output->clear();
output_data temp;
int const last_fm_channel = m_dac_enable ? 5 : 6;
for (int chan = 0; chan < last_fm_channel; chan++)
{
m_fm.output(temp.clear(), 5, 256, 1 << chan);
output->data[0] += dac_discontinuity(temp.data[0]);
output->data[1] += dac_discontinuity(temp.data[1]);
}
if (m_dac_enable)
{
int32_t dacval = dac_discontinuity(int16_t(m_dac_data << 7) >> 7);
output->data[0] += m_fm.regs().ch_output_0(0x102) ? dacval : dac_discontinuity(0);
output->data[1] += m_fm.regs().ch_output_1(0x102) ? dacval : dac_discontinuity(0);
}
output->data[0] = (output->data[0] * 128) * 64 / (6 * 65);
output->data[1] = (output->data[1] * 128) * 64 / (6 * 65);
}
}
void ym3438::generate(output_data *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
m_fm.clock(fm_engine::ALL_CHANNELS);
if (!m_dac_enable)
{
m_fm.output(output->clear(), 5, 256, fm_engine::ALL_CHANNELS);
}
else
{
int32_t dacval = int16_t(m_dac_data << 7) >> 7;
output->data[0] = m_fm.regs().ch_output_0(0x102) ? dacval : 0;
output->data[1] = m_fm.regs().ch_output_1(0x102) ? dacval : 0;
m_fm.output(*output, 5, 256, fm_engine::ALL_CHANNELS ^ (1 << 5));
}
output->data[0] = (output->data[0] * 128) / 6;
output->data[1] = (output->data[1] * 128) / 6;
}
}
void ymf276::generate(output_data *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
m_fm.clock(fm_engine::ALL_CHANNELS);
if (!m_dac_enable)
{
m_fm.output(output->clear(), 0, 8191, fm_engine::ALL_CHANNELS);
}
else
{
int32_t dacval = int16_t(m_dac_data << 7) >> 7;
output->data[0] = m_fm.regs().ch_output_0(0x102) ? dacval : 0;
output->data[1] = m_fm.regs().ch_output_1(0x102) ? dacval : 0;
m_fm.output(*output, 0, 8191, fm_engine::ALL_CHANNELS ^ (1 << 5));
}
output->data[0] = clamp(output->data[0] >> 1, -32768, 32767);
output->data[1] = clamp(output->data[1] >> 1, -32768, 32767);
}
}
}