/*Jazz sample rates :
  386-33 - 12kHz
  486-33 - 20kHz
  486-50 - 32kHz
  Pentium - 45kHz*/

#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include "ibm.h"

#include "device.h"
#include "dma.h"
#include "filters.h"
#include "io.h"
#include "pic.h"
#include "sound.h"
#include "sound_azt2316a.h"
#include "sound_sb_dsp.h"
#include "timer.h"
#include "x86.h"

/*The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb*/
#define SB_DSP_REC_SAFEFTY_MARGIN 4096

void pollsb(void *p);
void sb_poll_i(void *p);

//#define SB_DSP_RECORD_DEBUG
//#define SB_TEST_RECORDING_SAW

#ifdef SB_DSP_RECORD_DEBUG
FILE* soundf = 0/*NULL*/;
#endif

#ifdef SB_TEST_RECORDING_SAW
int counttest;
#endif

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, -1, 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, -1, -1, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, 0
	};

char sb16_copyright[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
uint16_t sb_dsp_versions[] = {0, 0, 0x105, 0x200, 0x201, 0x300, 0x302, 0x405, 0x40d};

/*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
};

float low_fir_sb16_coef[SB16_NCoef];

static inline double sinc(double x) {
	return sin(M_PI * x) / (M_PI * x);
}

static void recalc_sb16_filter(int playback_freq) {
	/*Cutoff frequency = playback / 2*/
	float fC = ((float)playback_freq / 2.0) / 48000.0;
	float gain;
	int n;

	for (n = 0; n < SB16_NCoef; n++) {
		/*Blackman window*/
		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*/
		double h = sinc(2.0 * fC * ((double)n - ((double)(SB16_NCoef - 1) / 2.0)));

		/*Create windowed-sinc filter*/
		low_fir_sb16_coef[n] = w * h;
	}

	low_fir_sb16_coef[(SB16_NCoef - 1) / 2] = 1.0;

	gain = 0.0;
	for (n = 0; n < SB16_NCoef; n++)
		gain += low_fir_sb16_coef[n];

	/*Normalise filter, to produce unity gain*/
	for (n = 0; n < SB16_NCoef; n++)
		low_fir_sb16_coef[n] /= gain;
}

void sb_irq(sb_dsp_t *dsp, int irq8) {
//        pclog("IRQ %i %02X\n",irq8,pic.mask);
	if (irq8)
		dsp->sb_irq8 = 1;
	else
		dsp->sb_irq16 = 1;
	picint(1 << dsp->sb_irqnum);
}
void sb_irqc(sb_dsp_t *dsp, int irq8) {
	if (irq8)
		dsp->sb_irq8 = 0;
	else
		dsp->sb_irq16 = 0;
	picintc(1 << dsp->sb_irqnum);
}

void sb_dsp_reset(sb_dsp_t *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;

	sb_irqc(dsp, 0);
	sb_irqc(dsp, 1);
	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 = -1;
	dsp->sbe2 = 0xAA;
	dsp->sbe2count = 0;

	dsp->sbreset = 0;

	dsp->record_pos_read = 0;
	dsp->record_pos_write = SB_DSP_REC_SAFEFTY_MARGIN;

	picintc(1 << dsp->sb_irqnum);

	dsp->asp_data_len = 0;

#ifdef SB_DSP_RECORD_DEBUG
	if (soundf != 0)
	{
	    fclose(soundf);
	    soundf=0;
	}
#endif
}

void sb_doreset(sb_dsp_t *dsp) {
	int c;

	sb_dsp_reset(dsp);

	if (IS_AZTECH(dsp)) {
		sb_commands[8] = 1;
		sb_commands[9] = 1;
	} else {
		if (dsp->sb_type == SB16)
			sb_commands[8] = 1;
		else
			sb_commands[8] = -1;
	}

	for (c = 0; c < 256; c++)
		dsp->sb_asp_regs[c] = 0;
	dsp->sb_asp_regs[5] = 0x01;
	dsp->sb_asp_regs[9] = 0xf8;
}

void sb_dsp_speed_changed(sb_dsp_t *dsp) {
	if (dsp->sb_timeo < 256)
		dsp->sblatcho = TIMER_USEC * (256 - dsp->sb_timeo);
	else
		dsp->sblatcho = (uint64_t)(TIMER_USEC * (1000000.0f / (float)(dsp->sb_timeo - 256)));

	if (dsp->sb_timei < 256)
		dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_timei);
	else
		dsp->sblatchi = (uint64_t)(TIMER_USEC * (1000000.0f / (float)(dsp->sb_timei - 256)));
}

void sb_add_data(sb_dsp_t *dsp, uint8_t v) {
	dsp->sb_read_data[dsp->sb_read_wp++] = v;
	dsp->sb_read_wp &= 0xff;
}

#define ADPCM_4  1
#define ADPCM_26 2
#define ADPCM_2  3

void sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) {
	dsp->sb_pausetime = -1;
	if (dma8) {
		dsp->sb_8_length = len;
		dsp->sb_8_format = format;
		dsp->sb_8_autoinit = autoinit;
		dsp->sb_8_pause = 0;
		dsp->sb_8_enable = 1;
		if (dsp->sb_16_enable && dsp->sb_16_output)
			dsp->sb_16_enable = 0;
		dsp->sb_8_output = 1;
		if (!timer_is_enabled(&dsp->output_timer))
			timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho);
		dsp->sbleftright = 0;
		dsp->sbdacpos = 0;
//                pclog("Start 8-bit DMA addr %06X len %04X\n",dma.ac[1]+(dma.page[1]<<16),len);
	} else {
		dsp->sb_16_length = len;
		dsp->sb_16_format = format;
		dsp->sb_16_autoinit = autoinit;
		dsp->sb_16_pause = 0;
		dsp->sb_16_enable = 1;
		if (dsp->sb_8_enable && dsp->sb_8_output)
			dsp->sb_8_enable = 0;
		dsp->sb_16_output = 1;
		if (!timer_is_enabled(&dsp->output_timer))
			timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho);
