From 99682f41167b4bb9f187df2eab68aa3d47e1319a Mon Sep 17 00:00:00 2001 From: Lord-Nightmare Date: Mon, 3 Sep 2018 12:33:58 -0400 Subject: 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] --- src/devices/sound/sn76496.cpp | 61 +++++++++++++++++++++++++++++-------------- src/devices/sound/sn76496.h | 20 ++++++++++---- src/mame/drivers/tandy1t.cpp | 6 ++--- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/devices/sound/sn76496.cpp b/src/devices/sound/sn76496.cpp index 87f1a95486e..23b29c208e3 100644 --- a/src/devices/sound/sn76496.cpp +++ b/src/devices/sound/sn76496.cpp @@ -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) +{ +} + +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) { } -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) +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") diff --git a/src/devices/sound/sn76496.h b/src/devices/sound/sn76496.h index 50ca44a3e1f..741b6b0bcd7 100644 --- a/src/devices/sound/sn76496.h +++ b/src/devices/sound/sn76496.h @@ -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 diff --git a/src/mame/drivers/tandy1t.cpp b/src/mame/drivers/tandy1t.cpp index 800225b2817..e476f0a63c3 100644 --- a/src/mame/drivers/tandy1t.cpp +++ b/src/mame/drivers/tandy1t.cpp @@ -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); -- cgit v1.2.3