1. Correct ChipChat mixer port length as well as making mixer reg 0x3a read back its value. 2. Swap the sb port addresses back so that they match the Piper adf. This fixes SoundPiper 16/32 detection. 3. Log cleanups and additions for better debugging.
3066 lines
117 KiB
C
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) && (dsp->sb_type < SB16_DSP_404)) // 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) && (dsp->sb_type < SB16_DSP_404)) // 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) && (dsp->sb_type < SB16_DSP_404)) // 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) && (dsp->sb_type < SB16_DSP_404)) // 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))
|
|
{
|
|
//
|
|
}
|