summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/ymfm/src/ymfm_ssg.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/ymfm/src/ymfm_ssg.cpp')
-rw-r--r--3rdparty/ymfm/src/ymfm_ssg.cpp277
1 files changed, 277 insertions, 0 deletions
diff --git a/3rdparty/ymfm/src/ymfm_ssg.cpp b/3rdparty/ymfm/src/ymfm_ssg.cpp
new file mode 100644
index 00000000000..2a7c9668ce2
--- /dev/null
+++ b/3rdparty/ymfm/src/ymfm_ssg.cpp
@@ -0,0 +1,277 @@
+// 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.
+
+#include "ymfm_ssg.h"
+
+namespace ymfm
+{
+
+//*********************************************************
+// SSG REGISTERS
+//*********************************************************
+
+//-------------------------------------------------
+// reset - reset the register state
+//-------------------------------------------------
+
+void ssg_registers::reset()
+{
+ std::fill_n(&m_regdata[0], REGISTERS, 0);
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ssg_registers::save_restore(ymfm_saved_state &state)
+{
+ state.save_restore(m_regdata);
+}
+
+
+
+//*********************************************************
+// SSG ENGINE
+//*********************************************************
+
+//-------------------------------------------------
+// ssg_engine - constructor
+//-------------------------------------------------
+
+ssg_engine::ssg_engine(ymfm_interface &intf) :
+ m_intf(intf),
+ m_tone_count{ 0,0,0 },
+ m_tone_state{ 0,0,0 },
+ m_envelope_count(0),
+ m_envelope_state(0),
+ m_noise_count(0),
+ m_noise_state(1),
+ m_override(nullptr)
+{
+}
+
+
+//-------------------------------------------------
+// reset - reset the engine state
+//-------------------------------------------------
+
+void ssg_engine::reset()
+{
+ // defer to the override if present
+ if (m_override != nullptr)
+ return m_override->ssg_reset();
+
+ // reset register state
+ m_regs.reset();
+
+ // reset engine state
+ for (int chan = 0; chan < 3; chan++)
+ {
+ m_tone_count[chan] = 0;
+ m_tone_state[chan] = 0;
+ }
+ m_envelope_count = 0;
+ m_envelope_state = 0;
+ m_noise_count = 0;
+ m_noise_state = 1;
+}
+
+
+//-------------------------------------------------
+// save_restore - save or restore the data
+//-------------------------------------------------
+
+void ssg_engine::save_restore(ymfm_saved_state &state)
+{
+ // save register state
+ m_regs.save_restore(state);
+
+ // save engine state
+ state.save_restore(m_tone_count);
+ state.save_restore(m_tone_state);
+ state.save_restore(m_envelope_count);
+ state.save_restore(m_envelope_state);
+ state.save_restore(m_noise_count);
+ state.save_restore(m_noise_state);
+}
+
+
+//-------------------------------------------------
+// clock - master clocking function
+//-------------------------------------------------
+
+void ssg_engine::clock()
+{
+ // clock tones; tone period units are clock/16 but since we run at clock/8
+ // that works out for us to toggle the state (50% duty cycle) at twice the
+ // programmed period
+ for (int chan = 0; chan < 3; chan++)
+ {
+ m_tone_count[chan]++;
+ if (m_tone_count[chan] >= m_regs.ch_tone_period(chan))
+ {
+ m_tone_state[chan] ^= 1;
+ m_tone_count[chan] = 0;
+ }
+ }
+
+ // clock noise; noise period units are clock/16 but since we run at clock/8,
+ // our counter needs a right shift prior to compare
+ m_noise_count++;
+ if ((m_noise_count >> 1) >= m_regs.noise_period())
+ {
+ m_noise_state ^= (bitfield(m_noise_state, 0) ^ bitfield(m_noise_state, 3)) << 17;
+ m_noise_state >>= 1;
+ m_noise_count = 0;
+ }
+
+ // clock envelope; envelope period units are clock/8 (manual says clock/256
+ // but that's for all 32 steps)
+ m_envelope_count++;
+ if (m_envelope_count >= m_regs.envelope_period())
+ {
+ m_envelope_state++;
+ m_envelope_count = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// output - output the current state
+//-------------------------------------------------
+
+void ssg_engine::output(output_data &output)
+{
+ // volume to amplitude table, taken from MAME's implementation but biased
+ // so that 0 == 0
+ static int16_t const s_amplitudes[32] =
+ {
+ 0, 32, 78, 141, 178, 222, 262, 306,
+ 369, 441, 509, 585, 701, 836, 965, 1112,
+ 1334, 1595, 1853, 2146, 2576, 3081, 3576, 4135,
+ 5000, 6006, 7023, 8155, 9963,11976,14132,16382
+ };
+
+ // compute the envelope volume
+ uint32_t envelope_volume;
+ if ((m_regs.envelope_hold() | (m_regs.envelope_continue() ^ 1)) && m_envelope_state >= 32)
+ {
+ m_envelope_state = 32;
+ envelope_volume = ((m_regs.envelope_attack() ^ m_regs.envelope_alternate()) & m_regs.envelope_continue()) ? 31 : 0;
+ }
+ else
+ {
+ uint32_t attack = m_regs.envelope_attack();
+ if (m_regs.envelope_alternate())
+ attack ^= bitfield(m_envelope_state, 5);
+ envelope_volume = (m_envelope_state & 31) ^ (attack ? 0 : 31);
+ }
+
+ // iterate over channels
+ for (int chan = 0; chan < 3; chan++)
+ {
+ // noise depends on the noise state, which is the LSB of m_noise_state
+ uint32_t noise_on = m_regs.ch_noise_enable(chan) & m_noise_state;
+
+ // tone depends on the current tone state
+ uint32_t tone_on = m_regs.ch_tone_enable(chan) & m_tone_state[chan];
+
+ // if neither tone nor noise enabled, return 0
+ uint32_t volume;
+ if ((noise_on | tone_on) == 0)
+ volume = 0;
+
+ // if the envelope is enabled, use its amplitude
+ else if (m_regs.ch_envelope_enable(chan))
+ volume = envelope_volume;
+
+ // otherwise, scale the tone amplitude up to match envelope values
+ // according to the datasheet, amplitude 15 maps to envelope 31
+ else
+ {
+ volume = m_regs.ch_amplitude(chan) * 2;
+ if (volume != 0)
+ volume |= 1;
+ }
+
+ // convert to amplitude
+ output.data[chan] = s_amplitudes[volume];
+ }
+}
+
+
+//-------------------------------------------------
+// read - handle reads from the SSG registers
+//-------------------------------------------------
+
+uint8_t ssg_engine::read(uint32_t regnum)
+{
+ // defer to the override if present
+ if (m_override != nullptr)
+ return m_override->ssg_read(regnum);
+
+ // read from the I/O ports call the handlers if they are configured for input
+ if (regnum == 0x0e && !m_regs.io_a_out())
+ return m_intf.ymfm_io_read(0);
+ else if (regnum == 0x0f && !m_regs.io_b_out())
+ return m_intf.ymfm_io_read(1);
+
+ // otherwise just return the register value
+ return m_regs.read(regnum);
+}
+
+
+//-------------------------------------------------
+// write - handle writes to the SSG registers
+//-------------------------------------------------
+
+void ssg_engine::write(uint32_t regnum, uint8_t data)
+{
+ // defer to the override if present
+ if (m_override != nullptr)
+ return m_override->ssg_write(regnum, data);
+
+ // store the raw value to the register array;
+ // most writes are passive, consumed only when needed
+ m_regs.write(regnum, data);
+
+ // writes to the envelope shape register reset the state
+ if (regnum == 0x0d)
+ m_envelope_state = 0;
+
+ // writes to the I/O ports call the handlers if they are configured for output
+ else if (regnum == 0x0e && m_regs.io_a_out())
+ m_intf.ymfm_io_write(0, data);
+ else if (regnum == 0x0f && m_regs.io_b_out())
+ m_intf.ymfm_io_write(1, data);
+}
+
+}