#include <stdlib.h>
#include "ibm.h"

#include "device.h"
#include "dma.h"
#include "filters.h"
#include "io.h"
#include "pic.h"
#include "pit.h"
#include "sound.h"
#include "sound_opl.h"
#include "sound_pas16.h"
#include "sound_sb_dsp.h"
#include "timer.h"
#include "x86.h"

/*      Original PAS uses
                2 x OPL2
                PIT - sample rate/count
                LMC835N/LMC1982 - mixer
                YM3802 - MIDI Control System
                

        9A01 - IO base
                base >> 2
                
        All below + IO base

        B89 - interrupt status / clear
                bit 2 - sample rate
                bit 3 - PCM
                bit 4 - MIDI
        
        B88 - Audio mixer control register
        
        B8A - Audio filter control
                bit 5 - mute?
        
        B8B - interrupt mask / board ID
                bits 5-7 - board ID (read only on PAS16)

        F88 - PCM data (low)
        
        F89 - PCM data (high)
        
        F8A - PCM control?
                bit 4 - input/output select (1 = output)
                bit 5 - mono/stereo select
                bit 6 - PCM enable
                
        1388-138b - PIT clocked at 1193180 Hz
                1388 - sample rate
                1389 - sample count
                
        178b - 
        2789 - board revision
        
        8389 -
                bit 2 - 8/16 bit
                
        BF88 - wait states
        
        EF8B -
                bit 3 - 16 bits okay ?
                 
        F388 - 
                bit 6 - joystick enable
        
        F389 -
                bits 0-2 - DMA
                
        F38A -
                bits 0-3 - IRQ

        F788 -
                bit 1 - SB emulation
                bit 0 - MPU401 emulation
                
        F789 - SB base addr
                bits 0-3 - addr bits 4-7
        
        FB8A - SB IRQ/DMA
                bits 3-5 - IRQ
                bits 6-7 - DMA
        
        FF88 - board model
                3 = PAS16
*/

typedef struct pas16_t {
	uint16_t base;

	int irq, dma;

	uint8_t audiofilt;

	uint8_t audio_mixer;

	uint8_t compat, compat_base;

	uint8_t enhancedscsi;

	uint8_t io_conf_1, io_conf_2, io_conf_3, io_conf_4;

	uint8_t irq_stat, irq_ena;

	uint8_t pcm_ctrl;
	uint16_t pcm_dat;

	uint16_t pcm_dat_l, pcm_dat_r;

	uint8_t sb_irqdma;

	int stereo_lr;

	uint8_t sys_conf_1, sys_conf_2, sys_conf_3, sys_conf_4;

	struct {
		uint32_t l[3];
		int c[3];
		pc_timer_t timer[3];
		uint8_t m[3];
		uint8_t ctrl, ctrls[2];
		int wp, rm[3], wm[3];
		uint16_t rl[3];
		int thit[3];
		int delay[3];
		int rereadlatch[3];
		int enable[3];
	} pit;

	opl_t opl;
	sb_dsp_t dsp;

	int16_t pcm_buffer[2][MAXSOUNDBUFLEN];

	int pos;
} pas16_t;

static uint8_t pas16_pit_in(uint16_t port, void *priv);
static void pas16_pit_out(uint16_t port, uint8_t val, void *priv);
static void pas16_update(pas16_t *pas16);

static int pas16_dmas[8] = {4, 1, 2, 3, 0, 5, 6, 7};
static int pas16_irqs[16] = {0, 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, 15, 0, 0, 0, 0};
static int pas16_sb_irqs[8] = {0, 2, 3, 5, 7, 10, 11, 12};
static int pas16_sb_dmas[8] = {0, 1, 2, 3};

enum {
	PAS16_INT_SAMP = 0x04,
	PAS16_INT_PCM = 0x08,
};

enum {
	PAS16_PCM_MONO = 0x20,
	PAS16_PCM_ENA = 0x40
};

enum {
	PAS16_SC2_16BIT = 0x04,
	PAS16_SC2_MSBINV = 0x10
};

enum {
	PAS16_FILT_MUTE = 0x20
};

