summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2021-05-23 13:24:36 -0700
committer Aaron Giles <aaron@aarongiles.com>2021-05-23 13:24:36 -0700
commitac96627377b2eb960d02557dc956401ae157f280 (patch)
treebb847e41cdc05257b086eba06e306919adc94cd9
parente3aa064b18d74cb5e3fe0fa314f15e1a99afd44e (diff)
ymfm: Some progress on OPZ. Some instruments in the TX81Z sound kind of ok now.
-rw-r--r--3rdparty/ymfm/src/ymfm_opz.cpp164
-rw-r--r--3rdparty/ymfm/src/ymfm_opz.h26
2 files changed, 122 insertions, 68 deletions
diff --git a/3rdparty/ymfm/src/ymfm_opz.cpp b/3rdparty/ymfm/src/ymfm_opz.cpp
index ae814e45b13..fc6a3ab44c4 100644
--- a/3rdparty/ymfm/src/ymfm_opz.cpp
+++ b/3rdparty/ymfm/src/ymfm_opz.cpp
@@ -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;
-
- 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 0xf0:
- printf("D1L/RR %d.%d = %02X\n", m_address & 7, (m_address >> 3) & 3, 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 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;
+ }
}
// special cases
diff --git a/3rdparty/ymfm/src/ymfm_opz.h b/3rdparty/ymfm/src/ymfm_opz.h
index 3a6bd5733cd..c38dd246ab9 100644
--- a/3rdparty/ymfm/src/ymfm_opz.h
+++ b/3rdparty/ymfm/src/ymfm_opz.h
@@ -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); }