This is sid.c in view mode; [Download] [Up]
/*
* sid.c - MOS6581 (SID) emulation.
*
* Written by
* Teemu Rantanen (tvr@cs.hut.fi)
* Michael Schwendt (sidplay@geocities.com)
*
* Resource and cmdline code by
* Ettore Perazzoli (ettore@comm2000.it)
*
* This file is part of VICE, the Versatile Commodore Emulator.
* See README for copyright notice.
*
* 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.
*
*/
#include <stdio.h>
#include <math.h>
#include "vice.h"
#include "sid.h"
#include "cmdline.h"
#include "resources.h"
#include "utils.h"
#include "maincpu.h"
#include "vicii.h"
#ifdef HAVE_RESID
#include "resid.h"
static int useresid;
#endif
#ifdef HAVE_MOUSE
#include "mouse.h"
#endif
/* ------------------------------------------------------------------------- */
/* Resource handling -- Added by Ettore 98-04-26. */
/* FIXME: We need sanity checks! And do we really need all of these
`close_sound()' calls? */
static int sid_filters_enabled; /* app_resources.sidFilters */
static int sid_model; /* app_resources.sidModel */
static int sid_useresid;
static int set_sid_filters_enabled(resource_value_t v)
{
sid_filters_enabled = (int)v;
sound_close();
return 0;
}
static int set_sid_model(resource_value_t v)
{
sid_model = (int)v;
sound_close();
return 0;
}
static int set_sid_useresid(resource_value_t v)
{
sid_useresid = (int)v;
sound_close();
return 0;
}
static resource_t resources[] = {
{ "SidFilters", RES_INTEGER, (resource_value_t) 1,
(resource_value_t *) &sid_filters_enabled, set_sid_filters_enabled },
{ "SidModel", RES_INTEGER, (resource_value_t) 0,
(resource_value_t *) &sid_model, set_sid_model },
{ "SidUseResid", RES_INTEGER, (resource_value_t) 0,
(resource_value_t *) &sid_useresid, set_sid_useresid },
{ NULL }
};
int sid_init_resources(void)
{
return resources_register(resources);
}
/* ------------------------------------------------------------------------- */
/* Command-line options -- Added by Ettore 98-05-09. */
static cmdline_option_t cmdline_options[] = {
{ "-sidmodel", SET_RESOURCE, 1, NULL, NULL, "SidModel", NULL,
"<model>", "Specify SID model (1: 8580, 0: 6581)" },
{ "-sidfilters", SET_RESOURCE, 0, NULL, NULL, "SidFilters", (resource_value_t) 1,
NULL, "Emulate SID filters" },
{ "+sidfilters", SET_RESOURCE, 0, NULL, NULL, "SidFilters", (resource_value_t) 0,
NULL, "Do not emulate SID filters" },
#ifdef HAVE_RESID
{ "-resid", SET_RESOURCE, 0, NULL, NULL, "SidUseResid", (resource_value_t) 1,
NULL, "Use reSID emulation" },
{ "+resid", SET_RESOURCE, 0, NULL, NULL, "SidUseResid", (resource_value_t) 0,
NULL, "Use fast SID emulation" },
#endif
{ NULL }
};
int sid_init_cmdline_options(void)
{
return cmdline_register_options(cmdline_options);
}
/* ------------------------------------------------------------------------- */
/* warnings */
static warn_t *pwarn;
/* argh */
static BYTE siddata[32];
/* use wavetables (sampled waveforms) */
#define WAVETABLES
/* ADSR state */
#define ATTACK 0
#define DECAY 1
#define SUSTAIN 2
#define RELEASE 3
#define IDLE 4
#ifndef WAVETABLES
/* Current waveform */
#define TESTWAVE 0
#define PULSEWAVE 1
#define SAWTOOTHWAVE 2
#define TRIANGLEWAVE 3
#define NOISEWAVE 4
#define NOWAVE 5
#define RINGWAVE 6
#define PULSETRIANGLEWAVE 7
#define PULSESAWTOOTHWAVE 8
#endif
/* noise magic */
#define NSHIFT(v, n) (((v)<<(n))|((((v)>>(23-(n)))^(v>>(18-(n))))&((1<<(n))-1)))
#define NVALUE(v) (noiseLSB[v&0xff]|noiseMID[(v>>8)&0xff]|noiseMSB[(v>>16)&0xff])
#define NSEED 0x7ffff8
#ifdef WAVETABLES
#include "wave6581.h"
#include "wave8580.h"
static WORD wavetable00[2];
static WORD wavetable10[4096];
static WORD wavetable20[4096];
static WORD wavetable30[4096];
static WORD wavetable40[8192];
static WORD wavetable50[8192];
static WORD wavetable60[8192];
static WORD wavetable70[8192];
#endif
/* Noise tables */
#define NOISETABLESIZE 256
static BYTE noiseMSB[NOISETABLESIZE];
static BYTE noiseMID[NOISETABLESIZE];
static BYTE noiseLSB[NOISETABLESIZE];
/* needed data for one voice */
typedef struct voice_s
{
struct sound_s *s;
struct voice_s *vprev;
struct voice_s *vnext;
int nr;
/* counter value */
DWORD f;
/* counter step / sample */
DWORD fs;
#ifdef WAVETABLES
/* do we have noise enabled? */
BYTE noise;
#else
/* waveform that we use */
BYTE fm;
/* pulse threshold compared to the 32-bit counter */
DWORD pw;
#endif
/* 31-bit adsr counter */
DWORD adsr;
/* adsr counter step / sample */
SDWORD adsrs;
/* adsr sustain level compared to the 31-bit counter */
DWORD adsrz;
/* does this voice use hard sync? */
BYTE sync;
/* does this voice use filter? */
BYTE filter;
/* does this structure need updating before next sample? */
BYTE update;
/* did we do multiple gate flips after last calculated sample? */
BYTE gateflip;
/* ADSR mode */
BYTE adsrm;
/* 4-bit attack value */
BYTE attack;
/* 4-bit decay value */
BYTE decay;
/* 4-bit sustain value */
BYTE sustain;
/* 4-bit release value */
BYTE release;
/* pointer to registers of this voice */
BYTE *d;
/* noise shift register. Note! rv may be 0 to 15 shifts 'behind' the
real noise shift register value. Remaining shifts are done when
it is referenced */
DWORD rv;
#ifdef WAVETABLES
/* pointer to wavetable data */
WORD *wt;
/* 32-bit offset to add to the counter before referencing the wavetable.
This is used on combined waveforms, when other waveforms are combined
with pulse */
DWORD wtpf;
/* length of wavetable (actually number of shifts needed for 32-bit
counter) */
DWORD wtl;
/* kludge for ring modulation. Set wtr[1] = 0x7fff if ring modulation is
used */
WORD wtr[2];
#endif
signed char filtIO;
float filtLow, filtRef;
} voice_t;
/* needed data for SID */
struct sound_s
{
/* number of voices */
voice_t v[3];
/* SID registers */
BYTE d[32];
/* is voice 3 enabled? */
BYTE has3;
/* 4-bit volume value */
BYTE vol;
/* ADSR counter step values for each adsr values */
SDWORD adrs[16];
/* sustain values compared to 31-bit ADSR counter */
DWORD sz[16];
/* internal constant used for sample rate dependent calculations */
DWORD speed1;
/* does this structure need updating before next sample? */
BYTE update;
#ifdef WAVETABLES
/* do we have a new sid or an old one? */
BYTE newsid;
#endif
/* constants needed to implement write-only register reads */
BYTE laststore;
BYTE laststorebit;
CLOCK laststoreclk;
/* do we want to use filters? */
BYTE emulatefilter;
};
/* XXX: check these */
/* table for internal ADSR counter step calculations */
static WORD adrtable[16] =
{
1, 4, 8, 12, 19, 28, 34, 40, 50, 125, 250, 400, 500, 1500, 2500, 4000
};
/* XXX: check these */
/* table for pseudo-exponential ADSR calculations */
static DWORD exptable[6] =
{
0x30000000, 0x1c000000, 0x0e000000, 0x08000000, 0x04000000, 0x00000000
};
/* clockcycles for each dropping bit when write-only register read is done */
static DWORD sidreadclocks[9];
static float lowPassParam[0x800];
#define filterTable lowPassParam
static float bandPassParam[0x800];
static float filterResTable[16];
static float filterDy, filterResDy;
static BYTE filterType = 0;
static BYTE filterCurType = 0;
static WORD filterValue;
static const float filterRefFreq = 44100.0;
static signed char ampMod1x8[256];
inline static void dofilter(voice_t *pVoice)
{
float sample, sample2;
int tmp;
if ( pVoice->filter )
{
if ( filterType != 0 )
{
if ( filterType == 0x20 )
{
pVoice->filtLow += ( pVoice->filtRef * filterDy );
sample = pVoice->filtIO;
sample2 = sample - pVoice->filtLow;
sample2 -= pVoice->filtRef * filterResDy;
pVoice->filtRef += ( sample2 * filterDy );
pVoice->filtIO = (signed char)(pVoice->filtRef-pVoice->filtLow/4);
}
else if ( filterType == 0x40 )
{
pVoice->filtLow += ( pVoice->filtRef * filterDy * 0.1 );
sample = pVoice->filtIO;
sample2 = sample - pVoice->filtLow;
sample2 -= pVoice->filtRef * filterResDy;
pVoice->filtRef += ( sample2 * filterDy );
sample2 = pVoice->filtRef - pVoice->filtIO/8;
if (sample2 < -128)
sample2 = -128;
if (sample2 > 127)
sample2 = 127;
pVoice->filtIO = (signed char)sample2;
}
else
{
pVoice->filtLow += ( pVoice->filtRef * filterDy );
sample = pVoice->filtIO;
sample2 = sample - pVoice->filtLow;
tmp = (int)sample2;
sample2 -= pVoice->filtRef * filterResDy;
pVoice->filtRef += ( sample2 * filterDy );
if ( filterType == 0x10 )
pVoice->filtIO = (signed char)pVoice->filtLow;
else if ( filterType == 0x30 )
pVoice->filtIO = (signed char)pVoice->filtLow;
else if ( filterType == 0x50 )
pVoice->filtIO = (signed char)(sample - (tmp >> 1));
else if ( filterType == 0x60 )
pVoice->filtIO = (signed char)tmp;
else if ( filterType == 0x70 )
pVoice->filtIO = (signed char)(sample - (tmp >> 1));
}
}
else /* filterType == 0x00 */
{
pVoice->filtIO = 0;
}
}
}
/* 15-bit oscillator value */
#ifdef WAVETABLES
inline static DWORD doosc(voice_t *pv)
{
if (pv->noise)
return ((DWORD)NVALUE(NSHIFT(pv->rv, pv->f >> 28))) << 7;
return pv->wt[(pv->f + pv->wtpf) >> pv->wtl] ^ pv->wtr[pv->vprev->f >> 31];
}
#else
static DWORD doosc(voice_t *pv)
{
DWORD f = pv->f;
switch (pv->fm)
{
case PULSESAWTOOTHWAVE:
if (f <= pv->pw)
return 0x0000;
case SAWTOOTHWAVE:
return f >> 17;
case RINGWAVE:
f ^= pv->vprev->f & 0x80000000;
case TRIANGLEWAVE:
if (f < 0x80000000)
return f >> 16;
return 0xffff - (f >> 16);
case PULSETRIANGLEWAVE:
if (f <= pv->pw)
return 0x0000;
if (f < 0x80000000)
return f >> 16;
return 0xffff - (f >> 16);
case NOISEWAVE:
return ((DWORD)NVALUE(NSHIFT(pv->rv, pv->f >> 28))) << 7;
case PULSEWAVE:
if (f >= pv->pw)
return 0x7fff;
}
return 0x0000;
}
#endif
/* change ADSR state and all related variables */
static void set_adsr(voice_t *pv, BYTE fm)
{
int i;
switch (fm)
{
case ATTACK:
pv->adsrs = pv->s->adrs[pv->attack];
pv->adsrz = 0;
break;
case DECAY:
/* XXX: fix this */
if (pv->adsr <= pv->s->sz[pv->sustain])
{
set_adsr(pv, SUSTAIN);
return;
}
for (i = 0; pv->adsr < exptable[i]; i++);
pv->adsrs = -pv->s->adrs[pv->decay] >> i;
pv->adsrz = pv->s->sz[pv->sustain];
if (exptable[i] > pv->adsrz)
pv->adsrz = exptable[i];
break;
case SUSTAIN:
if (pv->adsr > pv->s->sz[pv->sustain])
{
set_adsr(pv, DECAY);
return;
}
pv->adsrs = 0;
pv->adsrz = 0;
break;
case RELEASE:
if (!pv->adsr)
{
set_adsr(pv, IDLE);
return;
}
for (i = 0; pv->adsr < exptable[i]; i++);
pv->adsrs = -pv->s->adrs[pv->release] >> i;
pv->adsrz = exptable[i];
break;
case IDLE:
pv->adsrs = 0;
pv->adsrz = 0;
break;
}
pv->adsrm = fm;
}
/* ADSR counter triggered state change */
static void trigger_adsr(voice_t *pv)
{
switch (pv->adsrm)
{
case ATTACK:
pv->adsr = 0x7fffffff;
set_adsr(pv, DECAY);
break;
case DECAY:
case RELEASE:
if (pv->adsr >= 0x80000000)
pv->adsr = 0;
set_adsr(pv, pv->adsrm);
break;
}
}
static void print_voice(char *buf, voice_t *pv)
{
char *m = "ADSRI";
#ifdef WAVETABLES
char *w = "0123456789abcdef";
#else
char *w = "TPSTN-R5";
#endif
sprintf(buf,
"#SID: V%d: e=%5.1f%%(%c) w=%6.1fHz(%c) f=%5.1f%% p=%5.1f%%\n",
pv->nr,
(double)pv->adsr*100.0 / (((DWORD)1 << 31) - 1), m[pv->adsrm],
(double)pv->fs / (pv->s->speed1*16),
#ifdef WAVETABLES
w[pv->d[4]>>4],
#else
w[pv->fm],
#endif
(double)pv->f*100.0 / ((DWORD)-1),
#ifdef WAVETABLES
(double)(pv->d[2] + (pv->d[3]&0x0f)*0x100)/40.95
#else
(double)pv->pw*100.0 / ((DWORD)-1)
#endif
);
}
char *sound_machine_dump_state(sound_t *psid)
{
int i;
char buf[1024];
#ifdef HAVE_RESID
if (useresid)
return resid_sound_machine_dump_state(psid);
#endif
sprintf(buf, "#SID: clk=%d v=%d s3=%d\n", clk, psid->vol, psid->has3);
for (i = 0; i < 3; i++)
print_voice(buf + strlen(buf), &psid->v[i]);
return stralloc(buf);
}
/* update SID structure */
inline static void setup_sid(sound_t *psid)
{
if (!psid->update)
return;
psid->vol = psid->d[0x18] & 0x0f;
psid->has3 = psid->d[0x18] & 0x80 ? 0 : 1;
if (psid->emulatefilter)
{
psid->v[0].filter = psid->d[0x17] & 0x01 ? 1 : 0;
psid->v[1].filter = psid->d[0x17] & 0x02 ? 1 : 0;
psid->v[2].filter = psid->d[0x17] & 0x04 ? 1 : 0;
filterType = psid->d[0x18]&0x70;
if (filterType != filterCurType)
{
filterCurType = filterType;
psid->v[0].filtLow = 0;
psid->v[0].filtRef = 0;
psid->v[1].filtLow = 0;
psid->v[1].filtRef = 0;
psid->v[2].filtLow = 0;
psid->v[2].filtRef = 0;
}
filterValue = 0x7ff&((psid->d[0x15]&7)|((WORD)psid->d[0x16])<<3);
if (filterType == 0x20)
filterDy = bandPassParam[filterValue];
else
filterDy = lowPassParam[filterValue];
filterResDy = filterResTable[psid->d[0x17]>>4]-filterDy;
if (filterResDy < 1.0)
filterResDy = 1.0;
if (psid->d[0x17] & 0x07)
warn(pwarn, 0, "program uses filters");
}
else
{
psid->v[0].filter = 0;
psid->v[1].filter = 0;
psid->v[2].filter = 0;
if (psid->d[0x17] & 0x07)
warn(pwarn, 0, "filters are disabled");
}
psid->update = 0;
}
/* update voice structure */
inline static void setup_voice(voice_t *pv)
{
if (!pv->update)
return;
pv->attack = pv->d[5] / 0x10;
pv->decay = pv->d[5] & 0x0f;
pv->sustain = pv->d[6] / 0x10;
pv->release = pv->d[6] & 0x0f;
#ifndef WAVETABLES
pv->pw = (pv->d[2] + (pv->d[3]&0x0f)*0x100) * 0x100100;
#endif
pv->sync = pv->d[4] & 0x02 ? 1 : 0;
if (pv->sync)
warn(pwarn, 1, "program uses hard sync");
pv->fs = pv->s->speed1 * (pv->d[0] + pv->d[1]*0x100);
#ifdef WAVETABLES
if (pv->d[4] & 0x08)
{
pv->f = pv->fs = 0;
pv->rv = NSEED;
}
pv->noise = 0;
pv->wtl = 20;
pv->wtpf = 0;
pv->wtr[1] = 0;
switch ((pv->d[4] & 0xf0) >> 4)
{
case 0:
pv->wt = wavetable00;
pv->wtl = 31;
break;
case 1:
pv->wt = wavetable10;
if (pv->d[4] & 0x04)
pv->wtr[1] = 0x7fff;
break;
case 2:
pv->wt = wavetable20;
break;
case 3:
pv->wt = wavetable30;
if (pv->d[4] & 0x04)
pv->wtr[1] = 0x7fff;
warn(pwarn, 3, "program combines waveforms");
break;
case 4:
if (pv->d[4] & 0x08)
pv->wt = &wavetable40[4096];
else
pv->wt = &wavetable40[4096 - (pv->d[2] + (pv->d[3]&0x0f)*0x100)];
break;
case 5:
warn(pwarn, 9, "program combines pulse and triangle waveforms");
pv->wt = &wavetable50[pv->wtpf = 4096 - (pv->d[2] + (pv->d[3]&0x0f)*0x100)];
pv->wtpf <<= 20;
if (pv->d[4] & 0x04)
pv->wtr[1] = 0x7fff;
break;
case 6:
warn(pwarn, 10, "program combines pulse and sawtooth waveforms");
pv->wt = &wavetable60[pv->wtpf = 4096 - (pv->d[2] + (pv->d[3]&0x0f)*0x100)];
pv->wtpf <<= 20;
break;
case 7:
pv->wt = &wavetable70[pv->wtpf = 4096 - (pv->d[2] + (pv->d[3]&0x0f)*0x100)];
pv->wtpf <<= 20;
if (pv->d[4] & 0x04 && pv->s->newsid)
pv->wtr[1] = 0x7fff;
warn(pwarn, 3, "program combines waveforms");
break;
case 8:
pv->noise = 1;
pv->wt = NULL;
pv->wtl = 0;
break;
default:
/* XXX: noise locking correct? */
pv->rv = 0;
pv->wt = wavetable00;
pv->wtl = 31;
}
if (pv->wtr[1])
warn(pwarn, 2, "program uses ring modulation");
#else
if (pv->d[4] & 0x08)
{
pv->fm = TESTWAVE;
pv->pw = pv->f = pv->fs = 0;
pv->rv = NSEED;
}
else switch ((pv->d[4] & 0xf0) >> 4)
{
case 4:
pv->fm = PULSEWAVE;
break;
case 2:
pv->fm = SAWTOOTHWAVE;
break;
case 1:
if (pv->d[4] & 0x04)
{
pv->fm = RINGWAVE;
warn(pwarn, 2, "program uses ring modulation");
}
else
pv->fm = TRIANGLEWAVE;
break;
case 8:
pv->fm = NOISEWAVE;
break;
case 0:
pv->fm = NOWAVE;
break;
case 5:
pv->fm = PULSETRIANGLEWAVE;
warn(pwarn, 9, "program combines pulse and triangle waveforms");
break;
case 6:
pv->fm = PULSESAWTOOTHWAVE;
warn(pwarn, 10,
"program combines pulse and sawtooth waveforms");
break;
default:
pv->fm = NOWAVE;
warn(pwarn, 3, "program combines waveforms");
}
#endif
switch (pv->adsrm)
{
case ATTACK:
case DECAY:
case SUSTAIN:
if (pv->d[4] & 0x01)
set_adsr(pv, pv->gateflip ? ATTACK : pv->adsrm);
else
set_adsr(pv, RELEASE);
break;
case RELEASE:
case IDLE:
if (pv->d[4] & 0x01)
set_adsr(pv, ATTACK);
else
set_adsr(pv, pv->adsrm);
break;
}
pv->update = 0;
pv->gateflip = 0;
}
int sound_machine_calculate_samples(sound_t *psid, SWORD *pbuf, int nr)
{
register DWORD o0, o1, o2;
register int dosync1, dosync2, i;
#ifdef HAVE_RESID
if (useresid)
return resid_sound_machine_calculate_samples(psid, pbuf, nr);
#endif
setup_sid(psid);
setup_voice(&psid->v[0]);
setup_voice(&psid->v[1]);
setup_voice(&psid->v[2]);
for (i = 0; i < nr; i++)
{
/* addfptrs, noise & hard sync test */
dosync1 = 0;
if ((psid->v[0].f += psid->v[0].fs) < psid->v[0].fs)
{
psid->v[0].rv = NSHIFT(psid->v[0].rv, 16);
if (psid->v[1].sync)
dosync1 = 1;
}
dosync2 = 0;
if ((psid->v[1].f += psid->v[1].fs) < psid->v[1].fs)
{
psid->v[1].rv = NSHIFT(psid->v[1].rv, 16);
if (psid->v[2].sync)
dosync2 = 1;
}
if ((psid->v[2].f += psid->v[2].fs) < psid->v[2].fs)
{
psid->v[2].rv = NSHIFT(psid->v[2].rv, 16);
if (psid->v[0].sync)
{
/* hard sync */
psid->v[0].rv = NSHIFT(psid->v[0].rv, psid->v[0].f >> 28);
psid->v[0].f = 0;
}
}
/* hard sync */
if (dosync2)
{
psid->v[2].rv = NSHIFT(psid->v[2].rv, psid->v[2].f >> 28);
psid->v[2].f = 0;
}
if (dosync1)
{
psid->v[1].rv = NSHIFT(psid->v[1].rv, psid->v[1].f >> 28);
psid->v[1].f = 0;
}
/* do adsr */
if ((psid->v[0].adsr += psid->v[0].adsrs) + 0x80000000 <
psid->v[0].adsrz + 0x80000000)
trigger_adsr(&psid->v[0]);
if ((psid->v[1].adsr += psid->v[1].adsrs) + 0x80000000 <
psid->v[1].adsrz + 0x80000000)
trigger_adsr(&psid->v[1]);
if ((psid->v[2].adsr += psid->v[2].adsrs) + 0x80000000 <
psid->v[2].adsrz + 0x80000000)
trigger_adsr(&psid->v[2]);
/* oscillators */
o0 = psid->v[0].adsr >> 16;
o1 = psid->v[1].adsr >> 16;
o2 = psid->v[2].adsr >> 16;
if (o0)
o0 *= doosc(&psid->v[0]);
if (o1)
o1 *= doosc(&psid->v[1]);
if (psid->has3 && o2)
o2 *= doosc(&psid->v[2]);
else
o2 = 0;
/* sample */
if (psid->emulatefilter)
{
psid->v[0].filtIO = ampMod1x8[(o0>>22)];
dofilter(&psid->v[0]);
o0 = ((DWORD)(psid->v[0].filtIO)+0x80)<<(7+15);
psid->v[1].filtIO = ampMod1x8[(o1>>22)];
dofilter(&psid->v[1]);
o1 = ((DWORD)(psid->v[1].filtIO)+0x80)<<(7+15);
psid->v[2].filtIO = ampMod1x8[(o2>>22)];
dofilter(&psid->v[2]);
o2 = ((DWORD)(psid->v[2].filtIO)+0x80)<<(7+15);
}
pbuf[i] = ((SDWORD)((o0+o1+o2)>>20)-0x600)*psid->vol;
}
return 0;
}
static void init_filter(sound_t *psid, int freq)
{
WORD uk;
float rk;
long int si;
float yMax = 1.0;
float yMin = 0.01;
float resDyMax = 1.0;
float resDyMin = 2.0;
float resDy = resDyMin;
float yAdd, yTmp;
float filterFs = 400.0;
float filterFm = 60.0;
float filterFt = 0.05;
float filterAmpl = 1.0;
filterValue = 0;
filterType = 0;
filterCurType = 0;
filterDy = 0;
filterResDy = 0;
for ( uk = 0, rk = 0; rk < 0x800; rk++, uk++ )
{
lowPassParam[uk] = (((exp(rk/2048*log(filterFs))/filterFm)+filterFt)
*filterRefFreq) / freq;
if ( lowPassParam[uk] < yMin )
lowPassParam[uk] = yMin;
if ( lowPassParam[uk] > yMax )
lowPassParam[uk] = yMax;
}
yMax = 0.22;
yMin = 0.002;
yAdd = (yMax-yMin)/2048.0;
yTmp = yMin;
for ( uk = 0, rk = 0; rk < 0x800; rk++, uk++ )
{
bandPassParam[uk] = (yTmp*filterRefFreq) / freq;
yTmp += yAdd;
}
for ( uk = 0; uk < 16; uk++ )
{
filterResTable[uk] = resDy;
resDy -= (( resDyMin - resDyMax ) / 15 );
}
filterResTable[0] = resDyMin;
filterResTable[15] = resDyMax;
/* XXX: if psid->emulatefilter = 0, ampMod1x8 is never referenced */
if (psid->emulatefilter)
filterAmpl = 0.7;
else
filterAmpl = 1.0;
for ( uk = 0, si = 0; si < 256; si++, uk++ )
ampMod1x8[uk] = (signed char)((si-0x80)*filterAmpl);
}
/* SID initialization routine */
sound_t *sound_machine_open(int speed, int cycles_per_sec)
{
DWORD i;
sound_t *psid;
#ifdef HAVE_RESID
useresid = sid_useresid;
if (useresid)
return resid_sound_machine_open(speed, cycles_per_sec,
sid_filters_enabled, siddata,
sid_model, clk);
#endif
psid = xmalloc(sizeof(*psid));
memset(psid, 0, sizeof(*psid));
memcpy(psid->d, siddata, 32);
psid->speed1 = (cycles_per_sec << 8) / speed;
for (i = 0; i < 16; i++)
{
psid->adrs[i] = 500*8*psid->speed1/adrtable[i];
psid->sz[i] = 0x8888888*i;
}
psid->update = 1;
psid->emulatefilter = sid_filters_enabled;
setup_sid(psid);
init_filter(psid, speed);
for (i = 0; i < 3; i++)
{
psid->v[i].vprev = &psid->v[(i+2)%3];
psid->v[i].vnext = &psid->v[(i+1)%3];
psid->v[i].nr = i;
psid->v[i].d = psid->d + i*7;
psid->v[i].s = psid;
psid->v[i].rv = NSEED;
psid->v[i].filtLow = 0;
psid->v[i].filtRef = 0;
psid->v[i].filtIO = 0;
psid->v[i].update = 1;
setup_voice(&psid->v[i]);
}
#ifdef WAVETABLES
psid->newsid = sid_model == 1;
for (i = 0; i < 4096; i++)
{
wavetable10[i] = i < 2048 ? i << 4 : 0xffff - (i << 4);
wavetable20[i] = i << 3;
wavetable30[i] = waveform30_8580[i] << 7;
wavetable40[i + 4096] = 0x7fff;
if (psid->newsid)
{
wavetable50[i + 4096] = waveform50_8580[i] << 7;
wavetable60[i + 4096] = waveform60_8580[i] << 7;
wavetable70[i + 4096] = waveform70_8580[i] << 7;
}
else
{
wavetable50[i + 4096] = waveform50_6581[i >> 3] << 7;
wavetable60[i + 4096] = 0;
wavetable70[i + 4096] = 0;
}
}
#endif
for (i = 0; i < NOISETABLESIZE; i++)
{
noiseLSB[i] = (((i>>(7-2))&0x04)|((i>>(4-1))&0x02)|((i>>(2-0))&0x01));
noiseMID[i] = (((i>>(13-8-4))&0x10)|((i<<(3-(11-8)))&0x08));
noiseMSB[i] = (((i<<(7-(22-16)))&0x80)|((i<<(6-(20-16)))&0x40)
|((i<<(5-(16-16)))&0x20));
}
for (i = 0; i < 9; i++)
sidreadclocks[i] = 13;
return psid;
}
void sound_machine_close(sound_t *psid)
{
#ifdef HAVE_RESID
if (useresid)
resid_sound_machine_close(psid);
else
#endif
free(psid);
}
/* read register value from sid */
static BYTE lastsidread;
BYTE REGPARM1 read_sid(ADDRESS addr)
{
int val;
vic_ii_handle_pending_alarms(0);
addr = addr & 0x1f;
#ifdef HAVE_MOUSE
if (addr == 0x19)
val = (mouse_get_x() >> 1) & 0x7e;
else if (addr == 0x1a)
val = (~mouse_get_y() >> 1) & 0x7e;
else
#endif
val = sound_read(addr);
if (val < 0)
{
if (addr == 0x19 || addr == 0x1a)
val = 0xff;
else
{
warn(pwarn, 5, "program reading sid-registers (no sound)");
if (addr == 0x1b || addr == 0x1c)
val = rand();
else
val = 0;
}
}
lastsidread = val;
return val;
}
BYTE sound_machine_read(sound_t *psid, ADDRESS addr)
{
BYTE ret;
WORD ffix;
register WORD rvstore;
register CLOCK tmp;
#ifdef HAVE_RESID
if (useresid)
return resid_sound_machine_read(psid, addr, clk);
#endif
switch (addr)
{
case 0x19:
/* pot/x */
ret = 0xff;
break;
case 0x1a:
/* pot/y */
ret = 0xff;
break;
case 0x1b:
/* osc3 / random */
ffix = sound_sample_position()*psid->v[2].fs;
rvstore = psid->v[2].rv;
if (
#ifdef WAVETABLES
psid->v[2].noise
#else
psid->v[2].fm == NOISEWAVE
#endif
&& psid->v[2].f + ffix < psid->v[2].f)
{
psid->v[2].rv = NSHIFT(psid->v[2].rv, 16);
}
psid->v[2].f += ffix;
ret = doosc(&psid->v[2]) >> 7;
psid->v[2].f -= ffix;
psid->v[2].rv = rvstore;
warn(pwarn, 6, "program reading osc3 register");
break;
case 0x1c:
ret = psid->v[2].adsr >> 23;
warn(pwarn, 7, "program reading env3 register");
break;
default:
while ((tmp = psid->laststorebit) &&
(tmp = psid->laststoreclk + sidreadclocks[tmp]) < clk)
{
psid->laststoreclk = tmp;
psid->laststore &= 0xfeff >> psid->laststorebit--;
}
ret = psid->laststore;
}
return ret;
}
/* write register value to sid */
void REGPARM2 store_sid(ADDRESS addr, BYTE byte)
{
addr &= 0x1f;
siddata[addr] = byte;
vic_ii_handle_pending_alarms(rmw_flag + 1);
if (rmw_flag)
{
clk--;
sound_store(addr, lastsidread);
clk++;
#if 0
/* XXX: remove me some day */
warn(pwarn, 4, "rmw instruction");
#endif
}
sound_store(addr, byte);
}
void sound_machine_store(sound_t *psid, ADDRESS addr, BYTE byte)
{
#ifdef HAVE_RESID
if (useresid)
{
resid_sound_machine_store(psid, addr, byte, clk);
return;
}
#endif
switch (addr)
{
case 4:
if ((psid->d[addr] ^ byte) & 1)
psid->v[0].gateflip = 1;
case 0: case 1: case 2: case 3: case 5: case 6:
psid->v[0].update = 1;
break;
case 11:
if ((psid->d[addr] ^ byte) & 1)
psid->v[1].gateflip = 1;
case 7: case 8: case 9: case 10: case 12: case 13:
psid->v[1].update = 1;
break;
case 18:
if ((psid->d[addr] ^ byte) & 1)
psid->v[2].gateflip = 1;
case 14: case 15: case 16: case 17: case 19: case 20:
psid->v[2].update = 1;
break;
default:
psid->update = 1;
}
psid->d[addr] = byte;
psid->laststore = byte;
psid->laststorebit = 8;
psid->laststoreclk = clk;
}
void sid_reset(void)
{
int i;
memset(siddata, 0, 32);
for (i = 0; i < 32; i++)
sound_store(i, 0);
warn_reset(pwarn);
sound_prevent_clk_overflow(clk);
}
void sound_machine_init(void)
{
pwarn = warn_init("SID", 128);
#ifdef HAVE_RESID
resid_sound_machine_init();
#endif
}
void sound_machine_prevent_clk_overflow(sound_t *psid, CLOCK sub)
{
#ifdef HAVE_RESID
if (useresid)
resid_sound_machine_prevent_clk_overflow(psid, sub);
else
#endif
psid->laststoreclk -= sub;
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.