From 57b72c8a5c1d644cab6b32dd53f828004b373008 Mon Sep 17 00:00:00 2001 From: win2kgamer <47463859+win2kgamer@users.noreply.github.com> Date: Sun, 12 Oct 2025 00:08:02 -0500 Subject: [PATCH] Aztech AZT1605 (Clinton/Nova 16 Extra) fixes (#6325) * Add missing cpu.h include to fix compile errors when logging is enabled * SB DSP: Move Aztech variable command length handling to correct if block, fixes EEPROM writing hang in MIXTSR * SB DSP: Increment RP after unknown Aztech command 0x08 subcommand 0x01 is run, fixes HWSET detection after EMUTSR is run * Aztech: Give AZT1605 its own read function and only allocate I/O ports in the 62x/64x range * Aztech: Add logging code * AZT1605: Implement the SBPro mixer readout ports, fixes scrambled mixer settings in MIXTSR and HWSET * AZT1605: Restore WSS mixer from EEPROM during init and use sane initial EEPROM mixer values * Aztech: Always use AUX1 WSS channel for CD audio on AZT1605, fixes CD volume control on Win3.1 drivers * AZT1605: Filter OPL3 through WSS AUX2 mixer, fixes FM volume control on Win3.1 drivers --- src/sound/snd_azt2316a.c | 178 ++++++++++++++++++++++++++++++++++----- src/sound/snd_sb_dsp.c | 21 +++-- 2 files changed, 171 insertions(+), 28 deletions(-) diff --git a/src/sound/snd_azt2316a.c b/src/sound/snd_azt2316a.c index 65d10532f..1f88db719 100644 --- a/src/sound/snd_azt2316a.c +++ b/src/sound/snd_azt2316a.c @@ -151,6 +151,24 @@ #include <86box/snd_azt2316a.h> #include <86box/snd_sb.h> #include <86box/plat_unused.h> +#include <86box/log.h> + +#ifdef ENABLE_AZTECH_LOG +int aztech_do_log = ENABLE_AZTECH_LOG; + +static void +aztech_log(void *priv, const char *fmt, ...) +{ + if (aztech_log) { + va_list ap; + va_start(ap, fmt); + log_out(priv, fmt, ap); + va_end(ap); + } +} +#else +# define aztech_log(fmt, ...) +#endif /*530, 11, 3 - 530=23*/ /*530, 11, 1 - 530=22*/ @@ -195,8 +213,18 @@ typedef struct azt2316a_t { mpu_t *mpu; sb_t *sb; + + void * log; /* New logging system */ } azt2316a_t; +static void +azt1605_filter_opl(void *priv, double *out_l, double *out_r) +{ + azt2316a_t *azt2316a = (azt2316a_t *) priv; + + ad1848_filter_channel((void *) &azt2316a->ad1848, AD1848_AUX2, out_l, out_r); +} + static uint8_t azt2316a_wss_read(uint16_t addr, void *priv) { @@ -211,15 +239,18 @@ azt2316a_wss_read(uint16_t addr, void *priv) else temp = 4 | (azt2316a->wss_config & 0xC0); + aztech_log(azt2316a->log, "Aztech WSS: [R] (%04X) = %02X\n", addr, temp); return temp; } static void -azt2316a_wss_write(UNUSED(uint16_t addr), uint8_t val, void *priv) +azt2316a_wss_write(uint16_t addr, uint8_t val, void *priv) { azt2316a_t *azt2316a = (azt2316a_t *) priv; int interrupt = 0; + aztech_log(azt2316a->log, "Aztech WSS: [W] (%04X) = %02X\n", addr, val); + if (azt2316a->wss_interrupt_after_config) { if ((azt2316a->wss_config & 0x40) && !(val & 0x40)) { // TODO: is this the right edge? interrupt = 1; @@ -421,6 +452,7 @@ azt1605_create_config_word(void *priv) } azt2316a->config_word = temp; + aztech_log(azt2316a->log, "Aztech 1605 Config Word Create: %08X\n", temp); } static void @@ -676,6 +708,70 @@ azt2316a_create_config_word(void *priv) } azt2316a->config_word = temp; + aztech_log(azt2316a->log, "Aztech 2316 Config Word Create: %08X\n", temp); +} + +static uint8_t +azt1605_config_read(uint16_t addr, void *priv) +{ + const azt2316a_t *azt2316a = (azt2316a_t *) priv; + uint8_t temp = 0; + + /* Some WSS config here + config change enable bit + (setting bit 7 and writing back) */ + + if (addr == (azt2316a->cur_addr + 0x404)) { + /* TODO: what is the real meaning of the read value? + I got a mention of bit 0x10 for WSS from disassembling the source + code of the driver, and when playing with the I/O ports on real + hardware after doing some configuration, but didn't dig into it. + Bit 0x08 seems to be a busy flag and generates a timeout + (continuous re-reading when initializing windows 98) */ + temp = azt2316a->cur_mode ? 0x07 : 0x0F; + if (azt2316a->config_word_unlocked) { + temp |= 0x80; + } + } else { + /* Rest of config. Bytes 0x00-0x03 are documented in the Linux driver. */ + /* 0x07 causes EMUTSR to resync mixers when nonzero values are written but the mechanism is unknown */ + /* 0x08-0x0F mixer registers are not documented */ + switch (addr & 0x0f) { + case 0x00: + temp = azt2316a->config_word & 0xFF; + break; + case 0x01: + temp = (azt2316a->config_word >> 8); + break; + case 0x02: + temp = (azt2316a->config_word >> 16); + break; + case 0x03: + temp = (azt2316a->config_word >> 24); + break; + case 0x08: /* SBPro Voice mixer readout */ + temp = azt2316a->sb->mixer_sbpro.regs[0x04]; + break; + case 0x09: /* SBPro Mic mixer readout */ + temp = azt2316a->sb->mixer_sbpro.regs[0x0A]; + break; + case 0x0C: /* SBPro Master mixer readout */ + temp = azt2316a->sb->mixer_sbpro.regs[0x22]; + break; + case 0x0D: /* SBPro FM mixer readout */ + temp = azt2316a->sb->mixer_sbpro.regs[0x26]; + break; + case 0x0E: /* SBPro CD mixer readout */ + temp = azt2316a->sb->mixer_sbpro.regs[0x28]; + break; + default: + temp = 0x00; + break; + } + } + + aztech_log(azt2316a->log, "Aztech 1605 Config Word Read: (%04X) = %02X\n", addr, temp); + + return temp; } static uint8_t @@ -719,6 +815,8 @@ azt2316a_config_read(uint16_t addr, void *priv) } } + aztech_log(azt2316a->log, "Aztech 2316 Config Word Read: (%04X) = %02X\n", addr, temp); + return temp; } @@ -728,6 +826,8 @@ azt1605_config_write(uint16_t addr, uint8_t val, void *priv) azt2316a_t *azt2316a = (azt2316a_t *) priv; uint8_t temp; + aztech_log(azt2316a->log, "Aztech 1605 Config Word Write: (%04X) = %02X\n", addr, val); + if (addr == (azt2316a->cur_addr + 0x404)) { if (val & 0x80) azt2316a->config_word_unlocked = 1; @@ -832,10 +932,7 @@ azt2316a_config_write(uint16_t addr, uint8_t val, void *priv) azt2316a_t *azt2316a = (azt2316a_t *) priv; uint8_t temp; - if (azt2316a->type == SB_SUBTYPE_CLONE_AZT1605_0X0C) { - azt1605_config_write(addr, val, azt2316a); - return; - } + aztech_log(azt2316a->log, "Aztech 2316 Config Word Write: (%04X) = %02X\n", addr, val); if (addr == (azt2316a->cur_addr + 0x404)) { if (val & 0x80) @@ -978,6 +1075,8 @@ azt_init(const device_t *info) azt2316a->type = info->local; + azt2316a->log = log_open("AztechWSS"); + if (azt2316a->type == SB_SUBTYPE_CLONE_AZT1605_0X0C) { fn = "azt1605.nvr"; } else if (azt2316a->type == SB_SUBTYPE_CLONE_AZT2316A_0X11) { @@ -1023,22 +1122,22 @@ azt_init(const device_t *info) read_eeprom[14] = 0x01; read_eeprom[15] = 0x00; } else if (azt2316a->type == SB_SUBTYPE_CLONE_AZT1605_0X0C) { - read_eeprom[0] = 0x80; - read_eeprom[1] = 0x80; - read_eeprom[2] = 0x9F; - read_eeprom[3] = 0x13; - read_eeprom[4] = 0x16; - read_eeprom[5] = 0x13; - read_eeprom[6] = 0x00; - read_eeprom[7] = 0x00; - read_eeprom[8] = 0x16; - read_eeprom[9] = 0x0B; - read_eeprom[10] = 0x06; + read_eeprom[0] = 0x80; /* WSS ADC L mixer value */ + read_eeprom[1] = 0x80; /* WSS ADC R mixer value */ + read_eeprom[2] = 0x08; /* WSS AUX1 L mixer value */ + read_eeprom[3] = 0x08; /* WSS AUX1 R mixer value */ + read_eeprom[4] = 0x08; /* WSS AUX2 L mixer value */ + read_eeprom[5] = 0x08; /* WSS AUX2 R mixer value */ + read_eeprom[6] = 0x08; /* WSS DAC L mixer value */ + read_eeprom[7] = 0x08; /* WSS DAC R mixer value */ + read_eeprom[8] = 0x08; /* WSS LINE L mixer value (CS4231) */ + read_eeprom[9] = 0x08; /* WSS LINE R mixer value (CS4231) */ + read_eeprom[10] = 0x80; /* WSS MIC mixer value (CS4231) */ read_eeprom[11] = 0x01; read_eeprom[12] = 0x1C; read_eeprom[13] = 0x14; read_eeprom[14] = 0x04; - read_eeprom[15] = 0x1C; + read_eeprom[15] = 0xFF; /* SBPro Master volume (EMUTSR) */ } } @@ -1211,12 +1310,18 @@ azt_init(const device_t *info) /* wss part */ ad1848_init(&azt2316a->ad1848, device_get_config_int("codec")); - ad1848_set_cd_audio_channel(&azt2316a->ad1848, (device_get_config_int("codec") == AD1848_TYPE_CS4248) ? AD1848_AUX1 : AD1848_LINE_IN); + if (azt2316a->type == SB_SUBTYPE_CLONE_AZT2316A_0X11) + ad1848_set_cd_audio_channel(&azt2316a->ad1848, (device_get_config_int("codec") == AD1848_TYPE_CS4248) ? AD1848_AUX1 : AD1848_LINE_IN); + else + ad1848_set_cd_audio_channel(&azt2316a->ad1848, AD1848_AUX1); ad1848_setirq(&azt2316a->ad1848, azt2316a->cur_wss_irq); ad1848_setdma(&azt2316a->ad1848, azt2316a->cur_wss_dma); - io_sethandler(azt2316a->cur_addr + 0x0400, 0x0040, azt2316a_config_read, NULL, NULL, azt2316a_config_write, NULL, NULL, azt2316a); + if (azt2316a->type == SB_SUBTYPE_CLONE_AZT2316A_0X11) + io_sethandler(azt2316a->cur_addr + 0x0400, 0x0040, azt2316a_config_read, NULL, NULL, azt2316a_config_write, NULL, NULL, azt2316a); + else /* Aztech 1605 only needs 62x/64x */ + io_sethandler(azt2316a->cur_addr + 0x0400, 0x0010, azt1605_config_read, NULL, NULL, azt1605_config_write, NULL, NULL, azt2316a); io_sethandler(azt2316a->cur_wss_addr, 0x0004, azt2316a_wss_read, NULL, NULL, azt2316a_wss_write, NULL, NULL, azt2316a); io_sethandler(azt2316a->cur_wss_addr + 0x0004, 0x0004, ad1848_read, NULL, NULL, ad1848_write, NULL, NULL, &azt2316a->ad1848); @@ -1253,8 +1358,18 @@ azt_init(const device_t *info) azt2316a_create_config_word(azt2316a); sound_add_handler(azt2316a_get_buffer, azt2316a); - if (azt2316a->sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sbpro, azt2316a->sb); + + if (azt2316a->type == SB_SUBTYPE_CLONE_AZT2316A_0X11) { + if (azt2316a->sb->opl_enabled) + music_add_handler(sb_get_music_buffer_sbpro, azt2316a->sb); + } + else { + if (azt2316a->sb->opl_enabled) { + azt2316a->sb->opl_mixer = azt2316a; + azt2316a->sb->opl_mix = azt1605_filter_opl; + music_add_handler(sb_get_music_buffer_sbpro, azt2316a->sb); + } + } sound_set_cd_audio_filter(sbpro_filter_cd_audio, azt2316a->sb); if (azt2316a->cur_mpu401_enabled) { @@ -1266,6 +1381,21 @@ azt_init(const device_t *info) if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &azt2316a->sb->dsp); + /* Restore WSS mixer settings from EEPROM on AZT1605 cards */ + if (azt2316a->type == SB_SUBTYPE_CLONE_AZT1605_0X0C) { + azt2316a->ad1848.regs[0] = read_eeprom[0]; /* WSS ADC L */ + azt2316a->ad1848.regs[1] = read_eeprom[1]; /* WSS ADC R */ + azt2316a->ad1848.regs[2] = read_eeprom[2]; /* WSS AUX1/CD L */ + azt2316a->ad1848.regs[3] = read_eeprom[3]; /* WSS AUX1/CD R */ + azt2316a->ad1848.regs[4] = read_eeprom[4]; /* WSS AUX2/FM L */ + azt2316a->ad1848.regs[5] = read_eeprom[5]; /* WSS AUX2/FM R */ + azt2316a->ad1848.regs[6] = read_eeprom[6]; /* WSS DAC L */ + azt2316a->ad1848.regs[7] = read_eeprom[7]; /* WSS DAC R */ + azt2316a->ad1848.regs[18] = read_eeprom[8]; /* CS4231 LINE/SB Voice L */ + azt2316a->ad1848.regs[19] = read_eeprom[9]; /* CS4231 LINE/SB Voice R */ + azt2316a->ad1848.regs[26] = read_eeprom[10]; /* CS4231 Mic */ + } + return azt2316a; } @@ -1301,6 +1431,12 @@ azt_close(void *priv) sb_close(azt2316a->sb); free(azt2316a->mpu); + + if (azt2316a->log != NULL) { + log_close(azt2316a->log); + azt2316a->log = NULL; + } + free(azt2316a); } diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 80f6c9555..890395805 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -28,6 +28,7 @@ #include <86box/snd_sb.h> #include <86box/plat_fallthrough.h> #include <86box/plat_unused.h> +#include "cpu.h" /* NON-PCM SAMPLE FORMATS */ #define ADPCM_4 1 @@ -1300,6 +1301,12 @@ sb_exec_command(sb_dsp_t *dsp) sb_dsp_log("EEPROM read = %02x\n", dsp->azt_eeprom[dsp->sb_data[1]]); sb_add_data(dsp, dsp->azt_eeprom[dsp->sb_data[1]]); break; + } else if ((dsp->sb_data[0] == 0x01) && (dsp->sb_subtype == SB_SUBTYPE_CLONE_AZT1605_0X0C)) { + /* Unknown command executed by EMUTSR after DSP reset */ + sb_dsp_log("AZT1605: Command 0x08 Subcommand 0x01\n"); + /* HACK: Aztech HWSET seems to rely on RP being incremented for detection to work after EMUTSR is run */ + dsp->sb_read_rp++; + break; } else sb_dsp_log("AZT2316A: UNKNOWN 0x08 COMMAND: %02X\n", dsp->sb_data[0]); /* 0x08 (when shutting down, driver tries to read 1 byte of response), 0x55, 0x0D, 0x08D seen */ break; @@ -1919,13 +1926,6 @@ sb_write(uint16_t addr, uint8_t val, void *priv) if (val == 0x01) sb_add_data(dsp, 0); dsp->sb_data_stat++; - if (IS_AZTECH(dsp)) { - /* variable length commands */ - if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x08) - sb_commands[dsp->sb_command] = 3; - else if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x07) - sb_commands[dsp->sb_command] = 2; - } if (IS_ESS(dsp) && dsp->sb_command >= 0x64 && dsp->sb_command <= 0x6F) { sb_commands[dsp->sb_command] = 2; } else if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) { @@ -1944,6 +1944,13 @@ sb_write(uint16_t addr, uint8_t val, void *priv) } } else { dsp->sb_data[dsp->sb_data_stat++] = val; + if (IS_AZTECH(dsp)) { + /* variable length commands */ + if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x08) + sb_commands[dsp->sb_command] = 3; + else if (dsp->sb_command == 0x08 && dsp->sb_data_stat == 1 && dsp->sb_data[0] == 0x07) + sb_commands[dsp->sb_command] = 2; + } } if (dsp->sb_data_stat == sb_commands[dsp->sb_command] || sb_commands[dsp->sb_command] == -1) { sb_exec_command(dsp);