summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/es1373.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/es1373.cpp')
-rw-r--r--src/devices/sound/es1373.cpp499
1 files changed, 499 insertions, 0 deletions
diff --git a/src/devices/sound/es1373.cpp b/src/devices/sound/es1373.cpp
new file mode 100644
index 00000000000..509b4a873d2
--- /dev/null
+++ b/src/devices/sound/es1373.cpp
@@ -0,0 +1,499 @@
+// license:BSD-3-Clause
+// copyright-holders:Ted Green
+#include "es1373.h"
+
+#define LOG_ES (0)
+#define LOG_ES_REG (0)
+#define LOG_ES_FILE (0)
+
+
+static MACHINE_CONFIG_FRAGMENT( es1373 )
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+MACHINE_CONFIG_END
+
+machine_config_constructor es1373_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( es1373 );
+}
+
+const device_type ES1373 = &device_creator<es1373_device>;
+
+DEVICE_ADDRESS_MAP_START(map, 32, es1373_device)
+ AM_RANGE(0x00, 0x3f) AM_READWRITE (reg_r, reg_w)
+ADDRESS_MAP_END
+
+es1373_device::es1373_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pci_device(mconfig, ES1373, "Creative Labs Ensoniq AudioPCI97 ES1373", tag, owner, clock, "es1373", __FILE__),
+ device_sound_interface(mconfig, *this),
+ m_eslog(NULL),
+ m_irq_num(-1)
+{
+}
+
+void es1373_device::set_irq_info(const char *tag, const int irq_num)
+{
+ m_cpu_tag = tag;
+ m_irq_num = irq_num;
+}
+
+//-------------------------------------------------
+// device_stop - device-specific stop
+//-------------------------------------------------
+void es1373_device::device_stop()
+{
+ /* debugging */
+ if (LOG_ES_FILE && m_eslog)
+ {
+ fclose(m_eslog);
+ m_eslog = NULL;
+ }
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void es1373_device::device_start()
+{
+ m_cpu = machine().device<cpu_device>(m_cpu_tag);
+ pci_device::device_start();
+ add_map(0x40, M_IO, FUNC(es1373_device::map));
+
+ // create the stream
+ m_stream = machine().sound().stream_alloc(*this, 0, 2, 44100/2);
+
+ m_timer = timer_alloc(0, NULL);
+ m_timer->adjust(attotime::zero, 0, attotime::from_hz(44100/2/16));
+
+}
+
+void es1373_device::device_reset()
+{
+ // debugging
+ m_tempCount = 0;
+ if (LOG_ES_FILE && m_eslog)
+ {
+ fclose(m_eslog);
+ m_eslog = NULL;
+ }
+ if (LOG_ES_FILE && !m_eslog)
+ m_eslog = fopen("es.log", "w");
+
+ pci_device::device_reset();
+ memset(m_es_regs, 0, sizeof(m_es_regs));
+ memset(m_ac97_regs, 0, sizeof(m_ac97_regs));
+ m_ac97_regs[0] = 0x0800;
+ // Reset ADC channel info
+ m_adc.number = 0;
+ m_adc.enable = false;
+ m_adc.initialized = false;
+ m_adc.buf_rptr = 0x20;
+ m_adc.buf_wptr = 0x20;
+ // Reset DAC1 channel info
+ m_dac1.number = 1;
+ m_dac1.enable = false;
+ m_dac1.initialized = false;
+ m_dac1.buf_rptr = 0x0;
+ m_dac1.buf_wptr = 0x0;
+ // Reset DAC2 channel info
+ m_dac2.number = 2;
+ m_dac2.enable = false;
+ m_dac2.initialized = false;
+ m_dac2.buf_rptr = 0x10;
+ m_dac2.buf_wptr = 0x10; // Start PCI writing to bottom half of buffer
+
+ m_stream->update();
+}
+
+void es1373_device::map_extra(UINT64 memory_window_start, UINT64 memory_window_end, UINT64 memory_offset, address_space *memory_space,
+ UINT64 io_window_start, UINT64 io_window_end, UINT64 io_offset, address_space *io_space)
+{
+ m_memory_space = memory_space;
+}
+
+//-------------------------------------------------
+// device_timer - called when our device timer expires
+//-------------------------------------------------
+void es1373_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr)
+{
+ m_stream->update();
+}
+
+//-------------------------------------------------
+// sound_stream_update - handle update requests for
+// our sound stream
+//-------------------------------------------------
+void es1373_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ if (m_dac1.enable) {
+ logerror("%s: sound_stream_update DAC1 not implemented yet\n", tag());
+ }
+
+ if (m_dac2.enable) {
+ send_audio_out(m_dac2, ICSTATUS_DAC2_INT_MASK, outputs[0], outputs[1], samples);
+ }
+
+ if (m_adc.enable) {
+ if (m_adc.format!=SCTRL_16BIT_MONO) {
+ logerror("%s: sound_stream_update Only SCTRL_16BIT_MONO recorded supported\n", tag());
+ } else {
+ for (int i=0; i<samples; i++) {
+ if (m_adc.buf_count<=m_adc.buf_size) {
+ if (LOG_ES)
+ logerror("%s: ADC buf_count: %i buf_size: %i buf_rptr: %i buf_wptr: %i\n", machine().describe_context(),
+ m_adc.buf_count, m_adc.buf_size, m_adc.buf_rptr, m_adc.buf_wptr);
+ if ((m_adc.buf_count&0x1)) {
+ m_adc.buf_wptr++;
+ }
+ m_adc.buf_count++;
+ if (m_adc.buf_count>m_adc.buf_size) {
+ if (m_adc.loop_en) {
+ // Keep playing
+ m_adc.buf_count = 0;
+ if (LOG_ES)
+ logerror("%X: send_audio_out ADC clearing buf_count\n", machine().device("maincpu")->safe_pc());
+ }
+ if (m_adc.int_en) {
+ m_es_regs[ES_INT_CS_STATUS] |= ICSTATUS_ADC_INT_MASK;
+ if (LOG_ES)
+ logerror("%X: send_audio_out Setting ADC interrupt\n", machine().device("maincpu")->safe_pc());
+ }
+ }
+ if (!(m_adc.buf_count&1) && !(m_adc.buf_wptr&0xf)) {
+ m_adc.buf_wptr -= 0x10;
+ }
+ // PCI Write Transfer
+ if (command & 0x4) {
+ if ((m_adc.buf_rptr&8)^(m_adc.buf_wptr&8)) {
+ transfer_pci_audio(m_adc, ES_PCI_WRITE);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (m_es_regs[ES_INT_CS_STATUS]&(ICSTATUS_DAC1_INT_MASK|ICSTATUS_DAC2_INT_MASK|ICSTATUS_ADC_INT_MASK)) {
+ m_es_regs[ES_INT_CS_STATUS] |= ICSTATUS_INTR_MASK;
+ // Assert interrupt
+ //m_cpu->set_input_line(ES_IRQ_NUM, ASSERT_LINE);
+ if (m_irq_num!=-1) {
+ m_cpu->set_input_line(m_irq_num, ASSERT_LINE);
+ }
+ }
+}
+
+//-------------------------------------------------
+// send_audio_out - Sends channel audio output data
+//-------------------------------------------------
+void es1373_device::send_audio_out(chan_info& chan, UINT32 intr_mask, stream_sample_t *outL, stream_sample_t *outR, int samples)
+{
+ // Only transfer PCI data if bus mastering is enabled
+ // Fill initial half buffer
+ if (1 && (command & 0x4) && (!chan.initialized)) {
+ chan.initialized = true;
+ transfer_pci_audio(chan, ES_PCI_READ);
+ }
+ //UINT32 sample_size = calc_size(chan.format);
+ // Send data to sound stream
+ bool buf_row_done;
+ for (int i=0; i<samples; i++) {
+ buf_row_done = false;
+ if (chan.buf_count<=chan.buf_size) {
+ // Only transfer PCI data if bus mastering is enabled
+ // Fill half-buffer when read pointer is at start of next half
+ //if ((command & 0x4) && ((chan.buf_rptr&8)^(chan.buf_wptr&8)) && !(m_es_regs[ES_INT_CS_STATUS] & intr_mask)) {
+ if ((command & 0x4) && ((chan.buf_rptr&8)^(chan.buf_wptr&8))) {
+ transfer_pci_audio(chan, ES_PCI_READ);
+ }
+ if (LOG_ES && i==0)
+ logerror("%X: chan: %X samples: %i buf_count: %X buf_size: %X buf_rptr: %X buf_wptr: %X\n",
+ machine().device("maincpu")->safe_pc(), chan.number, samples, chan.buf_count, chan.buf_size, chan.buf_rptr, chan.buf_wptr);
+ // Buffer is 4 bytes per location, need to switch on sample mode
+ switch (chan.format) {
+ case SCTRL_8BIT_MONO:
+ logerror("es1373_device::send_audio_out SCTRL_8BIT_MONO not implemented yet\n");
+ break;
+ case SCTRL_8BIT_STEREO:
+ logerror("es1373_device::send_audio_out SCTRL_8BIT_STEREO not implemented yet\n");
+ break;
+ case SCTRL_16BIT_MONO:
+ // The sound cache is 32 bit wide fifo, so each entry is two mono 16 bit samples
+ if ((chan.buf_count&0x1)) {
+ // Read high 16 bits
+ outL[i] = outR[i] = (INT16)(m_sound_cache[chan.buf_rptr]>>16);
+ chan.buf_rptr++;
+ buf_row_done = true;
+ } else {
+ // Read low 16 bits
+ outL[i] = outR[i] = (INT16)(m_sound_cache[chan.buf_rptr]&0xffff);
+ }
+ break;
+ case SCTRL_16BIT_STEREO:
+ // The sound cache is 32 bit wide fifo, so each entry is one stereo 16 bit sample
+ outL[i] = (INT16) m_sound_cache[chan.buf_rptr]&0xffff;
+ outR[i] = (INT16) m_sound_cache[chan.buf_rptr]>>16;
+ chan.buf_rptr++;
+ buf_row_done = true;
+ break;
+ }
+ if (LOG_ES_FILE && m_tempCount<1000000) {
+ m_tempCount++;
+ //logerror("es1373_device::sound_stream_update count: %i samp16: %X\n", i, samp16);
+ //if (LOG_ES_FILE && m_eslog)
+ //fprintf(m_eslog, "%i\n", samp16);
+ }
+ chan.buf_count++;
+ if (chan.buf_count > chan.buf_size) {
+ if (chan.loop_en) {
+ // Keep playing
+ //chan.buf_count -= 1; // Should check SCTRL_P2_END_MASK
+ chan.buf_count = 0;
+ //chan.buf_rptr -= 1;
+ if (LOG_ES)
+ logerror("%X: send_audio_out DAC2 clearing buf_count\n", machine().device("maincpu")->safe_pc());
+ }
+ if (chan.int_en) {
+ m_es_regs[ES_INT_CS_STATUS] |= intr_mask;
+ if (LOG_ES)
+ logerror("%X: send_audio_out Setting DAC2 interrupt\n", machine().device("maincpu")->safe_pc());
+ }
+ }
+ if (buf_row_done && !(chan.buf_rptr&0xf)) {
+ chan.buf_rptr -= 0x10;
+ }
+ } else {
+ // Send zeros?
+ outL[i] = outR[i] = 0;
+ }
+ }
+}
+
+void es1373_device::transfer_pci_audio(chan_info& chan, int type)
+{
+ UINT32 pci_addr, data;
+ pci_addr = chan.pci_addr + (chan.pci_count<<2);
+ if (LOG_ES)
+ logerror("%s: transfer_pci_audio start chan: %X pci_addr: %08X pci_count: %X pci_size: %X buf_rptr: %X buf_wptr: %X\n",
+ machine().describe_context(), chan.number, pci_addr, chan.pci_count, chan.pci_size, chan.buf_rptr, chan.buf_wptr);
+ // Always transfer 8 longwords
+ for (int i=0; i<8; i++) {
+ pci_addr = chan.pci_addr + (chan.pci_count<<2);
+ if (type==ES_PCI_READ) {
+ data = m_memory_space->read_dword(pci_addr, 0xffffffff);
+ m_sound_cache[chan.buf_wptr++] = data;
+ if (!(chan.buf_wptr&0xf)) {
+ chan.buf_wptr -= 0x10;
+ }
+ } else {
+ data = m_sound_cache[chan.buf_rptr++];
+ m_memory_space->write_dword(pci_addr, data);
+ if (!(chan.buf_rptr&0xf)) {
+ chan.buf_rptr -= 0x10;
+ }
+ }
+ if (chan.pci_count==chan.pci_size) {
+ chan.pci_count = 0;
+ } else {
+ chan.pci_count++;
+ }
+ }
+}
+
+UINT32 es1373_device::calc_size(const UINT8 &format)
+{
+ switch (format) {
+ case SCTRL_8BIT_MONO:
+ return 1;
+ break;
+ case SCTRL_8BIT_STEREO:
+ return 2;
+ break;
+ case SCTRL_16BIT_MONO:
+ return 2;
+ break;
+ case SCTRL_16BIT_STEREO:
+ return 4;
+ break;
+ }
+ logerror("%s: calc_size Invalid format = %X specified\n", tag(), format);
+ return 0;
+}
+
+READ32_MEMBER (es1373_device::reg_r)
+{
+ UINT32 result = m_es_regs[offset];
+ switch (offset) {
+ case ES_CODEC:
+ break;
+ case ES_DAC2_CNT:
+ result = ((m_dac2.buf_size-m_dac2.buf_count)<<16) | m_dac2.buf_size;
+ break;
+ case ES_HOST_IF0: // 0x30
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x0];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = m_dac1.pci_addr;
+ break;
+ case 0xd:
+ result = m_adc.pci_addr;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF1: // 0x34
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x1];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = (m_dac1.pci_count<<16) | m_dac1.pci_size;
+ break;
+ case 0xd:
+ result = (m_adc.pci_count<<16) | m_adc.pci_size;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF2: // 0x38
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x2];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = m_dac2.pci_addr;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF3: // 0x3C
+ result = m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x3];
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ result = ((m_dac2.pci_count)<<16) | m_dac2.pci_size;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ if (LOG_ES_REG)
+ logerror("%08X:ES1373 read from offset %02X = %08X & %08X\n", machine().device("maincpu")->safe_pc(), offset*4, result, mem_mask);
+ return result;
+}
+
+WRITE32_MEMBER(es1373_device::reg_w)
+{
+ COMBINE_DATA(&m_es_regs[offset]);
+ switch (offset) {
+ case ES_INT_CS_CTRL:
+ m_dac1.enable = (m_es_regs[ES_INT_CS_CTRL] & ICCTRL_DAC1_EN_MASK);
+ m_dac2.enable = (m_es_regs[ES_INT_CS_CTRL] & ICCTRL_DAC2_EN_MASK);
+ m_adc.enable = (m_es_regs[ES_INT_CS_CTRL] & ICCTRL_ADC_EN_MASK);
+ break;
+ case ES_SRC_IF:
+ if (data&(1<<24)) {
+ // Write to Sample Rate Converter Ram
+ m_src_ram[(data>>25)&0x7F] = data&0xFFFF;
+ } else {
+ // Read From Sample Rate Converter Ram
+ m_es_regs[offset] = (data&0xFFFF0000) | m_src_ram[(data>>25)&0x7F];
+ }
+ break;
+ case ES_CODEC:
+ if (data&(1<<23)) {
+ // Read from AC97 codec registers
+ m_es_regs[offset] = (data&0xFFFF0000) | m_ac97_regs[(data>>16)&0x7f] | 0x80000000;
+ } else {
+ // Write to AC97 codec registers
+ m_ac97_regs[(data>>16)&0x7f] = data&0xFFFF;
+ }
+ break;
+ case ES_SERIAL_CTRL:
+ m_adc.loop_en = !(m_es_regs[ES_SERIAL_CTRL] & SCTRL_R1_LOOP_MASK);
+ m_dac2.loop_en = !(m_es_regs[ES_SERIAL_CTRL] & SCTRL_P2_LOOP_MASK);
+ m_dac1.loop_en = !(m_es_regs[ES_SERIAL_CTRL] & SCTRL_P1_LOOP_MASK);
+ m_adc.int_en = m_es_regs[ES_SERIAL_CTRL] & SCTRL_R1_INT_EN_MASK;
+ m_dac2.int_en = m_es_regs[ES_SERIAL_CTRL] & SCTRL_P2_INT_EN_MASK;
+ m_dac1.int_en = m_es_regs[ES_SERIAL_CTRL] & SCTRL_P1_INT_EN_MASK;
+ m_adc.format = (m_es_regs[ES_SERIAL_CTRL] & SCTRL_R1_S_MASK)>>4;
+ m_dac2.format = (m_es_regs[ES_SERIAL_CTRL] & SCTRL_P2_S_MASK)>>2;
+ m_dac1.format = (m_es_regs[ES_SERIAL_CTRL] & SCTRL_P1_S_MASK)>>0;
+ if (!m_adc.int_en) m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_ADC_INT_MASK;
+ if (!m_dac1.int_en) m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_DAC1_INT_MASK;
+ if (!m_dac2.int_en) m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_DAC2_INT_MASK;
+ // Clear the summary interrupt and irq line
+ if (!(m_es_regs[ES_INT_CS_STATUS]&(ICSTATUS_DAC1_INT_MASK|ICSTATUS_DAC2_INT_MASK|ICSTATUS_ADC_INT_MASK))) {
+ // Deassert interrupt
+ if (m_es_regs[ES_INT_CS_STATUS]&ICSTATUS_INTR_MASK && m_irq_num!=-1) {
+ m_cpu->set_input_line(m_irq_num, CLEAR_LINE);
+ m_es_regs[ES_INT_CS_STATUS] &= ~ICSTATUS_INTR_MASK;
+ if (0 && LOG_ES_REG)
+ logerror("%X: es1373_device::reg_w Clearing interrupt\n", machine().device("maincpu")->safe_pc());
+ }
+ }
+ if (0 && LOG_ES_REG)
+ logerror("%s: es1373_device::reg_w adc_int_en: %i dac1_int_en: %i dac2_int_en: %i\n", tag(), m_adc.int_en, m_dac1.int_en, m_dac2.int_en);
+ break;
+ case ES_DAC2_CNT:
+ m_dac2.buf_count = 0;
+ m_dac2.buf_size = data&0xffff;
+ break;
+ case ES_HOST_IF0: // 0x30
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x0] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac1.pci_addr = data;
+ break;
+ case 0xd:
+ m_adc.pci_addr = data;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF1: // 0x34
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x1] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac1.pci_count = (data>>16)&0xffff;
+ m_dac1.pci_size = data&0xffff;
+ break;
+ case 0xd:
+ m_adc.pci_count = (data>>16)&0xffff;
+ m_adc.pci_size = data&0xffff;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF2: // 0x38
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x2] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac2.pci_addr = data;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ES_HOST_IF3: // 0x3C
+ m_sound_cache[(m_es_regs[ES_MEM_PAGE]<<2) | 0x3] = data;
+ switch (m_es_regs[ES_MEM_PAGE]&0xf) {
+ case 0xc:
+ m_dac2.pci_count = (data>>16)&0xffff;
+ m_dac2.pci_size = data&0xffff;
+ if (LOG_ES_REG)
+ logerror("%08X:ES1373 write to offset %02X = %08X & %08X\n", machine().device("maincpu")->safe_pc(), offset*4, data, mem_mask);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (LOG_ES_REG)
+ logerror("%08X:ES1373 write to offset %02X = %08X & %08X\n", machine().device("maincpu")->safe_pc(), offset*4, data, mem_mask);
+
+}