mirror of
https://github.com/holub/mame
synced 2025-04-20 15:32:45 +03:00
ymfm: Some progress on OPZ. Some instruments in the TX81Z sound kind of ok now.
This commit is contained in:
parent
e3aa064b18
commit
ac96627377
150
3rdparty/ymfm/src/ymfm_opz.cpp
vendored
150
3rdparty/ymfm/src/ymfm_opz.cpp
vendored
@ -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
|
||||
|
26
3rdparty/ymfm/src/ymfm_opz.h
vendored
26
3rdparty/ymfm/src/ymfm_opz.h
vendored
@ -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); }
|
||||
|
Loading…
Reference in New Issue
Block a user