From 61b10c57e244c64ce0b946bd16a6d5e3383a7fc3 Mon Sep 17 00:00:00 2001 From: EkeEke Date: Sun, 18 Dec 2016 23:36:59 +0100 Subject: [PATCH] [Core/Sound] rewrote PSG core from scratch & replaced deprecated "PSG boot noise" option with optional Hiqh Quality PSG resampling --- core/sound/sn76489.c | 418 ------------------------------------------- 1 file changed, 418 deletions(-) delete mode 100644 core/sound/sn76489.c diff --git a/core/sound/sn76489.c b/core/sound/sn76489.c deleted file mode 100644 index da0a1b6..0000000 --- a/core/sound/sn76489.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - SN76489 emulation - by Maxim in 2001 and 2002 - converted from my original Delphi implementation - - I'm a C newbie so I'm sure there are loads of stupid things - in here which I'll come back to some day and redo - - Includes: - - Super-high quality tone channel "oversampling" by calculating fractional positions on transitions - - Noise output pattern reverse engineered from actual SMS output - - Volume levels taken from actual SMS output - - 07/08/04 Charles MacDonald - Modified for use with SMS Plus: - - Added support for multiple PSG chips. - - Added reset/config/update routines. - - Added context management routines. - - Removed SN76489_GetValues(). - - Removed some unused variables. - - 25/04/07 Eke-Eke (Genesis Plus GX) - - Removed stereo GG support (unused) - - Made SN76489_Update outputs 16bits mono samples - - Replaced volume table with VGM plugin's one - - 05/01/09 Eke-Eke (Genesis Plus GX) - - Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746) - - 24/08/10 Eke-Eke (Genesis Plus GX) - - Removed multichip support (unused) - - Removed alternate volume table, panning & mute support (unused) - - Removed configurable Feedback and Shift Register Width (always use Sega ones) - - Added linear resampling using Blip Buffer (based on Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376) - - 01/09/12 Eke-Eke (Genesis Plus GX) - - Added generic Blip-Buffer support internally, using common Master Clock as timebase - - Re-added stereo GG support - - Re-added configurable Feedback and Shift Register Width - - Rewrote core with various optimizations - - 04/11/16 Eke-Eke (Genesis Plus GX) - - improved resampling quality (removes aliasing noise when using high frequency tones) - - removed cut-off value (improves emulation accuracy of highest frequency tones) - - modified channels output to 0/1 like real chip instead of -1/+1 (fixes PCM voices when cut-off value is removed) - -*/ - -#include "shared.h" - -#define PSG_MCYCLES_RATIO (16 * 15) - -/* Initial state of shift register */ -#define NoiseInitialState 0x8000 - -/* original Texas Instruments TMS SN76489AN (rev. A) used in SG-1000, SC-3000H & SF-7000 computers */ -#define FB_DISCRETE 0x0006 -#define SRW_DISCRETE 15 - -/* SN76489AN clone integrated in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */ -#define FB_SEGAVDP 0x0009 -#define SRW_SEGAVDP 16 - -typedef struct -{ - /* Configuration */ - int PreAmp[4][2]; /* stereo channels pre-amplification ratio (%) */ - int NoiseFeedback; - int SRWidth; - - /* PSG registers: */ - int Registers[8]; /* Tone, vol x4 */ - int LatchedRegister; - int NoiseShiftRegister; - int NoiseFreq; /* Noise channel signal generator frequency */ - - /* Output calculation variables */ - int ToneFreqVals[4]; /* Frequency register values (counters) */ - int ToneFreqPos[4]; /* Frequency channel flip-flops */ - int Channel[4][2]; /* current amplitude of each (stereo) channel */ - int ChanOut[4][2]; /* current output value of each (stereo) channel */ - - /* Internal M-clock counter */ - unsigned long clocks; - -} SN76489_Context; - -static const uint16 PSGVolumeValues[16] = -{ - /* These values are taken from a real SMS2's output */ - /*{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, */ - /* I can't remember why 892... :P some scaling I did at some point */ - /* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1) */ - 1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0 -}; - -static SN76489_Context SN76489; - -void SN76489_Init(int type) -{ - int i; - - for (i=0; i<4; i++) - { - SN76489.PreAmp[i][0] = 100; - SN76489.PreAmp[i][1] = 100; - } - - if (type == SN_DISCRETE) - { - SN76489.NoiseFeedback = FB_DISCRETE; - SN76489.SRWidth = SRW_DISCRETE; - } - else - { - SN76489.NoiseFeedback = FB_SEGAVDP; - SN76489.SRWidth = SRW_SEGAVDP; - } -} - -void SN76489_Reset() -{ - int i; - - for(i = 0; i <= 3; i++) - { - /* Initialise PSG state */ - SN76489.Registers[2*i] = 1; /* tone freq=1 */ - SN76489.Registers[2*i+1] = 0xf; /* vol=off */ - - /* Set counters to 0 */ - SN76489.ToneFreqVals[i] = 0; - - /* Set flip-flops to 1 */ - SN76489.ToneFreqPos[i] = 1; - - /* Clear stereo channels amplitude */ - SN76489.Channel[i][0] = 0; - SN76489.Channel[i][1] = 0; - - /* Clear stereo channel outputs in delta buffer */ - SN76489.ChanOut[i][0] = 0; - SN76489.ChanOut[i][1] = 0; - } - - /* Initialise latched register index */ - SN76489.LatchedRegister = 0; - - /* Initialise noise generator */ - SN76489.NoiseShiftRegister=NoiseInitialState; - SN76489.NoiseFreq = 0x10; - - /* Reset internal M-cycle counter */ - SN76489.clocks = 0; -} - -void *SN76489_GetContextPtr(void) -{ - return (uint8 *)&SN76489; -} - -int SN76489_GetContextSize(void) -{ - return sizeof(SN76489_Context); -} - -/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */ -INLINE void UpdateToneAmplitude(int i, int time) -{ - /* left & right output */ - int delta_l = (SN76489.Channel[i][0] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][0]; - int delta_r = (SN76489.Channel[i][1] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][1]; - blip_add_delta(snd.blips[0], time, delta_l, delta_r); - SN76489.ChanOut[i][0] += delta_l; - SN76489.ChanOut[i][1] += delta_r; -} - -/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */ -INLINE void UpdateNoiseAmplitude(int time) -{ - /* left & right output */ - int delta_l = (SN76489.Channel[3][0] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][0]; - int delta_r = (SN76489.Channel[3][1] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][1]; - blip_add_delta(snd.blips[0], time, delta_l, delta_r); - SN76489.ChanOut[3][0] += delta_l; - SN76489.ChanOut[3][1] += delta_r; -} - -/* Runs tone channel for clock_length clocks */ -static void RunTone(int i, int clocks) -{ - int time; - - /* Update in case a register changed etc. */ - UpdateToneAmplitude(i, SN76489.clocks); - - /* Time of next transition */ - time = SN76489.ToneFreqVals[i]; - - /* Process any transitions that occur within clocks we're running */ - while (time < clocks) - { - /* Flip the flip-flop */ - SN76489.ToneFreqPos[i] ^= 1; - UpdateToneAmplitude(i, time); - - /* Advance to time of next transition */ - time += SN76489.Registers[i*2] * PSG_MCYCLES_RATIO; - } - - /* Update channel tone counter */ - SN76489.ToneFreqVals[i] = time; -} - -/* Runs noise channel for clock_length clocks */ -static void RunNoise(int clocks) -{ - int time; - - /* Noise channel: match to tone2 if in slave mode */ - int NoiseFreq = SN76489.NoiseFreq; - if (NoiseFreq == 0x80) - { - NoiseFreq = SN76489.Registers[2*2]; - SN76489.ToneFreqVals[3] = SN76489.ToneFreqVals[2]; - } - - /* Update in case a register changed etc. */ - UpdateNoiseAmplitude(SN76489.clocks); - - /* Time of next transition */ - time = SN76489.ToneFreqVals[3]; - - /* Process any transitions that occur within clocks we're running */ - while (time < clocks) - { - /* Flip the flip-flop */ - SN76489.ToneFreqPos[3] ^= 1; - if (SN76489.ToneFreqPos[3]) - { - /* On the positive edge of the square wave (only once per cycle) */ - int Feedback = SN76489.NoiseShiftRegister; - if ( SN76489.Registers[6] & 0x4 ) - { - /* White noise */ - /* Calculate parity of fed-back bits for feedback */ - /* Do some optimised calculations for common (known) feedback values */ - /* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */ - /* since that's (one or more bits set) && (not all bits set) */ - Feedback = ((Feedback & SN76489.NoiseFeedback) && ((Feedback & SN76489.NoiseFeedback) ^ SN76489.NoiseFeedback)); - } - else /* Periodic noise */ - Feedback = Feedback & 1; - - SN76489.NoiseShiftRegister = (SN76489.NoiseShiftRegister >> 1) | (Feedback << (SN76489.SRWidth - 1)); - UpdateNoiseAmplitude(time); - } - - /* Advance to time of next transition */ - time += NoiseFreq * PSG_MCYCLES_RATIO; - } - - /* Update channel tone counter */ - SN76489.ToneFreqVals[3] = time; -} - -static void SN76489_RunUntil(unsigned int clocks) -{ - int i; - - /* Run noise first, since it might use current value of third tone frequency counter */ - RunNoise(clocks); - - /* Run tone channels */ - for (i=0; i<3; ++i) - { - RunTone(i, clocks); - } -} - -void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo) -{ - int i; - - /* cycle-accurate Game Gear stereo */ - if (clocks > SN76489.clocks) - { - /* Run chip until current timestamp */ - SN76489_RunUntil(clocks); - - /* Update internal M-cycle counter */ - SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO; - } - - for (i=0; i<4; i++) - { - /* stereo channel pre-amplification */ - SN76489.PreAmp[i][0] = preAmp * ((stereo >> (i + 4)) & 1); - SN76489.PreAmp[i][1] = preAmp * ((stereo >> (i + 0)) & 1); - - /* noise channel boost (applied to all channels) */ - SN76489.PreAmp[i][0] = SN76489.PreAmp[i][0] << boostNoise; - SN76489.PreAmp[i][1] = SN76489.PreAmp[i][1] << boostNoise; - - /* update stereo channel amplitude */ - SN76489.Channel[i][0]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][0]) / 100; - SN76489.Channel[i][1]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][1]) / 100; - } -} - -void SN76489_Update(unsigned int clocks) -{ - int i; - - if (clocks > SN76489.clocks) - { - /* Run chip until current timestamp */ - SN76489_RunUntil(clocks); - - /* Update internal M-cycle counter */ - SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO; - } - - /* Adjust internal M-cycle counter for next frame */ - SN76489.clocks -= clocks; - - /* Adjust channel time counters for new frame */ - for (i=0; i<4; ++i) - { - SN76489.ToneFreqVals[i] -= clocks; - } -} - -void SN76489_Write(unsigned int clocks, unsigned int data) -{ - unsigned int index; - - if (clocks > SN76489.clocks) - { - /* run chip until current timestamp */ - SN76489_RunUntil(clocks); - - /* update internal M-cycle counter */ - SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO; - } - - if (data & 0x80) - { - /* latch byte %1 cc t dddd */ - SN76489.LatchedRegister = index = (data >> 4) & 0x07; - } - else - { - /* restore latched register index */ - index = SN76489.LatchedRegister; - } - - switch (index) - { - case 0: - case 2: - case 4: /* Tone Channels frequency */ - { - if (data & 0x80) - { - /* Data byte %1 cc t dddd */ - SN76489.Registers[index] = (SN76489.Registers[index] & 0x3f0) | (data & 0xf); - } - else - { - /* Data byte %0 - dddddd */ - SN76489.Registers[index] = (SN76489.Registers[index] & 0x00f) | ((data & 0x3f) << 4); - } - - /* zero frequency behaves the same as a value of 1 */ - if (SN76489.Registers[index] == 0) - { - SN76489.Registers[index] = 1; - } - break; - } - - case 1: - case 3: - case 5: /* Tone Channels attenuation */ - { - data &= 0x0f; - SN76489.Registers[index] = data; - data = PSGVolumeValues[data]; - index >>= 1; - SN76489.Channel[index][0] = (data * SN76489.PreAmp[index][0]) / 100; - SN76489.Channel[index][1] = (data * SN76489.PreAmp[index][1]) / 100; - break; - } - - case 6: /* Noise control */ - { - SN76489.Registers[6] = data & 0x0f; - - /* reset shift register */ - SN76489.NoiseShiftRegister = NoiseInitialState; - - /* set noise signal generator frequency */ - SN76489.NoiseFreq = 0x10 << (data&0x3); - break; - } - - case 7: /* Noise attenuation */ - { - data &= 0x0f; - SN76489.Registers[7] = data; - data = PSGVolumeValues[data]; - SN76489.Channel[3][0] = (data * SN76489.PreAmp[3][0]) / 100; - SN76489.Channel[3][1] = (data * SN76489.PreAmp[3][1]) / 100; - break; - } - } -}