summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/ymfm/src/ymfm_opz.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/ymfm/src/ymfm_opz.cpp')
-rw-r--r--3rdparty/ymfm/src/ymfm_opz.cpp164
1 files changed, 109 insertions, 55 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