Update SN76489/A to have correct PRNG waveforms (a 0 bit was missing at the beginning, and beginning of SN76489 waveform was all 1s instead of 0s as measured on hardware). Add SN94624 (same as SN76489). [Lord Nightmare, plgDavid (David Viens)]

This commit is contained in:
Jonathan Gevaryahu 2009-11-04 18:21:30 +00:00
parent 45dfc2315a
commit d3a8485a6a
2 changed files with 50 additions and 21 deletions

View File

@ -12,9 +12,11 @@
duty cycle channel. duty cycle channel.
Noise emulation for all chips should be accurate: Noise emulation for all chips should be accurate:
SN76489 uses a 15-bit shift register with taps on bits D and E, output on /E SN76489 uses a 15-bit shift register with taps on bits D and E, output on E,
XOR function; SN94624 is identical to SN76489.
* It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle. * It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
SN76489A uses a 16-bit shift register with taps on bits D and F, output on F SN76489A uses a 16-bit shift register with taps on bits D and E, output on F,
XNOR function
* It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle. * It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle.
SN76494 and SN76496 are PROBABLY identical in operation to the SN76489A SN76494 and SN76496 are PROBABLY identical in operation to the SN76489A
* They have an audio input line which is mixed with the 4 channels of output. * They have an audio input line which is mixed with the 4 channels of output.
@ -37,13 +39,19 @@
Add READY line readback; cleaned up struct a bit. Cleaned up comments. Add READY line readback; cleaned up struct a bit. Cleaned up comments.
Add more TODOs. Fixed some unsaved savestate related stuff. Add more TODOs. Fixed some unsaved savestate related stuff.
04/11/2009 : Lord Nightmare
Changed the way that the invert works (it now selects between XOR and XNOR
for the taps), and added R->OldNoise to simulate the extra 0 that is always
output before the noise LFSR contents are after an LFSR reset.
This fixes SN76489/A to match chips. Added SN94624.
TODO: * Implement a function for setting stereo regs for the 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 Requires making the core support both mono and stereo, and have
a select register which determines which channels go where. a select register which determines which channels go where.
* Implement the TMS9919 and SN94624, which are earlier versions, * Implement the TMS9919 which is an earlier version, possibly
possibly lacking the /8 clock divider, of the SN76489, and hence lacking the /8 clock divider, of the SN76489, and hence would
would have a max clock of 500Khz and 4 clocks per sample, as have a max clock of 500Khz and 4 clocks per sample, as opposed to
opposed to max of 4Mhz and 32 clocks per sample on the SN76489A max of 4Mhz and 32 clocks per sample on the SN76489A.
* Implement the T6W28; has registers in a weird order, needs writes * Implement the T6W28; has registers in a weird order, needs writes
to be 'sanitized' first. Also is stereo, similar to game gear. to be 'sanitized' first. Also is stereo, similar to game gear.
* Implement the NCR 7496; Is probably 100% compatible with SN76496, * Implement the NCR 7496; Is probably 100% compatible with SN76496,
@ -73,9 +81,10 @@ struct _sn76496_state
UINT32 RNG; /* noise generator LFSR*/ UINT32 RNG; /* noise generator LFSR*/
INT32 FeedbackMask; /* mask for feedback */ INT32 FeedbackMask; /* mask for feedback */
INT32 WhitenoiseTaps; /* mask for white noise taps */ INT32 WhitenoiseTaps; /* mask for white noise taps */
INT32 WhitenoiseInvert; /* white noise invert flag */ INT32 FeedbackInvert; /* feedback invert flag (xor vs xnor) */
INT32 Period[4]; /* Length of 1/2 of waveform */ INT32 Period[4]; /* Length of 1/2 of waveform */
INT32 Count[4]; /* Position within the waveform */ INT32 Count[4]; /* Position within the waveform */
INT32 OldNoise; /* 1 bit output of the PREVIOUS noise output, buffered by the volume amp */
INT32 Output[4]; /* 1-bit output of each channel, pre-volume */ INT32 Output[4]; /* 1-bit output of each channel, pre-volume */
INT32 CyclestoREADY;/* number of cycles until the READY line goes active */ INT32 CyclestoREADY;/* number of cycles until the READY line goes active */
}; };
@ -247,7 +256,7 @@ static STREAM_UPDATE( SN76496Update )
{ {
if (NOISEMODE == 1) /* White Noise Mode */ if (NOISEMODE == 1) /* White Noise Mode */
{ {
if (((R->RNG & R->WhitenoiseTaps) != R->WhitenoiseTaps) && ((R->RNG & R->WhitenoiseTaps) != 0)) /* crappy xor! */ if ((((R->RNG & R->WhitenoiseTaps) != R->WhitenoiseTaps) && ((R->RNG & R->WhitenoiseTaps) != 0)) ^ R->FeedbackInvert ) /* XOR or XNOR */
{ {
R->RNG >>= 1; R->RNG >>= 1;
R->RNG |= R->FeedbackMask; R->RNG |= R->FeedbackMask;
@ -256,7 +265,6 @@ static STREAM_UPDATE( SN76496Update )
{ {
R->RNG >>= 1; R->RNG >>= 1;
} }
R->Output[3] = R->WhitenoiseInvert ? !(R->RNG & 1) : R->RNG & 1;
} }
else /* Periodic noise mode */ else /* Periodic noise mode */
{ {
@ -269,8 +277,9 @@ static STREAM_UPDATE( SN76496Update )
{ {
R->RNG >>= 1; R->RNG >>= 1;
} }
R->Output[3] = R->RNG & 1;
} }
R->Output[3] = R->OldNoise;
R->OldNoise = R->RNG & 1;
R->Count[3] += R->Period[3]; R->Count[3] += R->Period[3];
if (R->Output[3]) vol[3] += R->Period[3]; if (R->Output[3]) vol[3] += R->Period[3];
} }
@ -344,17 +353,19 @@ static int SN76496_init(const device_config *device, sn76496_state *R)
/* Default is SN76489 non-A */ /* Default is SN76489 non-A */
R->FeedbackMask = 0x4000; /* mask for feedback */ R->FeedbackMask = 0x4000; /* mask for feedback */
R->WhitenoiseTaps = 0x03; /* mask for white noise taps */ R->WhitenoiseTaps = 0x03; /* mask for white noise taps */
R->WhitenoiseInvert = 1; /* white noise invert flag */ R->FeedbackInvert = 0; /* feedback invert flag */
R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/ R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/
R->RNG = R->FeedbackMask; R->RNG = R->FeedbackMask;
R->Output[3] = R->RNG & 1; R->OldNoise = 0;
R->Output[3] = R->OldNoise;
R->OldNoise = R->RNG & 1;
return 0; return 0;
} }
static void generic_start(const device_config *device, int feedbackmask, int noisetaps, int noiseinvert) static void generic_start(const device_config *device, int feedbackmask, int noisetaps, int feedbackinvert)
{ {
sn76496_state *chip = get_safe_token(device); sn76496_state *chip = get_safe_token(device);
@ -364,7 +375,7 @@ static void generic_start(const device_config *device, int feedbackmask, int noi
chip->FeedbackMask = feedbackmask; chip->FeedbackMask = feedbackmask;
chip->WhitenoiseTaps = noisetaps; chip->WhitenoiseTaps = noisetaps;
chip->WhitenoiseInvert = noiseinvert; chip->FeedbackInvert = feedbackinvert;
state_save_register_device_item_array(device, 0, chip->VolTable); state_save_register_device_item_array(device, 0, chip->VolTable);
state_save_register_device_item_array(device, 0, chip->Register); state_save_register_device_item_array(device, 0, chip->Register);
@ -373,9 +384,10 @@ static void generic_start(const device_config *device, int feedbackmask, int noi
state_save_register_device_item(device, 0, chip->RNG); state_save_register_device_item(device, 0, chip->RNG);
state_save_register_device_item(device, 0, chip->FeedbackMask); 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->WhitenoiseTaps);
state_save_register_device_item(device, 0, chip->WhitenoiseInvert); state_save_register_device_item(device, 0, chip->FeedbackInvert);
state_save_register_device_item_array(device, 0, chip->Period); 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_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_array(device, 0, chip->Output);
state_save_register_device_item(device, 0, chip->CyclestoREADY); state_save_register_device_item(device, 0, chip->CyclestoREADY);
} }
@ -383,32 +395,37 @@ static void generic_start(const device_config *device, int feedbackmask, int noi
static DEVICE_START( sn76489 ) static DEVICE_START( sn76489 )
{ {
generic_start(device, 0x4000, 0x03, TRUE); generic_start(device, 0x4000, 0x03, FALSE);
} }
static DEVICE_START( sn76489a ) static DEVICE_START( sn76489a )
{ {
generic_start(device, 0x8000, 0x06, FALSE); generic_start(device, 0x8000, 0x06, TRUE);
} }
static DEVICE_START( sn76494 ) static DEVICE_START( sn76494 )
{ {
generic_start(device, 0x8000, 0x06, FALSE); generic_start(device, 0x8000, 0x06, TRUE);
} }
static DEVICE_START( sn76496 ) static DEVICE_START( sn76496 )
{ {
generic_start(device, 0x8000, 0x06, FALSE); generic_start(device, 0x8000, 0x06, TRUE);
}
static DEVICE_START( sn94624 )
{
generic_start(device, 0x4000, 0x03, FALSE);
} }
static DEVICE_START( gamegear ) static DEVICE_START( gamegear )
{ {
generic_start(device, 0x8000, 0x09, FALSE); generic_start(device, 0x8000, 0x09, TRUE);
} }
static DEVICE_START( smsiii ) static DEVICE_START( smsiii )
{ {
generic_start(device, 0x8000, 0x09, FALSE); generic_start(device, 0x8000, 0x09, TRUE);
} }
@ -447,6 +464,16 @@ DEVICE_GET_INFO( sn76489 )
} }
} }
DEVICE_GET_INFO( sn94624 )
{
switch (state)
{
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( sn94624 ); break;
case DEVINFO_STR_NAME: strcpy(info->s, "SN94624"); break;
default: DEVICE_GET_INFO_CALL(sn76496); break;
}
}
DEVICE_GET_INFO( sn76489a ) DEVICE_GET_INFO( sn76489a )
{ {
switch (state) switch (state)

View File

@ -10,6 +10,7 @@ DEVICE_GET_INFO( sn76496 );
DEVICE_GET_INFO( sn76489 ); DEVICE_GET_INFO( sn76489 );
DEVICE_GET_INFO( sn76489a ); DEVICE_GET_INFO( sn76489a );
DEVICE_GET_INFO( sn76494 ); DEVICE_GET_INFO( sn76494 );
DEVICE_GET_INFO( sn94624 );
DEVICE_GET_INFO( gamegear ); DEVICE_GET_INFO( gamegear );
DEVICE_GET_INFO( smsiii ); DEVICE_GET_INFO( smsiii );
@ -17,6 +18,7 @@ DEVICE_GET_INFO( smsiii );
#define SOUND_SN76489 DEVICE_GET_INFO_NAME( sn76489 ) #define SOUND_SN76489 DEVICE_GET_INFO_NAME( sn76489 )
#define SOUND_SN76489A DEVICE_GET_INFO_NAME( sn76489a ) #define SOUND_SN76489A DEVICE_GET_INFO_NAME( sn76489a )
#define SOUND_SN76494 DEVICE_GET_INFO_NAME( sn76494 ) #define SOUND_SN76494 DEVICE_GET_INFO_NAME( sn76494 )
#define SOUND_SN94624 DEVICE_GET_INFO_NAME( sn94624 )
#define SOUND_GAMEGEAR DEVICE_GET_INFO_NAME( gamegear ) #define SOUND_GAMEGEAR DEVICE_GET_INFO_NAME( gamegear )
#define SOUND_SMSIII DEVICE_GET_INFO_NAME( smsiii ) #define SOUND_SMSIII DEVICE_GET_INFO_NAME( smsiii )