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

                        


                                                                            
                                                                       
 


                       
                         
                        

                    









                                                                            
                                                                         
 
                                                                                                               
                                                        
                                                                          









                                  

                                         






                                   
                                                      
 





                                        


                                    


                                   
 



                













                                                     

                                                                                                                                
                        
 

                                         


                                  
                                                                     


                                                          
                                                                           


                                                                
 











                                                                                         
 




                                                    
 



                                                   
 

                                                           
                 
 
                                    





                            




                           


                       





                                                            


 
                                          

                           
                    
 

























                                                                                        
 




                                                                                     
 









                                                                                                          
                                                                  









                                                                                                                    
                                                                       


                                                      
 





                                                                            
 










                                                                                          
                                                                    
                                                                                                                        
                                                  
                                                                                                                             

                                                       


                                                       



                                                         
 





                                              
 
// license:BSD-3-Clause
// copyright-holders:hap
/***************************************************************************

    tc8830f.c - Toshiba TC8830F, CMOS voice recording/reproducing LSI
    1-bit ADM (Adaptive Delta Modulation), similar to TC8801 and T6668.

    Very preliminary...

    TODO:
    - improve ADM decoder
    - remaining commands
    - manual control
    - chip read
    - RAM
    - recording

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

#include "emu.h"
#include "tc8830f.h"


// device type definition
DEFINE_DEVICE_TYPE(TC8830F, tc8830f_device, "tc8830f", "Toshiba TC8830F")

tc8830f_device::tc8830f_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, TC8830F, tag, owner, clock),
		device_sound_interface(mconfig, *this), m_stream(nullptr),
		m_playing(false),
		m_address(0),
		m_stop_address(0),
		m_bitcount(0),
		m_bitrate(0),
		m_prevbits(0),
		m_delta(1),
		m_output(0),
		m_command(0),
		m_cmd_rw(0),
		m_phrase(0),
		m_mem(*this, DEVICE_SELF)
{
}


void tc8830f_device::device_start()
{
	// create the stream
	m_stream = stream_alloc(0, 1, clock() / 0x10);

	// register for savestates
	save_item(NAME(m_playing));
	save_item(NAME(m_address));
	save_item(NAME(m_stop_address));
	save_item(NAME(m_bitcount));
	save_item(NAME(m_bitrate));
	save_item(NAME(m_prevbits));
	save_item(NAME(m_delta));
	save_item(NAME(m_output));
	save_item(NAME(m_command));
	save_item(NAME(m_cmd_rw));
	save_item(NAME(m_phrase));

	reset();
}


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


void tc8830f_device::device_clock_changed()
{
	int divisor = 0x10 * (4 - (m_bitrate & 3));
	m_stream->set_sample_rate(clock() / divisor);
}



void tc8830f_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
	int32_t mix = 0;

	for (int i = 0; i < samples; i++)
	{
		if (m_playing)
		{
			// get bit
			int bit = m_mem[m_address] >> m_bitcount & 1;
			m_bitcount = (m_bitcount + 1) & 7;
			if (m_bitcount == 0)
			{
				m_address = (m_address + 1) & m_mem.mask();
				if (m_address == m_stop_address)
					m_playing = false;
			}

			// compute sample, ADM decoding
			// if previous bits are 111 or 000, delta increases exponentially
			// otherwise, delta decreases linearly
			if ((m_prevbits & 7) == 7 || (m_prevbits & 7) == 0)
			{
				if (m_delta < 0x2000)
					m_delta <<= 1;
			}
			else
				m_delta -= 8;
			if (m_delta <= 0)
				m_delta = 1;

			// determine direction
			if (bit)
				m_output += m_delta;
			else
				m_output -= m_delta;

			if (m_output > 32767)
				m_output = 32767;
			else if (m_output < -32768)
				m_output = -32768;

			m_prevbits = m_prevbits << 1 | bit;
			mix = m_output;
		}

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


void tc8830f_device::reset()
{
	m_stream->update();

	m_playing = false;
	m_address = 0x100;
	m_bitcount = 0;
	m_prevbits = 0;
	m_delta = 1;
	m_output = 0;
	m_cmd_rw = 0;

	// in cpu control, enter play mode and reset bitrate
	write_p(1);
	write_p(6);
	write_p(0);
}


void tc8830f_device::write_p(uint8_t data)
{
	m_stream->update();
	data &= 0xf;

	if (m_cmd_rw == 0)
	{
		// select command
		m_command = data;
		switch (m_command)
		{
			// immediate commands
			case 0x1:
				break;

			case 0x2:
				m_playing = true;
				break;

			case 0x3:
				m_playing = false;
				break;

			// multi-nibble commands
			case 0x4: case 0x5: case 0x6: case 0x7:
				m_cmd_rw = 1;
				break;

			case 0x8: case 0x9: case 0xa: case 0xb:
				logerror("tc8830f: Unemulated command %X\n", m_command);
				break;

			default:
				logerror("tc8830f: Invalid command %X\n", m_command);
				break;
		}
	}

	else
	{
		// write command
		switch (m_command)
		{
			case 0x4:
				// ADLD1: set address counter
				m_address = (m_address & ~(0xf << (m_cmd_rw*4))) | (data << (m_cmd_rw*4));
				if (m_cmd_rw == 5)
				{
					m_address &= m_mem.mask();
					m_bitcount = 0;
					m_cmd_rw = -1;
				}
				break;

			case 0x5:
				// ADLD2: set address stop
				m_stop_address = (m_stop_address & ~(0xf << (m_cmd_rw*4))) | (data << (m_cmd_rw*4));
				if (m_cmd_rw == 5)
				{
					m_stop_address &= m_mem.mask();
					m_cmd_rw = -1;
				}
				break;

			case 0x6:
				// CNDT: d0-d1: bitrate, d2: enable overflow
				m_bitrate = data & 3;
				device_clock_changed();
				m_cmd_rw = -1;
				break;

			case 0x7:
				// LABEL: set phrase
				if (m_cmd_rw == 1)
				{
					m_phrase = (m_phrase & 0x30) | data;
				}
				else
				{
					m_phrase = (m_phrase & 0x0f) | (data << 4 & 0x30);

					// update addresses and start
					uint8_t offs = m_phrase * 4;
					m_address = (m_mem[offs] | m_mem[offs|1]<<8 | m_mem[offs|2]<<16) & m_mem.mask();
					offs += 4;
					m_stop_address = (m_mem[offs] | m_mem[offs|1]<<8 | m_mem[offs|2]<<16) & m_mem.mask();

					m_bitcount = 0;
					m_prevbits = 0;
					m_delta = 1;
					m_output = 0;
					m_playing = true;
					m_cmd_rw = -1;
				}
				break;

			default:
				m_cmd_rw = -1;
				break;
		}
		m_cmd_rw++;
	}
}