mirror of
https://github.com/wavemotion-dave/GimliDS.git
synced 2025-06-18 22:05:33 -04:00
1365 lines
46 KiB
C++
1365 lines
46 KiB
C++
// =====================================================================================
|
|
// GimliDS Copyright (c) 2025 Dave Bernazzani (wavemotion-dave)
|
|
//
|
|
// As GimliDS is a port of the Frodo emulator for the DS/DSi/XL/LL handhelds,
|
|
// any copying or distribution of this emulator, its source code and associated
|
|
// readme files, with or without modification, are permitted per the original
|
|
// Frodo emulator license shown below. Hugest thanks to Christian Bauer for his
|
|
// efforts to provide a clean open-source emulation base for the C64.
|
|
//
|
|
// Numerous hacks and 'unsafe' optimizations have been performed on the original
|
|
// Frodo emulator codebase to get it running on the small handheld system. You
|
|
// are strongly encouraged to seek out the official Frodo sources if you're at
|
|
// all interested in this emulator code.
|
|
//
|
|
// The GimliDS emulator is offered as-is, without any warranty. Please see readme.md
|
|
// =====================================================================================
|
|
|
|
/*
|
|
* SID.cpp - 6581 emulation
|
|
*
|
|
* Frodo Copyright (C) Christian Bauer
|
|
*
|
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* Incompatibilities:
|
|
* ------------------
|
|
*
|
|
* - Lots of empirically determined constants in the filter calculations
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include "VIC.h"
|
|
#include <nds.h>
|
|
#include <maxmod9.h>
|
|
#include "soundbank.h"
|
|
#include "soundbank_bin.h"
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
#include "SID.h"
|
|
#include "Prefs.h"
|
|
|
|
#define USE_FIXPOINT_MATHS
|
|
#define FIXPOINT_PREC 16 // number of fractional bits used in fixpoint representation
|
|
#define PRECOMPUTE_RESONANCE
|
|
#define ldSINTAB 9 // size of sinus table (0 to 90 degrees)
|
|
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
#include "FixPoint.h"
|
|
#endif
|
|
|
|
|
|
uint8 regs[32] __attribute__((section(".dtcm"))); // Copies of the 32 write-only SID registers
|
|
uint8 last_sid_byte __attribute__((section(".dtcm"))); // Last value written to SID
|
|
uint32_t sid_random_seed __attribute__((section(".dtcm"))); // Random seed - global so it's deterministic
|
|
|
|
|
|
/*
|
|
* Resonance frequency polynomials
|
|
*/
|
|
|
|
#define CALC_RESONANCE_LP(f) (227.755 - 1.7635 * f - 0.0176385 * f * f + 0.00333484 * f * f * f)
|
|
#define CALC_RESONANCE_HP(f) (366.374 - 14.0052 * f + 0.603212 * f * f - 0.000880196 * f * f * f)
|
|
|
|
/*
|
|
* Random number generator for noise waveform
|
|
*/
|
|
static uint8 sid_random(void)
|
|
{
|
|
sid_random_seed = sid_random_seed * 1103515245 + 12345;
|
|
return sid_random_seed >> 16;
|
|
}
|
|
|
|
|
|
/*
|
|
* Constructor
|
|
*/
|
|
|
|
MOS6581::MOS6581(C64 *c64) : the_c64(c64)
|
|
{
|
|
the_renderer = NULL;
|
|
for (int i=0; i<32; i++)
|
|
regs[i] = 0;
|
|
|
|
// Open the renderer
|
|
open_close_renderer(SIDTYPE_NONE, ThePrefs.SIDType);
|
|
}
|
|
|
|
|
|
/*
|
|
* Destructor
|
|
*/
|
|
|
|
MOS6581::~MOS6581()
|
|
{
|
|
// Close the renderer
|
|
open_close_renderer(ThePrefs.SIDType, SIDTYPE_NONE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the SID
|
|
*/
|
|
|
|
void MOS6581::Reset(void)
|
|
{
|
|
for (int i=0; i<32; i++)
|
|
regs[i] = 0;
|
|
last_sid_byte = 0;
|
|
fake_v3_count = 0x555555;
|
|
sid_random_seed = 1;
|
|
|
|
// Reset the renderer
|
|
if (the_renderer != NULL)
|
|
the_renderer->Reset();
|
|
}
|
|
|
|
|
|
/*
|
|
* Preferences may have changed
|
|
*/
|
|
|
|
void MOS6581::NewPrefs(Prefs *prefs)
|
|
{
|
|
open_close_renderer(ThePrefs.SIDType, prefs->SIDType);
|
|
if (the_renderer != NULL)
|
|
the_renderer->NewPrefs(prefs);
|
|
}
|
|
|
|
|
|
/*
|
|
* Pause sound output
|
|
*/
|
|
|
|
void MOS6581::PauseSound(void)
|
|
{
|
|
if (the_renderer != NULL)
|
|
the_renderer->Pause();
|
|
}
|
|
|
|
|
|
/*
|
|
* Resume sound output
|
|
*/
|
|
|
|
void MOS6581::ResumeSound(void)
|
|
{
|
|
if (the_renderer != NULL)
|
|
the_renderer->Resume();
|
|
}
|
|
|
|
|
|
/*
|
|
* Get SID state
|
|
*/
|
|
|
|
void MOS6581::GetState(MOS6581State *ss)
|
|
{
|
|
ss->freq_lo_1 = regs[0];
|
|
ss->freq_hi_1 = regs[1];
|
|
ss->pw_lo_1 = regs[2];
|
|
ss->pw_hi_1 = regs[3];
|
|
ss->ctrl_1 = regs[4];
|
|
ss->AD_1 = regs[5];
|
|
ss->SR_1 = regs[6];
|
|
|
|
ss->freq_lo_2 = regs[7];
|
|
ss->freq_hi_2 = regs[8];
|
|
ss->pw_lo_2 = regs[9];
|
|
ss->pw_hi_2 = regs[10];
|
|
ss->ctrl_2 = regs[11];
|
|
ss->AD_2 = regs[12];
|
|
ss->SR_2 = regs[13];
|
|
|
|
ss->freq_lo_3 = regs[14];
|
|
ss->freq_hi_3 = regs[15];
|
|
ss->pw_lo_3 = regs[16];
|
|
ss->pw_hi_3 = regs[17];
|
|
ss->ctrl_3 = regs[18];
|
|
ss->AD_3 = regs[19];
|
|
ss->SR_3 = regs[20];
|
|
|
|
ss->fc_lo = regs[21];
|
|
ss->fc_hi = regs[22];
|
|
ss->res_filt = regs[23];
|
|
ss->mode_vol = regs[24];
|
|
|
|
ss->pot_x = 0xff;
|
|
ss->pot_y = 0xff;
|
|
ss->osc_3 = 0;
|
|
ss->env_3 = 0;
|
|
|
|
ss->v3_count = fake_v3_count;
|
|
ss->v3_eg_level = fake_v3_eg_level;
|
|
ss->v3_eg_state = fake_v3_eg_state;
|
|
ss->sid_seed = sid_random_seed;
|
|
}
|
|
|
|
|
|
/*
|
|
* Restore SID state
|
|
*/
|
|
|
|
void MOS6581::SetState(MOS6581State *ss)
|
|
{
|
|
regs[0] = ss->freq_lo_1;
|
|
regs[1] = ss->freq_hi_1;
|
|
regs[2] = ss->pw_lo_1;
|
|
regs[3] = ss->pw_hi_1;
|
|
regs[4] = ss->ctrl_1;
|
|
regs[5] = ss->AD_1;
|
|
regs[6] = ss->SR_1;
|
|
|
|
regs[7] = ss->freq_lo_2;
|
|
regs[8] = ss->freq_hi_2;
|
|
regs[9] = ss->pw_lo_2;
|
|
regs[10] = ss->pw_hi_2;
|
|
regs[11] = ss->ctrl_2;
|
|
regs[12] = ss->AD_2;
|
|
regs[13] = ss->SR_2;
|
|
|
|
regs[14] = ss->freq_lo_3;
|
|
regs[15] = ss->freq_hi_3;
|
|
regs[16] = ss->pw_lo_3;
|
|
regs[17] = ss->pw_hi_3;
|
|
regs[18] = ss->ctrl_3;
|
|
regs[19] = ss->AD_3;
|
|
regs[20] = ss->SR_3;
|
|
|
|
regs[21] = ss->fc_lo;
|
|
regs[22] = ss->fc_hi;
|
|
regs[23] = ss->res_filt;
|
|
regs[24] = ss->mode_vol;
|
|
|
|
fake_v3_count = ss->v3_count;
|
|
fake_v3_eg_level = ss->v3_eg_level;
|
|
fake_v3_eg_state = ss->v3_eg_state;
|
|
sid_random_seed = ss->sid_seed;
|
|
|
|
// Stuff the new register values into the renderer
|
|
if (the_renderer != NULL)
|
|
for (int i=0; i<25; i++)
|
|
the_renderer->WriteRegister(i, regs[i]);
|
|
}
|
|
|
|
|
|
/**
|
|
** Renderer for digital SID emulation (SIDTYPE_DIGITAL)
|
|
**/
|
|
|
|
const uint32 SAMPLE_FREQ = 22050; // NDS Sample Rate - reasonable quality and speed
|
|
const uint32 SID_FREQ = 985248; // SID frequency in Hz
|
|
const uint32 SID_CYCLES_FIX = ((SID_FREQ << 11)/SAMPLE_FREQ)<<5; // # of SID clocks per sample frame * 65536
|
|
const uint32 SID_CYCLES = SID_CYCLES_FIX << 16; // # of SID clocks per sample frame
|
|
const int SAMPLE_BUF_SIZE = 0x138*2;// Size of buffer for sampled voice (double buffered)
|
|
|
|
uint8 sample_buf[SAMPLE_BUF_SIZE] __attribute__((section(".dtcm"))); // Buffer for sampled voice
|
|
int sample_in_ptr __attribute__((section(".dtcm"))); // Index in sample_buf for writing
|
|
|
|
// SID waveforms (some of them :-)
|
|
enum {
|
|
WAVE_NONE,
|
|
WAVE_TRI,
|
|
WAVE_SAW,
|
|
WAVE_TRISAW,
|
|
WAVE_RECT,
|
|
WAVE_TRIRECT,
|
|
WAVE_SAWRECT,
|
|
WAVE_TRISAWRECT,
|
|
WAVE_NOISE
|
|
};
|
|
|
|
|
|
// Filter types
|
|
enum {
|
|
FILT_NONE,
|
|
FILT_LP,
|
|
FILT_BP,
|
|
FILT_LPBP,
|
|
FILT_HP,
|
|
FILT_NOTCH,
|
|
FILT_HPBP,
|
|
FILT_ALL
|
|
};
|
|
|
|
// Structure for one voice
|
|
struct DRVoice {
|
|
int wave; // Selected waveform
|
|
int eg_state; // Current state of EG
|
|
DRVoice *mod_by; // Voice that modulates this one
|
|
DRVoice *mod_to; // Voice that is modulated by this one
|
|
|
|
uint32 count; // Counter for waveform generator, 8.16 fixed
|
|
uint32 add; // Added to counter in every frame
|
|
|
|
uint16 freq; // SID frequency value
|
|
uint16 pw; // SID pulse-width value
|
|
|
|
int32 a_add; // EG parameters
|
|
int32 d_sub;
|
|
int32 s_level;
|
|
int32 r_sub;
|
|
int32 eg_level; // Current EG level, 8.16 fixed
|
|
|
|
uint32 noise; // Last noise generator output value
|
|
|
|
bool gate; // EG gate bit
|
|
bool ring; // Ring modulation bit
|
|
bool test; // Test bit
|
|
bool filter; // Flag: Voice filtered
|
|
|
|
// The following bit is set for the modulating
|
|
// voice, not for the modulated one (as the SID bits)
|
|
bool sync; // Sync modulation bit
|
|
bool mute; // Voice muted (voice 3 only)
|
|
};
|
|
|
|
DRVoice voice[3] __attribute__((section(".dtcm"))); // Data for 3 voices
|
|
|
|
// Renderer class
|
|
class DigitalRenderer : public SIDRenderer {
|
|
public:
|
|
|
|
DigitalRenderer();
|
|
virtual ~DigitalRenderer();
|
|
|
|
virtual void Reset(void);
|
|
virtual void EmulateLine(void);
|
|
virtual void WriteRegister(uint16 adr, uint8 byte);
|
|
virtual void NewPrefs(Prefs *prefs);
|
|
virtual void Pause(void);
|
|
virtual void Resume(void);
|
|
int16 calc_buffer(int16 *buf, long count);
|
|
bool ready; // Flag: Renderer has initialized and is ready
|
|
private:
|
|
void init_sound(void);
|
|
void calc_filter(void);
|
|
uint8 volume; // Master volume
|
|
|
|
static const uint16 TriSawTable[0x100];
|
|
static const uint16 TriRectTable[0x100];
|
|
static const uint16 SawRectTable[0x100];
|
|
static const uint16 TriSawRectTable[0x100];
|
|
static const int32_t EGTable[16]; // Increment/decrement values for all A/D/R settings
|
|
|
|
uint8 f_type; // Filter type
|
|
uint8 f_freq; // SID filter frequency (upper 8 bits)
|
|
uint8 f_freq_low; // SID filter frequency (lower 4 bits)
|
|
uint8 f_res; // Filter resonance (0..15)
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
FixPoint f_ampl;
|
|
FixPoint d1, d2, g1, g2;
|
|
int32 xn1, xn2, yn1, yn2; // can become very large
|
|
FixPoint sidquot;
|
|
#ifdef PRECOMPUTE_RESONANCE
|
|
FixPoint resonanceLP[257];
|
|
FixPoint resonanceHP[257];
|
|
#endif
|
|
#else
|
|
float f_ampl; // IIR filter input attenuation
|
|
float d1, d2, g1, g2; // IIR filter coefficients
|
|
float xn1, xn2, yn1, yn2; // IIR filter previous input/output signal
|
|
#ifdef PRECOMPUTE_RESONANCE
|
|
float resonanceLP[257]; // shortcut for calc_filter
|
|
float resonanceHP[257];
|
|
#endif
|
|
#endif
|
|
int16 *sound_buffer;
|
|
};
|
|
|
|
|
|
#ifndef EMUL_MOS8580
|
|
// Sampled from a 6581R4
|
|
const uint16 DigitalRenderer::TriSawTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1010, 0x3C3C
|
|
};
|
|
|
|
const uint16 DigitalRenderer::TriRectTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0xC0C0,
|
|
0x0000, 0x8080, 0x8080, 0xE0E0, 0x8080, 0xE0E0, 0xF0F0, 0xFCFC,
|
|
0xFFFF, 0xFCFC, 0xFAFA, 0xF0F0, 0xF6F6, 0xE0E0, 0xE0E0, 0x8080,
|
|
0xEEEE, 0xE0E0, 0xE0E0, 0x8080, 0xC0C0, 0x0000, 0x0000, 0x0000,
|
|
0xDEDE, 0xC0C0, 0xC0C0, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000,
|
|
0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0xBEBE, 0x8080, 0x8080, 0x0000, 0x8080, 0x0000, 0x0000, 0x0000,
|
|
0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x7E7E, 0x4040, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
|
};
|
|
|
|
const uint16 DigitalRenderer::SawRectTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7878
|
|
};
|
|
|
|
const uint16 DigitalRenderer::TriSawRectTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
|
};
|
|
#else
|
|
// Sampled from an 8580R5
|
|
const uint16 DigitalRenderer::TriSawTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0808,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1818, 0x3C3C,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1C1C,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x0000, 0x8080, 0x8080,
|
|
0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0,
|
|
0xF0F0, 0xF0F0, 0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE
|
|
};
|
|
|
|
const uint16 DigitalRenderer::TriRectTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0xFFFF, 0xFCFC, 0xF8F8, 0xF0F0, 0xF4F4, 0xF0F0, 0xF0F0, 0xE0E0,
|
|
0xECEC, 0xE0E0, 0xE0E0, 0xC0C0, 0xE0E0, 0xC0C0, 0xC0C0, 0xC0C0,
|
|
0xDCDC, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0x8080, 0x8080,
|
|
0xC0C0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000, 0x0000,
|
|
0xBEBE, 0xA0A0, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x0000,
|
|
0x8080, 0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x8080, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x7E7E, 0x7070, 0x6060, 0x0000, 0x4040, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
|
};
|
|
|
|
const uint16 DigitalRenderer::SawRectTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080,
|
|
0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xB0B0, 0xBEBE,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080,
|
|
0x0000, 0x0000, 0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0,
|
|
0x0000, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0,
|
|
0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xDCDC,
|
|
0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0, 0xC0C0,
|
|
0xC0C0, 0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xECEC,
|
|
0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF0F0, 0xF4F4,
|
|
0xF0F0, 0xF0F0, 0xF8F8, 0xF8F8, 0xF8F8, 0xFCFC, 0xFEFE, 0xFFFF
|
|
};
|
|
|
|
const uint16 DigitalRenderer::TriSawRectTable[0x100] = {
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, 0x8080,
|
|
0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0x8080, 0xC0C0, 0xC0C0,
|
|
0xC0C0, 0xC0C0, 0xE0E0, 0xE0E0, 0xE0E0, 0xF0F0, 0xF8F8, 0xFCFC
|
|
};
|
|
#endif
|
|
|
|
int16_t EGDivTable[16] __attribute__((section(".dtcm"))) = {
|
|
9, 32,
|
|
63, 95,
|
|
149, 220,
|
|
267, 313,
|
|
392, 977,
|
|
1954, 3126,
|
|
3906, 11720,
|
|
19531, 31251
|
|
};
|
|
|
|
const int32_t DigitalRenderer::EGTable[16] = {
|
|
SID_CYCLES_FIX / 9, SID_CYCLES_FIX / 32,
|
|
SID_CYCLES_FIX / 63, SID_CYCLES_FIX / 95,
|
|
SID_CYCLES_FIX / 149, SID_CYCLES_FIX / 220,
|
|
SID_CYCLES_FIX / 267, SID_CYCLES_FIX / 313,
|
|
SID_CYCLES_FIX / 392, SID_CYCLES_FIX / 977,
|
|
SID_CYCLES_FIX / 1954, SID_CYCLES_FIX / 3126,
|
|
SID_CYCLES_FIX / 3906, SID_CYCLES_FIX / 11720,
|
|
SID_CYCLES_FIX / 19531, SID_CYCLES_FIX / 31251
|
|
};
|
|
|
|
uint8_t EGDRShift[256] __attribute__((section(".dtcm"))) = {
|
|
5,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4,
|
|
3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,
|
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
int16 SampleTab[16] __attribute__((section(".dtcm"))) = {
|
|
0x8000, 0x9111, 0xa222, 0xb333, 0xc444, 0xd555, 0xe666, 0xf777,
|
|
0x0888, 0x1999, 0x2aaa, 0x3bbb, 0x4ccc, 0x5ddd, 0x6eee, 0x7fff,
|
|
};
|
|
|
|
|
|
/*
|
|
* Constructor
|
|
*/
|
|
|
|
DigitalRenderer::DigitalRenderer()
|
|
{
|
|
// Link voices together
|
|
voice[0].mod_by = &voice[2];
|
|
voice[1].mod_by = &voice[0];
|
|
voice[2].mod_by = &voice[1];
|
|
voice[0].mod_to = &voice[1];
|
|
voice[1].mod_to = &voice[2];
|
|
voice[2].mod_to = &voice[0];
|
|
|
|
#ifdef PRECOMPUTE_RESONANCE
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
// slow floating point doesn't matter much on startup!
|
|
for (int i=0; i<257; i++) {
|
|
resonanceLP[i] = FixNo(CALC_RESONANCE_LP(i));
|
|
resonanceHP[i] = FixNo(CALC_RESONANCE_HP(i));
|
|
}
|
|
// Pre-compute the quotient. No problem since int-part is small enough
|
|
sidquot = SID_CYCLES_FIX;
|
|
// compute lookup table for sin and cos
|
|
InitFixSinTab();
|
|
#else
|
|
for (int i=0; i<257; i++) {
|
|
resonanceLP[i] = CALC_RESONANCE_LP(i);
|
|
resonanceHP[i] = CALC_RESONANCE_HP(i);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
Reset();
|
|
|
|
// System specific initialization
|
|
init_sound();
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset emulation
|
|
*/
|
|
|
|
void DigitalRenderer::Reset(void)
|
|
{
|
|
volume = 0;
|
|
|
|
for (int v=0; v<3; v++) {
|
|
voice[v].wave = WAVE_NONE;
|
|
voice[v].eg_state = EG_RELEASE;
|
|
voice[v].count = 0x555555;
|
|
voice[v].add = 0;
|
|
voice[v].freq = voice[v].pw = 0;
|
|
voice[v].eg_level = voice[v].s_level = 0;
|
|
voice[v].a_add = voice[v].d_sub = voice[v].r_sub = EGTable[0];
|
|
voice[v].gate = voice[v].ring = voice[v].test = false;
|
|
voice[v].filter = voice[v].sync = voice[v].mute = false;
|
|
}
|
|
|
|
f_type = FILT_NONE;
|
|
f_freq = f_res = 0;
|
|
f_freq_low = 0;
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
f_ampl = FixNo(1);
|
|
d1 = d2 = g1 = g2 = 0;
|
|
xn1 = xn2 = yn1 = yn2 = 0;
|
|
#else
|
|
f_ampl = 1.0;
|
|
d1 = d2 = g1 = g2 = 0.0;
|
|
xn1 = xn2 = yn1 = yn2 = 0.0;
|
|
#endif
|
|
|
|
sample_in_ptr = 0;
|
|
memset(sample_buf, 0, SAMPLE_BUF_SIZE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Write to register
|
|
*/
|
|
|
|
void DigitalRenderer::WriteRegister(uint16 adr, uint8 byte)
|
|
{
|
|
if (!ready)
|
|
return;
|
|
|
|
int v = adr/7; // Voice number
|
|
|
|
switch (adr) {
|
|
case 0:
|
|
case 7:
|
|
case 14:
|
|
voice[v].freq = (voice[v].freq & 0xff00) | byte;
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
voice[v].add = sidquot.imul((int)voice[v].freq);
|
|
#else
|
|
voice[v].add = (uint32)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ);
|
|
#endif
|
|
break;
|
|
|
|
case 1:
|
|
case 8:
|
|
case 15:
|
|
voice[v].freq = (voice[v].freq & 0xff) | (byte << 8);
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
voice[v].add = sidquot.imul((int)voice[v].freq);
|
|
#else
|
|
voice[v].add = (uint32)((float)voice[v].freq * SID_FREQ / SAMPLE_FREQ);
|
|
#endif
|
|
break;
|
|
|
|
case 2:
|
|
case 9:
|
|
case 16:
|
|
voice[v].pw = (voice[v].pw & 0x0f00) | byte;
|
|
break;
|
|
|
|
case 3:
|
|
case 10:
|
|
case 17:
|
|
voice[v].pw = (voice[v].pw & 0xff) | ((byte & 0xf) << 8);
|
|
break;
|
|
|
|
case 4:
|
|
case 11:
|
|
case 18:
|
|
voice[v].wave = (byte >> 4) & 0xf;
|
|
if ((byte & 1) != voice[v].gate)
|
|
{
|
|
if (byte & 1) // Gate turned on
|
|
voice[v].eg_state = EG_ATTACK;
|
|
else // Gate turned off
|
|
{
|
|
voice[v].eg_state = EG_RELEASE;
|
|
}
|
|
}
|
|
voice[v].gate = byte & 1;
|
|
voice[v].mod_by->sync = byte & 2;
|
|
voice[v].ring = byte & 4;
|
|
if ((voice[v].test = byte & 8) != 0)
|
|
voice[v].count = 0;
|
|
break;
|
|
|
|
case 5:
|
|
case 12:
|
|
case 19:
|
|
voice[v].a_add = EGTable[byte >> 4];
|
|
voice[v].d_sub = EGTable[byte & 0xf];
|
|
break;
|
|
|
|
case 6:
|
|
case 13:
|
|
case 20:
|
|
voice[v].s_level = (byte >> 4) * 0x111111;
|
|
voice[v].r_sub = EGTable[byte & 0xf];
|
|
break;
|
|
|
|
case 21: // Filter Frequency - lower 3 bits
|
|
f_freq_low = ((byte & 0x7) > 3) ? 1:0;
|
|
calc_filter();
|
|
break;
|
|
|
|
case 22: // Filter Frequency - upper 8 bits
|
|
f_freq = byte;
|
|
calc_filter();
|
|
break;
|
|
|
|
case 23:
|
|
voice[0].filter = byte & 1;
|
|
voice[1].filter = byte & 2;
|
|
voice[2].filter = byte & 4;
|
|
if ((byte >> 4) != f_res) {
|
|
f_res = byte >> 4;
|
|
calc_filter();
|
|
}
|
|
break;
|
|
|
|
case 24:
|
|
volume = byte & 0xf;
|
|
voice[2].mute = byte & 0x80;
|
|
if (((byte >> 4) & 7) != f_type) {
|
|
f_type = (byte >> 4) & 7;
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
xn1 = xn2 = yn1 = yn2 = 0;
|
|
#else
|
|
xn1 = xn2 = yn1 = yn2 = 0.0;
|
|
#endif
|
|
calc_filter();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Preferences may have changed
|
|
*/
|
|
|
|
void DigitalRenderer::NewPrefs(Prefs *prefs)
|
|
{
|
|
calc_filter();
|
|
}
|
|
|
|
|
|
/*
|
|
* Calculate IIR filter coefficients
|
|
*/
|
|
|
|
void DigitalRenderer::calc_filter(void)
|
|
{
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
FixPoint fr, arg;
|
|
|
|
if (f_type == FILT_ALL)
|
|
{
|
|
d1 = 0; d2 = 0; g1 = 0; g2 = 0; f_ampl = FixNo(1); return;
|
|
}
|
|
else if (f_type == FILT_NONE)
|
|
{
|
|
d1 = 0; d2 = 0; g1 = 0; g2 = 0; f_ampl = 0; return;
|
|
}
|
|
#else
|
|
float fr, arg;
|
|
|
|
// Check for some trivial cases
|
|
if (f_type == FILT_ALL) {
|
|
d1 = 0.0; d2 = 0.0;
|
|
g1 = 0.0; g2 = 0.0;
|
|
f_ampl = 1.0;
|
|
return;
|
|
} else if (f_type == FILT_NONE) {
|
|
d1 = 0.0; d2 = 0.0;
|
|
g1 = 0.0; g2 = 0.0;
|
|
f_ampl = 0.0;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Calculate resonance frequency
|
|
if (f_type == FILT_LP || f_type == FILT_LPBP)
|
|
#ifdef PRECOMPUTE_RESONANCE
|
|
fr = resonanceLP[f_freq + f_freq_low];
|
|
#else
|
|
fr = CALC_RESONANCE_LP(f_freq);
|
|
#endif
|
|
else
|
|
#ifdef PRECOMPUTE_RESONANCE
|
|
fr = resonanceHP[f_freq+f_freq_low];
|
|
#else
|
|
fr = CALC_RESONANCE_HP(f_freq);
|
|
#endif
|
|
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
// explanations see below.
|
|
#ifdef __NDS__
|
|
arg = fr / (int)(SAMPLE_FREQ >> 1);
|
|
#else
|
|
arg = fr / (SAMPLE_FREQ >> 1);
|
|
#endif
|
|
if (arg > FixNo(0.99)) {arg = FixNo(0.99);}
|
|
if (arg < FixNo(0.01)) {arg = FixNo(0.01);}
|
|
|
|
g2 = FixNo(0.55) + FixNo(1.2) * arg * (arg - 1) + FixNo(0.0133333333) * f_res;
|
|
g1 = FixNo(-2) * g2.sqrt() * fixcos(arg);
|
|
|
|
if (f_type == FILT_LPBP || f_type == FILT_HPBP) {g2 += FixNo(0.1);}
|
|
|
|
if (g1.abs() >= g2 + 1)
|
|
{
|
|
if (g1 > 0) {g1 = g2 + FixNo(0.99);}
|
|
else {g1 = -(g2 + FixNo(0.99));}
|
|
}
|
|
|
|
switch (f_type)
|
|
{
|
|
case FILT_LPBP:
|
|
case FILT_LP:
|
|
d1 = FixNo(2); d2 = FixNo(1); f_ampl = FixNo(0.25) * (1 + g1 + g2); break;
|
|
case FILT_HPBP:
|
|
case FILT_HP:
|
|
d1 = FixNo(-2); d2 = FixNo(1); f_ampl = FixNo(0.25) * (1 - g1 + g2); break;
|
|
case FILT_BP:
|
|
d1 = 0; d2 = FixNo(-1);
|
|
f_ampl = FixNo(0.25) * (1 + g1 + g2) * (1 + fixcos(arg)) / fixsin(arg);
|
|
break;
|
|
case FILT_NOTCH:
|
|
d1 = FixNo(-2) * fixcos(arg); d2 = FixNo(1);
|
|
f_ampl = FixNo(0.25) * (1 + g1 + g2) * (1 + fixcos(arg)) / fixsin(arg);
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
#else
|
|
|
|
// Limit to <1/2 sample frequency, avoid div by 0 in case FILT_BP below
|
|
arg = fr / (float)(SAMPLE_FREQ >> 1);
|
|
if (arg > 0.99)
|
|
arg = 0.99;
|
|
if (arg < 0.01)
|
|
arg = 0.01;
|
|
|
|
// Calculate poles (resonance frequency and resonance)
|
|
g2 = 0.55 + 1.2 * arg * arg - 1.2 * arg + (float)f_res * 0.0133333333;
|
|
g1 = -2.0 * sqrt(g2) * cos(M_PI * arg);
|
|
|
|
// Increase resonance if LP/HP combined with BP
|
|
if (f_type == FILT_LPBP || f_type == FILT_HPBP)
|
|
g2 += 0.1;
|
|
|
|
// Stabilize filter
|
|
if (fabs(g1) >= g2 + 1.0)
|
|
if (g1 > 0.0)
|
|
g1 = g2 + 0.99;
|
|
else
|
|
g1 = -(g2 + 0.99);
|
|
|
|
// Calculate roots (filter characteristic) and input attenuation
|
|
switch (f_type) {
|
|
|
|
case FILT_LPBP:
|
|
case FILT_LP:
|
|
d1 = 2.0; d2 = 1.0;
|
|
f_ampl = 0.25 * (1.0 + g1 + g2);
|
|
break;
|
|
|
|
case FILT_HPBP:
|
|
case FILT_HP:
|
|
d1 = -2.0; d2 = 1.0;
|
|
f_ampl = 0.25 * (1.0 - g1 + g2);
|
|
break;
|
|
|
|
case FILT_BP:
|
|
d1 = 0.0; d2 = -1.0;
|
|
f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / sin(M_PI * arg);
|
|
break;
|
|
|
|
case FILT_NOTCH:
|
|
d1 = -2.0 * cos(M_PI * arg); d2 = 1.0;
|
|
f_ampl = 0.25 * (1.0 + g1 + g2) * (1 + cos(M_PI * arg)) / (sin(M_PI * arg));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill one audio buffer with calculated SID sound
|
|
*/
|
|
|
|
ITCM_CODE int16 DigitalRenderer::calc_buffer(int16 *buf, long count)
|
|
{
|
|
// Get filter coefficients, so the emulator won't change
|
|
// them in the middle of our calculations
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
FixPoint cf_ampl = f_ampl;
|
|
FixPoint cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2;
|
|
#else
|
|
float cf_ampl = f_ampl;
|
|
float cd1 = d1, cd2 = d2, cg1 = g1, cg2 = g2;
|
|
#endif
|
|
|
|
// Index in sample_buf for reading, 16.16 fixed
|
|
uint32 sample_count = (sample_in_ptr + SAMPLE_BUF_SIZE/2) << 16;
|
|
|
|
count >>= 1; // 16 bit mono output, count is in bytes
|
|
|
|
while (count--)
|
|
{
|
|
// Get current master volume from sample buffer,
|
|
// calculate sampled voice
|
|
uint8 master_volume = sample_buf[(sample_count >> 16) % SAMPLE_BUF_SIZE];
|
|
sample_count += ((0x138 * 50) << 16) / SAMPLE_FREQ;
|
|
int32 sum_output = SampleTab[master_volume] << 8;
|
|
int32 sum_output_filter = 0;
|
|
|
|
// Loop for all three voices
|
|
for (int j=0; j<3; j++)
|
|
{
|
|
DRVoice *v = &voice[j];
|
|
|
|
// Envelope generators
|
|
uint16 envelope;
|
|
|
|
switch (v->eg_state) {
|
|
case EG_ATTACK:
|
|
v->eg_level += v->a_add;
|
|
if (v->eg_level > 0xffffff) {
|
|
v->eg_level = 0xffffff;
|
|
v->eg_state = EG_DECAY_SUSTAIN;
|
|
}
|
|
break;
|
|
case EG_DECAY_SUSTAIN:
|
|
v->eg_level -= v->d_sub >> EGDRShift[v->eg_level >> 16];
|
|
if (v->eg_level < v->s_level) {
|
|
v->eg_level = v->s_level;
|
|
}
|
|
break;
|
|
case EG_RELEASE:
|
|
v->eg_level -= v->r_sub >> EGDRShift[v->eg_level >> 16];
|
|
if (v->eg_level < 0) {
|
|
v->eg_level = 0;
|
|
}
|
|
break;
|
|
}
|
|
envelope = ((v->eg_level >> 16) * master_volume) >> 4;
|
|
|
|
// Waveform generator
|
|
if (v->mute)
|
|
continue;
|
|
uint16 output;
|
|
|
|
if (!v->test)
|
|
v->count += v->add;
|
|
|
|
if (v->sync && (v->count > 0x1000000))
|
|
v->mod_to->count = 0;
|
|
|
|
v->count &= 0xffffff;
|
|
|
|
switch (v->wave)
|
|
{
|
|
case WAVE_TRI: {
|
|
uint32_t ctrl = v->count;
|
|
if (v->ring) {
|
|
ctrl ^= v->mod_by->count;
|
|
}
|
|
if (ctrl & 0x800000) {
|
|
output = (v->count >> 7) ^ 0xffff;
|
|
} else {
|
|
output = v->count >> 7;
|
|
}
|
|
}
|
|
break;
|
|
case WAVE_SAW:
|
|
output = v->count >> 8;
|
|
break;
|
|
case WAVE_RECT:
|
|
if (v->count > (uint32)(v->pw << 12))
|
|
output = 0xffff;
|
|
else
|
|
output = 0;
|
|
break;
|
|
case WAVE_TRISAW:
|
|
output = TriSawTable[v->count >> 16];
|
|
break;
|
|
case WAVE_TRIRECT:
|
|
if (v->count > (uint32)(v->pw << 12))
|
|
output = TriRectTable[v->count >> 16];
|
|
else
|
|
output = 0;
|
|
break;
|
|
case WAVE_SAWRECT:
|
|
if (v->count > (uint32)(v->pw << 12))
|
|
output = SawRectTable[v->count >> 16];
|
|
else
|
|
output = 0;
|
|
break;
|
|
case WAVE_TRISAWRECT:
|
|
if (v->count > (uint32)(v->pw << 12))
|
|
output = TriSawRectTable[v->count >> 16];
|
|
else
|
|
output = 0;
|
|
break;
|
|
case WAVE_NOISE:
|
|
if (v->count > 0x100000) {
|
|
output = v->noise = sid_random() << 8;
|
|
v->count &= 0xfffff;
|
|
} else
|
|
output = v->noise;
|
|
break;
|
|
default:
|
|
output = 0x8000;
|
|
break;
|
|
}
|
|
if (v->filter)
|
|
sum_output_filter += (int16)(output ^ 0x8000) * envelope;
|
|
else
|
|
sum_output += (int16)(output ^ 0x8000) * envelope;
|
|
}
|
|
|
|
// Filter
|
|
#ifdef USE_FIXPOINT_MATHS
|
|
int32 xn = cf_ampl.imul(sum_output_filter);
|
|
int32 yn = xn+cd1.imul(xn1)+cd2.imul(xn2)-cg1.imul(yn1)-cg2.imul(yn2);
|
|
yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn;
|
|
sum_output_filter = yn;
|
|
#else
|
|
float xn = (float)sum_output_filter * cf_ampl;
|
|
float yn = xn + cd1 * xn1 + cd2 * xn2 - cg1 * yn1 - cg2 * yn2;
|
|
yn2 = yn1; yn1 = yn; xn2 = xn1; xn1 = xn;
|
|
sum_output_filter = (int32)yn;
|
|
#endif
|
|
|
|
// Write to buffer
|
|
*buf++ = ((sum_output - sum_output_filter) >> 10 );
|
|
}
|
|
buf--; return *buf;
|
|
}
|
|
|
|
/*
|
|
* SID_NDS.i
|
|
*
|
|
* RISC OS specific parts of the sound emulation
|
|
* Frodo (C) 1994-1997,2002 Christian Bauer
|
|
* Acorn port by Andreas Dehmel, 1997
|
|
*
|
|
*/
|
|
|
|
DigitalRenderer* p __attribute__((section(".dtcm")));
|
|
bool paused __attribute__((section(".dtcm"))) = false;
|
|
int16 last_sample = 0x8000;
|
|
|
|
ITCM_CODE mm_word SoundMixCallback(mm_word len, mm_addr stream, mm_stream_formats format)
|
|
{
|
|
if (paused)
|
|
{
|
|
s16 *p = (s16*)stream;
|
|
for (mm_word i=0; i<len; i++)
|
|
{
|
|
*p++ = last_sample; // To prevent pops and clicks... just keep outputting the last sample
|
|
*p++ = last_sample; // To prevent pops and clicks... just keep outputting the last sample
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_sample = p->calc_buffer((int16*)stream, len*2);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
void init_maxmod(void)
|
|
{
|
|
//----------------------------------------------------------------
|
|
// initialize maxmod with our small 3-effect soundbank
|
|
//----------------------------------------------------------------
|
|
mmInitDefaultMem((mm_addr)soundbank_bin);
|
|
|
|
mmLoadEffect(SFX_FLOPPY);
|
|
mmLoadEffect(SFX_KEYCLICK);
|
|
mmLoadEffect(SFX_MUS_INTRO);
|
|
}
|
|
|
|
void DigitalRenderer::init_sound(void)
|
|
{
|
|
p = this;
|
|
|
|
mm_stream mstream;
|
|
memset(&mstream, 0, sizeof(mstream));
|
|
mstream.sampling_rate = SAMPLE_FREQ;
|
|
mstream.buffer_length = 0x138 * 2;
|
|
mstream.callback = SoundMixCallback;
|
|
mstream.format = MM_STREAM_16BIT_MONO;
|
|
mstream.timer = MM_TIMER2;
|
|
mstream.manual = false;
|
|
DC_FlushAll();
|
|
mmStreamOpen(&mstream);
|
|
|
|
ready = true;
|
|
}
|
|
|
|
DigitalRenderer::~DigitalRenderer()
|
|
{
|
|
|
|
}
|
|
|
|
void DigitalRenderer::EmulateLine(void)
|
|
{
|
|
sample_buf[sample_in_ptr] = volume;
|
|
sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE;
|
|
}
|
|
|
|
void DigitalRenderer::Pause(void)
|
|
{
|
|
paused = true;
|
|
}
|
|
|
|
void DigitalRenderer::Resume(void)
|
|
{
|
|
paused = false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Open/close the renderer, according to old and new prefs
|
|
*/
|
|
|
|
void MOS6581::open_close_renderer(int old_type, int new_type)
|
|
{
|
|
if (old_type == new_type)
|
|
return;
|
|
|
|
// Delete the old renderer
|
|
delete the_renderer;
|
|
|
|
// Create new renderer
|
|
the_renderer = new DigitalRenderer;
|
|
|
|
// Stuff the current register values into the new renderer
|
|
if (the_renderer != NULL)
|
|
for (int i=0; i<25; i++)
|
|
the_renderer->WriteRegister(i, regs[i]);
|
|
}
|
|
|
|
/*
|
|
* Simulate oscillator 3 read-back
|
|
*/
|
|
uint8_t MOS6581::read_osc3() const
|
|
{
|
|
uint8_t v3_ctrl = regs[0x12]; // Voice 3 control register
|
|
if (v3_ctrl & 0x10) { // Triangle wave
|
|
// TODO: ring modulation from voice 2
|
|
if (fake_v3_count & 0x800000) {
|
|
return (fake_v3_count >> 15) ^ 0xff;
|
|
} else {
|
|
return fake_v3_count >> 15;
|
|
}
|
|
} else if (v3_ctrl & 0x20) { // Sawtooth wave
|
|
return fake_v3_count >> 16;
|
|
} else if (v3_ctrl & 0x40) { // Rectangle wave
|
|
uint32_t pw = ((regs[0x11] & 0x0f) << 8) | regs[0x10];
|
|
if (fake_v3_count > (pw << 12)) {
|
|
return 0xff;
|
|
} else {
|
|
return 0x00;
|
|
}
|
|
} else if (v3_ctrl & 0x80) { // Noise wave
|
|
return sid_random();
|
|
} else {
|
|
// TODO: combined waveforms
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Simulate EG 3 read-back
|
|
*/
|
|
|
|
uint8_t MOS6581::read_env3() const
|
|
{
|
|
return (uint8_t)(fake_v3_eg_level >> 16);
|
|
}
|
|
|