static uint8_t pas16_in(uint16_t port, void *p) {
	pas16_t *pas16 = (pas16_t *)p;
	uint8_t temp = 0xff;
/*        if (CS == 0xCA53 && pc == 0x3AFC)
                fatal("here");*/
	switch ((port - pas16->base) + 0x388) {
	case 0x388:
	case 0x389:
	case 0x38a:
	case 0x38b:temp = opl3_read((port - pas16->base) + 0x388, &pas16->opl);
		break;

	case 0xb88:temp = pas16->audio_mixer;
		break;

	case 0xb89:temp = pas16->irq_stat;
		break;

	case 0xb8a:temp = pas16->audiofilt;
		break;

	case 0xb8b:temp = (pas16->irq_ena & ~0xe0) | 0x20;
		break;

	case 0xf8a:temp = pas16->pcm_ctrl;
		break;

	case 0x1388:
	case 0x1389:
	case 0x138a:
	case 0x138b:temp = pas16_pit_in(port, pas16);
		break;

	case 0x2789: /*Board revision*/
		temp = 0;
		break;

	case 0x7f89:temp = pas16->enhancedscsi & ~1;
		break;

	case 0x8388:temp = pas16->sys_conf_1;
		break;
	case 0x8389:temp = pas16->sys_conf_2;
		break;
	case 0x838b:temp = pas16->sys_conf_3;
		break;
	case 0x838c:temp = pas16->sys_conf_4;
		break;

	case 0xef8b:temp = 0x0c;
		break;

	case 0xf388:temp = pas16->io_conf_1;
		break;
	case 0xf389:temp = pas16->io_conf_2;
		break;
	case 0xf38b:temp = pas16->io_conf_3;
		break;
	case 0xf38c:temp = pas16->io_conf_4;
		break;

	case 0xf788:temp = pas16->compat;
		break;
	case 0xf789:temp = pas16->compat_base;
		break;

	case 0xfb8a:temp = pas16->sb_irqdma;
		break;

	case 0xff88: /*Board model*/
		temp = 4; /*PAS16*/
		break;
	case 0xff8b: /*Master mode read*/
		temp = 0x20 | 0x10 | 0x01; /*AT bus, XT/AT timing*/
		break;
	}
/*        if (port != 0x388 && port != 0x389 && port != 0xb8b) */pclog("pas16_in : port %04X return %02X  %04X:%04X\n", port, temp, CS, cpu_state.pc);
/*        if (CS == 0x1FF4 && pc == 0x0585)
        {
                if (output)
                        fatal("here");
                output = 3;
        }*/
	return temp;
}

static void pas16_out(uint16_t port, uint8_t val, void *p) {
	pas16_t *pas16 = (pas16_t *)p;
/*        if (port != 0x388 && port != 0x389) */pclog("pas16_out : port %04X val %02X  %04X:%04X\n", port, val, CS, cpu_state.pc);
/*        if (CS == 0x369 && pc == 0x2AC5)
                fatal("here\n");*/
	switch ((port - pas16->base) + 0x388) {
	case 0x388:
	case 0x389:
	case 0x38a:
	case 0x38b:opl3_write((port - pas16->base) + 0x388, val, &pas16->opl);
		break;

	case 0xb88:pas16->audio_mixer = val;
		break;

	case 0xb89:pas16->irq_stat &= ~val;
//                pas16_update_irqs();
		break;

	case 0xb8a:pas16_update(pas16);
		pas16->audiofilt = val;
		break;

	case 0xb8b:pas16->irq_ena = val;
//                pas16_update_irqs();
		break;

	case 0xf88:pas16_update(pas16);
		pas16->pcm_dat = (pas16->pcm_dat & 0xff00) | val;
		break;
	case 0xf89:pas16_update(pas16);
		pas16->pcm_dat = (pas16->pcm_dat & 0x00ff) | (val << 8);
		break;
	case 0xf8a:
		if ((val & PAS16_PCM_ENA) && !(pas16->pcm_ctrl & PAS16_PCM_ENA)) /*Guess*/
			pas16->stereo_lr = 0;
		pas16->pcm_ctrl = val;
		break;

	case 0x1388:
	case 0x1389:
	case 0x138a:
	case 0x138b:pas16_pit_out(port, val, pas16);
		break;

	case 0x7f89:pas16->enhancedscsi = val;
		break;

	case 0x8388:pas16->sys_conf_1 = val;
		break;
	case 0x8389:pas16->sys_conf_2 = val;
		break;
	case 0x838a:pas16->sys_conf_3 = val;
		break;
	case 0x838b:pas16->sys_conf_4 = val;
		break;

	case 0xf388:pas16->io_conf_1 = val;
		break;
	case 0xf389:pas16->io_conf_2 = val;
		pas16->dma = pas16_dmas[val & 0x7];
		pclog("pas16_out : set PAS DMA %i\n", pas16->dma);
		break;
	case 0xf38a:pas16->io_conf_3 = val;
		pas16->irq = pas16_irqs[val & 0xf];
		pclog("pas16_out : set PAS IRQ %i\n", pas16->irq);
		break;
	case 0xf38b:pas16->io_conf_4 = val;
		break;

	case 0xf788:pas16->compat = val;
		if (pas16->compat & 0x02)
			sb_dsp_setaddr(&pas16->dsp, ((pas16->compat_base & 0xf) << 4) | 0x200);
		else
			sb_dsp_setaddr(&pas16->dsp, 0);
		break;
	case 0xf789:pas16->compat_base = val;
		if (pas16->compat & 0x02)
			sb_dsp_setaddr(&pas16->dsp, ((pas16->compat_base & 0xf) << 4) | 0x200);
		break;

	case 0xfb8a:pas16->sb_irqdma = val;
		sb_dsp_setirq(&pas16->dsp, pas16_sb_irqs[(val >> 3) & 7]);
		sb_dsp_setdma8(&pas16->dsp, pas16_sb_dmas[(val >> 6) & 3]);
		pclog("pas16_out : set SB IRQ %i DMA %i\n", pas16_sb_irqs[(val >> 3) & 7], pas16_sb_dmas[(val >> 6) & 3]);
		break;

	default:pclog("pas16_out : unknown %04X\n", port);
	}
	if (cpu_state.pc == 0x80048CF3) {
		if (output)
			fatal("here\n");
		output = 3;
	}
/*        if (CS == 0x1FF4 && pc == 0x0431)
                output = 3;*/
}

