diff --git a/.gitattributes b/.gitattributes index 41f7ed151c4..12117a412e0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5865,6 +5865,8 @@ src/mess/audio/upd1771.h svneol=native#text/plain src/mess/audio/vboy.c svneol=native#text/plain src/mess/audio/vboy.h svneol=native#text/plain src/mess/audio/vc4000.c svneol=native#text/plain +src/mess/audio/vrc6.c svneol=native#text/plain +src/mess/audio/vrc6.h svneol=native#text/plain src/mess/audio/wswan.c svneol=native#text/plain src/mess/drivers/4004clk.c svneol=native#text/plain src/mess/drivers/68ksbc.c svneol=native#text/plain diff --git a/src/mess/audio/vrc6.c b/src/mess/audio/vrc6.c new file mode 100644 index 00000000000..48864dc3ebc --- /dev/null +++ b/src/mess/audio/vrc6.c @@ -0,0 +1,320 @@ +/*************************************************************************** + + vrc6.c + Konami VRC6 additional sound channels + + Emulation by R. Belmont + + References: + http://wiki.nesdev.com/w/index.php/VRC6_audio + http://nesdev.com/vrcvi.txt + +***************************************************************************/ + +#include "emu.h" +#include "vrc6.h" + +#define DISABLE_VRC6_SOUND // not ready yet + +// device type definition +const device_type VRC6 = &device_creator; + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// vrc6snd_device - constructor +//------------------------------------------------- + +vrc6snd_device::vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, VRC6, "VRC6 sound", tag, owner, clock), + device_sound_interface(mconfig, *this) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void vrc6snd_device::device_start() +{ + m_stream = machine().sound().stream_alloc(*this, 0, 1, clock(), this); + + m_freqctrl = m_pulsectrl[0] = m_pulsectrl[1] = 0; + m_pulsefrql[0] = m_pulsefrql[1] = m_pulsefrqh[0] = m_pulsefrqh[1] = 0; + m_sawaccum = m_sawfrql = m_sawfrqh = m_sawclock = m_sawrate = 0; + m_ticks[0] = m_ticks[1] = m_ticks[2] = 0; + m_output[0] = m_output[1] = m_output[2] = 0; + m_pulseduty[0] = m_pulseduty[1] = 15; + + save_item(NAME(m_freqctrl)); + save_item(NAME(m_pulsectrl)); + save_item(NAME(m_sawrate)); + save_item(NAME(m_sawaccum)); + save_item(NAME(m_pulsefrql)); + save_item(NAME(m_pulsefrqh)); + save_item(NAME(m_sawfrql)); + save_item(NAME(m_sawfrqh)); + save_item(NAME(m_ticks)); + save_item(NAME(m_output)); + save_item(NAME(m_pulseduty)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void vrc6snd_device::device_reset() +{ + m_stream->update(); + + m_freqctrl = m_pulsectrl[0] = m_pulsectrl[1] = 0; + m_pulsefrql[0] = m_pulsefrql[1] = 0; + m_sawaccum = m_sawfrql = m_sawclock = m_sawrate = 0; + m_ticks[0] = m_ticks[1] = m_ticks[2] = 0; + m_output[0] = m_output[1] = m_output[2] = 0; + m_pulseduty[0] = m_pulseduty[1] = 15; + m_pulsefrqh[0] = m_pulsefrqh[1] = m_sawfrqh = 0; +} + +//------------------------------------------------- +// sound_stream_update - handle update requests for +// our sound stream +//------------------------------------------------- + +void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *out = outputs[0]; + INT16 tmp; + int i; + + // check global halt bit + if (m_freqctrl & 1) + { + return; + } + + for (i = 0; i < samples; i++) + { + // update pulse1 + if (m_pulsefrqh[0] & 0x80) + { + m_ticks[0]--; + if (m_ticks[0] == 0) + { + m_ticks[0] = m_pulsefrql[0] | (m_pulsefrqh[0] & 0xf)<<4; + + m_pulseduty[0]--; + if (m_pulsectrl[0] & 0x80) + { + m_output[0] = m_pulsectrl[0] & 0xf; + } + else + { + if (m_pulseduty[0] <= ((m_pulsectrl[0]>>4) & 0x7)) + { + m_output[0] = m_pulsectrl[0] & 0xf; + } + else + { + m_output[0] = 0; + } + } + + if (m_pulseduty[0] == 0) + { + m_pulseduty[0] = 15; + } + } + } + else + { + m_output[0] = 0; + } + + // update pulse2 + if (m_pulsefrqh[1] & 0x80) + { + m_ticks[1]--; + if (m_ticks[1] == 0) + { + m_ticks[1] = m_pulsefrql[1] | (m_pulsefrqh[1] & 0xf)<<4; + + m_pulseduty[1]--; + if (m_pulsectrl[1] & 0x80) + { + m_output[1] = m_pulsectrl[1] & 0xf; + } + else + { + if (m_pulseduty[1] <= ((m_pulsectrl[1]>>4) & 0x7)) + { + m_output[1] = m_pulsectrl[1] & 0xf; + } + else + { + m_output[1] = 0; + } + } + + if (m_pulseduty[1] == 0) + { + m_pulseduty[1] = 15; + } + } + } + else + { + m_output[1] = 0; + } + + // update saw + if (m_sawfrqh & 0x80) + { + m_ticks[2]--; + if (m_ticks[2] == 0) + { + m_ticks[2] = m_sawfrql | (m_sawfrqh & 0xf)<<4; + + // only update on even steps + if ((m_sawclock > 0) && (!(m_sawclock & 1))) + { + m_sawaccum += (m_sawrate & 0x3f); + m_output[2] = (m_sawaccum>>3); + } + m_sawclock++; + + if (m_sawclock >= 14) + { + m_sawclock = m_sawaccum = 0; + m_output[2] = 0; + } + } + } + else + { + m_output[2] = 0; + } + + // sum 2 4-bit pulses, 1 5-bit saw = unsigned 6 bit output + tmp = (INT16)(UINT8)(m_output[0] + m_output[1] + m_output[2]); + tmp <<= 8; + + out[i] = tmp; + } +} + +//--------------------------------------- +// write - write to the chip's registers +//--------------------------------------- + +WRITE8_MEMBER( vrc6snd_device::write ) +{ + switch (offset >> 8) + { + case 0: + m_stream->update(); + switch (offset & 3) + { + case 0: + m_pulsectrl[0] = data; + break; + + case 1: + m_pulsefrql[0] = data; + if (!(m_pulsefrqh[1] & 0x80)) + { + m_ticks[0] &= ~0xff; + m_ticks[0] |= m_pulsefrql[0]; + } + break; + + case 2: + #ifndef DISABLE_VRC6_SOUND + m_pulsefrqh[0] = data; + // if disabling channel, reset phase + if (!(data & 0x80)) + { + m_pulseduty[0] = 15; + m_ticks[0] &= 0xff; + m_ticks[0] |= (m_pulsefrqh[0] & 0xf)<<4; + } + #endif + break; + + case 3: + m_freqctrl = data; + break; + } + break; + + case 1: + m_stream->update(); + switch (offset & 3) + { + case 0: + m_pulsectrl[1] = data; + break; + + case 1: + m_pulsefrql[1] = data; + if (!(m_pulsefrqh[1] & 0x80)) + { + m_ticks[1] &= ~0xff; + m_ticks[1] |= m_pulsefrql[1]; + } + break; + + case 2: + #ifndef DISABLE_VRC6_SOUND + m_pulsefrqh[1] = data; + // if disabling channel, reset phase + if (!(data & 0x80)) + { + m_pulseduty[1] = 15; + m_ticks[1] &= 0xff; + m_ticks[1] |= (m_pulsefrqh[1] & 0xf)<<4; + } + #endif + break; + } + break; + + case 2: + m_stream->update(); + switch (offset & 3) + { + case 0: + m_sawrate = data; + break; + + case 1: + m_sawfrql = data; + if (!(m_sawfrqh & 0x80)) + { + m_ticks[2] &= ~0xff; + m_ticks[2] |= m_sawfrql; + } + break; + + case 2: + #ifndef DISABLE_VRC6_SOUND + m_sawfrqh = data; + // if disabling channel, reset phase + if (!(data & 0x80)) + { + m_sawaccum = 0; + m_ticks[2] &= 0xff; + m_ticks[2] |= (m_sawfrqh & 0xf)<<4; + } + #endif + break; + } + break; + } + +} + + diff --git a/src/mess/audio/vrc6.h b/src/mess/audio/vrc6.h new file mode 100644 index 00000000000..bef5383b5b2 --- /dev/null +++ b/src/mess/audio/vrc6.h @@ -0,0 +1,61 @@ +/*************************************************************************** + + vrc6.h + Konami VRC6 add-on sound + +***************************************************************************/ + +#pragma once + +#ifndef __VRC6_H__ +#define __VRC6_H__ + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_VRC6_ADD(_tag, _clock) \ + MCFG_DEVICE_ADD(_tag, VRC6, _clock) + +#define MCFG_VRC6_REPLACE(_tag, _clock) \ + MCFG_DEVICE_REPLACE(_tag, VRC6, _clock) + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> vrc6snd_device + +class vrc6snd_device : public device_t, public device_sound_interface +{ +public: + // construction/destruction + vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + DECLARE_WRITE8_MEMBER(write); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); + +private: + UINT8 m_freqctrl, m_pulsectrl[2], m_sawrate; + UINT8 m_pulsefrql[2], m_pulsefrqh[2], m_pulseduty[2]; + UINT8 m_sawfrql, m_sawfrqh, m_sawclock, m_sawaccum; + UINT16 m_ticks[3]; + UINT8 m_output[3]; + + sound_stream *m_stream; +}; + + +// device type definition +extern const device_type VRC6; + + +#endif /* __VRC6_H__ */ + + diff --git a/src/mess/machine/nes_konami.c b/src/mess/machine/nes_konami.c index 7dedaa7d399..969b07fd6b8 100644 --- a/src/mess/machine/nes_konami.c +++ b/src/mess/machine/nes_konami.c @@ -39,6 +39,7 @@ #define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) +#define N2A03_DEFAULTCLOCK (21477272.724 / 12) //------------------------------------------------- // constructor @@ -78,7 +79,8 @@ nes_konami_vrc4_device::nes_konami_vrc4_device(const machine_config &mconfig, co } nes_konami_vrc6_device::nes_konami_vrc6_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : nes_konami_vrc4_device(mconfig, NES_VRC6, "NES Cart Konami VRC-6 PCB", tag, owner, clock, "nes_vrc6", __FILE__) + : nes_konami_vrc4_device(mconfig, NES_VRC6, "NES Cart Konami VRC-6 PCB", tag, owner, clock, "nes_vrc6", __FILE__), + m_vrc6snd(*this, "vrc6snd") { } @@ -187,6 +189,11 @@ void nes_konami_vrc4_device::pcb_reset() memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); } +void nes_konami_vrc6_device::device_start() +{ + nes_konami_vrc4_device::device_start(); +} + void nes_konami_vrc7_device::device_start() { m_ym2413 = device().subdevice("ym"); @@ -573,9 +580,11 @@ WRITE8_MEMBER(nes_konami_vrc6_device::write_h) case 0x4000: prg8_cd(data); break; - case 0x1000: - case 0x2000: - LOG_MMC(("Konami VRC-6 Sound write, offset: %04x, data: %02x\n", (offset & 0x7000) | add_lines, data)); + case 0x1000: // pulse 1 & global control + m_vrc6snd->write(space, add_lines>>8, data); + break; + case 0x2000: // pulse 2 + m_vrc6snd->write(space, (add_lines>>8) | 0x100, data); break; case 0x3000: if (add_lines == 0x300) @@ -588,8 +597,10 @@ WRITE8_MEMBER(nes_konami_vrc6_device::write_h) case 0x0c: set_nt_mirroring(PPU_MIRROR_HIGH); break; } } - else - LOG_MMC(("Konami VRC-6 Sound write, offset: %04x, data: %02x\n", (offset & 0x7000) | add_lines, data)); + else // saw + { + m_vrc6snd->write(space, (add_lines>>8) | 0x200, data); + } break; case 0x5000: case 0x6000: @@ -623,6 +634,25 @@ WRITE8_MEMBER(nes_konami_vrc6_device::write_h) } } +static MACHINE_CONFIG_FRAGMENT( vrc6 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("vrc6snd", VRC6, N2A03_DEFAULTCLOCK) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.5) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_konami_vrc6_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( vrc6 ); +} + /*------------------------------------------------- Konami VRC7 diff --git a/src/mess/machine/nes_konami.h b/src/mess/machine/nes_konami.h index 546228cf183..4ffee71959c 100644 --- a/src/mess/machine/nes_konami.h +++ b/src/mess/machine/nes_konami.h @@ -2,6 +2,7 @@ #define __NES_KONAMI_H #include "machine/nes_nxrom.h" +#include "audio/vrc6.h" // ======================> nes_konami_vrc1_device @@ -111,9 +112,11 @@ public: nes_konami_vrc6_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; virtual DECLARE_WRITE8_MEMBER(write_h); - // TODO: emulate sound capabilities! + required_device m_vrc6snd; }; diff --git a/src/mess/mess.mak b/src/mess/mess.mak index 13c684f8c1f..3ffe174f32f 100644 --- a/src/mess/mess.mak +++ b/src/mess/mess.mak @@ -1425,6 +1425,7 @@ $(MESSOBJ)/nintendo.a: \ $(MESS_MACHINE)/nes_jy.o \ $(MESS_MACHINE)/nes_kaiser.o \ $(MESS_MACHINE)/nes_konami.o \ + $(MESS_AUDIO)/vrc6.o \ $(MESS_MACHINE)/nes_legacy.o \ $(MESS_MACHINE)/nes_multigame.o \ $(MESS_MACHINE)/nes_namcot.o \