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
This commit is contained in:
win2kgamer
2025-10-12 00:08:02 -05:00
committed by GitHub
parent 74678a391b
commit 57b72c8a5c
2 changed files with 171 additions and 28 deletions

View File

@@ -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);
}

View File

@@ -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);