static void pas16_pit_out(uint16_t port, uint8_t val, void *p) {
	pas16_t *pas16 = (pas16_t *)p;
	int t;
	switch (port & 3) {
	case 3: /*CTRL*/
		if ((val & 0xC0) == 0xC0) {
			if (!(val & 0x20)) {
				if (val & 2)
					pas16->pit.rl[0] = timer_get_remaining_u64(&pit.timer[0]) / PITCONST;;
				if (val & 4)
					pas16->pit.rl[1] = pas16->pit.c[1];
				if (val & 8)
					pas16->pit.rl[2] = pas16->pit.c[2];
			}
			return;
		}
		t = val >> 6;
		pas16->pit.ctrls[t] = pas16->pit.ctrl = val;
		if (t == 3) {
			printf("Bad PIT reg select\n");
			return;
		}
		if (!(pas16->pit.ctrl & 0x30)) {
			if (!t)
				pas16->pit.rl[t] = timer_get_remaining_u64(&pit.timer[t]) / PITCONST;
			else {
				pas16->pit.rl[t] = pas16->pit.c[t];
				if (pas16->pit.c[t] < 0)
					pas16->pit.rl[t] = 0;
			}
			pas16->pit.ctrl |= 0x30;
			pas16->pit.rereadlatch[t] = 0;
			pas16->pit.rm[t] = 3;
		} else {
			pas16->pit.rm[t] = pas16->pit.wm[t] = (pas16->pit.ctrl >> 4) & 3;
			pas16->pit.m[t] = (val >> 1) & 7;
			if (pas16->pit.m[t] > 5)
				pas16->pit.m[t] &= 3;
			if (!pas16->pit.rm[t]) {
				pas16->pit.rm[t] = 3;
				if (!t)
					pas16->pit.rl[t] = timer_get_remaining_u64(&pit.timer[t]) / PITCONST;
				else
					pas16->pit.rl[t] = pas16->pit.c[t];
			}
			pas16->pit.rereadlatch[t] = 1;
		}
		pas16->pit.wp = 0;
		pas16->pit.thit[t] = 0;
		break;
	case 0:
	case 1:
	case 2: /*Timers*/
		t = port & 3;
		switch (pas16->pit.wm[t]) {
		case 1:pas16->pit.l[t] = val;
			pas16->pit.thit[t] = 0;
			pas16->pit.c[t] = pas16->pit.l[t];
			if (!t)
				timer_set_delay_u64(&pas16->pit.timer[t], pas16->pit.c[t] * PITCONST);
			pas16->pit.enable[t] = 1;
			break;
		case 2:pas16->pit.l[t] = val << 8;
			pas16->pit.thit[t] = 0;
			pas16->pit.c[t] = pas16->pit.l[t];
			if (!t)
				timer_set_delay_u64(&pas16->pit.timer[t], pas16->pit.c[t] * PITCONST);
			pas16->pit.enable[t] = 1;
			break;
		case 0:pas16->pit.l[t] &= 0xFF;
			pas16->pit.l[t] |= (val << 8);
			pas16->pit.c[t] = pas16->pit.l[t];
			if (!t)
				timer_set_delay_u64(&pas16->pit.timer[t], pas16->pit.c[t] * PITCONST);
			pas16->pit.thit[t] = 0;
			pas16->pit.wm[t] = 3;
			pas16->pit.enable[t] = 1;
			break;
		case 3:pas16->pit.l[t] &= 0xFF00;
			pas16->pit.l[t] |= val;
			pas16->pit.wm[t] = 0;
			break;
		}
		if (!pas16->pit.l[t]) {
			pas16->pit.l[t] |= 0x10000;
			pas16->pit.c[t] = pas16->pit.l[t];
			if (!t)
				timer_set_delay_u64(&pas16->pit.timer[t], pas16->pit.c[t] * PITCONST);
		}
		break;
	}
}

