summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/hal2.cpp
blob: 9c6d30180ce38152c8803e1fb4c16e2cb409a768 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                       



                                                              
 
                           

                     



                                                                                                         

                               


 

                                
                               

                               












                                                



                                
                  
                  

                                                       


                                   
                                                       


                                   



                                                              




                                                      

 
                                         
 
                       

                        

                                                                                                          
                          

                                                                                                       

                                
                                                                                                                                


                                
                                                                                                                                


                                
                                                                                                                                


                                
                                                                                                                                
                                
         
                                                                                                                                 


                 
                                                     



                        
                                                                                                             



                                                                                                                       

                                  
                                                                                                                              



                                        
                                                                


                                               
                                                                            

                                      
                                                                          

                                      
                                                                           

                                      

































                                                                                                        
                                      
                         
                                    

































                                                                                                        
                                      
                         
                                    
                                                                                 
                                      


                                                                           


                              
                 
                                                                 

                                                                               
                         


































                                                                                                                                          
                                                                                                                                      










                                                                                             

                              
                 

                            
                                                                  


                                               
                                                                              
                                      


                                                                           



                              
                                                                          


                                               
                                                                               
                                      


                                                                           



                              
                                          
                                                            


                                                             
                                                                                        


                                
                                                                                                                             



                                
                                                                                                                             



                                
                                                                                                                             



                                
                                                                                                                             

                                

                
                                                                                                                                          
                      
         
 
















                                                                      
                                                                                                             














































                                                                                                                                     
// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/**********************************************************************

    SGI HAL2 Audio Controller emulation

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

#include "emu.h"
#include "machine/hal2.h"

#define LOG_UNKNOWN     (1 << 0U)
#define LOG_READS       (1 << 1U)
#define LOG_WRITES      (1 << 2U)
#define LOG_ALL         (LOG_UNKNOWN | LOG_READS | LOG_WRITES)

#define VERBOSE         (0)
#include "logmacro.h"

DEFINE_DEVICE_TYPE(SGI_HAL2, hal2_device, "hal2", "SGI HAL2")

hal2_device::hal2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SGI_HAL2, tag, owner, clock)
	, m_ldac(*this, "ldac")
	, m_rdac(*this, "rdac")
{
}

void hal2_device::device_start()
{
	save_item(NAME(m_isr));
	save_item(NAME(m_iar));
	save_item(NAME(m_idr));
	save_item(NAME(m_codeca_ctrl));
	save_item(NAME(m_codeca_channel));
	save_item(NAME(m_codeca_clock));
	save_item(NAME(m_codeca_channel_count));
	save_item(NAME(m_codecb_ctrl));
	save_item(NAME(m_codecb_channel));
	save_item(NAME(m_codecb_clock));
	save_item(NAME(m_codecb_channel_count));
	save_item(NAME(m_bres_clock_sel));
	save_item(NAME(m_bres_clock_inc));
	save_item(NAME(m_bres_clock_modctrl));
	save_item(NAME(m_bres_clock_freq));
	save_item(NAME(m_curr_dac));
}

void hal2_device::device_reset()
{
	m_isr = 0;
	m_iar = 0;
	memset(m_idr, 0, sizeof(uint16_t) * 4);
	memset(m_codeca_ctrl, 0, sizeof(uint16_t) * 2);
	m_codeca_channel = 0;
	m_codeca_clock = 0;
	m_codeca_channel_count = 0;
	memset(m_codecb_ctrl, 0, sizeof(uint16_t) * 2);
	m_codecb_channel = 0;
	m_codecb_clock = 0;
	m_codecb_channel_count = 0;
	memset(m_bres_clock_sel, 0, sizeof(uint16_t) * 3);
	memset(m_bres_clock_inc, 0, sizeof(uint16_t) * 3);
	memset(m_bres_clock_modctrl, 0, sizeof(uint16_t) * 3);
	memset(m_bres_clock_freq, 0, sizeof(uint16_t) * 3);
	for (int i = 0; i < 3; i++)
	{
		m_bres_clock_rate[i] = attotime::zero;
	}
	m_curr_dac = 0;
}

