diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h
index 985abe777..5f91ec9d0 100644
--- a/src/include/86box/sound.h
+++ b/src/include/86box/sound.h
@@ -12,9 +12,11 @@
*
* Authors: Sarah Walker,
* Miran Grca,
+ * Jasmine Iwanek,
*
* Copyright 2008-2018 Sarah Walker.
- * Copyright 2016-2018 Miran Grca.
+ * Copyright 2016-2025 Miran Grca.
+ * Copyright 2024-2025 Jasmine Iwanek.
*/
#ifndef EMU_SOUND_H
@@ -132,6 +134,12 @@ extern const device_t cmi8738_device;
extern const device_t cmi8738_onboard_device;
extern const device_t cmi8738_6ch_onboard_device;
+/* Covox ISA */
+extern const device_t voicemasterkey_device;
+extern const device_t soundmasterplus_device;
+extern const device_t isadacr0_device;
+extern const device_t isadacr1_device;
+
/* Creative Labs Game Blaster */
extern const device_t cms_device;
@@ -214,6 +222,9 @@ extern const device_t pasplus_device;
extern const device_t pas16_device;
extern const device_t pas16d_device;
+/* Rainbow Arts PC-Soundman */
+extern const device_t soundman_device;
+
/* Tandy PSSJ */
extern const device_t pssj_device;
extern const device_t pssj_isa_device;
diff --git a/src/sound/CMakeLists.txt b/src/sound/CMakeLists.txt
index 66a0ee4e3..9e2a75198 100644
--- a/src/sound/CMakeLists.txt
+++ b/src/sound/CMakeLists.txt
@@ -36,6 +36,7 @@ add_library(snd OBJECT
snd_azt2316a.c
snd_cms.c
snd_cmi8x38.c
+ snd_covox.c
snd_cs423x.c
snd_gus.c
snd_sb.c
diff --git a/src/sound/snd_adlib.c b/src/sound/snd_adlib.c
index 17990e842..b21a1f472 100644
--- a/src/sound/snd_adlib.c
+++ b/src/sound/snd_adlib.c
@@ -1,3 +1,21 @@
+/*
+ * 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.
+ *
+ * Adlib emulation.
+ *
+ * Authors: Sarah Walker,
+ * Miran Grca,
+ * Jasmine Iwanek,
+ *
+ * Copyright 2008-2018 Sarah Walker.
+ * Copyright 2016-2025 Miran Grca.
+ * Copyright 2024-2025 Jasmine Iwanek.
+ */
#include
#include
#include
@@ -33,7 +51,7 @@ adlib_log(const char *fmt, ...)
# define adlib_log(fmt, ...)
#endif
-typedef struct adlib_t {
+typedef struct adlib_s {
fm_drv_t opl;
uint8_t pos_regs[8];
diff --git a/src/sound/snd_covox.c b/src/sound/snd_covox.c
new file mode 100644
index 000000000..aaffbcf08
--- /dev/null
+++ b/src/sound/snd_covox.c
@@ -0,0 +1,475 @@
+/*
+ * 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.
+ *
+ * Rainbow Arts PC-Soundman Emulation
+ *
+ * Authors: Jasmine Iwanek,
+ *
+ * Copyright 2025 Jasmine Iwanek.
+ */
+#include
+#include
+#include
+#include
+#include
+#define HAVE_STDARG_H
+
+#include <86box/86box.h>
+#include <86box/device.h>
+#include <86box/io.h>
+#include <86box/mca.h>
+#include <86box/sound.h>
+#include <86box/filters.h>
+#include <86box/timer.h>
+#include <86box/snd_opl.h>
+#include <86box/plat_fallthrough.h>
+#include <86box/plat_unused.h>
+
+#define COVOX_SOUNDMAN 0
+#define COVOX_VOICEMASTERKEY 1
+#define COVOX_SOUNDMASTERPLUS 2
+#define COVOX_ISADACR0 3
+#define COVOX_ISADACR1 4
+
+#ifdef ENABLE_COVOX_LOG
+int covox_do_log = ENABLE_COVOX_LOG;
+
+static void
+covox_log(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (covox_do_log) {
+ va_start(ap, fmt);
+ pclog_ex(fmt, ap);
+ va_end(ap);
+ }
+}
+#else
+# define covox_log(fmt, ...)
+#endif
+
+typedef struct covox_s {
+ fm_drv_t opl;
+
+ uint8_t dac_val;
+
+ int16_t buffer[2][SOUNDBUFLEN];
+ int pos;
+} covox_t;
+
+// TODO: Can this be rolled into covox_get_buffer?
+static void
+covox_update(covox_t *covox)
+{
+ for (; covox->pos < sound_pos_global; covox->pos++) {
+ covox->buffer[0][covox->pos] = (int8_t) (covox->dac_val ^ 0x80) * 0x40;
+ covox->buffer[1][covox->pos] = (int8_t) (covox->dac_val ^ 0x80) * 0x40;
+ }
+}
+
+uint8_t
+covox_read(uint16_t addr, void *priv)
+{
+#if 0
+ const covox_t *covox = (covox_t *) priv;
+#endif
+
+ covox_log("covox_read: addr=%04x\n", addr);
+
+ return 0xff;
+}
+
+void
+covox_write(uint16_t addr, uint8_t val, void *priv)
+{
+ covox_t *covox = (covox_t *) priv;
+
+ covox_log("covox_write: addr=%04x val=%02x\n", addr, val);
+
+ switch (addr) {
+ case 0x221: // Soundman
+ case 0x229: // Soundman
+ case 0x22f: // Soundman, voicemasterkey
+ case 0x231: // isadac-r1?
+ case 0x24f: // voicemasterkey
+ case 0x279: // isadac-r0 (lPT2)
+ case 0x28f: // voicemasterkey
+ case 0x2cf: // voicemasterkey
+ case 0x301: // Soundman
+ case 0x309: // Soundman
+ case 0x30f: // soundman
+ case 0x331: // soundmasterplus
+ case 0x339: // soundmasterplus
+ case 0x371: // isadac-r0
+ case 0x379: // isadac-r0 (lPT1)
+ case 0x381: // isadac-r0
+ case 0x3bd: // isadac-r0 (lPT1-Mono)
+ covox->dac_val = val;
+ // TODO: Is this needed here?
+ covox_update(covox);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+covox_get_buffer(int32_t *buffer, int len, void *priv)
+{
+ covox_t *covox = (covox_t *) priv;
+
+ covox_update(covox);
+
+ for (int c = 0; c < len; c++) {
+ buffer[c * 2] += dac_iir(0, covox->buffer[0][c]);
+ buffer[c * 2 + 1] += dac_iir(1, covox->buffer[1][c]);
+ }
+ covox->pos = 0;
+}
+
+static void
+covox_get_music_buffer(int32_t *buffer, int len, void *priv)
+{
+ covox_t *covox = (covox_t *) priv;
+
+ const int32_t *opl_buf = covox->opl.update(covox->opl.priv);
+
+ for (int c = 0; c < len * 2; c++)
+ buffer[c] += opl_buf[c];
+
+ if (covox->opl.reset_buffer)
+ covox->opl.reset_buffer(covox->opl.priv);
+}
+
+#define IO_SETHANDLER_COVOX_DAC(addr, len) \
+ io_sethandler((addr), (len), \
+ covox_read, NULL, NULL, \
+ covox_write, NULL, NULL, \
+ covox)
+
+#define IO_SETHANDLER_COVOX_ADLIB(addr, len) \
+ io_sethandler((addr), (len), \
+ covox->opl.read, NULL, NULL, \
+ covox->opl.write, NULL, NULL, \
+ covox->opl.priv)
+
+void *
+covox_init(UNUSED(const device_t *info))
+{
+ covox_t *covox = calloc(1, sizeof(covox_t));
+ uint8_t has_adlib = 0;
+ uint8_t has_stereo = 0;
+ uint8_t fixed_address = 0;
+ if (!covox)
+ return NULL;
+
+ covox_log("covox_init\n");
+ switch (info->local) {
+ case COVOX_SOUNDMAN:
+ fixed_address = 1;
+ fallthrough;
+ case COVOX_SOUNDMASTERPLUS:
+ has_adlib = 1;
+ break;
+
+ case COVOX_ISADACR0:
+ has_stereo = 1;
+ break;
+
+ case COVOX_ISADACR1:
+ has_stereo = 2;
+ break;
+
+ default:
+ break;
+ }
+
+ if (fixed_address) {
+ IO_SETHANDLER_COVOX_DAC(0x220, 0x0002);
+ IO_SETHANDLER_COVOX_DAC(0x228, 0x0002);
+ IO_SETHANDLER_COVOX_DAC(0x22e, 0x0002);
+#if 0
+ // According to vgmpf, this is the address
+ IO_SETHANDLER_COVOX_DAC(0x22f, 0x0001);
+#endif
+ IO_SETHANDLER_COVOX_DAC(0x300, 0x0002);
+ IO_SETHANDLER_COVOX_DAC(0x308, 0x0002);
+ IO_SETHANDLER_COVOX_DAC(0x30e, 0x0002);
+ } else {
+ IO_SETHANDLER_COVOX_DAC(device_get_config_hex16("base"), 0x0002);
+
+ // TODO: Needs more work
+ if (has_stereo)
+ IO_SETHANDLER_COVOX_DAC(device_get_config_hex16("base2"), 0x0002);
+ }
+ sound_add_handler(covox_get_buffer, covox);
+
+ if (has_adlib) {
+ fm_driver_get(FM_YM3812, &covox->opl);
+ if (fixed_address) {
+ // Adlib Clone part
+ IO_SETHANDLER_COVOX_ADLIB(0x380, 0x0002);
+ IO_SETHANDLER_COVOX_ADLIB(0x388, 0x0002);
+ IO_SETHANDLER_COVOX_ADLIB(0x38e, 0x0002);
+ } else
+ IO_SETHANDLER_COVOX_ADLIB(device_get_config_hex16("adlibbase"), 0x0002);
+
+ music_add_handler(covox_get_music_buffer, covox);
+ }
+
+ return covox;
+}
+
+void
+covox_close(void *priv)
+{
+ covox_t *covox = (covox_t *) priv;
+
+ if (covox)
+ free(covox);
+}
+
+// clang-format off
+static const device_config_t voicemasterkey_config[] = {
+ {
+ .name = "base",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x388,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x22f", .value = 0x22f },
+ { .description = "0x24f", .value = 0x24f },
+ { .description = "0x28f", .value = 0x28f },
+ { .description = "0x2cf", .value = 0x2cf },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ { .name = "", .description = "", .type = CONFIG_END }
+};
+// clang-format on
+
+// Note: We don't support sound input on this yet
+const device_t voicemasterkey_device = {
+ .name = "Covox Voice Master Key",
+ .internal_name = "voicemasterkey",
+ .flags = DEVICE_ISA | DEVICE_SIDECAR,
+ .local = COVOX_VOICEMASTERKEY,
+ .init = covox_init,
+ .close = covox_close,
+ .reset = NULL,
+ .available = NULL,
+ .speed_changed = NULL,
+ .force_redraw = NULL,
+ .config = voicemasterkey_config
+};
+
+// clang-format off
+static const device_config_t soundmasterplus_config[] = {
+ {
+ .name = "base",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x330,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x330", .value = 0x330 },
+ { .description = "0x338", .value = 0x338 },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ {
+ .name = "adlibbase",
+ .description = "Adlib Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x388,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x388", .value = 0x388 },
+ { .description = "0x380", .value = 0x380 },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ { .name = "", .description = "", .type = CONFIG_END }
+};
+// clang-format on
+
+const device_t soundmasterplus_device = {
+ .name = "Covox Sound Master Plus",
+ .internal_name = "soundmasterplus",
+ .flags = DEVICE_ISA | DEVICE_SIDECAR,
+ .local = COVOX_SOUNDMASTERPLUS,
+ .init = covox_init,
+ .close = covox_close,
+ .reset = NULL,
+ .available = NULL,
+ .speed_changed = NULL,
+ .force_redraw = NULL,
+ .config = soundmasterplus_config
+};
+
+// clang-format off
+static const device_config_t isadacr0_config[] = {
+ {
+ .name = "base",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x380,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x220", .value = 0x220 },
+ { .description = "0x228", .value = 0x228 },
+ { .description = "0x22e", .value = 0x22e },
+ { .description = "0x230", .value = 0x230 },
+ { .description = "0x24e", .value = 0x24e },
+ { .description = "0x278", .value = 0x278 },
+ { .description = "0x28e", .value = 0x28e },
+ { .description = "0x2ce", .value = 0x2ce },
+ { .description = "0x300", .value = 0x300 },
+ { .description = "0x308", .value = 0x308 },
+ { .description = "0x303", .value = 0x30e },
+ { .description = "0x330", .value = 0x330 },
+ { .description = "0x338", .value = 0x338 },
+ { .description = "0x370", .value = 0x370 },
+ { .description = "0x378", .value = 0x378 },
+ { .description = "0x380", .value = 0x380 },
+ { .description = "0x3bc", .value = 0x3bc },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ {
+ .name = "base2",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x370,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x220", .value = 0x220 },
+ { .description = "0x228", .value = 0x228 },
+ { .description = "0x22e", .value = 0x22e },
+ { .description = "0x230", .value = 0x230 },
+ { .description = "0x24e", .value = 0x24e },
+ { .description = "0x278", .value = 0x278 },
+ { .description = "0x28e", .value = 0x28e },
+ { .description = "0x2ce", .value = 0x2ce },
+ { .description = "0x300", .value = 0x300 },
+ { .description = "0x308", .value = 0x308 },
+ { .description = "0x303", .value = 0x30e },
+ { .description = "0x330", .value = 0x330 },
+ { .description = "0x338", .value = 0x338 },
+ { .description = "0x370", .value = 0x370 },
+ { .description = "0x378", .value = 0x378 },
+ { .description = "0x380", .value = 0x380 },
+ { .description = "0x3bc", .value = 0x3bc },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ { .name = "", .description = "", .type = CONFIG_END }
+};
+// clang-format on
+
+// Note: We don't support stereo on this yet
+const device_t isadacr0_device = {
+ .name = "ISA DAC-r0",
+ .internal_name = "isadacr0",
+ .flags = DEVICE_ISA | DEVICE_SIDECAR,
+ .local = COVOX_ISADACR0,
+ .init = covox_init,
+ .close = covox_close,
+ .reset = NULL,
+ .available = NULL,
+ .speed_changed = NULL,
+ .force_redraw = NULL,
+ .config = isadacr0_config
+};
+
+// clang-format off
+static const device_config_t isadacr1_config[] = {
+ {
+ .name = "base",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x378,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x378", .value = 0x378 },
+ { .description = "0x3bc", .value = 0x3bc },
+ { .description = "0x278", .value = 0x278 },
+ { .description = "0x230", .value = 0x230 },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ {
+ .name = "base2",
+ .description = "Address",
+ .type = CONFIG_HEX16,
+ .default_string = NULL,
+ .default_int = 0x278,
+ .file_filter = NULL,
+ .spinner = { 0 },
+ .selection = {
+ { .description = "0x378", .value = 0x378 },
+ { .description = "0x3bc", .value = 0x3bc },
+ { .description = "0x278", .value = 0x278 },
+ { .description = "0x230", .value = 0x230 },
+ { .description = "" }
+ },
+ .bios = { { 0 } }
+ },
+ { .name = "", .description = "", .type = CONFIG_END }
+};
+// clang-format on
+
+// Note: We don't support stereo on this yet
+const device_t isadacr1_device = {
+ .name = "ISA DAC-r1",
+ .internal_name = "isadacr1",
+ .flags = DEVICE_ISA | DEVICE_SIDECAR,
+ .local = COVOX_ISADACR1,
+ .init = covox_init,
+ .close = covox_close,
+ .reset = NULL,
+ .available = NULL,
+ .speed_changed = NULL,
+ .force_redraw = NULL,
+ .config = isadacr1_config
+};
+
+const device_t soundman_device = {
+ .name = "Rainbow Arts PC-Soundman",
+ .internal_name = "soundman",
+ .flags = DEVICE_ISA | DEVICE_SIDECAR,
+ .local = COVOX_SOUNDMAN,
+ .init = covox_init,
+ .close = covox_close,
+ .reset = NULL,
+ .available = NULL,
+ .speed_changed = NULL,
+ .force_redraw = NULL,
+ .config = NULL
+};
diff --git a/src/sound/sound.c b/src/sound/sound.c
index 615df88bc..c81dc47b0 100644
--- a/src/sound/sound.c
+++ b/src/sound/sound.c
@@ -12,9 +12,11 @@
*
* Authors: Sarah Walker,
* Miran Grca,
+ * Jasmine Iwanek,
*
* Copyright 2008-2020 Sarah Walker.
- * Copyright 2016-2020 Miran Grca.
+ * Copyright 2016-2025 Miran Grca.
+ * Copyright 2024-2025 Jasmine Iwanek.
*/
#include
#include
@@ -111,6 +113,11 @@ static const SOUND_CARD sound_cards[] = {
{ &ssi2001_device },
{ &mmb_device },
{ &pasplus_device },
+ { &voicemasterkey_device },
+ { &soundmasterplus_device },
+ { &soundman_device },
+ { &isadacr0_device },
+ { &isadacr1_device },
{ &sb_1_device },
{ &sb_15_device },
{ &sb_2_device },