static uint8_t pas16_pit_in(uint16_t port, void *p) {
	pas16_t *pas16 = (pas16_t *)p;
	uint8_t temp = 0xff;
	int t = port & 3;
//        printf("Read PIT %04X ",addr);
	switch (port & 3) {
	case 0:
	case 1:
	case 2: /*Timers*/
		if (pas16->pit.rereadlatch[t]) {
			pas16->pit.rereadlatch[t] = 0;
			if (!t) {
				pas16->pit.rl[t] = timer_get_remaining_u64(&pit.timer[t]) / PITCONST;
				if ((timer_get_remaining_u64(&pit.timer[t]) / PITCONST) > 65536)
					pas16->pit.rl[t] = 0xFFFF;
			} else {
				pas16->pit.rl[t] = pas16->pit.c[t];
				if (pas16->pit.c[t] > 65536)
					pas16->pit.rl[t] = 0xFFFF;
			}
		}
		switch (pas16->pit.rm[t]) {
		case 0:temp = pas16->pit.rl[t] >> 8;
			pas16->pit.rm[t] = 3;
			pas16->pit.rereadlatch[t] = 1;
			break;
		case 1:temp = (pas16->pit.rl[t]) & 0xFF;
			pas16->pit.rereadlatch[t] = 1;
			break;
		case 2:temp = (pas16->pit.rl[t]) >> 8;
			pas16->pit.rereadlatch[t] = 1;
			break;
		case 3:temp = (pas16->pit.rl[t]) & 0xFF;
			if (pas16->pit.m[t] & 0x80)
				pas16->pit.m[t] &= 7;
			else
				pas16->pit.rm[t] = 0;
			break;
		}
		break;
	case 3: /*Control*/
		temp = pas16->pit.ctrl;
		break;
	}
//        printf("%02X %i %i %04X:%04X\n",temp,pit.rm[addr&3],pit.wp,cs>>4,pc);
	return temp;
}

static uint8_t pas16_readdma(pas16_t *pas16) {
	return dma_channel_read(pas16->dma);
}

static void pas16_pcm_poll(void *p) {
	pas16_t *pas16 = (pas16_t *)p;

	pas16_update(pas16);
//        if (pas16->pcm_ctrl & PAS16_PCM_ENA)
//                pclog("pas16_pcm_poll : poll %i %i ", pas16->pit.c[0], pas16->pit.l[0]);
	if (pas16->pit.m[0] & 2) {
		if (pas16->pit.l[0])
			timer_advance_u64(&pas16->pit.timer[0], pas16->pit.l[0] * PITCONST);
		else
			timer_advance_u64(&pas16->pit.timer[0], 0x10000 * PITCONST);
	} else {
		pas16->pit.enable[0] = 0;
	}
//        if (pas16->pcm_ctrl & PAS16_PCM_ENA)
//                pclog(" %i\n", pas16->pit.c[0]);

	pas16->irq_stat |= PAS16_INT_SAMP;
	if (pas16->irq_ena & PAS16_INT_SAMP)
		picint(1 << pas16->irq);
//        pas16_update_irqs();

	/*Update sample rate counter*/
	if (pas16->pit.enable[1]) {
		if (pas16->pcm_ctrl & PAS16_PCM_ENA) {
			uint16_t temp;

			if (pas16->sys_conf_2 & PAS16_SC2_16BIT) {
				temp = pas16_readdma(pas16) << 8;
				temp |= pas16_readdma(pas16);
			} else
				temp = (pas16_readdma(pas16) ^ 0x80) << 8;

			if (pas16->sys_conf_2 & PAS16_SC2_MSBINV)
				temp ^= 0x8000;
			if (pas16->pcm_ctrl & PAS16_PCM_MONO)
				pas16->pcm_dat_l = pas16->pcm_dat_r = temp;
			else {
				if (pas16->stereo_lr)
					pas16->pcm_dat_r = temp;
				else
					pas16->pcm_dat_l = temp;

				pas16->stereo_lr = !pas16->stereo_lr;
			}
//                        pclog("pas16_pcm_poll : %04X %i\n", temp, pas16->stereo_lr);
//                        pclog("pas16_pcm_poll : %i %02X  %i\n", pas16->pit.c[1], temp, pas16->pit.c[0]);
/*                        if (!pas16_pcm)
                                pas16_pcm=fopen("pas16->pcm", "wb");
                        putc(temp, pas16_pcm);*/
		}
		if (pas16->sys_conf_2 & PAS16_SC2_16BIT)
			pas16->pit.c[1] -= 2;
		else
			pas16->pit.c[1]--;
		if (pas16->pit.c[1] == 0) {
//                        if (pas16->pcm_ctrl & PAS16_PCM_ENA)
//                                pclog("pas16_pcm_poll : buffer over\n");
			if (pas16->pit.m[1] & 2) {
				if (pas16->pit.l[1])
					pas16->pit.c[1] += pas16->pit.l[1];
				else
					pas16->pit.c[1] += 0x10000;
			} else {
				pas16->pit.c[1] = -1;
				pas16->pit.enable[1] = 0;
			}

			pas16->irq_stat |= PAS16_INT_PCM;
			if (pas16->irq_ena & PAS16_INT_PCM) {
				pclog("pas16_pcm_poll : cause IRQ %i %02X\n", pas16->irq, 1 << pas16->irq);
				picint(1 << pas16->irq);
			}
		}
	}
}

