ymfm: Some progress on OPZ. Some instruments in the TX81Z sound kind of ok now.

This commit is contained in:
Aaron Giles 2021-05-23 13:24:36 -07:00
parent e3aa064b18
commit ac96627377
2 changed files with 115 additions and 61 deletions

View File

@ -31,6 +31,8 @@
#include "ymfm_opz.h"
#include "ymfm_fm.ipp"
#define TEMPORARY_DEBUG_PRINTS (0)
//
// OPZ (aka YM2414)
//
@ -102,10 +104,15 @@ opz_registers::opz_registers() :
for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
m_waveform[0][index] = abs_sin_attenuation(index) | (bitfield(index, 9) << 15);
// we only have the diagrams to judge from, but suspecting waveform 1 (and
// derived waveforms) are sin^2, based on OPX description of similar wave-
// forms; since our sin table is logarithmic, this ends up just being
// 2*existing value
uint16_t zeroval = m_waveform[0][0];
for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
m_waveform[1][index] = (zeroval - m_waveform[0][(index & 0x1ff) ^ 0x100]) | (bitfield(index, 9) << 15);
m_waveform[1][index] = std::min<uint16_t>(2 * (m_waveform[0][index] & 0x7fff), zeroval) | (bitfield(index, 9) << 15);
// remaining waveforms are just derivations of the 2 main ones
for (uint32_t index = 0; index < WAVEFORM_LENGTH; index++)
{
m_waveform[2][index] = bitfield(index, 9) ? zeroval : m_waveform[0][index];
@ -209,17 +216,17 @@ bool opz_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint3
assert(index < REGISTERS);
// special mappings:
// 0x16 -> 0x148 if bit 7 is set
// 0x19 -> 0x149 if bit 7 is set
// 0x38..0x3F -> 0x140..0x147 if bit 7 is set
// 0x16 -> 0x188 if bit 7 is set
// 0x19 -> 0x189 if bit 7 is set
// 0x38..0x3F -> 0x180..0x187 if bit 7 is set
// 0x40..0x5F -> 0x100..0x11F if bit 7 is set
// 0xC0..0xDF -> 0x120..0x13F if bit 5 is set
if (index == 0x17 && bitfield(data, 7) != 0)
m_regdata[0x148] = data;
m_regdata[0x188] = data;
else if (index == 0x19 && bitfield(data, 7) != 0)
m_regdata[0x149] = data;
m_regdata[0x189] = data;
else if ((index & 0xf8) == 0x38 && bitfield(data, 7) != 0)
m_regdata[0x140 + (index & 7)] = data;
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)
@ -227,16 +234,60 @@ bool opz_registers::write(uint16_t index, uint8_t data, uint32_t &channel, uint3
else if (index < 0x100)
m_regdata[index] = data;
// preset writes restore some values from a preset memory; not sure
// how this really works but the TX81Z will overwrite the sustain level/
// release rate register and the envelope shift/reverb rate register to
// dampen sound, then write the preset number to register 8 to restore them
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];
}
// store the presets under some unknown condition; the pattern of writes
// when setting a new preset is:
//
// 08 (0-7), 80-9F, A0-BF, C0-DF, C0-DF (alt), 20-27, 40-5F, 40-5F (alt),
// C0-DF (alt -- again?), 38-3F, 1B, 18, E0-FF
//
// So it writes 0-7 to 08 to either reset all presets or to indicate
// that we're going to be loading them. Immediately after all the writes
// above, the very next write will be temporary values to blow away the
// values loaded into E0-FF, so somehow it also knows that anything after
// that point is not part of the preset.
//
// For now, try using the 40-5F (alt) writes as flags that presets are
// being loaded until the E0-FF writes happen.
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;
}
// handle writes to the key on index
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; //bitfield(data, 3, 4);
opmask = ch_key_on(channel) ? 0xf : 0;
// according to the TX81Z manual, the sync option causes the LFOs
// to reset at each note on; assume that's when operator 4 gets
// a note on signal
if (bitfield(opmask, 3) != 0)
// to reset at each note on
if (opmask != 0)
{
if (lfo_sync())
m_lfo_counter[0] = 0;
@ -650,51 +701,54 @@ void ym2414::write_data(uint8_t data)
{
// write the FM register
m_fm.write(m_address, data);
switch (m_address & 0xe0)
if (TEMPORARY_DEBUG_PRINTS)
{
case 0x00:
printf("CTL %02X = %02X\n", m_address, data);
break;
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 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 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 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 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 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 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 0xf0:
printf("D1L/RR %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;
}
}
// special cases

View File

@ -49,11 +49,7 @@ namespace ymfm
// OPZ register map:
//
// System-wide registers:
// 08 -x------ Key on/off operator 4
// --x----- Key on/off operator 3
// ---x---- Key on/off operator 2
// ----x--- Key on/off operator 1
// -----xxx Channel select
// 08 -----xxx Load preset (not sure how it gets saved)
// 0F x------- Noise enable
// ---xxxxx Noise frequency
// 10 xxxxxxxx Timer A value (upper 8 bits)
@ -117,10 +113,14 @@ namespace ymfm
// ----xxxx Fine? (0-15)
// 120-13F xx------ Envelope generator shift (0-3)
// -----xxx Reverb rate (0-7)
// 140-147 -xxx---- LFO #2 PM sensitivity
// 140-15F xxxx---- Preset sustain level (0-15)
// ----xxxx Preset release rate (0-15)
// 160-17F xx------ Envelope generator shift (0-3)
// -----xxx Reverb rate (0-7)
// 180-187 -xxx---- LFO #2 PM sensitivity
// ---- xxx LFO #2 AM shift
// 148 -xxxxxxx LFO #2 PM depth
// 149 -xxxxxxx LFO PM depth
// 188 -xxxxxxx LFO #2 PM depth
// 189 -xxxxxxx LFO PM depth
//
class opz_registers : public fm_registers_base
@ -135,7 +135,7 @@ public:
static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
static constexpr uint32_t OPERATORS = CHANNELS * 4;
static constexpr uint32_t WAVEFORMS = 8;
static constexpr uint32_t REGISTERS = 0x150;
static constexpr uint32_t REGISTERS = 0x190;
static constexpr uint32_t DEFAULT_PRESCALE = 2;
static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
@ -205,12 +205,12 @@ public:
uint32_t enable_timer_a() const { return byte(0x14, 2, 1); }
uint32_t load_timer_b() const { return byte(0x14, 1, 1); }
uint32_t load_timer_a() const { return byte(0x14, 0, 1); }
uint32_t lfo2_pm_depth() const { return byte(0x148, 0, 7); } // fake
uint32_t lfo2_pm_depth() const { return byte(0x188, 0, 7); } // fake
uint32_t lfo2_rate() const { return byte(0x16, 0, 8); }
uint32_t lfo2_am_depth() const { return byte(0x17, 0, 7); }
uint32_t lfo_rate() const { return byte(0x18, 0, 8); }
uint32_t lfo_am_depth() const { return byte(0x19, 0, 7); }
uint32_t lfo_pm_depth() const { return byte(0x149, 0, 7); } // fake
uint32_t lfo_pm_depth() const { return byte(0x189, 0, 7); } // fake
uint32_t output_bits() const { return byte(0x1b, 6, 2); }
uint32_t lfo2_sync() const { return byte(0x1b, 5, 1); }
uint32_t lfo_sync() const { return byte(0x1b, 4, 1); }
@ -230,8 +230,8 @@ public:
uint32_t ch_block_freq(uint32_t choffs) const { return word(0x28, 0, 7, 0x30, 2, 6, choffs); }
uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x38, 4, 3, choffs); }
uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x38, 0, 2, choffs); }
uint32_t ch_lfo2_pm_sens(uint32_t choffs) const { return byte(0x140, 4, 3, choffs); } // fake
uint32_t ch_lfo2_am_sens(uint32_t choffs) const { return byte(0x140, 0, 2, choffs); } // fake
uint32_t ch_lfo2_pm_sens(uint32_t choffs) const { return byte(0x180, 4, 3, choffs); } // fake
uint32_t ch_lfo2_am_sens(uint32_t choffs) const { return byte(0x180, 0, 2, choffs); } // fake
// per-operator registers
uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); }