//                pclog("Start 16-bit DMA addr %06X len %04X\n",dma16.ac[1]+(dma16.page[1]<<16),len);
	}
}

void sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) {
	if (dma8) {
#ifdef SB_TEST_RECORDING_SAW
		switch (dsp->sb_8_format)
		{
		    case 00:case 20:counttest=0x80;break;
		    case 10:case 30:counttest=0;break;
		}
#endif
		dsp->sb_8_length = len;
		dsp->sb_8_format = format;
		dsp->sb_8_autoinit = autoinit;
		dsp->sb_8_pause = 0;
		dsp->sb_8_enable = 1;
		if (dsp->sb_16_enable && !dsp->sb_16_output)
			dsp->sb_16_enable = 0;
		dsp->sb_8_output = 0;
		if (!timer_is_enabled(&dsp->input_timer))
			timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi);
//                pclog("Start 8-bit input DMA addr %06X len %04X\n",dma.ac[1]+(dma.page[1]<<16),len);
	} else {
#ifdef SB_TEST_RECORDING_SAW
		switch (dsp->sb_16_format)
		{
		    case 00:case 20:counttest=0x8000;break;
		    case 10:case 30:counttest=0;break;
		}
#endif
		dsp->sb_16_length = len;
		dsp->sb_16_format = format;
		dsp->sb_16_autoinit = autoinit;
		dsp->sb_16_pause = 0;
		dsp->sb_16_enable = 1;
		if (dsp->sb_8_enable && !dsp->sb_8_output)
			dsp->sb_8_enable = 0;
		dsp->sb_16_output = 0;
		if (!timer_is_enabled(&dsp->input_timer))
			timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi);
//                pclog("Start 16-bit input DMA addr %06X len %04X\n",dma.ac[1]+(dma.page[1]<<16),len);
	}
	memset(dsp->record_buffer, 0, sizeof(dsp->record_buffer));

#ifdef SB_DSP_RECORD_DEBUG
	if (soundf != 0)
	{
	    fclose(soundf);
	    soundf=0;
	}
#endif

}

int sb_8_read_dma(sb_dsp_t *dsp) {
	return dma_channel_read(dsp->sb_8_dmanum);
}
void sb_8_write_dma(sb_dsp_t *dsp, uint8_t val) {
	dma_channel_write(dsp->sb_8_dmanum, val);
#ifdef SB_DSP_RECORD_DEBUG
	if (!soundf) soundf=fopen("sound_dsp.pcm","wb");
	fwrite(&val,1,1,soundf);
#endif
}
int sb_16_read_dma(sb_dsp_t *dsp) {
	return dma_channel_read(dsp->sb_16_dmanum);
}
int sb_16_write_dma(sb_dsp_t *dsp, uint16_t val) {
	int ret = dma_channel_write(dsp->sb_16_dmanum, val);
#ifdef SB_DSP_RECORD_DEBUG
	if (!soundf) soundf=fopen("sound_dsp.pcm","wb");
	fwrite(&val,2,1,soundf);
#endif
	return (ret == DMA_NODATA);
}

void sb_dsp_setirq(sb_dsp_t *dsp, int irq) {
	dsp->sb_irqnum = irq;
}

void sb_dsp_setdma8(sb_dsp_t *dsp, int dma) {
	dsp->sb_8_dmanum = dma;
}

