/*1SN76489 emulation2by Maxim in 2001 and 20023converted from my original Delphi implementation45I'm a C newbie so I'm sure there are loads of stupid things6in here which I'll come back to some day and redo78Includes:9- Super-high quality tone channel "oversampling" by calculating fractional positions on transitions10- Noise output pattern reverse engineered from actual SMS output11- Volume levels taken from actual SMS output121307/08/04 Charles MacDonald14Modified for use with SMS Plus:15- Added support for multiple PSG chips.16- Added reset/config/update routines.17- Added context management routines.18- Removed SN76489_GetValues().19- Removed some unused variables.202125/04/07 Eke-Eke (Genesis Plus GX)22- Removed stereo GG support (unused)23- Made SN76489_Update outputs 16bits mono samples24- Replaced volume table with VGM plugin's one252605/01/09 Eke-Eke (Genesis Plus GX)27- Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)282924/08/10 Eke-Eke (Genesis Plus GX)30- Removed multichip support (unused)31- Removed alternate volume table, panning & mute support (unused)32- Removed configurable Feedback and Shift Register Width (always use Sega ones)33- Added linear resampling using Blip Buffer (based on Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)343501/09/12 Eke-Eke (Genesis Plus GX)36- Added generic Blip-Buffer support internally, using common Master Clock as timebase37- Re-added stereo GG support38- Re-added configurable Feedback and Shift Register Width39- Rewrote core with various optimizations40*/4142#include "shared.h"4344#define PSG_MCYCLES_RATIO (16 * 15)4546/* Initial state of shift register */47#define NoiseInitialState 0x80004849/* Value below which PSG does not output */50/*#define PSG_CUTOFF 0x6*/51#define PSG_CUTOFF 0x15253/* original Texas Instruments TMS SN76489AN (rev. A) used in SG-1000, SC-3000H & SF-7000 computers */54#define FB_DISCRETE 0x000655#define SRW_DISCRETE 155657/* SN76489AN clone integrated in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */58#define FB_SEGAVDP 0x000959#define SRW_SEGAVDP 166061typedef struct62{63/* Configuration */64int PreAmp[4][2]; /* stereo channels pre-amplification ratio (%) */65int NoiseFeedback;66int SRWidth;6768/* PSG registers: */69int Registers[8]; /* Tone, vol x4 */70int LatchedRegister;71int NoiseShiftRegister;72int NoiseFreq; /* Noise channel signal generator frequency */7374/* Output calculation variables */75int ToneFreqVals[4]; /* Frequency register values (counters) */76int ToneFreqPos[4]; /* Frequency channel flip-flops */77int Channel[4][2]; /* current amplitude of each (stereo) channel */78int ChanOut[4][2]; /* current output value of each (stereo) channel */7980/* Internal M-clock counter */81unsigned long clocks;8283} SN76489_Context;8485static const uint16 PSGVolumeValues[16] =86{87/* These values are taken from a real SMS2's output */88/*{892,892,892,760,623,497,404,323,257,198,159,123,96,75,60,0}, */89/* I can't remember why 892... :P some scaling I did at some point */90/* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1) */911516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,092};9394SN76489_Context SN76489;9596static blip_t* blip[2];9798void SN76489_Init(blip_t* left, blip_t* right, int type)99{100int i;101102blip[0] = left;103blip[1] = right;104105for (i=0; i<4; i++)106{107SN76489.PreAmp[i][0] = 100;108SN76489.PreAmp[i][1] = 100;109}110111if (type == SN_DISCRETE)112{113SN76489.NoiseFeedback = FB_DISCRETE;114SN76489.SRWidth = SRW_DISCRETE;115}116else117{118SN76489.NoiseFeedback = FB_SEGAVDP;119SN76489.SRWidth = SRW_SEGAVDP;120}121}122123void SN76489_Reset()124{125int i;126127for(i = 0; i <= 3; i++)128{129/* Initialise PSG state */130SN76489.Registers[2*i] = 1; /* tone freq=1 */131SN76489.Registers[2*i+1] = 0xf; /* vol=off */132133/* Set counters to 0 */134SN76489.ToneFreqVals[i] = 0;135136/* Set flip-flops to 1 */137SN76489.ToneFreqPos[i] = 1;138139/* Clear stereo channels amplitude */140SN76489.Channel[i][0] = 0;141SN76489.Channel[i][1] = 0;142143/* Clear stereo channel outputs in delta buffer */144SN76489.ChanOut[i][0] = 0;145SN76489.ChanOut[i][1] = 0;146}147148/* Initialise latched register index */149SN76489.LatchedRegister = 0;150151/* Initialise noise generator */152SN76489.NoiseShiftRegister=NoiseInitialState;153SN76489.NoiseFreq = 0x10;154155/* Reset internal M-cycle counter */156SN76489.clocks = 0;157}158159void *SN76489_GetContextPtr(void)160{161return (uint8 *)&SN76489;162}163164int SN76489_GetContextSize(void)165{166return sizeof(SN76489_Context);167}168169/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */170INLINE void UpdateToneAmplitude(int i, int time)171{172int delta;173174/* left output */175delta = (SN76489.Channel[i][0] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][0];176if (delta != 0)177{178SN76489.ChanOut[i][0] += delta;179blip_add_delta_fast(blip[0], time, delta);180}181182/* right output */183delta = (SN76489.Channel[i][1] * SN76489.ToneFreqPos[i]) - SN76489.ChanOut[i][1];184if (delta != 0)185{186SN76489.ChanOut[i][1] += delta;187blip_add_delta_fast(blip[1], time, delta);188}189}190191/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */192INLINE void UpdateNoiseAmplitude(int time)193{194int delta;195196/* left output */197delta = (SN76489.Channel[3][0] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][0];198if (delta != 0)199{200SN76489.ChanOut[3][0] += delta;201blip_add_delta_fast(blip[0], time, delta);202}203204/* right output */205delta = (SN76489.Channel[3][1] * ( SN76489.NoiseShiftRegister & 0x1 )) - SN76489.ChanOut[3][1];206if (delta != 0)207{208SN76489.ChanOut[3][1] += delta;209blip_add_delta_fast(blip[1], time, delta);210}211}212213/* Runs tone channel for clock_length clocks */214static void RunTone(int i, int clocks)215{216int time;217218/* Update in case a register changed etc. */219UpdateToneAmplitude(i, SN76489.clocks);220221/* Time of next transition */222time = SN76489.ToneFreqVals[i];223224/* Process any transitions that occur within clocks we're running */225while (time < clocks)226{227if (SN76489.Registers[i*2]>PSG_CUTOFF) {228/* Flip the flip-flop */229SN76489.ToneFreqPos[i] = -SN76489.ToneFreqPos[i];230} else {231/* stuck value */232SN76489.ToneFreqPos[i] = 1;233}234UpdateToneAmplitude(i, time);235236/* Advance to time of next transition */237time += SN76489.Registers[i*2] * PSG_MCYCLES_RATIO;238}239240/* Update channel tone counter */241SN76489.ToneFreqVals[i] = time;242}243244/* Runs noise channel for clock_length clocks */245static void RunNoise(int clocks)246{247int time;248249/* Noise channel: match to tone2 if in slave mode */250int NoiseFreq = SN76489.NoiseFreq;251if (NoiseFreq == 0x80)252{253NoiseFreq = SN76489.Registers[2*2];254SN76489.ToneFreqVals[3] = SN76489.ToneFreqVals[2];255}256257/* Update in case a register changed etc. */258UpdateNoiseAmplitude(SN76489.clocks);259260/* Time of next transition */261time = SN76489.ToneFreqVals[3];262263/* Process any transitions that occur within clocks we're running */264while (time < clocks)265{266/* Flip the flip-flop */267SN76489.ToneFreqPos[3] = -SN76489.ToneFreqPos[3];268if (SN76489.ToneFreqPos[3] == 1)269{270/* On the positive edge of the square wave (only once per cycle) */271int Feedback = SN76489.NoiseShiftRegister;272if ( SN76489.Registers[6] & 0x4 )273{274/* White noise */275/* Calculate parity of fed-back bits for feedback */276/* Do some optimised calculations for common (known) feedback values */277/* If two bits fed back, I can do Feedback=(nsr & fb) && (nsr & fb ^ fb) */278/* since that's (one or more bits set) && (not all bits set) */279Feedback = ((Feedback & SN76489.NoiseFeedback) && ((Feedback & SN76489.NoiseFeedback) ^ SN76489.NoiseFeedback));280}281else /* Periodic noise */282Feedback = Feedback & 1;283284SN76489.NoiseShiftRegister = (SN76489.NoiseShiftRegister >> 1) | (Feedback << (SN76489.SRWidth - 1));285UpdateNoiseAmplitude(time);286}287288/* Advance to time of next transition */289time += NoiseFreq * PSG_MCYCLES_RATIO;290}291292/* Update channel tone counter */293SN76489.ToneFreqVals[3] = time;294}295296static void SN76489_RunUntil(unsigned int clocks)297{298int i;299300/* Run noise first, since it might use current value of third tone frequency counter */301RunNoise(clocks);302303/* Run tone channels */304for (i=0; i<3; ++i)305{306RunTone(i, clocks);307}308}309310void SN76489_Config(unsigned int clocks, int preAmp, int boostNoise, int stereo)311{312int i;313314/* cycle-accurate Game Gear stereo */315if (clocks > SN76489.clocks)316{317/* Run chip until current timestamp */318SN76489_RunUntil(clocks);319320/* Update internal M-cycle counter */321SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;322}323324for (i=0; i<4; i++)325{326/* stereo channel pre-amplification */327SN76489.PreAmp[i][0] = preAmp * ((stereo >> (i + 4)) & 1);328SN76489.PreAmp[i][1] = preAmp * ((stereo >> (i + 0)) & 1);329330/* noise channel boost */331if (i == 3)332{333SN76489.PreAmp[3][0] = SN76489.PreAmp[3][0] << boostNoise;334SN76489.PreAmp[3][1] = SN76489.PreAmp[3][1] << boostNoise;335}336337/* update stereo channel amplitude */338SN76489.Channel[i][0]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][0]) / 100;339SN76489.Channel[i][1]= (PSGVolumeValues[SN76489.Registers[i*2 + 1]] * SN76489.PreAmp[i][1]) / 100;340}341}342343void SN76489_Update(unsigned int clocks)344{345int i;346347if (clocks > SN76489.clocks)348{349/* Run chip until current timestamp */350SN76489_RunUntil(clocks);351352/* Update internal M-cycle counter */353SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;354}355356/* Adjust internal M-cycle counter for next frame */357SN76489.clocks -= clocks;358359/* Adjust channel time counters for new frame */360for (i=0; i<4; ++i)361{362SN76489.ToneFreqVals[i] -= clocks;363}364}365366void SN76489_Write(unsigned int clocks, unsigned int data)367{368unsigned int index;369370if (clocks > SN76489.clocks)371{372/* run chip until current timestamp */373SN76489_RunUntil(clocks);374375/* update internal M-cycle counter */376SN76489.clocks += ((clocks - SN76489.clocks + PSG_MCYCLES_RATIO - 1) / PSG_MCYCLES_RATIO) * PSG_MCYCLES_RATIO;377}378379if (data & 0x80)380{381/* latch byte %1 cc t dddd */382SN76489.LatchedRegister = index = (data >> 4) & 0x07;383}384else385{386/* restore latched register index */387index = SN76489.LatchedRegister;388}389390switch (index)391{392case 0:393case 2:394case 4: /* Tone Channels frequency */395{396if (data & 0x80)397{398/* Data byte %1 cc t dddd */399SN76489.Registers[index] = (SN76489.Registers[index] & 0x3f0) | (data & 0xf);400}401else402{403/* Data byte %0 - dddddd */404SN76489.Registers[index] = (SN76489.Registers[index] & 0x00f) | ((data & 0x3f) << 4);405}406407/* zero frequency behaves the same as a value of 1 */408if (SN76489.Registers[index] == 0)409{410SN76489.Registers[index] = 1;411}412break;413}414415case 1:416case 3:417case 5: /* Tone Channels attenuation */418{419data &= 0x0f;420SN76489.Registers[index] = data;421data = PSGVolumeValues[data];422index >>= 1;423SN76489.Channel[index][0] = (data * SN76489.PreAmp[index][0]) / 100;424SN76489.Channel[index][1] = (data * SN76489.PreAmp[index][1]) / 100;425break;426}427428case 6: /* Noise control */429{430SN76489.Registers[6] = data & 0x0f;431432/* reset shift register */433SN76489.NoiseShiftRegister = NoiseInitialState;434435/* set noise signal generator frequency */436SN76489.NoiseFreq = 0x10 << (data&0x3);437break;438}439440case 7: /* Noise attenuation */441{442data &= 0x0f;443SN76489.Registers[7] = data;444data = PSGVolumeValues[data];445SN76489.Channel[3][0] = (data * SN76489.PreAmp[3][0]) / 100;446SN76489.Channel[3][1] = (data * SN76489.PreAmp[3][1]) / 100;447break;448}449}450}451452453