uint16_t hal2_device::read(offs_t offset)
{
	switch (offset)
	{
	case STATUS_REG:
		LOGMASKED(LOG_READS, "%s: HAL2 Status Read: %08x\n", machine().describe_context(), m_isr);
		return m_isr;
	case REVISION_REG:
		LOGMASKED(LOG_READS, "%s: HAL2 Revision Read: 0x4010\n", machine().describe_context());
		return 0x4010;

	case INDIRECT_DATA0_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 0 Read: %04x\n", machine().describe_context(), m_idr[0]);
		return m_idr[0];

	case INDIRECT_DATA1_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 1 Read: %04x\n", machine().describe_context(), m_idr[1]);
		return m_idr[1];

	case INDIRECT_DATA2_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 2 Read: %04x\n", machine().describe_context(), m_idr[2]);
		return m_idr[2];

	case INDIRECT_DATA3_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 3 Read: %04x\n", machine().describe_context(), m_idr[3]);
		return m_idr[3];
	}
	LOGMASKED(LOG_READS | LOG_UNKNOWN, "%s: Unknown HAL2 read: %08x\n", machine().describe_context(), 0x1fbd8000 + offset*4);
	return 0;
}

void hal2_device::write(offs_t offset, uint16_t data)
{
	switch (offset)
	{
	case STATUS_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Status Write: 0x%04x\n", machine().describe_context(), data);
		LOGMASKED(LOG_WRITES, "    HAL2 Global Reset %s\n", (data & ISR_GLOBAL_RESET) ? "Inactive" : "Active");
		LOGMASKED(LOG_WRITES, "    HAL2 Codec Reset %s\n", (data & ISR_CODEC_RESET) ? "Inactive" : "Active");
		m_isr &= ~0x1c;
		m_isr |= data & 0x1c;
		break;
	case INDIRECT_ADDRESS_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Address Register Write: %04x\n", machine().describe_context(), data);
		m_iar = data;
		switch (data & IAR_TYPE)
		{
		case 0x1000:
			LOGMASKED(LOG_WRITES, "    DMA Port\n");
			switch (data & IAR_NUM)
			{
			case 0x0100:
				LOGMASKED(LOG_WRITES, "        Synth In\n");
				break;
			case 0x0200:
				LOGMASKED(LOG_WRITES, "        AES In\n");
				break;
			case 0x0300:
				LOGMASKED(LOG_WRITES, "        AES Out\n");
				break;
			case 0x0400:
			{
				LOGMASKED(LOG_WRITES, "        Codec A (DAC) Out\n");
				const uint32_t param = (data & IAR_PARAM) >> IAR_PARAM_SHIFT;
				switch (param)
				{
					case 1:
						LOGMASKED(LOG_WRITES, "            Control 1\n");
						if (data & IAR_ACCESS_SEL)
						{
							m_idr[0] = m_codeca_ctrl[0];
						}
						else
						{
							m_codeca_ctrl[0] = m_idr[0];
							m_codeca_channel = m_idr[0] & 3;
							m_codeca_clock = (m_idr[0] >> 3) & 3;
							m_codeca_channel_count = (m_idr[0] >> 8) & 3;
						}
						break;
					case 2:
						LOGMASKED(LOG_WRITES, "            Control 2\n");
						if (data & IAR_ACCESS_SEL)
						{
							m_idr[0] = m_codeca_ctrl[1];
						}
						else
						{
							m_codeca_ctrl[1] = m_idr[0];
						}
						break;
					default:
						LOGMASKED(LOG_WRITES, "            Unknown Register\n");
						break;
				}
				break;
			}
			case 0x0500:
			{
				LOGMASKED(LOG_WRITES, "        Codec B (ADC) Out\n");
				const uint32_t param = (data & IAR_PARAM) >> IAR_PARAM_SHIFT;
				switch (param)
				{
					case 1:
						LOGMASKED(LOG_WRITES, "            Control 1\n");
						if (data & IAR_ACCESS_SEL)
						{
							m_idr[0] = m_codecb_ctrl[0];
						}
						else
						{
							m_codecb_ctrl[0] = m_idr[0];
							m_codecb_channel = m_idr[0] & 3;
							m_codecb_clock = (m_idr[0] >> 3) & 3;
							m_codecb_channel_count = (m_idr[0] >> 8) & 3;
						}
						break;
					case 2:
						LOGMASKED(LOG_WRITES, "            Control 2\n");
						if (data & IAR_ACCESS_SEL)
						{
							m_idr[0] = m_codecb_ctrl[1];
						}
						else
						{
							m_codecb_ctrl[1] = m_idr[0];
						}
						break;
					default:
						LOGMASKED(LOG_WRITES, "            Unknown Register\n");
						break;
				}
				break;
			}
			case 0x0600:
				LOGMASKED(LOG_WRITES, "        Synth Control\n");
				break;
			default:
				LOGMASKED(LOG_WRITES, "        Unknown\n");
				break;
			}
			break;
		case 0x2000:
		{
			LOGMASKED(LOG_WRITES, "    Bresenham\n");
			uint32_t clock_gen = (data & IAR_NUM) >> IAR_NUM_SHIFT;
			if (clock_gen >= 1 && clock_gen <= 3)
			{
				LOGMASKED(LOG_WRITES, "        Bresenham Clock Gen %d\n", clock_gen);
				clock_gen--;
				const uint32_t param = (data & IAR_PARAM) >> IAR_PARAM_SHIFT;
				if (param == 1)
				{
					LOGMASKED(LOG_WRITES, "            Control 1 (Clock Select)\n");
					if (data & IAR_ACCESS_SEL)
					{
						m_idr[0] = m_bres_clock_sel[clock_gen];
					}
					else
					{
						m_bres_clock_sel[clock_gen] = m_idr[0];
						switch (m_idr[0])
						{
							case 0:  LOGMASKED(LOG_WRITES, "                48kHz\n"); break;
							case 1:  LOGMASKED(LOG_WRITES, "                44.1kHz\n"); break;
							case 2:  LOGMASKED(LOG_WRITES, "                Off\n"); break;
							default: LOGMASKED(LOG_WRITES, "                Unknown (%d)\n", m_idr[0]); break;
						}
						update_clock_freq(clock_gen);
					}
				}
				else if (param == 2)
				{
					LOGMASKED(LOG_WRITES, "            Control 2 (Inc/Mod Ctrl)\n");
					if (data & IAR_ACCESS_SEL)
					{
						m_idr[0] = m_bres_clock_inc[clock_gen];
						m_idr[1] = m_bres_clock_modctrl[clock_gen];
					}
					else
					{
						m_bres_clock_inc[clock_gen] = m_idr[0];
						m_bres_clock_modctrl[clock_gen] = m_idr[1];
						LOGMASKED(LOG_WRITES, "                Inc:%04x, ModCtrl:%04x\n", m_idr[0], m_idr[1]);
						update_clock_freq(clock_gen);
					}
				}
				else
				{
					LOGMASKED(LOG_WRITES, "            Unknown Param\n");
				}
			}
			else
			{
				LOGMASKED(LOG_WRITES, "        Unknown\n");
			}
			break;
		}

		case 0x3000:
			LOGMASKED(LOG_WRITES, "    Unix Timer\n");
			switch (data & IAR_NUM)
			{
			case 0x0100:
				LOGMASKED(LOG_WRITES, "        Unix Timer\n");
				break;
			default:
				LOGMASKED(LOG_WRITES, "        Unknown\n");
				break;
			}
			break;

		case 0x9000:
			LOGMASKED(LOG_WRITES, "    Global DMA Control\n");
			switch (data & IAR_NUM)
			{
			case 0x0100:
				LOGMASKED(LOG_WRITES, "        DMA Control\n");
				break;
			default:
				LOGMASKED(LOG_WRITES, "        Unknown\n");
				break;
			}
			break;
		}

		if (data & IAR_ACCESS_SEL)
			LOGMASKED(LOG_WRITES, "    Read\n");
		else
			LOGMASKED(LOG_WRITES, "    Write\n");

		LOGMASKED(LOG_WRITES, "    Parameter: %01x\n", (data & IAR_PARAM) >> 2);
		return;

	case INDIRECT_DATA0_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 0 Write: %04x\n", machine().describe_context(), data);
		m_idr[0] = data;
		return;

	case INDIRECT_DATA1_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 1 Write: %04x\n", machine().describe_context(), data);
		m_idr[1] = data;
		return;

	case INDIRECT_DATA2_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 2 Write: %04x\n", machine().describe_context(), data);
		m_idr[2] = data;
		return;

	case INDIRECT_DATA3_REG:
		LOGMASKED(LOG_WRITES, "%s: HAL2 Indirect Data Register 3 Write: %04x\n", machine().describe_context(), data);
		m_idr[3] = data;
		return;

	default:
		LOGMASKED(LOG_WRITES, "%s: Unknown HAL2 Write: %08x = %04x\n", machine().describe_context(), 0x1fbd8000 + offset*4, data);
		break;
	}
}