void sb_dsp_setdma16(sb_dsp_t *dsp, int dma) {
	dsp->sb_16_dmanum = dma;
}
void sb_exec_command(sb_dsp_t *dsp) {
	int temp, c;
//        pclog("sb_exec_command : SB command %02X\n", dsp->sb_command);
	switch (dsp->sb_command) {
	case 0x01: /*???*/
		if (dsp->sb_type < SB16)
			break;
		dsp->asp_data_len = dsp->sb_data[0] + (dsp->sb_data[1] << 8) + 1;
		break;
	case 0x03: /*ASP status*/
		sb_add_data(dsp, 0);
		break;
	case 0x10: /*8-bit direct mode*/
		sb_dsp_update(dsp);
		dsp->sbdat = dsp->sbdatl = dsp->sbdatr = (dsp->sb_data[0] ^ 0x80) << 8;
		break;
	case 0x14: /*8-bit single cycle DMA output*/
		sb_start_dma(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
		break;
	case 0x17: /*2-bit ADPCM output with reference*/
		dsp->sbref = sb_8_read_dma(dsp);
		dsp->sbstep = 0;
//                pclog("Ref byte 2 %02X\n",sbref);
	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 = sb_8_read_dma(dsp);
		dsp->sb_8_length--;
		if (dsp->sb_command == 0x17)
			dsp->sb_8_length--;
		break;
	case 0x1C: /*8-bit autoinit DMA output*/
		if (dsp->sb_type < SB15)
			break;
		sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen);
		break;
	case 0x1F: /*2-bit ADPCM autoinit output*/
		if (dsp->sb_type < SB15)
			break;
		sb_start_dma(dsp, 1, 1, ADPCM_2, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
		dsp->sbdat2 = sb_8_read_dma(dsp);
		dsp->sb_8_length--;
		break;
	case 0x20: /*8-bit direct input*/
		sb_add_data(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80);
		/*Due to the current implementation, I need to emulate a samplerate, even if this
		 * mode does not imply such samplerate. Position is increased in sb_poll_i*/
		if (!timer_is_enabled(&dsp->input_timer)) {
			dsp->sb_timei = 256 - 22;
			dsp->sblatchi = TIMER_USEC * 22;
			temp = 1000000 / 22;
			dsp->sb_freq = temp;
			timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi);
		}
		break;
	case 0x24: /*8-bit single cycle DMA input*/
		sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
		break;
	case 0x2C: /*8-bit autoinit DMA input*/
		if (dsp->sb_type < SB15)
			break;
		sb_start_dma_i(dsp, 1, 1, 0, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
		break;
	case 0x40: /*Set time constant*/
		dsp->sb_timei = dsp->sb_timeo = dsp->sb_data[0];
		dsp->sblatcho = dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_data[0]);
		temp = 256 - dsp->sb_data[0];
		temp = 1000000 / temp;
//                pclog("Sample rate - %ihz (%i)\n",temp, dsp->sblatcho);
		if (dsp->sb_freq != temp && dsp->sb_type >= SB16)
			recalc_sb16_filter(temp);
		dsp->sb_freq = temp;
		break;
	case 0x41: /*Set output sampling rate*/
	case 0x42: /*Set input sampling rate*/
		if (dsp->sb_type < SB16)
			break;
		dsp->sblatcho = (uint64_t)(TIMER_USEC * (1000000.0f / (float)(dsp->sb_data[1] + (dsp->sb_data[0] << 8))));
//                pclog("Sample rate - %ihz (%i)\n",dsp->sb_data[1]+(dsp->sb_data[0]<<8), dsp->sblatcho);
		temp = dsp->sb_freq;
		dsp->sb_freq = dsp->sb_data[1] + (dsp->sb_data[0] << 8);
		dsp->sb_timeo = 256 + dsp->sb_freq;
		dsp->sblatchi = dsp->sblatcho;
		dsp->sb_timei = dsp->sb_timeo;
		if (dsp->sb_freq != temp && dsp->sb_type >= SB16)
			recalc_sb16_filter(dsp->sb_freq);
		break;
	case 0x48: /*Set DSP block transfer size*/
		dsp->sb_8_autolen = dsp->sb_data[0] + (dsp->sb_data[1] << 8);
		break;
	case 0x75: /*4-bit ADPCM output with reference*/
		dsp->sbref = sb_8_read_dma(dsp);
		dsp->sbstep = 0;
//                pclog("Ref byte 4 %02X\n",sbref);
	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 = sb_8_read_dma(dsp);
		dsp->sb_8_length--;
		if (dsp->sb_command == 0x75)
			dsp->sb_8_length--;
		break;
	case 0x77: /*2.6-bit ADPCM output with reference*/
		dsp->sbref = sb_8_read_dma(dsp);
		dsp->sbstep = 0;
//                pclog("Ref byte 26 %02X\n",sbref);
	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 = sb_8_read_dma(dsp);
		dsp->sb_8_length--;
		if (dsp->sb_command == 0x77)
			dsp->sb_8_length--;
		break;
	case 0x7D: /*4-bit ADPCM autoinit output*/
		if (dsp->sb_type < SB15)
			break;
		sb_start_dma(dsp, 1, 1, ADPCM_4, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
		dsp->sbdat2 = sb_8_read_dma(dsp);
		dsp->sb_8_length--;
		break;
	case 0x7F: /*2.6-bit ADPCM autoinit output*/
		if (dsp->sb_type < SB15)
			break;
		sb_start_dma(dsp, 1, 1, ADPCM_26, dsp->sb_data[0] + (dsp->sb_data[1] << 8));
		dsp->sbdat2 = sb_8_read_dma(dsp);
		dsp->sb_8_length--;
		break;
	case 0x80: /*Pause DAC*/
		dsp->sb_pausetime = dsp->sb_data[0] + (dsp->sb_data[1] << 8);
//                pclog("SB pause %04X\n",sb_pausetime);
		if (!timer_is_enabled(&dsp->output_timer))
			timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho);
		break;
	case 0x90: /*High speed 8-bit autoinit DMA output*/
		if (dsp->sb_type < SB2)
			break;
		sb_start_dma(dsp, 1, 1, 0, dsp->sb_8_autolen);
		break;
	case 0x91: /*High speed 8-bit single cycle DMA output*/
		if (dsp->sb_type < SB2)
			break;
		sb_start_dma(dsp, 1, 0, 0, dsp->sb_8_autolen);
		break;
	case 0x98: /*High speed 8-bit autoinit DMA input*/
		if (dsp->sb_type < SB2)
			break;
		sb_start_dma_i(dsp, 1, 1, 0, dsp->sb_8_autolen);
		break;
	case 0x99: /*High speed 8-bit single cycle DMA input*/
		if (dsp->sb_type < SB2)
			break;
		sb_start_dma_i(dsp, 1, 0, 0, dsp->sb_8_autolen);
		break;
	case 0xA0: /*Set input mode to mono*/
	case 0xA8: /*Set input mode to stereo*/
		if (dsp->sb_type < SB2 || dsp->sb_type > SBPRO2)
			break;
		//TODO: Implement. 3.xx-only command.
		break;
	case 0xB0:
	case 0xB1:
	case 0xB2:
	case 0xB3:
	case 0xB4:
	case 0xB5:
	case 0xB6:
	case 0xB7: /*16-bit DMA output*/
		if (dsp->sb_type < SB16)
			break;
		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)
			break;
		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)
			break;
		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)
			break;
		sb_start_dma_i(dsp, 1, dsp->sb_command & 4, dsp->sb_data[0], dsp->sb_data[1] + (dsp->sb_data[2] << 8));
		dsp->sb_8_autolen = dsp->sb_data[1] + (dsp->sb_data[2] << 8);
		break;
	case 0xD0: /*Pause 8-bit DMA*/
		dsp->sb_8_pause = 1;
		break;
	case 0xD1: /*Speaker on*/
		if (dsp->sb_type < SB15)
			dsp->sb_8_pause = 1;
		else if (dsp->sb_type < SB16)
			dsp->muted = 0;
		dsp->sb_speaker = 1;
		break;
	case 0xD3: /*Speaker off*/
		if (dsp->sb_type < SB15)
			dsp->sb_8_pause = 1;
		else if (dsp->sb_type < SB16)
			dsp->muted = 1;
		dsp->sb_speaker = 0;
		break;
	case 0xD4: /*Continue 8-bit DMA*/
		dsp->sb_8_pause = 0;
		break;
	case 0xD5: /*Pause 16-bit DMA*/
		if (dsp->sb_type < SB16)
			break;
		dsp->sb_16_pause = 1;
		break;
	case 0xD6: /*Continue 16-bit DMA*/
		if (dsp->sb_type < SB16)
			break;
		dsp->sb_16_pause = 0;
		break;
	case 0xD8: /*Get speaker status*/
		sb_add_data(dsp, dsp->sb_speaker ? 0xff : 0);
		break;
	case 0xD9: /*Exit 16-bit auto-init mode*/
		if (dsp->sb_type < SB16)
			break;
		dsp->sb_16_autoinit = 0;
		break;
	case 0xDA: /*Exit 8-bit auto-init mode*/
		dsp->sb_8_autoinit = 0;
		break;
	case 0xE0: /*DSP identification*/
		sb_add_data(dsp, ~dsp->sb_data[0]);
		break;
	case 0xE1: /*Get DSP version*/
		if (IS_AZTECH(dsp)) {
			if (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11) {
				sb_add_data(dsp, 0x3);
				sb_add_data(dsp, 0x1);
			} else if (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT1605_0X0C) {
				sb_add_data(dsp, 0x2);
				sb_add_data(dsp, 0x1);
			}
			break;
		}
		sb_add_data(dsp, sb_dsp_versions[dsp->sb_type] >> 8);
		sb_add_data(dsp, sb_dsp_versions[dsp->sb_type] & 0xff);
		break;
	case 0xE2: /*Stupid ID/protection*/
		for (c = 0; c < 8; c++)
			if (dsp->sb_data[0] & (1 << c))
				dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][c];
		dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][8];
		dsp->sbe2count++;
		sb_8_write_dma(dsp, dsp->sbe2);
		break;
	case 0xE3: /*DSP copyright*/
		if (dsp->sb_type < SB16)
			break;
		c = 0;
		while (sb16_copyright[c])
			sb_add_data(dsp, sb16_copyright[c++]);
		sb_add_data(dsp, 0);
		break;
	case 0xE4: /*Write test register*/
		dsp->sb_test = dsp->sb_data[0];
		break;
	case 0xE8: /*Read test register*/
		sb_add_data(dsp, dsp->sb_test);
		break;
	case 0xF2: /*Trigger 8-bit IRQ*/
