365 lines
11 KiB
Text
365 lines
11 KiB
Text
/*
|
|
* This file is part of libsidplayfp, a SID player engine.
|
|
*
|
|
* Copyright 2011-2014 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"
|
|
|
|
namespace reSIDfp
|
|
{
|
|
|
|
/**
|
|
* A 24 bit accumulator is the basis for waveform generation.
|
|
* FREQ is added to the lower 16 bits of the accumulator each cycle.
|
|
* The accumulator is set to zero when TEST is set, and starts counting
|
|
* when TEST is cleared.
|
|
*
|
|
* Waveforms are generated as follows:
|
|
*
|
|
* - No waveform:
|
|
* When no waveform is selected, the DAC input is floating.
|
|
*
|
|
*
|
|
* - Triangle:
|
|
* The upper 12 bits of the accumulator are used.
|
|
* The MSB is used to create the falling edge of the triangle by inverting
|
|
* the lower 11 bits. The MSB is thrown away and the lower 11 bits are
|
|
* left-shifted (half the resolution, full amplitude).
|
|
* Ring modulation substitutes the MSB with MSB EOR sync_source MSB.
|
|
*
|
|
*
|
|
* - Sawtooth:
|
|
* The output is identical to the upper 12 bits of the accumulator.
|
|
*
|
|
*
|
|
* - Pulse:
|
|
* The upper 12 bits of the accumulator are used.
|
|
* These bits are compared to the pulse width register by a 12 bit digital
|
|
* comparator; output is either all one or all zero bits.
|
|
* The pulse setting is delayed one cycle after the compare; this is only
|
|
* modeled for single cycle clocking.
|
|
* The test bit, when set to one, holds the pulse waveform output at 0xfff
|
|
* regardless of the pulse width setting.
|
|
*
|
|
*
|
|
* - Noise:
|
|
* The noise output is taken from intermediate bits of a 23-bit shift register
|
|
* which is clocked by bit 19 of the accumulator.
|
|
* The shift is delayed 2 cycles after bit 19 is set high; this is only
|
|
* modeled for single cycle clocking.
|
|
*
|
|
* Operation: Calculate EOR result, shift register, set bit 0 = result.
|
|
*
|
|
* reset -------------------------------------------
|
|
* | | |
|
|
* test--OR-->EOR<-- |
|
|
* | | |
|
|
* 2 2 2 1 1 1 1 1 1 1 1 1 1 |
|
|
* Register bits: 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 <---
|
|
* | | | | | | | |
|
|
* Waveform bits: 1 1 9 8 7 6 5 4
|
|
* 1 0
|
|
*
|
|
* The low 4 waveform bits are zero (grounded).
|
|
*/
|
|
class WaveformGenerator
|
|
{
|
|
private:
|
|
matrix_t* model_wave;
|
|
|
|
short* wave;
|
|
|
|
// PWout = (PWn/40.95)%
|
|
unsigned int pw;
|
|
|
|
unsigned int shift_register;
|
|
|
|
/// Remaining time to fully reset shift register.
|
|
int shift_register_reset;
|
|
|
|
/// Emulation of pipeline causing bit 19 to clock the shift register.
|
|
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;
|
|
|
|
/// The control register right-shifted 4 bits; used for output function table lookup.
|
|
unsigned int waveform;
|
|
|
|
int floating_output_ttl;
|
|
|
|
unsigned int waveform_output;
|
|
|
|
/// Current and previous accumulator value.
|
|
unsigned int accumulator;
|
|
|
|
// Fout = (Fn*Fclk/16777216)Hz
|
|
unsigned int freq;
|
|
|
|
/// The control register bits. Gate is handled by EnvelopeGenerator.
|
|
//@{
|
|
bool test;
|
|
bool sync;
|
|
//@}
|
|
|
|
/// Tell whether the accumulator MSB was set high on this cycle.
|
|
bool msb_rising;
|
|
|
|
float dac[4096];
|
|
|
|
private:
|
|
void clock_shift_register();
|
|
|
|
void write_shift_register();
|
|
|
|
void reset_shift_register();
|
|
|
|
void set_noise_output();
|
|
|
|
public:
|
|
void setWaveformModels(matrix_t* models);
|
|
|
|
/**
|
|
* Set the chip model.
|
|
* This determines the type of the analog DAC emulation:
|
|
* 8580 is perfectly linear while 6581 is nonlinear.
|
|
*
|
|
* @param chipModel
|
|
*/
|
|
void setChipModel(ChipModel chipModel);
|
|
|
|
/**
|
|
* SID clocking - 1 cycle.
|
|
*/
|
|
void clock();
|
|
|
|
/**
|
|
* Synchronize oscillators.
|
|
* This must be done after all the oscillators have been clock()'ed,
|
|
* so that they are in the same state.
|
|
*
|
|
* @param syncDest The oscillator I am syncing
|
|
* @param syncSource The oscillator syncing me.
|
|
*/
|
|
void synchronize(WaveformGenerator* syncDest, const WaveformGenerator* syncSource) const;
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
WaveformGenerator() :
|
|
model_wave(0),
|
|
wave(0),
|
|
pw(0),
|
|
shift_register(0),
|
|
shift_register_reset(0),
|
|
shift_pipeline(0),
|
|
ring_msb_mask(0),
|
|
no_noise(0),
|
|
noise_output(0),
|
|
no_noise_or_noise_output(no_noise | noise_output),
|
|
no_pulse(0),
|
|
pulse_output(0),
|
|
waveform(0),
|
|
floating_output_ttl(0),
|
|
waveform_output(0),
|
|
accumulator(0),
|
|
freq(0),
|
|
test(false),
|
|
sync(false),
|
|
msb_rising(false) {}
|
|
|
|
/**
|
|
* Write FREQ LO register.
|
|
*
|
|
* @param freq_lo low 8 bits of frequency
|
|
*/
|
|
void writeFREQ_LO(unsigned char freq_lo) { freq = (freq & 0xff00) | (freq_lo & 0xff); }
|
|
|
|
/**
|
|
* Write FREQ HI register.
|
|
*
|
|
* @param freq_hi high 8 bits of frequency
|
|
*/
|
|
void writeFREQ_HI(unsigned char freq_hi) { freq = (freq_hi << 8 & 0xff00) | (freq & 0xff); }
|
|
|
|
/**
|
|
* Write PW LO register.
|
|
*
|
|
* @param pw_lo low 8 bits of pulse width
|
|
*/
|
|
void writePW_LO(unsigned char pw_lo) { pw = (pw & 0xf00) | (pw_lo & 0x0ff); }
|
|
|
|
/**
|
|
* Write PW HI register.
|
|
*
|
|
* @param pw_hi high 8 bits of pulse width
|
|
*/
|
|
void writePW_HI(unsigned char pw_hi) { pw = (pw_hi << 8 & 0xf00) | (pw & 0x0ff); }
|
|
|
|
/**
|
|
* Write CONTROL REGISTER register.
|
|
*
|
|
* @param control control register value
|
|
*/
|
|
void writeCONTROL_REG(unsigned char control);
|
|
|
|
/**
|
|
* SID reset.
|
|
*/
|
|
void reset();
|
|
|
|
/**
|
|
* Get the Waveform Generator output.
|
|
* The output from SID 8580 is delayed one cycle compared to SID 6581;
|
|
*
|
|
* @param ringModulator The oscillator ring-modulating me.
|
|
* @return output from waveform generator
|
|
*/
|
|
float output(const WaveformGenerator* ringModulator);
|
|
|
|
/**
|
|
* Read OSC3 value (6581, not latched/delayed version)
|
|
*/
|
|
unsigned char readOSC() const { return static_cast<unsigned char>(waveform_output >> 4); }
|
|
|
|
/**
|
|
* Read accumulator value.
|
|
*/
|
|
unsigned int readAccumulator() const { return accumulator; }
|
|
|
|
/**
|
|
* Read freq value.
|
|
*/
|
|
unsigned int readFreq() const { return freq; }
|
|
|
|
/**
|
|
* Read test value.
|
|
*/
|
|
bool readTest() const { return test; }
|
|
|
|
/**
|
|
* Read sync value.
|
|
*/
|
|
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))
|
|
{
|
|
reset_shift_register();
|
|
}
|
|
|
|
// The test bit sets pulse high.
|
|
pulse_output = 0xfff;
|
|
}
|
|
else
|
|
{
|
|
// Calculate new accumulator value;
|
|
const unsigned int accumulator_next = (accumulator + freq) & 0xffffff;
|
|
const unsigned int accumulator_bits_set = ~accumulator & accumulator_next;
|
|
accumulator = accumulator_next;
|
|
|
|
// 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 && --shift_pipeline == 0)
|
|
{
|
|
clock_shift_register();
|
|
}
|
|
}
|
|
}
|
|
|
|
RESID_INLINE
|
|
float WaveformGenerator::output(const WaveformGenerator* ringModulator)
|
|
{
|
|
// Set output value.
|
|
if (likely(waveform != 0))
|
|
{
|
|
// The bit masks no_pulse and no_noise are used to achieve branch-free
|
|
// calculation of the output value.
|
|
const unsigned int ix = (accumulator ^ (ringModulator->accumulator & ring_msb_mask)) >> 12;
|
|
waveform_output = wave[ix] & (no_pulse | pulse_output) & no_noise_or_noise_output;
|
|
|
|
if (unlikely(waveform > 0x8))
|
|
{
|
|
// Combined waveforms that include noise
|
|
// write to the shift register.
|
|
write_shift_register();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Age floating DAC input.
|
|
if (likely(floating_output_ttl != 0) && unlikely(--floating_output_ttl == 0))
|
|
{
|
|
waveform_output = 0;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
|
|
// DAC imperfections are emulated by using waveform_output as an index
|
|
// into a DAC lookup table. readOSC() uses waveform_output directly.
|
|
return dac[waveform_output];
|
|
}
|
|
|
|
} // namespace reSIDfp
|
|
|
|
#endif
|
|
|
|
#endif
|