summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/sound/vrc6.cpp
blob: b769fde4043ec3a6d7ded9eb01d7d01dcef6169e (plain) (tree)
1
2
3
4
5
6
7
8
9

                               

                                                                            
            


                                         
 

                                                 

                               




                                                                            
                         
                                                                          








                                                                            
                                                                                                          

                                                    
                                                                            




                                                                       








                                                   
                                                                        






                                                                              
                          



                                     
                                       

























                                                            
                          








                                                                                                                                
                                                


                                
                       
 
                                         



                                          

                                            
                                                                                                               
 
                                                                            














                                                                                          
                         

                                             








                                          

                                            
                                                                                                               
 
                                                                            














                                                                                          
                         

                                             








                                        
                                            
                         
                                                                                                     














                                                                            

                                             






                                                                          
                                                                             

                          
                                    






                                         
                                                  












                                                              
                                                                     
                                         
                                                                                                                               



                                              




                                                                            
                                                                                                                               
                                         



                                                          


















                                                                                                                               















                                                                     
                                                                                                                               



                                              




                                                                            
                                                                                                                               
                                         















                                                                
                                                                                                                     



                                              




                                                                            
                                                                                                                     
                                         





                                              
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/***************************************************************************

    vrc6.cpp
    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"

// device type definition
DEFINE_DEVICE_TYPE(VRC6, vrc6snd_device, "vrc6snd", "Konami VRC6 (Sound)")

//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  vrc6snd_device - constructor
//-------------------------------------------------

vrc6snd_device::vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, VRC6, tag, owner, clock)
	, device_sound_interface(mconfig, *this)
	, m_freqctrl(0), m_pulsectrl{ 0, 0 }, m_sawrate(0), m_master_freq(0)
	, m_pulsefrql{ 0, 0 }, m_pulsefrqh{ 0, 0 }, m_pulseduty{ 0, 0 }
	, m_sawfrql(0), m_sawfrqh(0), m_sawclock(0), m_sawaccum(0)
	, m_ticks{ 0, 0, 0 }
	, m_output{ 0, 0, 0 }
	, m_stream(nullptr)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void vrc6snd_device::device_start()
{
	m_stream = machine().sound().stream_alloc(*this, 0, 1, clock());

	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;
	m_master_freq = 0;

	save_item(NAME(m_freqctrl));
	save_item(NAME(m_pulsectrl));
	save_item(NAME(m_sawrate));
	save_item(NAME(m_master_freq));
	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;
	m_master_freq = 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)
{
	std::fill_n(&outputs[0][0], samples, 0);

	// check global halt bit
	if (m_freqctrl & 1)
		return;

	for (int i = 0; i < samples; i++)
	{
		// update pulse1
		if (m_pulsefrqh[0] & 0x80)
		{
			if (m_ticks[0] == 0)
			{
				m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq;

				m_pulseduty[0] = (m_pulseduty[0] - 1) & 0xf;
				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;
					}
				}
			}
			else
				m_ticks[0]--;
		}
		else
		{
			m_output[0] = 0;
		}

		// update pulse2
		if (m_pulsefrqh[1] & 0x80)
		{
			if (m_ticks[1] == 0)
			{
				m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq;

				m_pulseduty[1] = (m_pulseduty[1] - 1) & 0xf;
				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;
					}
				}
			}
			else
				m_ticks[1]--;
		}
		else
		{
			m_output[1] = 0;
		}

		// update saw
		if (m_sawfrqh & 0x80)
		{
			if (m_ticks[2] == 0)
			{
				m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq;

				// 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_ticks[2]--;
		}
		else
		{
			m_output[2] = 0;
		}

		// sum 2 4-bit pulses, 1 5-bit saw = unsigned 6 bit output
		s16 tmp = (s16)(u8)(m_output[0] + m_output[1] + m_output[2]);
		tmp <<= 8;

		outputs[0][i] = tmp;
	}
}

//---------------------------------------
//  write - write to the chip's registers
//---------------------------------------

void vrc6snd_device::write(offs_t offset, u8 data)
{
	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[0] & 0x80))
					{
						m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq;
					}
					break;

				case 2:
					m_pulsefrqh[0] = data;
					// if disabling channel, reset phase
					if (!(data & 0x80))
					{
						m_pulseduty[0] = 15;
						m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq;
					}
					break;

				case 3:
					m_freqctrl = data;
					if (m_freqctrl & 4)
						m_master_freq = 0x8;
					else if (m_freqctrl & 2)
						m_master_freq = 0x4;
					else
						m_master_freq = 0;

					if (!(m_pulsefrqh[0] & 0x80))
					{
						m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq;
					}
					if (!(m_pulsefrqh[1] & 0x80))
					{
						m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq;
					}
					if (!(m_sawfrqh & 0x80))
					{
						m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq;
					}
					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] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq;
					}
					break;

				case 2:
					m_pulsefrqh[1] = data;
					// if disabling channel, reset phase
					if (!(data & 0x80))
					{
						m_pulseduty[1] = 15;
						m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq;
					}
					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] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq;
					}
					break;

				case 2:
					m_sawfrqh = data;
					// if disabling channel, reset phase
					if (!(data & 0x80))
					{
						m_sawaccum = 0;
						m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq;
					}
					break;
			}
			break;
	}

}