//                pclog("Trigger IRQ\n");
		sb_irq(dsp, 1);
		break;
	case 0xF3: /*Trigger 16-bit IRQ*/
//                pclog("Trigger IRQ\n");
		sb_irq(dsp, 0);
		break;
	case 0xE7: /*???*/
	case 0xFA: /*???*/
		break;
	case 0x07: /*No, that's not how you program auto-init DMA*/
	case 0xFF:break;
	case 0x08: /*ASP get version*/
		if (IS_AZTECH(dsp)) {
			if ((dsp->sb_data[0] == 0x55 || dsp->sb_data[0] == 0x05) && 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] == 0x55 || dsp->sb_data[0] == 0x05) && 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]);
				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_add_data(dsp, dsp->azt_eeprom[dsp->sb_data[1]]);
				break;
			} else
				pclog("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 < SB16)
			break;
		sb_add_data(dsp, 0x18);
		break;
	case 0x0E: /*ASP set register*/
		if (dsp->sb_type < SB16)
			break;
		dsp->sb_asp_regs[dsp->sb_data[0]] = dsp->sb_data[1];
//                pclog("ASP write reg %02X %02X\n", sb_data[0], sb_data[1]);
		break;
	case 0x0F: /*ASP get register*/
		if (dsp->sb_type < SB16)
			break;
//                sb_add_data(0);
		sb_add_data(dsp, dsp->sb_asp_regs[dsp->sb_data[0]]);
//                pclog("ASP read reg %02X %02X\n", sb_data[0], sb_asp_regs[sb_data[0]]);
		break;
	case 0xF8:
		if (dsp->sb_type >= SB16)
			break;
		sb_add_data(dsp, 0);
		break;
	case 0xF9:
		if (dsp->sb_type < SB16)
			break;
		if (dsp->sb_data[0] == 0x0e)
			sb_add_data(dsp, 0xff);
		else if (dsp->sb_data[0] == 0x0f)
			sb_add_data(dsp, 0x07);
		else if (dsp->sb_data[0] == 0x37)
			sb_add_data(dsp, 0x38);
		else
			sb_add_data(dsp, 0x00);
	case 0x04:
	case 0x05:break;
	case 0x09: /*AZTECH mode set*/
		if (IS_AZTECH(dsp)) {
			if (dsp->sb_data[0] == 0x00) {
				pclog("AZT2316A: WSS MODE!\n");
				azt2316a_enable_wss(1, dsp->parent);
			} else if (dsp->sb_data[0] == 0x01) {
				pclog("AZT2316A: SB8PROV2 MODE!\n");
				azt2316a_enable_wss(0, dsp->parent);
			} else
				pclog("AZT2316A: UNKNOWN MODE!\n"); // sequences 0x02->0xFF, 0x04->0xFF seen
		}
		break;
	case 0x38: /*TODO: AZTECH MIDI-related? */
		break;
//                default:
//                fatal("Exec bad SB command %02X\n",dsp->sb_command);


		/*TODO: Some more data about the DSP registeres
		 * http://the.earth.li/~tfm/oldpage/sb_dsp.html
		 * http://www.synchrondata.com/pheaven/www/area19.htm
		 * http://www.dcee.net/Files/Programm/Sound/
 0E3h           DSP Copyright                                       SBPro2???
 0F0h           Sine Generator                                      SB
 0F1h           DSP Auxiliary Status (Obsolete)                     SB-Pro2
 0F2h           IRQ Request, 8-bit                                  SB
 0F3h           IRQ Request, 16-bit                                 SB16
 0FBh           DSP Status                                          SB16
 0FCh           DSP Auxiliary Status                                SB16
 0FDh           DSP Command Status                                  SB16
		 */

	}
}

