WaveformGenerator.h
/*
* This file is part of libsidplayfp, a SID player engine.
*
* Copyright 2011-2023 Leandro Nini <drfiemost@users.sourceforge.net>
* Copyright 2007-2010 Antti Lankila
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef WAVEFORMGENERATOR_H
#define WAVEFORMGENERATOR_H
#include "siddefs-fp.h"
#include "array.h"
#include "sidcxx11.h"
// print SR debugging info
//#define TRACE 1
#ifdef TRACE
# include <iostream>
#endif
namespace reSIDfp
{
class WaveformGenerator
{
private:
matrix_t* model_wave;
matrix_t* model_pulldown;
short* wave;
short* pulldown;
// PWout = (PWn/40.95)%
unsigned int pw;
unsigned int shift_register;
unsigned int shift_latch;
int shift_pipeline;
unsigned int ring_msb_mask;
unsigned int no_noise;
unsigned int noise_output;
unsigned int no_noise_or_noise_output;
unsigned int no_pulse;
unsigned int pulse_output;
unsigned int waveform;
unsigned int waveform_output;
unsigned int accumulator;
// Fout = (Fn*Fclk/16777216)Hz
unsigned int freq;
unsigned int tri_saw_pipeline;
unsigned int osc3;
unsigned int shift_register_reset;
// The wave signal TTL when no waveform is selected.
unsigned int floating_output_ttl;
bool test;
bool sync;
bool test_or_reset;
bool msb_rising;
bool is6581; //-V730_NOINIT this is initialized in the SID constructor
private:
void shift_phase2(unsigned int waveform_old, unsigned int waveform_new);
void write_shift_register();
void set_noise_output();
void set_no_noise_or_noise_output();
void waveBitfade();
void shiftregBitfade();
public:
void setWaveformModels(matrix_t* models);
void setPulldownModels(matrix_t* models);
void setModel(bool is6581) { this->is6581 = is6581; }
void clock();
void synchronize(WaveformGenerator* syncDest, const WaveformGenerator* syncSource) const;
WaveformGenerator() :
model_wave(nullptr),
model_pulldown(nullptr),
wave(nullptr),
pulldown(nullptr),
pw(0),
shift_register(0),
shift_pipeline(0),
ring_msb_mask(0),
no_noise(0),
noise_output(0),
no_noise_or_noise_output(0),
no_pulse(0),
pulse_output(0),
waveform(0),
waveform_output(0),
accumulator(0x555555), // Accumulator's even bits are high on powerup
freq(0),
tri_saw_pipeline(0x555),
osc3(0),
shift_register_reset(0),
floating_output_ttl(0),
test(false),
sync(false),
msb_rising(false) {}
void writeFREQ_LO(unsigned char freq_lo) { freq = (freq & 0xff00) | (freq_lo & 0xff); }
void writeFREQ_HI(unsigned char freq_hi) { freq = (freq_hi << 8 & 0xff00) | (freq & 0xff); }
void writePW_LO(unsigned char pw_lo) { pw = (pw & 0xf00) | (pw_lo & 0x0ff); }
void writePW_HI(unsigned char pw_hi) { pw = (pw_hi << 8 & 0xf00) | (pw & 0x0ff); }
void writeCONTROL_REG(unsigned char control);
void reset();
unsigned int output(const WaveformGenerator* ringModulator);
unsigned char readOSC() const { return static_cast<unsigned char>(osc3 >> 4); }
unsigned int readAccumulator() const { return accumulator; }
unsigned int readFreq() const { return freq; }
bool readTest() const { return test; }
bool readSync() const { return sync; }
};
} // namespace reSIDfp
#if RESID_INLINING || defined(WAVEFORMGENERATOR_CPP)
namespace reSIDfp
{
RESID_INLINE
void WaveformGenerator::clock()
{
if (unlikely(test))
{
if (unlikely(shift_register_reset != 0) && unlikely(--shift_register_reset == 0))
{
#ifdef TRACE
std::cout << "shiftregBitfade" << std::endl;
#endif
shiftregBitfade();
shift_latch = shift_register;
// New noise waveform output.
set_noise_output();
}
// Latch the test bit value for shift phase 2.
test_or_reset = true;
// The test bit sets pulse high.
pulse_output = 0xfff;
}
else
{
// Calculate new accumulator value;
const unsigned int accumulator_old = accumulator;
accumulator = (accumulator + freq) & 0xffffff;
// Check which bit have changed from low to high
const unsigned int accumulator_bits_set = ~accumulator_old & accumulator;
// Check whether the MSB is set high. This is used for synchronization.
msb_rising = (accumulator_bits_set & 0x800000) != 0;
// Shift noise register once for each time accumulator bit 19 is set high.
// The shift is delayed 2 cycles.
if (unlikely((accumulator_bits_set & 0x080000) != 0))
{
// Pipeline: Detect rising bit, shift phase 1, shift phase 2.
shift_pipeline = 2;
}
else if (unlikely(shift_pipeline != 0))
{
switch (--shift_pipeline)
{
case 0:
#ifdef TRACE
std::cout << "shift phase 2" << std::endl;
#endif
shift_phase2(waveform, waveform);
break;
case 1:
#ifdef TRACE
std::cout << "shift phase 1" << std::endl;
#endif
// Start shift phase 1.
test_or_reset = false;
shift_latch = shift_register;
break;
}
}
}
}
RESID_INLINE
unsigned int WaveformGenerator::output(const WaveformGenerator* ringModulator)
{
// Set output value.
if (likely(waveform != 0))
{
const unsigned int ix = (accumulator ^ (~ringModulator->accumulator & ring_msb_mask)) >> 12;
// The bit masks no_pulse and no_noise are used to achieve branch-free
// calculation of the output value.
waveform_output = wave[ix] & (no_pulse | pulse_output) & no_noise_or_noise_output;
if (pulldown != nullptr)
waveform_output = pulldown[waveform_output];
// Triangle/Sawtooth output is delayed half cycle on 8580.
// This will appear as a one cycle delay on OSC3 as it is latched
// in the first phase of the clock.
if ((waveform & 3) && !is6581)
{
osc3 = tri_saw_pipeline & (no_pulse | pulse_output) & no_noise_or_noise_output;
if (pulldown != nullptr)
osc3 = pulldown[osc3];
tri_saw_pipeline = wave[ix];
}
else
{
osc3 = waveform_output;
}
// In the 6581 the top bit of the accumulator may be driven low by combined waveforms
// when the sawtooth is selected
if (is6581
&& (waveform & 0x2)
&& ((waveform_output & 0x800) == 0))
accumulator &= 0x7fffff;
write_shift_register();
}
else
{
// Age floating DAC input.
if (likely(floating_output_ttl != 0) && unlikely(--floating_output_ttl == 0))
{
waveBitfade();
}
}
// The pulse level is defined as (accumulator >> 12) >= pw ? 0xfff : 0x000.
// The expression -((accumulator >> 12) >= pw) & 0xfff yields the same
// results without any branching (and thus without any pipeline stalls).
// NB! This expression relies on that the result of a boolean expression
// is either 0 or 1, and furthermore requires two's complement integer.
// A few more cycles may be saved by storing the pulse width left shifted
// 12 bits, and dropping the and with 0xfff (this is valid since pulse is
// used as a bit mask on 12 bit values), yielding the expression
// -(accumulator >= pw24). However this only results in negligible savings.
// The result of the pulse width compare is delayed one cycle.
// Push next pulse level into pulse level pipeline.
pulse_output = ((accumulator >> 12) >= pw) ? 0xfff : 0x000;
return waveform_output;
}
} // namespace reSIDfp
#endif
#endif
matrix< short >
reSIDfp::WaveformGenerator
Definition WaveformGenerator.h:94
reSIDfp::WaveformGenerator::synchronize
void synchronize(WaveformGenerator *syncDest, const WaveformGenerator *syncSource) const
Definition WaveformGenerator.cpp:338
reSIDfp::WaveformGenerator::readTest
bool readTest() const
Definition WaveformGenerator.h:292
reSIDfp::WaveformGenerator::output
unsigned int output(const WaveformGenerator *ringModulator)
Definition WaveformGenerator.h:373
reSIDfp::WaveformGenerator::clock
void clock()
Definition WaveformGenerator.h:308
reSIDfp::WaveformGenerator::readFreq
unsigned int readFreq() const
Definition WaveformGenerator.h:287
reSIDfp::WaveformGenerator::writePW_HI
void writePW_HI(unsigned char pw_hi)
Definition WaveformGenerator.h:252
reSIDfp::WaveformGenerator::writeFREQ_HI
void writeFREQ_HI(unsigned char freq_hi)
Definition WaveformGenerator.h:238
reSIDfp::WaveformGenerator::writePW_LO
void writePW_LO(unsigned char pw_lo)
Definition WaveformGenerator.h:245
reSIDfp::WaveformGenerator::readOSC
unsigned char readOSC() const
Definition WaveformGenerator.h:277
reSIDfp::WaveformGenerator::writeFREQ_LO
void writeFREQ_LO(unsigned char freq_lo)
Definition WaveformGenerator.h:231
reSIDfp::WaveformGenerator::writeCONTROL_REG
void writeCONTROL_REG(unsigned char control)
Definition WaveformGenerator.cpp:354
reSIDfp::WaveformGenerator::WaveformGenerator
WaveformGenerator()
Definition WaveformGenerator.h:200
reSIDfp::WaveformGenerator::readAccumulator
unsigned int readAccumulator() const
Definition WaveformGenerator.h:282
reSIDfp::WaveformGenerator::readSync
bool readSync() const
Definition WaveformGenerator.h:297
reSIDfp::WaveformGenerator::setModel
void setModel(bool is6581)
Definition WaveformGenerator.h:180
reSIDfp::WaveformGenerator::reset
void reset()
Definition WaveformGenerator.cpp:453