void hal2_device::update_clock_freq(int clock_gen)
{
	switch (m_bres_clock_sel[clock_gen])
	{
		case 0: // 48kHz
			m_bres_clock_freq[clock_gen] = 48000;
			break;
		case 1: // 44.1kHz
			m_bres_clock_freq[clock_gen] = 44100;
			break;
		default:
			m_bres_clock_freq[clock_gen] = 0;
			m_bres_clock_rate[clock_gen] = attotime::zero;
			return;
	}

	const uint32_t mod = 0x10000 - ((m_bres_clock_modctrl[clock_gen] + 1) - m_bres_clock_inc[clock_gen]);
	if (mod == 0)
		m_bres_clock_rate[clock_gen] = attotime::from_ticks(1, m_bres_clock_freq[clock_gen] * m_bres_clock_inc[clock_gen]);
	else
		m_bres_clock_rate[clock_gen] = attotime::from_ticks(mod, m_bres_clock_freq[clock_gen] * m_bres_clock_inc[clock_gen]);
}

attotime hal2_device::get_rate(const uint32_t channel)
{
	if ((channel == m_codeca_channel || channel == (3 - m_codeca_channel)) && m_codeca_clock > 0 && m_codeca_channel_count > 0)
		return m_bres_clock_rate[m_codeca_clock - 1] / m_codeca_channel_count;
	if ((channel == m_codecb_channel || channel == (3 - m_codecb_channel)) && m_codecb_clock > 0 && m_codecb_channel_count > 0)
		return m_bres_clock_rate[m_codecb_clock - 1] / m_codecb_channel_count;
	return attotime::zero;
}