void sb_write(uint16_t a, uint8_t v, void *priv) {
	sb_dsp_t *dsp = (sb_dsp_t *)priv;
//        pclog("sb_write : Write soundblaster %04X %02X %04X:%04X %02X\n",a,v,CS,pc,dsp->sb_command);
	switch (a & 0xF) {
	case 6: /*Reset*/
		if (!(v & 1) && (dsp->sbreset & 1)) {
			sb_dsp_reset(dsp);
			sb_add_data(dsp, 0xAA);
		}
		dsp->sbreset = v;
		return;
	case 0xC: /*Command/data write*/
		timer_set_delay_u64(&dsp->wb_timer, TIMER_USEC * 1);
		if (dsp->asp_data_len) {
//                        pclog("ASP data %i\n", dsp->asp_data_len);
			dsp->asp_data_len--;
			if (!dsp->asp_data_len)
				sb_add_data(dsp, 0);
			return;
		}
		if (dsp->sb_data_stat == -1) {
			dsp->sb_command = v;
			if (v == 0x01)
				sb_add_data(dsp, 0);
//                        if (sb_commands[v]==-1)
//                           fatal("Bad SB command %02X\n",v);
			dsp->sb_data_stat++;
		} else {
			dsp->sb_data[dsp->sb_data_stat++] = v;
			if (IS_AZTECH(dsp)) {
				// variable length commands
				if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x08)
					sb_commands[dsp->sb_command] = 3;
				else if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x07)
					sb_commands[dsp->sb_command] = 2;
			}
		}
		if (dsp->sb_data_stat == sb_commands[dsp->sb_command] || sb_commands[dsp->sb_command] == -1) {
			sb_exec_command(dsp);
			dsp->sb_data_stat = -1;
			if (IS_AZTECH(dsp)) {
				// variable length commands
				if (dsp->sb_command == 0x08)
					sb_commands[dsp->sb_command] = 1;
			}
		}
		break;
	}
}

