#include "ymfm_opq.h"
#include "ymfm_fm.ipp"
#define TEMPORARY_DEBUG_PRINTS (0)
namespace ymfm
{
opq_registers::opq_registers() :
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);
uint16_t zeroval = m_waveform[0][0];
for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
m_waveform[1][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index];
}
void opq_registers::reset()
{
std::fill_n(&m_regdata[0], REGISTERS, 0);
m_regdata[0x10] = m_regdata[0x11] = m_regdata[0x12] = m_regdata[0x13] = 0xc0;
m_regdata[0x14] = m_regdata[0x15] = m_regdata[0x16] = m_regdata[0x17] = 0xc0;
}
void opq_registers::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_lfo_counter);
state.save_restore(m_lfo_am);
state.save_restore(m_regdata);
}
void opq_registers::operator_map(operator_mapping &dest) const
{
static const operator_mapping s_fixed_map =
{ {
operator_list( 0, 8, 16, 24 ), operator_list( 1, 9, 17, 25 ), operator_list( 2, 10, 18, 26 ), operator_list( 3, 11, 19, 27 ), operator_list( 4, 12, 20, 28 ), operator_list( 5, 13, 21, 29 ), operator_list( 6, 14, 22, 30 ), operator_list( 7, 15, 23, 31 ), } };
dest = s_fixed_map;
}
bool opq_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
{
assert(index < REGISTERS);
if ((index & 0xe0) == 0x40 && bitfield(data, 7) != 0)
index += 0xc0;
m_regdata[index] = data;
if (index == 0x05)
{
channel = bitfield(data, 0, 3);
opmask = bitfield(data, 3, 4);
return true;
}
return false;
}
int32_t opq_registers::clock_noise_and_lfo()
{
if (!lfo_enable())
{
m_lfo_counter = 0;
m_lfo_am = 0;
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;
}
uint32_t opq_registers::lfo_am_offset(uint32_t choffs) const
{
uint32_t am_sensitivity = ch_lfo_am_sens(choffs);
if (am_sensitivity == 0)
return 0;
return m_lfo_am << (am_sensitivity - 1);
}
void opq_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache)
{
cache.waveform = &m_waveform[op_waveform(opoffs)][0];
uint32_t block_freq = cache.block_freq = (opoffs & 8) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs);
uint32_t keycode = bitfield(block_freq, 11, 4) << 1;
keycode |= bitfield(0xfe80, bitfield(block_freq, 8, 4));
int32_t detune = int32_t(op_detune(opoffs)) - 0x20;
int32_t abs_detune = std::abs(detune);
int32_t adjust = (abs_detune / 3) * detune_adjustment(3, keycode) + detune_adjustment(abs_detune % 3, keycode);
cache.detune = (detune >= 0) ? adjust : -adjust;
static const uint8_t s_multiple_map[16] = { 1,2,4,6,8,10,12,14,16,18,20,24,30,32,34,36 };
cache.multiple = s_multiple_map[op_multiple(opoffs)];
if (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);
cache.eg_rate[EG_REVERB] = (ch_reverb(choffs) != 0) ? 5*4 : cache.eg_rate[EG_RELEASE];
cache.eg_shift = 0;
}
uint32_t opq_registers::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, 12);
uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
if (pm_sensitivity != 0)
{
fnum += opn_lfo_pm_phase_adjustment(bitfield(cache.block_freq, 5, 7), pm_sensitivity, lfo_raw_pm);
fnum &= 0xfff;
}
uint32_t block = bitfield(cache.block_freq, 12, 3);
uint32_t phase_step = (fnum << block) >> 2;
phase_step += cache.detune;
phase_step &= 0x1ffff;
return (phase_step * cache.multiple) >> 1;
}
std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
{
uint32_t chnum = choffs;
uint32_t opnum = opoffs;
char buffer[256];
int end = 0;
end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
chnum, opnum,
(opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs),
int32_t(op_detune(opoffs)) - 0x20,
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),
ch_output_0(choffs) ? 'L' : '-',
ch_output_1(choffs) ? 'R' : '-');
bool am = (lfo_enable() && 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 = (lfo_enable() && 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 (ch_reverb(choffs))
end += snprintf(&buffer[end], sizeof(buffer) - end, " reverb");
return buffer;
}
ym3806::ym3806(ymfm_interface &intf) :
m_fm(intf)
{
}
void ym3806::reset()
{
m_fm.reset();
}
void ym3806::save_restore(ymfm_saved_state &state)
{
m_fm.save_restore(state);
}
uint8_t ym3806::read_status()
{
uint8_t result = m_fm.status();
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ym3806::read(uint32_t offset)
{
uint8_t result = 0xff;
switch (offset)
{
case 0: result = read_status();
break;
default: debug::log_unexpected_read_write("Unexpected read from YM3806 offset %02X\n", offset);
break;
}
if (TEMPORARY_DEBUG_PRINTS && offset != 0) printf("Read %02X = %02X\n", offset, result);
return result;
}
void ym3806::write(uint32_t offset, uint8_t data)
{
if (TEMPORARY_DEBUG_PRINTS && (offset != 3 || data != 0x71)) printf("Write %02X = %02X\n", offset, data);
m_fm.write(offset, data);
}
void ym3806::generate(output_data *output, uint32_t numsamples)
{
for (uint32_t samp = 0; samp < numsamples; samp++, output++)
{
m_fm.clock(fm_engine::ALL_CHANNELS);
m_fm.output(output->clear(), 0, 32767, fm_engine::ALL_CHANNELS);
output->roundtrip_fp();
}
}
}