Finish DSP part of ESS

This commit is contained in:
Cacodemon345
2024-03-03 16:02:56 +06:00
committed by Kagamiin~
parent eda528d98c
commit e7e582cd74
4 changed files with 454 additions and 26 deletions

View File

@@ -143,6 +143,7 @@ typedef struct sb_dsp_t {
uint8_t ess_regs[256]; /* ESS registers. */
uint8_t ess_playback_mode;
uint8_t ess_extended_mode;
uint8_t ess_reload_len;
mpu_t *mpu;
} sb_dsp_t;

View File

@@ -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_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c snd_ess.c)
if(OPENAL)
if(VCPKG_TOOLCHAIN)

258
src/sound/snd_ess.c Normal file
View File

@@ -0,0 +1,258 @@
/*
* 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.
*
* Sound Blaster emulation.
*
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* TheCollector1995, <mariogplayer@gmail.com>
*
* Copyright 2008-2020 Sarah Walker.
* Copyright 2016-2020 Miran Grca.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#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>
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
};
/* 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;
/*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];
} ess_mixer_t;
typedef struct ess_t {
uint8_t mixer_enabled;
fm_drv_t opl;
sb_dsp_t dsp;
union {
ess_mixer_t mixer_sbpro;
};
mpu_t *mpu;
emu8k_t emu8k;
void *gameport;
int pnp;
uint8_t pos_regs[8];
uint8_t pnp_rom[512];
uint16_t opl_pnp_addr;
uint16_t gameport_addr;
void *opl_mixer;
void (*opl_mix)(void*, double*, double*);
} ess_t;
static inline uint8_t expand16to32(const uint8_t t) {
/* 4-bit -> 5-bit expansion.
*
* 0 -> 0
* 1 -> 2
* 2 -> 4
* 3 -> 6
* ....
* 7 -> 14
* 8 -> 17
* 9 -> 19
* 10 -> 21
* 11 -> 23
* ....
* 15 -> 31 */
return (t << 1) | (t >> 3);
}
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;
} 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;
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 0x22:
case 0x26:
case 0x28:
mixer->regs[mixer->index - 0x20] = (val & 0xe);
break;
/* More compatibility:
SoundBlaster Pro selects register 020h for 030h, 022h for 032h,
026h for 036h, and 028h for 038h. */
case 0x30:
case 0x32:
case 0x36:
case 0x38:
mixer->regs[mixer->index - 0x10] = (val & 0xee);
break;
case 0x00:
case 0x04:
case 0x0a:
case 0x0c:
case 0x0e:
case 0x2e:
break;
default:
//sb_log("ess: Unknown register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
break;
}
}
mixer->voice_l = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 5) & 0x7] / 32768.0;
mixer->voice_r = sb_att_4dbstep_3bits[(mixer->regs[0x04] >> 1) & 0x7] / 32768.0;
mixer->master_l = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 5) & 0x7] / 32768.0;
mixer->master_r = sb_att_4dbstep_3bits[(mixer->regs[0x22] >> 1) & 0x7] / 32768.0;
mixer->fm_l = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 5) & 0x7] / 32768.0;
mixer->fm_r = sb_att_4dbstep_3bits[(mixer->regs[0x26] >> 1) & 0x7] / 32768.0;
mixer->cd_l = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 5) & 0x7] / 32768.0;
mixer->cd_r = sb_att_4dbstep_3bits[(mixer->regs[0x28] >> 1) & 0x7] / 32768.0;
mixer->line_l = sb_att_4dbstep_3bits[(mixer->regs[0x2e] >> 5) & 0x7] / 32768.0;
mixer->line_r = sb_att_4dbstep_3bits[(mixer->regs[0x2e] >> 1) & 0x7] / 32768.0;
mixer->mic = sb_att_7dbstep_2bits[(mixer->regs[0x0a] >> 1) & 0x3] / 32768.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);
switch (mixer->regs[0xc] & 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;
}
/* TODO: pcspeaker volume? Or is it not worth? */
}
}
uint8_t
ess_mixer_read(uint16_t addr, void *priv)
{
const ess_t *ess = (ess_t *) priv;
const 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 0x22:
case 0x26:
case 0x28:
case 0x2e:
case 0x02:
case 0x06:
case 0x30:
case 0x32:
case 0x36:
case 0x38:
return mixer->regs[mixer->index];
default:
//sb_log("ess: Unknown register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
break;
}
return 0xff;
}
void
ess_mixer_reset(ess_t *ess)
{
ess_mixer_write(4, 0, ess);
ess_mixer_write(5, 0, ess);
}

View File