uint8_t sb_read(uint16_t a, void *priv) {
	sb_dsp_t *dsp = (sb_dsp_t *)priv;
//        pclog("sb_read : Read soundblaster %04X %04X:%04X\n",a,CS,pc);
	switch (a & 0xf) {
	case 0xA: /*Read data*/
		dsp->sbreaddat = dsp->sb_read_data[dsp->sb_read_rp];
		if (dsp->sb_read_rp != dsp->sb_read_wp) {
			dsp->sb_read_rp++;
			dsp->sb_read_rp &= 0xFF;
		}
//                pclog("SB read %02X\n",sbreaddat);
		return dsp->sbreaddat;
	case 0xC: /*Write data ready*/
		if (dsp->sb_8_enable || dsp->sb_type >= SB16)
			dsp->busy_count = (dsp->busy_count + 1) & 3;
		else
			dsp->busy_count = 0;
		if (dsp->wb_full || (dsp->busy_count & 2)) {
			dsp->wb_full = timer_is_enabled(&dsp->wb_timer);
//                        pclog("SB read 0x80\n");
			if (IS_AZTECH(dsp))
				return 0x80;
			else
				return 0xFF;
		}
//                pclog("SB read 0x00\n");
		if (IS_AZTECH(dsp))
			return 0x00;
		else
			return 0x7F;
	case 0xE: /*Read data ready*/
		picintc(1 << dsp->sb_irqnum);
		dsp->sb_irq8 = dsp->sb_irq16 = 0;
		// Only bit 7 is defined but aztech diagnostics fail if the others are set. Keep the original behavior to not interfere with what's already working.
		if (IS_AZTECH(dsp)) {
//                        pclog("SB read %02X\n",(dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80);
			return (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80;
		} else {
//                        pclog("SB read %02X\n",(dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7F : 0xFF);
			return (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7F : 0xFF;
		}
	case 0xF: /*16-bit ack*/
		dsp->sb_irq16 = 0;
		if (!dsp->sb_irq8)
			picintc(1 << dsp->sb_irqnum);
		return 0xff;
	}
	return 0;
}

static void sb_wb_clear(void *p) {
}

void sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) {
	dsp->sb_type = type;
	dsp->sb_subtype = subtype;
	dsp->parent = parent;

	// Default values. Use sb_dsp_setxxx() methods to change.
	dsp->sb_irqnum = 7;
	dsp->sb_8_dmanum = 1;
	dsp->sb_16_dmanum = 5;

	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, sb_wb_clear, 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(3200 * 2);
}

void sb_dsp_setaddr(sb_dsp_t *dsp, uint16_t addr) {
//        pclog("sb_dsp_setaddr : %04X\n", addr);
	io_removehandler(dsp->sb_addr + 6, 0x0002, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp);
	io_removehandler(dsp->sb_addr + 0xa, 0x0006, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp);
	dsp->sb_addr = addr;
	if (dsp->sb_addr != 0) {
		io_sethandler(dsp->sb_addr + 6, 0x0002, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp);
		io_sethandler(dsp->sb_addr + 0xa, 0x0006, sb_read, NULL, NULL, sb_write, NULL, NULL, dsp);
	}
}

void sb_dsp_set_stereo(sb_dsp_t *dsp, int stereo) {
	dsp->stereo = stereo;
}

void pollsb(void *p) {
	sb_dsp_t *dsp = (sb_dsp_t *)p;
	int tempi, ref;

	timer_advance_u64(&dsp->output_timer, dsp->sblatcho);

//        pclog("PollSB %i %i %i %i\n",sb_8_enable,sb_8_pause,sb_pausetime,sb_8_output);
	if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && dsp->sb_8_output) {
		int data[2];

		sb_dsp_update(dsp);
//                pclog("Dopoll %i %02X %i\n", sb_8_length, sb_8_format, sblatcho);
		switch (dsp->sb_8_format) {
		case 0x00: /*Mono unsigned*/
			data[0] = sb_8_read_dma(dsp);
			/*Needed to prevent clicking in Worms, which programs the DSP to
			  auto-init DMA but programs the DMA controller to single cycle*/
			if (data[0] == DMA_NODATA)
				break;
			dsp->sbdat = (data[0] ^ 0x80) << 8;
			if (dsp->sb_type >= SBPRO && dsp->sb_type < SB16 && dsp->stereo) {
				if (dsp->sbleftright)
					dsp->sbdatl = dsp->sbdat;
				else
					dsp->sbdatr = dsp->sbdat;
				dsp->sbleftright = !dsp->sbleftright;
			} else
				dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
			dsp->sb_8_length--;
			break;
		case 0x10: /*Mono signed*/
			data[0] = sb_8_read_dma(dsp);
			if (data[0] == DMA_NODATA)
				break;
			dsp->sbdat = data[0] << 8;
			if (dsp->sb_type >= SBPRO && dsp->sb_type < SB16 && dsp->stereo) {
				if (dsp->sbleftright)
					dsp->sbdatl = dsp->sbdat;
				else
					dsp->sbdatr = dsp->sbdat;
				dsp->sbleftright = !dsp->sbleftright;
			} else
				dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
			dsp->sb_8_length--;
			break;
		case 0x20: /*Stereo unsigned*/
			data[0] = sb_8_read_dma(dsp);
			data[1] = sb_8_read_dma(dsp);
			if (data[0] == DMA_NODATA || data[1] == DMA_NODATA)
				break;
			dsp->sbdatl = (data[0] ^ 0x80) << 8;
			dsp->sbdatr = (data[1] ^ 0x80) << 8;
			dsp->sb_8_length -= 2;
			break;
		case 0x30: /*Stereo signed*/
			data[0] = sb_8_read_dma(dsp);
			data[1] = sb_8_read_dma(dsp);
			if (data[0] == DMA_NODATA || data[1] == DMA_NODATA)
				break;
			dsp->sbdatl = data[0] << 8;
			dsp->sbdatr = data[1] << 8;
			dsp->sb_8_length -= 2;
			break;

		case ADPCM_4:
			if (dsp->sbdacpos)
				tempi = (dsp->sbdat2 & 0xF) + dsp->sbstep;
			else
				tempi = (dsp->sbdat2 >> 4) + dsp->sbstep;
			if (tempi < 0)
				tempi = 0;
			if (tempi > 63)
				tempi = 63;

			ref = dsp->sbref + scaleMap4[tempi];
			if (ref > 0xff)
				dsp->sbref = 0xff;
			else if (ref < 0x00)
				dsp->sbref = 0x00;
			else
				dsp->sbref = ref;

			dsp->sbstep = (dsp->sbstep + adjustMap4[tempi]) & 0xff;

			dsp->sbdat = (dsp->sbref ^ 0x80) << 8;

			dsp->sbdacpos++;
			if (dsp->sbdacpos >= 2) {
				dsp->sbdacpos = 0;
				dsp->sbdat2 = sb_8_read_dma(dsp);
				dsp->sb_8_length--;
			}

			if (dsp->sb_type >= SBPRO && dsp->sb_type < SB16 && dsp->stereo) {
				if (dsp->sbleftright)
					dsp->sbdatl = dsp->sbdat;
				else
					dsp->sbdatr = dsp->sbdat;
				dsp->sbleftright = !dsp->sbleftright;
			} else
				dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
			break;

		case ADPCM_26:
			if (!dsp->sbdacpos)
				tempi = (dsp->sbdat2 >> 5) + dsp->sbstep;
			else if (dsp->sbdacpos == 1)
				tempi = ((dsp->sbdat2 >> 2) & 7) + dsp->sbstep;
			else
				tempi = ((dsp->sbdat2 << 1) & 7) + dsp->sbstep;

			if (tempi < 0)
				tempi = 0;
			if (tempi > 39)
				tempi = 39;

			ref = dsp->sbref + scaleMap26[tempi];
			if (ref > 0xff)
				dsp->sbref = 0xff;
			else if (ref < 0x00)
				dsp->sbref = 0x00;
			else
				dsp->sbref = ref;
			dsp->sbstep = (dsp->sbstep + adjustMap26[tempi]) & 0xff;

			dsp->sbdat = (dsp->sbref ^ 0x80) << 8;

			dsp->sbdacpos++;
			if (dsp->sbdacpos >= 3) {
				dsp->sbdacpos = 0;
				dsp->sbdat2 = sb_8_read_dma(dsp);
				dsp->sb_8_length--;
			}

			if (dsp->sb_type >= SBPRO && dsp->sb_type < SB16 && dsp->stereo) {
				if (dsp->sbleftright)
					dsp->sbdatl = dsp->sbdat;
				else
					dsp->sbdatr = dsp->sbdat;
				dsp->sbleftright = !dsp->sbleftright;
			} else
				dsp->sbdatl = dsp->sbdatr = dsp->sbdat;
			break;

		case ADPCM_2:tempi = ((dsp->sbdat2 >> ((3 - dsp->sbdacpos) * 2)) & 3) + dsp->sbstep;
			if (tempi < 0)
				tempi = 0;
			if (tempi > 23)
				tempi = 23;

			ref = dsp->sbref + scaleMap2[tempi];
			if (ref > 0xff)
				dsp->sbref = 0xff;
			else if (ref < 0x00)
				dsp->sbref = 0x00;
			else
				dsp->sbref = ref;
			dsp->sbstep = (dsp->sbstep + adjustMap2[tempi]) & 0xff;

			dsp->sbdat = (dsp->sbref ^ 0x80) << 8;

			dsp->sbdacpos++;
			if (dsp->sbdacpos >= 4) {
				dsp->sbdacpos = 0;
				dsp->sbdat2 = sb_8_read_dma(dsp);
			}

			if (dsp->sb_type >= SBPRO && dsp->sb_type < SB16 && dsp->stereo) {
				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:
			//fatal("Unrecognised SB 8-bit format %02X\n",sb_8_format);
		}

		if (dsp->sb_8_length < 0) {
			if (dsp->sb_8_autoinit)
				dsp->sb_8_length = dsp->sb_8_autolen;
			else {
				dsp->sb_8_enable = 0;
				timer_disable(&dsp->output_timer);
			}
			sb_irq(dsp, 1);
		}
	}
	if (dsp->sb_16_enable && !dsp->sb_16_pause && dsp->sb_pausetime < 0 && dsp->sb_16_output) {
		int data[2];

		sb_dsp_update(dsp);

		switch (dsp->sb_16_format) {
		case 0x00: /*Mono unsigned*/
			data[0] = sb_16_read_dma(dsp);
			if (data[0] == DMA_NODATA)
				break;
			dsp->sbdatl = dsp->sbdatr = data[0] ^ 0x8000;
			dsp->sb_16_length--;
			break;
		case 0x10: /*Mono signed*/
			data[0] = sb_16_read_dma(dsp);
			if (data[0] == DMA_NODATA)
				break;
			dsp->sbdatl = dsp->sbdatr = data[0];
			dsp->sb_16_length--;
			break;
		case 0x20: /*Stereo unsigned*/
			data[0] = sb_16_read_dma(dsp);
			data[1] = sb_16_read_dma(dsp);
			if (data[0] == DMA_NODATA || data[1] == DMA_NODATA)
				break;
			dsp->sbdatl = data[0] ^ 0x8000;
			dsp->sbdatr = data[1] ^ 0x8000;
			dsp->sb_16_length -= 2;
			break;
		case 0x30: /*Stereo signed*/
			data[0] = sb_16_read_dma(dsp);
			data[1] = sb_16_read_dma(dsp);
			if (data[0] == DMA_NODATA || data[1] == DMA_NODATA)
				break;
			dsp->sbdatl = data[0];
			dsp->sbdatr = data[1];
			dsp->sb_16_length -= 2;
			break;
//                        default:
//                                fatal("Unrecognised SB 16-bit format %02X\n",sb_16_format);
		}

		if (dsp->sb_16_length < 0) {
//                        pclog("16DMA over %i\n",dsp->sb_16_autoinit);
			if (dsp->sb_16_autoinit)
				dsp->sb_16_length = dsp->sb_16_autolen;
			else {
				dsp->sb_16_enable = 0;
				timer_disable(&dsp->output_timer);
			}
			sb_irq(dsp, 0);
		}
	}
	if (dsp->sb_pausetime > -1) {
		dsp->sb_pausetime--;
		if (dsp->sb_pausetime < 0) {
			sb_irq(dsp, 1);
			if (!dsp->sb_8_enable)
				timer_disable(&dsp->output_timer);
//                        pclog("SB pause over\n");
		}
	}
}

void sb_poll_i(void *p) {
	sb_dsp_t *dsp = (sb_dsp_t *)p;
	int processed = 0;

	timer_advance_u64(&dsp->input_timer, dsp->sblatchi);

//        pclog("PollSBi %i %i %i %i\n",sb_8_enable,sb_8_pause,sb_pausetime,sb_8_output);        
	if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output) {
		switch (dsp->sb_8_format) {
#ifdef SB_TEST_RECORDING_SAW
			case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/
			case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/
			sb_8_write_dma(dsp, counttest);
			counttest+=0x10;
			counttest&=0xFF;
			dsp->sb_8_length--;
			break;
			case 0x20: /*Unsigned stereo*/
			case 0x30: /*Signed stereo*/
			sb_8_write_dma(dsp, counttest);
			sb_8_write_dma(dsp, counttest);
			counttest+=0x10;
			counttest&=0xFF;
			dsp->sb_8_length -= 2;
			break;
#else
		case 0x00: /*Mono unsigned As the manual says, only the left channel is recorded*/
			sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80);
			dsp->sb_8_length--;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
		case 0x10: /*Mono signed As the manual says, only the left channel is recorded*/
			sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8));
			dsp->sb_8_length--;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
		case 0x20: /*Stereo unsigned*/
			sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8) ^ 0x80);
			sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8) ^ 0x80);
			dsp->sb_8_length -= 2;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
		case 0x30: /*Stereo signed*/
			sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read] >> 8));
			sb_8_write_dma(dsp, (dsp->record_buffer[dsp->record_pos_read + 1] >> 8));
			dsp->sb_8_length -= 2;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
