mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
279 lines
8.2 KiB
C++
279 lines
8.2 KiB
C++
// 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; note that a period of 0
|
|
// should produce an indentical result to a period of 1, so add a special
|
|
// check against that case
|
|
m_noise_count++;
|
|
if ((m_noise_count >> 1) >= m_regs.noise_period() && m_noise_count != 1)
|
|
{
|
|
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_n(chan) | m_noise_state;
|
|
|
|
// tone depends on the current tone state
|
|
uint32_t tone_on = m_regs.ch_tone_enable_n(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_external_read(ACCESS_IO, 0);
|
|
else if (regnum == 0x0f && !m_regs.io_b_out())
|
|
return m_intf.ymfm_external_read(ACCESS_IO, 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_external_write(ACCESS_IO, 0, data);
|
|
else if (regnum == 0x0f && m_regs.io_b_out())
|
|
m_intf.ymfm_external_write(ACCESS_IO, 1, data);
|
|
}
|
|
|
|
}
|