@@ -6,6 +6,7 @@
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -418,30 +419,65 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len)
memset(dsp->record_buffer, 0, sizeof(dsp->record_buffer));
}
void
sb_start_dma_ess(sb_dsp_t* dsp, int dma8, int autoinit, uint8_t format, int len)
static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp)
{
unsigned int r;
r = (unsigned int)ESSreg(0xA5) << 8U;
r |= (unsigned int)ESSreg(0xA4);
/* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */
return 0x10000U-r;
}
void
sb_start_dma_ess(sb_dsp_t* dsp)
{
uint8_t real_format = 0;
uint32_t len = !(ESSreg(0xB7) & 4) ? dsp->sb_8_length : dsp->sb_16_length;
if (!dsp->ess_reload_len) {
len = sb_ess_get_dma_len(dsp);
}
if (IS_ESS(dsp)) {
dma_set_drq(dsp->sb_8_dmanum, 0);
dma_set_drq(dsp->sb_16_8_dmanum, 0);
}
sb_start_dma(dsp, dma8, autoinit, format, len);
real_format |= !!(ESSreg(0xB7) & 0x20) ? 0x10 : 0;
real_format |= !!(ESSreg(0xB7) & 0x8) ? 0x20 : 0;
if (!!(ESSreg(0xB8) & 8))
sb_start_dma_i(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, sb_ess_get_dma_len(dsp));
else
sb_start_dma(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, sb_ess_get_dma_len(dsp));
dsp->ess_playback_mode = 1;
dma_set_drq(dsp->sb_8_dmanum, 1);
dma_set_drq(dsp->sb_16_8_dmanum, 1);
}
void
sb_start_dma_ess_i(sb_dsp_t* dsp, int dma8, int autoinit, uint8_t format, int len)
sb_stop_dma_ess(sb_dsp_t* dsp)
{
if (IS_ESS(dsp)) {
dma_set_drq(dsp->sb_8_dmanum, 0);
dma_set_drq(dsp->sb_16_8_dmanum, 0);
dsp->sb_8_enable = dsp->sb_16_enable = 0;
dma_set_drq(dsp->sb_16_8_dmanum, 0);
dma_set_drq(dsp->sb_8_dmanum, 0);
}
static void sb_ess_update_dma_status(sb_dsp_t* dsp)
{
bool dma_en = (ESSreg(0xB8) & 1)?true:false;
// if the DRQ is disabled, do not start
if (!(ESSreg(0xB2) & 0x40))
dma_en = false;
if (dma_en) {
if (!dsp->sb_8_enable && !dsp->sb_16_enable) sb_start_dma_ess(dsp);
}
else {
if (dsp->sb_8_enable || dsp->sb_16_enable) sb_stop_dma_ess(dsp);
}
sb_start_dma_i(dsp, dma8, autoinit, format, len);
dsp->ess_playback_mode = 1;
dma_set_drq(dsp->sb_8_dmanum, 1);
dma_set_drq(dsp->sb_16_8_dmanum, 1);
}
int
@@ -598,17 +634,6 @@ static void sb_ess_update_filter_freq(sb_dsp_t *dsp)
ESSreg(0xA2) = 256 - (7160000 / (freq * 82));
}
static unsigned int sb_ess_get_dma_len(sb_dsp_t *dsp)
{
unsigned int r;
r = (unsigned int)ESSreg(0xA5) << 8U;
r |= (unsigned int)ESSreg(0xA4);
/* the 16-bit counter is a "two's complement" of the DMA count because it counts UP to 0 and triggers IRQ on overflow */
return 0x10000U-r;
}
static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg)
{
switch (reg) {
@@ -619,6 +644,120 @@ static uint8_t sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg)
return 0xFF;
}
static void sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data)
{
uint8_t chg = 0x00;
sb_dsp_log("ESS register write reg=%02xh val=%02xh\n",reg,data);
switch (reg) {
case 0xA1: /* Extended Mode Sample Rate Generator */
{
ESSreg(reg) = data;
if (data & 0x80)
dsp->sb_freq = 795500UL / (256ul - data);
else
dsp->sb_freq = 397700UL / (128ul - data);
if (dsp->sb_16_enable || dsp->sb_8_enable) {
sb_stop_dma_ess(dsp);
sb_start_dma_ess(dsp);
}
break;
}
case 0xA2: /* Filter divider (effectively, a hardware lowpass filter under S/W control) */
ESSreg(reg) = data;
break;
case 0xA4: /* DMA Transfer Count Reload (low) */
case 0xA5: /* DMA Transfer Count Reload (high) */
ESSreg(reg) = data;
if (dsp->sb_16_length == 0 || dsp->sb_8_length == 0)
dsp->ess_reload_len = 1;
break;
case 0xA8: /* Analog Control */
/* bits 7:5 0 Reserved. Always write 0
* bit 4 1 Reserved. Always write 1
* bit 3 Record monitor 1=Enable record monitor
* enable
* bit 2 0 Reserved. Always write 0
* bits 1:0 Stereo/mono select 00=Reserved
* 01=Stereo
* 10=Mono
* 11=Reserved */
chg = ESSreg(reg) ^ data;
ESSreg(reg) = data;
if (chg & 0x3) {
if (dsp->sb_16_enable || dsp->sb_8_enable) {
sb_stop_dma_ess(dsp);
sb_start_dma_ess(dsp);
}
}
break;
case 0xB1: /* Legacy Audio Interrupt Control */
case 0xB2: /* DRQ Control */
chg = ESSreg(reg) ^ data;
ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable
if (chg & 0x40) sb_ess_update_dma_status(dsp);
break;
case 0xB5: /* DAC Direct Access Holding (low) */
case 0xB6: /* DAC Direct Access Holding (high) */
ESSreg(reg) = data;
break;
case 0xB7: /* Audio 1 Control 1 */
/* bit 7 Enable FIFO to/from codec
* bit 6 Opposite from bit 3 Must be set opposite to bit 3
* bit 5 FIFO signed mode 1=Data is signed twos-complement 0=Data is unsigned
* bit 4 Reserved Always write 1
* bit 3 FIFO stereo mode 1=Data is stereo
* bit 2 FIFO 16-bit mode 1=Data is 16-bit
* bit 1 Reserved Always write 0
* bit 0 Generate load signal */
chg = ESSreg(reg) ^ data;
ESSreg(reg) = data;
if (chg & 0x0C) {
if (dsp->sb_16_enable || dsp->sb_8_enable) {
sb_stop_dma_ess(dsp);
sb_start_dma_ess(dsp);
}
}
break;
case 0xB8: /* Audio 1 Control 2 */
/* bits 7:4 reserved
* bit 3 CODEC mode 1=first DMA converter in ADC mode
* 0=first DMA converter in DAC mode
* bit 2 DMA mode 1=auto-initialize mode
* 0=normal DMA mode
* bit 1 DMA read enable 1=first DMA is read (for ADC)
* 0=first DMA is write (for DAC)
* bit 0 DMA xfer enable 1=DMA is allowed to proceed */
data &= 0xF;
chg = ESSreg(reg) ^ data;
ESSreg(reg) = data;
if (chg & 1)
dsp->ess_reload_len = 1;
if (chg & 0xB) {
if (chg & 0xA) sb_stop_dma_ess(dsp); /* changing capture/playback direction? stop DMA to reinit */
sb_ess_update_dma_status(dsp);
}
break;
case 0xB9: /* Audio 1 Transfer Type */
case 0xBA: /* Left Channel ADC Offset Adjust */
case 0xBB: /* Right Channel ADC Offset Adjust */
ESSreg(reg) = data;
break;
default:
break;
}
}
void
sb_exec_command(sb_dsp_t *dsp)
{
@@ -632,11 +771,17 @@ sb_exec_command(sb_dsp_t *dsp)
if (dsp->sb_type >= SB16)
dsp->sb_8051_ram[0x20] = dsp->sb_command;
if (IS_ESS(dsp)) {
if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7){
if (IS_ESS(dsp) && dsp->sb_command >= 0xA0 && dsp->sb_command <= 0xCF) {
if (dsp->sb_command == 0xC6 || dsp->sb_command == 0xC7) {
dsp->ess_extended_mode = !!(dsp->sb_command == 0xC6);
return;
}
if (dsp->sb_command == 0xC0) {
sb_add_data(dsp, sb_ess_read_reg(dsp, dsp->sb_data[0]));
} else if (dsp->sb_command < 0xC0 && dsp->ess_extended_mode) {
sb_ess_write_reg(dsp, dsp->sb_command, dsp->sb_data[0]);
}
return;
}
switch (dsp->sb_command) {
@@ -1062,12 +1207,29 @@ sb_exec_command(sb_dsp_t *dsp)
while (sb16_copyright[c])
sb_add_data(dsp, sb16_copyright[c++]);
sb_add_data(dsp, 0);
} else if (IS_ESS(dsp)) {
sb_add_data(dsp, 0);
}
break;
case 0xE4: /* Write test register */
dsp->sb_test = dsp->sb_data[0];
break;
case 0xE7: /* ???? */
case 0xE7: /* ???? */ /* ESS detect/read config on ESS cards */
if (IS_ESS(dsp)) {
switch (dsp->sb_subtype) {
default:
break;
case SB_SUBTYPE_ESS_ES688:
sb_add_data(dsp, 0x68);
sb_add_data(dsp, 0x80 | 0x04);
break;
case SB_SUBTYPE_ESS_ES1688:
// Determined via Windows driver debugging.
sb_add_data(dsp, 0x68);
sb_add_data(dsp, 0x80 | 0x09);
break;
}
}
break;
case 0xE8: /* Read test register */
sb_add_data(dsp, dsp->sb_test);
@@ -1170,6 +1332,13 @@ sb_write(uint16_t a, uint8_t v, void *priv)
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 >= 0xA0 && dsp->sb_command <= 0xCF) {
if (dsp->sb_command <= 0xC0) {
sb_commands[dsp->sb_command] = 1;
} else {
sb_commands[dsp->sb_command] = 0;
}
}
}
if (dsp->sb_data_stat == sb_commands[dsp->sb_command] || sb_commands[dsp->sb_command] == -1) {
sb_exec_command(dsp);