#endif
//                        default:
//                                fatal("Unrecognised SB 8-bit input format %02X\n",sb_8_format);
		}

		if (dsp->sb_8_length < 0) {
//                        pclog("Input DMA over %i\n",sb_8_autoinit);
			if (dsp->sb_8_autoinit)
				dsp->sb_8_length = dsp->sb_8_autolen;
			else {
				dsp->sb_8_enable = 0;
				timer_disable(&dsp->input_timer);
			}
			sb_irq(dsp, 1);
		}
		processed = 1;
	}
	if (dsp->sb_16_enable && !dsp->sb_16_pause && dsp->sb_pausetime < 0 && !dsp->sb_16_output) {
		switch (dsp->sb_16_format) {
#ifdef SB_TEST_RECORDING_SAW
			case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/
			case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/
			if (sb_16_write_dma(dsp, counttest))
				return;
			counttest+=0x1000;
			counttest&=0xFFFF;
			dsp->sb_16_length--;
			break;
			case 0x20: /*Unsigned stereo*/
			case 0x30: /*Signed stereo*/
			if (sb_16_write_dma(dsp, counttest))
				return;
			sb_16_write_dma(dsp, counttest);
			counttest+=0x1000;
			counttest&=0xFFFF;
			dsp->sb_16_length -= 2;
			break;
#else
		case 0x00: /*Unsigned mono. As the manual says, only the left channel is recorded*/
			if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000))
				return;
			dsp->sb_16_length--;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
		case 0x10: /*Signed mono. As the manual says, only the left channel is recorded*/
			if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]))
				return;
			dsp->sb_16_length--;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
		case 0x20: /*Unsigned stereo*/
			if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read] ^ 0x8000))
				return;
			sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read + 1] ^ 0x8000);
			dsp->sb_16_length -= 2;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
		case 0x30: /*Signed stereo*/
			if (sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read]))
				return;
			sb_16_write_dma(dsp, dsp->record_buffer[dsp->record_pos_read + 1]);
			dsp->sb_16_length -= 2;
			dsp->record_pos_read += 2;
			dsp->record_pos_read &= 0xFFFF;
			break;
