summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Lord-Nightmare <Lord-Nightmare@users.noreply.github.com>2018-09-03 12:33:58 -0400
committer Lord-Nightmare <Lord-Nightmare@users.noreply.github.com>2018-09-03 12:33:58 -0400
commit99682f41167b4bb9f187df2eab68aa3d47e1319a (patch)
treecc09624b038a7005f459561c3dc193d8d6c1c5f1
parent1b1f173aebb5e9ac7c954e4f3d6d699a08702f27 (diff)
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]
-rw-r--r--src/devices/sound/sn76496.cpp61
-rw-r--r--src/devices/sound/sn76496.h20
-rw-r--r--src/mame/drivers/tandy1t.cpp6
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);