From 5e41c0e195be5f74c3e4f567f142b3bf904fb621 Mon Sep 17 00:00:00 2001 From: Angelo Salese Date: Thu, 30 Jan 2025 14:23:12 +0100 Subject: [PATCH] 3rdparty/ymfm: sync to latest (#13263) --- .../ymfm/{ => examples/buildall}/buildall.cpp | 0 3rdparty/ymfm/src/ymfm.h | 21 +- 3rdparty/ymfm/src/ymfm_fm.h | 4 +- 3rdparty/ymfm/src/ymfm_fm.ipp | 14 +- 3rdparty/ymfm/src/ymfm_misc.h | 1 - 3rdparty/ymfm/src/ymfm_opl.cpp | 44 +-- 3rdparty/ymfm/src/ymfm_opm.cpp | 20 +- 3rdparty/ymfm/src/ymfm_opn.cpp | 16 +- 3rdparty/ymfm/src/ymfm_opn.h | 2 +- 3rdparty/ymfm/src/ymfm_opq.cpp | 12 +- 3rdparty/ymfm/src/ymfm_opx.h | 290 ++++++++++++++++++ 3rdparty/ymfm/src/ymfm_opz.cpp | 36 +-- 3rdparty/ymfm/src/ymfm_pcm.cpp | 1 - 3rdparty/ymfm/src/ymfm_ssg.h | 2 + 14 files changed, 378 insertions(+), 85 deletions(-) rename 3rdparty/ymfm/{ => examples/buildall}/buildall.cpp (100%) create mode 100644 3rdparty/ymfm/src/ymfm_opx.h diff --git a/3rdparty/ymfm/buildall.cpp b/3rdparty/ymfm/examples/buildall/buildall.cpp similarity index 100% rename from 3rdparty/ymfm/buildall.cpp rename to 3rdparty/ymfm/examples/buildall/buildall.cpp diff --git a/3rdparty/ymfm/src/ymfm.h b/3rdparty/ymfm/src/ymfm.h index 47ff90072cc..6427e34cda2 100644 --- a/3rdparty/ymfm/src/ymfm.h +++ b/3rdparty/ymfm/src/ymfm.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -109,17 +110,6 @@ inline int32_t clamp(int32_t value, int32_t minval, int32_t maxval) } -//------------------------------------------------- -// array_size - return the size of an array -//------------------------------------------------- - -template -constexpr uint32_t array_size(ArrayType (&array)[ArraySize]) -{ - return ArraySize; -} - - //------------------------------------------------- // count_leading_zeros - return the number of // leading zeros in a 32-bit value; CPU-optimized @@ -254,7 +244,8 @@ inline int16_t roundtrip_fp(int32_t value) // apply the shift back and forth to zero out bits that are lost exponent -= 1; - return (value >> exponent) << exponent; + int32_t mask = (1 << exponent) - 1; + return value & ~mask; } @@ -350,7 +341,7 @@ public: { // create file char name[20]; - sprintf(name, "wavlog-%02d.wav", m_index); + snprintf(&name[0], sizeof(name), "wavlog-%02d.wav", m_index); FILE *out = fopen(name, "wb"); // make the wav file header @@ -483,6 +474,8 @@ public: class ymfm_engine_callbacks { public: + virtual ~ymfm_engine_callbacks() = default; + // timer callback; called by the interface when a timer fires virtual void engine_timer_expired(uint32_t tnum) = 0; @@ -504,6 +497,8 @@ class ymfm_interface template friend class fm_engine_base; public: + virtual ~ymfm_interface() = default; + // the following functions must be implemented by any derived classes; the // default implementations are sufficient for some minimal operation, but will // likely need to be overridden to integrate with the outside world; they are diff --git a/3rdparty/ymfm/src/ymfm_fm.h b/3rdparty/ymfm/src/ymfm_fm.h index 81795f8fe43..d40409fddfd 100644 --- a/3rdparty/ymfm/src/ymfm_fm.h +++ b/3rdparty/ymfm/src/ymfm_fm.h @@ -267,7 +267,7 @@ public: // assign operators void assign(uint32_t index, fm_operator *op) { - assert(index < array_size(m_op)); + assert(index < m_op.size()); m_op[index] = op; if (op != nullptr) op->set_choffs(m_choffs); @@ -330,7 +330,7 @@ private: uint32_t m_choffs; // channel offset in registers int16_t m_feedback[2]; // feedback memory for operator 1 mutable int16_t m_feedback_in; // next input value for op 1 feedback (set in output) - fm_operator *m_op[4]; // up to 4 operators + std::array *, 4> m_op; // up to 4 operators RegisterType &m_regs; // direct reference to registers fm_engine_base &m_owner; // reference to the owning engine }; diff --git a/3rdparty/ymfm/src/ymfm_fm.ipp b/3rdparty/ymfm/src/ymfm_fm.ipp index 78e3a62d9c8..675424839ce 100644 --- a/3rdparty/ymfm/src/ymfm_fm.ipp +++ b/3rdparty/ymfm/src/ymfm_fm.ipp @@ -839,12 +839,12 @@ void fm_channel::save_restore(ymfm_saved_state &state) template void fm_channel::keyonoff(uint32_t states, keyon_type type, uint32_t chnum) { - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) m_op[opnum]->keyonoff(bitfield(states, opnum), type); if (debug::LOG_KEYON_EVENTS && ((debug::GLOBAL_FM_CHANNEL_MASK >> chnum) & 1) != 0) - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) debug::log_keyon("%c%s\n", bitfield(states, opnum) ? '+' : '-', m_regs.log_keyon(m_choffs, m_op[opnum]->opoffs()).c_str()); } @@ -860,7 +860,7 @@ bool fm_channel::prepare() uint32_t active_mask = 0; // prepare all operators and determine if they are active - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) if (m_op[opnum]->prepare()) active_mask |= 1 << opnum; @@ -880,7 +880,7 @@ void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) m_feedback[0] = m_feedback[1]; m_feedback[1] = m_feedback_in; - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) if (m_op[opnum] != nullptr) m_op[opnum]->clock(env_counter, lfo_raw_pm); @@ -888,7 +888,7 @@ void fm_channel::clock(uint32_t env_counter, int32_t lfo_raw_pm) useful temporary code for envelope debugging if (m_choffs == 0x101) { - for (uint32_t opnum = 0; opnum < array_size(m_op); opnum++) + for (uint32_t opnum = 0; opnum < m_op.size(); opnum++) { auto &op = *m_op[((opnum & 1) << 1) | ((opnum >> 1) & 1)]; printf(" %c%03X%c%c ", @@ -1504,6 +1504,8 @@ void fm_engine_base::update_timer(uint32_t tnum, uint32_t enable, template void fm_engine_base::engine_timer_expired(uint32_t tnum) { + assert(tnum == 0 || tnum == 1); + // update status if (tnum == 0 && m_regs.enable_timer_a()) set_reset_status(STATUS_TIMERA, 0); @@ -1515,7 +1517,7 @@ void fm_engine_base::engine_timer_expired(uint32_t tnum) for (uint32_t chnum = 0; chnum < CHANNELS; chnum++) if (bitfield(RegisterType::CSM_TRIGGER_MASK, chnum)) { - m_channel[chnum]->keyonoff(1, KEYON_CSM, chnum); + m_channel[chnum]->keyonoff(0xf, KEYON_CSM, chnum); m_modified_channels |= 1 << chnum; } diff --git a/3rdparty/ymfm/src/ymfm_misc.h b/3rdparty/ymfm/src/ymfm_misc.h index 0c3e2fa2310..628d128f699 100644 --- a/3rdparty/ymfm/src/ymfm_misc.h +++ b/3rdparty/ymfm/src/ymfm_misc.h @@ -90,5 +90,4 @@ protected: } - #endif // YMFM_MISC_H diff --git a/3rdparty/ymfm/src/ymfm_opl.cpp b/3rdparty/ymfm/src/ymfm_opl.cpp index f3f62955ecb..8e8025fd9cc 100644 --- a/3rdparty/ymfm/src/ymfm_opl.cpp +++ b/3rdparty/ymfm/src/ymfm_opl.cpp @@ -386,9 +386,9 @@ std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t op uint32_t opnum = (opoffs & 31) - 2 * ((opoffs & 31) / 8) + 18 * bitfield(opoffs, 8); char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += sprintf(end, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%2u.%02u freq=%04X fb=%u alg=%X mul=%X tl=%02X ksr=%u ns=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u", chnum, opnum, ch_block_freq(choffs), ch_feedback(choffs), @@ -405,25 +405,25 @@ std::string opl_registers_base::log_keyon(uint32_t choffs, uint32_t op op_eg_sustain(opoffs)); if (OUTPUTS > 1) - end += sprintf(end, " out=%c%c%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c%c%c", ch_output_0(choffs) ? 'L' : '-', ch_output_1(choffs) ? 'R' : '-', ch_output_2(choffs) ? '0' : '-', ch_output_3(choffs) ? '1' : '-'); if (op_lfo_am_enable(opoffs) != 0) - end += sprintf(end, " am=%u", lfo_am_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", lfo_am_depth()); if (op_lfo_pm_enable(opoffs) != 0) - end += sprintf(end, " pm=%u", lfo_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", lfo_pm_depth()); if (waveform_enable() && op_waveform(opoffs) != 0) - end += sprintf(end, " wf=%u", op_waveform(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs)); if (is_rhythm(choffs)) - end += sprintf(end, " rhy=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1"); if (DYNAMIC_OPS) { operator_mapping map; operator_map(map); if (bitfield(map.chan[chnum], 16, 8) != 0xff) - end += sprintf(end, " 4op"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " 4op"); } return buffer; @@ -685,9 +685,9 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += sprintf(end, "%u.%02u freq=%04X inst=%X fb=%u mul=%X", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X inst=%X fb=%u mul=%X", chnum, opnum, ch_block_freq(choffs), ch_instrument(choffs), @@ -695,11 +695,11 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) op_multiple(opoffs)); if (bitfield(opoffs, 0) == 1 || (is_rhythm(choffs) && choffs >= 6)) - end += sprintf(end, " vol=%X", op_volume(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " vol=%X", op_volume(opoffs)); else - end += sprintf(end, " tl=%02X", ch_total_level(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " tl=%02X", ch_total_level(choffs)); - end += sprintf(end, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u", + end += snprintf(&buffer[end], sizeof(buffer) - end, " ksr=%u ksl=%u adr=%X/%X/%X sl=%X sus=%u/%u", op_ksr(opoffs), op_ksl(opoffs), op_attack_rate(opoffs), @@ -710,13 +710,13 @@ std::string opll_registers::log_keyon(uint32_t choffs, uint32_t opoffs) ch_sustain(choffs)); if (op_lfo_am_enable(opoffs)) - end += sprintf(end, " am=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=1"); if (op_lfo_pm_enable(opoffs)) - end += sprintf(end, " pm=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=1"); if (op_waveform(opoffs) != 0) - end += sprintf(end, " wf=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=1"); if (is_rhythm(choffs)) - end += sprintf(end, " rhy=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " rhy=1"); return buffer; } @@ -1715,9 +1715,15 @@ uint8_t ymf278b::read_status() uint8_t ymf278b::read_data_pcm() { - // write to FM + // read from PCM if (bitfield(m_address, 9) != 0) - return m_pcm.read(m_address & 0xff); + { + uint8_t result = m_pcm.read(m_address & 0xff); + if ((m_address & 0xff) == 0x02) + result |= 0x20; + + return result; + } return 0; } diff --git a/3rdparty/ymfm/src/ymfm_opm.cpp b/3rdparty/ymfm/src/ymfm_opm.cpp index 544bbe89a29..03f54fb9039 100644 --- a/3rdparty/ymfm/src/ymfm_opm.cpp +++ b/3rdparty/ymfm/src/ymfm_opm.cpp @@ -60,17 +60,17 @@ opm_registers::opm_registers() : { // waveform 0 is a sawtooth uint8_t am = index ^ 0xff; - int8_t pm = int8_t(index); + uint8_t pm = index; m_lfo_waveform[0][index] = am | (pm << 8); // waveform 1 is a square wave am = bitfield(index, 7) ? 0 : 0xff; - pm = int8_t(am ^ 0x80); + pm = am ^ 0x80; m_lfo_waveform[1][index] = am | (pm << 8); // waveform 2 is a triangle wave am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1); - pm = int8_t(bitfield(index, 6) ? am : ~am); + pm = bitfield(index, 6) ? am : ~am; m_lfo_waveform[2][index] = am | (pm << 8); // waveform 3 is noise; it is filled in dynamically @@ -330,7 +330,7 @@ uint32_t opm_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opd if (pm_sensitivity < 6) delta += lfo_raw_pm >> (6 - pm_sensitivity); else - delta += lfo_raw_pm << (pm_sensitivity - 5); + delta += uint32_t(lfo_raw_pm) << (pm_sensitivity - 5); } // apply delta and convert to a frequency number @@ -354,9 +354,9 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += sprintf(end, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt2=%u dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, ch_block_freq(choffs), op_detune2(opoffs), @@ -376,14 +376,14 @@ std::string opm_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_am_depth() != 0 && ch_lfo_am_sens(choffs) != 0 && op_lfo_am_enable(opoffs) != 0); if (am) - end += sprintf(end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + 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 += sprintf(end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) - end += sprintf(end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); if (noise_enable() && opoffs == 31) - end += sprintf(end, " noise=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1"); return buffer; } diff --git a/3rdparty/ymfm/src/ymfm_opn.cpp b/3rdparty/ymfm/src/ymfm_opn.cpp index 053ad97701c..16ca3416c69 100644 --- a/3rdparty/ymfm/src/ymfm_opn.cpp +++ b/3rdparty/ymfm/src/ymfm_opn.cpp @@ -409,9 +409,9 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof } char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += sprintf(end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X", chnum, opnum, block_freq, op_detune(opoffs), @@ -427,21 +427,21 @@ std::string opn_registers_base::log_keyon(uint32_t choffs, uint32_t opof op_sustain_level(opoffs)); if (OUTPUTS > 1) - end += sprintf(end, " out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, " out=%c%c", ch_output_0(choffs) ? 'L' : '-', ch_output_1(choffs) ? 'R' : '-'); if (op_ssg_eg_enable(opoffs)) - end += sprintf(end, " ssg=%X", op_ssg_eg_mode(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " ssg=%X", op_ssg_eg_mode(opoffs)); bool am = (op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs)); bool pm = (ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) - end += sprintf(end, " lfo=%02X", lfo_rate()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate()); if (multi_freq() && choffs == 2) - end += sprintf(end, " multi=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " multi=1"); return buffer; } diff --git a/3rdparty/ymfm/src/ymfm_opn.h b/3rdparty/ymfm/src/ymfm_opn.h index bab68ed93c5..daed8b0bf24 100644 --- a/3rdparty/ymfm/src/ymfm_opn.h +++ b/3rdparty/ymfm/src/ymfm_opn.h @@ -793,7 +793,7 @@ public: ymf276(ymfm_interface &intf) : ym2612(intf) { } // generate one sample of sound - void generate(output_data *output, uint32_t numsamples); + void generate(output_data *output, uint32_t numsamples = 1); }; } diff --git a/3rdparty/ymfm/src/ymfm_opq.cpp b/3rdparty/ymfm/src/ymfm_opq.cpp index 3467c0ddf95..78ae1616409 100644 --- a/3rdparty/ymfm/src/ymfm_opq.cpp +++ b/3rdparty/ymfm/src/ymfm_opq.cpp @@ -339,9 +339,9 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += sprintf(end, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u freq=%04X dt=%+2d fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", chnum, opnum, (opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs), int32_t(op_detune(opoffs)) - 0x20, @@ -360,14 +360,14 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs) bool am = (lfo_enable() && op_lfo_am_enable(opoffs) && ch_lfo_am_sens(choffs) != 0); if (am) - end += sprintf(end, " am=%u", ch_lfo_am_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " am=%u", ch_lfo_am_sens(choffs)); bool pm = (lfo_enable() && ch_lfo_pm_sens(choffs) != 0); if (pm) - end += sprintf(end, " pm=%u", ch_lfo_pm_sens(choffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u", ch_lfo_pm_sens(choffs)); if (am || pm) - end += sprintf(end, " lfo=%02X", lfo_rate()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo=%02X", lfo_rate()); if (ch_reverb(choffs)) - end += sprintf(end, " reverb"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " reverb"); return buffer; } diff --git a/3rdparty/ymfm/src/ymfm_opx.h b/3rdparty/ymfm/src/ymfm_opx.h new file mode 100644 index 00000000000..f8ee1c34f18 --- /dev/null +++ b/3rdparty/ymfm/src/ymfm_opx.h @@ -0,0 +1,290 @@ +// BSD 3-Clause License +// +// Copyright (c) 2021, Aaron Giles +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef YMFM_OPX_H +#define YMFM_OPX_H + +#pragma once + +#include "ymfm.h" +#include "ymfm_fm.h" + +namespace ymfm +{ + +//********************************************************* +// REGISTER CLASSES +//********************************************************* + +// ======================> opx_registers + +// +// OPX register map: +// +// System-wide registers: +// +// Per-channel registers (channel in address bits 0-2) +// +// Per-operator registers (4 banks): +// 00-0F x------- Enable +// -xxxx--- EXT out +// -------x Key on +// 10-1F xxxxxxxx LFO frequency +// 20-2F xx------ AM sensitivity (0-3) +// --xxx--- PM sensitivity (0-7) +// ------xx LFO waveform (0=disable, 1=saw, 2= +// 30-3F -xxx---- Detune (0-7) +// ----xxxx Multiple (0-15) +// 40-4F -xxxxxxx Total level (0-127) +// 50-5F xxx----- Key scale (0-7) +// ---xxxxx Attack rate (0-31) +// 60-6F ---xxxxx Decay rate (0-31) +// 70-7F ---xxxxx Sustain rate (0-31) +// 80-8F xxxx---- Sustain level (0-15) +// ----xxxx Release rate (0-15) +// 90-9F xxxxxxxx Frequency number (low 8 bits) +// A0-AF xxxx---- Block (0-15) +// ----xxxx Frequency number (high 4 bits) +// B0-BF x------- Acc on +// -xxx---- Feedback level (0-7) +// -----xxx Waveform (0-7, 7=PCM) +// C0-CF ----xxxx Algorithm (0-15) +// D0-DF xxxx---- CH0 level (0-15) +// ----xxxx CH1 level (0-15) +// E0-EF xxxx---- CH2 level (0-15) +// ----xxxx CH3 level (0-15) +// + +class opx_registers : public fm_registers_base +{ + // LFO waveforms are 256 entries long + static constexpr uint32_t LFO_WAVEFORM_LENGTH = 256; + +public: + // constants + static constexpr uint32_t OUTPUTS = 8; + static constexpr uint32_t CHANNELS = 24; + static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1; + static constexpr uint32_t OPERATORS = CHANNELS * 2; + static constexpr uint32_t WAVEFORMS = 8; + static constexpr uint32_t REGISTERS = 0x800; + static constexpr uint32_t DEFAULT_PRESCALE = 8; + static constexpr uint32_t EG_CLOCK_DIVIDER = 2; + static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS; + static constexpr uint32_t REG_MODE = 0x14; + static constexpr uint8_t STATUS_TIMERA = 0x01; + static constexpr uint8_t STATUS_TIMERB = 0x02; + static constexpr uint8_t STATUS_BUSY = 0x80; + static constexpr uint8_t STATUS_IRQ = 0; + + // constructor + opx_registers(); + + // reset to initial state + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // map channel number to register offset + static constexpr uint32_t channel_offset(uint32_t chnum) + { + assert(chnum < CHANNELS); + return chnum; + } + + // map operator number to register offset + static constexpr uint32_t operator_offset(uint32_t opnum) + { + assert(opnum < OPERATORS); + return opnum; + } + + // return an array of operator indices for each channel + struct operator_mapping { uint32_t chan[CHANNELS]; }; + void operator_map(operator_mapping &dest) const; + + // handle writes to the register array + bool write(uint16_t index, uint8_t data, uint32_t &chan, uint32_t &opmask); + + // clock the noise and LFO, if present, returning LFO PM value + int32_t clock_noise_and_lfo(); + + // return the AM offset from LFO for the given channel + uint32_t lfo_am_offset(uint32_t choffs) const; + + // return the current noise state, gated by the noise clock + uint32_t noise_state() const { return m_noise_state; } + + // caching helpers + void cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache); + + // compute the phase step, given a PM value + uint32_t compute_phase_step(uint32_t choffs, uint32_t opoffs, opdata_cache const &cache, int32_t lfo_raw_pm); + + // log a key-on event + std::string log_keyon(uint32_t choffs, uint32_t opoffs); + + // system-wide registers + uint32_t noise_frequency() const { return byte(0x0f, 0, 5); } + uint32_t noise_enable() const { return byte(0x0f, 7, 1); } + uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); } + uint32_t timer_b_value() const { return byte(0x12, 0, 8); } + uint32_t csm() const { return byte(0x14, 7, 1); } + uint32_t reset_timer_b() const { return byte(0x14, 5, 1); } + uint32_t reset_timer_a() const { return byte(0x14, 4, 1); } + uint32_t enable_timer_b() const { return byte(0x14, 3, 1); } + 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_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 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); } + uint32_t lfo2_waveform() const { return byte(0x1b, 2, 2); } + uint32_t lfo_waveform() const { return byte(0x1b, 0, 2); } + + // per-channel registers + uint32_t ch_volume(uint32_t choffs) const { return byte(0x00, 0, 8, choffs); } + uint32_t ch_output_any(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); } + uint32_t ch_output_0(uint32_t choffs) const { return byte(0x30, 0, 1, choffs); } + uint32_t ch_output_1(uint32_t choffs) const { return byte(0x20, 7, 1, choffs) | byte(0x30, 0, 1, choffs); } + uint32_t ch_output_2(uint32_t choffs) const { return 0; } + uint32_t ch_output_3(uint32_t choffs) const { return 0; } + uint32_t ch_key_on(uint32_t choffs) const { return byte(0x20, 6, 1, choffs); } + uint32_t ch_feedback(uint32_t choffs) const { return byte(0x20, 3, 3, choffs); } + uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x20, 0, 3, choffs); } + 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 + + // per-operator registers + uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); } + uint32_t op_multiple(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); } + uint32_t op_fix_range(uint32_t opoffs) const { return byte(0x40, 4, 3, opoffs); } + uint32_t op_fix_frequency(uint32_t opoffs) const { return byte(0x40, 0, 4, opoffs); } + uint32_t op_waveform(uint32_t opoffs) const { return byte(0x100, 4, 3, opoffs); } // fake + uint32_t op_fine(uint32_t opoffs) const { return byte(0x100, 0, 4, opoffs); } // fake + uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); } + uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); } + uint32_t op_fix_mode(uint32_t opoffs) const { return byte(0x80, 5, 1, opoffs); } + uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); } + uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); } + uint32_t op_decay_rate(uint32_t opoffs) const { return byte(0xa0, 0, 5, opoffs); } + uint32_t op_detune2(uint32_t opoffs) const { return byte(0xc0, 6, 2, opoffs); } + uint32_t op_sustain_rate(uint32_t opoffs) const { return byte(0xc0, 0, 5, opoffs); } + uint32_t op_eg_shift(uint32_t opoffs) const { return byte(0x120, 6, 2, opoffs); } // fake + uint32_t op_reverb_rate(uint32_t opoffs) const { return byte(0x120, 0, 3, opoffs); } // fake + uint32_t op_sustain_level(uint32_t opoffs) const { return byte(0xe0, 4, 4, opoffs); } + uint32_t op_release_rate(uint32_t opoffs) const { return byte(0xe0, 0, 4, opoffs); } + +protected: + // return a bitfield extracted from a byte + uint32_t byte(uint32_t offset, uint32_t start, uint32_t count, uint32_t extra_offset = 0) const + { + return bitfield(m_regdata[offset + extra_offset], start, count); + } + + // return a bitfield extracted from a pair of bytes, MSBs listed first + uint32_t word(uint32_t offset1, uint32_t start1, uint32_t count1, uint32_t offset2, uint32_t start2, uint32_t count2, uint32_t extra_offset = 0) const + { + return (byte(offset1, start1, count1, extra_offset) << count2) | byte(offset2, start2, count2, extra_offset); + } + + // internal state + uint32_t m_lfo_counter[2]; // LFO counter + uint32_t m_noise_lfsr; // noise LFSR state + uint8_t m_noise_counter; // noise counter + uint8_t m_noise_state; // latched noise state + uint8_t m_noise_lfo; // latched LFO noise value + uint8_t m_lfo_am[2]; // current LFO AM value + uint8_t m_regdata[REGISTERS]; // register data + uint16_t m_phase_substep[OPERATORS]; // phase substep for fixed frequency + int16_t m_lfo_waveform[4][LFO_WAVEFORM_LENGTH]; // LFO waveforms; AM in low 8, PM in upper 8 + uint16_t m_waveform[WAVEFORMS][WAVEFORM_LENGTH]; // waveforms +}; + + + +//********************************************************* +// IMPLEMENTATION CLASSES +//********************************************************* + +// ======================> ymf271 + +class ymf271 +{ +public: + using fm_engine = fm_engine_base; + static constexpr uint32_t OUTPUTS = fm_engine::OUTPUTS; + using output_data = fm_engine::output_data; + + // constructor + ymf271(ymfm_interface &intf); + + // reset + void reset(); + + // save/restore + void save_restore(ymfm_saved_state &state); + + // pass-through helpers + uint32_t sample_rate(uint32_t input_clock) const { return m_fm.sample_rate(input_clock); } + void invalidate_caches() { m_fm.invalidate_caches(); } + + // read access + uint8_t read_status(); + uint8_t read(uint32_t offset); + + // write access + void write_address(uint8_t data); + void write_data(uint8_t data); + void write(uint32_t offset, uint8_t data); + + // generate one sample of sound + void generate(output_data *output, uint32_t numsamples = 1); + +protected: + // internal state + uint8_t m_address; // address register + fm_engine m_fm; // core FM engine +}; + +} + + +#endif // YMFM_OPX_H diff --git a/3rdparty/ymfm/src/ymfm_opz.cpp b/3rdparty/ymfm/src/ymfm_opz.cpp index adeefd79f19..1178417bb8a 100644 --- a/3rdparty/ymfm/src/ymfm_opz.cpp +++ b/3rdparty/ymfm/src/ymfm_opz.cpp @@ -129,17 +129,17 @@ opz_registers::opz_registers() : { // waveform 0 is a sawtooth uint8_t am = index ^ 0xff; - int8_t pm = int8_t(index); + uint8_t pm = index; m_lfo_waveform[0][index] = am | (pm << 8); // waveform 1 is a square wave am = bitfield(index, 7) ? 0 : 0xff; - pm = int8_t(am ^ 0x80); + pm = am ^ 0x80; m_lfo_waveform[1][index] = am | (pm << 8); // waveform 2 is a triangle wave am = bitfield(index, 7) ? (index << 1) : ((index ^ 0xff) << 1); - pm = int8_t(bitfield(index, 6) ? am : ~am); + pm = bitfield(index, 6) ? am : ~am; m_lfo_waveform[2][index] = am | (pm << 8); // waveform 3 is noise; it is filled in dynamically @@ -555,16 +555,16 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) uint32_t opnum = opoffs; char buffer[256]; - char *end = &buffer[0]; + int end = 0; - end += sprintf(end, "%u.%02u", chnum, opnum); + end += snprintf(&buffer[end], sizeof(buffer) - end, "%u.%02u", chnum, opnum); if (op_fix_mode(opoffs)) - end += sprintf(end, " fixfreq=%X fine=%X shift=%X", op_fix_frequency(opoffs), op_fine(opoffs), op_fix_range(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 += sprintf(end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " freq=%04X dt2=%u fine=%X", ch_block_freq(choffs), op_detune2(opoffs), op_fine(opoffs)); - end += sprintf(end, " dt=%u fb=%u alg=%X mul=%X tl=%02X ksr=%u adsr=%02X/%02X/%02X/%X sl=%X out=%c%c", + 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), @@ -580,32 +580,32 @@ std::string opz_registers::log_keyon(uint32_t choffs, uint32_t opoffs) ch_output_1(choffs) ? 'R' : '-'); if (op_eg_shift(opoffs) != 0) - end += sprintf(end, " egshift=%u", op_eg_shift(opoffs)); + 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 += sprintf(end, " am=%u/%02X", ch_lfo_am_sens(choffs), lfo_am_depth()); + 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 += sprintf(end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm=%u/%02X", ch_lfo_pm_sens(choffs), lfo_pm_depth()); if (am || pm) - end += sprintf(end, " lfo=%02X/%c", lfo_rate(), "WQTN"[lfo_waveform()]); + 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 += sprintf(end, " am2=%u/%02X", ch_lfo2_am_sens(choffs), lfo2_am_depth()); + 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 += sprintf(end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); + end += snprintf(&buffer[end], sizeof(buffer) - end, " pm2=%u/%02X", ch_lfo2_pm_sens(choffs), lfo2_pm_depth()); if (am2 || pm2) - end += sprintf(end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); + end += snprintf(&buffer[end], sizeof(buffer) - end, " lfo2=%02X/%c", lfo2_rate(), "WQTN"[lfo2_waveform()]); if (op_reverb_rate(opoffs) != 0) - end += sprintf(end, " rev=%u", op_reverb_rate(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " rev=%u", op_reverb_rate(opoffs)); if (op_waveform(opoffs) != 0) - end += sprintf(end, " wf=%u", op_waveform(opoffs)); + end += snprintf(&buffer[end], sizeof(buffer) - end, " wf=%u", op_waveform(opoffs)); if (noise_enable() && opoffs == 31) - end += sprintf(end, " noise=1"); + end += snprintf(&buffer[end], sizeof(buffer) - end, " noise=1"); return buffer; } diff --git a/3rdparty/ymfm/src/ymfm_pcm.cpp b/3rdparty/ymfm/src/ymfm_pcm.cpp index 50595133bf7..34417490ccd 100644 --- a/3rdparty/ymfm/src/ymfm_pcm.cpp +++ b/3rdparty/ymfm/src/ymfm_pcm.cpp @@ -46,7 +46,6 @@ namespace ymfm void pcm_registers::reset() { std::fill_n(&m_regdata[0], REGISTERS, 0); - m_regdata[0x02] = 0x20; m_regdata[0xf8] = 0x1b; } diff --git a/3rdparty/ymfm/src/ymfm_ssg.h b/3rdparty/ymfm/src/ymfm_ssg.h index 749ad146fe7..cb7ec9e7c41 100644 --- a/3rdparty/ymfm/src/ymfm_ssg.h +++ b/3rdparty/ymfm/src/ymfm_ssg.h @@ -49,6 +49,8 @@ namespace ymfm class ssg_override { public: + virtual ~ssg_override() = default; + // reset our status virtual void ssg_reset() = 0;