#endif
//                        default:
//                                fatal("Unrecognised SB 16-bit input format %02X\n",sb_16_format);
		}

		if (dsp->sb_16_length < 0) {
//                        pclog("16iDMA over %i\n",sb_16_autoinit);
			if (dsp->sb_16_autoinit)
				dsp->sb_16_length = dsp->sb_16_autolen;
			else {
				dsp->sb_16_enable = 0;
				timer_disable(&dsp->input_timer);
			}
			sb_irq(dsp, 0);
		}
		processed = 1;
	}
	//Assume this is direct mode
	if (!processed) {
		dsp->record_pos_read += 2;
		dsp->record_pos_read &= 0xFFFF;
	}
}

void sb_dsp_update(sb_dsp_t *dsp) {
	if (dsp->muted) {
		dsp->sbdatl = 0;
		dsp->sbdatr = 0;
	}
	for (; dsp->pos < sound_pos_global; dsp->pos++) {
		dsp->buffer[dsp->pos * 2] = dsp->sbdatl;
		dsp->buffer[dsp->pos * 2 + 1] = dsp->sbdatr;
	}
}
void sb_dsp_close(sb_dsp_t *dsp) {
#ifdef SB_DSP_RECORD_DEBUG
	if (soundf != 0)
	{
	    fclose(soundf);
	    soundf=0;
	}
#endif
}

void sb_dsp_add_status_info(char *s, int max_len, sb_dsp_t *dsp) {
	char temps[128];
	int freq;

	if (dsp->sb_timeo < 256)
		freq = 1000000 / (256 - dsp->sb_timeo);
	else
		freq = dsp->sb_timeo - 256;

	if (dsp->sb_8_enable && dsp->sb_8_output) {
		switch (dsp->sb_8_format) {
		case 0x00: /*Mono unsigned*/
		case 0x10: /*Mono signed*/
			if (dsp->sb_type >= SBPRO && dsp->sb_type < SB16 && dsp->stereo) {
				strcpy(temps, "SB playback format : 8-bit stereo\n");
				freq /= 2;
			} else
				strcpy(temps, "SB playback format : 8-bit mono\n");
			break;
		case 0x20: /*Stereo unsigned*/
		case 0x30: /*Stereo signed*/
			strcpy(temps, "SB playback format : 8-bit stereo\n");
			break;
		case ADPCM_4:strcpy(temps, "SB playback format : 4-bit ADPCM\n");
			break;
		case ADPCM_26:strcpy(temps, "SB playback format : 2.6-bit ADPCM\n");
			break;
		case ADPCM_2:strcpy(temps, "SB playback format : 2-bit ADPCM\n");
			break;
		}
	} else if (dsp->sb_16_enable && dsp->sb_16_output) {
		switch (dsp->sb_16_format) {
		case 0x00: /*Mono unsigned*/
		case 0x10: /*Mono signed*/
			strcpy(temps, "SB playback format : 16-bit mono\n");
			break;
		case 0x20: /*Stereo unsigned*/
		case 0x30: /*Stereo signed*/
			strcpy(temps, "SB playback format : 16-bit stereo\n");
			break;
		}
	} else
		strcpy(temps, "SB playback stopped\n");
	strncat(s, temps, max_len);

	if ((dsp->sb_8_enable && dsp->sb_8_output) || (dsp->sb_16_enable && dsp->sb_16_output)) {
		sprintf(temps, "SB playback frequency : %iHz\n", freq);
		strncat(s, temps, max_len);
	}
	strncat(s, "\n", max_len);
}
