mirror of
https://github.com/holub/mame
synced 2025-05-20 20:58:51 +03:00
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:
parent
a123086b67
commit
d14e3c7d09
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
sn76496.c
|
sn76496.c
|
||||||
by Nicola Salmoria
|
by Nicola Salmoria
|
||||||
|
with contributions by others
|
||||||
|
|
||||||
Routines to emulate the:
|
Routines to emulate the:
|
||||||
Texas Instruments SN76489, SN76489A, SN76494/SN76496
|
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.
|
** 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
|
** SN76496 is identical in operation to the SN76489A, but the audio input is
|
||||||
documented.
|
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
|
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)
|
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
|
** 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.
|
||||||
@ -39,6 +42,8 @@
|
|||||||
for bits 7 6 5 4 3 2 1 0
|
for bits 7 6 5 4 3 2 1 0
|
||||||
L3 L2 L1 L0 R3 R2 R1 R0
|
L3 L2 L1 L0 R3 R2 R1 R0
|
||||||
Noise is an XOR function, and audio output is negated before being output.
|
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
|
** 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
|
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.
|
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
|
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.
|
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?
|
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.
|
||||||
|
* Convert to modern device
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include "emu.h"
|
#include "emu.h"
|
||||||
@ -131,6 +143,7 @@ struct _sn76496_state
|
|||||||
INT32 Count[4]; /* Position within the waveform */
|
INT32 Count[4]; /* Position within the waveform */
|
||||||
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 */
|
||||||
|
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 2: /* tone 1 : frequency */
|
||||||
case 4: /* tone 2 : frequency */
|
case 4: /* tone 2 : frequency */
|
||||||
if ((data & 0x80) == 0) R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
|
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;
|
else R->Period[c] = 0x400;
|
||||||
|
|
||||||
if (r == 4)
|
if (r == 4)
|
||||||
{
|
{
|
||||||
/* update noise shift frequency */
|
/* 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->Stereo = stereo; /* depends on init */
|
||||||
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->StereoMask = 0xFF; /* all channels enabled */
|
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->RNG = R->FeedbackMask;
|
||||||
R->Output[3] = R->RNG & 1;
|
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);
|
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->Stereo = stereo;
|
||||||
chip->ClockDivider = clockdivider;
|
chip->ClockDivider = clockdivider;
|
||||||
chip->CurrentClock = clockdivider-1;
|
chip->CurrentClock = clockdivider-1;
|
||||||
|
chip->Freq0IsMax = freq0;
|
||||||
|
|
||||||
device->save_item(NAME(chip->VolTable));
|
device->save_item(NAME(chip->VolTable));
|
||||||
device->save_item(NAME(chip->Register));
|
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->Count));
|
||||||
device->save_item(NAME(chip->Output));
|
device->save_item(NAME(chip->Output));
|
||||||
device->save_item(NAME(chip->CyclestoREADY));
|
device->save_item(NAME(chip->CyclestoREADY));
|
||||||
|
device->save_item(NAME(chip->Freq0IsMax));
|
||||||
}
|
}
|
||||||
|
|
||||||
// function parameters: device, feedback destination tap, feedback source taps,
|
// 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 )
|
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 )
|
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 )
|
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 )
|
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 )
|
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 )
|
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 )
|
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 )
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user