Got rid of R->OldNoise since it causes problems for gamegear/sms. Changed noise taps accordingly. Redid change to gamegear/sms for XOR vs XNOR, now verified by Justin Kerk. Verified gamegear noise taps thanks to sample provided by Justin Kerk.

Add stereo sound support to sn76496.c for gamegear. [Lord Nightmare, Justin Kerk]
This commit is contained in:
Jonathan Gevaryahu 2009-11-18 08:09:19 +00:00
parent c3ea836b07
commit 3dbbc11f0d
2 changed files with 71 additions and 37 deletions

View File

@ -30,6 +30,7 @@
The register, connected to a z80 port, means:
for bits 7 6 5 4 3 2 1 0
L3 L2 L1 L0 R3 R2 R1 R0
Noise is an XOR function, and audio output is negated before being output.
** NCR7496 (as used on the Tandy 1000) is similar to the SN76489 but with a
different noise LFSR patttern: taps on bits A and E, output on E
It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
@ -62,11 +63,14 @@
Fix screeching in regulus: When summing together four equal channels, the
size of the max amplitude per channel should be 1/4 of the max range, not
1/3. Added NCR7496.
18/11/2009 : Lord Nightmare
Modify Init functions to support negating the audio output. The gamegear
psg does this. Change gamegear and sega psgs to use XOR rather than XNOR
based on testing. Got rid of R->OldNoise and fixed taps accordingly.
Added stereo support for game gear.
TODO: * Implement a function for setting stereo regs for the game gear.
Requires making the core support both mono and stereo, and have
a select register which determines which channels go where.
* Implement the TMS9919 which is an earlier version, possibly
TODO: * Implement the TMS9919 which is an earlier version, possibly
lacking the /8 clock divider, of the SN76489, and hence would
have a max clock of 500Khz and 4 clocks per sample, as opposed to
max of 4Mhz and 32 clocks per sample on the SN76489A.
@ -74,8 +78,6 @@
to be 'sanitized' first. Also is stereo, similar to game gear.
* Test the NCR7496; Smspower says the whitenoise taps are A and E,
but this needs verification on real hardware.
* Test the SMS PSG/Gamegear PSG to see whether it uses XOR or XNOR
taps. (17/11/2009: this seems to be XNOR? Need a better sample.)
* Factor out common code so that the SAA1099 can share some code.
***************************************************************************/
@ -101,9 +103,11 @@ struct _sn76496_state
INT32 FeedbackMask; /* mask for feedback */
INT32 WhitenoiseTaps; /* mask for white noise taps */
INT32 FeedbackInvert; /* feedback invert flag (xor vs xnor) */
INT32 Negate; /* output negate flag */
INT32 Stereo; /* whether we're dealing with stereo or not */
INT32 StereoMask; /* the stereo output mask */
INT32 Period[4]; /* Length of 1/2 of waveform */
INT32 Count[4]; /* Position within the waveform */
INT32 OldNoise; /* 1 bit output of the PREVIOUS noise output */
INT32 Output[4]; /* 1-bit output of each channel, pre-volume */
INT32 CyclestoREADY;/* number of cycles until the READY line goes active */
};
@ -132,6 +136,14 @@ READ8_DEVICE_HANDLER( sn76496_ready_r )
return (R->CyclestoREADY? 0 : 1);
}
WRITE8_DEVICE_HANDLER( sn76496_stereo_w )
{
sn76496_state *R = get_safe_token(device);
stream_update(R->Channel);
if (R->Stereo) R->StereoMask = data;
else fatalerror("Call to stereo write with mono chip!\n");
}
WRITE8_DEVICE_HANDLER( sn76496_w )
{
sn76496_state *R = get_safe_token(device);
@ -192,9 +204,7 @@ WRITE8_DEVICE_HANDLER( sn76496_w )
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (1 << (5+(n&3)));
/* Reset noise shifter */
R->RNG = R->FeedbackMask;
R->OldNoise = 0;
R->Output[3] = R->OldNoise;
R->OldNoise = R->RNG & 1;
R->Output[3] = R->RNG & 1;
}
break;
}
@ -204,13 +214,14 @@ static STREAM_UPDATE( SN76496Update )
{
int i;
sn76496_state *R = (sn76496_state *)param;
stream_sample_t *buffer = outputs[0];
stream_sample_t *lbuffer = outputs[0];
stream_sample_t *rbuffer = outputs[1];
while (samples > 0)
{
// clock chip once
INT16 out;
INT16 out = 0;
INT16 out2 = 0;
/* decrement Cycles to READY by one */
if (R->CyclestoREADY >0) R->CyclestoREADY--;
@ -254,8 +265,7 @@ static STREAM_UPDATE( SN76496Update )
R->RNG >>= 1;
}
}
R->Output[3] = R->OldNoise;
R->OldNoise = R->RNG & 1;
R->Output[3] = R->RNG & 1;
R->Count[3] = R->Period[3];
}
/* //bipolar output, doesn't seem to work right with sonic 2 on gamegear at least
@ -264,13 +274,30 @@ static STREAM_UPDATE( SN76496Update )
+(R->Output[2]?R->Volume[2]:(0-R->Volume[2]))
+(R->Output[3]?R->Volume[3]:(0-R->Volume[3]));
*/
out = (R->Output[0]?R->Volume[0]:0)
+(R->Output[1]?R->Volume[1]:0)
+(R->Output[2]?R->Volume[2]:0)
+(R->Output[3]?R->Volume[3]:0);
if (R->Stereo)
{
out = (((R->StereoMask&0x10)&&R->Output[0])?R->Volume[0]:0)
+ (((R->StereoMask&0x20)&&R->Output[1])?R->Volume[1]:0)
+ (((R->StereoMask&0x40)&&R->Output[2])?R->Volume[2]:0)
+ (((R->StereoMask&0x80)&&R->Output[3])?R->Volume[3]:0);
out2 = (((R->StereoMask&0x1)&&R->Output[0])?R->Volume[0]:0)
+ (((R->StereoMask&0x2)&&R->Output[1])?R->Volume[1]:0)
+ (((R->StereoMask&0x4)&&R->Output[2])?R->Volume[2]:0)
+ (((R->StereoMask&0x8)&&R->Output[3])?R->Volume[3]:0);
}
else
{
out = (R->Output[0]?R->Volume[0]:0)
+(R->Output[1]?R->Volume[1]:0)
+(R->Output[2]?R->Volume[2]:0)
+(R->Output[3]?R->Volume[3]:0);
}
*(buffer++) = out;
if(R->Negate) { out = -out; out2 = -out2; }
*(lbuffer++) = out;
if (R->Stereo) *(rbuffer++) = out2;
samples--;
}
}
@ -294,7 +321,7 @@ static void SN76496_set_gain(sn76496_state *R,int gain)
for (i = 0;i < 15;i++)
{
/* limit volume to avoid clipping */
if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3;
if (out > MAX_OUTPUT / 4) R->VolTable[i] = MAX_OUTPUT / 4;
else R->VolTable[i] = out;
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
@ -304,12 +331,12 @@ static void SN76496_set_gain(sn76496_state *R,int gain)
static int SN76496_init(const device_config *device, sn76496_state *R)
static int SN76496_init(const device_config *device, sn76496_state *R, int stereo)
{
int sample_rate = device->clock/16;
int i;
R->Channel = stream_create(device,0,1,sample_rate,R,SN76496Update);
R->Channel = stream_create(device,0,(stereo?2:1),sample_rate,R,SN76496Update);
for (i = 0;i < 4;i++) R->Volume[i] = 0;
@ -330,27 +357,30 @@ static int SN76496_init(const device_config *device, sn76496_state *R)
R->WhitenoiseTaps = 0x03; /* mask for white noise taps */
R->FeedbackInvert = 0; /* feedback invert flag */
R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/
R->Negate = 0; /* channels are not negated */
R->Stereo = stereo; /* depends on init */
R->StereoMask = 0xFF; /* all channels enabled */
R->RNG = R->FeedbackMask;
R->OldNoise = 0;
R->Output[3] = R->OldNoise;
R->OldNoise = R->RNG & 1;
R->Output[3] = R->RNG & 1;
return 0;
}
static void generic_start(const device_config *device, int feedbackmask, int noisetaps, int feedbackinvert)
static void generic_start(const device_config *device, int feedbackmask, int noisetaps, int feedbackinvert, int negate, int stereo)
{
sn76496_state *chip = get_safe_token(device);
if (SN76496_init(device,chip) != 0)
if (SN76496_init(device,chip,stereo) != 0)
fatalerror("Error creating SN76496 chip");
SN76496_set_gain(chip, 0);
chip->FeedbackMask = feedbackmask;
chip->WhitenoiseTaps = noisetaps;
chip->FeedbackInvert = feedbackinvert;
chip->Negate = negate;
chip->Stereo = stereo;
state_save_register_device_item_array(device, 0, chip->VolTable);
state_save_register_device_item_array(device, 0, chip->Register);
@ -360,52 +390,55 @@ static void generic_start(const device_config *device, int feedbackmask, int noi
state_save_register_device_item(device, 0, chip->FeedbackMask);
state_save_register_device_item(device, 0, chip->WhitenoiseTaps);
state_save_register_device_item(device, 0, chip->FeedbackInvert);
state_save_register_device_item(device, 0, chip->Negate);
state_save_register_device_item(device, 0, chip->Stereo);
state_save_register_device_item(device, 0, chip->StereoMask);
state_save_register_device_item_array(device, 0, chip->Period);
state_save_register_device_item_array(device, 0, chip->Count);
state_save_register_device_item(device, 0, chip->OldNoise);
state_save_register_device_item_array(device, 0, chip->Output);
state_save_register_device_item(device, 0, chip->CyclestoREADY);
}
// function parameters: device, feedback destination tap, feedback source taps, xor(false)/xnor(true), normal(false)/invert(true), mono(false)/stereo(true)
static DEVICE_START( sn76489 )
{
generic_start(device, 0x4000, 0x03, FALSE); // todo: verify; assumed to be the same as sn94624
generic_start(device, 0x8000, 0x06, FALSE, FALSE, FALSE); // todo: verify; assumed to be the same as sn94624
}
static DEVICE_START( sn76489a )
{
generic_start(device, 0x8000, 0x06, TRUE); // verified by plgdavid
generic_start(device, 0x10000, 0x0C, TRUE, FALSE, FALSE); // verified by plgdavid
}
static DEVICE_START( sn76494 )
{
generic_start(device, 0x8000, 0x06, TRUE); // todo: verify; assumed to be the same as sn76489a
generic_start(device, 0x10000, 0x0C, TRUE, FALSE, FALSE); // todo: verify; assumed to be the same as sn76489a
}
static DEVICE_START( sn76496 )
{
generic_start(device, 0x8000, 0x06, TRUE); // todo: verify; assumed to be the same as sn76489a
generic_start(device, 0x10000, 0x0C, TRUE, FALSE, FALSE); // todo: verify; assumed to be the same as sn76489a
}
static DEVICE_START( sn94624 )
{
generic_start(device, 0x4000, 0x03, FALSE); // verified by plgdavid
generic_start(device, 0x10000, 0x0C, FALSE, FALSE, FALSE); // verified by plgdavid
}
static DEVICE_START( ncr7496 )
{
generic_start(device, 0x4000, 0x11, FALSE); // todo: verify; from smspower wiki
generic_start(device, 0x8000, 0x22, FALSE, FALSE, FALSE); // todo: verify; from smspower wiki
}
static DEVICE_START( gamegear )
{
generic_start(device, 0x8000, 0x09, TRUE); // semi-verified by Lord Nightmare, need better sample
generic_start(device, 0x8000, 0x09, FALSE, TRUE, TRUE); // Verified by Justin Kerk
}
static DEVICE_START( smsiii )
{
generic_start(device, 0x8000, 0x09, TRUE); // todo: verify; from smspower wiki, assumed to have same invert as gamegear
generic_start(device, 0x8000, 0x09, FALSE, TRUE, FALSE); // todo: verify; from smspower wiki, assumed to have same invert as gamegear
}

View File

@ -5,6 +5,7 @@
READ8_DEVICE_HANDLER( sn76496_ready_r );
WRITE8_DEVICE_HANDLER( sn76496_w );
WRITE8_DEVICE_HANDLER( sn76496_stereo_w );
DEVICE_GET_INFO( sn76496 );
DEVICE_GET_INFO( sn76489 );