// 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); }