/*Jazz sample rates : 386-33 - 12kHz 486-33 - 20kHz 486-50 - 32kHz Pentium - 45kHz*/ #define _USE_MATH_DEFINES #include #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/device.h> #include <86box/dma.h> #include <86box/filters.h> #include <86box/io.h> #include <86box/midi.h> #include <86box/pic.h> #include <86box/snd_azt2316a.h> #include <86box/sound.h> #include <86box/timer.h> #include <86box/snd_sb.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> #define ADPCM_4 1 #define ADPCM_26 2 #define ADPCM_2 3 /*The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb*/ #define SB_DSP_REC_SAFEFTY_MARGIN 4096 void pollsb(void *priv); void sb_poll_i(void *priv); static int sbe2dat[4][9] = { {0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106}, { -0x01, 0x02, -0x04, 0x08, 0x10, -0x20, 0x40, -0x80, 165 }, { -0x01, 0x02, 0x04, -0x08, 0x10, -0x20, -0x40, 0x80, -151}, { 0x01, -0x02, 0x04, -0x08, -0x10, 0x20, -0x40, 0x80, 90 } }; static int sb_commands[256] = { -1, 2, -1, 0, 1, 2, -1, 0, 1, -1, -1, -1, -1, -1, 2, 1, 1, -1, -1, -1, 2, -1, 2, 2, -1, -1, -1, -1, 0, -1, -1, 0, 0, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 2, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, -1, -1, -1, -1, -1, 0, -1, 0, 2, 2, -1, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1, -1, -1, -1, 1, 0, 1, 0, 1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, 0 }; char sb16_copyright[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; uint16_t sb_dsp_versions[] = { 0, 0, 0x105, 0x200, 0x201, 0x300, 0x302, 0x405, 0x40c, 0x40d, 0x410 }; /*These tables were 'borrowed' from DOSBox*/ int8_t scaleMap4[64] = { 0, 1, 2, 3, 4, 5, 6, 7, 0, -1, -2, -3, -4, -5, -6, -7, 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, 2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30, 4, 12, 20, 28, 36, 44, 52, 60, -4, -12, -20, -28, -36, -44, -52, -60 }; uint8_t adjustMap4[64] = { 0, 0, 0, 0, 0, 16, 16, 16, 0, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0 }; int8_t scaleMap26[40] = { 0, 1, 2, 3, 0, -1, -2, -3, 1, 3, 5, 7, -1, -3, -5, -7, 2, 6, 10, 14, -2, -6, -10, -14, 4, 12, 20, 28, -4, -12, -20, -28, 5, 15, 25, 35, -5, -15, -25, -35 }; uint8_t adjustMap26[40] = { 0, 0, 0, 8, 0, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 0, 248, 0, 0, 0 }; int8_t scaleMap2[24] = { 0, 1, 0, -1, 1, 3, -1, -3, 2, 6, -2, -6, 4, 12, -4, -12, 8, 24, -8, -24, 6, 48, -16, -48 }; uint8_t adjustMap2[24] = { 0, 4, 0, 4, 252, 4, 252, 4, 252, 4, 252, 4, 252, 4, 252, 4, 252, 4, 252, 4, 252, 0, 252, 0 }; double low_fir_sb16_coef[3][SB16_NCoef]; #ifdef ENABLE_SB_DSP_LOG int sb_dsp_do_log = ENABLE_SB_DSP_LOG; static void sb_dsp_log(const char *fmt, ...) { va_list ap; if (sb_dsp_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define sb_dsp_log(fmt, ...) #endif static __inline double sinc(double x) { return sin(M_PI * x) / (M_PI * x); } static void recalc_sb16_filter(int c, int playback_freq) { /* Cutoff frequency = playback / 2 */ int n; double w; double h; double fC = ((double) playback_freq) / (double) FREQ_96000; double gain; for (n = 0; n < SB16_NCoef; n++) { /* Blackman window */ w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); /* Sinc filter */ h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); /* Create windowed-sinc filter */ low_fir_sb16_coef[c][n] = w * h; } low_fir_sb16_coef[c][(SB16_NCoef - 1) / 2] = 1.0; gain = 0.0; for (n = 0; n < SB16_NCoef; n++) gain += low_fir_sb16_coef[c][n]; /* Normalise filter, to produce unity gain */ for (n = 0; n < SB16_NCoef; n++) low_fir_sb16_coef[c][n] /= gain; } static void sb_irq_update_pic(void *priv, int set) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; if (set) picint(1 << dsp->sb_irqnum); else picintc(1 << dsp->sb_irqnum); } void sb_update_mask(sb_dsp_t *dsp, int irqm8, int irqm16, int irqm401) { int clear = 0; if (!dsp->sb_irqm8 && irqm8) clear |= 1; dsp->sb_irqm8 = irqm8; if (!dsp->sb_irqm16 && irqm16) clear |= 1; dsp->sb_irqm16 = irqm16; if (!dsp->sb_irqm401 && irqm401) clear |= 1; dsp->sb_irqm401 = irqm401; if (clear) dsp->irq_update(dsp->irq_priv, 0); } void sb_update_status(sb_dsp_t *dsp, int bit, int set) { int masked = 0; switch (bit) { default: case 0: dsp->sb_irq8 = set; masked = dsp->sb_irqm8; break; case 1: dsp->sb_irq16 = set; masked = dsp->sb_irqm16; break; case 2: dsp->sb_irq401 = set; masked = dsp->sb_irqm401; break; } if (set && !masked) dsp->irq_update(dsp->irq_priv, 1); else if (!set) dsp->irq_update(dsp->irq_priv, 0); } void sb_irq(sb_dsp_t *dsp, int irq8) { sb_update_status(dsp, !irq8, 1); } void sb_irqc(sb_dsp_t *dsp, int irq8) { sb_update_status(dsp, !irq8, 0); } static void sb_dsp_irq_update(void *priv, int set) { sb_dsp_t *dsp = (sb_dsp_t *) priv; sb_update_status(dsp, 2, set); } static int sb_dsp_irq_pending(void *priv) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; return dsp->sb_irq401; } void sb_dsp_set_mpu(sb_dsp_t *dsp, mpu_t *mpu) { dsp->mpu = mpu; if (mpu != NULL) mpu401_irq_attach(mpu, sb_dsp_irq_update, sb_dsp_irq_pending, dsp); } void sb_dsp_reset(sb_dsp_t *dsp) { midi_clear_buffer(); timer_disable(&dsp->output_timer); timer_disable(&dsp->input_timer); dsp->sb_command = 0; dsp->sb_8_length = 0xffff; dsp->sb_8_autolen = 0xffff; dsp->sb_irq8 = 0; dsp->sb_irq16 = 0; dsp->sb_irq401 = 0; dsp->sb_16_pause = 0; dsp->sb_read_wp = dsp->sb_read_rp = 0; dsp->sb_data_stat = -1; dsp->sb_speaker = 0; dsp->sb_pausetime = -1LL; dsp->sbe2 = 0xAA; dsp->sbe2count = 0; dsp->sbreset = 0; dsp->record_pos_read = 0; dsp->record_pos_write = SB_DSP_REC_SAFEFTY_MARGIN; dsp->irq_update(dsp->irq_priv, 0); dsp->asp_data_len = 0; } void sb_doreset(sb_dsp_t *dsp) { sb_dsp_reset(dsp); if (IS_AZTECH(dsp)) { sb_commands[8] = 1; sb_commands[9] = 1; } else { if (dsp->sb_type >= SB16) sb_commands[8] = 1; else sb_commands[8] = -1; } dsp->sb_asp_mode = 0; dsp->sb_asp_ram_index = 0; for (uint16_t c = 0; c < 256; c++) dsp->sb_asp_regs[c] = 0; dsp->sb_asp_regs[5] = 0x01; dsp->sb_asp_regs[9] = 0xf8; } void sb_dsp_speed_changed(sb_dsp_t *dsp) { if (dsp->sb_timeo < 256) dsp->sblatcho = TIMER_USEC * (256 - dsp->sb_timeo); else dsp->sblatcho = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_timeo - 256))); if (dsp->sb_timei < 256) dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_timei); else dsp->sblatchi = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_timei - 256))); } void sb_add_data(sb_dsp_t *dsp, uint8_t v) { dsp->sb_read_data[dsp->sb_read_wp++] = v; dsp->sb_read_wp &= 0xff; } void sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { dsp->sb_pausetime = -1; if (dma8) { dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; dsp->sb_8_autoinit = autoinit; dsp->sb_8_pause = 0; dsp->sb_8_enable = 1; if (dsp->sb_16_enable && dsp->sb_16_output) dsp->sb_16_enable = 0; dsp->sb_8_output = 1; if (!timer_is_enabled(&dsp->output_timer)) timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); dsp->sbleftright = dsp->sbleftright_default; dsp->sbdacpos = 0; } else { dsp->sb_16_length = dsp->sb_16_origlength = len; dsp->sb_16_format = format; dsp->sb_16_autoinit = autoinit; dsp->sb_16_pause = 0; dsp->sb_16_enable = 1; if (dsp->sb_8_enable && dsp->sb_8_output) dsp->sb_8_enable = 0; dsp->sb_16_output = 1; if (!timer_is_enabled(&dsp->output_timer)) timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); } } void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) { if (dma8) { dsp->sb_8_length = dsp->sb_8_origlength = len; dsp->sb_8_format = format; dsp->sb_8_autoinit = autoinit; dsp->sb_8_pause = 0; dsp->sb_8_enable = 1; if (dsp->sb_16_enable && !dsp->sb_16_output) dsp->sb_16_enable = 0; dsp->sb_8_output = 0; if (!timer_is_enabled(&dsp->input_timer)) timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); } else { dsp->sb_16_length = dsp->sb_16_origlength = len; dsp->sb_16_format = format; dsp->sb_16_autoinit = autoinit; dsp->sb_16_pause = 0; dsp->sb_16_enable = 1; if (dsp->sb_8_enable && !dsp->sb_8_output) dsp->sb_8_enable = 0; dsp->sb_16_output = 0; if (!timer_is_enabled(&dsp->input_timer)) timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); } memset(dsp->record_buffer, 0, sizeof(dsp->record_buffer)); } int sb_8_read_dma(void *priv) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; return dma_channel_read(dsp->sb_8_dmanum); } int sb_8_write_dma(void *priv, uint8_t val) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; return dma_channel_write(dsp->sb_8_dmanum, val) == DMA_NODATA; } /* Supported High DMA Translation Channel ---------------------------------------------------- 0 0 0 First 8-bit 0 0 1 First 8-bit 0 1 0 Second 8-bit 0 1 1 Second 8-bit 1 0 0 First 8-bit 1 0 1 First 8-bit 1 1 0 16-bit 1 1 1 Second 8-bit */ int sb_16_read_dma(void *priv) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; int temp, ret = 0; int dma_flags, dma_ch = dsp->sb_16_dmanum; if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate) ret = dma_channel_read(dma_ch); else { if (dsp->sb_16_dma_enabled) { /* High DMA channel enabled, either translation is enabled or 16-bit transfers are not supported. */ if (dsp->sb_16_dma_translate || !dsp->sb_16_dma_supported) dma_ch = dsp->sb_16_8_dmanum; } else /* High DMA channel disabled, always use the first 8-bit channel. */ dma_ch = dsp->sb_8_dmanum; temp = dma_channel_read(dma_ch); ret = temp; if ((temp != DMA_NODATA) && !(temp & DMA_OVER)) { temp = dma_channel_read(dma_ch); if (temp == DMA_NODATA) ret = DMA_NODATA; else { dma_flags = temp & DMA_OVER; temp &= ~DMA_OVER; ret |= (temp << 8) | dma_flags; } } } return ret; } int sb_16_write_dma(void *priv, uint16_t val) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; int temp, ret = 0; int dma_ch = dsp->sb_16_dmanum; if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate) ret = dma_channel_write(dma_ch, val) == DMA_NODATA; else { if (dsp->sb_16_dma_enabled) { /* High DMA channel enabled, either translation is enabled or 16-bit transfers are not supported. */ if (dsp->sb_16_dma_translate || !dsp->sb_16_dma_supported) dma_ch = dsp->sb_16_8_dmanum; } else /* High DMA channel disabled, always use the first 8-bit channel. */ dma_ch = dsp->sb_8_dmanum; temp = dma_channel_write(dma_ch, val & 0xff); ret = temp; if ((temp != DMA_NODATA) && (temp != DMA_OVER)) { temp = dma_channel_write(dma_ch, val >> 8); ret = temp; } } return ret; } void sb_dsp_setirq(sb_dsp_t *dsp, int irq) { sb_dsp_log("IRQ now: %i\n", irq); dsp->sb_irqnum = irq; } void sb_dsp_setdma8(sb_dsp_t *dsp, int dma) { sb_dsp_log("8-bit DMA now: %i\n", dma); dsp->sb_8_dmanum = dma; } void sb_dsp_setdma16(sb_dsp_t *dsp, int dma) { sb_dsp_log("16-bit DMA now: %i\n", dma); dsp->sb_16_dmanum = dma; } void sb_dsp_setdma16_8(sb_dsp_t *dsp, int dma) { sb_dsp_log("16-bit to 8-bit translation DMA now: %i\n", dma); dsp->sb_16_8_dmanum = dma; } void sb_dsp_setdma16_enabled(sb_dsp_t *dsp, int enabled) { sb_dsp_log("16-bit DMA now: %sabled\n", enabled ? "en" : "dis"); dsp->sb_16_dma_enabled = enabled; } void sb_dsp_setdma16_supported(sb_dsp_t *dsp, int supported) { sb_dsp_log("16-bit DMA now: %ssupported\n", supported ? "" : "not "); dsp->sb_16_dma_supported = supported; } void sb_dsp_setdma16_translate(sb_dsp_t *dsp, int translate) { sb_dsp_log("16-bit to 8-bit translation now: %sabled\n", translate ? "en" : "dis"); dsp->sb_16_dma_translate = translate; } void sb_exec_command(sb_dsp_t *dsp) { int temp; int c; sb_dsp_log("sb_exec_command : SB command %02X\n", dsp->sb_command); /* Update 8051 ram with the current DSP command. See https://github.com/joncampbell123/dosbox-x/issues/1044 */ if (dsp->sb_type >= SB16) dsp->sb_8051_ram[0x20] = dsp->sb_command; switch (dsp->sb_command) { case 0x01: /* ???? */ if (dsp->sb_type >= SB16) dsp->asp_data_len = dsp->sb_data[0] + (dsp->sb_data[1] << 8) + 1; break; case 0x03: /* ASP status */ if (dsp->sb_type >= SB16) sb_add_data(dsp, 0); break; case 0x04: /* ASP set mode register */ if (dsp->sb_type >= SB16) { dsp->sb_asp_mode = dsp->sb_data[0]; if (dsp->sb_asp_mode & 4) dsp->sb_asp_ram_index = 0; sb_dsp_log("SB16 ASP set mode %02X\n", dsp->sb_asp_mode); } /* else DSP Status (Obsolete) */ break; case 0x05: /* ASP set codec parameter */ if (dsp->sb_type >= SB16) sb_dsp_log("SB16 ASP unknown codec params %02X, %02X\n", dsp->sb_data[0], dsp->sb_data[1]); break; case 0x07: break; case 0x08: /* ASP get version / AZTECH type/EEPROM access */ if (IS_AZTECH(dsp)) { if ((dsp->sb_data[0] == 0x05 || dsp->sb_data[0] == 0x55) && dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11) sb_add_data(dsp, 0x11); /* AZTECH get type, WASHINGTON/latest - according to devkit. E.g.: The one in the Itautec Infoway Multimidia */ else if ((dsp->sb_data[0] == 0x05 || dsp->sb_data[0] == 0x55) && dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT1605_0X0C) sb_add_data(dsp, 0x0C); /* AZTECH get type, CLINTON - according to devkit. E.g.: The one in the Packard Bell Legend 100CD */ else if (dsp->sb_data[0] == 0x08) { /* EEPROM address to write followed by byte */ if (dsp->sb_data[1] < 0 || dsp->sb_data[1] >= AZTECH_EEPROM_SIZE) fatal("AZT EEPROM: out of bounds write to %02X\n", dsp->sb_data[1]); sb_dsp_log("EEPROM write = %02x\n", dsp->sb_data[2]); dsp->azt_eeprom[dsp->sb_data[1]] = dsp->sb_data[2]; break; } else if (dsp->sb_data[0] == 0x07) { /* EEPROM address to read */ if (dsp->sb_data[1] < 0 || dsp->sb_data[1] >= AZTECH_EEPROM_SIZE) fatal("AZT EEPROM: out of bounds read to %02X\n", dsp->sb_data[1]); sb_dsp_log("EEPROM read = %02x\n", dsp->azt_eeprom[dsp->sb_data[1]]); sb_add_data(dsp, dsp->azt_eeprom[dsp->sb_data[1]]); break; } else sb_dsp_log("AZT2316A: UNKNOWN 0x08 COMMAND: %02X\n", dsp->sb_data[0]); /* 0x08 (when shutting down, driver tries to read 1 byte of response), 0x55, 0x0D, 0x08D seen */ break; } if (dsp->sb_type == SBAWE64) /* AWE64 has no ASP or a socket for it */ sb_add_data(dsp, 0xFF); else if (dsp->sb_type >= SB16) sb_add_data(dsp, 0x18); break; case 0x09: /* AZTECH mode set */ if (IS_AZTECH(dsp)) { if (dsp->sb_data[0] == 0x00) { sb_dsp_log("AZT2316A: WSS MODE!\n"); azt2316a_enable_wss(1, dsp->parent); } else if (dsp->sb_data[0] == 0x01) { sb_dsp_log("AZT2316A: SB8PROV2 MODE!\n"); azt2316a_enable_wss(0, dsp->parent); } else sb_dsp_log("AZT2316A: UNKNOWN MODE! = %02x\n", dsp->sb_data[0]); // sequences 0x02->0xFF, 0x04->0xFF seen } break; case 0x0E: /* ASP set register */ if (dsp->sb_type >= SB16) { dsp->sb_asp_regs[dsp->sb_data[0]] = dsp->sb_data[1]; if ((dsp->sb_data[0] == 0x83) && (dsp->sb_asp_mode & 128) && (dsp->sb_asp_mode & 8)) { /* ASP memory write */ if (dsp->sb_asp_mode & 8) dsp->sb_asp_ram_index = 0; dsp->sb_asp_ram[dsp->sb_asp_ram_index] = dsp->sb_data[1]; if (dsp->sb_asp_mode & 2) { dsp->sb_asp_ram_index++; if (dsp->sb_asp_ram_index >= 2048) dsp->sb_asp_ram_index = 0; } } sb_dsp_log("SB16 ASP write reg %02X, val %02X\n", dsp->sb_data[0], dsp->sb_data[1]); } break; case 0x0F: /* ASP get register */ if (dsp->sb_type >= SB16) { if ((dsp->sb_data[0] == 0x83) && (dsp->sb_asp_mode & 128) && (dsp->sb_asp_mode & 8)) { /* ASP memory read */ if (dsp->sb_asp_mode & 8) dsp->sb_asp_ram_index = 0; dsp->sb_asp_regs[0x83] = dsp->sb_asp_ram[dsp->sb_asp_ram_index]; if (dsp->sb_asp_mode & 1) { dsp->sb_asp_ram_index++; if (dsp->sb_asp_ram_index >= 2048) dsp->sb_asp_ram_index = 0; } } else if (dsp->sb_data[0] == 0x83) { dsp->sb_asp_regs[0x83] = 0x18; } sb_add_data(dsp, dsp->sb_asp_regs[dsp->sb_data[0]]); sb_dsp_log("SB16 ASP read reg %02X, val %02X\n", dsp->sb_data[0], dsp->sb_asp_regs[dsp->sb_data[0]]); } break; case 0x10: /* 8-bit direct mode */ sb_dsp_update(dsp); dsp->sbdat = dsp->sbdatl = dsp->sbdatr = (dsp->sb_data[0] ^ 0x80) << 8; break; case 0x14: /* 8-bit single cycle DMA output */ sb_start_dma(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); break; case 0x17: /* 2-bit ADPCM output with reference */ dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; fallthrough; case 0x16: /* 2-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x17) dsp->sb_8_length--; break; case 0x1C: /* 8-bit autoinit DMA output */ if (dsp->sb_type >= SB15) sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen); break; case 0x1F: /* 2-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; case 0x20: /* 8-bit direct input */ sb_add_data(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); /* Due to the current implementation, I need to emulate a samplerate, even if this mode does not imply such samplerate. Position is increased in sb_poll_i(). */ if (!timer_is_enabled(&dsp->input_timer)) { dsp->sb_timei = 256 - 22; dsp->sblatchi = TIMER_USEC * 22; temp = 1000000 / 22; dsp->sb_freq = temp; timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); } break; case 0x24: /* 8-bit single cycle DMA input */ sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); break; case 0x28: /* Direct ADC, 8-bit (Burst) */ break; case 0x2C: /* 8-bit autoinit DMA input */ if (dsp->sb_type >= SB15) sb_start_dma_i(dsp, 1, 1, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); break; case 0x30: /* MIDI Polling mode input */ sb_dsp_log("MIDI polling mode input\n"); dsp->midi_in_poll = 1; dsp->uart_irq = 0; break; case 0x31: /* MIDI Interrupt mode input */ sb_dsp_log("MIDI interrupt mode input\n"); dsp->midi_in_poll = 0; dsp->uart_irq = 1; break; case 0x32: /* MIDI Read Timestamp Poll */ break; case 0x33: /* MIDI Read Timestamp Interrupt */ break; case 0x34: /* MIDI In poll */ if (dsp->sb_type < SB2) break; sb_dsp_log("MIDI poll in\n"); dsp->midi_in_poll = 1; dsp->uart_midi = 1; dsp->uart_irq = 0; break; case 0x35: /* MIDI In irq */ if (dsp->sb_type < SB2) break; sb_dsp_log("MIDI irq in\n"); dsp->midi_in_poll = 0; dsp->uart_midi = 1; dsp->uart_irq = 1; break; case 0x36: case 0x37: /* MIDI timestamps */ break; case 0x38: /* Write to SB MIDI Output (Raw) */ dsp->onebyte_midi = 1; break; case 0x40: /* Set time constant */ dsp->sb_timei = dsp->sb_timeo = dsp->sb_data[0]; dsp->sblatcho = dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_data[0]); temp = 256 - dsp->sb_data[0]; temp = 1000000 / temp; sb_dsp_log("Sample rate - %ihz (%i)\n", temp, dsp->sblatcho); if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) recalc_sb16_filter(0, temp); dsp->sb_freq = temp; break; case 0x41: /* Set output sampling rate */ case 0x42: /* Set input sampling rate */ if (dsp->sb_type >= SB16) { dsp->sblatcho = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_data[1] + (dsp->sb_data[0] << 8)))); sb_dsp_log("Sample rate - %ihz (%i)\n", dsp->sb_data[1] + (dsp->sb_data[0] << 8), dsp->sblatcho); temp = dsp->sb_freq; dsp->sb_freq = dsp->sb_data[1] + (dsp->sb_data[0] << 8); dsp->sb_timeo = 256LL + dsp->sb_freq; dsp->sblatchi = dsp->sblatcho; dsp->sb_timei = dsp->sb_timeo; if (dsp->sb_freq != temp && dsp->sb_type >= SB16) recalc_sb16_filter(0, dsp->sb_freq); dsp->sb_8051_ram[0x13] = dsp->sb_freq & 0xff; dsp->sb_8051_ram[0x14] = (dsp->sb_freq >> 8) & 0xff; } break; case 0x45: /* Continue Auto-Initialize DMA, 8-bit */ break; case 0x47: /* Continue Auto-Initialize DMA, 16-bit */ break; case 0x48: /* Set DSP block transfer size */ dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8); break; case 0x75: /* 4-bit ADPCM output with reference */ dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; fallthrough; case 0x74: /* 4-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x75) dsp->sb_8_length--; break; case 0x77: /* 2.6-bit ADPCM output with reference */ dsp->sbref = dsp->dma_readb(dsp->dma_priv); dsp->sbstep = 0; fallthrough; case 0x76: /* 2.6-bit ADPCM output */ sb_start_dma(dsp, 1, 0, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; if (dsp->sb_command == 0x77) dsp->sb_8_length--; break; case 0x7D: /* 4-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; case 0x7F: /* 2.6-bit ADPCM autoinit output */ if (dsp->sb_type >= SB15) { sb_start_dma(dsp, 1, 1, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8)); dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } break; case 0x80: /* Pause DAC */ dsp->sb_pausetime = dsp->sb_data[0] + (dsp->sb_data[1] << 8); if (!timer_is_enabled(&dsp->output_timer)) timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); break; case 0x90: /* High speed 8-bit autoinit DMA output */ if (dsp->sb_type >= SB2) sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen); break; case 0x91: /* High speed 8-bit single cycle DMA output */ if (dsp->sb_type >= SB2) sb_start_dma(dsp, 1, 0, 0, dsp->sb_8_autolen); break; case 0x98: /* High speed 8-bit autoinit DMA input */ if (dsp->sb_type >= SB2) sb_start_dma_i(dsp, 1, 1, 0, dsp->sb_8_autolen); break; case 0x99: /* High speed 8-bit single cycle DMA input */ if (dsp->sb_type >= SB2) sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_8_autolen); break; case 0xA0: /* Set input mode to mono */ case 0xA8: /* Set input mode to stereo */ if ((dsp->sb_type < SB2) || (dsp->sb_type > SBPRO2)) break; /* TODO: Implement. 3.xx-only command. */ break; case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7: /* 16-bit DMA output */ if (dsp->sb_type >= SB16) { sb_start_dma(dsp, 0, dsp->sb_command & 4, dsp->sb_data[0], dsp->sb_data[1] + (dsp->sb_data[2] << 8)); dsp->sb_16_autolen = dsp->sb_data[1] + (dsp->sb_data[2] << 8); } break; case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF: /* 16-bit DMA input */ if (dsp->sb_type >= SB16) { sb_start_dma_i(dsp, 0, dsp->sb_command & 4, dsp->sb_data[0], dsp->sb_data[1] + (dsp->sb_data[2] << 8)); dsp->sb_16_autolen = dsp->sb_data[1] + (dsp->sb_data[2] << 8); } break; case 0xC0: case 0xC1: case 0xC2: case 0xC3: case 0xC4: case 0xC5: case 0xC6: case 0xC7: /* 8-bit DMA output */ if (dsp->sb_type >= SB16) { sb_start_dma(dsp, 1, dsp->sb_command & 4, dsp->sb_data[0], dsp->sb_data[1] + (dsp->sb_data[2] << 8)); dsp->sb_8_autolen = dsp->sb_data[1] + (dsp->sb_data[2] << 8); } break; case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xCD: case 0xCE: case 0xCF: /* 8-bit DMA input */ if (dsp->sb_type >= SB16) { sb_start_dma_i(dsp, 1, dsp->sb_command & 4, dsp->sb_data[0], dsp->sb_data[1] + (dsp->sb_data[2] << 8)); dsp->sb_8_autolen = dsp->sb_data[1] + (dsp->sb_data[2] << 8); } break; case 0xD0: /* Pause 8-bit DMA */ dsp->sb_8_pause = 1; break; case 0xD1: /* Speaker on */ if (dsp->sb_type < SB15) dsp->sb_8_pause = 1; else if (dsp->sb_type < SB16) dsp->muted = 0; dsp->sb_speaker = 1; break; case 0xD3: /* Speaker off */ if (dsp->sb_type < SB15) dsp->sb_8_pause = 1; else if (dsp->sb_type < SB16) dsp->muted = 1; dsp->sb_speaker = 0; break; case 0xD4: /* Continue 8-bit DMA */ dsp->sb_8_pause = 0; break; case 0xD5: /* Pause 16-bit DMA */ if (dsp->sb_type >= SB16) dsp->sb_16_pause = 1; break; case 0xD6: /* Continue 16-bit DMA */ if (dsp->sb_type >= SB16) dsp->sb_16_pause = 0; break; case 0xD8: /* Get speaker status */ sb_add_data(dsp, dsp->sb_speaker ? 0xff : 0); break; case 0xD9: /* Exit 16-bit auto-init mode */ if (dsp->sb_type >= SB16) dsp->sb_16_autoinit = 0; break; case 0xDA: /* Exit 8-bit auto-init mode */ dsp->sb_8_autoinit = 0; break; case 0xE0: /* DSP identification */ sb_add_data(dsp, ~dsp->sb_data[0]); break; case 0xE1: /* Get DSP version */ if (IS_AZTECH(dsp)) { if (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11) { sb_add_data(dsp, 0x3); sb_add_data(dsp, 0x1); } else if (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT1605_0X0C) { sb_add_data(dsp, 0x2); sb_add_data(dsp, 0x1); } break; } sb_add_data(dsp, sb_dsp_versions[dsp->sb_type] >> 8); sb_add_data(dsp, sb_dsp_versions[dsp->sb_type] & 0xff); break; case 0xE2: /* Stupid ID/protection */ for (c = 0; c < 8; c++) { if (dsp->sb_data[0] & (1 << c)) dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][c]; } dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][8]; dsp->sbe2count++; dsp->dma_writeb(dsp->dma_priv, dsp->sbe2); break; case 0xE3: /* DSP copyright */ if (dsp->sb_type >= SB16) { c = 0; while (sb16_copyright[c]) sb_add_data(dsp, sb16_copyright[c++]); sb_add_data(dsp, 0); } break; case 0xE4: /* Write test register */ dsp->sb_test = dsp->sb_data[0]; break; case 0xE7: /* ???? */ break; case 0xE8: /* Read test register */ sb_add_data(dsp, dsp->sb_test); break; case 0xF2: /* Trigger 8-bit IRQ */ sb_dsp_log("Trigger IRQ\n"); sb_irq(dsp, 1); break; case 0xF3: /* Trigger 16-bit IRQ */ sb_dsp_log("Trigger IRQ\n"); sb_irq(dsp, 0); break; case 0xF8: if (dsp->sb_type < SB16) sb_add_data(dsp, 0); break; case 0xF9: /* SB16 8051 RAM read */ if (dsp->sb_type >= SB16) sb_add_data(dsp, dsp->sb_8051_ram[dsp->sb_data[0]]); break; case 0xFA: /* SB16 8051 RAM write */ if (dsp->sb_type >= SB16) dsp->sb_8051_ram[dsp->sb_data[0]] = dsp->sb_data[1]; break; case 0xFF: /* No, that's not how you program auto-init DMA */ break; /* TODO: Some more data about the DSP registeres * http://the.earth.li/~tfm/oldpage/sb_dsp.html * http://www.synchrondata.com/pheaven/www/area19.htm * http://www.dcee.net/Files/Programm/Sound/ * 0E3h DSP Copyright SBPro2??? * 0F0h Sine Generator SB * 0F1h DSP Auxiliary Status (Obsolete) SB-Pro2 * 0F2h IRQ Request, 8-bit SB * 0F3h IRQ Request, 16-bit SB16 * 0FBh DSP Status SB16 * 0FCh DSP Auxiliary Status SB16 * 0FDh DSP Command Status SB16 */ default: sb_dsp_log("Unknown DSP command: %02X\n", dsp->sb_command); break; } /* Update 8051 ram with the last DSP command. See https://github.com/joncampbell123/dosbox-x/issues/1044 */ if (dsp->sb_type >= SB16) dsp->sb_8051_ram[0x30] = dsp->sb_command; } void sb_write(uint16_t a, uint8_t v, void *priv) { sb_dsp_t *dsp = (sb_dsp_t *) priv; /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ if (dsp->sb_type < SB16) a &= 0xfffe; switch (a & 0xF) { case 6: /* Reset */ if (!dsp->uart_midi) { if (!(v & 1) && (dsp->sbreset & 1)) { sb_dsp_reset(dsp); sb_add_data(dsp, 0xAA); } dsp->sbreset = v; } dsp->uart_midi = 0; dsp->uart_irq = 0; dsp->onebyte_midi = 0; return; case 0xC: /* Command/data write */ if (dsp->uart_midi || dsp->onebyte_midi) { midi_raw_out_byte(v); dsp->onebyte_midi = 0; return; } timer_set_delay_u64(&dsp->wb_timer, TIMER_USEC * 1); if (dsp->asp_data_len) { sb_dsp_log("ASP data %i\n", dsp->asp_data_len); dsp->asp_data_len--; if (!dsp->asp_data_len) sb_add_data(dsp, 0); return; } if (dsp->sb_data_stat == -1) { dsp->sb_command = v; if (v == 0x01) sb_add_data(dsp, 0); dsp->sb_data_stat++; } else { dsp->sb_data[dsp->sb_data_stat++] = v; if (IS_AZTECH(dsp)) { /* variable length commands */ if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x08) sb_commands[dsp->sb_command] = 3; else if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x07) sb_commands[dsp->sb_command] = 2; } } if (dsp->sb_data_stat == sb_commands[dsp->sb_command] || sb_commands[dsp->sb_command] == -1) { sb_exec_command(dsp); dsp->sb_data_stat = -1; if (IS_AZTECH(dsp)) { /* variable length commands */ if (dsp->sb_command == 0x08) sb_commands[dsp->sb_command] = 1; } } break; default: break; } } uint8_t sb_read(uint16_t a, void *priv) { sb_dsp_t *dsp = (sb_dsp_t *) priv; uint8_t ret = 0x00; /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ if (dsp->sb_type < SB16) a &= 0xfffe; switch (a & 0xf) { case 0xA: /* Read data */ if (dsp->mpu && dsp->uart_midi) { ret = MPU401_ReadData(dsp->mpu); } else { dsp->sbreaddat = dsp->sb_read_data[dsp->sb_read_rp]; if (dsp->sb_read_rp != dsp->sb_read_wp) { dsp->sb_read_rp++; dsp->sb_read_rp &= 0xff; } return dsp->sbreaddat; } break; case 0xC: /* Write data ready */ if (dsp->sb_8_enable || dsp->sb_type >= SB16) dsp->busy_count = (dsp->busy_count + 1) & 3; else dsp->busy_count = 0; if (dsp->wb_full || (dsp->busy_count & 2)) { dsp->wb_full = timer_is_enabled(&dsp->wb_timer); if (IS_AZTECH(dsp)) { sb_dsp_log("SB Write Data Aztech read 0x80\n"); return 0x80; } else { sb_dsp_log("SB Write Data Creative read 0xff\n"); return 0xff; } } if (IS_AZTECH(dsp)) { sb_dsp_log("SB Write Data Aztech read 0x00\n"); ret = 0x00; } else { sb_dsp_log("SB Write Data Creative read 0x7f\n"); ret = 0x7f; } break; case 0xE: /* Read data ready */ dsp->irq_update(dsp->irq_priv, 0); dsp->sb_irq8 = dsp->sb_irq16 = 0; /* Only bit 7 is defined but aztech diagnostics fail if the others are set. Keep the original behavior to not interfere with what's already working. */ if (IS_AZTECH(dsp)) { sb_dsp_log("SB Read Data Aztech read %02X, Read RP = %d, Read WP = %d\n", (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80, dsp->sb_read_rp, dsp->sb_read_wp); ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80; } else { sb_dsp_log("SB Read Data Creative read %02X\n", (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7f : 0xff); ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7f : 0xff; } break; case 0xF: /* 16-bit ack */ dsp->sb_irq16 = 0; if (!dsp->sb_irq8) dsp->irq_update(dsp->irq_priv, 0); sb_dsp_log("SB 16-bit ACK read 0xFF\n"); ret = 0xff; break; default: break; } return ret; } void sb_dsp_input_msg(void *priv, uint8_t *msg, uint32_t len) { sb_dsp_t *dsp = (sb_dsp_t *) priv; sb_dsp_log("MIDI in sysex = %d, uart irq = %d, msg = %d\n", dsp->midi_in_sysex, dsp->uart_irq, len); if (!dsp->uart_irq && !dsp->midi_in_poll && (dsp->mpu != NULL)) { MPU401_InputMsg(dsp->mpu, msg, len); return; } if (dsp->midi_in_sysex) return; if (dsp->uart_irq) { for (uint32_t i = 0; i < len; i++) sb_add_data(dsp, msg[i]); sb_irq(dsp, 1); } else if (dsp->midi_in_poll) { for (uint32_t i = 0; i < len; i++) sb_add_data(dsp, msg[i]); } } int sb_dsp_input_sysex(void *priv, uint8_t *buffer, uint32_t len, int abort) { sb_dsp_t *dsp = (sb_dsp_t *) priv; if (!dsp->uart_irq && !dsp->midi_in_poll && (dsp->mpu != NULL)) return MPU401_InputSysex(dsp->mpu, buffer, len, abort); if (abort) { dsp->midi_in_sysex = 0; return 0; } dsp->midi_in_sysex = 1; for (uint32_t i = 0; i < len; i++) { if (dsp->sb_read_rp == dsp->sb_read_wp) { sb_dsp_log("Length sysex SB = %d\n", len - i); return (len - i); } sb_add_data(dsp, buffer[i]); } dsp->midi_in_sysex = 0; return 0; } void sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) { dsp->sb_type = type; dsp->sb_subtype = subtype; dsp->parent = parent; /* Default values. Use sb_dsp_setxxx() methods to change. */ dsp->sb_irqnum = 7; dsp->sb_8_dmanum = 1; dsp->sb_16_dmanum = 5; dsp->mpu = NULL; dsp->sbleftright_default = 0; dsp->irq_update = sb_irq_update_pic; dsp->irq_priv = dsp; dsp->dma_readb = sb_8_read_dma; dsp->dma_readw = sb_16_read_dma; dsp->dma_writeb = sb_8_write_dma; dsp->dma_writew = sb_16_write_dma; dsp->dma_priv = dsp; sb_doreset(dsp); timer_add(&dsp->output_timer, pollsb, dsp, 0); timer_add(&dsp->input_timer, sb_poll_i, dsp, 0); timer_add(&dsp->wb_timer, NULL, dsp, 0); /* Initialise SB16 filter to same cutoff as 8-bit SBs (3.2 kHz). This will be recalculated when a set frequency command is sent. */ recalc_sb16_filter(0, 3200 * 2); recalc_sb16_filter(1, FREQ_44100); recalc_sb16_filter(2, 18939); /* Initialize SB16 8051 RAM and ASP internal RAM */ memset(dsp->sb_8051_ram, 0x00, sizeof(dsp->sb_8051_ram)); dsp->sb_8051_ram[0x0e] = 0xff; dsp->sb_8051_ram[0x0f] = 0x07; dsp->sb_8051_ram[0x37] = 0x38; memset(dsp->sb_asp_ram, 0xff, sizeof(dsp->sb_asp_ram)); } void sb_dsp_setaddr(sb_dsp_t *dsp, uint16_t addr) { sb_dsp_log("sb_dsp_setaddr : %04X\n", addr); if (dsp->sb_addr != 0) { io_removehandler(dsp->sb_addr + 6, 0x0002, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp); io_removehandler(dsp->sb_addr + 0xa, 0x0006, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp); } dsp->sb_addr = addr; if (dsp->sb_addr != 0) { io_sethandler(dsp->sb_addr + 6, 0x0002, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp); io_sethandler(dsp->sb_addr + 0xa, 0x0006, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp); } } void sb_dsp_set_stereo(sb_dsp_t *dsp, int stereo) { dsp->stereo = stereo; } void sb_dsp_irq_attach(sb_dsp_t *dsp, void (*irq_update)(void *priv, int set), void *priv) { dsp->irq_update = irq_update; dsp->irq_priv = priv; } void sb_dsp_dma_attach(sb_dsp_t *dsp, int (*dma_readb)(void *priv), int (*dma_readw)(void *priv), int (*dma_writeb)(void *priv, uint8_t val), int (*dma_writew)(void *priv, uint16_t val), void *priv) { dsp->dma_readb = dma_readb; dsp->dma_readw = dma_readw; dsp->dma_writeb = dma_writeb; dsp->dma_writew = dma_writew; dsp->dma_priv = priv; } void pollsb(void *priv) { sb_dsp_t *dsp = (sb_dsp_t *) priv; int tempi; int ref; int data[2]; timer_advance_u64(&dsp->output_timer, dsp->sblatcho); if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && dsp->sb_8_output) { sb_dsp_update(dsp); switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned */ data[0] = dsp->dma_readb(dsp->dma_priv); /* Needed to prevent clicking in Worms, which programs the DSP to auto-init DMA but programs the DMA controller to single cycle */ if (data[0] == DMA_NODATA) break; dsp->sbdat = (data[0] ^ 0x80) << 8; if (dsp->stereo) { sb_dsp_log("pollsb: Mono unsigned, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else dsp->sbdatr = dsp->sbdat; dsp->sbleftright = !dsp->sbleftright; } else dsp->sbdatl = dsp->sbdatr = dsp->sbdat; dsp->sb_8_length--; break; case 0x10: /* Mono signed */ data[0] = dsp->dma_readb(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdat = data[0] << 8; if (dsp->stereo) { sb_dsp_log("pollsb: Mono signed, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", data[0], dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else dsp->sbdatr = dsp->sbdat; dsp->sbleftright = !dsp->sbleftright; } else dsp->sbdatl = dsp->sbdatr = dsp->sbdat; dsp->sb_8_length--; break; case 0x20: /* Stereo unsigned */ data[0] = dsp->dma_readb(dsp->dma_priv); data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = (data[0] ^ 0x80) << 8; dsp->sbdatr = (data[1] ^ 0x80) << 8; dsp->sb_8_length -= 2; break; case 0x30: /* Stereo signed */ data[0] = dsp->dma_readb(dsp->dma_priv); data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0] << 8; dsp->sbdatr = data[1] << 8; dsp->sb_8_length -= 2; break; case ADPCM_4: if (dsp->sbdacpos) tempi = (dsp->sbdat2 & 0xF) + dsp->sbstep; else tempi = (dsp->sbdat2 >> 4) + dsp->sbstep; if (tempi < 0) tempi = 0; if (tempi > 63) tempi = 63; ref = dsp->sbref + scaleMap4[tempi]; if (ref > 0xff) dsp->sbref = 0xff; else if (ref < 0x00) dsp->sbref = 0x00; else dsp->sbref = ref; dsp->sbstep = (dsp->sbstep + adjustMap4[tempi]) & 0xff; dsp->sbdat = (dsp->sbref ^ 0x80) << 8; dsp->sbdacpos++; if (dsp->sbdacpos >= 2) { dsp->sbdacpos = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 4, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else dsp->sbdatr = dsp->sbdat; dsp->sbleftright = !dsp->sbleftright; } else dsp->sbdatl = dsp->sbdatr = dsp->sbdat; break; case ADPCM_26: if (!dsp->sbdacpos) tempi = (dsp->sbdat2 >> 5) + dsp->sbstep; else if (dsp->sbdacpos == 1) tempi = ((dsp->sbdat2 >> 2) & 7) + dsp->sbstep; else tempi = ((dsp->sbdat2 << 1) & 7) + dsp->sbstep; if (tempi < 0) tempi = 0; if (tempi > 39) tempi = 39; ref = dsp->sbref + scaleMap26[tempi]; if (ref > 0xff) dsp->sbref = 0xff; else if (ref < 0x00) dsp->sbref = 0x00; else dsp->sbref = ref; dsp->sbstep = (dsp->sbstep + adjustMap26[tempi]) & 0xff; dsp->sbdat = (dsp->sbref ^ 0x80) << 8; dsp->sbdacpos++; if (dsp->sbdacpos >= 3) { dsp->sbdacpos = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); dsp->sb_8_length--; } if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 26, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else dsp->sbdatr = dsp->sbdat; dsp->sbleftright = !dsp->sbleftright; } else dsp->sbdatl = dsp->sbdatr = dsp->sbdat; break; case ADPCM_2: tempi = ((dsp->sbdat2 >> ((3 - dsp->sbdacpos) * 2)) & 3) + dsp->sbstep; if (tempi < 0) tempi = 0; if (tempi > 23) tempi = 23; ref = dsp->sbref + scaleMap2[tempi]; if (ref > 0xff) dsp->sbref = 0xff; else if (ref < 0x00) dsp->sbref = 0x00; else dsp->sbref = ref; dsp->sbstep = (dsp->sbstep + adjustMap2[tempi]) & 0xff; dsp->sbdat = (dsp->sbref ^ 0x80) << 8; dsp->sbdacpos++; if (dsp->sbdacpos >= 4) { dsp->sbdacpos = 0; dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv); } if (dsp->stereo) { sb_dsp_log("pollsb: ADPCM 2, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); if (dsp->sbleftright) dsp->sbdatl = dsp->sbdat; else dsp->sbdatr = dsp->sbdat; dsp->sbleftright = !dsp->sbleftright; } else dsp->sbdatl = dsp->sbdatr = dsp->sbdat; break; default: break; } if (dsp->sb_8_length < 0) { if (dsp->sb_8_autoinit) dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen; else { dsp->sb_8_enable = 0; timer_disable(&dsp->output_timer); } sb_irq(dsp, 1); } } if (dsp->sb_16_enable && !dsp->sb_16_pause && (dsp->sb_pausetime < 0LL) && dsp->sb_16_output) { sb_dsp_update(dsp); switch (dsp->sb_16_format) { case 0x00: /* Mono unsigned */ data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdatl = dsp->sbdatr = data[0] ^ 0x8000; dsp->sb_16_length--; break; case 0x10: /* Mono signed */ data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; dsp->sbdatl = dsp->sbdatr = data[0]; dsp->sb_16_length--; break; case 0x20: /* Stereo unsigned */ data[0] = dsp->dma_readw(dsp->dma_priv); data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0] ^ 0x8000; dsp->sbdatr = data[1] ^ 0x8000; dsp->sb_16_length -= 2; break; case 0x30: /* Stereo signed */ data[0] = dsp->dma_readw(dsp->dma_priv); data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; dsp->sbdatl = data[0]; dsp->sbdatr = data[1]; dsp->sb_16_length -= 2; break; default: break; } if (dsp->sb_16_length < 0) { sb_dsp_log("16DMA over %i\n", dsp->sb_16_autoinit); if (dsp->sb_16_autoinit) dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen; else { dsp->sb_16_enable = 0; timer_disable(&dsp->output_timer); } sb_irq(dsp, 0); } } if (dsp->sb_pausetime > -1) { dsp->sb_pausetime--; if (dsp->sb_pausetime < 0) { sb_irq(dsp, 1); if (!dsp->sb_8_enable) timer_disable(&dsp->output_timer); sb_dsp_log("SB pause over\n"); } } } void sb_poll_i(void *priv) { sb_dsp_t *dsp = (sb_dsp_t *) priv; int processed = 0; timer_advance_u64(&dsp->input_timer, dsp->sblatchi); if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output) { switch (dsp->sb_8_format) { case 0x00: /* Mono unsigned As the manual says, only the left channel is recorded */ dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); dsp->sb_8_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x10: /* Mono signed As the manual says, only the left channel is recorded */ dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8)); dsp->sb_8_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x20: /* Stereo unsigned */ dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80); dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80); dsp->sb_8_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x30: /* Stereo signed */ dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read] >> 8)); dsp->dma_writeb(dsp->dma_priv, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8)); dsp->sb_8_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; default: break; } if (dsp->sb_8_length < 0) { if (dsp->sb_8_autoinit) dsp->sb_8_length = dsp->sb_8_origlength = dsp->sb_8_autolen; else { dsp->sb_8_enable = 0; timer_disable(&dsp->input_timer); } sb_irq(dsp, 1); } processed = 1; } if (dsp->sb_16_enable && !dsp->sb_16_pause && (dsp->sb_pausetime < 0LL) && !dsp->sb_16_output) { switch (dsp->sb_16_format) { case 0x00: /* Unsigned mono. As the manual says, only the left channel is recorded */ if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) return; dsp->sb_16_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x10: /* Signed mono. As the manual says, only the left channel is recorded */ if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read])) return; dsp->sb_16_length--; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x20: /* Unsigned stereo */ if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000)) return; dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000); dsp->sb_16_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; case 0x30: /* Signed stereo */ if (dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read])) return; dsp->dma_writew(dsp->dma_priv, dsp->record_buffer[dsp->record_pos_read + 1]); dsp->sb_16_length -= 2; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; break; default: break; } if (dsp->sb_16_length < 0) { if (dsp->sb_16_autoinit) dsp->sb_16_length = dsp->sb_16_origlength = dsp->sb_16_autolen; else { dsp->sb_16_enable = 0; timer_disable(&dsp->input_timer); } sb_irq(dsp, 0); } processed = 1; } /* Assume this is direct mode */ if (!processed) { dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; } } void sb_dsp_update(sb_dsp_t *dsp) { if (dsp->muted) { dsp->sbdatl = 0; dsp->sbdatr = 0; } for (; dsp->pos < sound_pos_global; dsp->pos++) { dsp->buffer[dsp->pos * 2] = dsp->sbdatl; dsp->buffer[dsp->pos * 2 + 1] = dsp->sbdatr; } } void sb_dsp_close(UNUSED(sb_dsp_t *dsp)) { // }