#include "ymfm_opz.h"
#include "ymfm_fm.ipp"
#define TEMPORARY_DEBUG_PRINTS (0)
namespace ymfm
{
opz_registers::opz_registers() :
m_lfo_counter{ 0, 0 },
m_noise_lfsr(1),
m_noise_counter(0),
m_noise_state(0),
m_noise_lfo(0),
m_lfo_am{ 0, 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] = std::min<uint16_t>(2 * (m_waveform[0][index] & 0x7fff), zeroval) | (bitfield(index, 9) << 15);
for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
{
m_waveform[2][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index];
m_waveform[3][index] = bitfield(index, 9) ? zeroval : m_waveform[1][index];
m_waveform[4][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index * 2];
m_waveform[5][index] = bitfield(index, 9) ? zeroval : m_waveform[1][index * 2];
m_waveform[6][index] = bitfield(index, 9) ? zeroval : m_waveform[0][(index * 2) & 0x1ff];
m_waveform[7][index] = bitfield(index, 9) ? zeroval : m_waveform[1][(index * 2) & 0x1ff];
}
for (uint32_t index = 0; index < LFO_WAVEFORM_LENGTH; index++)
{
uint8_t am = index ^ 0xff;
uint8_t pm = index;
m_lfo_waveform[0][index] = am | (pm << 8);
am = bitfield(index, 7) ? 0 : 0xff;
pm = am ^ 0x80;
m_lfo_waveform[1][index] = am | (pm << 8);
am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1);
pm = bitfield(index, 6) ? am : ~am;
m_lfo_waveform[2][index] = am | (pm << 8);
m_lfo_waveform[3][index] = 0;
}
}
void opz_registers::reset()
{
std::fill_n(&m_regdata[0], REGISTERS, 0);
std::fill_n(&m_phase_substep[0], OPERATORS, 0);
m_regdata[0x30] = m_regdata[0x31] = m_regdata[0x32] = m_regdata[0x33] = 0x01;
m_regdata[0x34] = m_regdata[0x35] = m_regdata[0x36] = m_regdata[0x37] = 0x01;
}
void opz_registers::save_restore(ymfm_saved_state &state)
{
state.save_restore(m_lfo_counter);
state.save_restore(m_lfo_am);
state.save_restore(m_noise_lfsr);
state.save_restore(m_noise_counter);
state.save_restore(m_noise_state);
state.save_restore(m_noise_lfo);
state.save_restore(m_regdata);
state.save_restore(m_phase_substep);
}
void opz_registers::operator_map(operator_mapping &dest) const
{
static const operator_mapping s_fixed_map =
{ {
operator_list( 0, 16, 8, 24 ), operator_list( 1, 17, 9, 25 ), operator_list( 2, 18, 10, 26 ), operator_list( 3, 19, 11, 27 ), operator_list( 4, 20, 12, 28 ), operator_list( 5, 21, 13, 29 ), operator_list( 6, 22, 14, 30 ), operator_list( 7, 23, 15, 31 ), } };
dest = s_fixed_map;
}
bool opz_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint32_t &opmask)
{
assert(index < REGISTERS);
if (index == 0x17 && bitfield(data, 7) != 0)
m_regdata[0x188] = data;
else if (index == 0x19 && bitfield(data, 7) != 0)
m_regdata[0x189] = data;
else if ((index & 0xf8) == 0x38 && bitfield(data, 7) != 0)
m_regdata[0x180 + (index & 7)] = data;
else if ((index & 0xe0) == 0x40 && bitfield(data, 7) != 0)
m_regdata[0x100 + (index & 0x1f)] = data;
else if ((index & 0xe0) == 0xc0 && bitfield(data, 5) != 0)
m_regdata[0x120 + (index & 0x1f)] = data;
else if (index < 0x100)
m_regdata[index] = data;
if (index == 0x08)
{
int chan = bitfield(data, 0, 3);
if (TEMPORARY_DEBUG_PRINTS)
printf("Loading preset %d\n", chan);
m_regdata[0xe0 + chan + 0] = m_regdata[0x140 + chan + 0];
m_regdata[0xe0 + chan + 8] = m_regdata[0x140 + chan + 8];
m_regdata[0xe0 + chan + 16] = m_regdata[0x140 + chan + 16];
m_regdata[0xe0 + chan + 24] = m_regdata[0x140 + chan + 24];
m_regdata[0x120 + chan + 0] = m_regdata[0x160 + chan + 0];
m_regdata[0x120 + chan + 8] = m_regdata[0x160 + chan + 8];
m_regdata[0x120 + chan + 16] = m_regdata[0x160 + chan + 16];
m_regdata[0x120 + chan + 24] = m_regdata[0x160 + chan + 24];
}
bool is_setting_preset = (bitfield(m_regdata[0x100 + (index & 0x1f)], 7) != 0);
if (is_setting_preset)
{
if ((index & 0xe0) == 0xe0)
{
m_regdata[0x140 + (index & 0x1f)] = data;
m_regdata[0x100 + (index & 0x1f)] &= 0x7f;
}
else if ((index & 0xe0) == 0xc0 && bitfield(data, 5) != 0)
m_regdata[0x160 + (index & 0x1f)] = data;
}
if ((index & 0xf8) == 0x20 && bitfield(index, 0, 3) == bitfield(m_regdata[0x08], 0, 3))
{
channel = bitfield(index, 0, 3);
opmask = ch_key_on(channel) ? 0xf : 0;
if (opmask != 0)
{
if (lfo_sync())
m_lfo_counter[0] = 0;
if (lfo2_sync())
m_lfo_counter[1] = 0;
}
return true;
}
return false;
}
int32_t opz_registers::clock_noise_and_lfo()
{
uint32_t freq = noise_frequency();
for (int rep = 0; rep < 2; rep++)
{
m_noise_lfsr <<= 1;
m_noise_lfsr |= bitfield(m_noise_lfsr, 17) ^ bitfield(m_noise_lfsr, 14) ^ 1;
if (m_noise_counter++ >= freq)
{
m_noise_counter = 0;
m_noise_state = bitfield(m_noise_lfsr, 17);
}
}
uint32_t rate0 = lfo_rate();
uint32_t rate1 = lfo2_rate();
m_lfo_counter[0] += (0x10 | bitfield(rate0, 0, 4)) << bitfield(rate0, 4, 4);
m_lfo_counter[1] += (0x10 | bitfield(rate1, 0, 4)) << bitfield(rate1, 4, 4);
uint32_t lfo0 = bitfield(m_lfo_counter[0], 22, 8);
uint32_t lfo1 = bitfield(m_lfo_counter[1], 22, 8);
uint32_t lfo_noise = bitfield(m_noise_lfsr, 17, 8);
m_lfo_waveform[3][(lfo0 + 1) & 0xff] = lfo_noise | (lfo_noise << 8);
m_lfo_waveform[3][(lfo1 + 1) & 0xff] = lfo_noise | (lfo_noise << 8);
int32_t ampm0 = m_lfo_waveform[lfo_waveform()][lfo0];
int32_t ampm1 = m_lfo_waveform[lfo2_waveform()][lfo1];
m_lfo_am[0] = ((ampm0 & 0xff) * lfo_am_depth()) >> 7;
m_lfo_am[1] = ((ampm1 & 0xff) * lfo2_am_depth()) >> 7;
int32_t pm0 = ((ampm0 >> 8) * int32_t(lfo_pm_depth())) >> 7;
int32_t pm1 = ((ampm1 >> 8) * int32_t(lfo2_pm_depth())) >> 7;
return (pm0 & 0xff) | (pm1 << 8);
}
uint32_t opz_registers::lfo_am_offset(uint32_t choffs) const
{
uint32_t result = 0;
uint32_t am_sensitivity = ch_lfo_am_sens(choffs);
if (am_sensitivity != 0)
result = m_lfo_am[0] << (am_sensitivity - 1);
uint32_t am_sensitivity2 = ch_lfo2_am_sens(choffs);
if (am_sensitivity2 != 0)
result += m_lfo_am[1] << (am_sensitivity2 - 1);
return result;
}
void opz_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 = ch_block_freq(choffs);
uint32_t keycode = bitfield(block_freq, 8, 5);
cache.detune = detune_adjustment(op_detune(opoffs), keycode);
cache.multiple = op_multiple(opoffs) << 4;
if (cache.multiple == 0)
cache.multiple = 0x08;
cache.multiple |= op_fine(opoffs);
if (!op_fix_mode(opoffs) && (lfo_pm_depth() == 0 || ch_lfo_pm_sens(choffs) == 0) && (lfo2_pm_depth() == 0 || ch_lfo2_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] = cache.eg_rate[EG_RELEASE];
uint32_t reverb = op_reverb_rate(opoffs);
if (reverb != 0)
cache.eg_rate[EG_REVERB] = std::min<uint32_t>(effective_rate(reverb * 4 + 2, ksrval), cache.eg_rate[EG_REVERB]);
cache.eg_shift = ((opoffs & 0x18) == 0) ? 0 : op_eg_shift(opoffs);
}
uint32_t opz_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm)
{
uint32_t phase_step;
if (op_fix_mode(opoffs))
{
uint32_t freq = op_fix_frequency(opoffs) << 4;
if (freq == 0)
freq = 8;
freq |= op_fine(opoffs);
freq <<= op_fix_range(opoffs);
uint32_t substep = m_phase_substep[opoffs];
substep += 75 * freq;
phase_step = substep >> 12;
m_phase_substep[opoffs] = substep & 0xfff;
return phase_step;
}
else
{
static const int16_t s_detune2_delta[4] = { 0, (600*64+50)/100, (781*64+50)/100, (950*64+50)/100 };
int32_t delta = s_detune2_delta[op_detune2(opoffs)];
uint32_t pm_sensitivity = ch_lfo_pm_sens(choffs);
if (pm_sensitivity != 0)
{
if (pm_sensitivity < 6)
delta += int8_t(lfo_raw_pm) >> (6 - pm_sensitivity);
else
delta += int8_t(lfo_raw_pm) << (pm_sensitivity - 5);
}
uint32_t pm_sensitivity2 = ch_lfo2_pm_sens(choffs);
if (pm_sensitivity2 != 0)
{
if (pm_sensitivity2 < 6)
delta += int8_t(lfo_raw_pm >> 8) >> (6 - pm_sensitivity2);
else
delta += int8_t(lfo_raw_pm >> 8) << (pm_sensitivity2 - 5);
}
phase_step = opm_key_code_to_phase_step(cache.block_freq, delta);
phase_step += cache.detune;
return (phase_step * cache.multiple) >> 4;
}
}
std::string opz_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", chnum, opnum);
if (op_fix_mode(opoffs))
end += snprintf(&buffer[end], sizeof(buffer) - end, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(opoffs));
else
end += snprintf(&buffer[end], sizeof(buffer) - end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs));
end += snprintf(&buffer[end], sizeof(buffer) - end, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
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),
ch_output_0(choffs) ? 'L' : '-',
ch_output_1(choffs) ? 'R' : '-');
if (op_eg_shift(opoffs) != 0)
end += snprintf(&buffer[end], sizeof(buffer) - end, " egshift=%u", op_eg_shift(opoffs));
bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
if (am)
end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth());
bool pm = (lfo_pm_depth() != 0 && ch_lfo_pm_sens(choffs) != 0);
if (pm)
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth());
if (am || pm)
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]);
bool am2 = (lfo2_am_depth() != 0 && ch_lfo2_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0);
if (am2)
end += snprintf(&buffer[end], sizeof(buffer) - end, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth());
bool pm2 = (lfo2_pm_depth() != 0 && ch_lfo2_pm_sens(choffs) != 0);
if (pm2)
end += snprintf(&buffer[end], sizeof(buffer) - end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth());
if (am2 || pm2)
end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]);
if (op_reverb_rate(opoffs) != 0)
end += snprintf(&buffer[end], sizeof(buffer) - end, " rev=%u", op_reverb_rate(opoffs));
if (op_waveform(opoffs) != 0)
end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs));
if (noise_enable() && opoffs == 31)
end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1");
return buffer;
}
ym2414::ym2414(ymfm_interface &intf) :
m_address(0),
m_fm(intf)
{
}
void ym2414::reset()
{
m_fm.reset();
}
void ym2414::save_restore(ymfm_saved_state &state)
{
m_fm.save_restore(state);
state.save_restore(m_address);
}
uint8_t ym2414::read_status()
{
uint8_t result = m_fm.status();
if (m_fm.intf().ymfm_is_busy())
result |= fm_engine::STATUS_BUSY;
return result;
}
uint8_t ym2414::read(uint32_t offset)
{
uint8_t result = 0xff;
switch (offset & 1)
{
case 0: debug::log_unexpected_read_write("Unexpected read from YM2414 offset %d\n", offset & 3);
break;
case 1: result = read_status();
break;
}
return result;
}
void ym2414::write_address(uint8_t data)
{
m_address = data;
}
void ym2414::write_data(uint8_t data)
{
m_fm.write(m_address, data);
if (TEMPORARY_DEBUG_PRINTS)
{
switch (m_address & 0xe0)
{
case 0x00:
printf("CTL %02X = %02X\n", m_address, data);
break;
case 0x20:
switch (m_address & 0xf8)
{
case 0x20: printf("R/FBL/ALG %d = %02X\n", m_address & 7, data); break;
case 0x28: printf("KC %d = %02X\n", m_address & 7, data); break;
case 0x30: printf("KF/M %d = %02X\n", m_address & 7, data); break;
case 0x38: printf("PMS/AMS %d = %02X\n", m_address & 7, data); break;
}
break;
case 0x40:
if (bitfield(data, 7) == 0)
printf("DT1/MUL %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
else
printf("OW/FINE %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
break;
case 0x60:
printf("TL %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
break;
case 0x80:
printf("KRS/FIX/AR %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
break;
case 0xa0:
printf("A/D1R %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
break;
case 0xc0:
if (bitfield(data, 5) == 0)
printf("DT2/D2R %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
else
printf("EGS/REV %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
break;
case 0xe0:
printf("D1L/RR %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, data);
break;
}
}
if (m_address == 0x1b)
{
m_fm.intf().ymfm_external_write(ACCESS_IO, 0, data >> 6);
}
m_fm.intf().ymfm_set_busy_end(32 * m_fm.clock_prescale());
}
void ym2414::write(uint32_t offset, uint8_t data)
{
switch (offset & 1)
{
case 0: write_address(data);
break;
case 1: write_data(data);
break;
}
}
void ym2414::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();
}
}
}