diff --git a/src/include/86box/snd_sb.h b/src/include/86box/snd_sb.h
index f236c284a..1ab94c9e1 100644
--- a/src/include/86box/snd_sb.h
+++ b/src/include/86box/snd_sb.h
@@ -105,15 +105,13 @@ typedef struct sb_ct1745_mixer_t {
int input_selector_left;
int input_selector_right;
-#define INPUT_MIC 1
-#define INPUT_CD_R 2
-#define INPUT_CD_L 4
-#define INPUT_LINE_R 8
-#define INPUT_LINE_L 16
-#define INPUT_MIDI_R 32
-#define INPUT_MIDI_L 64
-#define INPUT_MIXER_L 128
-#define INPUT_MIXER_R 256
+#define INPUT_MIC 1
+#define INPUT_CD_R 2
+#define INPUT_CD_L 4
+#define INPUT_LINE_R 8
+#define INPUT_LINE_L 16
+#define INPUT_MIDI_R 32
+#define INPUT_MIDI_L 64
int mic_agc;
@@ -128,6 +126,42 @@ typedef struct sb_ct1745_mixer_t {
int output_filter; /* for clones */
} sb_ct1745_mixer_t;
+/* ESS AudioDrive */
+typedef struct ess_mixer_t {
+ double master_l;
+ double master_r;
+ double voice_l;
+ double voice_r;
+ double fm_l;
+ double fm_r;
+ double cd_l;
+ double cd_r;
+ double line_l;
+ double line_r;
+ double mic_l;
+ double mic_r;
+ double auxb_l;
+ double auxb_r;
+ /*see sb_ct1745_mixer for values for input selector*/
+ int32_t input_selector;
+ /* extra values for input selector */
+ #define INPUT_MIXER_L 128
+ #define INPUT_MIXER_R 256
+
+ int input_filter;
+ int in_filter_freq;
+ int output_filter;
+
+ int stereo;
+ int stereo_isleft;
+
+ uint8_t index;
+ uint8_t regs[256];
+
+ uint8_t ess_id_str[4];
+ uint8_t ess_id_str_pos;
+} ess_mixer_t;
+
typedef struct sb_t {
uint8_t cms_enabled;
uint8_t opl_enabled;
@@ -140,6 +174,7 @@ typedef struct sb_t {
sb_ct1335_mixer_t mixer_sb2;
sb_ct1345_mixer_t mixer_sbpro;
sb_ct1745_mixer_t mixer_sb16;
+ ess_mixer_t mixer_ess;
};
mpu_t *mpu;
emu8k_t emu8k;
@@ -165,6 +200,10 @@ extern void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *priv);
extern uint8_t sb_ct1745_mixer_read(uint16_t addr, void *priv);
extern void sb_ct1745_mixer_reset(sb_t *sb);
+extern void sb_ess_mixer_write(uint16_t addr, uint8_t val, void *priv);
+extern uint8_t sb_ess_mixer_read(uint16_t addr, void *priv);
+extern void sb_ess_mixer_reset(sb_t *sb);
+
extern void sb_get_buffer_sbpro(int32_t *buffer, int len, void *priv);
extern void sb_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv);
extern void sbpro_filter_cd_audio(int channel, double *buffer, void *priv);
diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt
index 9206f6be9..9b2cc1416 100644
--- a/src/sound/CMakeLists.txt
+++ b/src/sound/CMakeLists.txt
@@ -18,7 +18,7 @@ add_library(snd OBJECT sound.c snd_opl.c snd_opl_nuked.c snd_opl_ymfm.cpp snd_re
snd_lpt_dss.c snd_ps1.c snd_adlib.c snd_adlibgold.c snd_ad1848.c snd_audiopci.c
snd_azt2316a.c snd_cms.c snd_cmi8x38.c snd_cs423x.c snd_gus.c snd_sb.c snd_sb_dsp.c
snd_emu8k.c snd_mpu401.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c
- snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c snd_ess.c)
+ snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c)
if(OPENAL)
if(VCPKG_TOOLCHAIN)
diff --git a/src/sound/snd_ess.c b/src/sound/snd_ess.c
deleted file mode 100644
index 0239506bd..000000000
--- a/src/sound/snd_ess.c
+++ /dev/null
@@ -1,661 +0,0 @@
-/*
- * 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.
- *
- * ESS AudioDrive emulation.
- *
- *
- *
- * Authors: Sarah Walker,
- * Miran Grca,
- * TheCollector1995,
- * Cacodemon345,
- * Kagamiin~,
- *
- * Copyright 2008-2020 Sarah Walker.
- * Copyright 2016-2020 Miran Grca.
- * Copyright 2024 Cacodemon345
- * Copyright 2024 Kagamiin~
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#define HAVE_STDARG_H
-
-#include <86box/86box.h>
-#include <86box/device.h>
-#include <86box/filters.h>
-#include <86box/gameport.h>
-#include <86box/hdc.h>
-#include <86box/isapnp.h>
-#include <86box/hdc_ide.h>
-#include <86box/io.h>
-#include <86box/mca.h>
-#include <86box/mem.h>
-#include <86box/midi.h>
-#include <86box/pic.h>
-#include <86box/rom.h>
-#include <86box/sound.h>
-#include <86box/timer.h>
-#include <86box/snd_sb.h>
-#include <86box/plat_unused.h>
-
-# define sb_log(fmt, ...)
-
-// clang-format off
-static const double sb_att_4dbstep_3bits[] = {
- 164.0, 2067.0, 3276.0, 5193.0, 8230.0, 13045.0, 20675.0, 32767.0
-};
-
-static const double sb_att_7dbstep_2bits[] = {
- 164.0, 6537.0, 14637.0, 32767.0
-};
-
-/* Attenuation table for 4-bit microphone volume.
- * The last step is a jump to -48 dB. */
-static const double sb_att_1p4dbstep_4bits[] = {
- 164.0, 3431.0, 4031.0, 4736.0, 5565.0, 6537.0, 7681.0, 9025.0,
- 10603.0, 12458.0, 14637.0, 17196.0, 20204.0, 23738.0, 27889.0, 32767.0
-};
-
-/* Attenuation table for 4-bit mixer volume.
- * The last step is a jump to -48 dB. */
-static const double sb_att_2dbstep_4bits[] = {
- 164.0, 1304.0, 1641.0, 2067.0, 2602.0, 3276.0, 4125.0, 5192.0,
- 6537.0, 8230.0, 10362.0, 13044.0, 16422.0, 20674.0, 26027.0, 32767.0
-};
-// clang-format on
-
-/* SB PRO */
-typedef struct ess_mixer_t {
- double master_l;
- double master_r;
- double voice_l;
- double voice_r;
- double fm_l;
- double fm_r;
- double cd_l;
- double cd_r;
- double line_l;
- double line_r;
- double mic_l;
- double mic_r;
- double auxb_l;
- double auxb_r;
- /*see sb_ct1745_mixer for values for input selector*/
- int32_t input_selector;
-
- int input_filter;
- int in_filter_freq;
- int output_filter;
-
- int stereo;
- int stereo_isleft;
-
- uint8_t index;
- uint8_t regs[256];
-
- uint8_t ess_id_str[4];
- uint8_t ess_id_str_pos;
-} ess_mixer_t;
-
-typedef struct ess_t {
- uint8_t mixer_enabled;
- fm_drv_t opl;
- sb_dsp_t dsp;
- ess_mixer_t mixer_sbpro;
-
- mpu_t *mpu;
- void *gameport;
-
- uint16_t gameport_addr;
-
- void *opl_mixer;
- void (*opl_mix)(void *, double *, double *);
-} ess_t;
-
-static double
-ess_mixer_get_vol_4bit(uint8_t vol)
-{
- return sb_att_2dbstep_4bits[vol & 0x0F] / 32767.0;
-}
-
-void
-ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
-{
- ess_t *ess = (ess_t *) priv;
- ess_mixer_t *mixer = &ess->mixer_sbpro;
-
- if (!(addr & 1)) {
- mixer->index = val;
- mixer->regs[0x01] = val;
- if (val == 0x40) {
- mixer->ess_id_str_pos = 0;
- }
- } else {
- if (mixer->index == 0) {
- /* Reset */
- mixer->regs[0x0a] = mixer->regs[0x0c] = 0x00;
- mixer->regs[0x0e] = 0x00;
- /* Changed default from -11dB to 0dB */
- mixer->regs[0x04] = mixer->regs[0x22] = 0xee;
- mixer->regs[0x26] = mixer->regs[0x28] = 0xee;
- mixer->regs[0x2e] = 0x00;
-
- /* Initialize ESS regs. */
- mixer->regs[0x14] = mixer->regs[0x32] = 0x88;
- mixer->regs[0x36] = 0x88;
- mixer->regs[0x38] = 0x88;
- mixer->regs[0x3a] = 0x00;
- mixer->regs[0x3e] = 0x00;
-
- sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2);
- } else {
- mixer->regs[mixer->index] = val;
-
- switch (mixer->index) {
- /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */
- case 0x02:
- case 0x06:
- case 0x08:
- mixer->regs[mixer->index + 0x20] = ((val & 0xe) << 4) | (val & 0xe);
- break;
-
- case 0x0A:
- {
- uint8_t mic_vol_2bit = (mixer->regs[0x0a] >> 1) & 0x3;
- mixer->mic_l = mixer->mic_r = sb_att_7dbstep_2bits[mic_vol_2bit] / 32768.0;
- mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2) | (mic_vol_2bit << 4) | (mic_vol_2bit << 6);
- break;
- }
-
- case 0x0C:
- switch (mixer->regs[0x0C] & 6) {
- case 2:
- mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
- break;
- case 6:
- mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
- break;
- default:
- mixer->input_selector = INPUT_MIC;
- break;
- }
- break;
-
- case 0x14:
- mixer->regs[0x4] = val & 0xee;
- break;
-
- case 0x1A:
- mixer->mic_l = sb_att_1p4dbstep_4bits[(mixer->regs[0x1A] >> 4) & 0xF];
- mixer->mic_r = sb_att_1p4dbstep_4bits[mixer->regs[0x1A] & 0xF];
- break;
-
- case 0x1C:
- if ((mixer->regs[0x1C] & 0x07) == 0x07) {
- mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R;
- } else if ((mixer->regs[0x1C] & 0x07) == 0x06) {
- mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
- } else if ((mixer->regs[0x1C] & 0x06) == 0x02) {
- mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
- } else if ((mixer->regs[0x1C] & 0x02) == 0) {
- mixer->input_selector = INPUT_MIC;
- }
- break;
-
- case 0x22:
- case 0x26:
- case 0x28:
- case 0x2E:
- mixer->regs[mixer->index - 0x20] = (val & 0xe);
- mixer->regs[mixer->index + 0x10] = val;
- break;
-
- /* More compatibility:
- SoundBlaster Pro selects register 020h for 030h, 022h for 032h,
- 026h for 036h, and 028h for 038h. */
- case 0x30:
- mixer->regs[mixer->index - 0x10] = (val & 0xee);
- break;
- case 0x32:
- case 0x36:
- case 0x38:
- case 0x3e:
- mixer->regs[mixer->index - 0x10] = (val & 0xee);
- break;
-
- case 0x3a:
- break;
-
- case 0x00:
- case 0x04:
- break;
-
- case 0x0e:
- break;
-
- case 0x64:
- mixer->regs[mixer->index] &= ~0x8;
- break;
-
- case 0x40:
- {
- uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30);
- gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200);
-
- if (0) {
- /* Not on ES1688. */
- io_removehandler(0x0388, 0x0004,
- ess->opl.read, NULL, NULL,
- ess->opl.write, NULL, NULL,
- ess->opl.priv);
- if ((mixer->regs[0x40] & 0x1) != 0) {
- io_sethandler(0x0388, 0x0004,
- ess->opl.read, NULL, NULL,
- ess->opl.write, NULL, NULL,
- ess->opl.priv);
- }
- }
- switch ((mixer->regs[0x40] >> 5) & 0x7) {
- case 0:
- mpu401_change_addr(ess->mpu, 0x00);
- mpu401_setirq(ess->mpu, -1);
- break;
- case 1:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, -1);
- break;
- case 2:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, ess->dsp.sb_irqnum);
- break;
- case 3:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, 11);
- break;
- case 4:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, 9);
- break;
- case 5:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, 5);
- break;
- case 6:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, 7);
- break;
- case 7:
- mpu401_change_addr(ess->mpu, mpu401_base_addr);
- mpu401_setirq(ess->mpu, 10);
- break;
- }
- break;
- }
-
- default:
- sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
- break;
- }
- }
-
- mixer->voice_l = ess_mixer_get_vol_4bit(mixer->regs[0x14] >> 4);
- mixer->voice_r = ess_mixer_get_vol_4bit(mixer->regs[0x14]);
- mixer->master_l = ess_mixer_get_vol_4bit(mixer->regs[0x32] >> 4);
- mixer->master_r = ess_mixer_get_vol_4bit(mixer->regs[0x32]);
- mixer->fm_l = ess_mixer_get_vol_4bit(mixer->regs[0x36] >> 4);
- mixer->fm_r = ess_mixer_get_vol_4bit(mixer->regs[0x36]);
- mixer->cd_l = ess_mixer_get_vol_4bit(mixer->regs[0x38] >> 4);
- mixer->cd_r = ess_mixer_get_vol_4bit(mixer->regs[0x38]);
- mixer->line_l = ess_mixer_get_vol_4bit(mixer->regs[0x3e] >> 4);
- mixer->line_r = ess_mixer_get_vol_4bit(mixer->regs[0x3e]);
- mixer->auxb_l = ess_mixer_get_vol_4bit(mixer->regs[0x3a] >> 4);
- mixer->auxb_r = ess_mixer_get_vol_4bit(mixer->regs[0x3a]);
-
- mixer->output_filter = !(mixer->regs[0xe] & 0x20);
- mixer->input_filter = !(mixer->regs[0xc] & 0x20);
- mixer->in_filter_freq = ((mixer->regs[0xc] & 0x8) == 0) ? 3200 : 8800;
- mixer->stereo = mixer->regs[0xe] & 2;
- if (mixer->index == 0xe)
- sb_dsp_set_stereo(&ess->dsp, val & 2);
-
- /* TODO: pcspeaker volume? Or is it not worth? */
- }
-}
-
-uint8_t
-ess_mixer_read(uint16_t addr, void *priv)
-{
- ess_t *ess = (ess_t *) priv;
- ess_mixer_t *mixer = &ess->mixer_sbpro;
-
- if (!(addr & 1))
- return mixer->index;
-
- switch (mixer->index) {
- case 0x00:
- case 0x04:
- case 0x0a:
- case 0x0c:
- case 0x0e:
- case 0x14:
- case 0x22:
- case 0x26:
- case 0x28:
- case 0x2e:
- case 0x02:
- case 0x06:
- case 0x30:
- case 0x32:
- case 0x36:
- case 0x38:
- case 0x3e:
- return mixer->regs[mixer->index];
-
- case 0x40:
-
- if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
- uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos];
- mixer->ess_id_str_pos++;
- if (mixer->ess_id_str_pos >= 4)
- mixer->ess_id_str_pos = 0;
- return val;
- } else {
- return mixer->regs[mixer->index];
- }
-
- default:
- sb_log("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
- break;
- }
-
- return 0x0a;
-}
-
-void
-ess_mixer_reset(ess_t *ess)
-{
- ess_mixer_write(4, 0, ess);
- ess_mixer_write(5, 0, ess);
-}
-
-void
-ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv)
-{
- ess_t *ess = (ess_t *) priv;
- const ess_mixer_t *mixer = &ess->mixer_sbpro;
- double out_l = 0.0;
- double out_r = 0.0;
-
- sb_dsp_update(&ess->dsp);
-
- for (int c = 0; c < len * 2; c += 2) {
- out_l = 0.0;
- out_r = 0.0;
-
- /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */
- if (mixer->output_filter) {
- out_l += (low_fir_sb16(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.0;
- out_r += (low_fir_sb16(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
- } else {
- out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0;
- out_r += (ess->dsp.buffer[c + 1] * mixer->voice_r) / 3.0;
- }
-
- /* TODO: recording CD, Mic with AGC or line in. Note: mic volume does not affect recording. */
- out_l *= mixer->master_l;
- out_r *= mixer->master_r;
-
- buffer[c] += (int32_t) out_l;
- buffer[c + 1] += (int32_t) out_r;
- }
-
- ess->dsp.pos = 0;
-}
-
-void
-ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv)
-{
- ess_t *ess = (ess_t *) priv;
- const ess_mixer_t *mixer = &ess->mixer_sbpro;
- double out_l = 0.0;
- double out_r = 0.0;
- const int32_t *opl_buf = NULL;
-
- opl_buf = ess->opl.update(ess->opl.priv);
-
- for (int c = 0; c < len * 2; c += 2) {
- out_l = 0.0;
- out_r = 0.0;
-
- {
- out_l = (((double) opl_buf[c]) * mixer->fm_l) * 0.7171630859375;
- out_r = (((double) opl_buf[c + 1]) * mixer->fm_r) * 0.7171630859375;
- if (ess->opl_mix && ess->opl_mixer)
- ess->opl_mix(ess->opl_mixer, &out_l, &out_r);
- }
-
- /* TODO: recording CD, Mic with AGC or line in. Note: mic volume does not affect recording. */
- out_l *= mixer->master_l;
- out_r *= mixer->master_r;
-
- buffer[c] += (int32_t) out_l;
- buffer[c + 1] += (int32_t) out_r;
- }
-
- ess->opl.reset_buffer(ess->opl.priv);
-}
-
-void
-ess_filter_cd_audio(int channel, double *buffer, void *priv)
-{
- const ess_t *ess = (ess_t *) priv;
- const ess_mixer_t *mixer = &ess->mixer_sbpro;
- double c;
- double cd = channel ? mixer->cd_r : mixer->cd_l;
- double master = channel ? mixer->master_r : mixer->master_l;
-
- c = (*buffer * cd) / 3.0;
- *buffer = c * master;
-}
-
-static void *
-ess_1688_init(UNUSED(const device_t *info))
-{
- /* SB Pro 2 port mappings, 220h or 240h.
- 2x0 to 2x3 -> FM chip (18 voices)
- 2x4 to 2x5 -> Mixer interface
- 2x6, 2xA, 2xC, 2xE -> DSP chip
- 2x8, 2x9, 388 and 389 FM chip (9 voices)
- 2x0+10 to 2x0+13 CDROM interface. */
- ess_t *ess = calloc(sizeof(ess_t), 1);
- uint16_t addr = device_get_config_hex16("base");
-
- fm_driver_get(FM_ESFM, &ess->opl);
-
- sb_dsp_set_real_opl(&ess->dsp, 1);
- sb_dsp_init(&ess->dsp, SBPRO2, SB_SUBTYPE_ESS_ES1688, ess);
- sb_dsp_setaddr(&ess->dsp, addr);
- sb_dsp_setirq(&ess->dsp, device_get_config_int("irq"));
- sb_dsp_setdma8(&ess->dsp, device_get_config_int("dma"));
- sb_dsp_setdma16_8(&ess->dsp, device_get_config_int("dma"));
- sb_dsp_setdma16_supported(&ess->dsp, 0);
- ess_mixer_reset(ess);
-
- /* DSP I/O handler is activated in sb_dsp_setaddr */
- {
- io_sethandler(addr, 0x0004,
- ess->opl.read, NULL, NULL,
- ess->opl.write, NULL, NULL,
- ess->opl.priv);
- io_sethandler(addr + 8, 0x0002,
- ess->opl.read, NULL, NULL,
- ess->opl.write, NULL, NULL,
- ess->opl.priv);
- io_sethandler(0x0388, 0x0004,
- ess->opl.read, NULL, NULL,
- ess->opl.write, NULL, NULL,
- ess->opl.priv);
- }
-
- ess->mixer_enabled = 1;
- io_sethandler(addr + 4, 0x0002,
- ess_mixer_read, NULL, NULL,
- ess_mixer_write, NULL, NULL,
- ess);
- sound_add_handler(ess_get_buffer_sbpro, ess);
- music_add_handler(ess_get_music_buffer_sbpro, ess);
- sound_set_cd_audio_filter(ess_filter_cd_audio, ess);
-
- if (device_get_config_int("receive_input"))
- midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp);
- {
- ess->mixer_sbpro.ess_id_str[0] = 0x16;
- ess->mixer_sbpro.ess_id_str[1] = 0x88;
- ess->mixer_sbpro.ess_id_str[2] = (addr >> 8) & 0xff;
- ess->mixer_sbpro.ess_id_str[3] = addr & 0xff;
- }
-
- ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t));
- mpu401_init(ess->mpu, 0, -1, M_UART, 1);
- sb_dsp_set_mpu(&ess->dsp, ess->mpu);
-
- ess->gameport = gameport_add(&gameport_pnp_device);
- ess->gameport_addr = 0x200;
- gameport_remap(ess->gameport, ess->gameport_addr);
-
- return ess;
-}
-
-void
-ess_close(void *priv)
-{
- ess_t *ess = (ess_t *) priv;
- sb_dsp_close(&ess->dsp);
-
- free(ess);
-}
-
-void
-ess_speed_changed(void *priv)
-{
- ess_t *ess = (ess_t *) priv;
-
- sb_dsp_speed_changed(&ess->dsp);
-}
-
-// clang-format off
-static const device_config_t ess_config[] = {
- {
- .name = "base",
- .description = "Address",
- .type = CONFIG_HEX16,
- .default_string = "",
- .default_int = 0x220,
- .file_filter = "",
- .spinner = { 0 },
- .selection = {
- {
- .description = "0x220",
- .value = 0x220
- },
- {
- .description = "0x240",
- .value = 0x240
- },
- { .description = "" }
- }
- },
- {
- .name = "irq",
- .description = "IRQ",
- .type = CONFIG_SELECTION,
- .default_string = "",
- .default_int = 5,
- .file_filter = "",
- .spinner = { 0 },
- .selection = {
- {
- .description = "IRQ 2",
- .value = 2
- },
- {
- .description = "IRQ 5",
- .value = 5
- },
- {
- .description = "IRQ 7",
- .value = 7
- },
- {
- .description = "IRQ 10",
- .value = 10
- },
- { .description = "" }
- }
- },
- {
- .name = "dma",
- .description = "DMA",
- .type = CONFIG_SELECTION,
- .default_string = "",
- .default_int = 1,
- .file_filter = "",
- .spinner = { 0 },
- .selection = {
- {
- .description = "DMA 0",
- .value = 0
- },
- {
- .description = "DMA 1",
- .value = 1
- },
- {
- .description = "DMA 3",
- .value = 3
- },
- { .description = "" }
- }
- },
- {
- .name = "opl",
- .description = "Enable OPL",
- .type = CONFIG_BINARY,
- .default_string = "",
- .default_int = 1
- },
- {
- .name = "receive_input",
- .description = "Receive input (SB MIDI)",
- .type = CONFIG_BINARY,
- .default_string = "",
- .default_int = 1
- },
- { .name = "", .description = "", .type = CONFIG_END }
-};
-// clang-format on
-
-const device_t ess_1688_device = {
- .name = "ESS Technology ES1688",
- .internal_name = "ess_es1688",
- .flags = DEVICE_ISA,
- .local = 0,
- .init = ess_1688_init,
- .close = ess_close,
- .reset = NULL,
- { .available = NULL },
- .speed_changed = ess_speed_changed,
- .force_redraw = NULL,
- .config = ess_config
-};
diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c
index 2365419b1..1489cb769 100644
--- a/src/sound/snd_sb.c
+++ b/src/sound/snd_sb.c
@@ -75,6 +75,20 @@ static const double sb_att_4dbstep_3bits[] = {
static const double sb_att_7dbstep_2bits[] = {
164.0, 6537.0, 14637.0, 32767.0
};
+
+/* Attenuation table for ESS 4-bit microphone volume.
+ * The last step is a jump to -48 dB. */
+static const double sb_att_1p4dbstep_4bits[] = {
+ 164.0, 3431.0, 4031.0, 4736.0, 5565.0, 6537.0, 7681.0, 9025.0,
+ 10603.0, 12458.0, 14637.0, 17196.0, 20204.0, 23738.0, 27889.0, 32767.0
+};
+
+/* Attenuation table for ESS 4-bit mixer volume.
+ * The last step is a jump to -48 dB. */
+static const double sb_att_2dbstep_4bits[] = {
+ 164.0, 1304.0, 1641.0, 2067.0, 2602.0, 3276.0, 4125.0, 5192.0,
+ 6537.0, 8230.0, 10362.0, 13044.0, 16422.0, 20674.0, 26027.0, 32767.0
+};
// clang-format on
static const uint16_t sb_mcv_addr[8] = { 0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270 };
@@ -667,6 +681,87 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv)
*buffer = c * output_gain;
}
+void
+ess_get_buffer_sbpro(int32_t *buffer, int len, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ const ess_mixer_t *mixer = &ess->mixer_ess;
+ double out_l = 0.0;
+ double out_r = 0.0;
+
+ sb_dsp_update(&ess->dsp);
+
+ for (int c = 0; c < len * 2; c += 2) {
+ out_l = 0.0;
+ out_r = 0.0;
+
+ /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */
+ if (mixer->output_filter) {
+ out_l += (low_fir_sb16(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.0;
+ out_r += (low_fir_sb16(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
+ } else {
+ out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0;
+ out_r += (ess->dsp.buffer[c + 1] * mixer->voice_r) / 3.0;
+ }
+
+ /* TODO: recording from the mixer. */
+ out_l *= mixer->master_l;
+ out_r *= mixer->master_r;
+
+ buffer[c] += (int32_t) out_l;
+ buffer[c + 1] += (int32_t) out_r;
+ }
+
+ ess->dsp.pos = 0;
+}
+
+void
+ess_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ const ess_mixer_t *mixer = &ess->mixer_ess;
+ double out_l = 0.0;
+ double out_r = 0.0;
+ const int32_t *opl_buf = NULL;
+
+ opl_buf = ess->opl.update(ess->opl.priv);
+
+ for (int c = 0; c < len * 2; c += 2) {
+ out_l = 0.0;
+ out_r = 0.0;
+
+ {
+ out_l = (((double) opl_buf[c]) * mixer->fm_l) * 0.7171630859375;
+ out_r = (((double) opl_buf[c + 1]) * mixer->fm_r) * 0.7171630859375;
+ if (ess->opl_mix && ess->opl_mixer)
+ ess->opl_mix(ess->opl_mixer, &out_l, &out_r);
+ }
+
+ /* TODO: recording from the mixer. */
+ out_l *= mixer->master_l;
+ out_r *= mixer->master_r;
+
+ buffer[c] += (int32_t) out_l;
+ buffer[c + 1] += (int32_t) out_r;
+ }
+
+ ess->opl.reset_buffer(ess->opl.priv);
+}
+
+void
+ess_filter_cd_audio(int channel, double *buffer, void *priv)
+{
+ const sb_t *ess = (sb_t *) priv;
+ const ess_mixer_t *mixer = &ess->mixer_ess;
+ double c;
+ double cd = channel ? mixer->cd_r : mixer->cd_l;
+ double master = channel ? mixer->master_r : mixer->master_l;
+
+ /* TODO: recording from the mixer. */
+ c = (*buffer * cd) / 3.0;
+ *buffer = c * master;
+}
+
void
sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *priv)
{
@@ -1304,6 +1399,265 @@ sb_ct1745_mixer_reset(sb_t *sb)
sb_ct1745_mixer_write(5, 0, sb);
}
+void
+ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ ess_mixer_t *mixer = &ess->mixer_ess;
+
+ if (!(addr & 1)) {
+ mixer->index = val;
+ mixer->regs[0x01] = val;
+ if (val == 0x40) {
+ mixer->ess_id_str_pos = 0;
+ }
+ } else {
+ if (mixer->index == 0) {
+ /* Reset */
+ mixer->regs[0x0a] = mixer->regs[0x0c] = 0x00;
+ mixer->regs[0x0e] = 0x00;
+ /* Changed default from -11dB to 0dB */
+ mixer->regs[0x04] = mixer->regs[0x22] = 0xee;
+ mixer->regs[0x26] = mixer->regs[0x28] = 0xee;
+ mixer->regs[0x2e] = 0x00;
+
+ /* Initialize ESS regs. */
+ mixer->regs[0x14] = mixer->regs[0x32] = 0x88;
+ mixer->regs[0x36] = 0x88;
+ mixer->regs[0x38] = 0x88;
+ mixer->regs[0x3a] = 0x00;
+ mixer->regs[0x3e] = 0x00;
+
+ sb_dsp_set_stereo(&ess->dsp, mixer->regs[0x0e] & 2);
+ } else {
+ mixer->regs[mixer->index] = val;
+
+ switch (mixer->index) {
+ /* Compatibility: chain registers 0x02 and 0x22 as well as 0x06 and 0x26 */
+ case 0x02:
+ case 0x06:
+ case 0x08:
+ mixer->regs[mixer->index + 0x20] = ((val & 0xe) << 4) | (val & 0xe);
+ break;
+
+ case 0x0A:
+ {
+ uint8_t mic_vol_2bit = (mixer->regs[0x0a] >> 1) & 0x3;
+ mixer->mic_l = mixer->mic_r = sb_att_7dbstep_2bits[mic_vol_2bit] / 32767.0;
+ mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2) | (mic_vol_2bit << 4) | (mic_vol_2bit << 6);
+ break;
+ }
+
+ case 0x0C:
+ switch (mixer->regs[0x0C] & 6) {
+ case 2:
+ mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
+ break;
+ case 6:
+ mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
+ break;
+ default:
+ mixer->input_selector = INPUT_MIC;
+ break;
+ }
+ break;
+
+ case 0x14:
+ mixer->regs[0x4] = val & 0xee;
+ break;
+
+ case 0x1A:
+ mixer->mic_l = sb_att_1p4dbstep_4bits[(mixer->regs[0x1A] >> 4) & 0xF] / 32767.0;
+ mixer->mic_r = sb_att_1p4dbstep_4bits[mixer->regs[0x1A] & 0xF] / 32767.0;
+ break;
+
+ case 0x1C:
+ if ((mixer->regs[0x1C] & 0x07) == 0x07) {
+ mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R;
+ } else if ((mixer->regs[0x1C] & 0x07) == 0x06) {
+ mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
+ } else if ((mixer->regs[0x1C] & 0x06) == 0x02) {
+ mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
+ } else if ((mixer->regs[0x1C] & 0x02) == 0) {
+ mixer->input_selector = INPUT_MIC;
+ }
+ break;
+
+ case 0x22:
+ case 0x26:
+ case 0x28:
+ case 0x2E:
+ mixer->regs[mixer->index - 0x20] = (val & 0xe);
+ mixer->regs[mixer->index + 0x10] = val;
+ break;
+
+ /* More compatibility:
+ SoundBlaster Pro selects register 020h for 030h, 022h for 032h,
+ 026h for 036h, and 028h for 038h. */
+ case 0x30:
+ mixer->regs[mixer->index - 0x10] = (val & 0xee);
+ break;
+ case 0x32:
+ case 0x36:
+ case 0x38:
+ case 0x3e:
+ mixer->regs[mixer->index - 0x10] = (val & 0xee);
+ break;
+
+ case 0x3a:
+ break;
+
+ case 0x00:
+ case 0x04:
+ break;
+
+ case 0x0e:
+ break;
+
+ case 0x64:
+ mixer->regs[mixer->index] &= ~0x8;
+ break;
+
+ case 0x40:
+ {
+ uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30);
+ gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200);
+
+ if (0) {
+ /* Not on ES1688. */
+ io_removehandler(0x0388, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ if ((mixer->regs[0x40] & 0x1) != 0) {
+ io_sethandler(0x0388, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ }
+ }
+ switch ((mixer->regs[0x40] >> 5) & 0x7) {
+ case 0:
+ mpu401_change_addr(ess->mpu, 0x00);
+ mpu401_setirq(ess->mpu, -1);
+ break;
+ case 1:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, -1);
+ break;
+ case 2:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, ess->dsp.sb_irqnum);
+ break;
+ case 3:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 11);
+ break;
+ case 4:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 9);
+ break;
+ case 5:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 5);
+ break;
+ case 6:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 7);
+ break;
+ case 7:
+ mpu401_change_addr(ess->mpu, mpu401_base_addr);
+ mpu401_setirq(ess->mpu, 10);
+ break;
+ }
+ break;
+ }
+
+ default:
+ sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
+ break;
+ }
+ }
+
+ mixer->voice_l = sb_att_2dbstep_4bits[(mixer->regs[0x14] >> 4) & 0x0F] / 32767.0;
+ mixer->voice_r = sb_att_2dbstep_4bits[mixer->regs[0x14] & 0x0F] / 32767.0;
+ mixer->master_l = sb_att_2dbstep_4bits[(mixer->regs[0x32] >> 4) & 0x0F] / 32767.0;
+ mixer->master_r = sb_att_2dbstep_4bits[mixer->regs[0x32] & 0x0F] / 32767.0;
+ mixer->fm_l = sb_att_2dbstep_4bits[(mixer->regs[0x36] >> 4) & 0x0F] / 32767.0;
+ mixer->fm_r = sb_att_2dbstep_4bits[mixer->regs[0x36] & 0x0F] / 32767.0;
+ mixer->cd_l = sb_att_2dbstep_4bits[(mixer->regs[0x38] >> 4) & 0x0F] / 32767.0;
+ mixer->cd_r = sb_att_2dbstep_4bits[mixer->regs[0x38] & 0x0F] / 32767.0;
+ mixer->line_l = sb_att_2dbstep_4bits[(mixer->regs[0x3e] >> 4) & 0x0F] / 32767.0;
+ mixer->line_r = sb_att_2dbstep_4bits[mixer->regs[0x3e] & 0x0F] / 32767.0;
+ mixer->auxb_l = sb_att_2dbstep_4bits[(mixer->regs[0x3a] >> 4) & 0x0F] / 32767.0;
+ mixer->auxb_r = sb_att_2dbstep_4bits[mixer->regs[0x3a] & 0x0F] / 32767.0;
+
+ mixer->output_filter = !(mixer->regs[0xe] & 0x20);
+ mixer->input_filter = !(mixer->regs[0xc] & 0x20);
+ mixer->in_filter_freq = ((mixer->regs[0xc] & 0x8) == 0) ? 3200 : 8800;
+ mixer->stereo = mixer->regs[0xe] & 2;
+ if (mixer->index == 0xe)
+ sb_dsp_set_stereo(&ess->dsp, val & 2);
+
+ /* TODO: pcspeaker volume? Or is it not worth? */
+ }
+}
+
+uint8_t
+ess_mixer_read(uint16_t addr, void *priv)
+{
+ sb_t *ess = (sb_t *) priv;
+ ess_mixer_t *mixer = &ess->mixer_ess;
+
+ if (!(addr & 1))
+ return mixer->index;
+
+ switch (mixer->index) {
+ case 0x00:
+ case 0x04:
+ case 0x0a:
+ case 0x0c:
+ case 0x0e:
+ case 0x14:
+ case 0x22:
+ case 0x26:
+ case 0x28:
+ case 0x2e:
+ case 0x02:
+ case 0x06:
+ case 0x30:
+ case 0x32:
+ case 0x36:
+ case 0x38:
+ case 0x3e:
+ return mixer->regs[mixer->index];
+
+ case 0x40:
+ if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
+ uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos];
+ mixer->ess_id_str_pos++;
+ if (mixer->ess_id_str_pos >= 4)
+ mixer->ess_id_str_pos = 0;
+ return val;
+ } else {
+ return mixer->regs[mixer->index];
+ }
+
+ default:
+ sb_log("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
+ break;
+ }
+
+ return 0x0a;
+}
+
+void
+ess_mixer_reset(sb_t *ess)
+{
+ ess_mixer_write(4, 0, ess);
+ ess_mixer_write(5, 0, ess);
+}
+
uint8_t
sb_mcv_read(int port, void *priv)
{
@@ -2755,6 +3109,76 @@ sb_awe32_pnp_init(const device_t *info)
return sb;
}
+static void *
+ess_1688_init(UNUSED(const device_t *info))
+{
+ /* SB Pro 2 port mappings, 220h or 240h.
+ 2x0 to 2x3 -> FM chip (18 voices)
+ 2x4 to 2x5 -> Mixer interface
+ 2x6, 2xA, 2xC, 2xE -> DSP chip
+ 2x8, 2x9, 388 and 389 FM chip (9 voices)
+ 2x0+10 to 2x0+13 CDROM interface. */
+ sb_t *ess = calloc(sizeof(sb_t), 1);
+ uint16_t addr = device_get_config_hex16("base");
+
+ fm_driver_get(FM_ESFM, &ess->opl);
+
+ sb_dsp_set_real_opl(&ess->dsp, 1);
+ sb_dsp_init(&ess->dsp, SBPRO2, SB_SUBTYPE_ESS_ES1688, ess);
+ sb_dsp_setaddr(&ess->dsp, addr);
+ sb_dsp_setirq(&ess->dsp, device_get_config_int("irq"));
+ sb_dsp_setdma8(&ess->dsp, device_get_config_int("dma"));
+ sb_dsp_setdma16_8(&ess->dsp, device_get_config_int("dma"));
+ sb_dsp_setdma16_supported(&ess->dsp, 0);
+ ess_mixer_reset(ess);
+
+ /* DSP I/O handler is activated in sb_dsp_setaddr */
+ {
+ io_sethandler(addr, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ io_sethandler(addr + 8, 0x0002,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ io_sethandler(0x0388, 0x0004,
+ ess->opl.read, NULL, NULL,
+ ess->opl.write, NULL, NULL,
+ ess->opl.priv);
+ }
+
+ ess->mixer_enabled = 1;
+ io_sethandler(addr + 4, 0x0002,
+ ess_mixer_read, NULL, NULL,
+ ess_mixer_write, NULL, NULL,
+ ess);
+ sound_add_handler(ess_get_buffer_sbpro, ess);
+ music_add_handler(ess_get_music_buffer_sbpro, ess);
+ sound_set_cd_audio_filter(ess_filter_cd_audio, ess);
+
+ if (device_get_config_int("receive_input")) {
+ midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp);
+ }
+
+ ess->mixer_ess.ess_id_str[0] = 0x16;
+ ess->mixer_ess.ess_id_str[1] = 0x88;
+ ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff;
+ ess->mixer_ess.ess_id_str[3] = addr & 0xff;
+
+ ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t));
+ /* NOTE: The MPU is initialized disabled and with no IRQ assigned.
+ * It will be later initialized by the guest OS's drivers. */
+ mpu401_init(ess->mpu, 0, -1, M_UART, 1);
+ sb_dsp_set_mpu(&ess->dsp, ess->mpu);
+
+ ess->gameport = gameport_add(&gameport_pnp_device);
+ ess->gameport_addr = 0x200;
+ gameport_remap(ess->gameport, ess->gameport_addr);
+
+ return ess;
+}
+
void
sb_close(void *priv)
{
@@ -4023,6 +4447,104 @@ static const device_config_t sb_awe64_gold_config[] = {
},
{ .name = "", .description = "", .type = CONFIG_END }
};
+
+static const device_config_t ess_1688_config[] = {
+ {
+ .name = "base",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = "",
+ .default_int = 0x220,
+ .file_filter = "",
+ .spinner = { 0 },
+ .selection = {
+ {
+ .description = "0x220",
+ .value = 0x220
+ },
+ {
+ .description = "0x240",
+ .value = 0x240
+ },
+ {
+ .description = "0x260",
+ .value = 0x260
+ },
+ {
+ .description = "0x280",
+ .value = 0x280
+ },
+ { .description = "" }
+ }
+ },
+ {
+ .name = "irq",
+ .description = "IRQ",
+ .type = CONFIG_SELECTION,
+ .default_string = "",
+ .default_int = 5,
+ .file_filter = "",
+ .spinner = { 0 },
+ .selection = {
+ {
+ .description = "IRQ 2",
+ .value = 2
+ },
+ {
+ .description = "IRQ 5",
+ .value = 5
+ },
+ {
+ .description = "IRQ 7",
+ .value = 7
+ },
+ {
+ .description = "IRQ 10",
+ .value = 10
+ },
+ { .description = "" }
+ }
+ },
+ {
+ .name = "dma",
+ .description = "DMA",
+ .type = CONFIG_SELECTION,
+ .default_string = "",
+ .default_int = 1,
+ .file_filter = "",
+ .spinner = { 0 },
+ .selection = {
+ {
+ .description = "DMA 0",
+ .value = 0
+ },
+ {
+ .description = "DMA 1",
+ .value = 1
+ },
+ {
+ .description = "DMA 3",
+ .value = 3
+ },
+ { .description = "" }
+ }
+ },
+ {
+ .name = "opl",
+ .description = "Enable OPL",
+ .type = CONFIG_BINARY,
+ .default_string = "",
+ .default_int = 1
+ },
+ {
+ .name = "receive_input",
+ .description = "Receive input (SB MIDI)",
+ .type = CONFIG_BINARY,
+ .default_string = "",
+ .default_int = 1
+ },
+ { .name = "", .description = "", .type = CONFIG_END }
+};
// clang-format on
const device_t sb_1_device = {
@@ -4360,3 +4882,17 @@ const device_t sb_awe64_gold_device = {
.force_redraw = NULL,
.config = sb_awe64_gold_config
};
+
+const device_t ess_1688_device = {
+ .name = "ESS Technology ES1688",
+ .internal_name = "ess_es1688",
+ .flags = DEVICE_ISA,
+ .local = 0,
+ .init = ess_1688_init,
+ .close = sb_close,
+ .reset = NULL,
+ { .available = NULL },
+ .speed_changed = sb_speed_changed,
+ .force_redraw = NULL,
+ .config = ess_1688_config
+};