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


                                                         
 


                                                                         
 
                                                                            
 
                         
 
                           
 



                                                                           
 

                                                                        
 
                   
 

                                                                         
 
                                                                     


                                                                          
 


                                                                            






                                                                            
                         
                                                                              
 


                                                                                          
                                                   
 








                           






             
  
 
 
                                                                            
               
                                                                            
 
                                                   
                                 
                                                   
 
                                                                                                                 
                                                         
                                                       
                                                         
                                             
                              
                                  
                                         
 


 
                                                   









                                                                          

                                                   
 
                                    
 


                                                                      
                            
                                               
                                                                                  
 
                                   

                                      
                                                                      
         






                                                                              



         


                                                   
 
                                    
 
                           

                                       

 
 


                                                   
 

                                        
                               


 



                                                   
 
                                            
 
                                               
                                                     


 



                                                   
 
                                                                                                                                 


                                                             
 
                                                         
                                   
                                                                
 
 
 
                                                   
                                               
                                                   
 
                                        
 
                           


 



                                                   
 
                                        
 
                          
                                    
                               


 
                                                   
                                   
                                                   
 
                               
 
                                                                    
 
                                                                      
                           


                                                                      



                      
 
                                                   
                                         
                                                   
 
                                            


                                                           
         
                                                                                                              
                                             
                                                                                                             
                                                                              
 
                                    
                                   
 


                                                                                                            
                         
                                                                      
 



                                                                                      
 


                                                                                 
                                                         
 


                                                                                
                                                        
 
                                                         
                                         
                                                                                       






                                                                                       
                                                                                                
                                         

                                                                  
                                            
                                         
                                                                                                               
                                         
                                 

                                    
                                                                                                                    

                                 
 

                                    

         
                                                                                      

                                           


                                                      
         
                                                      
                                   

                                                                                               
                                             


                                                                                               
         
 
 











                                                                            



                                 








                                                   
                                                                                                                 






                                                       
         
                                             
                                                                                                        
 


                                                                                                                   
 

                                          
                 

                                          


                 
// license:BSD-3-Clause
// copyright-holders:Mirko Buffoni,Aaron Giles
/***************************************************************************

    okim6295.h

    OKIM 6295 ADCPM sound chip.

****************************************************************************

    Library to transcode from an ADPCM source to raw PCM.
    Written by Buffoni Mirko in 08/06/97
    References: various sources and documents.

    R. Belmont 31/10/2003
    Updated to allow a driver to use both MSM6295s and "raw" ADPCM voices
    (gcpinbal). Also added some error trapping for MAME_DEBUG builds

****************************************************************************

    OKIM 6295 ADPCM chip:

    Command bytes are sent:

        1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample
                    number to trigger
        abcd vvvv = second half of command; one of the abcd bits is set to
                    indicate which voice the v bits seem to be volumed

        0abc d000 = stop playing; one or more of the abcd bits is set to
                    indicate which voice(s)

    Status is read:

        ???? abcd = one bit per voice, set to 0 if nothing is playing, or
                    1 if it is active

    OKI Semiconductor produced this chip in two package variants. The
    44-pin QFP version, MSM6295GS, is the original one and by far the more
    common of the two. The 42-pin DIP version, MSM6295VRS, omits A17 and
    RD, which limits its ROM addressing to one megabit instead of two.

***************************************************************************/

#include "emu.h"
#include "okim6295.h"


//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(OKIM6295, okim6295_device, "okim6295", "OKI MSM6295 ADPCM")

// volume lookup table. The manual lists only 9 steps, ~3dB per step. Given the dB values,
// that seems to map to a 5-bit volume control. Any volume parameter beyond the 9th index
// results in silent playback.
const uint8_t okim6295_device::s_volume_table[16] =
{
	0x20,   //   0 dB
	0x16,   //  -3.2 dB
	0x10,   //  -6.0 dB
	0x0b,   //  -9.2 dB
	0x08,   // -12.0 dB
	0x06,   // -14.5 dB
	0x04,   // -18.0 dB
	0x03,   // -20.5 dB
	0x02,   // -24.0 dB
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
	0x00,
};


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

//-------------------------------------------------
//  okim6295_device - constructor
//-------------------------------------------------

okim6295_device::okim6295_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, OKIM6295, tag, owner, clock),
		device_sound_interface(mconfig, *this),
		device_rom_interface(mconfig, *this, 18),
		m_region(*this, DEVICE_SELF),
		m_command(-1),
		m_stream(nullptr),
		m_pin7_state(~uint8_t(0))
{
}


//-------------------------------------------------
//  device_validity_check - device-specific checks
//-------------------------------------------------

void okim6295_device::device_validity_check(validity_checker &valid) const
{
	if ((PIN7_LOW != m_pin7_state) && (PIN7_HIGH != m_pin7_state))
		osd_printf_error("Initial pin 7 state not configured\n");
}

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

void okim6295_device::device_start()
{
	if ((PIN7_LOW != m_pin7_state) && (PIN7_HIGH != m_pin7_state))
		m_pin7_state = 0;

	// create the stream
	int divisor = m_pin7_state ? 132 : 165;
	m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / divisor);

	save_item(NAME(m_command));
	save_item(NAME(m_pin7_state));

	for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++)
	{
		save_item(NAME(m_voice[voicenum].m_playing), voicenum);
		save_item(NAME(m_voice[voicenum].m_sample), voicenum);
		save_item(NAME(m_voice[voicenum].m_count), voicenum);
		save_item(NAME(m_voice[voicenum].m_adpcm.m_signal), voicenum);
		save_item(NAME(m_voice[voicenum].m_adpcm.m_step), voicenum);
		save_item(NAME(m_voice[voicenum].m_volume), voicenum);
		save_item(NAME(m_voice[voicenum].m_base_offset), voicenum);
	}
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void okim6295_device::device_reset()
{
	m_stream->update();
	for (auto & elem : m_voice)
		elem.m_playing = false;
}


