Fixed sn76489a periodic noise once again (and simplified a bit of code), now should be closer to or match real chips as sampled.

Added true clock divider emulation, so emulation may be a bit slower. [Lord Nightmare, PlgDavid, Kold666]
This commit is contained in:
Jonathan Gevaryahu 2010-01-24 10:38:09 +00:00
parent 730d70ee06
commit f79dfb098a

View File

@ -19,8 +19,10 @@
** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F, ** SN76489A uses a 15-bit shift register with taps on bits D and E, output on F,
XNOR function XNOR function
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.
** SN76494 and SN76496 are PROBABLY identical in operation to the SN76489A ** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input.
They have an audio input line which is mixed with the 4 channels of output. ** SN76496 is PROBABLY identical in operation to the SN76489A, need more info.
All the SN7xxxx chips have an audio input line which is mixed with the 4 channels
of output.
** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps ** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps
on bits C and F, output on F on bits C and F, output on F
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.
@ -70,24 +72,23 @@
based on testing. Got rid of R->OldNoise and fixed taps accordingly. based on testing. Got rid of R->OldNoise and fixed taps accordingly.
Added stereo support for game gear. Added stereo support for game gear.
11/15/2010 : Lord Nightmare 15/01/2010 : Lord Nightmare
Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods. Fix an issue with SN76489 and SN76489A having the wrong periodic noise periods.
Note that properly emulating the noise cycle bit timing accurately may require Note that properly emulating the noise cycle bit timing accurately may require
extensive rewriting. extensive rewriting.
TODO: * Implement the TMS9919 which is an earlier version, possibly 24/01/2010: Lord Nightmare
lacking the /8 clock divider, of the SN76489, and hence would Implement periodic noise as forcing one of the XNOR or XOR taps to 1 or 0 respectively.
have a max clock of 500Khz and 4 clocks per sample, as opposed to Thanks to PlgDavid for providing samples which helped immensely here.
max of 4Mhz and 32 clocks per sample on the SN76489A. Added true clock divider emulation, so sn94624 and sn76494 run 8x faster than
the others, as in real life.
TODO: * Implement the TMS9919 - any difference to sn94624?
* 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.
* Test the NCR7496; Smspower says the whitenoise taps are A and E, * Test the NCR7496; Smspower says the whitenoise taps are A and E,
but this needs verification on real hardware. but this needs verification on real hardware.
* Factor out common code so that the SAA1099 can share some code. * Factor out common code so that the SAA1099 can share some code.
* Implement periodic noise as using a pointer to a table rather than a
self feeding lfsr. Kold666's recording of dorunrun proves this is the
right way.
* Fix the whitenoise bit timing so that dorunrun matches Kold666's PCB sample.
***************************************************************************/ ***************************************************************************/
#include "emu.h" #include "emu.h"
@ -96,7 +97,6 @@
#define MAX_OUTPUT 0x7fff #define MAX_OUTPUT 0x7fff
// make the above 0x3fff if using bipolar output
#define NOISEMODE (R->Register[6]&4)?1:0 #define NOISEMODE (R->Register[6]&4)?1:0
@ -109,8 +109,11 @@ struct _sn76496_state
INT32 LastRegister; /* last register written */ INT32 LastRegister; /* last register written */
INT32 Volume[4]; /* db volume of voice 0-2 and noise */ INT32 Volume[4]; /* db volume of voice 0-2 and noise */
UINT32 RNG; /* noise generator LFSR*/ UINT32 RNG; /* noise generator LFSR*/
INT32 ClockDivider; /* clock divider */
INT32 CurrentClock;
INT32 FeedbackMask; /* mask for feedback */ INT32 FeedbackMask; /* mask for feedback */
INT32 WhitenoiseTaps; /* mask for white noise taps */ INT32 WhitenoiseTap1; /* mask for white noise tap 1 (higher one, usually bit 14) */
INT32 WhitenoiseTap2; /* mask for white noise tap 2 (lower one, usually bit 13)*/
INT32 FeedbackInvert; /* feedback invert flag (xor vs xnor) */ INT32 FeedbackInvert; /* feedback invert flag (xor vs xnor) */
INT32 Negate; /* output negate flag */ INT32 Negate; /* output negate flag */
INT32 Stereo; /* whether we're dealing with stereo or not */ INT32 Stereo; /* whether we're dealing with stereo or not */
@ -207,11 +210,11 @@ WRITE8_DEVICE_HANDLER( sn76496_w )
break; break;
case 6: /* noise : frequency, mode */ case 6: /* noise : frequency, mode */
{ {
if ((data & 0x80) == 0) logerror("sn76489: write to reg 6 with bit 7 clear; data was %03x, new write is %02x! report this to LN!\n", R->Register[6], data);
if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f); if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
n = R->Register[6]; n = R->Register[6];
/* N/512,N/1024,N/2048,Tone #3 output */ /* N/512,N/1024,N/2048,Tone #3 output */
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (1 << (5+(n&3))); R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (1 << (5+(n&3)));
/* Reset noise shifter */
R->RNG = R->FeedbackMask; R->RNG = R->FeedbackMask;
R->Output[3] = R->RNG & 1; R->Output[3] = R->RNG & 1;
} }
@ -225,13 +228,19 @@ static STREAM_UPDATE( SN76496Update )
sn76496_state *R = (sn76496_state *)param; sn76496_state *R = (sn76496_state *)param;
stream_sample_t *lbuffer = outputs[0]; stream_sample_t *lbuffer = outputs[0];
stream_sample_t *rbuffer = (R->Stereo)?outputs[1]:NULL; stream_sample_t *rbuffer = (R->Stereo)?outputs[1]:NULL;
INT16 out = 0;
INT16 out2 = 0;
while (samples > 0) while (samples > 0)
{ {
// clock chip once // clock chip once
INT16 out = 0; if (R->CurrentClock > 0) // not ready for new divided clock
INT16 out2 = 0; {
R->CurrentClock--;
}
else // ready for new divided clock, make a new sample
{
R->CurrentClock = R->ClockDivider-1;
/* decrement Cycles to READY by one */ /* decrement Cycles to READY by one */
if (R->CyclestoREADY >0) R->CyclestoREADY--; if (R->CyclestoREADY >0) R->CyclestoREADY--;
@ -250,9 +259,9 @@ static STREAM_UPDATE( SN76496Update )
R->Count[3]--; R->Count[3]--;
if (R->Count[3] <= 0) if (R->Count[3] <= 0)
{ {
if (NOISEMODE == 1) /* White Noise Mode */ // if noisemode is 1, both taps are enabled
{ // if noisemode is 0, the lower tap, whitenoisetap2, is held at 1 or 0 depending on whether FeedbackInvert is set or clear
if ((((R->RNG & R->WhitenoiseTaps) != R->WhitenoiseTaps) && ((R->RNG & R->WhitenoiseTaps) != 0)) ^ R->FeedbackInvert ) /* XOR or XNOR */ if (((R->RNG & R->WhitenoiseTap1)?1:0) ^ (((((R->RNG & R->WhitenoiseTap2)?0:1))*(NOISEMODE))^R->FeedbackInvert) ^ R->FeedbackInvert )
{ {
R->RNG >>= 1; R->RNG >>= 1;
R->RNG |= R->FeedbackMask; R->RNG |= R->FeedbackMask;
@ -261,28 +270,13 @@ static STREAM_UPDATE( SN76496Update )
{ {
R->RNG >>= 1; R->RNG >>= 1;
} }
}
else /* Periodic noise mode */
{
if (R->RNG & 1)
{
R->RNG >>= 1;
R->RNG |= R->FeedbackMask;
}
else
{
R->RNG >>= 1;
}
}
R->Output[3] = R->RNG & 1; R->Output[3] = R->RNG & 1;
R->Count[3] = R->Period[3]; R->Count[3] = R->Period[3];
} }
/* //bipolar output, doesn't seem to work right with sonic 2 on gamegear at least }
out = (R->Output[0]?R->Volume[0]:(0-R->Volume[0]))
+(R->Output[1]?R->Volume[1]:(0-R->Volume[1]))
+(R->Output[2]?R->Volume[2]:(0-R->Volume[2]))
+(R->Output[3]?R->Volume[3]:(0-R->Volume[3]));
*/
if (R->Stereo) if (R->Stereo)
{ {
out = (((R->StereoMask&0x10)&&R->Output[0])?R->Volume[0]:0) out = (((R->StereoMask&0x10)&&R->Output[0])?R->Volume[0]:0)
@ -342,7 +336,7 @@ static void SN76496_set_gain(sn76496_state *R,int gain)
static int SN76496_init(running_device *device, sn76496_state *R, int stereo) static int SN76496_init(running_device *device, sn76496_state *R, int stereo)
{ {
int sample_rate = device->clock/16; int sample_rate = device->clock/2;
int i; int i;
R->Channel = stream_create(device,0,(stereo?2:1),sample_rate,R,SN76496Update); R->Channel = stream_create(device,0,(stereo?2:1),sample_rate,R,SN76496Update);
@ -361,13 +355,15 @@ static int SN76496_init(running_device *device, sn76496_state *R, int stereo)
R->Output[i] = R->Period[i] = R->Count[i] = 0; R->Output[i] = R->Period[i] = R->Count[i] = 0;
} }
/* Default is SN76489 non-A */ /* Default is SN76489A */
R->FeedbackMask = 0x4000; /* mask for feedback */ R->ClockDivider = 8;
R->WhitenoiseTaps = 0x03; /* mask for white noise taps */ R->FeedbackMask = 0x10000; /* mask for feedback */
R->FeedbackInvert = 0; /* feedback invert flag */ R->WhitenoiseTap1 = 0x04; /* mask for white noise tap 1*/
R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/ R->WhitenoiseTap2 = 0x08; /* mask for white noise tap 2*/
R->FeedbackInvert = 1; /* feedback invert flag */
R->Negate = 0; /* channels are not negated */ R->Negate = 0; /* channels are not negated */
R->Stereo = stereo; /* depends on init */ R->Stereo = stereo; /* depends on init */
R->CyclestoREADY = 1; /* assume ready is not active immediately on init. is this correct?*/
R->StereoMask = 0xFF; /* all channels enabled */ R->StereoMask = 0xFF; /* all channels enabled */
R->RNG = R->FeedbackMask; R->RNG = R->FeedbackMask;
@ -377,7 +373,7 @@ static int SN76496_init(running_device *device, sn76496_state *R, int stereo)
} }
static void generic_start(running_device *device, int feedbackmask, int noisetaps, int feedbackinvert, int negate, int stereo) static void generic_start(running_device *device, int feedbackmask, int noisetap1, int noisetap2, int feedbackinvert, int negate, int stereo, int clockdivider)
{ {
sn76496_state *chip = get_safe_token(device); sn76496_state *chip = get_safe_token(device);
@ -386,18 +382,24 @@ static void generic_start(running_device *device, int feedbackmask, int noisetap
SN76496_set_gain(chip, 0); SN76496_set_gain(chip, 0);
chip->FeedbackMask = feedbackmask; chip->FeedbackMask = feedbackmask;
chip->WhitenoiseTaps = noisetaps; chip->WhitenoiseTap1 = noisetap1;
chip->WhitenoiseTap2 = noisetap2;
chip->FeedbackInvert = feedbackinvert; chip->FeedbackInvert = feedbackinvert;
chip->Negate = negate; chip->Negate = negate;
chip->Stereo = stereo; chip->Stereo = stereo;
chip->ClockDivider = clockdivider;
chip->CurrentClock = clockdivider-1;
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);
state_save_register_device_item(device, 0, chip->LastRegister); state_save_register_device_item(device, 0, chip->LastRegister);
state_save_register_device_item_array(device, 0, chip->Volume); state_save_register_device_item_array(device, 0, chip->Volume);
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->ClockDivider);
state_save_register_device_item(device, 0, chip->CurrentClock);
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->WhitenoiseTap1);
state_save_register_device_item(device, 0, chip->WhitenoiseTap2);
state_save_register_device_item(device, 0, chip->FeedbackInvert); 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->Negate);
state_save_register_device_item(device, 0, chip->Stereo); state_save_register_device_item(device, 0, chip->Stereo);
@ -408,46 +410,46 @@ static void generic_start(running_device *device, int feedbackmask, int noisetap
state_save_register_device_item(device, 0, chip->CyclestoREADY); 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) // function parameters: device, feedback destination tap, feedback source taps, xor(false)/xnor(true), normal(false)/invert(true), mono(false)/stereo(true), clock divider factor
static DEVICE_START( sn76489 ) static DEVICE_START( sn76489 )
{ {
generic_start(device, 0x4000, 0x03, FALSE, FALSE, FALSE); // todo: verify; assumed to be the same as sn94624 generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, FALSE, 8); // SN76489 not verified yet. todo: verify;
} }
static DEVICE_START( sn76489a ) static DEVICE_START( sn76489a )
{ {
generic_start(device, 0x4000, 0x03, TRUE, FALSE, FALSE); // verified by plgdavid generic_start(device, 0x10000, 0x04, 0x08, TRUE, FALSE, FALSE, 8); // SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid)
} }
static DEVICE_START( sn76494 ) static DEVICE_START( sn76494 )
{ {
generic_start(device, 0x4000, 0x03, TRUE, FALSE, FALSE); // todo: verify; assumed to be the same as sn76489a generic_start(device, 0x10000, 0x04, 0x08, TRUE, FALSE, FALSE, 1); // SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider)
} }
static DEVICE_START( sn76496 ) static DEVICE_START( sn76496 )
{ {
generic_start(device, 0x4000, 0x03, TRUE, FALSE, FALSE); // todo: verify; assumed to be the same as sn76489a generic_start(device, 0x10000, 0x04, 0x08, TRUE, FALSE, FALSE, 8); // SN76496 not verified; assumed to be the same as sn76489a
} }
static DEVICE_START( sn94624 ) static DEVICE_START( sn94624 )
{ {
generic_start(device, 0x4000, 0x03, FALSE, FALSE, FALSE); // verified by plgdavid generic_start(device, 0x4000, 0x01, 0x02, FALSE, TRUE, FALSE, 1); // SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid
} }
static DEVICE_START( ncr7496 ) static DEVICE_START( ncr7496 )
{ {
generic_start(device, 0x8000, 0x22, FALSE, FALSE, FALSE); // todo: verify; from smspower wiki generic_start(device, 0x8000, 0x02, 0x20, FALSE, FALSE, FALSE, 8); // NCR7496 not verified; info from smspower wiki
} }
static DEVICE_START( gamegear ) static DEVICE_START( gamegear )
{ {
generic_start(device, 0x8000, 0x09, FALSE, TRUE, TRUE); // Verified by Justin Kerk generic_start(device, 0x8000, 0x01, 0x08, FALSE, TRUE, TRUE, 8); // Verified by Justin Kerk
} }
static DEVICE_START( smsiii ) static DEVICE_START( smsiii )
{ {
generic_start(device, 0x8000, 0x09, FALSE, TRUE, FALSE); // todo: verify; from smspower wiki, assumed to have same invert as gamegear generic_start(device, 0x8000, 0x01, 0x08, FALSE, TRUE, FALSE, 8); // todo: verify; from smspower wiki, assumed to have same invert as gamegear
} }