ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/c64/sid.c

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.