Files
86Box/src/sound/snd_sb_dsp.c
2025-07-12 15:35:35 -04:00

3066 lines
117 KiB
C

/*Jazz sample rates :
386-33 - 12kHz
486-33 - 20kHz
486-50 - 32kHz
Pentium - 45kHz*/
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#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>
/* NON-PCM SAMPLE FORMATS */
#define ADPCM_4 1
#define ADPCM_26 2
#define ADPCM_2 3
#define ESPCM_4 4
#define ESPCM_3 5
/* ESPCM_2? */
#define ESPCM_1 7
#define ESPCM_4E 8 /* For differentiating between 4-bit encoding and decoding modes. */
/* 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
enum {
DSP_S_NORMAL = 0,
DSP_S_RESET,
DSP_S_RESET_WAIT
};
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
};
#if 0
// Currently unused, here for reference if ever needed
char sb202_copyright[] = "COPYRIGHT(C) CREATIVE TECHNOLOGY PTE. LTD. (1991) "
#endif
char sb16_copyright[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
uint16_t sb_dsp_versions[] = {
0, /* Pad */
0, /* SADLIB - No DSP */
0x105, /* SB_DSP_105 - SB1/1.5, DSP v1.05 */
0x200, /* SB_DSP_200 - SB1.5/2, DSP v2.00 */
0x201, /* SB_DSP_201 - SB1.5/2, DSP v2.01 - needed for high-speed DMA */
0x202, /* SB_DSP_202 - SB2, DSP v2.02 */
0x300, /* SB_PRO_DSP_300 - SB Pro, DSP v3.00 */
0x302, /* SBPRO2_DSP_302 - SB Pro 2, DSP v3.02 + OPL3 */
0x404, /* SB16_DSP_404 - DSP v4.04 + OPL3 */
0x405, /* SB16_405 - DSP v4.05 + OPL3 */
0x406, /* SB16_406 - DSP v4.06 + OPL3 */
0x40b, /* SB16_411 - DSP v4.11 + OPL3 */
0x40c, /* SBAWE32 - DSP v4.12 + OPL3 */
0x40d, /* SBAWE32PNP - DSP v4.13 + OPL3 */
0x410 /* SBAWE64 - DSP v4.16 + OPL3 */
};
/*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
};
// clang-format off
/* Upper half only used for ESPCM_3 mode. */
/* TODO: Extract actual table (or exact ranges + range interpolation algo, whatever it is) from chip, someday, somehow.
* This current table is part software reverse engineering, part guesswork/extrapolation.
* It's close enough to what's in the chip to produce acceptable results, but not exact.
**/
int8_t espcm_range_map[512] = {
-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7,
-10, -8, -7, -5, -4, -3, -2, -1, 0, 2, 3, 4, 5, 6, 8, 9,
-12, -11, -9, -8, -6, -5, -3, -2, 0, 2, 3, 5, 6, 8, 10, 11,
-14, -12, -11, -9, -7, -5, -4, -2, 0, 2, 4, 5, 7, 9, 11, 13,
-16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14,
-21, -18, -16, -13, -11, -8, -6, -3, 0, 2, 5, 7, 10, 12, 15, 18,
-27, -24, -21, -17, -14, -11, -8, -4, 0, 3, 7, 10, 13, 17, 20, 24,
-35, -28, -24, -20, -16, -12, -8, -4, 0, 4, 8, 12, 16, 20, 24, 28,
-40, -35, -30, -25, -20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35,
-48, -42, -36, -30, -24, -18, -12, -6, 0, 6, 12, 18, 24, 30, 36, 43,
-56, -49, -42, -35, -28, -21, -14, -7, 0, 7, 14, 21, 28, 35, 42, 49,
-72, -63, -54, -45, -36, -27, -18, -9, 0, 9, 18, 27, 36, 45, 54, 63,
-85, -74, -64, -53, -43, -32, -22, -11, 0, 11, 22, 33, 43, 54, 64, 75,
-102, -98, -85, -71, -58, -45, -31, -14, 0, 13, 26, 39, 52, 65, 78, 90,
-127,-112, -96, -80, -64, -48, -32, -16, 0, 16, 32, 48, 64, 80, 96, 112,
-128,-127,-109, -91, -73, -54, -36, -18, 0, 18, 36, 54, 73, 91, 109, 127,
-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7,
-10, -9, -8, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 6, 7, 8,
-13, -11, -9, -7, -6, -5, -3, -2, -1, 2, 3, 5, 6, 7, 9, 10,
-15, -13, -12, -10, -8, -6, -5, -3, -1, 2, 3, 5, 6, 8, 10, 12,
-18, -15, -13, -11, -9, -7, -5, -3, -1, 2, 3, 5, 7, 9, 11, 13,
-24, -20, -17, -15, -12, -10, -7, -5, -2, 2, 3, 6, 8, 11, 13, 16,
-29, -26, -23, -19, -16, -13, -10, -6, -2, 2, 5, 8, 11, 15, 18, 22,
-34, -30, -26, -22, -18, -14, -10, -6, -2, 2, 6, 10, 14, 18, 22, 26,
-43, -38, -33, -28, -23, -18, -13, -8, -3, 2, 7, 12, 17, 22, 27, 32,
-51, -45, -39, -33, -27, -21, -15, -9, -3, 3, 9, 15, 21, 27, 33, 39,
-60, -53, -46, -39, -32, -25, -18, -11, -4, 3, 10, 17, 24, 31, 38, 45,
-77, -68, -59, -50, -41, -32, -23, -14, -5, 4, 13, 22, 31, 40, 49, 58,
-90, -80, -69, -59, -48, -38, -27, -17, -6, 5, 16, 27, 38, 48, 59, 69,
-112,-104, -91, -78, -65, -52, -38, -23, -7, 6, 19, 32, 45, 58, 71, 84,
-128,-120,-104, -88, -72, -56, -40, -24, -8, 8, 24, 40, 56, 72, 88, 104,
-128,-128,-118,-100, -82, -64, -45, -27, -9, 9, 27, 45, 63, 82, 100, 118
};
/* address = table_index(9:8) | dsp->espcm_last_value(7:3) | codeword(2:0)
* the value is a base index into espcm_range_map with bits at (8, 3:0),
* to be OR'ed with dsp->espcm_range at (7:4)
*/
uint16_t espcm3_dpcm_tables[1024] =
{
/* Table 0 */
256, 257, 258, 259, 260, 263, 266, 269, 0, 257, 258, 259, 260, 263, 266, 269,
0, 1, 258, 259, 260, 263, 266, 269, 1, 2, 259, 260, 261, 263, 266, 269,
1, 3, 260, 261, 262, 264, 266, 269, 1, 3, 4, 261, 262, 264, 266, 269,
2, 4, 5, 262, 263, 264, 266, 269, 2, 4, 6, 263, 264, 265, 267, 269,
2, 4, 6, 7, 264, 265, 267, 269, 2, 5, 7, 8, 265, 266, 267, 269,
2, 5, 7, 8, 9, 266, 268, 270, 2, 5, 7, 9, 10, 267, 268, 270,
2, 5, 8, 10, 11, 268, 269, 270, 2, 5, 8, 11, 12, 269, 270, 271,
2, 5, 8, 11, 12, 13, 270, 271, 2, 5, 8, 11, 12, 13, 14, 271,
0, 257, 258, 259, 260, 263, 266, 269, 0, 1, 258, 259, 260, 263, 266, 269,
0, 1, 2, 259, 260, 263, 266, 269, 1, 2, 3, 260, 261, 263, 266, 269,
1, 3, 4, 261, 262, 264, 266, 269, 1, 3, 5, 262, 263, 264, 266, 269,
2, 4, 5, 6, 263, 264, 266, 269, 2, 4, 6, 7, 264, 265, 267, 269,
2, 4, 6, 7, 8, 265, 267, 269, 2, 5, 7, 8, 9, 266, 267, 269,
2, 5, 7, 9, 10, 267, 268, 270, 2, 5, 7, 9, 10, 11, 268, 270,
2, 5, 8, 10, 11, 12, 269, 270, 2, 5, 8, 11, 12, 13, 270, 271,
2, 5, 8, 11, 12, 13, 14, 271, 2, 5, 8, 11, 12, 13, 14, 15,
/* Table 1 */
257, 260, 262, 263, 264, 265, 267, 270, 257, 260, 262, 263, 264, 265, 267, 270,
1, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
1, 260, 262, 263, 264, 265, 267, 270, 1, 4, 262, 263, 264, 265, 267, 270,
1, 4, 262, 263, 264, 265, 267, 270, 1, 4, 6, 263, 264, 265, 267, 270,
1, 4, 6, 7, 264, 265, 267, 270, 1, 4, 6, 7, 8, 265, 267, 270,
1, 4, 6, 7, 8, 9, 267, 270, 1, 4, 6, 7, 8, 9, 267, 270,
1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 270,
1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 14,
257, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
1, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
1, 4, 262, 263, 264, 265, 267, 270, 1, 4, 262, 263, 264, 265, 267, 270,
1, 4, 6, 263, 264, 265, 267, 270, 1, 4, 6, 7, 264, 265, 267, 270,
1, 4, 6, 7, 8, 265, 267, 270, 1, 4, 6, 7, 8, 9, 267, 270,
1, 4, 6, 7, 8, 9, 267, 270, 1, 4, 6, 7, 8, 9, 11, 270,
1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 270,
1, 4, 6, 7, 8, 9, 11, 14, 1, 4, 6, 7, 8, 9, 11, 14,
/* Table 2 */
256, 257, 258, 259, 260, 262, 265, 268, 0, 257, 258, 259, 260, 262, 265, 268,
0, 1, 258, 259, 260, 262, 265, 269, 1, 2, 259, 260, 261, 263, 265, 269,
1, 3, 260, 261, 262, 263, 265, 269, 1, 3, 4, 261, 262, 263, 265, 269,
1, 3, 5, 262, 263, 264, 266, 269, 1, 4, 5, 6, 263, 264, 266, 269,
1, 4, 6, 7, 264, 265, 266, 269, 1, 4, 6, 7, 8, 265, 266, 269,
2, 4, 6, 7, 8, 9, 267, 269, 2, 4, 6, 7, 8, 9, 267, 269,
2, 5, 7, 8, 9, 10, 11, 270, 2, 5, 7, 8, 9, 10, 11, 270,
2, 5, 8, 9, 10, 11, 12, 270, 2, 6, 8, 10, 11, 12, 13, 14,
257, 258, 259, 260, 261, 263, 265, 269, 1, 259, 260, 261, 262, 263, 266, 269,
1, 260, 261, 262, 263, 264, 266, 269, 1, 260, 261, 262, 263, 264, 266, 269,
2, 4, 262, 263, 264, 265, 267, 269, 2, 4, 262, 263, 264, 265, 267, 269,
2, 5, 6, 263, 264, 265, 267, 270, 2, 5, 6, 7, 264, 265, 267, 270,
2, 5, 7, 8, 265, 266, 267, 270, 2, 5, 7, 8, 9, 266, 268, 270,
2, 6, 8, 9, 10, 267, 268, 270, 2, 6, 8, 9, 10, 11, 268, 270,
2, 6, 8, 10, 11, 12, 269, 270, 2, 6, 9, 11, 12, 13, 270, 271,
3, 6, 9, 11, 12, 13, 14, 271, 3, 6, 9, 11, 12, 13, 14, 15,
/* Table 3 */
256, 258, 260, 261, 262, 263, 264, 265, 0, 258, 260, 261, 262, 263, 264, 265,
1, 259, 260, 261, 262, 263, 264, 266, 1, 259, 260, 261, 262, 263, 264, 266,
1, 3, 260, 261, 262, 263, 264, 266, 1, 3, 4, 261, 262, 263, 264, 267,
1, 3, 4, 5, 262, 263, 264, 267, 1, 3, 4, 5, 6, 263, 264, 267,
1, 3, 5, 6, 7, 264, 265, 268, 1, 3, 5, 6, 7, 8, 265, 268,
1, 4, 6, 7, 8, 9, 266, 269, 1, 4, 6, 7, 8, 9, 10, 269,
1, 4, 6, 7, 8, 9, 10, 269, 1, 4, 6, 7, 8, 9, 11, 270,
1, 4, 6, 7, 8, 9, 11, 270, 1, 4, 6, 7, 8, 9, 11, 14,
257, 260, 262, 263, 264, 265, 267, 270, 1, 260, 262, 263, 264, 265, 267, 270,
1, 260, 262, 263, 264, 265, 267, 270, 2, 261, 262, 263, 264, 265, 267, 270,
2, 261, 262, 263, 264, 265, 267, 270, 2, 5, 262, 263, 264, 265, 267, 270,
3, 6, 263, 264, 265, 266, 268, 270, 3, 6, 7, 264, 265, 266, 268, 270,
4, 7, 8, 265, 266, 267, 268, 270, 4, 7, 8, 9, 266, 267, 268, 270,
4, 7, 8, 9, 10, 267, 268, 270, 5, 7, 8, 9, 10, 11, 268, 270,
5, 7, 8, 9, 10, 11, 12, 270, 5, 7, 8, 9, 10, 11, 12, 270,
6, 7, 8, 9, 10, 11, 13, 271, 6, 7, 8, 9, 10, 11, 13, 15
};
// clang-format on
double low_fir_sb16_coef[5][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
#define ESSreg(reg) (dsp)->ess_regs[reg - 0xA0]
static __inline double
sinc(double x)
{
return sin(M_PI * x) / (M_PI * x);
}
static void
recalc_sb16_filter(const int c, const int playback_freq)
{
/* Cutoff frequency = playback / 2 */
int n;
const double fC = ((double) playback_freq) / (double) FREQ_96000;
for (n = 0; n < SB16_NCoef; n++) {
/* Blackman window */
const double 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 */
const double 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;
double 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
recalc_opl_filter(const int playback_freq)
{
/* Cutoff frequency = playback / 2 */
int n;
const double fC = ((double) playback_freq) / (double) (FREQ_49716 * 2);
for (n = 0; n < SB16_NCoef; n++) {
/* Blackman window */
const double 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 */
const double h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0)));
/* Create windowed-sinc filter */
low_fir_sb16_coef[1][n] = w * h;
}
low_fir_sb16_coef[1][(SB16_NCoef - 1) / 2] = 1.0;
double gain = 0.0;
for (n = 0; n < SB16_NCoef; n++)
gain += low_fir_sb16_coef[1][n];
/* Normalise filter, to produce unity gain */
for (n = 0; n < SB16_NCoef; n++)
low_fir_sb16_coef[1][n] /= gain;
}
static void
sb_irq_update_pic(void *priv, const 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;
sb_dsp_log("SBIRQ8=%d, irqnum=%d, bit=%x, set=%x.\n", dsp->sb_irq8, dsp->sb_irqnum, bit, set);
if (dsp->sb_irq8 || dsp->sb_irq16)
return;
/* NOTE: not on ES1688 or ES1868 */
if (IS_ESS(dsp) && (dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688) && !(ESSreg(0xB1) & 0x10))
/* If ESS playback, and IRQ disabled, do not fire. */
return;
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;
}
/* NOTE: not on ES1688, apparently; investigate on ES1868 */
if (IS_ESS(dsp) && (dsp->sb_subtype > SB_SUBTYPE_ESS_ES1688)) {
/* TODO: Investigate real hardware for this (the ES1887 datasheet documents this bit somewhat oddly.) */
if (dsp->ess_playback_mode && bit <= 1 && set && !masked) {
if (!(ESSreg(0xB1) & 0x40)) // if ESS playback, and IRQ disabled, do not fire
{
return;
}
}
}
sb_dsp_log("Masked=%02x.\n", masked);
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, const 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 (IS_NOT_ESS(dsp) && (mpu != NULL))
mpu401_irq_attach(mpu, sb_dsp_irq_update, sb_dsp_irq_pending, dsp);
}
static void
sb_stop_dma(const sb_dsp_t *dsp)
{
dma_set_drq(dsp->sb_8_dmanum, 0);
if (dsp->sb_16_dmanum != 0xff) {
if (dsp->sb_16_dmanum == 4)
dma_set_drq(dsp->sb_8_dmanum, 0);
else
dma_set_drq(dsp->sb_16_dmanum, 0);
}
if (dsp->sb_16_8_dmanum != 0xff)
dma_set_drq(dsp->sb_16_8_dmanum, 0);
}
static void
sb_finish_dma(sb_dsp_t *dsp)
{
if (dsp->ess_playback_mode) {
ESSreg(0xB8) &= ~0x01;
dma_set_drq(dsp->sb_8_dmanum, 0);
} else
sb_stop_dma(dsp);
}
void
sb_dsp_reset(sb_dsp_t *dsp)
{
midi_clear_buffer();
if (dsp->sb_8_enable) {
dsp->sb_8_enable = 0;
sb_finish_dma(dsp);
}
if (dsp->sb_16_enable) {
dsp->sb_16_enable = 0;
sb_finish_dma(dsp);
}
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_DSP_404)
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;
/* Initialize ESS registers */
ESSreg(0xA5) = 0xf8;
}
void
sb_dsp_speed_changed(sb_dsp_t *dsp)
{
if (dsp->sb_timeo < 256)
dsp->sblatcho = (double) (TIMER_USEC * (256 - dsp->sb_timeo));
else
dsp->sblatcho = ((double) TIMER_USEC * (1000000.0 / (double) (dsp->sb_timeo - 256)));
if (dsp->sb_timei < 256)
dsp->sblatchi = (double) (TIMER_USEC * (256 - dsp->sb_timei));
else
dsp->sblatchi = ((double) TIMER_USEC * (1000000.0 / (double) (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;
}
static unsigned int
sb_ess_get_dma_counter(const sb_dsp_t *dsp)
{
unsigned int c = (unsigned int) ESSreg(0xA5) << 8U;
c |= (unsigned int) ESSreg(0xA4);
return c;
}
static unsigned int
sb_ess_get_dma_len(const sb_dsp_t *dsp)
{
return 0x10000U - sb_ess_get_dma_counter(dsp);
}
static void
sb_resume_dma(const sb_dsp_t *dsp, const int is_8)
{
if IS_ESS(dsp)
{
dma_set_drq(dsp->sb_8_dmanum, 1);
dma_set_drq(dsp->sb_16_8_dmanum, 1);
} else if (is_8)
dma_set_drq(dsp->sb_8_dmanum, 1);
else {
if (dsp->sb_16_dmanum != 0xff) {
if (dsp->sb_16_dmanum == 4)
dma_set_drq(dsp->sb_8_dmanum, 1);
else
dma_set_drq(dsp->sb_16_dmanum, 1);
}
if (dsp->sb_16_8_dmanum != 0xff)
dma_set_drq(dsp->sb_16_8_dmanum, 1);
}
}
void
sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
{
sb_stop_dma(dsp);
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;
dsp->dma_ff = 0;
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, (uint64_t) dsp->sblatcho);
dsp->sbleftright = dsp->sbleftright_default;
dsp->sbdacpos = 0;
dma_set_drq(dsp->sb_8_dmanum, 1);
} 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, (uint64_t) dsp->sblatcho);
if (dsp->sb_16_dma_supported) {
if (dsp->sb_16_dmanum == 4)
dma_set_drq(dsp->sb_8_dmanum, 1);
else
dma_set_drq(dsp->sb_16_dmanum, 1);
} else
dma_set_drq(dsp->sb_16_8_dmanum, 1);
}
/* This will be set later for ESS playback/record modes. */
dsp->ess_playback_mode = 0;
}
void
sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
{
sb_stop_dma(dsp);
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, (uint64_t) dsp->sblatchi);
dma_set_drq(dsp->sb_8_dmanum, 1);
} 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, (uint64_t) dsp->sblatchi);
if (dsp->sb_16_dma_supported) {
if (dsp->sb_16_dmanum == 4)
dma_set_drq(dsp->sb_8_dmanum, 1);
else
dma_set_drq(dsp->sb_16_dmanum, 1);
} else
dma_set_drq(dsp->sb_16_8_dmanum, 1);
}
memset(dsp->record_buffer, 0, sizeof(dsp->record_buffer));
}
void
sb_start_dma_ess(sb_dsp_t *dsp)
{
uint8_t real_format = 0;
dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
uint32_t len = sb_ess_get_dma_len(dsp);
if (IS_ESS(dsp)) {
dma_set_drq(dsp->sb_8_dmanum, 0);
dma_set_drq(dsp->sb_16_8_dmanum, 0);
}
real_format |= !!(ESSreg(0xB7) & 0x20) ? 0x10 : 0;
real_format |= !!(ESSreg(0xB7) & 0x8) ? 0x20 : 0;
if (!!(ESSreg(0xB8) & 8))
sb_start_dma_i(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, (int) len);
else
sb_start_dma(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, (int) len);
dsp->ess_playback_mode = 1;
dma_set_drq(dsp->sb_8_dmanum, 1);
dma_set_drq(dsp->sb_16_8_dmanum, 1);
}
void
sb_stop_dma_ess(sb_dsp_t *dsp)
{
dsp->sb_8_enable = dsp->sb_16_enable = 0;
dma_set_drq(dsp->sb_16_8_dmanum, 0);
dma_set_drq(dsp->sb_8_dmanum, 0);
}
static void
sb_ess_update_dma_status(sb_dsp_t *dsp)
{
bool dma_en = (ESSreg(0xB8) & 1) ? true : false;
/* If the DRQ is disabled, do not start. */
if (!(ESSreg(0xB2) & 0x40))
dma_en = false;
if (dma_en) {
if (!dsp->sb_8_enable && !dsp->sb_16_enable)
sb_start_dma_ess(dsp);
} else {
if (dsp->sb_8_enable || dsp->sb_16_enable)
sb_stop_dma_ess(dsp);
}
}
int
sb_8_read_dma(void *priv)
{
sb_dsp_t *dsp = (sb_dsp_t *) priv;
int ret;
dsp->activity &= 0xdf;
if (dsp->sb_8_dmanum >= 4) {
if (dsp->dma_ff) {
uint32_t temp = (dsp->dma_data & 0xff00) >> 8;
temp |= (dsp->dma_data & 0xffff0000);
ret = (int) temp;
} else {
dsp->dma_data = dma_channel_read(dsp->sb_8_dmanum);
if (dsp->dma_data == DMA_NODATA)
return DMA_NODATA;
ret = dsp->dma_data & 0xff;
}
dsp->dma_ff = !dsp->dma_ff;
} else
ret = dma_channel_read(dsp->sb_8_dmanum);
return ret;
}
int
sb_8_write_dma(void *priv, uint8_t val)
{
sb_dsp_t *dsp = (sb_dsp_t *) priv;
dsp->activity &= 0xdf;
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)
{
sb_dsp_t *dsp = (sb_dsp_t *) priv;
int ret;
int dma_ch = dsp->sb_16_dmanum;
dsp->activity &= 0xdf;
if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate && (dma_ch != 4))
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_supported && !dsp->sb_16_dma_translate && (dma_ch == 4))
dma_ch = dsp->sb_8_dmanum;
else
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;
int 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 {
const int 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)
{
sb_dsp_t *dsp = (sb_dsp_t *) priv;
int dma_ch = dsp->sb_16_dmanum;
int ret;
dsp->activity &= 0xdf;
if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate && (dma_ch != 4))
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_supported && !dsp->sb_16_dma_translate && (dma_ch == 4))
dma_ch = dsp->sb_8_dmanum;
else
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;
int 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_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy)
{
sb_t *ess = (sb_t *) dsp->parent;
ess_mixer_t *mixer = &ess->mixer_ess;
uint8_t t = 0x00;
/* IRQ control */
if (legacy) {
t |= 0x80;
}
switch (dsp->sb_irqnum) {
default:
break;
case 2:
case 9:
t |= 0x0;
break;
case 5:
t |= 0x5;
break;
case 7:
t |= 0xA;
break;
case 10:
t |= 0xF;
break;
}
ESSreg(0xB1) = (ESSreg(0xB1) & 0xF0) | t;
if ((mixer != NULL) && (ess->mpu != NULL) && (((mixer->regs[0x40] >> 5) & 0x7) == 2))
mpu401_setirq(ess->mpu, ess->dsp.sb_irqnum);
/* DRQ control */
t = 0x00;
if (legacy) {
t |= 0x80;
}
switch (dsp->sb_8_dmanum) {
default:
break;
case 0:
t |= 0x5;
break;
case 1:
t |= 0xA;
break;
case 3:
t |= 0xF;
break;
}
ESSreg(0xB2) = (ESSreg(0xB2) & 0xF0) | t;
}
void
sb_dsp_setirq(sb_dsp_t *dsp, int irq)
{
sb_dsp_log("IRQ now: %i\n", irq);
dsp->sb_irqnum = irq;
if (IS_ESS(dsp)) {
sb_ess_update_irq_drq_readback_regs(dsp, true);
ESSreg(0xB1) = (ESSreg(0xB1) & 0xEF) | 0x10;
}
}
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;
if (IS_ESS(dsp))
sb_ess_update_irq_drq_readback_regs(dsp, true);
}
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, const int translate)
{
sb_dsp_log("16-bit to 8-bit translation now: %sabled\n", translate ? "en" : "dis");
dsp->sb_16_dma_translate = translate;
}
static void
sb_ess_update_reg_a2(sb_dsp_t *dsp, const uint8_t val)
{
const double freq = (7160000.0 / (256.0 - ((double) val))) * 41.0;
const int temp = (int) freq;
ESSreg(0xA2) = val;
if (dsp->sb_freq != temp)
recalc_sb16_filter(0, temp);
dsp->sb_freq = temp;
}
/* TODO: Investigate ESS cards' filtering on real hardware as well.
(DOSBox-X did it purely off some laptop's ESS chip, which isn't a good look.) */
static void
sb_ess_update_filter_freq(sb_dsp_t *dsp)
{
const double temp = (7160000.0 / (((((double) dsp->sb_freq) / 2.0) * 0.80) * 82.0)) - 256.0;
if (dsp->sb_freq >= 22050)
ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq);
else
ESSreg(0xA1) = 128 - (397700UL / dsp->sb_freq);
sb_ess_update_reg_a2(dsp, (uint8_t) temp);
}
static uint8_t
sb_ess_read_reg(const sb_dsp_t *dsp, const uint8_t reg)
{
return ESSreg(reg);
}
static void
sb_ess_update_autolen(sb_dsp_t *dsp)
{
dsp->sb_8_autolen = dsp->sb_16_autolen = (int) sb_ess_get_dma_len(dsp);
}
static void
sb_ess_write_reg(sb_dsp_t *dsp, const uint8_t reg, uint8_t data)
{
uint8_t chg;
sb_dsp_log("ESS Write reg=%02x, val=%02x.\n", reg, data);
switch (reg) {
case 0xA1: /* Extended Mode Sample Rate Generator */
{
ESSreg(reg) = data;
if (data & 0x80)
dsp->sb_freq = (int) (795500UL / (256ul - data));
else
dsp->sb_freq = (int) (397700UL / (128ul - data));
const double temp = 1000000.0 / dsp->sb_freq;
dsp->sblatchi = dsp->sblatcho = ((double) TIMER_USEC * temp);
dsp->sb_timei = dsp->sb_timeo;
break;
}
case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */
sb_ess_update_reg_a2(dsp, data);
break;
case 0xA4: /* DMA Transfer Count Reload (low) */
case 0xA5: /* DMA Transfer Count Reload (high) */
ESSreg(reg) = data;
sb_ess_update_autolen(dsp);
if ((dsp->sb_16_length < 0 && !dsp->sb_16_enable) && (dsp->sb_8_length < 0 && !dsp->sb_8_enable))
dsp->ess_reload_len = 1;
break;
case 0xA8: /* Analog Control */
/* bits 7:5 0 Reserved. Always write 0
* bit 4 1 Reserved. Always write 1
* bit 3 Record monitor 1=Enable record monitor
* enable
* bit 2 0 Reserved. Always write 0
* bits 1:0 Stereo/mono select 00=Reserved
* 01=Stereo
* 10=Mono
* 11=Reserved */
chg = ESSreg(reg) ^ data;
ESSreg(reg) = data;
if (chg & 0x3) {
if (dsp->sb_16_enable || dsp->sb_8_enable) {
uint8_t real_format = 0x00;
real_format |= !!(ESSreg(0xB7) & 0x20) ? 0x10 : 0;
real_format |= !!(ESSreg(0xB7) & 0x8) ? 0x20 : 0;
if (dsp->sb_16_enable)
dsp->sb_16_format = real_format;
if (dsp->sb_8_enable)
dsp->sb_8_format = real_format;
}
}
break;
case 0xB1: /* Legacy Audio Interrupt Control */
ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable
switch (data & 0x0C) {
default:
break;
case 0x00:
dsp->sb_irqnum = 2;
break;
case 0x04:
dsp->sb_irqnum = 5;
break;
case 0x08:
dsp->sb_irqnum = 7;
break;
case 0x0C:
dsp->sb_irqnum = 10;
break;
}
sb_dsp_log("Legacy Audio IRQ control=%d.\n", dsp->sb_irqnum);
sb_ess_update_irq_drq_readback_regs(dsp, false);
break;
case 0xB2: /* DRQ Control */
chg = ESSreg(reg) ^ data;
ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable
switch (data & 0x0C) {
default:
break;
case 0x00:
dsp->sb_8_dmanum = -1;
break;
case 0x04:
dsp->sb_8_dmanum = 0;
break;
case 0x08:
dsp->sb_8_dmanum = 1;
break;
case 0x0C:
dsp->sb_8_dmanum = 3;
break;
}
sb_dsp_log("Legacy Audio DRQ control=%d, chg=%02x.\n", dsp->sb_8_dmanum, chg);
sb_ess_update_irq_drq_readback_regs(dsp, false);
if (chg & 0x40)
sb_ess_update_dma_status(dsp);
break;
case 0xB5: /* DAC Direct Access Holding (low) */
case 0xB6: /* DAC Direct Access Holding (high) */
ESSreg(reg) = data;
break;
case 0xB7: /* Audio 1 Control 1 */
/* bit 7 Enable FIFO to/from codec
* bit 6 Opposite from bit 3 Must be set opposite to bit 3
* bit 5 FIFO signed mode 1=Data is signed twos-complement 0=Data is unsigned
* bit 4 Reserved Always write 1
* bit 3 FIFO stereo mode 1=Data is stereo
* bit 2 FIFO 16-bit mode 1=Data is 16-bit
* bit 1 Reserved Always write 0
* bit 0 Generate load signal */
chg = ESSreg(reg) ^ data;
ESSreg(reg) = data;
if (chg & 4)
sb_ess_update_autolen(dsp);
if (chg & 0x0C) {
if (dsp->sb_16_enable || dsp->sb_8_enable) {
sb_stop_dma_ess(dsp);
sb_start_dma_ess(dsp);
}
}
break;
case 0xB8: /* Audio 1 Control 2 */
/* bits 7:4 reserved
* bit 3 CODEC mode 1=first DMA converter in ADC mode
* 0=first DMA converter in DAC mode
* bit 2 DMA mode 1=auto-initialize mode
* 0=normal DMA mode
* bit 1 DMA read enable 1=first DMA is read (for ADC)
* 0=first DMA is write (for DAC)
* bit 0 DMA xfer enable 1=DMA is allowed to proceed */
data &= 0xF;
chg = ESSreg(reg) ^ data;
ESSreg(reg) = data;
if (chg & 1) {
if (dsp->sb_16_enable || dsp->sb_8_enable) {
if (dsp->sb_16_enable)
dsp->sb_16_length = (int) sb_ess_get_dma_len(dsp);
if (dsp->sb_8_enable)
dsp->sb_8_length = (int) sb_ess_get_dma_len(dsp);
} else
dsp->ess_reload_len = 1;
}
if (chg & 0x4) {
if (dsp->sb_16_enable) {
dsp->sb_16_autoinit = (ESSreg(0xB8) & 0x4) != 0;
}
if (dsp->sb_8_enable) {
dsp->sb_8_autoinit = (ESSreg(0xB8) & 0x4) != 0;
}
}
if (chg & 0xB) {
if (chg & 0xA)
sb_stop_dma_ess(dsp); /* changing capture/playback direction? stop DMA to reinit */
sb_ess_update_dma_status(dsp);
}
break;
case 0xB9: /* Audio 1 Transfer Type */
case 0xBA: /* Left Channel ADC Offset Adjust */
case 0xBB: /* Right Channel ADC Offset Adjust */
case 0xC3: /* Internal state register */
case 0xCF: /* GPO0/1 power management register */
ESSreg(reg) = data;
break;
default:
sb_dsp_log("UNKNOWN ESS register write reg=%02xh val=%02xh\n", reg, data);
break;
}
}
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_404) {
dsp->sb_8051_ram[0x20] = dsp->sb_command;
}
if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) {
if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) {
dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6);
return;
} else if (dsp->sb_command == 0xC2) {
sb_ess_write_reg(dsp, 0xC3, dsp->sb_data[0]);
} else if (dsp->sb_command == 0xC3) {
sb_add_data(dsp, sb_ess_read_reg(dsp, 0xC3));
} else if (dsp->sb_command == 0xCE) {
sb_add_data(dsp, sb_ess_read_reg(dsp, 0xCF));
} else if (dsp->sb_command == 0xCF) {
sb_ess_write_reg(dsp, 0xCF, dsp->sb_data[0]);
} else if (dsp->sb_command == 0xC0) {
sb_add_data(dsp, sb_ess_read_reg(dsp, dsp->sb_data[0]));
} else if (dsp->sb_command < 0xC0 && dsp->ess_extended_mode) {
sb_ess_write_reg(dsp, dsp->sb_command, dsp->sb_data[0]);
}
return;
}
switch (dsp->sb_command) {
case 0x01: /* ???? */
if (dsp->sb_type >= SB16_DSP_404)
dsp->asp_data_len = dsp->sb_data[0] + (dsp->sb_data[1] << 8) + 1;
break;
case 0x03: /* ASP status */
if (dsp->sb_type >= SB16_DSP_404)
sb_add_data(dsp, 0);
break;
case 0x04: /* ASP set mode register */
if (dsp->sb_type >= SB16_DSP_404) {
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_DSP_404) {
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_DSP_416) /* AWE64 has no ASP or a socket for it */
sb_add_data(dsp, 0xFF);
else if (dsp->sb_type >= SB16_DSP_404)
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_404) {
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_DSP_404) {
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 = (int16_t) ((dsp->sb_data[0] ^ 0x80) << 8);
// FIXME: What does the ESS AudioDrive do to its filter/sample rate divider registers when emulating this Sound Blaster command?
ESSreg(0xA1) = 128 - (397700 / 22050);
ESSreg(0xA2) = 256 - (7160000 / (82 * ((4 * 22050) / 10)));
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--;
dsp->ess_dma_counter++;
if (dsp->sb_command == 0x17) {
dsp->sb_8_length--;
dsp->ess_dma_counter++;
}
break;
case 0x1C: /* 8-bit autoinit DMA output */
if (dsp->sb_type >= SB_DSP_200)
sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen);
break;
case 0x1F: /* 2-bit ADPCM autoinit output */
if (dsp->sb_type >= SB_DSP_200) {
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--;
dsp->ess_dma_counter++;
}
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 = (double) ((double) TIMER_USEC * 22.0);
temp = 1000000 / 22;
dsp->sb_freq = temp;
timer_set_delay_u64(&dsp->input_timer, (uint64_t) 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 >= SB_DSP_200)
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 */
case 0x33: /* MIDI Read Timestamp Interrupt */
break;
case 0x34: /* MIDI In poll */
if (dsp->sb_type < SB_DSP_200)
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 < SB_DSP_200)
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 = (double) (TIMER_USEC * (256 - dsp->sb_data[0]));
temp = 256 - dsp->sb_data[0];
temp = 1000000 / temp;
sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho);
if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16_DSP_404))
recalc_sb16_filter(0, temp);
dsp->sb_freq = temp;
if (IS_ESS(dsp)) {
sb_ess_update_filter_freq(dsp);
}
break;
case 0x41: /* Set output sampling rate */
case 0x42: /* Set input sampling rate */
if (dsp->sb_type >= SB16_DSP_404) {
dsp->sblatcho = (double) ((double) TIMER_USEC * (1000000.0 / (double) (dsp->sb_data[1] + (dsp->sb_data[0] << 8))));
sb_dsp_log("Sample rate - %ihz (%f)\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 = 256 + dsp->sb_freq;
dsp->sblatchi = dsp->sblatcho;
dsp->sb_timei = dsp->sb_timeo;
if (dsp->sb_freq != temp)
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 */
case 0x47: /* Continue Auto-Initialize DMA, 16-bit */
break;
case 0x48: /* Set DSP block transfer size */
if (dsp->sb_type >= SB_DSP_200)
dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8);
break;
case 0x65: /* 4-bit ESPCM output with reference */
case 0x64: /* 4-bit ESPCM output */
if (IS_ESS(dsp)) {
if (dsp->espcm_mode != ESPCM_4 || (dsp->sb_8_enable && dsp->sb_8_pause)) {
fifo_reset(dsp->espcm_fifo);
dsp->espcm_sample_idx = 0;
}
dsp->espcm_mode = ESPCM_4;
sb_start_dma(dsp, 1, 0, ESPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
}
break;
case 0x67: /* 3-bit ESPCM output with reference */
case 0x66: /* 3-bit ESPCM output */
if (IS_ESS(dsp)) {
if (dsp->espcm_mode != ESPCM_3 || (dsp->sb_8_enable && dsp->sb_8_pause)) {
fifo_reset(dsp->espcm_fifo);
dsp->espcm_sample_idx = 0;
}
dsp->espcm_mode = ESPCM_3;
sb_start_dma(dsp, 1, 0, ESPCM_3, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
}
break;
case 0x6D: /* 1-bit ESPCM output with reference */
case 0x6C: /* 1-bit ESPCM output */
if (IS_ESS(dsp)) {
if (dsp->espcm_mode != ESPCM_1 || (dsp->sb_8_enable && dsp->sb_8_pause)) {
fifo_reset(dsp->espcm_fifo);
dsp->espcm_sample_idx = 0;
}
dsp->espcm_mode = ESPCM_1;
sb_start_dma(dsp, 1, 0, ESPCM_1, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
}
break;
case 0x6F: /* 4-bit ESPCM input with reference */
case 0x6E: /* 4-bit ESPCM input */
if (IS_ESS(dsp)) {
if (dsp->espcm_mode != ESPCM_4E || (dsp->sb_8_enable && dsp->sb_8_pause)) {
fifo_reset(dsp->espcm_fifo);
dsp->espcm_sample_idx = 0;
}
dsp->espcm_mode = ESPCM_4E;
sb_start_dma_i(dsp, 1, 0, ESPCM_4E, 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--;
dsp->ess_dma_counter++;
if (dsp->sb_command == 0x75) {
dsp->sb_8_length--;
dsp->ess_dma_counter++;
}
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--;
dsp->ess_dma_counter++;
if (dsp->sb_command == 0x77) {
dsp->sb_8_length--;
dsp->ess_dma_counter++;
}
break;
case 0x7D: /* 4-bit ADPCM autoinit output */
if (dsp->sb_type >= SB_DSP_200) {
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--;
dsp->ess_dma_counter++;
}
break;
case 0x7F: /* 2.6-bit ADPCM autoinit output */
if (dsp->sb_type >= SB_DSP_200) {
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--;
dsp->ess_dma_counter++;
}
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, (uint64_t) trunc(dsp->sblatcho));
break;
case 0x90: /* High speed 8-bit autoinit DMA output */
if (dsp->sb_type >= SB_DSP_201) // TODO docs need validated
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 >= SB_DSP_201) // TODO docs need validated
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 >= SB_DSP_201) // TODO docs need validated
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 >= SB_DSP_201) // TODO docs need validated
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 < SBPRO_DSP_300) || (dsp->sb_type > SBPRO2_DSP_302))
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_DSP_404) {
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_DSP_404) {
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_DSP_404) {
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_DSP_404) {
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;
sb_stop_dma(dsp);
break;
case 0xD1: /* Speaker on */
if (IS_NOT_ESS(dsp)) {
if (dsp->sb_type < SB_DSP_200) {
dsp->sb_8_pause = 1;
sb_stop_dma(dsp);
} else if (dsp->sb_type < SB16_DSP_404)
dsp->muted = 0;
}
dsp->sb_speaker = 1;
break;
case 0xD3: /* Speaker off */
if (IS_NOT_ESS(dsp)) {
if (dsp->sb_type < SB_DSP_201) {
dsp->sb_8_pause = 1;
sb_stop_dma(dsp);
} else if (dsp->sb_type < SB16_DSP_404)
dsp->muted = 1;
}
dsp->sb_speaker = 0;
break;
case 0xD4: /* Continue 8-bit DMA */
dsp->sb_8_pause = 0;
sb_resume_dma(dsp, 1);
break;
case 0xD5: /* Pause 16-bit DMA */
if (dsp->sb_type >= SB16_DSP_404) {
dsp->sb_16_pause = 1;
sb_stop_dma(dsp);
}
break;
case 0xD6: /* Continue 16-bit DMA */
if (dsp->sb_type >= SB16_DSP_404) {
dsp->sb_16_pause = 0;
sb_resume_dma(dsp, 1);
}
break;
case 0xD8: /* Get speaker status */
if (dsp->sb_type >= SB_DSP_200)
sb_add_data(dsp, dsp->sb_speaker ? 0xff : 0);
break;
case 0xD9: /* Exit 16-bit auto-init mode */
if (dsp->sb_type >= SB16_DSP_404)
dsp->sb_16_autoinit = 0;
break;
case 0xDA: /* Exit 8-bit auto-init mode */
if (dsp->sb_type >= SB_DSP_200)
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_ESS(dsp)) {
/*
0x03 0x01 (Sound Blaster Pro compatibility) confirmed by both the
ES1888 datasheet and the probing of the real ES688 and ES1688 cards.
*/
sb_add_data(dsp, 0x3);
sb_add_data(dsp, 0x1);
break;
}
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++;
if (dsp->dma_writeb(dsp->dma_priv, dsp->sbe2))
/* Undocumented behavior: If write to DMA fails, the byte is written
to the CPU instead. The NT 3.1 Sound Blaster Pro driver relies on this. */
sb_add_data(dsp, dsp->sbe2);
break;
case 0xE3: /* DSP copyright */
if (dsp->sb_type >= SB16_DSP_404) {
c = 0;
while (sb16_copyright[c])
sb_add_data(dsp, sb16_copyright[c++]);
sb_add_data(dsp, 0);
} /* else if (IS_ESS(dsp))
sb_add_data(dsp, 0); */
/*
TODO: What ESS card returns 0x00 here? Probing of the real ES688 and ES1688 cards
revealed that they in fact return nothing on this command.
*/
break;
case 0xE4: /* Write test register */
dsp->sb_test = dsp->sb_data[0];
break;
case 0xE7: /* ESS detect/read config on ESS cards */
if (IS_ESS(dsp)) {
switch (dsp->sb_subtype) {
default:
break;
case SB_SUBTYPE_ESS_ES688:
sb_add_data(dsp, 0x68);
/*
80h: ESSCFG fails to detect the AudioDrive;
81h-83h: ES??88, Windows 3.1 driver expects MPU-401 and gives a legacy mixer error;
84h: ES688, Windows 3.1 driver expects MPU-401, returned by DOSBox-X;
85h-87h: ES688, Windows 3.1 driver does not expect MPU-401:
85h: Returned by MSDOS622's real ESS688,
86h: Returned by Dizzy's real ES688.
We return 86h if MPU is absent, 84h otherwise, who knows what the actual
PnP ES688 returns here.
*/
sb_add_data(dsp, 0x80 | ((dsp->mpu != NULL) ? 0x04 : 0x06));
break;
case SB_SUBTYPE_ESS_ES1688:
sb_add_data(dsp, 0x68);
/*
89h: ES1688, returned by DOSBox-X, determined via Windows driver
debugging;
8Bh: ES1688, returned by both MSDOS622's and Dizzy's real ES1688's.
*/
sb_add_data(dsp, 0x80 | 0x0b);
break;
}
}
break;
case 0xE8: /* Read test register */
sb_add_data(dsp, dsp->sb_test);
break;
case 0xF2: /* Trigger 8-bit IRQ */
sb_dsp_log("Trigger 8-bit IRQ\n");
timer_set_delay_u64(&dsp->irq_timer, (10ULL * TIMER_USEC));
break;
case 0xF3: /* Trigger 16-bit IRQ */
sb_dsp_log("Trigger 16-bit IRQ\n");
if (IS_ESS(dsp))
dsp->ess_irq_generic = true;
else
timer_set_delay_u64(&dsp->irq16_timer, (10ULL * TIMER_USEC));
break;
case 0xF8:
if (dsp->sb_type < SB16_DSP_404)
sb_add_data(dsp, 0);
break;
case 0xF9: /* SB16 8051 RAM read */
if (dsp->sb_type >= SB16_DSP_404)
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_404)
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/
* https://github.com/schlae/sb-firmware/blob/master/sbv202.asm
* 008h Halt (Infinate Loop) SB2???
* 018h DMA playback with auto init DMA. SB2???
* 028h Auto-init direct ADC SB2???
* 036h (Timestamp) SB???
* 037h (Timestamp) SB???
* 050h Stops playback of SRAM samples SB???
* 051h Plays back samples stored in SRAM. SB???
* 058h Load data into SRAM SB???
* 059h Fetches the samples and then immediately plays them back. SB???
* 078h Auto-init DMA ADPCM SB2???
* 07Ah 2.6-bit ADPCM SB???
* 0E3h DSP Copyright SBPro2??? (SBPRO2_DSP_302)
* 0F0h Sine Generator SB (SB_DSP_105, DSP20x)
* 0F1h DSP Auxiliary Status (Obsolete) SB-Pro2 (DSP20x, SBPRO2_DSP_302)
* 0F2h IRQ Request, 8-bit SB (SB_DSP_105, DSP20x)
* 0F3h IRQ Request, 16-bit SB16
* 0F4h Perform ROM checksum SB (SB_DSP_105, DSP20x)
* 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_404)
dsp->sb_8051_ram[0x30] = dsp->sb_command;
}
static void
sb_do_reset(sb_dsp_t *dsp, const uint8_t v)
{
if (((v & 1) != 0) && (dsp->state != DSP_S_RESET)) {
sb_dsp_reset(dsp);
dsp->sb_read_rp = dsp->sb_read_wp = 0;
dsp->state = DSP_S_RESET;
} else if (((v & 1) == 0) && (dsp->state == DSP_S_RESET)) {
dsp->state = DSP_S_RESET_WAIT;
dsp->sb_read_rp = dsp->sb_read_wp = 0;
sb_add_data(dsp, 0xaa);
}
}
void
sb_write(uint16_t addr, uint8_t val, 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_DSP_404) && (IS_NOT_ESS(dsp) || ((addr & 0xF) != 0xE)))
addr &= 0xfffe;
sb_dsp_log("[%04X:%08X] DSP: [W] %04X = %02X\n", CS, cpu_state.pc, addr, val);
switch (addr & 0xF) {
case 6: /* Reset */
sb_do_reset(dsp, val);
if (!(val & 2) && (dsp->espcm_fifo_reset & 2)) {
fifo_reset(dsp->espcm_fifo);
}
dsp->espcm_fifo_reset = val;
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(val);
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 = val;
if (val == 0x01)
sb_add_data(dsp, 0);
dsp->sb_data_stat++;
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 (IS_ESS(dsp) && dsp->sb_command >= 0x64 && dsp->sb_command <= 0x6F) {
sb_commands[dsp->sb_command] = 2;
} else if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) {
if (dsp->sb_command <= 0xC0
|| dsp->sb_command == 0xC2
|| dsp->sb_command == 0xCF) {
sb_commands[dsp->sb_command] = 1;
} else if (dsp->sb_command == 0xC3
|| dsp->sb_command == 0xC6
|| dsp->sb_command == 0xC7
|| dsp->sb_command == 0xCE) {
sb_commands[dsp->sb_command] = 0;
} else {
sb_commands[dsp->sb_command] = -1;
}
}
} else {
dsp->sb_data[dsp->sb_data_stat++] = val;
}
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 addr, 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_DSP_404) && (IS_NOT_ESS(dsp) || ((addr & 0xF) != 0xE)))
/* Exception: ESS AudioDrive does not alias port base+0xf */
addr &= 0xfffe;
switch (addr & 0xf) {
case 0x6:
if (IS_ESS(dsp)) {
ret = (dsp->espcm_fifo_reset & 0x03) | 0x08 | (dsp->activity & 0xe0);
dsp->activity |= 0xe0;
} else
ret = 0xff;
break;
case 0x7:
case 0xB:
/*
These two ports are tested for random noise by OS/2 Warp 4.0, so
return 0xff to get through said test.
*/
ret = 0xff;
break;
case 0xA: /* Read data */
if (dsp->mpu && dsp->uart_midi)
ret = MPU401_ReadData(dsp->mpu);
else {
if (dsp->sb_read_rp != dsp->sb_read_wp) {
dsp->sbreaddat = dsp->sb_read_data[dsp->sb_read_rp];
dsp->sb_read_rp++;
dsp->sb_read_rp &= 0xff;
}
ret = dsp->sbreaddat;
}
/* Advance the state just in case something reads from here
without reading the status first. */
if (dsp->state == DSP_S_RESET_WAIT)
dsp->state = DSP_S_NORMAL;
break;
case 0xC: /* Write data ready */
/* Advance the state just in case something reads from here
without reading the status first. */
if (dsp->state == DSP_S_RESET_WAIT)
dsp->state = DSP_S_NORMAL;
if ((dsp->state == DSP_S_NORMAL) || IS_ESS(dsp)) {
if (dsp->sb_8_enable || dsp->sb_type >= SB16_DSP_404)
dsp->busy_count = (dsp->busy_count + 1) & 3;
else
dsp->busy_count = 0;
if (IS_ESS(dsp)) {
if (dsp->wb_full || (dsp->busy_count & 2))
dsp->wb_full = timer_is_enabled(&dsp->wb_timer);
const uint8_t busy_flag = dsp->wb_full ? 0x80 : 0x00;
const uint8_t data_rdy = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x40;
const uint8_t fifo_full = 0; /* Unimplemented */
const uint8_t fifo_empty = 0; /* (this is for the 256-byte extended mode FIFO, */
const uint8_t fifo_half = 0; /* not the standard 64-byte FIFO) */
const uint8_t irq_generic = dsp->ess_irq_generic ? 0x04 : 0x00;
const uint8_t irq_fifohe = 0; /* Unimplemented (ditto) */
const uint8_t irq_dmactr = dsp->ess_irq_dmactr ? 0x01 : 0x00;
ret = busy_flag | data_rdy | fifo_full | fifo_empty | fifo_half | irq_generic | irq_fifohe | irq_dmactr;
} else 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");
ret = 0x80;
} else {
sb_dsp_log("SB Write Data Creative read 0xff\n");
if ((dsp->sb_type >= SB_DSP_201) && (dsp->sb_type < SB16_DSP_404) && IS_NOT_ESS(dsp))
ret = 0xaa;
else
ret = 0xff;
}
} else 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");
if ((dsp->sb_type >= SB_DSP_201) && (dsp->sb_type < SB16_DSP_404) && IS_NOT_ESS(dsp))
ret = 0x2a;
else
ret = 0x7f;
}
} else if (IS_AZTECH(dsp))
ret = 0x00;
else
ret = 0xff;
break;
case 0xE: /* Read data ready */
dsp->irq_update(dsp->irq_priv, 0);
dsp->sb_irq8 = dsp->sb_irq16 = 0;
dsp->ess_irq_generic = dsp->ess_irq_dmactr = false;
/*
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);
if ((dsp->sb_type < SB16_DSP_404) && IS_NOT_ESS(dsp))
ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x2a : 0xaa;
else
ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7f : 0xff;
}
if (dsp->state == DSP_S_RESET_WAIT)
dsp->state = DSP_S_NORMAL;
break;
case 0xF: /* 16-bit ack */
if (IS_NOT_ESS(dsp)) {
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;
}
sb_dsp_log("[%04X:%08X] DSP: [R] %04X = %02X\n", CS, cpu_state.pc, addr, ret);
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);
dsp->ess_irq_generic = true;
} 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 (int) (len - i);
}
sb_add_data(dsp, buffer[i]);
}
dsp->midi_in_sysex = 0;
return 0;
}
void
sb_dsp_irq_poll(void *priv)
{
sb_dsp_t *dsp = (sb_dsp_t *) priv;
sb_irq(dsp, 1);
dsp->ess_irq_generic = true;
}
void
sb_dsp_irq16_poll(void *priv)
{
sb_dsp_t *dsp = (sb_dsp_t *) priv;
sb_irq(dsp, 0);
dsp->ess_irq_generic = true;
}
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;
dsp->activity = 0xe0;
/* Default values. Use sb_dsp_setxxx() methods to change. */
dsp->sb_irqnum = 7;
dsp->sb_8_dmanum = 1;
if (type >= SB16_DSP_404)
dsp->sb_16_dmanum = 5;
else
dsp->sb_16_dmanum = 0xff;
if ((type >= SB16_DSP_404) || IS_ESS(dsp))
dsp->sb_16_8_dmanum = 0x1;
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);
timer_add(&dsp->irq_timer, sb_dsp_irq_poll, dsp, 0);
timer_add(&dsp->irq16_timer, sb_dsp_irq16_poll, dsp, 0);
if (IS_ESS(dsp))
/* Initialize ESS filter to 8 kHz. This will be recalculated when a set frequency command is
sent. */
recalc_sb16_filter(0, 8000 * 2);
else {
timer_add(&dsp->irq16_timer, sb_dsp_irq16_poll, 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);
}
if (IS_ESS(dsp) || (dsp->sb_type >= SBPRO2_DSP_302)) {
/* OPL3 or dual OPL2 is stereo. */
if (dsp->sb_has_real_opl)
recalc_opl_filter(FREQ_49716 * 2);
else
recalc_sb16_filter(1, FREQ_48000 * 2);
} else {
/* OPL2 is mono. */
if (dsp->sb_has_real_opl)
recalc_opl_filter(FREQ_49716);
else
recalc_sb16_filter(1, FREQ_48000);
}
/* CD Audio is stereo. */
recalc_sb16_filter(2, FREQ_44100 * 2);
/* PC speaker is mono. */
recalc_sb16_filter(3, 18939);
/* E-MU 8000 is stereo. */
recalc_sb16_filter(4, FREQ_44100 * 2);
/* 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));
dsp->espcm_fifo = fifo64_init();
fifo_set_trigger_len(dsp->espcm_fifo, 1);
}
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_real_opl(sb_dsp_t *dsp, uint8_t has_real_opl)
{
dsp->sb_has_real_opl = has_real_opl;
}
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
sb_espcm_fifoctl_run(sb_dsp_t *dsp)
{
if (fifo_get_empty(dsp->espcm_fifo) && !dsp->sb_8_pause) {
while (!fifo_get_full(dsp->espcm_fifo)) {
int32_t val;
val = dsp->dma_readb(dsp->dma_priv);
dsp->ess_dma_counter++;
fifo_write(val & 0xff, dsp->espcm_fifo);
if (val & DMA_OVER) {
break;
}
}
}
}
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, (uint64_t) dsp->sblatcho);
if (dsp->sb_8_enable && dsp->sb_pausetime < 0 && dsp->sb_8_output) {
sb_dsp_update(dsp);
sb_dsp_log("8-bit format=%02x, pause=%x, length=%d.\n", dsp->sb_8_format, dsp->sb_8_pause, dsp->sb_8_length);
switch (dsp->sb_8_format) {
case 0x00: /* Mono unsigned */
if (!dsp->sb_8_pause) {
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 = (int16_t) ((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--;
dsp->ess_dma_counter++;
}
break;
case 0x10: /* Mono signed */
if (!dsp->sb_8_pause) {
data[0] = dsp->dma_readb(dsp->dma_priv);
if (data[0] == DMA_NODATA)
break;
dsp->sbdat = (int16_t) (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--;
dsp->ess_dma_counter++;
}
break;
case 0x20: /* Stereo unsigned */
if (!dsp->sb_8_pause) {
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 = (int16_t) ((data[0] ^ 0x80) << 8);
dsp->sbdatr = (int16_t) ((data[1] ^ 0x80) << 8);
dsp->sb_8_length -= 2;
dsp->ess_dma_counter += 2;
}
break;
case 0x30: /* Stereo signed */
if (!dsp->sb_8_pause) {
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 = (int16_t) (data[0] << 8);
dsp->sbdatr = (int16_t) (data[1] << 8);
dsp->sb_8_length -= 2;
dsp->ess_dma_counter += 2;
}
break;
case ADPCM_4:
if (!dsp->sb_8_pause) {
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 = (int8_t) ((dsp->sbstep + adjustMap4[tempi]) & 0xff);
dsp->sbdat = (int16_t) ((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--;
dsp->ess_dma_counter++;
}
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->sb_8_pause) {
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 = (int8_t) ((dsp->sbstep + adjustMap26[tempi]) & 0xff);
dsp->sbdat = (int16_t) ((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--;
dsp->ess_dma_counter++;
}
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:
if (!dsp->sb_8_pause) {
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 = (int8_t) ((dsp->sbstep + adjustMap2[tempi]) & 0xff);
dsp->sbdat = (int16_t) ((dsp->sbref ^ 0x80) << 8);
dsp->sbdacpos++;
if (dsp->sbdacpos >= 4) {
dsp->sbdacpos = 0;
dsp->sbdat2 = dsp->dma_readb(dsp->dma_priv);
dsp->sb_8_length--;
dsp->ess_dma_counter++;
}
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;
case ESPCM_4:
if (dsp->espcm_sample_idx >= 19)
dsp->espcm_sample_idx = 0;
if (dsp->espcm_sample_idx == 0) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo))
break;
dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F;
tempi = dsp->espcm_byte_buffer[0] >> 4;
} else if (dsp->espcm_sample_idx & 1) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo))
break;
dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
dsp->sb_8_length--;
tempi = dsp->espcm_byte_buffer[0] & 0x0F;
} else
tempi = dsp->espcm_byte_buffer[0] >> 4;
if (dsp->espcm_sample_idx == 18)
dsp->sb_8_length--;
dsp->espcm_sample_idx++;
tempi |= (dsp->espcm_range << 4);
data[0] = (int) espcm_range_map[tempi];
dsp->sbdat = (int16_t) (data[0] << 8);
if (dsp->stereo) {
sb_dsp_log("pollsb: ESPCM 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 ESPCM_3:
if (dsp->espcm_sample_idx >= 19)
dsp->espcm_sample_idx = 0;
if (dsp->espcm_sample_idx == 0) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo))
break;
dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F;
tempi = dsp->espcm_byte_buffer[0] >> 4;
dsp->espcm_last_value = tempi;
} else if (dsp->espcm_sample_idx == 1) {
for (tempi = 0; tempi < 4; tempi++) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo))
break;
dsp->espcm_byte_buffer[tempi] = fifo_read(dsp->espcm_fifo);
dsp->sb_8_length--;
}
if (tempi < 4)
break;
dsp->espcm_table_index = dsp->espcm_byte_buffer[0] & 0x03;
dsp->espcm_code_buffer[0] = (dsp->espcm_byte_buffer[0] >> 2) & 0x07;
dsp->espcm_code_buffer[1] = (dsp->espcm_byte_buffer[0] >> 5) & 0x07;
dsp->espcm_code_buffer[2] = (dsp->espcm_byte_buffer[1]) & 0x07;
dsp->espcm_code_buffer[3] = (dsp->espcm_byte_buffer[1] >> 3) & 0x07;
dsp->espcm_code_buffer[4] = ((dsp->espcm_byte_buffer[1] >> 6) & 0x03) | ((dsp->espcm_byte_buffer[2] & 0x01) << 2);
dsp->espcm_code_buffer[5] = (dsp->espcm_byte_buffer[2] >> 1) & 0x07;
dsp->espcm_code_buffer[6] = (dsp->espcm_byte_buffer[2] >> 4) & 0x07;
dsp->espcm_code_buffer[7] = ((dsp->espcm_byte_buffer[2] >> 7) & 0x01) | ((dsp->espcm_byte_buffer[3] & 0x03) << 1);
dsp->espcm_code_buffer[8] = (dsp->espcm_byte_buffer[3] >> 2) & 0x07;
dsp->espcm_code_buffer[9] = (dsp->espcm_byte_buffer[3] >> 5) & 0x07;
tempi = (dsp->espcm_table_index << 8) | (dsp->espcm_last_value << 3) | dsp->espcm_code_buffer[0];
tempi = espcm3_dpcm_tables[tempi];
dsp->espcm_last_value = tempi;
} else if (dsp->espcm_sample_idx == 11) {
for (tempi = 1; tempi < 4; tempi++) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo))
break;
dsp->espcm_byte_buffer[tempi] = fifo_read(dsp->espcm_fifo);
dsp->sb_8_length--;
}
if (tempi < 4)
break;
dsp->espcm_code_buffer[0] = (dsp->espcm_byte_buffer[1]) & 0x07;
dsp->espcm_code_buffer[1] = (dsp->espcm_byte_buffer[1] >> 3) & 0x07;
dsp->espcm_code_buffer[2] = ((dsp->espcm_byte_buffer[1] >> 6) & 0x03) | ((dsp->espcm_byte_buffer[2] & 0x01) << 2);
dsp->espcm_code_buffer[3] = (dsp->espcm_byte_buffer[2] >> 1) & 0x07;
dsp->espcm_code_buffer[4] = (dsp->espcm_byte_buffer[2] >> 4) & 0x07;
dsp->espcm_code_buffer[5] = ((dsp->espcm_byte_buffer[2] >> 7) & 0x01) | ((dsp->espcm_byte_buffer[3] & 0x03) << 1);
dsp->espcm_code_buffer[6] = (dsp->espcm_byte_buffer[3] >> 2) & 0x07;
dsp->espcm_code_buffer[7] = (dsp->espcm_byte_buffer[3] >> 5) & 0x07;
tempi = (dsp->espcm_table_index << 8) | (dsp->espcm_last_value << 3) | dsp->espcm_code_buffer[0];
tempi = espcm3_dpcm_tables[tempi];
dsp->espcm_last_value = tempi;
} else {
tempi = (dsp->espcm_table_index << 8) | (dsp->espcm_last_value << 3) | dsp->espcm_code_buffer[(dsp->espcm_sample_idx - 1) % 10];
tempi = espcm3_dpcm_tables[tempi];
dsp->espcm_last_value = tempi;
}
if (dsp->espcm_sample_idx == 18) {
dsp->sb_8_length--;
}
dsp->espcm_sample_idx++;
tempi |= (dsp->espcm_range << 4);
data[0] = (int) (espcm_range_map[tempi]);
dsp->sbdat = (int16_t) (data[0] << 8);
if (dsp->stereo) {
sb_dsp_log("pollsb: ESPCM 3, 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 ESPCM_1:
if (dsp->espcm_sample_idx >= 19)
dsp->espcm_sample_idx = 0;
if (dsp->espcm_sample_idx == 0) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo))
break;
dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
dsp->espcm_range = dsp->espcm_byte_buffer[0] & 0x0F;
dsp->espcm_byte_buffer[0] >>= 5;
tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4;
dsp->espcm_byte_buffer[0] >>= 1;
} else if ((dsp->espcm_sample_idx == 3) | (dsp->espcm_sample_idx == 11)) {
sb_espcm_fifoctl_run(dsp);
if (fifo_get_empty(dsp->espcm_fifo)) {
break;
}
dsp->espcm_byte_buffer[0] = fifo_read(dsp->espcm_fifo);
dsp->sb_8_length--;
tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4;
dsp->espcm_byte_buffer[0] >>= 1;
} else {
tempi = dsp->espcm_byte_buffer[0] & 1 ? 0xC : 0x4;
dsp->espcm_byte_buffer[0] >>= 1;
}
if (dsp->espcm_sample_idx == 18)
dsp->sb_8_length--;
dsp->espcm_sample_idx++;
tempi |= (dsp->espcm_range << 4);
data[0] = (int) espcm_range_map[tempi];
dsp->sbdat = (int16_t) (data[0] << 8);
if (dsp->stereo) {
sb_dsp_log("pollsb: ESPCM 1, 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 && !dsp->ess_playback_mode) {
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_finish_dma(dsp);
}
sb_irq(dsp, 1);
dsp->ess_irq_generic = true;
}
if (dsp->ess_dma_counter > 0xffff) {
if (dsp->ess_playback_mode) {
if (!dsp->sb_8_autoinit) {
dsp->sb_8_enable = 0;
timer_disable(&dsp->output_timer);
sb_finish_dma(dsp);
}
if (ESSreg(0xB1) & 0x40) {
sb_irq(dsp, 1);
dsp->ess_irq_dmactr = true;
}
}
const uint32_t temp = dsp->ess_dma_counter & 0xffff;
dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
dsp->ess_dma_counter += temp;
}
}
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 = (int16_t) ((data[0] & 0xffff) ^ 0x8000);
dsp->sb_16_length--;
dsp->ess_dma_counter += 2;
break;
case 0x10: /* Mono signed */
data[0] = dsp->dma_readw(dsp->dma_priv);
if (data[0] == DMA_NODATA)
break;
dsp->sbdatl = dsp->sbdatr = (int16_t) (data[0] & 0xffff);
dsp->sb_16_length--;
dsp->ess_dma_counter += 2;
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 = (int16_t) ((data[0] & 0xffff) ^ 0x8000);
dsp->sbdatr = (int16_t) ((data[1] & 0xffff) ^ 0x8000);
dsp->sb_16_length -= 2;
dsp->ess_dma_counter += 4;
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 = (int16_t) (data[0] & 0xffff);
dsp->sbdatr = (int16_t) (data[1] & 0xffff);
dsp->sb_16_length -= 2;
dsp->ess_dma_counter += 4;
break;
default:
break;
}
if (dsp->sb_16_length < 0 && !dsp->ess_playback_mode) {
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_finish_dma(dsp);
}
sb_irq(dsp, 0);
dsp->ess_irq_generic = true;
}
if (dsp->ess_dma_counter > 0xffff) {
if (dsp->ess_playback_mode) {
if (!dsp->sb_16_autoinit) {
dsp->sb_16_enable = 0;
timer_disable(&dsp->output_timer);
sb_finish_dma(dsp);
}
if (ESSreg(0xB1) & 0x40) {
sb_irq(dsp, 0);
dsp->ess_irq_dmactr = true;
}
}
const uint32_t temp = dsp->ess_dma_counter & 0xffff;
dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
dsp->ess_dma_counter += temp;
}
}
if (dsp->sb_pausetime > -1) {
dsp->sb_pausetime--;
if (dsp->sb_pausetime < 0) {
sb_irq(dsp, 1);
dsp->ess_irq_generic = true;
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, (uint64_t) 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->ess_dma_counter++;
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->ess_dma_counter++;
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->ess_dma_counter += 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->ess_dma_counter += 2;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
case ESPCM_4E:
/*
I assume the real hardware double-buffers the blocks or something like that.
We're not gonna do that here.
*/
dsp->espcm_sample_buffer[dsp->espcm_sample_idx] = (int8_t) (dsp->record_buffer[dsp->record_pos_read] >> 8);
dsp->espcm_sample_idx++;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
if (dsp->espcm_sample_idx >= 19) {
int i, table_addr;
int8_t min_sample = 127, max_sample = -128, s;
for (i = 0; i < 19; i++) {
s = dsp->espcm_sample_buffer[i];
if (s < min_sample)
min_sample = s;
if (s > max_sample)
max_sample = s;
}
if (min_sample < 0) {
if (min_sample == -128)
min_sample = 127; /* Clip it to make it fit into int8_t. */
else
min_sample = (int8_t) -min_sample;
}
if (max_sample < 0) {
if (max_sample == -128)
max_sample = 127; /* Clip it to make it fit into int8_t. */
else
max_sample = (int8_t) -max_sample;
}
if (min_sample > max_sample)
max_sample = min_sample;
for (table_addr = 15; table_addr < 256; table_addr += 16) {
if (max_sample <= espcm_range_map[table_addr])
break;
}
dsp->espcm_range = table_addr >> 4;
for (i = 0; i < 19; i++) {
int last_sigma = 9999;
table_addr = dsp->espcm_range << 4;
s = dsp->espcm_sample_buffer[i];
for (; (table_addr >> 4) == dsp->espcm_range; table_addr++) {
int sigma = espcm_range_map[table_addr] - s;
if (sigma < 0)
sigma = -sigma;
if (sigma > last_sigma)
break;
last_sigma = sigma;
}
table_addr--;
dsp->espcm_code_buffer[i] = table_addr & 0x0F;
}
uint8_t b = dsp->espcm_range | (dsp->espcm_code_buffer[0] << 4);
dsp->dma_writeb(dsp->dma_priv, b);
dsp->sb_8_length--;
dsp->ess_dma_counter++;
for (i = 1; i < 10; i++) {
b = dsp->espcm_code_buffer[i * 2 - 1] | (dsp->espcm_code_buffer[i * 2] << 4);
dsp->dma_writeb(dsp->dma_priv, b);
dsp->sb_8_length--;
dsp->ess_dma_counter++;
}
dsp->espcm_sample_idx = 0;
}
default:
break;
}
if (dsp->sb_8_length < 0 && !dsp->ess_playback_mode) {
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_finish_dma(dsp);
}
sb_irq(dsp, 1);
dsp->ess_irq_generic = true;
}
if (dsp->ess_dma_counter > 0xffff) {
if (dsp->ess_playback_mode) {
if (!dsp->sb_8_autoinit) {
dsp->sb_8_enable = 0;
timer_disable(&dsp->input_timer);
sb_finish_dma(dsp);
}
if (ESSreg(0xB1) & 0x40) {
sb_irq(dsp, 1);
dsp->ess_irq_dmactr = true;
}
}
uint32_t temp = dsp->ess_dma_counter & 0xffff;
dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
dsp->ess_dma_counter += temp;
}
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->ess_dma_counter += 2;
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->ess_dma_counter += 2;
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->ess_dma_counter += 4;
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->ess_dma_counter += 4;
dsp->record_pos_read += 2;
dsp->record_pos_read &= 0xFFFF;
break;
default:
break;
}
if (dsp->sb_16_length < 0 && !dsp->ess_playback_mode) {
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_finish_dma(dsp);
}
sb_irq(dsp, 0);
dsp->ess_irq_generic = true;
}
if (dsp->ess_dma_counter > 0xffff) {
if (dsp->ess_playback_mode) {
if (!dsp->sb_16_autoinit) {
dsp->sb_16_enable = 0;
timer_disable(&dsp->input_timer);
sb_finish_dma(dsp);
}
if (ESSreg(0xB1) & 0x40) {
sb_irq(dsp, 0);
dsp->ess_irq_dmactr = true;
}
}
uint32_t temp = dsp->ess_dma_counter & 0xffff;
dsp->ess_dma_counter = sb_ess_get_dma_counter(dsp);
dsp->ess_dma_counter += temp;
}
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))
{
//
}