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.
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.
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.
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.
@ -37,13 +39,19 @@
Add READY line readback; cleaned up struct a bit. Cleaned up comments.
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.
Requires making the core support both mono and stereo, and have
a select register which determines which channels go where.
* Implement the TMS9919 and SN94624, which are earlier versions,
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
* 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.
* Implement the T6W28; has registers in a weird order, needs writes
to be 'sanitized' first. Also is stereo, similar to game gear.
* Implement the NCR 7496; Is probably 100% compatible with SN76496,
@ -73,9 +81,10 @@ struct _sn76496_state
UINT32 RNG; /* noise generator LFSR*/
INT32 FeedbackMask; /* mask for feedback */
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 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 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 (((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 |= R->FeedbackMask;
@ -256,7 +265,6 @@ static STREAM_UPDATE( SN76496Update )
{
R->RNG >>= 1;
}
R->Output[3] = R->WhitenoiseInvert ? !(R->RNG & 1) : R->RNG & 1;
}
else /* Periodic noise mode */
{
@ -269,8 +277,9 @@ static STREAM_UPDATE( SN76496Update )
{
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];
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 */
R->FeedbackMask = 0x4000; /* mask for feedback */
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->RNG = R->FeedbackMask;
R->Output[3] = R->RNG & 1;
R->OldNoise = 0;
R->Output[3] = R->OldNoise;
R->OldNoise = R->RNG & 1;
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);
@ -364,7 +375,7 @@ static void generic_start(const device_config *device, int feedbackmask, int noi
chip->FeedbackMask = feedbackmask;
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->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->FeedbackMask);
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->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);
}
@ -383,32 +395,37 @@ static void generic_start(const device_config *device, int feedbackmask, int noi
static DEVICE_START( sn76489 )
{
generic_start(device, 0x4000, 0x03, TRUE);
generic_start(device, 0x4000, 0x03, FALSE);
}
static DEVICE_START( sn76489a )
{
generic_start(device, 0x8000, 0x06, FALSE);
generic_start(device, 0x8000, 0x06, TRUE);
}
static DEVICE_START( sn76494 )
{
generic_start(device, 0x8000, 0x06, FALSE);
generic_start(device, 0x8000, 0x06, TRUE);
}
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 )
{
generic_start(device, 0x8000, 0x09, FALSE);
generic_start(device, 0x8000, 0x09, TRUE);
}
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 )
{
switch (state)

View File

@ -10,6 +10,7 @@ DEVICE_GET_INFO( sn76496 );
DEVICE_GET_INFO( sn76489 );
DEVICE_GET_INFO( sn76489a );
DEVICE_GET_INFO( sn76494 );
DEVICE_GET_INFO( sn94624 );
DEVICE_GET_INFO( gamegear );
DEVICE_GET_INFO( smsiii );
@ -17,6 +18,7 @@ DEVICE_GET_INFO( smsiii );
#define SOUND_SN76489 DEVICE_GET_INFO_NAME( sn76489 )
#define SOUND_SN76489A DEVICE_GET_INFO_NAME( sn76489a )
#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_SMSIII DEVICE_GET_INFO_NAME( smsiii )