From 3485391905694ab647f9b44d551c94e0936bcb78 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Fri, 30 May 2025 21:28:13 -0400 Subject: [PATCH] ISA Covox's and clones --- src/include/86box/sound.h | 13 +- src/sound/CMakeLists.txt | 1 + src/sound/snd_adlib.c | 20 +- src/sound/snd_covox.c | 475 ++++++++++++++++++++++++++++++++++++++ src/sound/sound.c | 9 +- 5 files changed, 515 insertions(+), 3 deletions(-) create mode 100644 src/sound/snd_covox.c 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 },