void hal2_device::dma_write(uint32_t channel, int16_t data)
{
	if (channel >= 2)
		return;

	if (m_curr_dac)
		m_rdac->write(data);
	else
		m_ldac->write(data);

	m_curr_dac++;
	if (m_curr_dac == m_codeca_channel_count)
		m_curr_dac = 0;
}

void hal2_device::device_add_mconfig(machine_config &config)
{
	SPEAKER(config, "lspeaker").front_left();
	SPEAKER(config, "rspeaker").front_right();

	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_ldac, 0);
	m_ldac->add_route(ALL_OUTPUTS, "lspeaker", 0.25);

	DAC_16BIT_R2R_TWOS_COMPLEMENT(config, m_rdac, 0);
	m_rdac->add_route(ALL_OUTPUTS, "rspeaker", 0.25);

	voltage_regulator_device &vreg = VOLTAGE_REGULATOR(config, "vref");
	vreg.add_route(0, "ldac",  1.0, DAC_VREF_POS_INPUT);
	vreg.add_route(0, "rdac",  1.0, DAC_VREF_POS_INPUT);
	vreg.add_route(0, "ldac", -1.0, DAC_VREF_NEG_INPUT);
	vreg.add_route(0, "rdac", -1.0, DAC_VREF_NEG_INPUT);
}