static void pas16_out_base(uint16_t port, uint8_t val, void *p) {
	pas16_t *pas16 = (pas16_t *)p;

	io_removehandler((pas16->base - 0x388) + 0x0388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x0788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x0b88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x0f88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x1388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x1788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x2788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x7f88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0x8388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xbf88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xe388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xe788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xeb88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xef88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xf388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xf788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xfb88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_removehandler((pas16->base - 0x388) + 0xff88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);

	pas16->base = val << 2;
	pclog("pas16_write_base : PAS16 base now at %04X\n", pas16->base);

	io_sethandler((pas16->base - 0x388) + 0x0388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x0788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x0b88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x0f88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x1388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x1788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x2788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x7f88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0x8388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xbf88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xe388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xe788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xeb88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xef88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xf388, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xf788, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xfb88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
	io_sethandler((pas16->base - 0x388) + 0xff88, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
}

static void pas16_update(pas16_t *pas16) {
	if (!(pas16->audiofilt & PAS16_FILT_MUTE)) {
		for (; pas16->pos < sound_pos_global; pas16->pos++) {
			pas16->pcm_buffer[0][pas16->pos] = 0;
			pas16->pcm_buffer[1][pas16->pos] = 0;
		}
	} else {
		for (; pas16->pos < sound_pos_global; pas16->pos++) {
			pas16->pcm_buffer[0][pas16->pos] = (int16_t)pas16->pcm_dat_l;
			pas16->pcm_buffer[1][pas16->pos] = (int16_t)pas16->pcm_dat_r;
		}
	}
}

void pas16_get_buffer(int32_t *buffer, int len, void *p) {
	pas16_t *pas16 = (pas16_t *)p;
	int c;

	opl3_update2(&pas16->opl);
	sb_dsp_update(&pas16->dsp);
	pas16_update(pas16);
	for (c = 0; c < len * 2; c++) {
		buffer[c] += pas16->opl.buffer[c];
		buffer[c] += (int16_t)(sb_iir(c & 1, (float)pas16->dsp.buffer[c]) / 1.3) / 2;
		buffer[c] += (pas16->pcm_buffer[c & 1][c >> 1] / 2);
	}

	pas16->pos = 0;
	pas16->opl.pos = 0;
	pas16->dsp.pos = 0;
}

void *pas16_init() {
	pas16_t *pas16 = malloc(sizeof(pas16_t));
	memset(pas16, 0, sizeof(pas16_t));

	opl3_init(&pas16->opl, 0);
	sb_dsp_init(&pas16->dsp, SB2, SB_SUBTYPE_DEFAULT, pas16);

	io_sethandler(0x9a01, 0x0001, NULL, NULL, NULL, pas16_out_base, NULL, NULL, pas16);

	timer_add(&pas16->pit.timer[0], pas16_pcm_poll, pas16, 0);

	sound_add_handler(pas16_get_buffer, pas16);

	return pas16;
}

void pas16_close(void *p) {
	pas16_t *pas16 = (pas16_t *)p;

	free(pas16);
}

device_t pas16_device =
	{
		"Pro Audio Spectrum 16",
		DEVICE_NOT_WORKING,
		pas16_init,
		pas16_close,
		NULL,
		NULL,
		NULL,
		NULL
	};
