diff --git a/src/hardware/mame/sn76496.cpp b/src/hardware/mame/sn76496.cpp index b2e3be6a..d11f0eaf 100644 --- a/src/hardware/mame/sn76496.cpp +++ b/src/hardware/mame/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 1000) 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,11 +117,21 @@ ValleyBell. Made Sega PSG chips start up with register 0x3 selected (volume for channel 2) based on hardware tests by Nemesis. + 26/08/2018: Lord Nightmare, Qbix, ValleyBell, NewRisingSun + * renamed the NCR8496 to its correct name, based on chip pictures on VGMPF + * fixed NCR8496 behavior on write to mirrored registers; unlike any of the + other variants, the NCR8496 seems to ignore writes to regs 1,3,5,6,7 if 0x80 + is not set. +***TODO: the above is NOT verified yet!*** + * 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 + * NCR8496's output is inverted (though PSSJ-3's output is not) + * add PSSJ-3 support for the later Tandy computers. + 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. ***************************************************************************/ @@ -141,6 +153,7 @@ sn76496_base_device::sn76496_base_device( bool negate, bool stereo, int clockdivider, + bool ncr, bool sega, device_t *owner, uint32_t clock) @@ -153,58 +166,64 @@ 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) { } @@ -271,6 +290,11 @@ void sn76496_base_device::device_start() //register_for_save_states(); } +void sn76496_base_device::device_clock_changed() +{ +// m_sound->set_sample_rate(clock()/2); +} + WRITE8_MEMBER( sn76496_base_device::stereo_w ) { // m_sound->update(); @@ -293,11 +317,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; m_register[r] = (m_register[r] & 0x3f0) | (data & 0x0f); } else { r = m_last_register; + if ((m_ncr_style_psg) && ((r & 1) || (r == 6))) return; // NCR8496 ignores writes to regs 1, 3, 5, 6 and 7 with bit 7 clear } c = r >> 1; @@ -330,7 +356,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; } @@ -365,7 +391,7 @@ void sn76496_base_device::sound_stream_update(sound_stream &stream, stream_sampl { int i; stream_sample_t *lbuffer = outputs[0]; - stream_sample_t *rbuffer = (m_stereo)? outputs[1] : 0; + stream_sample_t *rbuffer = (m_stereo)? outputs[1] : 0;//nullptr; int16_t out; int16_t out2 = 0; @@ -401,7 +427,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; @@ -485,7 +511,8 @@ 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/hardware/mame/sn76496.h b/src/hardware/mame/sn76496.h index a2bdb993..b464ba78 100644 --- a/src/hardware/mame/sn76496.h +++ b/src/hardware/mame/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) @@ -21,22 +22,23 @@ DECLARE_DEVICE_TYPE(SEGAPSG, segapsg_device) #define MCFG_SN76496_READY_HANDLER(cb) \ - devcb = &sn76496_base_device::set_ready_handler(*device, (DEVCB_##cb)); + downcast(*device).set_ready_handler((DEVCB_##cb)); #endif class sn76496_base_device : public device_t, public device_sound_interface { public: - // static configuration helpers -// template static devcb_base &set_ready_handler(device_t &device, Object &&cb) { return downcast(device).m_ready_handler.set_callback(std::forward(cb)); } + // configuration helpers +// template devcb_base &set_ready_handler(Object &&cb) { return m_ready_handler.set_callback(std::forward(cb)); } DECLARE_WRITE8_MEMBER( stereo_w ); void write(uint8_t data); DECLARE_WRITE8_MEMBER( write ); // DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; } +// auto ready_cb() { return m_ready_handler.bind(); } - void convert_samplerate(int32_t target_rate); + void convert_samplerate(int32_t target_rate); protected: sn76496_base_device( const machine_config &mconfig, @@ -48,11 +50,13 @@ protected: bool negate, bool stereo, int clockdivider, + bool ncr, bool sega, device_t *owner, uint32_t clock); virtual void device_start(); + virtual void device_clock_changed(); virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); private: @@ -74,7 +78,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 @@ -87,6 +92,7 @@ private: int32_t m_count[4]; // Position within the waveform int32_t m_output[4]; // 1-bit output of each channel, pre-volume int32_t m_cycles_to_ready; // number of cycles until the READY line goes active + //Modifications for easier sample conversion int32_t sample_rate; //Sample rate conversion @@ -115,13 +121,21 @@ public: y2404_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); }; -// SN76489 not verified yet. todo: verify; +// NCR8496 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun class sn76489_device : public sn76496_base_device { public: sn76489_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); +}; + + // SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid) class sn76489a_device : public sn76496_base_device { @@ -143,11 +157,11 @@ 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 not verified; info from smspower wiki and vgmpf wiki +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); }; // Verified by Justin Kerk diff --git a/src/hardware/tandy_sound.cpp b/src/hardware/tandy_sound.cpp index 99cc79b6..7e29739e 100644 --- a/src/hardware/tandy_sound.cpp +++ b/src/hardware/tandy_sound.cpp @@ -63,8 +63,11 @@ static struct { } dac; } tandy; -static sn76496_device device(machine_config(), 0, 0, SOUND_CLOCK ); +static sn76496_device device_sn76496(machine_config(), 0, 0, SOUND_CLOCK ); +static ncr8496_device device_ncr8496(machine_config(), 0, 0, SOUND_CLOCK); +static sn76496_base_device* activeDevice = &device_ncr8496; +#define device (*activeDevice) static void SN76496Write(Bitu /*port*/,Bitu data,Bitu /*iolen*/) { tandy.last_write=PIC_Ticks; @@ -73,6 +76,8 @@ static void SN76496Write(Bitu /*port*/,Bitu data,Bitu /*iolen*/) { tandy.enabled=true; } device.write(data); + +// LOG_MSG("3voice write %X at time %7.3f",data,PIC_FullIndex()); } static void SN76496Update(Bitu length) { @@ -271,6 +276,10 @@ public: enable_hw_tandy_dac=false; } + //Select the correct tandy chip implementation + if (machine == MCH_PCJR) activeDevice = &device_sn76496; + else activeDevice = &device_ncr8496; + real_writeb(0x40,0xd4,0x00); if (IS_TANDY_ARCH) { /* enable tandy sound if tandy=true/auto */