/* * sid.c - MOS6581 (SID) emulation. * * Written by * Teemu Rantanen * Michael Schwendt * Ettore Perazzoli * * 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 "vice.h" #include #include #include #include #include "cmdline.h" #include "log.h" #include "machine.h" #include "maincpu.h" #include "resources.h" #include "sid.h" #include "sound.h" #include "snapshot.h" #include "types.h" #include "utils.h" #ifdef HAVE_RESID #include "resid.h" static int useresid; #endif #ifdef HAVE_MOUSE #include "mouse.h" #endif #include "fixpoint.h" #include #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* parallel port address */ #define port_base 0x378 #define port_control port_base + 2 /* control register bits */ #define STROBE 0x01 #define AUTOFEED 0x02 #define nINIT 0x04 #define SELECTIN 0x08 #define PCD 0x20 /* chip control pin assignments */ #define chip_select ctrport |= STROBE; outb(port_control, ctrport) #define chip_deselect ctrport &= ~STROBE; outb(port_control, ctrport) #define reset_start ctrport |= SELECTIN; outb(port_control, ctrport) #define reset_end ctrport &= ~SELECTIN; outb(port_control, ctrport) #define latch_open ctrport &= ~AUTOFEED; outb(port_control, ctrport) #define latch_lock ctrport |= AUTOFEED; outb(port_control, ctrport) #define RW_write ctrport &= ~nINIT; outb(port_control, ctrport) #define RW_read ctrport |= nINIT; outb(port_control, ctrport) /* parallel port direction control */ #define port_write ctrport &= ~PCD; outb(port_control, ctrport) #define port_read ctrport |= PCD; outb(port_control, ctrport) /* data byte out/in */ #define port_out(byte) outb(port_base, byte) #define port_in inb(port_base) /* data direction shortcuts */ #define write_mode RW_write; port_write /* write to SID */ #define read_mode port_read; RW_read /* read from SID */ static unsigned char ctrport; static int fd; /* ------------------------------------------------------------------------- */ /* 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 void init_port(void) { fd = open("/dev/io", O_RDWR); if (fd < 0) { printf("Error opening /dev/io.\n"); exit(1); } ctrport = inb(port_control); } static void sidwait(void) { } static void close_port(void) { close(fd); } static void reset_sid(void) /* Reset SID */ { write_mode; chip_select; latch_open; port_out(0x00); reset_start; sleep(1); reset_end; latch_lock; chip_deselect; } static void addr_sid(int addr) /* Set SID register address */ { port_out(addr); latch_open; sidwait(); latch_lock; } static void write_sid(int addr, unsigned char outval) /* Write to SID register */ { addr_sid(addr); port_out(outval); chip_select; sidwait(); chip_deselect; } static int set_sid_filters_enabled(resource_value_t v) { sid_filters_enabled = (int)v; sound_state_changed = TRUE; return 0; } static int set_sid_model(resource_value_t v) { sid_model = (int)v; sound_state_changed = TRUE; return 0; } static int set_sid_useresid(resource_value_t v) { sid_useresid = (int)v; sound_state_changed = TRUE; 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, "", "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); } /* ------------------------------------------------------------------------- */ /* 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; vreal_t 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 vreal_t lowPassParam[0x800]; #define filterTable lowPassParam static vreal_t bandPassParam[0x800]; static vreal_t filterResTable[16]; static vreal_t 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) { if (!pVoice->filter) return; if (filterType) { if ( filterType == 0x20 ) { pVoice->filtLow += REAL_MULT( pVoice->filtRef, filterDy ); pVoice->filtRef += REAL_MULT(REAL_VALUE(pVoice->filtIO) - pVoice->filtLow - REAL_MULT(pVoice->filtRef, filterResDy), filterDy ); pVoice->filtIO = (signed char)(REAL_TO_INT(pVoice->filtRef-pVoice->filtLow/4)); } else if ( filterType == 0x40 ) { vreal_t sample; pVoice->filtLow += REAL_MULT(REAL_MULT(pVoice->filtRef, filterDy), REAL_VALUE(0.1)); pVoice->filtRef += REAL_MULT(REAL_VALUE(pVoice->filtIO) - pVoice->filtLow - REAL_MULT(pVoice->filtRef, filterResDy), filterDy ); sample = pVoice->filtRef - REAL_VALUE(pVoice->filtIO/8); if (sample < REAL_VALUE(-128)) sample = REAL_VALUE(-128); if (sample > REAL_VALUE(127)) sample = REAL_VALUE(127); pVoice->filtIO = (signed char)(REAL_TO_INT(sample)); } else { int tmp; vreal_t sample, sample2; pVoice->filtLow += REAL_MULT( pVoice->filtRef, filterDy ); sample = REAL_VALUE(pVoice->filtIO); sample2 = sample - pVoice->filtLow; tmp = (int)(REAL_TO_INT(sample2)); sample2 -= REAL_MULT(pVoice->filtRef, filterResDy); pVoice->filtRef += REAL_MULT( sample2, filterDy ); pVoice->filtIO = filterType==0x10 ? (signed char)(REAL_TO_INT(pVoice->filtLow)) : (filterType==0x30 ? (signed char)(REAL_TO_INT(pVoice->filtLow)) : (filterType==0x50 ? (signed char)(REAL_TO_INT(sample) - (tmp >> 1)): (filterType==0x60 ? (signed char)tmp: (filterType==0x70 ? (signed char)(REAL_TO_INT(sample) - (tmp >> 1)):0)))); } } 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) { const char *m = "ADSRI"; #ifdef WAVETABLES const char *w = "0123456789abcdef"; #else const 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=%ld v=%d s3=%d\n", (long)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)&&!(psid->d[0x17]&0x04)) ? 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 < REAL_VALUE(1.0)) filterResDy = REAL_VALUE(1.0); } else { psid->v[0].filter = 0; psid->v[1].filter = 0; psid->v[2].filter = 0; } 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; 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; 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: 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: 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; 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; } #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; } else pv->fm = TRIANGLEWAVE; break; case 8: pv->fm = NOISEWAVE; break; case 0: pv->fm = NOWAVE; break; case 5: pv->fm = PULSETRIANGLEWAVE; break; case 6: pv->fm = PULSESAWTOOTHWAVE; break; default: pv->fm = NOWAVE; } #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; voice_t *v0, *v1, *v2; #ifdef HAVE_RESID if (useresid) return resid_sound_machine_calculate_samples(psid, pbuf, nr); #endif setup_sid(psid); v0 = &psid->v[0]; setup_voice(v0); v1 = &psid->v[1]; setup_voice(v1); v2 = &psid->v[2]; setup_voice(v2); for (i = 0; i < nr; i++) { /* addfptrs, noise & hard sync test */ dosync1 = 0; if ((v0->f += v0->fs) < v0->fs) { v0->rv = NSHIFT(v0->rv, 16); if (v1->sync) dosync1 = 1; } dosync2 = 0; if ((v1->f += v1->fs) < v1->fs) { v1->rv = NSHIFT(v1->rv, 16); if (v2->sync) dosync2 = 1; } if ((v2->f += v2->fs) < v2->fs) { v2->rv = NSHIFT(v2->rv, 16); if (v0->sync) { /* hard sync */ v0->rv = NSHIFT(v0->rv, v0->f >> 28); v0->f = 0; } } /* hard sync */ if (dosync2) { v2->rv = NSHIFT(v2->rv, v2->f >> 28); v2->f = 0; } if (dosync1) { v1->rv = NSHIFT(v1->rv, v1->f >> 28); v1->f = 0; } /* do adsr */ if ((v0->adsr += v0->adsrs) + 0x80000000 < v0->adsrz + 0x80000000) trigger_adsr(v0); if ((v1->adsr += v1->adsrs) + 0x80000000 < v1->adsrz + 0x80000000) trigger_adsr(v1); if ((v2->adsr += v2->adsrs) + 0x80000000 < v2->adsrz + 0x80000000) trigger_adsr(v2); /* oscillators */ o0 = v0->adsr >> 16; o1 = v1->adsr >> 16; o2 = v2->adsr >> 16; if (o0) o0 *= doosc(v0); if (o1) o1 *= doosc(v1); if (psid->has3 && o2) o2 *= doosc(v2); else o2 = 0; /* sample */ if (psid->emulatefilter) { v0->filtIO = ampMod1x8[(o0>>22)]; dofilter(v0); o0 = ((DWORD)(v0->filtIO)+0x80)<<(7+15); v1->filtIO = ampMod1x8[(o1>>22)]; dofilter(v1); o1 = ((DWORD)(v1->filtIO)+0x80)<<(7+15); v2->filtIO = ampMod1x8[(o2>>22)]; dofilter(v2); o2 = ((DWORD)(v2->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; vreal_t 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++ ) { float h; h = (((exp(rk/2048*log(filterFs))/filterFm)+filterFt) * filterRefFreq) / freq; if ( h < yMin ) h = yMin; if ( h > yMax ) h = yMax; lowPassParam[uk] = REAL_VALUE(h); } yMax = 0.22; yMin = 0.002; yAdd = (yMax-yMin)/2048.0; yTmp = yMin; for ( uk = 0, rk = 0; rk < 0x800; rk++, uk++ ) { bandPassParam[uk] = REAL_VALUE((yTmp*filterRefFreq) / freq); yTmp += yAdd; } for ( uk = 0; uk < 16; uk++ ) { filterResTable[uk] = REAL_VALUE(resDy); resDy -= (( resDyMin - resDyMax ) / 15 ); } filterResTable[0] = REAL_VALUE(resDyMin); filterResTable[15] = REAL_VALUE(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; init_filter(psid, speed); setup_sid(psid); 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) { reset_sid(); close_port(); #ifdef HAVE_RESID if (useresid) resid_sound_machine_close(psid); else #endif free(psid); } /* read register value from sid */ static BYTE lastsidread; BYTE REGPARM1 sid_read(ADDRESS addr) { int val; machine_handle_pending_alarms(0); addr = addr & 0x1f; #ifdef HAVE_MOUSE if (addr == 0x19 && _mouse_enabled) val = mouse_get_x(); else if (addr == 0x1a && _mouse_enabled) val = mouse_get_y(); else #endif val = sound_read(addr); if (val < 0) { if (addr == 0x19 || addr == 0x1a) val = 0xff; else { 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 DWORD 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 = (WORD)BIG_FLOAT_TO_INT(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; break; case 0x1c: ret = psid->v[2].adsr >> 23; 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; } // Quick HardSID-Hack for linux by // Rainer Sinsch (sinsch@stud.uni-frankfurt.de) #include #include void sidPoke(int reg, unsigned char val) // pokes a value to a sid-register { write_sid(reg, val); return; } /* write register value to sid */ void REGPARM2 sid_store(ADDRESS addr, BYTE byte) { addr &= 0x1f; sidPoke(addr, byte); // put it to the sid return; // no data to the sid-emu => no wave-out disturbs hardsid siddata[addr] = byte; /*fprintf(logfile, "%x %x\n", addr, byte);*/ /* FIXME: The old warning system is removed, and the current warning system is not capable of tracking which messages have already been printed. The warning is disabled for now to avoid flooding the console. if (psid_mode && addr > 0x1c) { log_message(LOG_DEFAULT, "program uses PSID registers (not supported)"); } */ machine_handle_pending_alarms(rmw_flag + 1); if (rmw_flag) { clk--; sound_store(addr, lastsidread); clk++; #if 0 /* XXX: remove me some day */ #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); sound_reset(); reset_sid(); } void sound_machine_reset(sound_t *psid, CLOCK cpu_clk) { #ifdef HAVE_RESID if (useresid) resid_sound_machine_reset(psid, cpu_clk); else #endif psid->laststoreclk = cpu_clk; } void sound_machine_init(void) { init_port(); reset_sid(); #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; } /* ------------------------------------------------------------------------- */ static char snap_module_name[] = "SID"; #define SNAP_MAJOR 1 #define SNAP_MINOR 0 int sid_write_snapshot_module(snapshot_t *s) { snapshot_module_t *m; m = snapshot_module_create(s, snap_module_name, SNAP_MAJOR, SNAP_MINOR); if (m == NULL) return -1; if (snapshot_module_write_byte_array(m, siddata, 32) < 0) { snapshot_module_close(m); return -1; } snapshot_module_close(m); return 0; } int sid_read_snapshot_module(snapshot_t *s) { BYTE major_version, minor_version; snapshot_module_t *m; sound_close(); m = snapshot_module_open(s, snap_module_name, &major_version, &minor_version); if (m == NULL) return -1; if (major_version > SNAP_MAJOR || minor_version > SNAP_MINOR) { log_error(LOG_DEFAULT, "SID: Snapshot module version (%d.%d) newer than %d.%d.\n", major_version, minor_version, SNAP_MAJOR, SNAP_MINOR); return snapshot_module_close(m); } if (snapshot_module_read_byte_array(m, siddata, 32) < 0) return -1; return snapshot_module_close(m); }