diff --git a/src/dma.c b/src/dma.c
index 50ae598b8..b7c4b4863 100644
--- a/src/dma.c
+++ b/src/dma.c
@@ -1397,6 +1397,29 @@ dma_advance(dma_t *dma_c)
dma_c->ac = ((dma_c->ac & 0xffff0000) & dma_mask) | ((dma_c->ac + as) & 0xffff);
}
+int
+dma_channel_readable(int channel)
+{
+ dma_t *dma_c = &dma[channel];
+
+ if (channel < 4) {
+ if (dma_command[0] & 0x04)
+ return 0;
+ } else {
+ if (dma_command[1] & 0x04)
+ return 0;
+ }
+
+ if (!(dma_e & (1 << channel)))
+ return 0;
+ if ((dma_m & (1 << channel)) && !dma_req_is_soft)
+ return 0;
+ if ((dma_c->mode & 0xC) != 8)
+ return 0;
+
+ return 1;
+}
+
int
dma_channel_read(int channel)
{
diff --git a/src/include/86box/dma.h b/src/include/86box/dma.h
index 7ead53ba0..ff0dc0b5d 100644
--- a/src/include/86box/dma.h
+++ b/src/include/86box/dma.h
@@ -118,4 +118,6 @@ void dma_high_page_init(void);
void dma_remove_sg(void);
void dma_set_sg_base(uint8_t sg_base);
+extern int dma_channel_readable(int channel);
+
#endif /*EMU_DMA_H*/
diff --git a/src/sound/snd_pas16.c b/src/sound/snd_pas16.c
index 45a18ff73..0a5c046b7 100644
--- a/src/sound/snd_pas16.c
+++ b/src/sound/snd_pas16.c
@@ -1,3 +1,92 @@
+/*
+ * 86Box A hypervisor and IBM PC system emulator that specializes in
+ * running old operating systems and software designed for IBM
+ * PC systems and compatibles from 1981 through fairly recent
+ * system designs based on the PCI bus.
+ *
+ * This file is part of the 86Box distribution.
+ *
+ * Pro Audio Spectrum Plus and 16 emulation.
+ *
+ * Original PAS uses:
+ * - 2 x OPL2;
+ * - PIT - sample rate/count;
+ * - LMC835N/LMC1982 - mixer;
+ * - YM3802 - MIDI Control System.
+ *
+ * 9A01 - I/O base:
+ * - base >> 2.
+ *
+ * All below + I/O 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 address:
+ * - Bits 0-3 - Address bits 4-7.
+ *
+ * FB8A - SB IRQ/DMA:
+ * - Bits 3-5 - IRQ;
+ * - Bits 6-7 - DMA.
+ *
+ * FF88 - board model:
+ * - 3 = PAS16.
+ *
+ * Authors: Sarah Walker,
+ * Miran Grca,
+ * TheCollector1995,
+ *
+ * Copyright 2008-2024 Sarah Walker.
+ * Copyright 2024 Miran Grca.
+ */
+#include
#include
#include
#include
@@ -16,6 +105,7 @@
#include <86box/pic.h>
#include <86box/timer.h>
#include <86box/pit.h>
+#include <86box/pit_fast.h>
#include <86box/snd_mpu401.h>
#include <86box/sound.h>
#include <86box/snd_opl.h>
@@ -23,140 +113,69 @@
#include <86box/snd_sb_dsp.h>
#include <86box/plat_unused.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;
+ uint8_t this_id;
+ uint8_t board_id;
+ uint8_t master_ff;
+ uint8_t irq;
+ uint8_t dma;
+ uint8_t sb_irqdma;
+ uint8_t type;
+ uint8_t filter;
- int irq;
- int dma;
-
- uint8_t audiofilt;
-
- uint8_t audio_mixer;
-
- uint8_t compat;
- uint8_t compat_base;
-
- uint8_t enhancedscsi;
-
- uint8_t io_conf_1;
- uint8_t io_conf_2;
- uint8_t io_conf_3;
- uint8_t io_conf_4;
-
- uint8_t irq_stat;
- uint8_t irq_ena;
+ uint8_t audiofilt;
+ uint8_t audio_mixer;
+ uint8_t compat;
+ uint8_t compat_base;
+ uint8_t io_conf_1;
+ uint8_t io_conf_2;
+ uint8_t io_conf_3;
+ uint8_t io_conf_4;
+ uint8_t irq_stat;
+ uint8_t irq_ena;
uint8_t pcm_ctrl;
- uint16_t pcm_dat;
+ uint8_t prescale_div;
+ uint8_t stereo_lr;
+ uint8_t dma8_ff;
+ uint8_t waitstates;
+ uint8_t enhancedscsi;
+ uint8_t sys_conf_1;
+ uint8_t sys_conf_2;
+ uint8_t sys_conf_3;
+ uint8_t sys_conf_4;
+ uint8_t midi_ctrl;
+ uint8_t midi_stat;
+ uint8_t midi_data;
+ uint8_t fifo_stat;
+
+ uint8_t midi_queue[256];
+
+ uint16_t base;
+ uint16_t new_base;
uint16_t pcm_dat_l;
uint16_t pcm_dat_r;
- uint8_t sb_irqdma;
+ int16_t pcm_buffer[2][SOUNDBUFLEN * 2];
- int stereo_lr;
-
- uint8_t sys_conf_1;
- uint8_t sys_conf_2;
- uint8_t sys_conf_3;
- uint8_t sys_conf_4;
- uint8_t waitstates;
- uint8_t midi_ctrl;
- uint8_t midi_stat;
- uint8_t midi_data;
- uint8_t fifo_stat;
- int midi_r;
- int midi_w;
- int midi_uart_out;
- int midi_uart_in;
- uint8_t midi_queue[256];
- int sysex;
+ int pos;
+ int midi_r;
+ int midi_w;
+ int midi_uart_out;
+ int midi_uart_in;
+ int sysex;
fm_drv_t opl;
sb_dsp_t dsp;
- mpu_t *mpu;
- pc_timer_t timer;
- int16_t pcm_buffer[2][SOUNDBUFLEN];
+ mpu_t * mpu;
- int pos;
-
- pit_t *pit;
+ pitf_t * pit;
} pas16_t;
+static uint8_t pas16_next = 0;
+
static void pas16_update(pas16_t *pas16);
static int pas16_dmas[8] = { 4, 1, 2, 3, 0, 5, 6, 7 };
@@ -166,7 +185,7 @@ static int pas16_sb_dmas[8] = { 0, 1, 2, 3 };
enum {
PAS16_INT_SAMP = 0x04,
PAS16_INT_PCM = 0x08,
- PAS16_INT_MIDI = 0x10,
+ PAS16_INT_MIDI = 0x10
};
enum {
@@ -184,6 +203,47 @@ enum {
PAS16_FILT_MUTE = 0x20
};
+#define PAS16_PCM_AND_DMA_ENA (PAS16_PCM_ENA | PAS16_PCM_DMA_ENA)
+
+double low_fir_pas16_coef[4][SB16_NCoef];
+
+static __inline double
+sinc(double x)
+{
+ return sin(M_PI * x) / (M_PI * x);
+}
+
+static void
+recalc_pas16_filter(int c, int playback_freq)
+{
+ /* Cutoff frequency = playback / 2 */
+ int n;
+ double w;
+ double h;
+ double fC = ((double) playback_freq) / (double) FREQ_96000;
+ double gain;
+
+ for (n = 0; n < SB16_NCoef; n++) {
+ /* Blackman window */
+ w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1)));
+ /* Sinc filter */
+ h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0)));
+
+ /* Create windowed-sinc filter */
+ low_fir_pas16_coef[c][n] = w * h;
+ }
+
+ low_fir_pas16_coef[c][(SB16_NCoef - 1) / 2] = 1.0;
+
+ gain = 0.0;
+ for (n = 0; n < SB16_NCoef; n++)
+ gain += low_fir_pas16_coef[c][n];
+
+ /* Normalise filter, to produce unity gain */
+ for (n = 0; n < SB16_NCoef; n++)
+ low_fir_pas16_coef[c][n] /= gain;
+}
+
#ifdef ENABLE_PAS16_LOG
int pas16_do_log = ENABLE_PAS16_LOG;
@@ -226,45 +286,45 @@ static uint8_t
pas16_in(uint16_t port, void *priv)
{
pas16_t *pas16 = (pas16_t *) priv;
- uint8_t temp = 0xff;
+ uint8_t ret = 0xff;
+
+ port -= pas16->base;
+
switch (port) {
- case 0x388:
- case 0x389:
- case 0x38a:
- case 0x38b:
- temp = pas16->opl.read(port, pas16->opl.priv);
+ case 0x0000 ... 0x0003:
+ ret = pas16->opl.read(port + 0x0388, pas16->opl.priv);
break;
- case 0xb88:
- temp = pas16->audio_mixer;
+ case 0x0800:
+ ret = pas16->audio_mixer;
break;
- case 0xb89:
- temp = pas16->irq_stat;
+ case 0x0801:
+ ret = pas16->irq_stat & 0xdf;
break;
- case 0xb8a:
- temp = pas16->audiofilt;
+ case 0x0802:
+ ret = pas16->audiofilt;
break;
- case 0xb8b:
- temp = pas16->irq_ena | 0x20;
- pas16_log("IRQ Mask read=%02x.\n", temp);
+ case 0x0803:
+ ret = pas16->irq_ena | (pas16->type ? 0x20 : 0x00);
+ pas16_log("IRQ Mask read=%02x.\n", ret);
break;
- case 0xf8a:
- temp = pas16->pcm_ctrl;
+ case 0x0c02:
+ ret = pas16->pcm_ctrl;
break;
- case 0x1789:
- case 0x178b:
- temp = pas16->midi_ctrl;
+ case 0x1401:
+ case 0x1403:
+ ret = pas16->midi_ctrl;
break;
- case 0x178a:
- case 0x1b8a:
- temp = 0;
+ case 0x1402:
+ case 0x1802:
+ ret = 0;
if (pas16->midi_uart_in) {
if ((pas16->midi_data == 0xaa) && (pas16->midi_ctrl & 0x04))
- temp = pas16->midi_data;
+ ret = pas16->midi_data;
else {
- temp = pas16->midi_queue[pas16->midi_r];
+ ret = pas16->midi_queue[pas16->midi_r];
if (pas16->midi_r != pas16->midi_w) {
pas16->midi_r++;
pas16->midi_r &= 0xff;
@@ -275,206 +335,386 @@ pas16_in(uint16_t port, void *priv)
}
break;
- case 0x1b88:
- temp = pas16->midi_stat;
+ case 0x1800:
+ ret = pas16->midi_stat;
break;
- case 0x1b89:
- temp = pas16->fifo_stat;
+ case 0x1801:
+ ret = pas16->fifo_stat;
break;
- case 0x2789: /*Board revision*/
- temp = 0x00;
+ case 0x2401: /* Board revision */
+ ret = 0x00;
break;
- case 0x7f89:
- temp = pas16->enhancedscsi & ~0x01;
+ case 0x7c01:
+ ret = pas16->enhancedscsi & ~0x01;
break;
- case 0x8388:
- temp = pas16->sys_conf_1;
+ case 0x8000:
+ ret = pas16->sys_conf_1;
break;
- case 0x8389:
- temp = pas16->sys_conf_2;
+ case 0x8001:
+ ret = pas16->sys_conf_2;
break;
- case 0x838a:
- temp = pas16->sys_conf_3;
+ case 0x8002:
+ ret = pas16->sys_conf_3;
break;
- case 0x838b:
- temp = pas16->sys_conf_4;
+ case 0x8003:
+ ret = pas16->sys_conf_4;
break;
- case 0xbf88:
- temp = pas16->waitstates;
+ case 0xbc00:
+ ret = pas16->waitstates;
break;
- case 0xef8b:
- temp = 0x00;
+ case 0xbc02:
+ ret = pas16->prescale_div;
break;
- case 0xf388:
- temp = pas16->io_conf_1;
+ case 0xec03:
+ ret = pas16->type ? 0x0c : 0x04;
break;
- case 0xf389:
- temp = pas16->io_conf_2;
+
+ case 0xf000:
+ ret = pas16->io_conf_1;
+ break;
+ case 0xf001:
+ ret = pas16->io_conf_2;
pas16_log("pas16_in : set PAS DMA %i\n", pas16->dma);
break;
- case 0xf38a:
- temp = pas16->io_conf_3;
+ case 0xf002:
+ ret = pas16->io_conf_3;
pas16_log("pas16_in : set PAS IRQ %i\n", pas16->irq);
break;
- case 0xf38b:
- temp = pas16->io_conf_4;
+ case 0xf003:
+ ret = pas16->io_conf_4;
break;
- case 0xf788:
- temp = pas16->compat;
+ case 0xf400:
+ ret = pas16->compat;
break;
- case 0xf789:
- temp = pas16->compat_base;
+ case 0xf401:
+ ret = pas16->compat_base;
break;
- case 0xfb8a:
- temp = pas16->sb_irqdma;
+ case 0xf802:
+ ret = pas16->sb_irqdma;
break;
- case 0xff88: /*Board model*/
- temp = 0x04; /*PAS16*/
+ case 0xfc00: /* Board model */
+ /* PAS16 or PASPlus */
+ ret = pas16->type ? 0x04 : 0x01;
break;
- case 0xff8b: /*Master mode read*/
- temp = 0x20 | 0x10 | 0x01; /*AT bus, XT/AT timing*/
+ case 0xfc03: /* Master mode read */
+ /* AT bus, XT/AT timing */
+ ret = pas16->type ? (0x20 | 0x10 | 0x01) : (0x10 | 0x01);
break;
default:
break;
}
- pas16_log("pas16_in : port %04X return %02X %04X:%04X\n", port, temp, CS, cpu_state.pc);
- return temp;
+
+ pas16_log("[%04X:%08X] PAS16: [R] %04X (%04X) = %02X\n",
+ CS, cpu_state.pc, port + pas16->base, port, ret);
+
+ return ret;
+}
+
+static void
+pas16_change_pit_clock_speed(void *priv)
+{
+ pas16_t *pas16 = (pas16_t *) priv;
+ pitf_t *pit = (pitf_t *) pas16->pit;
+
+ if (pas16->type && (pas16->sys_conf_1 & 0x02) && pas16->prescale_div) {
+ pit_change_pas16_consts((double) pas16->prescale_div);
+ if (pas16->sys_conf_3 & 0x02)
+ pitf_set_pit_const(pit, PAS16CONST2);
+ else
+ pitf_set_pit_const(pit, PAS16CONST);
+ } else
+ pitf_set_pit_const(pit, PITCONST);
+}
+
+static void
+pas16_io_handler(pas16_t *pas16, int set)
+{
+ if (pas16->base != 0x0000) {
+ for (uint32_t addr = 0x0000; addr <= 0xffff; addr += 0x0400) {
+ pas16_log("%04X-%04X: %i\n", pas16->base + addr, pas16->base + addr + 3, set);
+ if (addr != 0x1000)
+ io_handler(set, pas16->base + addr, 0x0004,
+ pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
+ }
+
+ pitf_handler(set, pas16->base + 0x1000, 0x0004, pas16->pit);
+ }
+}
+
+static void
+pas16_reset_pcm(void *priv)
+{
+ pas16_t *pas16 = (pas16_t *) priv;
+
+ pas16->pcm_ctrl = 0x00;
+
+ pas16->stereo_lr = 0;
+ pas16->dma8_ff = 0;
+
+ pas16->irq_stat &= 0xd7;
+
+ if (!pas16->irq_stat)
+ picintc(1 << pas16->irq);
+}
+
+static void
+pas16_reset_regs(void *priv)
+{
+ pas16_t *pas16 = (pas16_t *) priv;
+ pitf_t *pit = (pitf_t *) pas16->pit;
+
+ picintc(1 << pas16->irq);
+
+ pas16->sys_conf_1 &= 0xfd;
+
+ pas16->sys_conf_2 = 0x00;
+ pas16->sys_conf_3 = 0x00;
+
+ pas16->prescale_div = 0x00;
+
+ pitf_set_pit_const(pit, PITCONST);
+
+ pas16->audiofilt = 0x00;
+ pas16->filter = 0;
+
+ pitf_ctr_set_gate(pit, 0, 0);
+ pitf_ctr_set_gate(pit, 1, 0);
+
+ pas16_reset_pcm(pas16);
+
+ pas16->irq_ena = 0x00;
+ pas16->irq_stat = 0x00;
+}
+
+static void
+pas16_reset_common(void *priv)
+{
+ pas16_t *pas16 = (pas16_t *) priv;
+
+ pas16_reset_regs(pas16);
+
+ picintc(1 << pas16->irq);
+
+ pas16_io_handler(pas16, 0);
+ pas16->base = 0x0000;
+}
+
+static void
+pas16_reset(void *priv)
+{
+ pas16_t *pas16 = (pas16_t *) priv;
+
+ pas16_reset_common(priv);
+
+ pas16->board_id = 0;
+ pas16->master_ff = 0;
+
+ pas16->base = 0x0388;
+ pas16_io_handler(pas16, 1);
+
+ pas16->new_base = 0x0388;
}
static void
pas16_out(uint16_t port, uint8_t val, void *priv)
{
pas16_t *pas16 = (pas16_t *) priv;
- pas16_log("pas16_out : port %04X val %02X %04X:%04X\n", port, val, CS, cpu_state.pc);
+
+ pas16_log("[%04X:%08X] PAS16: [W] %04X (%04X) = %02X\n",
+ CS, cpu_state.pc, port, port - pas16->base, val);
+
+ port -= pas16->base;
+
switch (port) {
- case 0x388:
- case 0x389:
- case 0x38a:
- case 0x38b:
- pas16->opl.write(port, val, pas16->opl.priv);
+ case 0x0000 ... 0x0003:
+ pas16->opl.write(port + 0x0388, val, pas16->opl.priv);
break;
- case 0xb88:
+ case 0x0800:
pas16->audio_mixer = val;
+ if (!(val & 0x01))
+ pas16_reset_pcm(pas16);
break;
- case 0xb89:
+ case 0x0801:
pas16->irq_stat &= ~val;
break;
- case 0xb8a:
+ case 0x0802:
+ if ((val & 0x20) && !(pas16->audiofilt & 0x20)) {
+ pas16_log("Reset.\n");
+ val = 0x20;
+ pas16_reset_regs(pas16);
+ }
+
pas16_update(pas16);
+ pitf_ctr_set_gate(pas16->pit, 0, 1);
+ pitf_ctr_set_gate(pas16->pit, 1, 1);
+ // pas16->pit->counters[0].gate = !!(val & 0x40);
+ // pas16->pit->counters[0].enabled = !!(val & 0x40);
+ // pas16->pit->counters[1].gate = !!(val & 0x80);
+ // pas16->pit->counters[1].enabled = !!(val & 0x80);
+ pas16->stereo_lr = 0;
+ pas16->irq_stat &= 0xdf;
+ pas16->dma8_ff = 0;
pas16->audiofilt = val;
+ if (val & 0x1f) {
+ pas16->filter = 1;
+ switch (val & 0x1f) {
+ default:
+ pas16->filter = 0;
+ break;
+ case 0x01:
+ recalc_pas16_filter(0, 17897);
+ break;
+ case 0x02:
+ recalc_pas16_filter(0, 15909);
+ break;
+ case 0x04:
+ recalc_pas16_filter(0, 2982);
+ break;
+ case 0x09:
+ recalc_pas16_filter(0, 11931);
+ break;
+ case 0x11:
+ recalc_pas16_filter(0, 8948);
+ break;
+ case 0x19:
+ recalc_pas16_filter(0, 5965);
+ break;
+ }
+ } else
+ pas16->filter = 0;
break;
- case 0xb8b:
+ case 0x0803:
pas16->irq_ena = val & 0x1f;
break;
- case 0xf88:
+ case 0x0c00:
pas16_update(pas16);
- pas16->pcm_dat = (pas16->pcm_dat & 0xff00) | val;
break;
- case 0xf89:
+ case 0x0c01:
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*/
+ case 0x0c02:
+ if ((val & PAS16_PCM_ENA) && !(pas16->pcm_ctrl & PAS16_PCM_ENA)) {
+ /* Guess */
pas16->stereo_lr = 0;
+ pas16->irq_stat &= 0xdf;
+ /* Needed for 8-bit DMA to work correctly on a 16-bit DMA channel. */
+ pas16->dma8_ff = 0;
+ }
pas16->pcm_ctrl = val;
+ pas16_log("Now in: %s (%02X)\n", (pas16->pcm_ctrl & PAS16_PCM_MONO) ? "Mono" : "Stereo", val);
break;
- case 0x1789:
- case 0x178b:
+ case 0x1401:
+ case 0x1403:
pas16->midi_ctrl = val;
if ((val & 0x60) == 0x60) {
pas16->midi_uart_out = 0;
pas16->midi_uart_in = 0;
- } else if (val & 0x18) {
+ } else if (val & 0x18)
pas16->midi_uart_out = 1;
- } else if (val & 0x04)
+ else if (val & 0x04)
pas16->midi_uart_in = 1;
else
pas16->midi_uart_out = 1;
pas16_update_irq(pas16);
break;
- case 0x178a:
- case 0x1b8a:
+ case 0x1402:
+ case 0x1802:
pas16->midi_data = val;
pas16_log("UART OUT=%d.\n", pas16->midi_uart_out);
if (pas16->midi_uart_out)
midi_raw_out_byte(val);
break;
- case 0x1b88:
+ case 0x1800:
pas16->midi_stat = val;
pas16_update_irq(pas16);
break;
- case 0x1b89:
+ case 0x1801:
pas16->fifo_stat = val;
break;
- case 0x7f89:
+ case 0x7c01:
pas16->enhancedscsi = val;
break;
- case 0x8388:
+ case 0x8000:
if ((val & 0xc0) && !(pas16->sys_conf_1 & 0xc0)) {
pas16_log("Reset.\n");
- picintc(1 << pas16->irq);
val = 0x00;
+ pas16_reset_common(pas16);
+ pas16->base = pas16->new_base;
+ pas16_io_handler(pas16, 1);
}
+
pas16->sys_conf_1 = val;
+ pas16_change_pit_clock_speed(pas16);
+ pas16_log("Now in: %s mode\n", (pas16->sys_conf_1 & 0x02) ? "native" : "compatibility");
break;
- case 0x8389:
+ case 0x8001:
pas16->sys_conf_2 = val;
+ pas16_log("Now in: %i bits (%02X)\n",
+ (pas16->sys_conf_2 & 0x04) ? ((pas16->sys_conf_2 & 0x08) ? 12 : 16) : 8, val);
break;
- case 0x838a:
+ case 0x8002:
pas16->sys_conf_3 = val;
+ pas16_change_pit_clock_speed(pas16);
+ pas16_log("Use 1.008 MHz clok for PCM: %c\n", (val & 0x02) ? 'Y' : 'N');
break;
- case 0x838b:
+ case 0x8003:
pas16->sys_conf_4 = val;
break;
- case 0xbf88:
+ case 0xbc00:
pas16->waitstates = val;
break;
+ case 0xbc02:
+ pas16->prescale_div = val;
+ pas16_change_pit_clock_speed(pas16);
+ pas16_log("Prescale divider now: %i\n", val);
+ break;
- case 0xf388:
+ case 0xf000:
pas16->io_conf_1 = val;
break;
- case 0xf389:
+ case 0xf001:
pas16->io_conf_2 = val;
pas16->dma = pas16_dmas[val & 0x7];
+ pas16_change_pit_clock_speed(pas16);
pas16_log("pas16_out : set PAS DMA %i\n", pas16->dma);
break;
- case 0xf38a:
+ case 0xf002:
pas16->io_conf_3 = val;
pas16->irq = val & 0x0f;
- if (pas16->irq <= 6) {
+ if (pas16->irq <= 6)
pas16->irq++;
- } else if ((pas16->irq > 6) && (pas16->irq < 0x0b))
+ else if ((pas16->irq > 6) && (pas16->irq < 0x0b))
pas16->irq += 3;
else
pas16->irq += 4;
pas16_log("pas16_out : set PAS IRQ %i, val=%02x\n", pas16->irq, val & 0x0f);
break;
- case 0xf38b:
+ case 0xf003:
pas16->io_conf_4 = val;
break;
- case 0xf788:
+ case 0xf400:
pas16->compat = val;
+ pas16_log("PCM compression is now %sabled\n", (val & 0x10) ? "en" : "dis");
if (pas16->compat & 0x02)
sb_dsp_setaddr(&pas16->dsp, ((pas16->compat_base & 0xf) << 4) | 0x200);
else
@@ -484,7 +724,7 @@ pas16_out(uint16_t port, uint8_t val, void *priv)
else
mpu401_change_addr(pas16->mpu, 0);
break;
- case 0xf789:
+ case 0xf401:
pas16->compat_base = val;
if (pas16->compat & 0x02)
sb_dsp_setaddr(&pas16->dsp, ((pas16->compat_base & 0xf) << 4) | 0x200);
@@ -492,11 +732,12 @@ pas16_out(uint16_t port, uint8_t val, void *priv)
mpu401_change_addr(pas16->mpu, ((pas16->compat_base & 0xf0) | 0x300));
break;
- case 0xfb8a:
+ case 0xf802:
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]);
- pas16_log("pas16_out : set SB IRQ %i DMA %i.\n", pas16_sb_irqs[(val >> 3) & 7], pas16_sb_dmas[(val >> 6) & 3]);
+ pas16_log("pas16_out : set SB IRQ %i DMA %i.\n", pas16_sb_irqs[(val >> 3) & 7],
+ pas16_sb_dmas[(val >> 6) & 3]);
break;
default:
@@ -504,35 +745,157 @@ pas16_out(uint16_t port, uint8_t val, void *priv)
}
}
-static uint8_t
-pas16_readdma(pas16_t *pas16)
+/*
+ 8-bit mono:
+ - 8-bit DMA : On every timer 0 over, read the 8-bit sample and ctr_clock();
+ (One ctr_clock() per timer 0 over)
+ - 16-bit DMA: On every even timer 0 over, read two 8-bit samples at once and ctr_clock();
+ On every odd timer 0 over, read the MSB of the previously read sample word.
+ (One ctr_clock() per two timer 0 overs)
+ 8-bit stereo:
+ - 8-bit DMA : On every timer 0, read two 8-bit samples and ctr_clock() twice;
+ (Two ctr_clock()'s per timer 0 over)
+ - 16-bit DMA: On every timer 0, read two 8-bit samples and ctr_clock() once.
+ (One ctr_clock() per timer 0 over)
+ 16-bit mono (to be verified):
+ - 8-bit DMA : On every timer 0, read one 16-bit sample and ctr_clock() twice;
+ (Two ctr_clock()'s per timer 0 over)
+ - 16-bit DMA: On every timer 0, read one 16-bit sample and ctr_clock() once.
+ (One ctr_clock() per timer 0 over)
+ 16-bit stereo:
+ - 8-bit DMA : On every timer 0, read one 16-bit sample and ctr_clock() twice;
+ (Two ctr_clock()'s per timer 0 over)
+ - 16-bit DMA: On every timer 0, read one 16-bit sample and ctr_clock() twice.
+ (Two ctr_clock()'s per timer 0 over)
+
+ What we can conclude from this is:
+ - Maximum 16 bits per timer 0 over;
+ - A 8-bit sample always takes one ctr_clock() tick, unless it has been read
+ alongside the previous sample;
+ - A 16-bit sample always takes two ctr_clock() ticks.
+ */
+static uint16_t
+pas16_dma_readb(pas16_t *pas16, uint8_t timer1_ticks)
{
- return dma_channel_read(pas16->dma);
+ uint16_t ret;
+
+ if (pas16->pcm_ctrl & PAS16_PCM_DMA_ENA)
+ ret = dma_channel_read(pas16->dma);
+ else
+ ret = 0x0000;
+
+ for (uint8_t i = 0; i < timer1_ticks; i++)
+ pitf_ctr_clock(pas16->pit, 1);
+
+ return ret;
+}
+
+static uint16_t
+pas16_dma_readw(pas16_t *pas16, uint8_t timer1_ticks)
+{
+ uint16_t ret;
+
+ if (pas16->pcm_ctrl & PAS16_PCM_DMA_ENA) {
+ ret = dma_channel_read(pas16->dma);
+
+ if (pas16->dma < 5)
+ ret |= (dma_channel_read(pas16->dma) << 8);
+ } else
+ ret = 0x0000;
+
+ for (uint8_t i = 0; i < timer1_ticks; i++)
+ pitf_ctr_clock(pas16->pit, 1);
+
+ return ret;
+}
+
+static uint16_t
+pas16_readdmab(pas16_t *pas16)
+{
+ static uint16_t temp;
+ uint16_t ret;
+
+ if (pas16->dma >= 5) {
+ if (pas16->dma8_ff)
+ temp >>= 8;
+ else
+ temp = pas16_dma_readb(pas16, 1);
+
+ pas16->dma8_ff = !pas16->dma8_ff;
+ } else
+ temp = pas16_dma_readb(pas16, 1);
+
+ ret = ((temp & 0xff) ^ 0x80) << 8;
+
+ return ret;
+}
+
+static uint16_t
+pas16_readdmaw_mono(pas16_t *pas16)
+{
+ uint16_t ret;
+
+ ret = pas16_dma_readw(pas16, 1 + (pas16->dma < 5));
+
+ return ret;
+}
+
+static uint16_t
+pas16_readdmaw_stereo(pas16_t *pas16)
+{
+ uint16_t ret;
+
+ ret = pas16_dma_readw(pas16, 2);
+
+ return ret;
+}
+
+static uint16_t
+pas16_readdma_mono(pas16_t *pas16)
+{
+ uint16_t ret;
+
+ if (pas16->sys_conf_2 & 0x04) {
+ ret = pas16_readdmaw_mono(pas16);
+
+ if (pas16->sys_conf_2 & 0x08)
+ ret &= 0xfff0;
+ } else
+ ret = pas16_readdmab(pas16);
+
+ if (pas16->sys_conf_2 & PAS16_SC2_MSBINV)
+ ret ^= 0x8000;
+
+ return ret;
+}
+
+static uint16_t
+pas16_readdma_stereo(pas16_t *pas16)
+{
+ uint16_t ret;
+
+ if (pas16->sys_conf_2 & 0x04) {
+ ret = pas16_readdmaw_stereo(pas16);
+
+ if (pas16->sys_conf_2 & 0x08)
+ ret &= 0xfff0;
+ } else
+ ret = pas16_readdmab(pas16);
+
+ if (pas16->sys_conf_2 & PAS16_SC2_MSBINV)
+ ret ^= 0x8000;
+
+ return ret;
}
static void
-pas16_pcm_poll(void *priv)
+pas16_pit_timer0(int new_out, UNUSED(int old_out), void *priv)
{
- pas16_t *pas16 = (pas16_t *) priv;
- pit_t *pit = (pit_t *) pas16->pit;
- int data;
- uint16_t temp = 0x0000;
+ pitf_t *pit = (pitf_t *) priv;
+ pas16_t *pas16 = (pas16_t *) pit->dev_priv;
+ uint16_t temp;
pas16_update(pas16);
- if (pit->counters[0].m & 0x02) {
- if (pit->counters[0].l & 0xff) {
- if (pas16->dma >= 5)
- timer_advance_u64(&pas16->timer, (pit->counters[0].l & 0xff) * (PITCONST << 1ULL));
- else
- timer_advance_u64(&pas16->timer, (pit->counters[0].l & 0xff) * PITCONST);
- } else {
- if (pas16->dma >= 5)
- timer_advance_u64(&pas16->timer, 0x100 * (PITCONST << 1ULL));
- else
- timer_advance_u64(&pas16->timer, 0x100 * PITCONST);
- }
- }
-
pas16_update_irq(pas16);
pas16->irq_stat |= PAS16_INT_SAMP;
@@ -541,88 +904,51 @@ pas16_pcm_poll(void *priv)
picint(1 << pas16->irq);
}
- /*Update sample rate counter*/
- pas16_log("T1=%d, master bit 1=%x, counter0=%d, counter1=%d, pcm dma ena=%02x 16bit?=%02x.\n", pit->counters[1].enable, pit->counters[0].m & 0x02, pit->counters[0].l, pit->counters[1].l, pas16->pcm_ctrl & 0xc0, pas16->sys_conf_2 & PAS16_SC2_16BIT);
- if (pit->counters[1].enable) {
- if ((pas16->pcm_ctrl & (PAS16_PCM_ENA | PAS16_PCM_DMA_ENA))) {
- if (pas16->sys_conf_2 & PAS16_SC2_16BIT) {
- data = pas16_readdma(pas16) << 8;
- data |= pas16_readdma(pas16);
- temp = data;
- } else {
- data = pas16_readdma(pas16);
- temp = (data ^ 0x80) << 8;
- }
+ if (((pas16->pcm_ctrl & PAS16_PCM_AND_DMA_ENA) == PAS16_PCM_AND_DMA_ENA) &&
+ dma_channel_readable(pas16->dma) && (pit->counters[1].m & 2) && new_out) {
+ if (pas16->pcm_ctrl & PAS16_PCM_MONO) {
+ temp = pas16_readdma_mono(pas16);
- 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 {
+ pas16->pcm_dat_l = pas16->pcm_dat_r = temp;
+ } else {
+ temp = pas16_readdma_stereo(pas16);
+
+ if (pas16->sys_conf_1 & 0x02) {
+ pas16->pcm_dat_l = temp;
+
+ temp = pas16_readdma_stereo(pas16);
+
+ 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;
+ pas16->irq_stat = (pas16->irq_stat & 0xdf) | (pas16->stereo_lr << 5);
}
}
- if (pas16->sys_conf_2 & PAS16_SC2_16BIT) {
- pit->counters[1].lback -= 2;
- if (!pit->counters[1].lback) {
- if (pit->counters[1].m & 0x02) {
- if (pit->counters[1].lback2 & 0xfffe)
- pit->counters[1].lback = pit->counters[1].lback2 & 0xfffe;
- else
- pit->counters[1].lback = 0;
- } else {
- pit->counters[1].lback = 0;
- pit->counters[1].enable = 0;
- }
- pas16_log("16-bit: New counter=%d, mode=%x.\n", pit->counters[1].lback, pit->counters[1].m & 0x03);
- pas16->irq_stat |= PAS16_INT_PCM;
- if (pas16->irq_ena & PAS16_INT_PCM) {
- pas16_log("16-bit: INT PCM.\n");
- picint(1 << pas16->irq);
- }
- }
- } else {
- pit->counters[1].lback--;
- if (!pit->counters[1].lback) {
- if (pit->counters[1].m & 0x02) {
- if (pit->counters[1].lback2 & 0xffff)
- pit->counters[1].lback = pit->counters[1].lback2 & 0xffff;
- else
- pit->counters[1].lback = 0;
- } else {
- pit->counters[1].lback = 0;
- pit->counters[1].enable = 0;
- }
- pas16_log("8-bit: New counter=%d, mode=%x.\n", pit->counters[1].lback, pit->counters[1].m & 0x03);
- pas16->irq_stat |= PAS16_INT_PCM;
- if (pas16->irq_ena & PAS16_INT_PCM) {
- pas16_log("8-bit: INT PCM.\n");
- picint(1 << pas16->irq);
- }
- }
- }
- }
+ }
}
static void
-pas16_pit_timer0(int new_out, UNUSED(int old_out), void *priv)
+pas16_pit_timer1(int new_out, UNUSED(int old_out), void *priv)
{
- pit_t *pit = (pit_t *)priv;
- pas16_t *pas16 = (pas16_t *)pit->dev_priv;
+ pitf_t *pit = (pitf_t * )priv;
+ pas16_t *pas16 = (pas16_t *) pit->dev_priv;
- pas16_log("PAS16 pit timer0 out=%x, cnt0=%d, cnt1=%d.\n", new_out, pit->counters[0].l, pit->counters[1].l);
- pit_ctr_set_clock(&pit->counters[0], new_out, pit);
- pit->counters[1].enable = new_out;
- if (!timer_is_enabled(&pas16->timer)) {
- if (pas16->dma >= 5)
- timer_set_delay_u64(&pas16->timer, (pit->counters[0].l & 0xff) * (PITCONST << 1ULL));
- else
- timer_set_delay_u64(&pas16->timer, (pit->counters[0].l & 0xff) * PITCONST);
+ /* At new_out = 0, it's in the counter reload phase. */
+ if ((pas16->pcm_ctrl & PAS16_PCM_ENA) && (pit->counters[1].m & 2) && new_out) {
+ if (pas16->irq_ena & PAS16_INT_PCM) {
+ pas16->irq_stat |= PAS16_INT_PCM;
+ pas16_log("pas16_pcm_poll : cause IRQ %i %02X\n", pas16->irq, 1 << pas16->irq);
+ picint(1 << pas16->irq);
+
+ pas16->stereo_lr = 0;
+ pas16->irq_stat &= 0xdf;
+ pas16->dma8_ff = 0;
+ }
}
}
@@ -631,16 +957,14 @@ pas16_out_base(UNUSED(uint16_t port), uint8_t val, void *priv)
{
pas16_t *pas16 = (pas16_t *) priv;
- for (uint32_t addr = 0x000; addr < 0x10000; addr += 0x400) {
- if (addr != 0x1000) {
- io_removehandler(pas16->base + addr, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
- io_sethandler(pas16->base + addr, 0x0004, pas16_in, NULL, NULL, pas16_out, NULL, NULL, pas16);
- }
- }
- pit_handler(0, pas16->base + 0x1000, 0x0004, pas16->pit);
- pit_handler(1, pas16->base + 0x1000, 0x0004, pas16->pit);
+ pas16_log("[%04X:%08X] PAS16: [W] %04X = %02X\n", CS, cpu_state.pc, port, val);
- pas16->base = val << 2;
+ if (pas16->master_ff && (pas16->board_id == pas16->this_id))
+ pas16->new_base = val << 2;
+ else if (!pas16->master_ff)
+ pas16->board_id = val;
+
+ pas16->master_ff = !pas16->master_ff;
}
static void
@@ -693,8 +1017,16 @@ pas16_update(pas16_t *pas16)
}
} 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;
+ pas16->pcm_buffer[0][pas16->pos] = 0;
+ pas16->pcm_buffer[1][pas16->pos] = 0;
+ if (pas16->pcm_ctrl & 0x08)
+ pas16->pcm_buffer[0][pas16->pos] += pas16->pcm_dat_l;
+ if (pas16->pcm_ctrl & 0x04)
+ pas16->pcm_buffer[0][pas16->pos] += pas16->pcm_dat_r;
+ if (pas16->pcm_ctrl & 0x02)
+ pas16->pcm_buffer[1][pas16->pos] += pas16->pcm_dat_l;
+ if (pas16->pcm_ctrl & 0x01)
+ pas16->pcm_buffer[1][pas16->pos] += pas16->pcm_dat_r;
}
}
}
@@ -708,7 +1040,10 @@ pas16_get_buffer(int32_t *buffer, int len, void *priv)
pas16_update(pas16);
for (int c = 0; c < len * 2; c++) {
buffer[c] += (int16_t) (sb_iir(0, c & 1, (double) pas16->dsp.buffer[c]) / 1.3) / 2;
- buffer[c] += (pas16->pcm_buffer[c & 1][c >> 1] / 2);
+ if (pas16->filter)
+ buffer[c] += (low_fir_pas16(0, c & 1, (double) pas16->pcm_buffer[c & 1][c >> 1])) / 2.0;
+ else
+ buffer[c] += (pas16->pcm_buffer[c & 1][c >> 1] / 2);
}
pas16->pos = 0;
@@ -730,31 +1065,22 @@ pas16_get_music_buffer(int32_t *buffer, int len, void *priv)
static void
pas16_speed_changed(void *priv)
{
- pas16_t *pas16 = (pas16_t *) priv;
-
- /* TODO: In PAS16 mode:
- pit_change_pas16_const(prescale);
- pit_set_pit_const(pas16->pit, PAS16CONST);
- */
-
- pit_set_pit_const(pas16->pit, PITCONST);
-}
-
-static void
-pas16_reset(void *priv)
-{
- pas16_t *pas16 = (pas16_t *) priv;
-
- /* TODO: Reset the entire PAS16 state here. */
- pit_set_pit_const(pas16->pit, PITCONST);
+ pas16_change_pit_clock_speed(priv);
}
static void *
-pas16_init(UNUSED(const device_t *info))
+pas16_init(const device_t *info)
{
- pas16_t *pas16 = malloc(sizeof(pas16_t));
- memset(pas16, 0, sizeof(pas16_t));
+ pas16_t *pas16 = calloc(1, sizeof(pas16_t));
+ if (pas16_next > 3) {
+ fatal("Attempting to add a Pro Audio Spectrum instance beyond the maximum amount\n");
+
+ free(pas16);
+ return NULL;
+ }
+
+ pas16->type = info->local & 0xff;
fm_driver_get(FM_YMF262, &pas16->opl);
sb_dsp_set_real_opl(&pas16->dsp, 1);
sb_dsp_init(&pas16->dsp, SB2, SB_SUBTYPE_DEFAULT, pas16);
@@ -763,20 +1089,22 @@ pas16_init(UNUSED(const device_t *info))
mpu401_init(pas16->mpu, 0, 0, M_UART, device_get_config_int("receive_input401"));
sb_dsp_set_mpu(&pas16->dsp, pas16->mpu);
- pas16->pit = device_add(&i8254_ext_io_device);
- pit_set_pit_const(pas16->pit, PITCONST);
- pas16->pit->dev_priv = pas16;
- pas16->irq = 10;
- pas16->dma = 3;
- pas16->base = 0x0388;
-
io_sethandler(0x9a01, 0x0001, NULL, NULL, NULL, pas16_out_base, NULL, NULL, pas16);
- pit_ctr_set_out_func(pas16->pit, 0, pas16_pit_timer0);
- pit_ctr_set_using_timer(pas16->pit, 0, 1);
- pit_ctr_set_using_timer(pas16->pit, 1, 0);
- pit_ctr_set_using_timer(pas16->pit, 2, 0);
+ pas16->this_id = 0xbc + pas16_next;
- timer_add(&pas16->timer, pas16_pcm_poll, pas16, 0);
+ pas16->pit = device_add(&i8254_ext_io_fast_device);
+ pas16_reset(pas16);
+ pas16->pit->dev_priv = pas16;
+ pas16->irq = pas16->type ? 10 : 5;
+ pas16->dma = 3;
+ for (uint8_t i = 0; i < 3; i++)
+ pitf_ctr_set_gate(pas16->pit, i, 0);
+
+ pitf_ctr_set_out_func(pas16->pit, 0, pas16_pit_timer0);
+ pitf_ctr_set_out_func(pas16->pit, 1, pas16_pit_timer1);
+ pitf_ctr_set_using_timer(pas16->pit, 0, 1);
+ pitf_ctr_set_using_timer(pas16->pit, 1, 0);
+ pitf_ctr_set_using_timer(pas16->pit, 2, 0);
sound_add_handler(pas16_get_buffer, pas16);
music_add_handler(pas16_get_music_buffer, pas16);
@@ -784,6 +1112,8 @@ pas16_init(UNUSED(const device_t *info))
if (device_get_config_int("receive_input"))
midi_in_handler(1, pas16_input_msg, pas16_input_sysex, pas16);
+ pas16_next++;
+
return pas16;
}
@@ -793,6 +1123,8 @@ pas16_close(void *priv)
pas16_t *pas16 = (pas16_t *) priv;
free(pas16);
+
+ pas16_next = 0;
}
static const device_config_t pas16_config[] = {
@@ -805,7 +1137,7 @@ static const device_config_t pas16_config[] = {
},
{
.name = "receive_input",
- .description = "Receive input (PAS16 MIDI)",
+ .description = "Receive input (PAS MIDI)",
.type = CONFIG_BINARY,
.default_string = "",
.default_int = 1
@@ -813,10 +1145,10 @@ static const device_config_t pas16_config[] = {
{ .name = "", .description = "", .type = CONFIG_END }
};
-const device_t pas16_device = {
- .name = "Pro Audio Spectrum 16",
- .internal_name = "pas16",
- .flags = DEVICE_ISA | DEVICE_AT,
+const device_t pasplus_device = {
+ .name = "Pro Audio Spectrum Plus",
+ .internal_name = "pasplus",
+ .flags = DEVICE_ISA,
.local = 0,
.init = pas16_init,
.close = pas16_close,
@@ -826,3 +1158,18 @@ const device_t pas16_device = {
.force_redraw = NULL,
.config = pas16_config
};
+
+
+const device_t pas16_device = {
+ .name = "Pro Audio Spectrum 16",
+ .internal_name = "pas16",
+ .flags = DEVICE_ISA | DEVICE_AT,
+ .local = 1,
+ .init = pas16_init,
+ .close = pas16_close,
+ .reset = pas16_reset,
+ { .available = NULL },
+ .speed_changed = pas16_speed_changed,
+ .force_redraw = NULL,
+ .config = pas16_config
+};
diff --git a/src/sound/sound.c b/src/sound/sound.c
index 9acf99460..cf7a7a33c 100644
--- a/src/sound/sound.c
+++ b/src/sound/sound.c
@@ -149,6 +149,7 @@ static const SOUND_CARD sound_cards[] = {
{ &sb_vibra16s_device },
{ &sb_vibra16xv_device },
{ &ssi2001_device },
+ { &pasplus_device },
{ &pas16_device },
{ &pssj_isa_device },
{ &tndy_device },