Add specific support for the fact that setting frequency to 0 does not behave as if frequency was set to 0x400 on the Sega-manufactured PSG clone chips, whereas it does on the original TI-made ones. Fixes Sega Master System 'Vigilante' music [Lord Nightmare, Enik]

This commit is contained in:
Jonathan Gevaryahu 2011-02-24 04:53:09 +00:00
parent a123086b67
commit d14e3c7d09

View File

@ -2,6 +2,7 @@
sn76496.c
by Nicola Salmoria
with contributions by others
Routines to emulate the:
Texas Instruments SN76489, SN76489A, SN76494/SN76496
@ -25,9 +26,11 @@
** SN76494 is the same as SN76489A but lacks the /8 divider on its clock input.
** SN76496 is identical in operation to the SN76489A, but the audio input is
documented.
All the SN7xxxx chips have an audio input line which is mixed with the 4 channels
All the TI-made PSG chips have an audio input line which is mixed with the 4 channels
of output. (It is undocumented and may not function properly on the sn76489, 76489a
and 76494; the sn76489a input is mentioned in datasheets for the tms5200)
All the TI-made PSG chips act as if the frequency was set to 0x400 if 0 is
written to the frequency register.
** Sega Master System III/MD/Genesis PSG uses a 16-bit shift register with taps
on bits C and F, output on F
It uses a 16-bit ring buffer for periodic noise/arbitrary duty cycle.
@ -39,6 +42,8 @@
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.
All the Sega-made PSG chips act as if the frequency was set to 0 if 0 is written
to the frequency register.
** 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.
@ -94,12 +99,19 @@
Fix phase of noise on sn94624 and sn76489; all chips use a standard XOR, the only inversion is the output itself - LN, Plgdavid
Thanks to PlgDavid and Michael Zapf for providing samples which helped immensely here.
23/02/2011: Lord Nightmare & Enik
Made it so the Sega PSG chips have a frequency of 0 if 0 is written to the
frequency register, while the others have 0x400 as before. Should fix a bug
or two on sega games, particularly Vigilante on Sega Master System. Verified
on SMS hardware.
TODO: * Implement the TMS9919 - any difference to sn94624?
* Implement the T6W28; has registers in a weird order, needs writes
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.
* Factor out common code so that the SAA1099 can share some code.
* Convert to modern device
***************************************************************************/
#include "emu.h"
@ -131,6 +143,7 @@ struct _sn76496_state
INT32 Count[4]; /* Position within the waveform */
INT32 Output[4]; /* 1-bit output of each channel, pre-volume */
INT32 CyclestoREADY;/* number of cycles until the READY line goes active */
INT32 Freq0IsMax; /* flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0 */
};
@ -200,8 +213,9 @@ WRITE8_DEVICE_HANDLER( sn76496_w )
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
if (R->Register[r] != 0) R->Period[c] = R->Register[r];
if ((R->Register[r] != 0) || (R->Freq0IsMax == 0)) R->Period[c] = R->Register[r];
else R->Period[c] = 0x400;
if (r == 4)
{
/* update noise shift frequency */
@ -371,6 +385,7 @@ static int SN76496_init(device_t *device, sn76496_state *R, int stereo)
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->Freq0IsMax = 1; /* frequency set to 0 results in freq = 0x400 rather than 0 */
R->RNG = R->FeedbackMask;
R->Output[3] = R->RNG & 1;
@ -379,7 +394,7 @@ static int SN76496_init(device_t *device, sn76496_state *R, int stereo)
}
static void generic_start(device_t *device, int feedbackmask, int noisetap1, int noisetap2, int negate, int stereo, int clockdivider)
static void generic_start(device_t *device, int feedbackmask, int noisetap1, int noisetap2, int negate, int stereo, int clockdivider, int freq0)
{
sn76496_state *chip = get_safe_token(device);
@ -394,6 +409,7 @@ static void generic_start(device_t *device, int feedbackmask, int noisetap1, int
chip->Stereo = stereo;
chip->ClockDivider = clockdivider;
chip->CurrentClock = clockdivider-1;
chip->Freq0IsMax = freq0;
device->save_item(NAME(chip->VolTable));
device->save_item(NAME(chip->Register));
@ -412,6 +428,7 @@ static void generic_start(device_t *device, int feedbackmask, int noisetap1, int
device->save_item(NAME(chip->Count));
device->save_item(NAME(chip->Output));
device->save_item(NAME(chip->CyclestoREADY));
device->save_item(NAME(chip->Freq0IsMax));
}
// function parameters: device, feedback destination tap, feedback source taps,
@ -419,42 +436,42 @@ static void generic_start(device_t *device, int feedbackmask, int noisetap1, int
static DEVICE_START( sn76489 )
{
generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 8); // SN76489 not verified yet. todo: verify;
generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 8, TRUE); // SN76489 not verified yet. todo: verify;
}
static DEVICE_START( sn76489a )
{
generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8); // SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid)
generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE); // SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid)
}
static DEVICE_START( sn76494 )
{
generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 1); // SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider)
generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 1, TRUE); // SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider)
}
static DEVICE_START( sn76496 )
{
generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8); // SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf)
generic_start(device, 0x10000, 0x04, 0x08, FALSE, FALSE, 8, TRUE); // SN76496: Whitenoise verified, phase verified, periodic verified (by Michael Zapf)
}
static DEVICE_START( sn94624 )
{
generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 1); // SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid
generic_start(device, 0x4000, 0x01, 0x02, TRUE, FALSE, 1, TRUE); // SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid
}
static DEVICE_START( ncr7496 )
{
generic_start(device, 0x8000, 0x02, 0x20, FALSE, FALSE, 8); // NCR7496 not verified; info from smspower wiki
generic_start(device, 0x8000, 0x02, 0x20, FALSE, FALSE, 8, TRUE); // NCR7496 not verified; info from smspower wiki
}
static DEVICE_START( gamegear )
{
generic_start(device, 0x8000, 0x01, 0x08, TRUE, TRUE, 8); // Verified by Justin Kerk
generic_start(device, 0x8000, 0x01, 0x08, TRUE, TRUE, 8, FALSE); // Verified by Justin Kerk
}
static DEVICE_START( smsiii )
{
generic_start(device, 0x8000, 0x01, 0x08, TRUE, FALSE, 8); // todo: verify; from smspower wiki, assumed to have same invert as gamegear
generic_start(device, 0x8000, 0x01, 0x08, TRUE, FALSE, 8, FALSE); // todo: verify; from smspower wiki, assumed to have same invert as gamegear
}