//-------------------------------------------------
//  device_post_load - device-specific post-load
//-------------------------------------------------

void okim6295_device::device_post_load()
{
	device_clock_changed();
}


//-------------------------------------------------
//  device_clock_changed - called if the clock
//  changes
//-------------------------------------------------

void okim6295_device::device_clock_changed()
{
	int divisor = m_pin7_state ? 132 : 165;
	m_stream->set_sample_rate(clock() / divisor);
}


//-------------------------------------------------
//  stream_generate - handle update requests for
//  our sound stream
//-------------------------------------------------

void okim6295_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
	// reset the output stream
	memset(outputs[0], 0, samples * sizeof(*outputs[0]));

	// iterate over voices and accumulate sample data
	for (auto & elem : m_voice)
		elem.generate_adpcm(*this, outputs[0], samples);
}


//-------------------------------------------------
//  rom_bank_updated - the rom bank has changed
//-------------------------------------------------

void okim6295_device::rom_bank_updated()
{
	m_stream->update();
}


//-------------------------------------------------
//  set_pin7 - change the state of pin 7, which
//  alters the frequency we output
//-------------------------------------------------

void okim6295_device::set_pin7(int pin7)
{
	assert(started());
	m_pin7_state = pin7 ? 1 : 0;
	device_clock_changed();
}


//-------------------------------------------------
//  read - read the status register
//-------------------------------------------------

uint8_t okim6295_device::read()
{
	uint8_t result = 0xf0;    // naname expects bits 4-7 to be 1

	// set the bit to 1 if something is playing on a given channel
	m_stream->update();
	for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++)
		if (m_voice[voicenum].m_playing)
			result |= 1 << voicenum;

	return result;
}


//-------------------------------------------------
//  write - write to the command register
//-------------------------------------------------

void okim6295_device::write(uint8_t command)
{
	// if a command is pending, process the second half
	if (m_command != -1)
	{
		// the manual explicitly says that it's not possible to start multiple voices at the same time
		int voicemask = command >> 4;
		//if (voicemask != 0 && voicemask != 1 && voicemask != 2 && voicemask != 4 && voicemask != 8)
		//  popmessage("OKI6295 start %x contact MAMEDEV", voicemask);

		// update the stream
		m_stream->update();

		// determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte)
		for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++, voicemask >>= 1)
			if (voicemask & 1)
			{
				okim_voice &voice = m_voice[voicenum];

				if (!voice.m_playing) // fixes Got-cha and Steel Force
				{
					// determine the start/stop positions
					offs_t base = m_command * 8;

					offs_t start = read_byte(base + 0) << 16;
					start |= read_byte(base + 1) << 8;
					start |= read_byte(base + 2) << 0;
					start &= 0x3ffff;

					offs_t stop = read_byte(base + 3) << 16;
					stop |= read_byte(base + 4) << 8;
					stop |= read_byte(base + 5) << 0;
					stop &= 0x3ffff;

					if (start < stop)
					{
						// set up the voice to play this sample
						voice.m_playing = true;
						voice.m_base_offset = start;
						voice.m_sample = 0;
						voice.m_count = 2 * (stop - start + 1);

						// also reset the ADPCM parameters
						voice.m_adpcm.reset();
						voice.m_volume = s_volume_table[command & 0x0f];
					}

					// invalid samples go here
					else
					{
						logerror("Requested to play invalid sample %02x\n", m_command);
					}
				}
				else
				{
					logerror("Requested to play sample %02x on non-stopped voice\n", m_command);
				}
			}

		// reset the command
		m_command = -1;
	}

	// if this is the start of a command, remember the sample number for next time
	else if (command & 0x80)
		m_command = command & 0x7f;

	// otherwise, see if this is a silence command
	else
	{
		// update the stream, then turn it off
		m_stream->update();

		// determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command
		int voicemask = command >> 3;
		for (int voicenum = 0; voicenum < OKIM6295_VOICES; voicenum++, voicemask >>= 1)
			if (voicemask & 1)
				m_voice[voicenum].m_playing = false;
	}
}



//**************************************************************************
//  OKIM VOICE
//**************************************************************************

//-------------------------------------------------
//  okim_voice - constructor
//-------------------------------------------------

okim6295_device::okim_voice::okim_voice()
	: m_playing(false),
		m_base_offset(0),
		m_sample(0),
		m_count(0),
		m_volume(0)
{
}


//-------------------------------------------------
//  generate_adpcm - generate ADPCM samples and
//  add them to an output stream
//-------------------------------------------------

void okim6295_device::okim_voice::generate_adpcm(device_rom_interface &rom, stream_sample_t *buffer, int samples)
{
	// skip if not active
	if (!m_playing)
		return;

	// loop while we still have samples to generate
	while (samples-- != 0)
	{
		// fetch the next sample byte
		int nibble = rom.read_byte(m_base_offset + m_sample / 2) >> (((m_sample & 1) << 2) ^ 4);

		// output to the buffer, scaling by the volume
		// signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767
		*buffer++ += m_adpcm.clock(nibble) * m_volume / 2;

		// next!
		if (++m_sample >= m_count)
		{
			m_playing = false;
			break;
		}
	}
}