mirror of
https://github.com/holub/mame
synced 2025-04-24 17:30:55 +03:00
sn76496.cpp: rename NCR7496 to NCR8496 for Tandy 1000TX and similar machines based on PCB pictures and service manual parts list; add support for PSSJ-3 ASIC used in later Tandy 1000 machines. Fix noise generatior behavior in NCR8496 and PSSJ-3 to match hardware tests. [Lord Nightmare, Qbix, ValleyBell, NewRisingSun]
This commit is contained in:
parent
1b1f173aeb
commit
99682f4116
@ -46,10 +46,12 @@
|
||||
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
|
||||
** NCR8496 (as used on the Tandy 1000TX) is similar to the SN76489 but with a
|
||||
different noise LFSR pattern: taps on bits A and E, output on E, XNOR function
|
||||
It uses a 15-bit ring buffer for periodic noise/arbitrary duty cycle.
|
||||
(all this chip's info needs to be verified)
|
||||
Its output is inverted.
|
||||
** PSSJ-3 (as used on the later Tandy 1000 series computers) is the same as the
|
||||
NCR8496 with the exception that its output is not inverted.
|
||||
|
||||
28/03/2005 : Sebastien Chevalier
|
||||
Update th SN76496Write func, according to SN76489 doc found on SMSPower.
|
||||
@ -77,7 +79,7 @@
|
||||
16/11/2009 : Lord Nightmare
|
||||
Fix screeching in regulus: When summing together four equal channels, the
|
||||
size of the max amplitude per channel should be 1/4 of the max range, not
|
||||
1/3. Added NCR7496.
|
||||
1/3. Added NCR8496.
|
||||
|
||||
18/11/2009 : Lord Nightmare
|
||||
Modify Init functions to support negating the audio output. The gamegear
|
||||
@ -115,12 +117,21 @@
|
||||
ValleyBell. Made Sega PSG chips start up with register 0x3 selected (volume
|
||||
for channel 2) based on hardware tests by Nemesis.
|
||||
|
||||
03/09/2018: Lord Nightmare, Qbix, ValleyBell, NewRisingSun
|
||||
* renamed the NCR8496 to its correct name, based on chip pictures on VGMPF
|
||||
* fixed NCR8496's noise LFSR behavior so it is only reset if the mode bit in
|
||||
register 6 is changed.
|
||||
* NCR8496's LFSR feedback function is an XNOR, which is now supported.
|
||||
* add PSSJ-3 support for the later Tandy 1000 series computers.
|
||||
* NCR8496's output is inverted, PSSJ-3's output is not.
|
||||
|
||||
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.
|
||||
* verify NCR8496/PSSJ-3 behavior on write to mirrored registers; unlike the
|
||||
other variants, the NCR-derived variants are implied to ignore writes to
|
||||
regs 1,3,5,6,7 if 0x80 is not set. This needs to be verified on real hardware.
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
@ -140,6 +151,7 @@ sn76496_base_device::sn76496_base_device(
|
||||
bool negate,
|
||||
bool stereo,
|
||||
int clockdivider,
|
||||
bool ncr,
|
||||
bool sega,
|
||||
device_t *owner,
|
||||
uint32_t clock)
|
||||
@ -152,57 +164,63 @@ sn76496_base_device::sn76496_base_device(
|
||||
, m_negate(negate)
|
||||
, m_stereo(stereo)
|
||||
, m_clock_divider(clockdivider)
|
||||
, m_ncr_style_psg(ncr)
|
||||
, m_sega_style_psg(sega)
|
||||
{
|
||||
}
|
||||
|
||||
sn76496_device::sn76496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, SN76496, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
u8106_device::u8106_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, U8106, tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, U8106, tag, 0x4000, 0x01, 0x02, true, false, 8, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
y2404_device::y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, Y2404, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, Y2404, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
sn76489_device::sn76489_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, SN76489, tag, 0x4000, 0x01, 0x02, true, false, 8, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, SN76489, tag, 0x4000, 0x01, 0x02, true, false, 8, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
sn76489a_device::sn76489a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, SN76489A, tag, 0x10000, 0x04, 0x08, false, false, 8, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, SN76489A, tag, 0x10000, 0x04, 0x08, false, false, 8, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
sn76494_device::sn76494_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, SN76494, tag, 0x10000, 0x04, 0x08, false, false, 1, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, SN76494, tag, 0x10000, 0x04, 0x08, false, false, 1, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
sn94624_device::sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, SN94624, tag, 0x4000, 0x01, 0x02, true, false, 1, true, owner, clock)
|
||||
: sn76496_base_device(mconfig, SN94624, tag, 0x4000, 0x01, 0x02, true, false, 1, false, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
ncr7496_device::ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, NCR7496, tag, 0x8000, 0x02, 0x20, false, false, 8, true, owner, clock)
|
||||
ncr8496_device::ncr8496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, NCR8496, tag, 0x8000, 0x02, 0x20, true, false, 8, true, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
pssj3_device::pssj3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, PSSJ3, tag, 0x8000, 0x02, 0x20, false, false, 8, true, true, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
gamegear_device::gamegear_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, GAMEGEAR, tag, 0x8000, 0x01, 0x08, true, true, 8, false, owner, clock)
|
||||
: sn76496_base_device(mconfig, GAMEGEAR, tag, 0x8000, 0x01, 0x08, true, true, 8, false, false, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
segapsg_device::segapsg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: sn76496_base_device(mconfig, SEGAPSG, tag, 0x8000, 0x01, 0x08, true, false, 8, false, owner, clock)
|
||||
: sn76496_base_device(mconfig, SEGAPSG, tag, 0x8000, 0x01, 0x08, true, false, 8, false, false, owner, clock)
|
||||
{
|
||||
}
|
||||
|
||||
@ -294,11 +312,13 @@ void sn76496_base_device::write(uint8_t data)
|
||||
{
|
||||
r = (data & 0x70) >> 4;
|
||||
m_last_register = r;
|
||||
if (((m_ncr_style_psg) && (r == 6)) && ((data&0x04) != (m_register[6]&0x04))) m_RNG = m_feedback_mask; // NCR-style PSG resets the LFSR only on a mode write which actually changes the state of bit 2 of register 6
|
||||
m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = m_last_register;
|
||||
//if ((m_ncr_style_psg) && ((r & 1) || (r == 6))) return; // NCR-style PSG ignores writes to regs 1, 3, 5, 6 and 7 with bit 7 clear; this behavior is not verified on hardware yet, uncomment it once verified.
|
||||
}
|
||||
|
||||
c = r >> 1;
|
||||
@ -331,7 +351,7 @@ void sn76496_base_device::write(uint8_t data)
|
||||
n = m_register[6];
|
||||
// N/512,N/1024,N/2048,Tone #3 output
|
||||
m_period[3] = ((n&3) == 3)? (m_period[2]<<1) : (1 << (5+(n&3)));
|
||||
m_RNG = m_feedback_mask;
|
||||
if (!(m_ncr_style_psg)) m_RNG = m_feedback_mask;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -397,7 +417,7 @@ void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sampl
|
||||
// if noisemode is 1, both taps are enabled
|
||||
// if noisemode is 0, the lower tap, whitenoisetap2, is held at 0
|
||||
// The != was a bit-XOR (^) before
|
||||
if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=0) && in_noise_mode()))
|
||||
if (((m_RNG & m_whitenoise_tap1)!=0) != (((m_RNG & m_whitenoise_tap2)!=(m_ncr_style_psg?m_whitenoise_tap2:0)) && in_noise_mode()))
|
||||
{
|
||||
m_RNG >>= 1;
|
||||
m_RNG |= m_feedback_mask;
|
||||
@ -469,6 +489,7 @@ DEFINE_DEVICE_TYPE(SN76489, sn76489_device, "sn76489", "SN76489")
|
||||
DEFINE_DEVICE_TYPE(SN76489A, sn76489a_device, "sn76489a", "SN76489A")
|
||||
DEFINE_DEVICE_TYPE(SN76494, sn76494_device, "sn76494", "SN76494")
|
||||
DEFINE_DEVICE_TYPE(SN94624, sn94624_device, "sn94624", "SN94624")
|
||||
DEFINE_DEVICE_TYPE(NCR7496, ncr7496_device, "ncr7496", "NCR7496")
|
||||
DEFINE_DEVICE_TYPE(NCR8496, ncr8496_device, "ncr8496", "NCR8496")
|
||||
DEFINE_DEVICE_TYPE(PSSJ3, pssj3_device, "pssj3", "PSSJ-3")
|
||||
DEFINE_DEVICE_TYPE(GAMEGEAR, gamegear_device, "gamegear_psg", "Game Gear PSG")
|
||||
DEFINE_DEVICE_TYPE(SEGAPSG, segapsg_device, "segapsg", "Sega VDP PSG")
|
||||
|
@ -13,7 +13,8 @@ DECLARE_DEVICE_TYPE(SN76489, sn76489_device)
|
||||
DECLARE_DEVICE_TYPE(SN76489A, sn76489a_device)
|
||||
DECLARE_DEVICE_TYPE(SN76494, sn76494_device)
|
||||
DECLARE_DEVICE_TYPE(SN94624, sn94624_device)
|
||||
DECLARE_DEVICE_TYPE(NCR7496, ncr7496_device)
|
||||
DECLARE_DEVICE_TYPE(NCR8496, ncr8496_device)
|
||||
DECLARE_DEVICE_TYPE(PSSJ3, pssj3_device)
|
||||
DECLARE_DEVICE_TYPE(GAMEGEAR, gamegear_device)
|
||||
DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device)
|
||||
|
||||
@ -44,6 +45,7 @@ protected:
|
||||
bool negate,
|
||||
bool stereo,
|
||||
int clockdivider,
|
||||
bool ncr,
|
||||
bool sega,
|
||||
device_t *owner,
|
||||
uint32_t clock);
|
||||
@ -69,7 +71,8 @@ private:
|
||||
const bool m_negate; // output negate flag
|
||||
const bool m_stereo; // whether we're dealing with stereo or not
|
||||
const int32_t m_clock_divider; // clock divider
|
||||
const bool m_sega_style_psg; // flag for if frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; AND if the initial register is pointing to 0x3 instead of 0x0 AND if the volume reg is preloaded with 0xF instead of 0x0
|
||||
const bool m_ncr_style_psg; // flag to ignore writes to regs 1,3,5,6,7 with bit 7 low
|
||||
const bool m_sega_style_psg; // flag to make frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; the initial register is pointing to 0x3 instead of 0x0; the volume reg is preloaded with 0xF instead of 0x0
|
||||
|
||||
int32_t m_vol_table[16]; // volume table (for 4-bit to db conversion)
|
||||
int32_t m_register[8]; // registers
|
||||
@ -133,11 +136,18 @@ public:
|
||||
sn94624_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
};
|
||||
|
||||
// NCR7496 not verified; info from smspower wiki
|
||||
class ncr7496_device : public sn76496_base_device
|
||||
// NCR8496 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun
|
||||
class ncr8496_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
ncr7496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
ncr8496_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
};
|
||||
|
||||
// PSSJ-3 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun
|
||||
class pssj3_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
pssj3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
};
|
||||
|
||||
// Verified by Justin Kerk
|
||||
|
@ -564,7 +564,7 @@ void tandy1000_state::tandy1000_io(address_map &map)
|
||||
map(0x0000, 0x00ff).m(m_mb, FUNC(t1000_mb_device::map));
|
||||
map(0x0060, 0x0063).rw(FUNC(tandy1000_state::tandy1000_pio_r), FUNC(tandy1000_state::tandy1000_pio_w));
|
||||
map(0x00a0, 0x00a0).w(FUNC(tandy1000_state::nmi_vram_bank_w));
|
||||
map(0x00c0, 0x00c0).w("sn76496", FUNC(ncr7496_device::command_w));
|
||||
map(0x00c0, 0x00c0).w("sn76496", FUNC(ncr8496_device::command_w));
|
||||
map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
|
||||
map(0x0378, 0x037f).rw(FUNC(tandy1000_state::pc_t1t_p37x_r), FUNC(tandy1000_state::pc_t1t_p37x_w));
|
||||
map(0x03d0, 0x03df).r(m_video, FUNC(pcvideo_t1000_device::read)).w(m_video, FUNC(pcvideo_t1000_device::write));
|
||||
@ -591,7 +591,7 @@ void tandy1000_state::tandy1000_16_io(address_map &map)
|
||||
map(0x0060, 0x0063).rw(FUNC(tandy1000_state::tandy1000_pio_r), FUNC(tandy1000_state::tandy1000_pio_w));
|
||||
map(0x0065, 0x0065).w(FUNC(tandy1000_state::devctrl_w));
|
||||
map(0x00a0, 0x00a0).r(FUNC(tandy1000_state::unk_r));
|
||||
map(0x00c0, 0x00c1).w("sn76496", FUNC(ncr7496_device::command_w));
|
||||
map(0x00c0, 0x00c1).w("sn76496", FUNC(ncr8496_device::command_w));
|
||||
map(0x0200, 0x0207).rw("pc_joy", FUNC(pc_joy_device::joy_port_r), FUNC(pc_joy_device::joy_port_w));
|
||||
map(0x0378, 0x037f).rw(FUNC(tandy1000_state::pc_t1t_p37x_r), FUNC(tandy1000_state::pc_t1t_p37x_w));
|
||||
map(0x03d0, 0x03df).r(m_video, FUNC(pcvideo_t1000_device::read)).w(m_video, FUNC(pcvideo_t1000_device::write));
|
||||
@ -659,7 +659,7 @@ MACHINE_CONFIG_START(tandy1000_state::tandy1000_common)
|
||||
MCFG_DEVICE_ADD("gfxdecode", GFXDECODE, "pcvideo_t1000:palette", gfx_t1000)
|
||||
|
||||
/* sound hardware */
|
||||
MCFG_DEVICE_ADD("sn76496", NCR7496, XTAL(14'318'181)/4)
|
||||
MCFG_DEVICE_ADD("sn76496", NCR8496, XTAL(14'318'181)/4)
|
||||
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mb:mono", 0.80)
|
||||
|
||||
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
|
||||
|
Loading…
Reference in New Issue
Block a user