diff options
Diffstat (limited to 'src/devices/bus/isa/sblaster.c')
-rw-r--r-- | src/devices/bus/isa/sblaster.c | 1682 |
1 files changed, 1682 insertions, 0 deletions
diff --git a/src/devices/bus/isa/sblaster.c b/src/devices/bus/isa/sblaster.c new file mode 100644 index 00000000000..9b02d91b5c4 --- /dev/null +++ b/src/devices/bus/isa/sblaster.c @@ -0,0 +1,1682 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont, Miodrag Milanovic +/*************************************************************************** + + ISA 8/16 bit Creative Labs Sound Blaster Sound Card + + TODO: + - implement DAC + - DSP type is a MCS-51 family, it has an internal ROM that needs decapping; + - implement jumpers DIP-SWs; + +***************************************************************************/ + +#include "sblaster.h" +#include "sound/speaker.h" +#include "sound/262intf.h" +#include "sound/dac.h" +#include "machine/pic8259.h" + +/* + adlib (YM3812/OPL2 chip), part of many many soundcards (soundblaster) + soundblaster: YM3812 also accessible at 0x228/9 (address jumperable) + soundblaster pro version 1: 2 YM3812 chips + at 0x388 both accessed, + at 0x220/1 left?, 0x222/3 right? (jumperable) + soundblaster pro version 2: 1 OPL3 chip + + pro audio spectrum +: 2 OPL2 + pro audio spectrum 16: 1 OPL3 + + 2 x saa1099 chips + inherited from game blaster + also on sound blaster 1.0 + option on sound blaster 1.5 + + jumperable? normally 0x220 +*/ +#define ym3812_StdClock XTAL_3_579545MHz +#define ymf262_StdClock XTAL_14_31818MHz + +static const int m_cmd_fifo_length[256] = +{ +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + -1, -1, -1, -1, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, /* 0x */ + 2, -1, -1, -1, 3, -1, 3, 3, -1, -1, -1, -1, 1, -1, -1, 1, /* 1x */ + -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 2x */ + 1, 1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, /* 3x */ + 2, 3, 3, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, /* 4x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 5x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 6x */ + -1, -1, -1, -1, 3, 3, 3, 3, -1, -1, -1, -1, -1, 1, -1, 1, /* 7x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 8x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 9x */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* Ax */ + 4, -1, -1, -1, -1, -1, 4, -1, 4, -1, -1, -1, -1, -1, 4, -1, /* Bx */ + 4, -1, -1, -1, -1, -1, 4, -1, 4, -1, -1, -1, -1, -1, 4, -1, /* Cx */ + 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, -1, -1, /* Dx */ + 2, 1, 2, 1, 2, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, /* Ex */ + -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1 /* Fx */ +}; + +static const int protection_magic[4] = { 0x96, 0xa5, 0x69, 0x5a }; + +static MACHINE_CONFIG_FRAGMENT( sblaster1_0_config ) + MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") + MCFG_SOUND_ADD("ym3812", YM3812, ym3812_StdClock) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 3.00) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 3.00) + MCFG_SAA1099_ADD("saa1099.1", 7159000) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.50) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.50) + MCFG_SAA1099_ADD("saa1099.2", 7159000) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 0.50) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 0.50) + + MCFG_SOUND_ADD("sbdacl", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.00) + MCFG_SOUND_ADD("sbdacr", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00) + + MCFG_PC_JOY_ADD("pc_joy") + MCFG_MIDI_PORT_ADD("mdin", midiin_slot, "midiin") + MCFG_MIDI_RX_HANDLER(DEVWRITELINE(DEVICE_SELF, sb_device, midi_rx_w)) + + MCFG_MIDI_PORT_ADD("mdout", midiout_slot, "midiout") +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( sblaster1_5_config ) + MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") + MCFG_SOUND_ADD("ym3812", YM3812, ym3812_StdClock) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.00) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00) + /* no CM/S support (empty sockets) */ + + MCFG_SOUND_ADD("sbdacl", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.00) + MCFG_SOUND_ADD("sbdacr", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00) + + MCFG_PC_JOY_ADD("pc_joy") + MCFG_MIDI_PORT_ADD("mdin", midiin_slot, "midiin") + MCFG_MIDI_RX_HANDLER(DEVWRITELINE(DEVICE_SELF, sb_device, midi_rx_w)) + + MCFG_MIDI_PORT_ADD("mdout", midiout_slot, "midiout") +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( sblaster_16_config ) + MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") + MCFG_SOUND_ADD("ymf262", YMF262, ymf262_StdClock) + MCFG_SOUND_ROUTE(0, "lspeaker", 1.00) + MCFG_SOUND_ROUTE(1, "rspeaker", 1.00) + MCFG_SOUND_ROUTE(2, "lspeaker", 1.00) + MCFG_SOUND_ROUTE(3, "rspeaker", 1.00) + MCFG_SOUND_ADD("sbdacl", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.00) + MCFG_SOUND_ADD("sbdacr", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.00) + + MCFG_PC_JOY_ADD("pc_joy") + MCFG_MIDI_PORT_ADD("mdin", midiin_slot, "midiin") + MCFG_MIDI_RX_HANDLER(DEVWRITELINE(DEVICE_SELF, sb_device, midi_rx_w)) + + MCFG_MIDI_PORT_ADD("mdout", midiout_slot, "midiout") +MACHINE_CONFIG_END + +READ8_MEMBER( sb8_device::ym3812_16_r ) +{ + UINT8 retVal = 0xff; + switch(offset) + { + case 0 : retVal = m_ym3812->status_port_r( space, offset ); break; + } + return retVal; +} + +WRITE8_MEMBER( sb8_device::ym3812_16_w ) +{ + switch(offset) + { + case 0 : m_ym3812->control_port_w( space, offset, data ); break; + case 1 : m_ym3812->write_port_w( space, offset, data ); break; + } +} + +READ8_MEMBER( isa8_sblaster1_0_device::saa1099_16_r ) +{ + return 0xff; +} + +WRITE8_MEMBER( isa8_sblaster1_0_device::saa1099_1_16_w ) +{ + switch(offset) + { + case 0 : m_saa1099_1->data_w( space, offset, data ); break; + case 1 : m_saa1099_1->control_w( space, offset, data ); break; + } +} + +WRITE8_MEMBER( isa8_sblaster1_0_device::saa1099_2_16_w ) +{ + switch(offset) + { + case 0 : m_saa1099_2->data_w( space, offset, data ); break; + case 1 : m_saa1099_2->control_w( space, offset, data ); break; + } +} + +void sb_device::queue(UINT8 data) +{ + if (m_dsp.fifo_ptr < 15) + { + m_dsp.fifo[m_dsp.fifo_ptr] = data; + + m_dsp.fifo_ptr++; + } + else + { + // FIFO gets to la-la-land + //logerror("FIFO?\n"); + } +} + +void sb_device::queue_r(UINT8 data) +{ + m_dsp.rbuf_status |= 0x80; + + if (m_dsp.fifo_r_ptr < 52) + { + m_dsp.fifo_r[m_dsp.fifo_r_ptr] = data; + + m_dsp.fifo_r_ptr++; + } + else + { + // FIFO gets to la-la-land + //logerror("FIFO?\n"); + } +} + +UINT8 sb_device::dequeue_r() +{ + UINT8 data = m_dsp.fifo_r[0]; + + if (m_dsp.fifo_r_ptr > 0) + { + for (int i = 0; i < 51; i++) + m_dsp.fifo_r[i] = m_dsp.fifo_r[i + 1]; + + m_dsp.fifo_r[51] = 0; + + m_dsp.fifo_r_ptr--; + } + + if(m_dsp.fifo_r_ptr == 0) + m_dsp.rbuf_status &= ~0x80; + + return data; +} + + +READ8_MEMBER( sb_device::dsp_reset_r ) +{ +// printf("read DSP reset @ %x\n", offset); + if(offset) + return 0xff; + logerror("Soundblaster DSP Reset port undocumented read\n"); + return 0xff; +} + +WRITE8_MEMBER( sb_device::dsp_reset_w ) +{ +// printf("%02x to DSP reset @ %x\n", data, offset); + if(offset) + return; + + // a reset while in UART MIDI mode simply restores the previous + // operating state (page 5-3 of the Creative manual). + if (!m_uart_midi) + { + if(data == 0 && m_dsp.reset_latch == 1) + { + // reset routine + m_dsp.fifo_ptr = 0; + m_dsp.fifo_r_ptr = 0; + for(int i=0;i < 15; i++) + { + m_dsp.fifo[i] = 0; + m_dsp.fifo_r[i] = 0; + } + queue_r(0xaa); // reset OK ID + } + + m_dsp.reset_latch = data; + drq_w(0); + m_dsp.dma_autoinit = 0; + irq_w(0, IRQ_ALL); + m_timer->adjust(attotime::never, 0); + m_dsp.d_rptr = 0; + m_dsp.d_wptr = 0; + m_dsp.dma_throttled = false; + m_dsp.dma_timer_started = false; + } + + m_onebyte_midi = false; + m_uart_midi = false; + m_uart_irq = false; + m_mpu_midi = false; + m_tx_busy = false; + m_xmit_read = m_xmit_write = 0; + m_recv_read = m_recv_write = 0; + m_rx_waiting = m_tx_waiting = 0; + + //printf("%02x\n",data); +} + +READ8_MEMBER( sb_device::dsp_data_r ) +{ +// printf("read DSP data @ %x\n", offset); + if(offset) + return 0xff; + + if (m_uart_midi) + { + UINT8 rv = m_recvring[m_recv_read++]; + if (m_recv_read >= MIDI_RING_SIZE) + { + m_recv_read = 0; + } + + if (m_rx_waiting) + { + m_rx_waiting--; + } + + return rv; + } + + return dequeue_r(); +} + +WRITE8_MEMBER( sb_device::dsp_data_w ) +{ +// printf("%02x to DSP data @ %x\n", data, offset); + if(offset) + return; + logerror("Soundblaster DSP data port undocumented write\n"); +} + +READ8_MEMBER(sb_device::dsp_rbuf_status_r) +{ +// printf("read Rbufstat @ %x\n", offset); + + if(offset) + { + if(m_dsp.version > 0x0400) + irq_w(0, IRQ_DMA16); + return 0xff; + } + +// printf("Clear IRQ5\n"); + irq_w(0, IRQ_DMA8); // reading this port ACKs the card's IRQ, 8-bit dma only? + + // in either SB-MIDI mode, bit 7 indicates if a character is available + // to read. + if (m_uart_midi || m_onebyte_midi) + { + if (m_rx_waiting) + { + return 0x80; + } + + return 0x00; + } + + return m_dsp.rbuf_status; +} + +READ8_MEMBER(sb_device::dsp_wbuf_status_r) +{ +// printf("read Wbufstat @ %x\n", offset); + + if(offset) + return 0xff; + + // in either SB-MIDI mode, bit 7 indicates if there's space to write. + // set = buffer full + if (m_uart_midi || m_onebyte_midi) + { + if (m_tx_waiting >= MIDI_RING_SIZE) + { + return 0x80; + } + + return 0x00; + } + + return m_dsp.wbuf_status; +} + +WRITE8_MEMBER(sb_device::dsp_rbuf_status_w) +{ +// printf("%02x to Rbufstat @ %x\n", data, offset); + if(offset) + return; + + logerror("Soundblaster DSP Read Buffer status undocumented write\n"); +} + +void sb_device::process_fifo(UINT8 cmd) +{ + if (m_cmd_fifo_length[cmd] == -1) + { + logerror("SB: unemulated or undefined fifo command %02x\n",cmd); + m_dsp.fifo_ptr = 0; + } + else if(m_dsp.fifo_ptr == m_cmd_fifo_length[cmd]) + { + /* get FIFO params */ +// printf("SB FIFO command: %02x\n", cmd); + switch(cmd) + { + case 0x10: // Direct DAC + break; + + case 0x14: // 8-bit DMA, no autoinit + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; +// printf("Start DMA (not autoinit, size = %x)\n", m_dsp.dma_length); + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 0; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = 0; + break; + + case 0x17: // 2-bit ADPCM w/new reference + m_dsp.adpcm_new_ref = true; + m_dsp.adpcm_step = 0; + case 0x16: // 2-bit ADPCM + m_dsp.adpcm_count = 0; + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 0; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = ADPCM2; + break; + + case 0x1c: // 8-bit DMA with autoinit + // printf("Start DMA (autoinit, size = %x)\n", m_dsp.dma_length); + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 1; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = 0; + break; + + case 0x24: // 8-bit ADC DMA + m_dsp.adc_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + // printf("Start DMA (not autoinit, size = %x)\n", m_dsp.adc_length); + m_dsp.adc_transferred = 0; + m_dsp.dma_autoinit = 0; + drq_w(1); + logerror("SB: ADC capture unimplemented\n"); + break; + + case 0x34: + m_uart_midi = true; + m_uart_irq = false; + break; + + case 0x35: + m_uart_midi = true; + m_uart_irq = true; + break; + + case 0x36: + case 0x37: // Enter UART mode + printf("timestamp MIDI mode not supported, contact MESSDEV!\n"); + break; + + case 0x38: // single-byte MIDI send + m_onebyte_midi = true; + break; + + case 0x40: // set time constant + m_dsp.frequency = (1000000 / (256 - m_dsp.fifo[1])); + //printf("Set time constant: %02x -> %d\n", m_dsp.fifo[1], m_dsp.frequency); + break; + + case 0x48: // set DMA block size (for auto-init) + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + break; + + case 0x75: // 4-bit ADPCM w/new reference + m_dsp.adpcm_new_ref = true; + m_dsp.adpcm_step = 0; + case 0x74: // 4-bit ADPCM + m_dsp.adpcm_count = 0; + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 0; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = ADPCM4; + break; + + case 0x77: // 2.6-bit ADPCM w/new reference + m_dsp.adpcm_new_ref = true; + m_dsp.adpcm_step = 0; + case 0x76: // 2.6-bit ADPCM + m_dsp.adpcm_count = 0; + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 0; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = ADPCM3; + break; + + case 0xd0: // halt 8-bit DMA + m_timer->adjust(attotime::never, 0); + drq_w(0); // drop DRQ + m_dsp.dma_throttled = false; + m_dsp.dma_timer_started = false; + break; + + case 0xd1: // speaker on + // ... + m_dsp.speaker_on = 1; + break; + + case 0xd3: // speaker off + // ... + m_dsp.speaker_on = 0; + break; + + case 0xd8: // speaker status + queue_r(m_dsp.speaker_on ? 0xff : 0x00); + break; + + case 0xe0: // get DSP identification + queue_r(m_dsp.fifo[1] ^ 0xff); + break; + + case 0xe1: // get DSP version + queue_r(m_dsp.version >> 8); + queue_r(m_dsp.version & 0xff); + break; + + case 0xe2: // DSP protection + m_dsp.prot_value += protection_magic[m_dsp.prot_count++] ^ m_dsp.fifo[1]; + m_dsp.prot_count &= 3; + m_dsp.adc_transferred = 0; + m_dsp.adc_length = 1; + m_dsp.wbuf_status = 0x80; + m_dsp.dma_no_irq = true; + m_dack_out = (UINT8)(m_dsp.prot_value & 0xff); + drq_w(1); + break; + + case 0xe4: // write test register + m_dsp.test_reg = m_dsp.fifo[1]; + break; + + case 0xe8: // read test register + queue_r(m_dsp.test_reg); + break; + + case 0xf2: // send PIC irq + irq_w(1, IRQ_DMA8); + break; + + case 0xf8: // ??? + logerror("SB: Unknown command write 0xf8\n"); + queue_r(0); + break; + default: + if(m_dsp.version >= 0x0201) // SB 2.0 + { + switch(cmd) + { + case 0x1f: // 2-bit autoinit ADPCM w/new reference + m_dsp.adpcm_new_ref = true; + m_dsp.adpcm_step = 0; + m_dsp.adpcm_count = 0; + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 1; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = ADPCM2; + break; + case 0x7d: // 4-bit autoinit ADPCM w/new reference + m_dsp.adpcm_new_ref = true; + m_dsp.adpcm_step = 0; + m_dsp.adpcm_count = 0; + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 1; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = ADPCM4; + break; + case 0x7f: // 2.6-bit autoinit ADPCM w/new reference + m_dsp.adpcm_new_ref = true; + m_dsp.adpcm_step = 0; + m_dsp.adpcm_count = 0; + m_dsp.dma_length = (m_dsp.fifo[1] + (m_dsp.fifo[2]<<8)) + 1; + m_dsp.dma_transferred = 0; + m_dsp.dma_autoinit = 1; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + drq_w(1); + m_dsp.flags = ADPCM3; + break; + case 0xda: // stop 8-bit autoinit + m_dsp.dma_autoinit = 0; + break; + } + } + if(m_dsp.version >= 0x0301) // SB Pro 2 + { + switch(cmd) + { + case 0xe3: // copyright notice, check if in pro 2 + const char* copyright = "NOT COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; + int j = strlen(copyright); + for(int k = 4; k <= j; k++) + queue_r(copyright[k]); + break; + } + } + if(m_dsp.version >= 0x0400) // SB16 + { + int mode; + switch(cmd) + { + case 0x0f: // read asp reg + queue_r(0); + case 0x0e: // write asp reg + case 0x02: // get asp version + case 0x04: // set asp mode register + case 0x05: // set asp codec param + logerror("SB16: unimplemented ASP command\n"); + break; + case 0x41: // set output sample rate + m_dsp.frequency = m_dsp.fifo[2] + (m_dsp.fifo[1] << 8); + break; + case 0x42: // set input sample rate + m_dsp.adc_freq = m_dsp.fifo[2] + (m_dsp.fifo[1] << 8); + break; + case 0xd5: // pause 16-bit dma + m_timer->adjust(attotime::never, 0); + drq16_w(0); // drop DRQ + m_dsp.dma_throttled = false; + m_dsp.dma_timer_started = false; + break; + case 0xd6: // resume 16-bit dma + logerror("SB: 16-bit dma resume\n"); + break; + case 0xd9: // stop 16-bit autoinit + m_dsp.dma_autoinit = 0; + break; + case 0xb0: + case 0xb6: + case 0xc0: + case 0xc6: + mode = m_dsp.fifo[1]; + m_dsp.flags = 0; + m_dsp.dma_length = (m_dsp.fifo[2] + (m_dsp.fifo[3]<<8)) + 1; + if((cmd & 0xf0) == 0xb0) + { + m_dsp.flags |= SIXTEENBIT; + m_dsp.dma_length <<= 1; + drq16_w(1); + } + else + drq_w(1); + if(cmd & 0x04) + m_dsp.dma_autoinit = 1; + if(mode & 0x10) + m_dsp.flags |= SIGNED; + if(mode & 0x20) + { + m_dsp.flags |= STEREO; + m_dsp.dma_length <<= 1; + } + m_dsp.dma_transferred = 0; + m_dsp.dma_timer_started = false; + m_dsp.dma_throttled = false; + break; + case 0xb8: + case 0xbe: + case 0xc8: + case 0xce: + mode = m_dsp.fifo[1]; + m_dsp.adc_length = (m_dsp.fifo[2] + (m_dsp.fifo[3]<<8)) + 1; + m_dsp.adc_transferred = 0; + if(cmd & 0x04) + m_dsp.dma_autoinit = 1; + if(mode & 0x20) + m_dsp.adc_length <<= 1; + if((cmd & 0xf0) == 0xb0) + { + m_dsp.adc_length <<= 1; + drq16_w(1); + } + else + drq_w(1); + logerror("SB: ADC capture unimplemented\n"); + break; + case 0xf3: // send PIC irq + irq_w(1, IRQ_DMA16); + break; + case 0xfc: + queue_r((((m_dsp.flags & SIXTEENBIT) && m_dsp.dma_autoinit) << 4) | ((!(m_dsp.flags & SIXTEENBIT) && m_dsp.dma_autoinit) << 2)); + break; + } + } + } + m_dsp.fifo_ptr = 0; + } +} + +WRITE8_MEMBER(sb_device::dsp_cmd_w) +{ +// printf("%02x to DSP command @ %x\n", data, offset); + + if(offset) + return; + + if (m_uart_midi || m_onebyte_midi) + { + xmit_char(data); + m_onebyte_midi = false; // clear onebyte (if this is uart, that's harmless) + return; + } + + queue(data); + + process_fifo(m_dsp.fifo[0]); +} + +void sb_device::adpcm_decode(UINT8 sample, int size) +{ + const UINT8 adpcm_2_table[] = {0, 1, 1, 3, 2, 6, 4, 12, 8, 24, 16, 48}; + const UINT8 step_2_table[] = {0, 2, 0, 4, 2, 6, 4, 8, 6, 10, 8, 10}; + + const UINT8 adpcm_3_table[] = {0, 1, 2, 3, 1, 3, 5, 7, + 2, 6, 10, 14, 4, 12, 20, 28, + 8, 24, 40, 56}; + const UINT8 step_3_table[] = {0, 0, 0, 4, 0, 4, 4, 8, + 4, 8, 8, 12, 8, 12, 12, 16, + 12, 16, 16, 16}; + + const UINT8 adpcm_4_table[] = {0, 1, 2, 3, 4, 5, 6, 7, + 1, 3, 5, 7, 9, 11, 13, 15, + 2, 6, 10, 14, 18, 22, 26, 30, + 4, 12, 20, 28, 36, 44, 52, 60}; + const UINT8 step_4_table[] = {0, 0, 0, 0, 0, 8, 8, 8, + 0, 8, 8, 8, 8, 16, 16, 16, + 8, 16, 16, 16, 16, 24, 24, 24, + 16, 24, 24, 24, 24, 24, 24, 24}; + + INT16 dec_sample = m_dsp.adpcm_ref; + UINT8 index; + switch(size) + { + case 2: + index = (sample & 1) | m_dsp.adpcm_step; + dec_sample += ((sample & 2)?-1:1) * adpcm_2_table[index]; + m_dsp.adpcm_step = step_2_table[index]; + break; + case 3: + index = (sample & 3) | m_dsp.adpcm_step; + dec_sample += ((sample & 4)?-1:1) * adpcm_3_table[index]; + m_dsp.adpcm_step = step_3_table[index]; + break; + case 4: + index = (sample & 7) | m_dsp.adpcm_step; + dec_sample += ((sample & 8)?-1:1) * adpcm_4_table[index]; + m_dsp.adpcm_step = step_4_table[index]; + break; + } + + if(dec_sample > 255) + dec_sample = 255; + else if(dec_sample < 0) + dec_sample = 0; + m_dsp.adpcm_ref = dec_sample; + m_dacl->write_unsigned8(m_dsp.adpcm_ref); + m_dacr->write_unsigned8(m_dsp.adpcm_ref); +} + +READ8_MEMBER( sb16_device::mpu401_r ) +{ + UINT8 res; + + irq_w(0, IRQ_MPU); + if(offset == 0) // data + { + res = m_recvring[m_recv_read++]; + if (m_recv_read >= MIDI_RING_SIZE) + { + m_recv_read = 0; + } + + if (m_rx_waiting) + { + m_rx_waiting--; + } + } + else // status + { + res = 0; + if (m_tx_waiting >= MIDI_RING_SIZE) + { + res |= 0x40; // tx full + } + if (m_rx_waiting == 0) + { + res |= 0x80; // rx empty + } + } + + return res; +} + +WRITE8_MEMBER( sb16_device::mpu401_w ) +{ + if(offset == 0) // data + { + logerror("SB MPU401:%02x %02x\n",offset,data); + if (m_mpu_midi) + { + xmit_char(data); + } + } + else // command + { + logerror("SB MPU401:%02x %02x\n",offset,data); + + switch(data) + { + case 0x3f: // enter MPU-401 UART mode + irq_w(1, IRQ_MPU); + m_recv_read = m_recv_write = 0; + m_xmit_read = m_xmit_write = m_tx_waiting = 0; + m_recvring[m_recv_write++] = 0xfe; + m_rx_waiting = 1; + m_mpu_midi = true; + break; + + case 0xff: // reset + irq_w(1, IRQ_MPU); + m_recv_read = m_recv_write = 0; + m_recvring[m_recv_write++] = 0xfe; + m_rx_waiting = 1; + m_mpu_midi = false; + break; + } + } + +} + +void sb16_device::mixer_set() +{ + ymf262_device *ymf = subdevice<ymf262_device>("ymf262"); + float lmain = m_mixer.main_vol[0]/248.0; + float rmain = m_mixer.main_vol[1]/248.0; + m_dacl->set_output_gain(ALL_OUTPUTS, lmain*(m_mixer.dac_vol[0]/248.0f)); + m_dacr->set_output_gain(ALL_OUTPUTS, rmain*(m_mixer.dac_vol[1]/248.0f)); + ymf->set_output_gain(0, lmain*(m_mixer.fm_vol[0]/248.0f)); + ymf->set_output_gain(1, rmain*(m_mixer.fm_vol[1]/248.0f)); + ymf->set_output_gain(2, lmain*(m_mixer.fm_vol[0]/248.0f)); + ymf->set_output_gain(3, rmain*(m_mixer.fm_vol[1]/248.0f)); +} + +void sb16_device::mixer_reset() +{ + m_mixer.status = 0x80; + m_mixer.main_vol[0] = 0xc0; + m_mixer.main_vol[1] = 0xc0; + m_mixer.dac_vol[0] = 0xc0; + m_mixer.dac_vol[1] = 0xc0; + m_mixer.fm_vol[0] = 0xc0; + m_mixer.fm_vol[1] = 0xc0; + m_mixer.cd_vol[0] = 0x00; + m_mixer.cd_vol[1] = 0x00; + m_mixer.line_vol[0] = 0x00; + m_mixer.line_vol[1] = 0x00; + m_mixer.mic_vol = 0x00; + m_mixer.pc_speaker_vol = 0x00; + m_mixer.output_ctl = 0x1f; + m_mixer.input_ctl[0] = 0x15; + m_mixer.input_ctl[1] = 0x0b; + m_mixer.input_gain[0] = 0x00; + m_mixer.input_gain[1] = 0x00; + m_mixer.output_gain[0] = 0x00; + m_mixer.output_gain[1] = 0x00; + m_mixer.agc = 0x00; + m_mixer.treble[0] = 0x80; + m_mixer.treble[1] = 0x80; + m_mixer.bass[0] = 0x80; + m_mixer.bass[1] = 0x80; + mixer_set(); +} + +READ8_MEMBER( sb16_device::mixer_r ) +{ + if(offset == 0) + return m_mixer.status; + return m_mixer.data; +} + +WRITE8_MEMBER( sb16_device::mixer_w ) +{ + if(offset == 0) + { + switch(data) + { + case 0x00: + mixer_reset(); + return; + case 0x01: + m_mixer.data = m_mixer.status; + break; + case 0x04: + m_mixer.data = (m_mixer.dac_vol[0] & 0xf0) | (m_mixer.dac_vol[1] >> 4); + break; + case 0x0a: + m_mixer.data = m_mixer.mic_vol >> 5; + break; + case 0x22: + m_mixer.data = (m_mixer.main_vol[0] & 0xf0) | (m_mixer.main_vol[1] >> 4); + break; + case 0x26: + m_mixer.data = (m_mixer.fm_vol[0] & 0xf0) | (m_mixer.fm_vol[1] >> 4); + break; + case 0x28: + m_mixer.data = (m_mixer.cd_vol[0] & 0xf0) | (m_mixer.cd_vol[1] >> 4); + break; + case 0x2e: + m_mixer.data = (m_mixer.line_vol[0] & 0xf0) | (m_mixer.line_vol[1] >> 4); + break; + case 0x30: + case 0x31: + m_mixer.data = m_mixer.main_vol[data & 1]; + break; + case 0x32: + case 0x33: + m_mixer.data = m_mixer.dac_vol[data & 1]; + break; + case 0x34: + case 0x35: + m_mixer.data = m_mixer.fm_vol[data & 1]; + break; + case 0x36: + case 0x37: + m_mixer.data = m_mixer.cd_vol[data & 1]; + break; + case 0x38: + case 0x39: + m_mixer.data = m_mixer.line_vol[data & 1]; + break; + case 0x3a: + m_mixer.data = m_mixer.mic_vol; + break; + case 0x3b: + m_mixer.data = m_mixer.pc_speaker_vol; + break; + case 0x3c: + m_mixer.data = m_mixer.output_ctl; + break; + case 0x3d: + case 0x3e: + m_mixer.data = m_mixer.input_ctl[(data + 1) & 1]; + break; + case 0x3f: + case 0x40: + m_mixer.data = m_mixer.input_gain[(data + 1) & 1]; + break; + case 0x41: + case 0x42: + m_mixer.data = m_mixer.output_gain[(data + 1) & 1]; + break; + case 0x43: + m_mixer.data = m_mixer.agc; + break; + case 0x44: + case 0x45: + m_mixer.data = m_mixer.treble[data & 1]; + break; + case 0x46: + case 0x47: + m_mixer.data = m_mixer.bass[data & 1]; + break; + case 0x80: + m_mixer.data = 0x12; // irq5 + break; + case 0x81: + m_mixer.data = 0x22; // dma1&5 + break; + case 0x82: + m_mixer.data = m_dsp.irq_active | 0x20; + break; + default: + logerror("SB: Unimplemented mixer index %02x\n", data); + m_mixer.status = data | 0x80; + m_mixer.data = 0x0a; + return; + } + m_mixer.status = data; + return; + } + switch(m_mixer.status) + { + case 0x04: + m_mixer.dac_vol[0] = (data & 0xf0) | 8; + m_mixer.dac_vol[1] = (data << 4) | 8; + break; + case 0x0a: + m_mixer.mic_vol = (data << 5) | 0x18; + break; + case 0x22: + m_mixer.main_vol[0] = (data & 0xf0) | 8; + m_mixer.main_vol[1] = (data << 4) | 8; + break; + case 0x26: + m_mixer.fm_vol[0] = (data & 0xf0) | 8; + m_mixer.fm_vol[1] = (data << 4) | 8; + break; + case 0x28: + m_mixer.cd_vol[0] = (data & 0xf0) | 8; + m_mixer.cd_vol[1] = (data << 4) | 8; + break; + case 0x2e: + m_mixer.line_vol[0] = (data & 0xf0) | 8; + m_mixer.line_vol[1] = (data << 4) | 8; + break; + case 0x30: + case 0x31: + m_mixer.main_vol[m_mixer.status & 1] = data & 0xf8; + break; + case 0x32: + case 0x33: + m_mixer.dac_vol[m_mixer.status & 1] = data & 0xf8; + break; + case 0x34: + case 0x35: + m_mixer.fm_vol[m_mixer.status & 1] = data & 0xf8; + break; + case 0x36: + case 0x37: + m_mixer.cd_vol[m_mixer.status & 1] = data & 0xf8; + break; + case 0x38: + case 0x39: + m_mixer.line_vol[m_mixer.status & 1] = data & 0xf8; + break; + case 0x3a: + m_mixer.mic_vol = data & 0xf8; + break; + case 0x3b: + m_mixer.pc_speaker_vol = data & 0xc0; + break; + case 0x3c: + m_mixer.output_ctl = data & 0x1f; + break; + case 0x3d: + case 0x3e: + m_mixer.input_ctl[(m_mixer.status + 1) & 1] = data & 0x7f; + break; + case 0x3f: + case 0x40: + m_mixer.input_gain[(m_mixer.status + 1) & 1] = data & 0xc0; + break; + case 0x41: + case 0x42: + m_mixer.output_gain[(m_mixer.status + 1) & 1] = data & 0xc0; + break; + case 0x43: + m_mixer.agc = data & 1; + break; + case 0x44: + case 0x45: + m_mixer.treble[m_mixer.status & 1] = data & 0xf0; + break; + case 0x46: + case 0x47: + m_mixer.bass[m_mixer.status & 1] = data & 0xf0; + break; + case 0x80: + case 0x81: + // don't support these yet + break; + default: + return; + } + m_mixer.data = data; + mixer_set(); + return; +} + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type ISA8_SOUND_BLASTER_1_0 = &device_creator<isa8_sblaster1_0_device>; +const device_type ISA8_SOUND_BLASTER_1_5 = &device_creator<isa8_sblaster1_5_device>; +const device_type ISA16_SOUND_BLASTER_16 = &device_creator<isa16_sblaster16_device>; + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor isa8_sblaster1_0_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sblaster1_0_config ); +} + +machine_config_constructor isa8_sblaster1_5_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sblaster1_5_config ); +} + +machine_config_constructor isa16_sblaster16_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sblaster_16_config ); +} + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +sb_device::sb_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, UINT32 clock, const char *name, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_serial_interface(mconfig, *this), + m_dacl(*this, "sbdacl"), + m_dacr(*this, "sbdacr"), + m_joy(*this, "pc_joy"), + m_mdout(*this, "mdout") +{ +} + +sb8_device::sb8_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, UINT32 clock, const char *name, const char *shortname, const char *source) : + sb_device(mconfig, type, tag, owner, clock, name, shortname, source), + device_isa8_card_interface(mconfig, *this), + m_ym3812(*this, "ym3812") +{ +} + +sb16_device::sb16_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, UINT32 clock, const char *name, const char *shortname, const char *source) : + sb_device(mconfig, type, tag, owner, clock, name, shortname, source), + device_isa16_card_interface(mconfig, *this) +{ +} + +//------------------------------------------------- +// isa8_sblaster_device - constructor +//------------------------------------------------- + +isa8_sblaster1_0_device::isa8_sblaster1_0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + sb8_device(mconfig, ISA8_SOUND_BLASTER_1_0, tag, owner, clock, "Sound Blaster 1.0", "isa_sblaster1_0", __FILE__), + m_saa1099_1(*this, "saa1099.1"), + m_saa1099_2(*this, "saa1099.2") +{ +} + +isa8_sblaster1_5_device::isa8_sblaster1_5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + sb8_device(mconfig, ISA8_SOUND_BLASTER_1_5, tag, owner, clock, "Sound Blaster 1.5", "isa_sblaster1_5", __FILE__) +{ +} + +isa16_sblaster16_device::isa16_sblaster16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + sb16_device(mconfig, ISA16_SOUND_BLASTER_16, tag, owner, clock, "Sound Blaster 16", "isa_sblaster_16", __FILE__) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sb8_device::device_start() +{ + m_isa->install_device(0x0200, 0x0207, 0, 0, read8_delegate(FUNC(pc_joy_device::joy_port_r), subdevice<pc_joy_device>("pc_joy")), write8_delegate(FUNC(pc_joy_device::joy_port_w), subdevice<pc_joy_device>("pc_joy"))); + m_isa->install_device(0x0226, 0x0227, 0, 0, read8_delegate(FUNC(sb_device::dsp_reset_r), this), write8_delegate(FUNC(sb_device::dsp_reset_w), this)); + m_isa->install_device(0x022a, 0x022b, 0, 0, read8_delegate(FUNC(sb_device::dsp_data_r), this), write8_delegate(FUNC(sb_device::dsp_data_w), this) ); + m_isa->install_device(0x022c, 0x022d, 0, 0, read8_delegate(FUNC(sb_device::dsp_wbuf_status_r), this), write8_delegate(FUNC(sb_device::dsp_cmd_w), this) ); + m_isa->install_device(0x022e, 0x022f, 0, 0, read8_delegate(FUNC(sb_device::dsp_rbuf_status_r), this), write8_delegate(FUNC(sb_device::dsp_rbuf_status_w), this) ); + if(m_dsp.version >= 0x0301) + { + ymf262_device *ymf262 = subdevice<ymf262_device>("ymf262"); + + m_isa->install_device(0x0388, 0x038b, 0, 0, read8_delegate(FUNC(ymf262_device::read), ymf262), write8_delegate(FUNC(ymf262_device::write), ymf262)); + m_isa->install_device(0x0220, 0x0223, 0, 0, read8_delegate(FUNC(ymf262_device::read), ymf262), write8_delegate(FUNC(ymf262_device::write), ymf262)); + m_isa->install_device(0x0228, 0x0229, 0, 0, read8_delegate(FUNC(ymf262_device::read), ymf262), write8_delegate(FUNC(ymf262_device::write), ymf262)); + } + else + { + m_isa->install_device(0x0388, 0x0389, 0, 0, read8_delegate( FUNC(sb8_device::ym3812_16_r), this ), write8_delegate( FUNC(sb8_device::ym3812_16_w), this ) ); + m_isa->install_device(0x0228, 0x0229, 0, 0, read8_delegate( FUNC(sb8_device::ym3812_16_r), this ), write8_delegate( FUNC(sb8_device::ym3812_16_w), this ) ); + } + + m_timer = timer_alloc(0, NULL); + + save_item(NAME(m_dack_out)); + save_item(NAME(m_onebyte_midi)); + save_item(NAME(m_uart_midi)); + save_item(NAME(m_uart_irq)); + save_item(NAME(m_mpu_midi)); + save_item(NAME(m_rx_waiting)); + save_item(NAME(m_tx_waiting)); + save_item(NAME(m_recvring)); + save_item(NAME(m_xmitring)); + save_item(NAME(m_xmit_read)); + save_item(NAME(m_xmit_write)); + save_item(NAME(m_recv_read)); + save_item(NAME(m_recv_write)); + save_item(NAME(m_tx_busy)); +} + +void isa8_sblaster1_0_device::device_start() +{ + set_isa_device(); + // 1.0 always has the SAA1099s for CMS back-compatibility + m_isa->install_device(0x0220, 0x0221, 0, 0, read8_delegate( FUNC(isa8_sblaster1_0_device::saa1099_16_r), this ), write8_delegate( FUNC(isa8_sblaster1_0_device::saa1099_1_16_w), this ) ); + m_isa->install_device(0x0222, 0x0223, 0, 0, read8_delegate( FUNC(isa8_sblaster1_0_device::saa1099_16_r), this ), write8_delegate( FUNC(isa8_sblaster1_0_device::saa1099_2_16_w), this ) ); + m_isa->set_dma_channel(1, this, FALSE); + m_dsp.version = 0x0105; + sb8_device::device_start(); +} + +void isa8_sblaster1_5_device::device_start() +{ + set_isa_device(); + /* 1.5 makes CM/S support optional (empty sockets, but they work if the user populates them!) */ + m_isa->set_dma_channel(1, this, FALSE); + m_dsp.version = 0x0200; + sb8_device::device_start(); +} + +void sb16_device::device_start() +{ + ymf262_device *ymf262 = subdevice<ymf262_device>("ymf262"); + + m_isa->install_device(0x0200, 0x0207, 0, 0, read8_delegate(FUNC(pc_joy_device::joy_port_r), subdevice<pc_joy_device>("pc_joy")), write8_delegate(FUNC(pc_joy_device::joy_port_w), subdevice<pc_joy_device>("pc_joy"))); + m_isa->install_device(0x0224, 0x0225, 0, 0, read8_delegate(FUNC(sb16_device::mixer_r), this), write8_delegate(FUNC(sb16_device::mixer_w), this)); + m_isa->install_device(0x0226, 0x0227, 0, 0, read8_delegate(FUNC(sb_device::dsp_reset_r), this), write8_delegate(FUNC(sb_device::dsp_reset_w), this)); + m_isa->install_device(0x022a, 0x022b, 0, 0, read8_delegate(FUNC(sb_device::dsp_data_r), this), write8_delegate(FUNC(sb_device::dsp_data_w), this) ); + m_isa->install_device(0x022c, 0x022d, 0, 0, read8_delegate(FUNC(sb_device::dsp_wbuf_status_r), this), write8_delegate(FUNC(sb_device::dsp_cmd_w), this) ); + m_isa->install_device(0x022e, 0x022f, 0, 0, read8_delegate(FUNC(sb_device::dsp_rbuf_status_r), this), write8_delegate(FUNC(sb_device::dsp_rbuf_status_w), this) ); + m_isa->install_device(0x0330, 0x0331, 0, 0, read8_delegate(FUNC(sb16_device::mpu401_r), this), write8_delegate(FUNC(sb16_device::mpu401_w), this)); + m_isa->install_device(0x0388, 0x038b, 0, 0, read8_delegate(FUNC(ymf262_device::read), ymf262), write8_delegate(FUNC(ymf262_device::write), ymf262)); + m_isa->install_device(0x0220, 0x0223, 0, 0, read8_delegate(FUNC(ymf262_device::read), ymf262), write8_delegate(FUNC(ymf262_device::write), ymf262)); + m_isa->install_device(0x0228, 0x0229, 0, 0, read8_delegate(FUNC(ymf262_device::read), ymf262), write8_delegate(FUNC(ymf262_device::write), ymf262)); + + m_timer = timer_alloc(0, NULL); + + save_item(NAME(m_mixer.data)); + save_item(NAME(m_mixer.status)); + save_item(NAME(m_mixer.main_vol)); + save_item(NAME(m_mixer.dac_vol)); + save_item(NAME(m_mixer.fm_vol)); + save_item(NAME(m_mixer.cd_vol)); + save_item(NAME(m_mixer.line_vol)); + save_item(NAME(m_mixer.mic_vol)); + save_item(NAME(m_mixer.pc_speaker_vol)); + save_item(NAME(m_mixer.output_ctl)); + save_item(NAME(m_mixer.input_ctl)); + save_item(NAME(m_mixer.input_gain)); + save_item(NAME(m_mixer.output_gain)); + save_item(NAME(m_mixer.agc)); + save_item(NAME(m_mixer.treble)); + save_item(NAME(m_mixer.bass)); +} + +void isa16_sblaster16_device::device_start() +{ + set_isa_device(); + m_isa->set_dma_channel(1, this, FALSE); + m_isa->set_dma_channel(5, this, FALSE); + m_dsp.version = 0x0405; // diagnose.exe rejects anything lower than 0x0402 + sb16_device::device_start(); +} + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void sb_device::device_reset() +{ + m_dsp.prot_value = 0xaa; + m_dsp.prot_count = 0; + m_dack_out = 0; + m_dsp.fifo_ptr = 0; + m_dsp.fifo_r_ptr = 0; + m_dsp.wbuf_status = 0; + m_dsp.rbuf_status = 0; + m_dsp.frequency = 8000; // per stereo-fx + m_dsp.irq_active = 0; + m_dsp.dma_no_irq = false; + mixer_reset(); + + m_onebyte_midi = false; + m_uart_midi = false; + m_uart_irq = false; + m_mpu_midi = false; + m_tx_busy = false; + m_xmit_read = m_xmit_write = 0; + m_recv_read = m_recv_write = 0; + m_rx_waiting = m_tx_waiting = 0; + + // MIDI is 31250 baud, 8-N-1 + set_data_frame(1, 8, PARITY_NONE, STOP_BITS_1); + set_rate(31250); +} + +UINT8 sb_device::dack_r(int line) +{ + m_dsp.adc_transferred++; + if(m_dsp.adc_transferred >= m_dsp.adc_length) + { + drq_w(0); + + if (m_dsp.dma_autoinit) + { + m_dsp.adc_transferred = 0; + drq_w(1); + } + else + m_dsp.wbuf_status = 0; + if(!m_dsp.dma_no_irq) + irq_w(1, IRQ_DMA8); + else + m_dsp.dma_no_irq = false; + } + else + drq_w(1); + return m_dack_out; +} + +UINT16 sb16_device::dack16_r(int line) +{ + m_dsp.adc_transferred += 2; + if (m_dsp.adc_transferred >= m_dsp.adc_length) + { + drq16_w(0); + + if (m_dsp.dma_autoinit) + { + m_dsp.adc_transferred = 0; + drq16_w(1); + } + irq_w(1, IRQ_DMA16); + } + else + drq16_w(1); + return m_dack_out; +} + +void sb16_device::dack16_w(int line, UINT16 data) +{ + // set the transfer timer on the 1st byte + if (!m_dsp.dma_timer_started) + { + m_timer->adjust(attotime::from_hz((double)m_dsp.frequency), 0, attotime::from_hz((double)m_dsp.frequency)); + m_dsp.d_rptr = m_dsp.d_wptr = 0; + m_dsp.dma_timer_started = true; + } + + m_dsp.data[m_dsp.d_wptr++] = data & 0xff; + m_dsp.data[m_dsp.d_wptr++] = data >> 8; + m_dsp.d_wptr %= 128; + + if (m_dsp.d_wptr == m_dsp.d_rptr) + { +// printf("throttling DRQ\n"); + drq16_w(0); // drop DRQ here + m_dsp.dma_throttled = true; + } + + m_dsp.dma_transferred += 2; + if (m_dsp.dma_transferred >= m_dsp.dma_length) + { +// printf("DMA fill completed (%d out of %d)\n", m_dsp.dma_transferred, m_dsp.dma_length); + + drq16_w(0); // drop DRQ here + + if (m_dsp.dma_autoinit) + { +// printf("autoinit reset\n"); + m_dsp.dma_transferred = 0; + if (!m_dsp.dma_throttled) // if we're not throttled, re-raise DRQ right now + { + drq16_w(1); // raise DRQ again (page 3-15 of the Creative manual indicates auto-init will keep going until you stop it) + } + } + + irq_w(1, IRQ_DMA16); // raise IRQ as per the Creative manual + } +} + +/* TODO: this mustn't be instant! */ +void sb_device::dack_w(int line, UINT8 data) +{ +// printf("dack_w: line %x data %02x\n", line, data); +// if(data != 0x80) +// printf("%02x\n",data); + + // set the transfer timer on the 1st byte + if (!m_dsp.dma_timer_started) + { + m_timer->adjust(attotime::from_hz((double)m_dsp.frequency), 0, attotime::from_hz((double)m_dsp.frequency)); + m_dsp.d_rptr = m_dsp.d_wptr = 0; + m_dsp.dma_timer_started = true; + } + + m_dsp.data[m_dsp.d_wptr++] = data; + m_dsp.d_wptr %= 128; + + if (m_dsp.d_wptr == m_dsp.d_rptr) + { +// printf("throttling DRQ\n"); + drq_w(0); // drop DRQ here + m_dsp.dma_throttled = true; + } + + m_dsp.dma_transferred++; + if (m_dsp.dma_transferred >= m_dsp.dma_length) + { +// printf("DMA fill completed (%d out of %d)\n", m_dsp.dma_transferred, m_dsp.dma_length); + + drq_w(0); // drop DRQ here + + if (m_dsp.dma_autoinit) + { +// printf("autoinit reset\n"); + m_dsp.dma_transferred = 0; + if (!m_dsp.dma_throttled) // if we're not throttled, re-raise DRQ right now + { + drq_w(1); // raise DRQ again (page 3-15 of the Creative manual indicates auto-init will keep going until you stop it) + } + } + + irq_w(1, IRQ_DMA8); // raise IRQ as per the Creative manual + } +} + +void sb_device::device_timer(emu_timer &timer, device_timer_id tid, int param, void *ptr) +{ +// printf("DMA timer expire\n"); + if (tid) + { + device_serial_interface::device_timer(timer, tid, param, ptr); + return; + } + + UINT16 lsample, rsample; + switch (m_dsp.flags) { + case 0: // 8-bit unsigned mono + m_dacl->write_unsigned8(m_dsp.data[m_dsp.d_rptr]); + m_dacr->write_unsigned8(m_dsp.data[m_dsp.d_rptr]); + m_dsp.data[m_dsp.d_rptr++] = 0x80; + break; + case SIGNED: // 8-bit signed mono + m_dacl->write_unsigned8(m_dsp.data[m_dsp.d_rptr] + 128); + m_dacr->write_unsigned8(m_dsp.data[m_dsp.d_rptr] + 128); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + break; + case STEREO: // 8-bit unsigned stereo + m_dacl->write_unsigned8(m_dsp.data[m_dsp.d_rptr]); + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dacr->write_unsigned8(m_dsp.data[m_dsp.d_rptr]); + m_dsp.data[m_dsp.d_rptr++] = 0x80; + break; + case SIGNED | STEREO: // 8-bit signed stereo + m_dacl->write_unsigned8(m_dsp.data[m_dsp.d_rptr] + 128); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dacr->write_unsigned8(m_dsp.data[m_dsp.d_rptr] + 128); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + break; + case SIXTEENBIT: // 16-bit unsigned mono + lsample = m_dsp.data[m_dsp.d_rptr] | (m_dsp.data[m_dsp.d_rptr+1] << 8); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dacl->write_unsigned16(lsample); + m_dacr->write_unsigned16(lsample); + break; + case SIXTEENBIT | SIGNED: // 16-bit signed mono + lsample = m_dsp.data[m_dsp.d_rptr] | (m_dsp.data[m_dsp.d_rptr+1] << 8); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dacl->write_unsigned16(lsample + 32768); + m_dacr->write_unsigned16(lsample + 32768); + break; + case SIXTEENBIT | STEREO: // 16-bit unsigned stereo + lsample = m_dsp.data[m_dsp.d_rptr] | (m_dsp.data[m_dsp.d_rptr+1] << 8); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dsp.d_rptr %= 128; + rsample = m_dsp.data[m_dsp.d_rptr] | (m_dsp.data[m_dsp.d_rptr+1] << 8); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dacl->write_unsigned16(lsample); + m_dacr->write_unsigned16(rsample); + break; + case SIXTEENBIT | SIGNED | STEREO: // 16-bit signed stereo + lsample = m_dsp.data[m_dsp.d_rptr] | (m_dsp.data[m_dsp.d_rptr+1] << 8); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.d_rptr %= 128; + rsample = m_dsp.data[m_dsp.d_rptr] | (m_dsp.data[m_dsp.d_rptr+1] << 8); + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dsp.data[m_dsp.d_rptr++] = 0x00; + m_dacl->write_unsigned16(lsample + 32768); + m_dacr->write_unsigned16(rsample + 32768); + break; + case ADPCM2: + if(m_dsp.adpcm_new_ref) + { + m_dsp.adpcm_ref = m_dsp.data[m_dsp.d_rptr++]; + m_dsp.adpcm_new_ref = false; + m_dacl->write_unsigned8(m_dsp.adpcm_ref); + m_dacr->write_unsigned8(m_dsp.adpcm_ref); + break; + } + lsample = m_dsp.data[m_dsp.d_rptr]; + switch(m_dsp.adpcm_count++) + { + case 0: + adpcm_decode(lsample >> 6, 2); + break; + case 1: + adpcm_decode((lsample >> 4) & 3, 2); + break; + case 2: + adpcm_decode((lsample >> 2) & 3, 2); + break; + case 3: + adpcm_decode(lsample & 3, 2); + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dsp.adpcm_count = 0; + break; + } + break; + case ADPCM3: + if(m_dsp.adpcm_new_ref) + { + m_dsp.adpcm_ref = m_dsp.data[m_dsp.d_rptr++]; + m_dsp.adpcm_new_ref = false; + m_dacl->write_unsigned8(m_dsp.adpcm_ref); + m_dacr->write_unsigned8(m_dsp.adpcm_ref); + break; + } + lsample = m_dsp.data[m_dsp.d_rptr]; + switch(m_dsp.adpcm_count++) + { + case 0: + adpcm_decode(lsample >> 5, 3); + break; + case 1: + adpcm_decode((lsample >> 2) & 7, 3); + break; + case 2: + adpcm_decode(((lsample & 2) << 1) | (lsample & 1), 3); + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dsp.adpcm_count = 0; + break; + } + break; + case ADPCM4: + if(m_dsp.adpcm_new_ref) + { + m_dsp.adpcm_ref = m_dsp.data[m_dsp.d_rptr++]; + m_dsp.adpcm_new_ref = false; + m_dacl->write_unsigned8(m_dsp.adpcm_ref); + m_dacr->write_unsigned8(m_dsp.adpcm_ref); + break; + } + lsample = m_dsp.data[m_dsp.d_rptr]; + switch(m_dsp.adpcm_count++) + { + case 0: + adpcm_decode(lsample >> 4, 4); + break; + case 1: + adpcm_decode(lsample & 15, 4); + m_dsp.data[m_dsp.d_rptr++] = 0x80; + m_dsp.adpcm_count = 0; + break; + } + break; + default: + logerror("SB: unimplemented sample type %x\n", m_dsp.flags); + } + m_dsp.d_rptr %= 128; + + if (m_dsp.dma_throttled) + { + if (m_dsp.d_rptr == m_dsp.d_wptr) + { +// printf("unthrottling DRQ\n"); + if(m_dsp.flags & SIXTEENBIT) // 16-bit audio through 8-bit dma? + drq16_w(1); + else + drq_w(1); // raise DRQ + m_dsp.dma_throttled = false; + } + } +} + +void sb_device::rcv_complete() // Rx completed receiving byte +{ + receive_register_extract(); + UINT8 data = get_received_char(); + + // in UART MIDI mode, we set the DMA8 IRQ on receiving a character + if (m_uart_midi) + { + m_recvring[m_recv_write++] = data; + if (m_recv_write >= MIDI_RING_SIZE) + { + m_recv_write = 0; + } + + if (m_recv_write != m_recv_read) + { + m_rx_waiting++; + } + if (m_uart_irq) + { + irq_w(1, IRQ_DMA8); + } + } +} + +void sb16_device::rcv_complete() // Rx completed receiving byte +{ + receive_register_extract(); + UINT8 data = get_received_char(); + + // for UART or MPU, add character to the receive queue + if (m_uart_midi || m_mpu_midi) + { + m_recvring[m_recv_write++] = data; + if (m_recv_write >= MIDI_RING_SIZE) + { + m_recv_write = 0; + } + + if (m_recv_write != m_recv_read) + { + m_rx_waiting++; + } + + if (m_uart_irq) + { + irq_w(1, IRQ_DMA8); + } + + if (m_mpu_midi) + { + irq_w(1, IRQ_MPU); + } + } +} + +void sb_device::tra_complete() // Tx completed sending byte +{ +// printf("Tx complete\n"); + // is there more waiting to send? + if (m_tx_waiting) + { + transmit_register_setup(m_xmitring[m_xmit_read++]); + if (m_xmit_read >= MIDI_RING_SIZE) + { + m_xmit_read = 0; + } + m_tx_waiting--; + } + else + { + m_tx_busy = false; + } +} + +void sb_device::tra_callback() // Tx send bit +{ + int bit = transmit_register_get_data_bit(); + m_mdout->write_txd(bit); +} + +void sb_device::xmit_char(UINT8 data) +{ +// printf("SB: xmit %02x\n", data); + + // if tx is busy it'll pick this up automatically when it completes + if (!m_tx_busy) + { + m_tx_busy = true; + transmit_register_setup(data); + } + else + { + // tx is busy, it'll pick this up next time + m_xmitring[m_xmit_write++] = data; + if (m_xmit_write >= MIDI_RING_SIZE) + { + m_xmit_write = 0; + } + m_tx_waiting++; + } +} |