mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-12-27 03:31:49 +01:00
fixed Savestates
reverted SN76489 core changes (problem with Sonic 2 first level music, more investigations needed)
This commit is contained in:
parent
acdfb2dbee
commit
e0737ff395
@ -6,7 +6,6 @@ Genesis Plus GX 1.4.0 (??/??/????) (Eke-Eke)
|
|||||||
------
|
------
|
||||||
|
|
||||||
* modified SN76489 cut-off frequency
|
* modified SN76489 cut-off frequency
|
||||||
* implemented optimized SN76489 core which uses Blip Buffer linear synthesis (Noise Channel is now linear interpolated) (credits to Blargg)
|
|
||||||
* added an option to boost SN76489 Noise Channel
|
* added an option to boost SN76489 Noise Channel
|
||||||
* removed outdated Gens YM2612 core
|
* removed outdated Gens YM2612 core
|
||||||
* improved YM2612 core general accuracy (SSG-EG, CSM mode,...) (based upon Nemesis recent tests on real MD)
|
* improved YM2612 core general accuracy (SSG-EG, CSM mode,...) (based upon Nemesis recent tests on real MD)
|
||||||
|
@ -21,287 +21,141 @@
|
|||||||
|
|
||||||
25/04/07 Eke-Eke (Genesis Plus GX)
|
25/04/07 Eke-Eke (Genesis Plus GX)
|
||||||
- Removed stereo GG support (unused)
|
- Removed stereo GG support (unused)
|
||||||
- Rade SN76489_Update outputs 16bits mono samples
|
- Made SN76489_Update outputs 16bits mono samples
|
||||||
- Replaced volume table with VGM plugin's one
|
- Replaced volume table with VGM plugin's one
|
||||||
|
|
||||||
05/01/09 Eke-Eke (Genesis Plus GX)
|
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)
|
- Modified Cut-Off frequency (according to Steve Snake: http://www.smspower.org/forums/viewtopic.php?t=1746)
|
||||||
|
|
||||||
25/05/09 Eke-Eke (Genesis Plus GX)
|
25/05/09 Eke-Eke (Genesis Plus GX)
|
||||||
- Removed multichip support (unused)
|
- 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 (Blargg's implementation: http://www.smspower.org/forums/viewtopic.php?t=11376)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
#include "blip.h"
|
|
||||||
#include <float.h>
|
|
||||||
|
|
||||||
/* Initial state of shift register */
|
#include <float.h> // for FLT_MIN
|
||||||
#define NoiseInitialState 0x8000
|
#include <string.h> // for memcpy
|
||||||
|
|
||||||
/* Value below which PSG does not output */
|
#define NoiseInitialState 0x8000 /* Initial state of shift register */
|
||||||
/*#define PSG_CUTOFF 0x6*/
|
//#define PSG_CUTOFF 0x6 /* Value below which PSG does not output */
|
||||||
#define PSG_CUTOFF 0x1
|
#define PSG_CUTOFF 0x1
|
||||||
|
|
||||||
static const int PSGVolumeValues[16] =
|
/*
|
||||||
|
More testing is needed to find and confirm feedback patterns for
|
||||||
|
SN76489 variants and compatible chips.
|
||||||
|
*/
|
||||||
|
enum feedback_patterns {
|
||||||
|
FB_BBCMICRO = 0x8005, /* Texas Instruments TMS SN76489N (original) from BBC Micro computer */
|
||||||
|
FB_SC3000 = 0x0006, /* Texas Instruments TMS SN76489AN (rev. A) from SC-3000H computer */
|
||||||
|
FB_SEGAVDP = 0x0009, /* SN76489 clone in Sega's VDP chips (315-5124, 315-5246, 315-5313, Game Gear) */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sr_widths {
|
||||||
|
SRW_SC3000BBCMICRO = 15,
|
||||||
|
SRW_SEGAVDP = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
enum volume_modes {
|
||||||
|
VOL_TRUNC = 0, /* Volume levels 13-15 are identical */
|
||||||
|
VOL_FULL = 1, /* Volume levels 13-15 are unique */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum mute_values {
|
||||||
|
MUTE_ALLOFF = 0, /* All channels muted */
|
||||||
|
MUTE_TONE1 = 1, /* Tone 1 mute control */
|
||||||
|
MUTE_TONE2 = 2, /* Tone 2 mute control */
|
||||||
|
MUTE_TONE3 = 4, /* Tone 3 mute control */
|
||||||
|
MUTE_NOISE = 8, /* Noise mute control */
|
||||||
|
MUTE_ALLON = 15, /* All channels enabled */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
{
|
{
|
||||||
|
int Mute; // per-channel muting
|
||||||
|
int VolumeArray;
|
||||||
|
int BoostNoise; // double noise volume when non-zero
|
||||||
|
|
||||||
|
/* Variables */
|
||||||
|
float Clock;
|
||||||
|
float dClock;
|
||||||
|
int PSGStereo;
|
||||||
|
int NumClocksForSample;
|
||||||
|
int WhiteNoiseFeedback;
|
||||||
|
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 Channels[4]; /* Value of each channel, before stereo is applied */
|
||||||
|
float IntermediatePos[4]; /* intermediate values used at boundaries between + and - (does not need double accuracy)*/
|
||||||
|
|
||||||
|
int panning[4]; /* fake stereo - 0..127..254 */
|
||||||
|
|
||||||
|
} SN76489_Context;
|
||||||
|
|
||||||
|
|
||||||
|
static const int PSGVolumeValues[2][16] = {
|
||||||
/* These values are taken from a real SMS2's output */
|
/* 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 */
|
{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), normalised at 760 */
|
/* these values are true volumes for 2dB drops at each step (multiply previous by 10^-0.1), normalised at 760 */
|
||||||
1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0
|
{1516,1205,957,760,603,479,381,303,240,191,152,120,96,76,60,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
static SN76489_Context SN76489;
|
static SN76489_Context SN76489;
|
||||||
|
|
||||||
void SN76489_Init(int PSGClockValue, int SamplingRate)
|
void SN76489_Init(int PSGClockValue, int SamplingRate)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489;
|
SN76489_Context *p = &SN76489;
|
||||||
|
p->dClock=(float)PSGClockValue/16/SamplingRate;
|
||||||
/* SamplingRate*16 instead of PSGClockValue/16 since division would lose some
|
SN76489_Config(MUTE_ALLON, VOL_FULL, FB_SEGAVDP, SRW_SEGAVDP, config.psgBoostNoise);
|
||||||
precision. blip_alloc doesn't care about the absolute sampling rate, just the
|
SN76489_Reset();
|
||||||
ratio to clock rate. */
|
|
||||||
p->blip_buffer = blip_alloc(PSGClockValue, SamplingRate * 16, SamplingRate / 4);
|
|
||||||
|
|
||||||
SN76489_Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_Reset()
|
void SN76489_Reset(void)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489;
|
SN76489_Context *p = &SN76489;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for(i = 0; i <= 3; i++)
|
p->PSGStereo = 0xFF;
|
||||||
{
|
|
||||||
/* Initialise PSG state */
|
|
||||||
p->Registers[2*i] = 1; /* tone freq=1 */
|
|
||||||
p->Registers[2*i+1] = 0xf; /* vol=off */
|
|
||||||
p->NoiseFreq = 0x10;
|
|
||||||
|
|
||||||
/* Set counters to 0 */
|
for(i = 0; i <= 3; i++)
|
||||||
p->ToneFreqVals[i] = 0;
|
{
|
||||||
|
/* Initialise PSG state */
|
||||||
|
p->Registers[2*i] = 1; /* tone freq=1 */
|
||||||
|
p->Registers[2*i+1] = 0xf; /* vol=off */
|
||||||
|
p->NoiseFreq = 0x10;
|
||||||
|
|
||||||
/* Set flip-flops to 1 */
|
/* Set counters to 0 */
|
||||||
p->ToneFreqPos[i] = 1;
|
p->ToneFreqVals[i] = 0;
|
||||||
|
|
||||||
/* Clear current amplitudes in Blip delta buffer */
|
/* Set flip-flops to 1 */
|
||||||
p->chan_amp[i] = 0;
|
p->ToneFreqPos[i] = 1;
|
||||||
}
|
|
||||||
|
|
||||||
p->LatchedRegister=0;
|
/* Set intermediate positions to do-not-use value */
|
||||||
|
p->IntermediatePos[i] = FLT_MIN;
|
||||||
|
|
||||||
/* Initialise noise generator */
|
/* Set panning to centre */
|
||||||
p->NoiseShiftRegister=NoiseInitialState;
|
p->panning[0]=127;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->LatchedRegister=0;
|
||||||
|
|
||||||
|
/* Initialise noise generator */
|
||||||
|
p->NoiseShiftRegister=NoiseInitialState;
|
||||||
|
|
||||||
|
/* Zero clock */
|
||||||
|
p->Clock=0;
|
||||||
|
|
||||||
/* Clear Blip delta buffer */
|
|
||||||
if (p->blip_buffer) blip_clear(p->blip_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_Shutdown(void)
|
void SN76489_Shutdown(void)
|
||||||
{
|
{
|
||||||
SN76489_Context *p = &SN76489;
|
|
||||||
if (p->blip_buffer) blip_free(p->blip_buffer);
|
|
||||||
p->blip_buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SN76489_SetContext(uint8 *data)
|
|
||||||
{
|
|
||||||
memcpy(&SN76489, data, sizeof(SN76489_Context));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SN76489_GetContext(uint8 *data)
|
|
||||||
{
|
|
||||||
memcpy(data, &SN76489, sizeof(SN76489_Context));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8 *SN76489_GetContextPtr(void)
|
|
||||||
{
|
|
||||||
return (uint8 *)&SN76489;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SN76489_GetContextSize(void)
|
|
||||||
{
|
|
||||||
return sizeof(SN76489_Context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SN76489_Write(int data)
|
|
||||||
{
|
|
||||||
SN76489_Context *p = &SN76489;
|
|
||||||
|
|
||||||
if (data&0x80)
|
|
||||||
{
|
|
||||||
/* Latch/data byte %1 cc t dddd */
|
|
||||||
p->LatchedRegister=((data>>4)&0x07);
|
|
||||||
p->Registers[p->LatchedRegister] =
|
|
||||||
(p->Registers[p->LatchedRegister] & 0x3f0) | /* zero low 4 bits */
|
|
||||||
(data&0xf); /* and replace with data */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Data byte %0 - dddddd */
|
|
||||||
if (!(p->LatchedRegister%2)&&(p->LatchedRegister<5))
|
|
||||||
/* Tone register */
|
|
||||||
p->Registers[p->LatchedRegister]=
|
|
||||||
(p->Registers[p->LatchedRegister] & 0x00f) | /* zero high 6 bits */
|
|
||||||
((data&0x3f)<<4); /* and replace with data */
|
|
||||||
else
|
|
||||||
/* Other register */
|
|
||||||
p->Registers[p->LatchedRegister]=data&0x0f; /* Replace with data */
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (p->LatchedRegister)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 2:
|
|
||||||
case 4: /* Tone channels */
|
|
||||||
/* Zero frequency changed to 1 to avoid div/0 */
|
|
||||||
if (p->Registers[p->LatchedRegister]==0) p->Registers[p->LatchedRegister]=1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6: /* Noise */
|
|
||||||
p->NoiseShiftRegister=NoiseInitialState; /* reset shift register */
|
|
||||||
p->NoiseFreq=0x10<<(p->Registers[6]&0x3); /* set noise signal generator frequency */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates channel amplitude in delta buffer. Call whenever amplitude might have changed. */
|
|
||||||
static inline void UpdateChanAmplitude(SN76489_Context* chip, int i, int time)
|
|
||||||
{
|
|
||||||
/* Build stereo result into buffer */
|
|
||||||
int buffer = chip->Channels[i];
|
|
||||||
|
|
||||||
/* Update amplitudes in left and right buffers */
|
|
||||||
int delta = buffer - chip->chan_amp[i];
|
|
||||||
if (delta != 0)
|
|
||||||
{
|
|
||||||
chip->chan_amp[i] = buffer;
|
|
||||||
blip_add(chip->blip_buffer, time, delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates tone amplitude in delta buffer. Call whenever amplitude might have changed. */
|
|
||||||
static inline void UpdateToneAmplitude(SN76489_Context* chip, int i, int time)
|
|
||||||
{
|
|
||||||
/* Tone channels */
|
|
||||||
chip->Channels[i]= PSGVolumeValues[chip->Registers[2 * i + 1]] * chip->ToneFreqPos[i];
|
|
||||||
|
|
||||||
UpdateChanAmplitude(chip, i, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Updates noise amplitude in delta buffer. Call whenever amplitude might have changed. */
|
|
||||||
static inline void UpdateNoiseAmplitude(SN76489_Context* chip, int time)
|
|
||||||
{
|
|
||||||
/* Noise channel */
|
|
||||||
chip->Channels[3] = PSGVolumeValues[chip->Registers[7]] * ( chip->NoiseShiftRegister & 0x1 );
|
|
||||||
|
|
||||||
/* Boost noise volume */
|
|
||||||
chip->Channels[3] <<= chip->BoostNoise;
|
|
||||||
|
|
||||||
UpdateChanAmplitude(chip, 3, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Runs tone channel for clock_length clocks */
|
|
||||||
static inline void RunTone(SN76489_Context* chip, int i, int clock_length)
|
|
||||||
{
|
|
||||||
int time;
|
|
||||||
|
|
||||||
/* Update in case a register changed etc. */
|
|
||||||
UpdateToneAmplitude(chip, i, 0);
|
|
||||||
|
|
||||||
/* Time of next transition */
|
|
||||||
time = chip->ToneFreqVals[i];
|
|
||||||
|
|
||||||
/* Process any transitions that occur within clocks we're running */
|
|
||||||
while (time < clock_length)
|
|
||||||
{
|
|
||||||
if (chip->Registers[i*2]>PSG_CUTOFF) {
|
|
||||||
/* Flip the flip-flop */
|
|
||||||
chip->ToneFreqPos[i] = -chip->ToneFreqPos[i];
|
|
||||||
} else {
|
|
||||||
/* stuck value */
|
|
||||||
chip->ToneFreqPos[i] = 1;
|
|
||||||
}
|
|
||||||
UpdateToneAmplitude(chip, i, time);
|
|
||||||
|
|
||||||
/* Advance to time of next transition */
|
|
||||||
time += chip->Registers[i*2] + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate new value for register, now that next transition is past number of clocks we're running */
|
|
||||||
chip->ToneFreqVals[i] = time - clock_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Runs noise channel for clock_length clocks */
|
|
||||||
static inline void RunNoise(SN76489_Context* chip, int clock_length)
|
|
||||||
{
|
|
||||||
int time;
|
|
||||||
|
|
||||||
/* Noise channel: match to tone2 if in slave mode */
|
|
||||||
int NoiseFreq = chip->NoiseFreq;
|
|
||||||
if (NoiseFreq == 0x80)
|
|
||||||
{
|
|
||||||
NoiseFreq = chip->Registers[2*2];
|
|
||||||
chip->ToneFreqVals[3] = chip->ToneFreqVals[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update in case a register changed etc. */
|
|
||||||
UpdateNoiseAmplitude(chip, 0);
|
|
||||||
|
|
||||||
/* Time of next transition */
|
|
||||||
time = chip->ToneFreqVals[3];
|
|
||||||
|
|
||||||
/* Process any transitions that occur within clocks we're running */
|
|
||||||
while ( time < clock_length )
|
|
||||||
{
|
|
||||||
/* Flip the flip-flop */
|
|
||||||
chip->ToneFreqPos[3] = -chip->ToneFreqPos[3];
|
|
||||||
if (chip->ToneFreqPos[3] == 1) {
|
|
||||||
/* On the positive edge of the square wave (only once per cycle) */
|
|
||||||
int Feedback;
|
|
||||||
if ( chip->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 = ( ( chip->NoiseShiftRegister & FB_SEGAVDP)
|
|
||||||
&& ( (chip->NoiseShiftRegister & FB_SEGAVDP) ^ FB_SEGAVDP) );
|
|
||||||
} else /* Periodic noise */
|
|
||||||
Feedback=chip->NoiseShiftRegister&1;
|
|
||||||
|
|
||||||
chip->NoiseShiftRegister=(chip->NoiseShiftRegister>>1) | (Feedback << (SRW_SEGAVDP-1));
|
|
||||||
UpdateNoiseAmplitude(chip, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advance to time of next transition */
|
|
||||||
time += NoiseFreq + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate new value for register, now that next transition is past number of clocks we're running */
|
|
||||||
chip->ToneFreqVals[3] = time - clock_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SN76489_Update(INT16 *buffer, int length)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
SN76489_Context *p = &SN76489;
|
|
||||||
|
|
||||||
/* Determine how many clocks we need to run until 'length' samples are available */
|
|
||||||
int clock_length = blip_clocks_needed(p->blip_buffer, length);
|
|
||||||
|
|
||||||
/* Run noise first, since it might use current value of third tone frequency counter */
|
|
||||||
RunNoise(p, clock_length);
|
|
||||||
|
|
||||||
/* Run tone channels */
|
|
||||||
for( i = 0; i <= 2; ++i )
|
|
||||||
RunTone(p, i, clock_length);
|
|
||||||
|
|
||||||
/* Read samples into output buffer */
|
|
||||||
blip_end_frame(p->blip_buffer,clock_length);
|
|
||||||
blip_read_samples(p->blip_buffer,buffer,length,0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SN76489_BoostNoise(int boost)
|
void SN76489_BoostNoise(int boost)
|
||||||
@ -309,3 +163,201 @@ void SN76489_BoostNoise(int boost)
|
|||||||
SN76489.BoostNoise = boost;
|
SN76489.BoostNoise = boost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SN76489_Config(int mute, int volume, int feedback, int sr_width, int boost_noise)
|
||||||
|
{
|
||||||
|
SN76489_Context *p = &SN76489;
|
||||||
|
|
||||||
|
p->Mute = mute;
|
||||||
|
p->VolumeArray = volume;
|
||||||
|
p->WhiteNoiseFeedback = feedback;
|
||||||
|
p->SRWidth = sr_width;
|
||||||
|
p->BoostNoise = boost_noise;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_SetContext(uint8 *data)
|
||||||
|
{
|
||||||
|
memcpy(&SN76489, data, sizeof(SN76489_Context));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_GetContext(uint8 *data)
|
||||||
|
{
|
||||||
|
memcpy(data, &SN76489, sizeof(SN76489_Context));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 *SN76489_GetContextPtr(void)
|
||||||
|
{
|
||||||
|
return (uint8 *)&SN76489;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SN76489_GetContextSize(void)
|
||||||
|
{
|
||||||
|
return sizeof(SN76489_Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_Write(int data)
|
||||||
|
{
|
||||||
|
SN76489_Context *p = &SN76489;
|
||||||
|
|
||||||
|
if (data&0x80) {
|
||||||
|
/* Latch/data byte %1 cc t dddd */
|
||||||
|
p->LatchedRegister=((data>>4)&0x07);
|
||||||
|
p->Registers[p->LatchedRegister]=
|
||||||
|
(p->Registers[p->LatchedRegister] & 0x3f0) /* zero low 4 bits */
|
||||||
|
| (data&0xf); /* and replace with data */
|
||||||
|
} else {
|
||||||
|
/* Data byte %0 - dddddd */
|
||||||
|
if (!(p->LatchedRegister%2)&&(p->LatchedRegister<5))
|
||||||
|
/* Tone register */
|
||||||
|
p->Registers[p->LatchedRegister]=
|
||||||
|
(p->Registers[p->LatchedRegister] & 0x00f) /* zero high 6 bits */
|
||||||
|
| ((data&0x3f)<<4); /* and replace with data */
|
||||||
|
else
|
||||||
|
/* Other register */
|
||||||
|
p->Registers[p->LatchedRegister]=data&0x0f; /* Replace with data */
|
||||||
|
}
|
||||||
|
switch (p->LatchedRegister) {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
case 4: /* Tone channels */
|
||||||
|
if (p->Registers[p->LatchedRegister]==0) p->Registers[p->LatchedRegister]=1; /* Zero frequency changed to 1 to avoid div/0 */
|
||||||
|
break;
|
||||||
|
case 6: /* Noise */
|
||||||
|
p->NoiseShiftRegister=NoiseInitialState; /* reset shift register */
|
||||||
|
p->NoiseFreq=0x10<<(p->Registers[6]&0x3); /* set noise signal generator frequency */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_GGStereoWrite(int data)
|
||||||
|
{
|
||||||
|
SN76489_Context *p = &SN76489;
|
||||||
|
p->PSGStereo=data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_Update(INT16 *buffer, int length)
|
||||||
|
{
|
||||||
|
SN76489_Context *p = &SN76489;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for(j = 0; j < length; j++)
|
||||||
|
{
|
||||||
|
for (i=0;i<=2;++i)
|
||||||
|
if (p->IntermediatePos[i]!=FLT_MIN)
|
||||||
|
p->Channels[i]=(short)((p->Mute >> i & 0x1)*PSGVolumeValues[p->VolumeArray][p->Registers[2*i+1]]*p->IntermediatePos[i]);
|
||||||
|
else
|
||||||
|
p->Channels[i]=(p->Mute >> i & 0x1)*PSGVolumeValues[p->VolumeArray][p->Registers[2*i+1]]*p->ToneFreqPos[i];
|
||||||
|
|
||||||
|
p->Channels[3]=(short)((p->Mute >> 3 & 0x1)*PSGVolumeValues[p->VolumeArray][p->Registers[7]]*(p->NoiseShiftRegister & 0x1));
|
||||||
|
|
||||||
|
if (p->BoostNoise) p->Channels[3]<<=1; /* double noise volume */
|
||||||
|
|
||||||
|
buffer[j] =0;
|
||||||
|
for (i=0;i<=3;++i) buffer[j] += p->Channels[i];
|
||||||
|
|
||||||
|
p->Clock+=p->dClock;
|
||||||
|
p->NumClocksForSample=(int)p->Clock; /* truncates */
|
||||||
|
p->Clock-=p->NumClocksForSample; /* remove integer part */
|
||||||
|
/* Looks nicer in Delphi... */
|
||||||
|
/* Clock:=Clock+p->dClock; */
|
||||||
|
/* NumClocksForSample:=Trunc(Clock); */
|
||||||
|
/* Clock:=Frac(Clock); */
|
||||||
|
|
||||||
|
/* Decrement tone channel counters */
|
||||||
|
for (i=0;i<=2;++i)
|
||||||
|
p->ToneFreqVals[i]-=p->NumClocksForSample;
|
||||||
|
|
||||||
|
/* Noise channel: match to tone2 or decrement its counter */
|
||||||
|
if (p->NoiseFreq==0x80) p->ToneFreqVals[3]=p->ToneFreqVals[2];
|
||||||
|
else p->ToneFreqVals[3]-=p->NumClocksForSample;
|
||||||
|
|
||||||
|
/* Tone channels: */
|
||||||
|
for (i=0;i<=2;++i) {
|
||||||
|
if (p->ToneFreqVals[i]<=0) { /* If it gets below 0... */
|
||||||
|
if (p->Registers[i*2]>PSG_CUTOFF) {
|
||||||
|
/* Calculate how much of the sample is + and how much is - */
|
||||||
|
/* Go to floating point and include the clock fraction for extreme accuracy :D */
|
||||||
|
/* Store as long int, maybe it's faster? I'm not very good at this */
|
||||||
|
p->IntermediatePos[i]=(p->NumClocksForSample-p->Clock+2*p->ToneFreqVals[i])*p->ToneFreqPos[i]/(p->NumClocksForSample+p->Clock);
|
||||||
|
p->ToneFreqPos[i]=-p->ToneFreqPos[i]; /* Flip the flip-flop */
|
||||||
|
} else {
|
||||||
|
p->ToneFreqPos[i]=1; /* stuck value */
|
||||||
|
p->IntermediatePos[i]=FLT_MIN;
|
||||||
|
}
|
||||||
|
p->ToneFreqVals[i]+=p->Registers[i*2]*(p->NumClocksForSample/p->Registers[i*2]+1);
|
||||||
|
} else p->IntermediatePos[i]=FLT_MIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Noise channel */
|
||||||
|
if (p->ToneFreqVals[3]<=0) { /* If it gets below 0... */
|
||||||
|
p->ToneFreqPos[3]=-p->ToneFreqPos[3]; /* Flip the flip-flop */
|
||||||
|
if (p->NoiseFreq!=0x80) /* If not matching tone2, decrement counter */
|
||||||
|
p->ToneFreqVals[3]+=p->NoiseFreq*(p->NumClocksForSample/p->NoiseFreq+1);
|
||||||
|
if (p->ToneFreqPos[3]==1) { /* Only once per cycle... */
|
||||||
|
int Feedback;
|
||||||
|
if (p->Registers[6]&0x4) { /* White noise */
|
||||||
|
/* Calculate parity of fed-back bits for feedback */
|
||||||
|
switch (p->WhiteNoiseFeedback) {
|
||||||
|
/* Do some optimised calculations for common (known) feedback values */
|
||||||
|
case 0x0003: /* SC-3000, BBC %00000011 */
|
||||||
|
case 0x0009: /* SMS, GG, MD %00001001 */
|
||||||
|
/* 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=((p->NoiseShiftRegister&p->WhiteNoiseFeedback) && ((p->NoiseShiftRegister&p->WhiteNoiseFeedback)^p->WhiteNoiseFeedback));
|
||||||
|
break;
|
||||||
|
default: /* Default handler for all other feedback values */
|
||||||
|
Feedback=p->NoiseShiftRegister&p->WhiteNoiseFeedback;
|
||||||
|
Feedback^=Feedback>>8;
|
||||||
|
Feedback^=Feedback>>4;
|
||||||
|
Feedback^=Feedback>>2;
|
||||||
|
Feedback^=Feedback>>1;
|
||||||
|
Feedback&=1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else /* Periodic noise */
|
||||||
|
Feedback=p->NoiseShiftRegister&1;
|
||||||
|
|
||||||
|
p->NoiseShiftRegister=(p->NoiseShiftRegister>>1) | (Feedback << (p->SRWidth-1));
|
||||||
|
|
||||||
|
/* Original code: */
|
||||||
|
/* p->NoiseShiftRegister=(p->NoiseShiftRegister>>1) | ((p->Registers[6]&0x4?((p->NoiseShiftRegister&0x9) && (p->NoiseShiftRegister&0x9^0x9)):p->NoiseShiftRegister&1)<<15); */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*void SN76489_UpdateOne(int which, int *l, int *r)
|
||||||
|
{
|
||||||
|
INT16 tl,tr;
|
||||||
|
INT16 *buff[2]={&tl,&tr};
|
||||||
|
SN76489_Update(which,buff,1);
|
||||||
|
*l=tl;
|
||||||
|
*r=tr;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
int SN76489_GetMute()
|
||||||
|
{
|
||||||
|
return SN76489.Mute;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_SetMute(int val)
|
||||||
|
{
|
||||||
|
SN76489.Mute=val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SN76489_GetVolType()
|
||||||
|
{
|
||||||
|
return SN76489.VolumeArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_SetVolType(int val)
|
||||||
|
{
|
||||||
|
SN76489.VolumeArray=val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SN76489_SetPanning(int ch0, int ch1, int ch2, int ch3)
|
||||||
|
{
|
||||||
|
SN76489.panning[0]=ch0;
|
||||||
|
SN76489.panning[1]=ch1;
|
||||||
|
SN76489.panning[2]=ch2;
|
||||||
|
SN76489.panning[3]=ch3;
|
||||||
|
}
|
||||||
|
@ -37,32 +37,6 @@
|
|||||||
#ifndef _SN76489_H_
|
#ifndef _SN76489_H_
|
||||||
#define _SN76489_H_
|
#define _SN76489_H_
|
||||||
|
|
||||||
/* SN76489 clone 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 BoostNoise; // double noise volume when non-zero
|
|
||||||
|
|
||||||
/* 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 Channels[4]; /* Value of each channel, before stereo is applied */
|
|
||||||
|
|
||||||
/* Blip-Buffer variables */
|
|
||||||
struct blip_buffer_t* blip_buffer; /* delta resampler */
|
|
||||||
int chan_amp[4]; /* current channel amplitudes in delta buffers */
|
|
||||||
|
|
||||||
} SN76489_Context;
|
|
||||||
|
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
extern void SN76489_Init(int PSGClockValue, int SamplingRate);
|
extern void SN76489_Init(int PSGClockValue, int SamplingRate);
|
||||||
extern void SN76489_Reset(void);
|
extern void SN76489_Reset(void);
|
||||||
@ -74,6 +48,7 @@ extern int SN76489_GetContextSize(void);
|
|||||||
extern void SN76489_Write(int data);
|
extern void SN76489_Write(int data);
|
||||||
extern void SN76489_Update(INT16 *buffer, int length);
|
extern void SN76489_Update(INT16 *buffer, int length);
|
||||||
extern void SN76489_BoostNoise(int boost);
|
extern void SN76489_BoostNoise(int boost);
|
||||||
|
extern void SN76489_Config(int mute, int volume, int feedback, int sr_width, int boost_noise);
|
||||||
|
|
||||||
#endif /* _SN76489_H_ */
|
#endif /* _SN76489_H_ */
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#define _STATE_H_
|
#define _STATE_H_
|
||||||
|
|
||||||
#define STATE_SIZE 0x28000
|
#define STATE_SIZE 0x28000
|
||||||
#define STATE_VERSION "GENPLUS-GX 1.3.2"
|
#define STATE_VERSION "GENPLUS-GX 1.4.0"
|
||||||
|
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
extern int state_load(unsigned char *buffer);
|
extern int state_load(unsigned char *buffer);
|
||||||
|
@ -86,7 +86,7 @@ void audio_update (int size)
|
|||||||
for (i = 0; i < size; i ++)
|
for (i = 0; i < size; i ++)
|
||||||
{
|
{
|
||||||
/* PSG samples (mono) */
|
/* PSG samples (mono) */
|
||||||
l = r = (((int)*psg++) * psg_preamp)/100;
|
l = r = ((*psg++) * psg_preamp)/100;
|
||||||
|
|
||||||
/* FM samples (stereo) */
|
/* FM samples (stereo) */
|
||||||
l += (*fm[0]++ * fm_preamp)/100;
|
l += (*fm[0]++ * fm_preamp)/100;
|
||||||
|
Loading…
Reference in New Issue
Block a user