Merge pull request #4296 from Kagamiin/feature/ess-audiodrive
feature/sound: ESS AudioDrive ES1688
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
#ifndef FIFO_H
|
||||
#define FIFO_H
|
||||
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
@@ -71,3 +74,5 @@ extern void fifo_reset(void *priv);
|
||||
extern void fifo_reset_evt(void *priv);
|
||||
extern void fifo_close(void *priv);
|
||||
extern void *fifo_init(int len);
|
||||
|
||||
#endif /*FIFO_H*/
|
||||
|
@@ -22,7 +22,8 @@ enum fm_type {
|
||||
FM_YMF262 = 1, /* OPL3 */
|
||||
FM_YMF289B = 2, /* OPL3-L */
|
||||
FM_YMF278B = 3, /* OPL 4 */
|
||||
FM_MAX = 4
|
||||
FM_ESFM = 4, /* ESFM */
|
||||
FM_MAX = 5
|
||||
};
|
||||
|
||||
enum fm_driver {
|
||||
@@ -45,6 +46,7 @@ extern uint8_t fm_driver_get(int chip_id, fm_drv_t *drv);
|
||||
|
||||
extern const fm_drv_t nuked_opl_drv;
|
||||
extern const fm_drv_t ymfm_drv;
|
||||
extern const fm_drv_t esfmu_opl_drv;
|
||||
|
||||
#ifdef EMU_DEVICE_H
|
||||
extern const device_t ym3812_nuked_device;
|
||||
@@ -54,6 +56,8 @@ extern const device_t ym3812_ymfm_device;
|
||||
extern const device_t ymf262_ymfm_device;
|
||||
extern const device_t ymf289b_ymfm_device;
|
||||
extern const device_t ymf278b_ymfm_device;
|
||||
|
||||
extern const device_t esfm_esfmu_device;
|
||||
#endif
|
||||
|
||||
#endif /*SOUND_OPL_H*/
|
||||
|
@@ -126,6 +126,43 @@ typedef struct sb_ct1745_mixer_t {
|
||||
int output_filter; /* for clones */
|
||||
} sb_ct1745_mixer_t;
|
||||
|
||||
/* ESS AudioDrive */
|
||||
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_l;
|
||||
double mic_r;
|
||||
double auxb_l;
|
||||
double auxb_r;
|
||||
double speaker;
|
||||
/*see sb_ct1745_mixer for values for input selector*/
|
||||
int32_t input_selector;
|
||||
/* extra values for input selector */
|
||||
#define INPUT_MIXER_L 128
|
||||
#define INPUT_MIXER_R 256
|
||||
|
||||
int input_filter;
|
||||
int in_filter_freq;
|
||||
int output_filter;
|
||||
|
||||
int stereo;
|
||||
int stereo_isleft;
|
||||
|
||||
uint8_t index;
|
||||
uint8_t regs[256];
|
||||
|
||||
uint8_t ess_id_str[4];
|
||||
uint8_t ess_id_str_pos;
|
||||
} ess_mixer_t;
|
||||
|
||||
typedef struct sb_t {
|
||||
uint8_t cms_enabled;
|
||||
uint8_t opl_enabled;
|
||||
@@ -138,6 +175,7 @@ typedef struct sb_t {
|
||||
sb_ct1335_mixer_t mixer_sb2;
|
||||
sb_ct1345_mixer_t mixer_sbpro;
|
||||
sb_ct1745_mixer_t mixer_sb16;
|
||||
ess_mixer_t mixer_ess;
|
||||
};
|
||||
mpu_t *mpu;
|
||||
emu8k_t emu8k;
|
||||
@@ -163,6 +201,10 @@ extern void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *priv);
|
||||
extern uint8_t sb_ct1745_mixer_read(uint16_t addr, void *priv);
|
||||
extern void sb_ct1745_mixer_reset(sb_t *sb);
|
||||
|
||||
extern void sb_ess_mixer_write(uint16_t addr, uint8_t val, void *priv);
|
||||
extern uint8_t sb_ess_mixer_read(uint16_t addr, void *priv);
|
||||
extern void sb_ess_mixer_reset(sb_t *sb);
|
||||
|
||||
extern void sb_get_buffer_sbpro(int32_t *buffer, int len, void *priv);
|
||||
extern void sb_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv);
|
||||
extern void sbpro_filter_cd_audio(int channel, double *buffer, void *priv);
|
||||
|
@@ -1,10 +1,16 @@
|
||||
#ifndef SOUND_SND_SB_DSP_H
|
||||
#define SOUND_SND_SB_DSP_H
|
||||
|
||||
#include <86box/fifo.h>
|
||||
|
||||
/*Sound Blaster Clones, for quirks*/
|
||||
#define SB_SUBTYPE_DEFAULT 0 /*Handle as a Creative card*/
|
||||
#define SB_SUBTYPE_CLONE_AZT2316A_0X11 1 /*Aztech Sound Galaxy Pro 16 AB, DSP 3.1 - SBPRO2 clone*/
|
||||
#define SB_SUBTYPE_CLONE_AZT1605_0X0C 2 /*Aztech Sound Galaxy Nova 16 Extra / Packard Bell Forte 16, DSP 2.1 - SBPRO2 clone*/
|
||||
#define SB_SUBTYPE_ESS_ES1688 3 /* ESS Technology ES1688 */
|
||||
|
||||
/* ESS-related */
|
||||
#define IS_ESS(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_ESS_ES1688) /* check for future ESS cards here */
|
||||
|
||||
/* aztech-related */
|
||||
#define IS_AZTECH(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_CLONE_AZT2316A_0X11 || (dsp)->sb_subtype == SB_SUBTYPE_CLONE_AZT1605_0X0C) /* check for future AZT cards here */
|
||||
@@ -125,6 +131,8 @@ typedef struct sb_dsp_t {
|
||||
pc_timer_t wb_timer;
|
||||
int wb_full;
|
||||
|
||||
pc_timer_t irq_timer;
|
||||
|
||||
int busy_count;
|
||||
|
||||
int record_pos_read;
|
||||
@@ -135,6 +143,28 @@ typedef struct sb_dsp_t {
|
||||
|
||||
uint8_t azt_eeprom[AZTECH_EEPROM_SIZE]; /* the eeprom in the Aztech cards is attached to the DSP */
|
||||
|
||||
uint8_t ess_regs[256]; /* ESS registers. */
|
||||
uint8_t ess_playback_mode;
|
||||
uint8_t ess_extended_mode;
|
||||
uint8_t ess_reload_len;
|
||||
uint32_t ess_dma_counter;
|
||||
|
||||
/* IRQ status flags (0x22C) */
|
||||
uint8_t ess_irq_generic;
|
||||
uint8_t ess_irq_dmactr;
|
||||
|
||||
/* ESPCM */
|
||||
fifo64_t *espcm_fifo;
|
||||
uint8_t espcm_fifo_reset;
|
||||
uint8_t espcm_mode; /* see ESPCM in "NON-PCM SAMPLE FORMATS" deflist in snd_sb_dsp.c */
|
||||
uint8_t espcm_sample_idx;
|
||||
uint8_t espcm_range;
|
||||
uint8_t espcm_byte_buffer[4];
|
||||
uint8_t espcm_code_buffer[19]; /* used for ESPCM_3 and for ESPCM_4 recording */
|
||||
int8_t espcm_sample_buffer[19]; /* used for ESPCM_4 recording */
|
||||
uint8_t espcm_table_index; /* used for ESPCM_3 */
|
||||
uint8_t espcm_last_value; /* used for ESPCM_3 */
|
||||
|
||||
mpu_t *mpu;
|
||||
} sb_dsp_t;
|
||||
|
||||
|
@@ -182,6 +182,10 @@ extern const device_t cmi8338_onboard_device;
|
||||
extern const device_t cmi8738_device;
|
||||
extern const device_t cmi8738_onboard_device;
|
||||
extern const device_t cmi8738_6ch_onboard_device;
|
||||
|
||||
/* ESS Technology */
|
||||
extern const device_t ess_1688_device;
|
||||
|
||||
#endif
|
||||
|
||||
#endif /*EMU_SOUND_H*/
|
||||
|
@@ -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_pas16.c snd_sn76489.c snd_ssi2001.c snd_wss.c snd_ym7128.c
|
||||
snd_optimc.c)
|
||||
snd_optimc.c esfmu/esfm.c esfmu/esfm_registers.c snd_opl_esfm.c)
|
||||
|
||||
if(OPENAL)
|
||||
if(VCPKG_TOOLCHAIN)
|
||||
|
2327
src/sound/esfmu/esfm.c
Normal file
2327
src/sound/esfmu/esfm.c
Normal file
File diff suppressed because it is too large
Load Diff
289
src/sound/esfmu/esfm.h
Normal file
289
src/sound/esfmu/esfm.h
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* ESFMu: emulator for the ESS "ESFM" enhanced OPL3 clone
|
||||
* Copyright (C) 2023 Kagamiin~
|
||||
*
|
||||
* ESFMu is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation, either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* ESFMu is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ESFMu. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ESFMu wouldn't have been possible without the hard work and dedication of
|
||||
* the retro computer hardware research and preservation community.
|
||||
*
|
||||
* I'd like to thank:
|
||||
* - Nuke.YKT
|
||||
* Developer of Nuked OPL3, which was the basis for ESFMu's code and
|
||||
* also a great learning resource on Yamaha FM synthesis for myself.
|
||||
* Nuke.YKT also gives shoutouts on behalf of Nuked OPL3 to:
|
||||
* - MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh):
|
||||
* Feedback and Rhythm part calculation information.
|
||||
* - forums.submarine.org.uk(carbon14, opl3):
|
||||
* Tremolo and phase generator calculation information.
|
||||
* - OPLx decapsulated(Matthew Gambrell, Olli Niemitalo):
|
||||
* OPL2 ROMs.
|
||||
* - siliconpr0n.org(John McMaster, digshadow):
|
||||
* YMF262 and VRC VII decaps and die shots.
|
||||
* - rainwarrior
|
||||
* For performing the initial research on ESFM drivers and documenting
|
||||
* ESS's patent on native mode operator organization.
|
||||
* - jwt27
|
||||
* For kickstarting the ESFM research project and compiling rainwarrior's
|
||||
* findings and more in an accessible document ("ESFM Demystified").
|
||||
* - pachuco/CatButts
|
||||
* For documenting ESS's patent on ESFM's feedback implementation, which
|
||||
* was vital in getting ESFMu's sound output to be accurate.
|
||||
* - akumanatt
|
||||
* For helping out with code optimization.
|
||||
* - And everybody who helped out with real hardware testing
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct _esfm_slot esfm_slot;
|
||||
typedef struct _esfm_slot_internal esfm_slot_internal;
|
||||
typedef struct _esfm_channel esfm_channel;
|
||||
typedef struct _esfm_chip esfm_chip;
|
||||
|
||||
|
||||
void ESFM_init (esfm_chip *chip);
|
||||
void ESFM_write_reg (esfm_chip *chip, uint16_t address, uint8_t data);
|
||||
void ESFM_write_reg_buffered (esfm_chip *chip, uint16_t address, uint8_t data);
|
||||
void ESFM_write_reg_buffered_fast (esfm_chip *chip, uint16_t address, uint8_t data);
|
||||
void ESFM_write_port (esfm_chip *chip, uint8_t offset, uint8_t data);
|
||||
uint8_t ESFM_readback_reg (esfm_chip *chip, uint16_t address);
|
||||
uint8_t ESFM_read_port (esfm_chip *chip, uint8_t offset);
|
||||
void ESFM_generate(esfm_chip *chip, int32_t *buf);
|
||||
void ESFM_generate_stream(esfm_chip *chip, int16_t *sndptr, uint32_t num_samples);
|
||||
int16_t ESFM_get_channel_output_native(esfm_chip *chip, int channel_idx);
|
||||
|
||||
|
||||
// These are fake types just for syntax sugar.
|
||||
// Beware of their underlying types when reading/writing to them.
|
||||
typedef uint8_t ebit;
|
||||
typedef uint8_t uint2;
|
||||
typedef uint8_t uint3;
|
||||
typedef uint8_t uint4;
|
||||
typedef uint8_t uint5;
|
||||
typedef uint8_t uint6;
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint9;
|
||||
typedef uint16_t uint10;
|
||||
typedef uint16_t uint11;
|
||||
typedef uint16_t uint12;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint19;
|
||||
typedef uint32_t uint23;
|
||||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint36;
|
||||
|
||||
typedef int16_t int13;
|
||||
typedef int16_t int14;
|
||||
typedef int16_t int16;
|
||||
typedef int32_t int32;
|
||||
|
||||
enum eg_states
|
||||
{
|
||||
EG_ATTACK,
|
||||
EG_DECAY,
|
||||
EG_SUSTAIN,
|
||||
EG_RELEASE
|
||||
};
|
||||
|
||||
|
||||
typedef struct _esfm_write_buf
|
||||
{
|
||||
uint64_t timestamp;
|
||||
uint16_t address;
|
||||
uint8_t data;
|
||||
ebit valid;
|
||||
|
||||
} esfm_write_buf;
|
||||
|
||||
typedef struct _emu_slot_channel_mapping
|
||||
{
|
||||
int channel_idx;
|
||||
int slot_idx;
|
||||
|
||||
} emu_slot_channel_mapping;
|
||||
|
||||
typedef struct _esfm_slot_internal
|
||||
{
|
||||
uint9 eg_position;
|
||||
uint9 eg_ksl_offset;
|
||||
uint10 eg_output;
|
||||
|
||||
uint4 keyscale;
|
||||
|
||||
int13 output;
|
||||
int13 emu_output_enable;
|
||||
int13 emu_mod_enable;
|
||||
int13 feedback_buf;
|
||||
int13 *mod_input;
|
||||
|
||||
uint19 phase_acc;
|
||||
uint10 phase_out;
|
||||
ebit phase_reset;
|
||||
ebit *key_on;
|
||||
ebit key_on_gate;
|
||||
|
||||
uint2 eg_state;
|
||||
ebit eg_delay_run;
|
||||
ebit eg_delay_transitioned_10;
|
||||
ebit eg_delay_transitioned_10_gate;
|
||||
ebit eg_delay_transitioned_01;
|
||||
ebit eg_delay_transitioned_01_gate;
|
||||
uint16 eg_delay_counter;
|
||||
uint16 eg_delay_counter_compare;
|
||||
|
||||
} esfm_slot_internal;
|
||||
|
||||
struct _esfm_slot
|
||||
{
|
||||
// Metadata
|
||||
esfm_channel *channel;
|
||||
esfm_chip *chip;
|
||||
uint2 slot_idx;
|
||||
|
||||
// Register data
|
||||
int13 out_enable[2];
|
||||
uint10 f_num;
|
||||
uint3 block;
|
||||
uint3 output_level;
|
||||
// a.k.a. feedback level in emu mode
|
||||
uint3 mod_in_level;
|
||||
|
||||
uint6 t_level;
|
||||
uint4 mult;
|
||||
uint3 waveform;
|
||||
// Only for 4th slot
|
||||
uint2 rhy_noise;
|
||||
|
||||
uint4 attack_rate;
|
||||
uint4 decay_rate;
|
||||
uint4 sustain_lvl;
|
||||
uint4 release_rate;
|
||||
|
||||
ebit tremolo_en;
|
||||
ebit tremolo_deep;
|
||||
ebit vibrato_en;
|
||||
ebit vibrato_deep;
|
||||
ebit emu_connection_typ;
|
||||
ebit env_sustaining;
|
||||
ebit ksr;
|
||||
uint2 ksl;
|
||||
uint3 env_delay;
|
||||
// overlaps with env_delay bit 0
|
||||
// TODO: check if emu mode only uses this, or if it actually overwrites the channel field used by native mode
|
||||
ebit emu_key_on;
|
||||
|
||||
// Internal state
|
||||
esfm_slot_internal in;
|
||||
};
|
||||
|
||||
struct _esfm_channel
|
||||
{
|
||||
esfm_chip *chip;
|
||||
esfm_slot slots[4];
|
||||
uint5 channel_idx;
|
||||
int16 output[2];
|
||||
ebit key_on;
|
||||
ebit emu_mode_4op_enable;
|
||||
// Only for 17th and 18th channels
|
||||
ebit key_on_2;
|
||||
ebit emu_mode_4op_enable_2;
|
||||
};
|
||||
|
||||
#define ESFM_WRITEBUF_SIZE 1024
|
||||
#define ESFM_WRITEBUF_DELAY 2
|
||||
|
||||
struct _esfm_chip
|
||||
{
|
||||
esfm_channel channels[18];
|
||||
int32 output_accm[2];
|
||||
uint16 addr_latch;
|
||||
|
||||
ebit emu_wavesel_enable;
|
||||
ebit emu_newmode;
|
||||
ebit native_mode;
|
||||
|
||||
ebit keyscale_mode;
|
||||
|
||||
// Global state
|
||||
uint36 eg_timer;
|
||||
uint10 global_timer;
|
||||
uint8 eg_clocks;
|
||||
ebit eg_tick;
|
||||
ebit eg_timer_overflow;
|
||||
uint8 tremolo;
|
||||
uint8 tremolo_pos;
|
||||
uint8 vibrato_pos;
|
||||
uint23 lfsr;
|
||||
|
||||
ebit rm_hh_bit2;
|
||||
ebit rm_hh_bit3;
|
||||
ebit rm_hh_bit7;
|
||||
ebit rm_hh_bit8;
|
||||
ebit rm_tc_bit3;
|
||||
ebit rm_tc_bit5;
|
||||
|
||||
// 0xbd register in emulation mode, exposed in 0x4bd in native mode
|
||||
// ("bass drum" register)
|
||||
uint8 emu_rhy_mode_flags;
|
||||
|
||||
ebit emu_vibrato_deep;
|
||||
ebit emu_tremolo_deep;
|
||||
|
||||
double timer_accumulator[2];
|
||||
uint8 timer_reload[2];
|
||||
uint8 timer_counter[2];
|
||||
ebit timer_enable[2];
|
||||
ebit timer_mask[2];
|
||||
ebit timer_overflow[2];
|
||||
ebit irq_bit;
|
||||
|
||||
// -- Test bits (NOT IMPLEMENTED) --
|
||||
// Halts the envelope generators from advancing. Written on bit 0, read back from bit 5.
|
||||
ebit test_bit_w0_r5_eg_halt;
|
||||
/*
|
||||
* Activates some sort of waveform test mode that amplifies the output volume greatly
|
||||
* and continuously shifts the waveform table downwards, possibly also outputting the
|
||||
* waveform's derivative? (it's so weird!)
|
||||
*/
|
||||
ebit test_bit_1_distort;
|
||||
// Seems to do nothing.
|
||||
ebit test_bit_2;
|
||||
// Seems to do nothing.
|
||||
ebit test_bit_3;
|
||||
// Appears to attenuate the output by about 3 dB.
|
||||
ebit test_bit_4_attenuate;
|
||||
// Written on bit 5, read back from bit 0. Seems to do nothing.
|
||||
ebit test_bit_w5_r0;
|
||||
// Resets all phase generators and holds them in the reset state while this bit is set.
|
||||
ebit test_bit_6_phase_stop_reset;
|
||||
// Seems to do nothing.
|
||||
ebit test_bit_7;
|
||||
|
||||
esfm_write_buf write_buf[ESFM_WRITEBUF_SIZE];
|
||||
size_t write_buf_start;
|
||||
size_t write_buf_end;
|
||||
uint64_t write_buf_timestamp;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
1009
src/sound/esfmu/esfm_registers.c
Normal file
1009
src/sound/esfmu/esfm_registers.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -68,6 +68,11 @@ fm_driver_get(int chip_id, fm_drv_t *drv)
|
||||
*drv = ymfm_drv;
|
||||
drv->priv = device_add_inst(&ymf278b_ymfm_device, fm_dev_inst[fm_driver][chip_id]++);
|
||||
break;
|
||||
|
||||
case FM_ESFM:
|
||||
*drv = esfmu_opl_drv;
|
||||
drv->priv = device_add_inst(&esfm_esfmu_device, fm_dev_inst[fm_driver][chip_id]++);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
|
355
src/sound/snd_opl_esfm.c
Normal file
355
src/sound/snd_opl_esfm.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* ESFMu ESFM emulator.
|
||||
*
|
||||
*
|
||||
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
* Alexey Khokholov (Nuke.YKT)
|
||||
* Cacodemon345
|
||||
*
|
||||
* Copyright 2017-2020 Fred N. van Kempen.
|
||||
* Copyright 2016-2020 Miran Grca.
|
||||
* Copyright 2013-2018 Alexey Khokholov (Nuke.YKT)
|
||||
* Copyright 2024 Cacodemon345
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esfmu/esfm.h"
|
||||
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/sound.h>
|
||||
#include <86box/device.h>
|
||||
#include <86box/timer.h>
|
||||
#include <86box/snd_opl.h>
|
||||
|
||||
#define RSM_FRAC 10
|
||||
|
||||
typedef struct {
|
||||
esfm_chip opl;
|
||||
int8_t flags;
|
||||
int8_t pad;
|
||||
|
||||
uint8_t status;
|
||||
uint8_t timer_ctrl;
|
||||
uint16_t timer_count[2];
|
||||
uint16_t timer_cur_count[2];
|
||||
|
||||
pc_timer_t timers[2];
|
||||
|
||||
int16_t samples[2];
|
||||
|
||||
int pos;
|
||||
int32_t buffer[MUSICBUFLEN * 2];
|
||||
} esfm_drv_t;
|
||||
|
||||
enum {
|
||||
FLAG_CYCLES = 0x02,
|
||||
FLAG_OPL3 = 0x01
|
||||
};
|
||||
|
||||
enum {
|
||||
STAT_TMR_OVER = 0x60,
|
||||
STAT_TMR1_OVER = 0x40,
|
||||
STAT_TMR2_OVER = 0x20,
|
||||
STAT_TMR_ANY = 0x80
|
||||
};
|
||||
|
||||
enum {
|
||||
CTRL_RESET = 0x80,
|
||||
CTRL_TMR_MASK = 0x60,
|
||||
CTRL_TMR1_MASK = 0x40,
|
||||
CTRL_TMR2_MASK = 0x20,
|
||||
CTRL_TMR2_START = 0x02,
|
||||
CTRL_TMR1_START = 0x01
|
||||
};
|
||||
|
||||
#ifdef ENABLE_OPL_LOG
|
||||
int esfm_do_log = ENABLE_OPL_LOG;
|
||||
|
||||
static void
|
||||
esfm_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (esfm_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define esfm_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
void
|
||||
esfm_generate_raw(esfm_drv_t *dev, int32_t *bufp)
|
||||
{
|
||||
ESFM_generate(&dev->opl, bufp);
|
||||
}
|
||||
|
||||
void
|
||||
esfm_drv_generate_stream(esfm_drv_t *dev, int32_t *sndptr, uint32_t num)
|
||||
{
|
||||
for (uint32_t i = 0; i < num; i++) {
|
||||
esfm_generate_raw(dev, sndptr);
|
||||
sndptr += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_timer_tick(esfm_drv_t *dev, int tmr)
|
||||
{
|
||||
dev->timer_cur_count[tmr] = (dev->timer_cur_count[tmr] + 1) & 0xff;
|
||||
|
||||
esfm_log("Ticking timer %i, count now %02X...\n", tmr, dev->timer_cur_count[tmr]);
|
||||
|
||||
if (dev->timer_cur_count[tmr] == 0x00) {
|
||||
dev->status |= ((STAT_TMR1_OVER >> tmr) & ~dev->timer_ctrl);
|
||||
dev->timer_cur_count[tmr] = dev->timer_count[tmr];
|
||||
|
||||
esfm_log("Count wrapped around to zero, reloading timer %i (%02X), status = %02X...\n", tmr, (STAT_TMR1_OVER >> tmr), dev->status);
|
||||
}
|
||||
|
||||
timer_on_auto(&dev->timers[tmr], (tmr == 1) ? 320.0 : 80.0);
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_timer_control(esfm_drv_t *dev, int tmr, int start)
|
||||
{
|
||||
timer_on_auto(&dev->timers[tmr], 0.0);
|
||||
|
||||
if (start) {
|
||||
esfm_log("Loading timer %i count: %02X = %02X\n", tmr, dev->timer_cur_count[tmr], dev->timer_count[tmr]);
|
||||
dev->timer_cur_count[tmr] = dev->timer_count[tmr];
|
||||
if (dev->flags & FLAG_OPL3)
|
||||
esfm_timer_tick(dev, tmr); /* Per the YMF 262 datasheet, OPL3 starts counting immediately, unlike OPL2. */
|
||||
else
|
||||
timer_on_auto(&dev->timers[tmr], (tmr == 1) ? 320.0 : 80.0);
|
||||
} else {
|
||||
esfm_log("Timer %i stopped\n", tmr);
|
||||
if (tmr == 1) {
|
||||
dev->status &= ~STAT_TMR2_OVER;
|
||||
} else
|
||||
dev->status &= ~STAT_TMR1_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_timer_1(void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
esfm_timer_tick(dev, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_timer_2(void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
esfm_timer_tick(dev, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_drv_set_do_cycles(void *priv, int8_t do_cycles)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
if (do_cycles)
|
||||
dev->flags |= FLAG_CYCLES;
|
||||
else
|
||||
dev->flags &= ~FLAG_CYCLES;
|
||||
}
|
||||
|
||||
static void *
|
||||
esfm_drv_init(const device_t *info)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) calloc(1, sizeof(esfm_drv_t));
|
||||
dev->flags = FLAG_CYCLES | FLAG_OPL3;
|
||||
|
||||
/* Initialize the ESFMu object. */
|
||||
ESFM_init(&dev->opl);
|
||||
|
||||
timer_add(&dev->timers[0], esfm_timer_1, dev, 0);
|
||||
timer_add(&dev->timers[1], esfm_timer_2, dev, 0);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_drv_close(void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static int32_t *
|
||||
esfm_drv_update(void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
if (dev->pos >= music_pos_global)
|
||||
return dev->buffer;
|
||||
|
||||
esfm_drv_generate_stream(dev,
|
||||
&dev->buffer[dev->pos * 2],
|
||||
music_pos_global - dev->pos);
|
||||
|
||||
for (; dev->pos < music_pos_global; dev->pos++) {
|
||||
dev->buffer[dev->pos * 2] /= 2;
|
||||
dev->buffer[(dev->pos * 2) + 1] /= 2;
|
||||
}
|
||||
|
||||
return dev->buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_drv_reset_buffer(void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
dev->pos = 0;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
esfm_drv_read(uint16_t port, void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
if (dev->flags & FLAG_CYCLES)
|
||||
cycles -= ((int) (isa_timing * 8));
|
||||
|
||||
esfm_drv_update(dev);
|
||||
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
switch (port & 0x0003) {
|
||||
case 0x0000:
|
||||
ret = dev->status;
|
||||
if (dev->status & STAT_TMR_OVER)
|
||||
ret |= STAT_TMR_ANY;
|
||||
break;
|
||||
case 0x0001:
|
||||
ret = ESFM_read_port(&dev->opl, port & 3);
|
||||
switch (dev->opl.addr_latch & 0x5ff) {
|
||||
case 0x402:
|
||||
ret = dev->timer_count[0];
|
||||
break;
|
||||
case 0x403:
|
||||
ret = dev->timer_count[1];
|
||||
break;
|
||||
case 0x404:
|
||||
ret = dev->timer_ctrl;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x0002:
|
||||
case 0x0003:
|
||||
ret = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_drv_write_buffered(esfm_drv_t *dev, uint8_t val)
|
||||
{
|
||||
uint16_t p = dev->opl.addr_latch & 0x07ff;
|
||||
|
||||
if (dev->opl.native_mode) {
|
||||
p -= 0x400;
|
||||
}
|
||||
p &= 0x1ff;
|
||||
|
||||
switch (p) {
|
||||
case 0x002: /* Timer 1 */
|
||||
dev->timer_count[0] = val;
|
||||
esfm_log("Timer 0 count now: %i\n", dev->timer_count[0]);
|
||||
break;
|
||||
|
||||
case 0x003: /* Timer 2 */
|
||||
dev->timer_count[1] = val;
|
||||
esfm_log("Timer 1 count now: %i\n", dev->timer_count[1]);
|
||||
break;
|
||||
|
||||
case 0x004: /* Timer control */
|
||||
if (val & CTRL_RESET) {
|
||||
esfm_log("Resetting timer status...\n");
|
||||
dev->status &= ~STAT_TMR_OVER;
|
||||
} else {
|
||||
dev->timer_ctrl = val;
|
||||
esfm_timer_control(dev, 0, val & CTRL_TMR1_START);
|
||||
esfm_timer_control(dev, 1, val & CTRL_TMR2_START);
|
||||
esfm_log("Status mask now %02X (val = %02X)\n", (val & ~CTRL_TMR_MASK) & CTRL_TMR_MASK, val);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ESFM_write_reg_buffered_fast(&dev->opl, dev->opl.addr_latch, val);
|
||||
}
|
||||
|
||||
static void
|
||||
esfm_drv_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
esfm_drv_t *dev = (esfm_drv_t *) priv;
|
||||
|
||||
if (dev->flags & FLAG_CYCLES)
|
||||
cycles -= ((int) (isa_timing * 8));
|
||||
|
||||
esfm_drv_update(dev);
|
||||
|
||||
if (dev->opl.native_mode) {
|
||||
if ((port & 0x0003) == 0x0001)
|
||||
esfm_drv_write_buffered(dev, val);
|
||||
else {
|
||||
ESFM_write_port(&dev->opl, port & 3, val);
|
||||
}
|
||||
} else {
|
||||
if ((port & 0x0001) == 0x0001)
|
||||
esfm_drv_write_buffered(dev, val);
|
||||
else {
|
||||
ESFM_write_port(&dev->opl, port & 3, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const device_t esfm_esfmu_device = {
|
||||
.name = "ESS Technology ESFM (ESFMu)",
|
||||
.internal_name = "esfm_esfmu",
|
||||
.flags = 0,
|
||||
.local = FM_ESFM,
|
||||
.init = esfm_drv_init,
|
||||
.close = esfm_drv_close,
|
||||
.reset = NULL,
|
||||
{ .available = NULL },
|
||||
.speed_changed = NULL,
|
||||
.force_redraw = NULL,
|
||||
.config = NULL
|
||||
};
|
||||
|
||||
const fm_drv_t esfmu_opl_drv = {
|
||||
&esfm_drv_read,
|
||||
&esfm_drv_write,
|
||||
&esfm_drv_update,
|
||||
&esfm_drv_reset_buffer,
|
||||
&esfm_drv_set_do_cycles,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
@@ -75,6 +75,25 @@ static const double sb_att_4dbstep_3bits[] = {
|
||||
static const double sb_att_7dbstep_2bits[] = {
|
||||
164.0, 6537.0, 14637.0, 32767.0
|
||||
};
|
||||
|
||||
/* Attenuation table for ESS 4-bit microphone volume.
|
||||
* The last step is a jump to -48 dB. */
|
||||
static const double sb_att_1p4dbstep_4bits[] = {
|
||||
164.0, 3431.0, 4031.0, 4736.0, 5565.0, 6537.0, 7681.0, 9025.0,
|
||||
10603.0, 12458.0, 14637.0, 17196.0, 20204.0, 23738.0, 27889.0, 32767.0
|
||||
};
|
||||
|
||||
/* Attenuation table for ESS 4-bit mixer volume.
|
||||
* The last step is a jump to -48 dB. */
|
||||
static const double sb_att_2dbstep_4bits[] = {
|
||||
164.0, 1304.0, 1641.0, 2067.0, 2602.0, 3276.0, 4125.0, 5192.0,
|
||||
6537.0, 8230.0, 10362.0, 13044.0, 16422.0, 20674.0, 26027.0, 32767.0
|
||||
};
|
||||
|
||||
/* Attenuation table for ESS 3-bit PC speaker volume. */
|
||||
static const double sb_att_3dbstep_3bits[] = {
|
||||
0.0, 4125.0, 5826.0, 8230.0, 11626.0, 16422.0, 23197.0, 32767.0
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static const uint16_t sb_mcv_addr[8] = { 0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270 };
|
||||
@@ -667,6 +686,85 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv)
|
||||
*buffer = c * output_gain;
|
||||
}
|
||||
|
||||
void
|
||||
sb_get_buffer_ess(int32_t *buffer, int len, void *priv)
|
||||
{
|
||||
sb_t *ess = (sb_t *) priv;
|
||||
const ess_mixer_t *mixer = &ess->mixer_ess;
|
||||
double out_l = 0.0;
|
||||
double out_r = 0.0;
|
||||
|
||||
sb_dsp_update(&ess->dsp);
|
||||
|
||||
for (int c = 0; c < len * 2; c += 2) {
|
||||
out_l = 0.0;
|
||||
out_r = 0.0;
|
||||
|
||||
/* TODO: Implement the stereo switch on the mixer instead of on the dsp? */
|
||||
if (mixer->output_filter) {
|
||||
out_l += (low_fir_sb16(0, 0, (double) ess->dsp.buffer[c]) * mixer->voice_l) / 3.0;
|
||||
out_r += (low_fir_sb16(0, 1, (double) ess->dsp.buffer[c + 1]) * mixer->voice_r) / 3.0;
|
||||
} else {
|
||||
out_l += (ess->dsp.buffer[c] * mixer->voice_l) / 3.0;
|
||||
out_r += (ess->dsp.buffer[c + 1] * mixer->voice_r) / 3.0;
|
||||
}
|
||||
|
||||
/* TODO: recording from the mixer. */
|
||||
out_l *= mixer->master_l;
|
||||
out_r *= mixer->master_r;
|
||||
|
||||
buffer[c] += (int32_t) out_l;
|
||||
buffer[c + 1] += (int32_t) out_r;
|
||||
}
|
||||
|
||||
ess->dsp.pos = 0;
|
||||
}
|
||||
|
||||
void
|
||||
sb_get_music_buffer_ess(int32_t *buffer, int len, void *priv)
|
||||
{
|
||||
sb_t *ess = (sb_t *) priv;
|
||||
const ess_mixer_t *mixer = &ess->mixer_ess;
|
||||
double out_l = 0.0;
|
||||
double out_r = 0.0;
|
||||
const int32_t *opl_buf = NULL;
|
||||
|
||||
opl_buf = ess->opl.update(ess->opl.priv);
|
||||
|
||||
for (int c = 0; c < len * 2; c += 2) {
|
||||
out_l = 0.0;
|
||||
out_r = 0.0;
|
||||
|
||||
out_l = (((double) opl_buf[c]) * mixer->fm_l) * 0.7171630859375;
|
||||
out_r = (((double) opl_buf[c + 1]) * mixer->fm_r) * 0.7171630859375;
|
||||
if (ess->opl_mix && ess->opl_mixer)
|
||||
ess->opl_mix(ess->opl_mixer, &out_l, &out_r);
|
||||
|
||||
/* TODO: recording from the mixer. */
|
||||
out_l *= mixer->master_l;
|
||||
out_r *= mixer->master_r;
|
||||
|
||||
buffer[c] += (int32_t) out_l;
|
||||
buffer[c + 1] += (int32_t) out_r;
|
||||
}
|
||||
|
||||
ess->opl.reset_buffer(ess->opl.priv);
|
||||
}
|
||||
|
||||
void
|
||||
ess_filter_cd_audio(int channel, double *buffer, void *priv)
|
||||
{
|
||||
const sb_t *ess = (sb_t *) priv;
|
||||
const ess_mixer_t *mixer = &ess->mixer_ess;
|
||||
double c;
|
||||
double cd = channel ? mixer->cd_r : mixer->cd_l;
|
||||
double master = channel ? mixer->master_r : mixer->master_l;
|
||||
|
||||
/* TODO: recording from the mixer. */
|
||||
c = (*buffer * cd) / 3.0;
|
||||
*buffer = c * master;
|
||||
}
|
||||
|
||||
void
|
||||
sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
@@ -1304,6 +1402,264 @@ sb_ct1745_mixer_reset(sb_t *sb)
|
||||
sb_ct1745_mixer_write(5, 0, sb);
|
||||
}
|
||||
|
||||
void
|
||||
ess_mixer_write(uint16_t addr, uint8_t val, void *priv)
|
||||
{
|
||||
sb_t *ess = (sb_t *) priv;
|
||||
ess_mixer_t *mixer = &ess->mixer_ess;
|
||||
|
||||
if (!(addr & 1)) {
|
||||
mixer->index = val;
|
||||
mixer->regs[0x01] = val;
|
||||
if (val == 0x40) {
|
||||
mixer->ess_id_str_pos = 0;
|
||||
}
|
||||
} 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;
|
||||
|
||||
/* Initialize ESS regs
|
||||
* Defaulting to 0dB instead of the standard -11dB. */
|
||||
mixer->regs[0x14] = mixer->regs[0x32] = 0xff;
|
||||
mixer->regs[0x36] = mixer->regs[0x38] = 0xff;
|
||||
mixer->regs[0x3a] = 0x00;
|
||||
mixer->regs[0x3c] = 0x05;
|
||||
mixer->regs[0x3e] = 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 0x0A:
|
||||
{
|
||||
uint8_t mic_vol_2bit = (mixer->regs[0x0a] >> 1) & 0x3;
|
||||
mixer->mic_l = mixer->mic_r = sb_att_7dbstep_2bits[mic_vol_2bit] / 32767.0;
|
||||
mixer->regs[0x1A] = mic_vol_2bit | (mic_vol_2bit << 2) | (mic_vol_2bit << 4) | (mic_vol_2bit << 6);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0C:
|
||||
switch (mixer->regs[0x0C] & 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;
|
||||
}
|
||||
mixer->input_filter = !(mixer->regs[0xC] & 0x20);
|
||||
mixer->in_filter_freq = ((mixer->regs[0xC] & 0x8) == 0) ? 3200 : 8800;
|
||||
break;
|
||||
|
||||
case 0x0E:
|
||||
mixer->output_filter = !(mixer->regs[0xE] & 0x20);
|
||||
mixer->stereo = mixer->regs[0xE] & 2;
|
||||
sb_dsp_set_stereo(&ess->dsp, val & 2);
|
||||
break;
|
||||
|
||||
case 0x14:
|
||||
mixer->regs[0x4] = val & 0xee;
|
||||
break;
|
||||
|
||||
case 0x1A:
|
||||
mixer->mic_l = sb_att_1p4dbstep_4bits[(mixer->regs[0x1A] >> 4) & 0xF] / 32767.0;
|
||||
mixer->mic_r = sb_att_1p4dbstep_4bits[mixer->regs[0x1A] & 0xF] / 32767.0;
|
||||
break;
|
||||
|
||||
case 0x1C:
|
||||
if ((mixer->regs[0x1C] & 0x07) == 0x07) {
|
||||
mixer->input_selector = INPUT_MIXER_L | INPUT_MIXER_R;
|
||||
} else if ((mixer->regs[0x1C] & 0x07) == 0x06) {
|
||||
mixer->input_selector = INPUT_LINE_L | INPUT_LINE_R;
|
||||
} else if ((mixer->regs[0x1C] & 0x06) == 0x02) {
|
||||
mixer->input_selector = INPUT_CD_L | INPUT_CD_R;
|
||||
} else if ((mixer->regs[0x1C] & 0x02) == 0) {
|
||||
mixer->input_selector = INPUT_MIC;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x22:
|
||||
case 0x26:
|
||||
case 0x28:
|
||||
case 0x2E:
|
||||
mixer->regs[mixer->index - 0x20] = (val & 0xe);
|
||||
mixer->regs[mixer->index + 0x10] = val;
|
||||
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:
|
||||
case 0x3e:
|
||||
mixer->regs[mixer->index - 0x10] = (val & 0xee);
|
||||
break;
|
||||
|
||||
case 0x3a:
|
||||
case 0x3c:
|
||||
break;
|
||||
|
||||
case 0x00:
|
||||
case 0x04:
|
||||
break;
|
||||
|
||||
case 0x64:
|
||||
mixer->regs[mixer->index] &= ~0x8;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
{
|
||||
uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30);
|
||||
gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200);
|
||||
|
||||
if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
|
||||
/* Not on ES1688. */
|
||||
io_removehandler(0x0388, 0x0004,
|
||||
ess->opl.read, NULL, NULL,
|
||||
ess->opl.write, NULL, NULL,
|
||||
ess->opl.priv);
|
||||
if ((mixer->regs[0x40] & 0x1) != 0) {
|
||||
io_sethandler(0x0388, 0x0004,
|
||||
ess->opl.read, NULL, NULL,
|
||||
ess->opl.write, NULL, NULL,
|
||||
ess->opl.priv);
|
||||
}
|
||||
}
|
||||
switch ((mixer->regs[0x40] >> 5) & 0x7) {
|
||||
case 0:
|
||||
mpu401_change_addr(ess->mpu, 0x00);
|
||||
mpu401_setirq(ess->mpu, -1);
|
||||
break;
|
||||
case 1:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, -1);
|
||||
break;
|
||||
case 2:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, ess->dsp.sb_irqnum);
|
||||
break;
|
||||
case 3:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, 11);
|
||||
break;
|
||||
case 4:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, 9);
|
||||
break;
|
||||
case 5:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, 5);
|
||||
break;
|
||||
case 6:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, 7);
|
||||
break;
|
||||
case 7:
|
||||
mpu401_change_addr(ess->mpu, mpu401_base_addr);
|
||||
mpu401_setirq(ess->mpu, 10);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
sb_log("ess: Unknown mixer register WRITE: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mixer->voice_l = sb_att_2dbstep_4bits[(mixer->regs[0x14] >> 4) & 0x0F] / 32767.0;
|
||||
mixer->voice_r = sb_att_2dbstep_4bits[mixer->regs[0x14] & 0x0F] / 32767.0;
|
||||
mixer->master_l = sb_att_2dbstep_4bits[(mixer->regs[0x32] >> 4) & 0x0F] / 32767.0;
|
||||
mixer->master_r = sb_att_2dbstep_4bits[mixer->regs[0x32] & 0x0F] / 32767.0;
|
||||
mixer->fm_l = sb_att_2dbstep_4bits[(mixer->regs[0x36] >> 4) & 0x0F] / 32767.0;
|
||||
mixer->fm_r = sb_att_2dbstep_4bits[mixer->regs[0x36] & 0x0F] / 32767.0;
|
||||
mixer->cd_l = sb_att_2dbstep_4bits[(mixer->regs[0x38] >> 4) & 0x0F] / 32767.0;
|
||||
mixer->cd_r = sb_att_2dbstep_4bits[mixer->regs[0x38] & 0x0F] / 32767.0;
|
||||
mixer->auxb_l = sb_att_2dbstep_4bits[(mixer->regs[0x3a] >> 4) & 0x0F] / 32767.0;
|
||||
mixer->auxb_r = sb_att_2dbstep_4bits[mixer->regs[0x3a] & 0x0F] / 32767.0;
|
||||
mixer->line_l = sb_att_2dbstep_4bits[(mixer->regs[0x3e] >> 4) & 0x0F] / 32767.0;
|
||||
mixer->line_r = sb_att_2dbstep_4bits[mixer->regs[0x3e] & 0x0F] / 32767.0;
|
||||
mixer->speaker = sb_att_3dbstep_3bits[mixer->regs[0x3c] & 0x07] / 32767.0;
|
||||
|
||||
/* TODO: PC Speaker volume */
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t
|
||||
ess_mixer_read(uint16_t addr, void *priv)
|
||||
{
|
||||
sb_t *ess = (sb_t *) priv;
|
||||
ess_mixer_t *mixer = &ess->mixer_ess;
|
||||
|
||||
if (!(addr & 1))
|
||||
return mixer->index;
|
||||
|
||||
switch (mixer->index) {
|
||||
case 0x00:
|
||||
case 0x04:
|
||||
case 0x0a:
|
||||
case 0x0c:
|
||||
case 0x0e:
|
||||
case 0x14:
|
||||
case 0x22:
|
||||
case 0x26:
|
||||
case 0x28:
|
||||
case 0x2e:
|
||||
case 0x02:
|
||||
case 0x06:
|
||||
case 0x30:
|
||||
case 0x32:
|
||||
case 0x36:
|
||||
case 0x38:
|
||||
case 0x3e:
|
||||
return mixer->regs[mixer->index];
|
||||
|
||||
case 0x40:
|
||||
if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) {
|
||||
uint8_t val = mixer->ess_id_str[mixer->ess_id_str_pos];
|
||||
mixer->ess_id_str_pos++;
|
||||
if (mixer->ess_id_str_pos >= 4)
|
||||
mixer->ess_id_str_pos = 0;
|
||||
return val;
|
||||
} else {
|
||||
return mixer->regs[mixer->index];
|
||||
}
|
||||
|
||||
default:
|
||||
sb_log("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0x0a;
|
||||
}
|
||||
|
||||
void
|
||||
ess_mixer_reset(sb_t *ess)
|
||||
{
|
||||
ess_mixer_write(4, 0, ess);
|
||||
ess_mixer_write(5, 0, ess);
|
||||
}
|
||||
|
||||
uint8_t
|
||||
sb_mcv_read(int port, void *priv)
|
||||
{
|
||||
@@ -2755,6 +3111,70 @@ sb_awe32_pnp_init(const device_t *info)
|
||||
return sb;
|
||||
}
|
||||
|
||||
static void *
|
||||
ess_1688_init(UNUSED(const device_t *info))
|
||||
{
|
||||
sb_t *ess = calloc(sizeof(sb_t), 1);
|
||||
uint16_t addr = device_get_config_hex16("base");
|
||||
|
||||
fm_driver_get(FM_ESFM, &ess->opl);
|
||||
|
||||
sb_dsp_set_real_opl(&ess->dsp, 1);
|
||||
sb_dsp_init(&ess->dsp, SBPRO2, SB_SUBTYPE_ESS_ES1688, ess);
|
||||
sb_dsp_setaddr(&ess->dsp, addr);
|
||||
sb_dsp_setirq(&ess->dsp, device_get_config_int("irq"));
|
||||
sb_dsp_setdma8(&ess->dsp, device_get_config_int("dma"));
|
||||
sb_dsp_setdma16_8(&ess->dsp, device_get_config_int("dma"));
|
||||
sb_dsp_setdma16_supported(&ess->dsp, 0);
|
||||
ess_mixer_reset(ess);
|
||||
|
||||
/* DSP I/O handler is activated in sb_dsp_setaddr */
|
||||
{
|
||||
io_sethandler(addr, 0x0004,
|
||||
ess->opl.read, NULL, NULL,
|
||||
ess->opl.write, NULL, NULL,
|
||||
ess->opl.priv);
|
||||
io_sethandler(addr + 8, 0x0002,
|
||||
ess->opl.read, NULL, NULL,
|
||||
ess->opl.write, NULL, NULL,
|
||||
ess->opl.priv);
|
||||
io_sethandler(0x0388, 0x0004,
|
||||
ess->opl.read, NULL, NULL,
|
||||
ess->opl.write, NULL, NULL,
|
||||
ess->opl.priv);
|
||||
}
|
||||
|
||||
ess->mixer_enabled = 1;
|
||||
io_sethandler(addr + 4, 0x0002,
|
||||
ess_mixer_read, NULL, NULL,
|
||||
ess_mixer_write, NULL, NULL,
|
||||
ess);
|
||||
sound_add_handler(sb_get_buffer_ess, ess);
|
||||
music_add_handler(sb_get_music_buffer_ess, ess);
|
||||
sound_set_cd_audio_filter(ess_filter_cd_audio, ess);
|
||||
|
||||
if (device_get_config_int("receive_input")) {
|
||||
midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp);
|
||||
}
|
||||
|
||||
ess->mixer_ess.ess_id_str[0] = 0x16;
|
||||
ess->mixer_ess.ess_id_str[1] = 0x88;
|
||||
ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff;
|
||||
ess->mixer_ess.ess_id_str[3] = addr & 0xff;
|
||||
|
||||
ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t));
|
||||
/* NOTE: The MPU is initialized disabled and with no IRQ assigned.
|
||||
* It will be later initialized by the guest OS's drivers. */
|
||||
mpu401_init(ess->mpu, 0, -1, M_UART, 1);
|
||||
sb_dsp_set_mpu(&ess->dsp, ess->mpu);
|
||||
|
||||
ess->gameport = gameport_add(&gameport_pnp_device);
|
||||
ess->gameport_addr = 0x200;
|
||||
gameport_remap(ess->gameport, ess->gameport_addr);
|
||||
|
||||
return ess;
|
||||
}
|
||||
|
||||
void
|
||||
sb_close(void *priv)
|
||||
{
|
||||
@@ -4023,6 +4443,104 @@ static const device_config_t sb_awe64_gold_config[] = {
|
||||
},
|
||||
{ .name = "", .description = "", .type = CONFIG_END }
|
||||
};
|
||||
|
||||
static const device_config_t ess_1688_config[] = {
|
||||
{
|
||||
.name = "base",
|
||||
.description = "Address",
|
||||
.type = CONFIG_HEX16,
|
||||
.default_string = "",
|
||||
.default_int = 0x220,
|
||||
.file_filter = "",
|
||||
.spinner = { 0 },
|
||||
.selection = {
|
||||
{
|
||||
.description = "0x220",
|
||||
.value = 0x220
|
||||
},
|
||||
{
|
||||
.description = "0x230",
|
||||
.value = 0x230
|
||||
},
|
||||
{
|
||||
.description = "0x240",
|
||||
.value = 0x240
|
||||
},
|
||||
{
|
||||
.description = "0x250",
|
||||
.value = 0x250
|
||||
},
|
||||
{ .description = "" }
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "irq",
|
||||
.description = "IRQ",
|
||||
.type = CONFIG_SELECTION,
|
||||
.default_string = "",
|
||||
.default_int = 5,
|
||||
.file_filter = "",
|
||||
.spinner = { 0 },
|
||||
.selection = {
|
||||
{
|
||||
.description = "IRQ 2",
|
||||
.value = 2
|
||||
},
|
||||
{
|
||||
.description = "IRQ 5",
|
||||
.value = 5
|
||||
},
|
||||
{
|
||||
.description = "IRQ 7",
|
||||
.value = 7
|
||||
},
|
||||
{
|
||||
.description = "IRQ 10",
|
||||
.value = 10
|
||||
},
|
||||
{ .description = "" }
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "dma",
|
||||
.description = "DMA",
|
||||
.type = CONFIG_SELECTION,
|
||||
.default_string = "",
|
||||
.default_int = 1,
|
||||
.file_filter = "",
|
||||
.spinner = { 0 },
|
||||
.selection = {
|
||||
{
|
||||
.description = "DMA 0",
|
||||
.value = 0
|
||||
},
|
||||
{
|
||||
.description = "DMA 1",
|
||||
.value = 1
|
||||
},
|
||||
{
|
||||
.description = "DMA 3",
|
||||
.value = 3
|
||||
},
|
||||
{ .description = "" }
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "opl",
|
||||
.description = "Enable OPL",
|
||||
.type = CONFIG_BINARY,
|
||||
.default_string = "",
|
||||
.default_int = 1
|
||||
},
|
||||
{
|
||||
.name = "receive_input",
|
||||
.description = "Receive input (SB MIDI)",
|
||||
.type = CONFIG_BINARY,
|
||||
.default_string = "",
|
||||
.default_int = 1
|
||||
},
|
||||
{ .name = "", .description = "", .type = CONFIG_END }
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
const device_t sb_1_device = {
|
||||
@@ -4360,3 +4878,17 @@ const device_t sb_awe64_gold_device = {
|
||||
.force_redraw = NULL,
|
||||
.config = sb_awe64_gold_config
|
||||
};
|
||||
|
||||
const device_t ess_1688_device = {
|
||||
.name = "ESS AudioDrive ES1688",
|
||||
.internal_name = "ess_es1688",
|
||||
.flags = DEVICE_ISA,
|
||||
.local = 0,
|
||||
.init = ess_1688_init,
|
||||
.close = sb_close,
|
||||
.reset = NULL,
|
||||
{ .available = NULL },
|
||||
.speed_changed = sb_speed_changed,
|
||||
.force_redraw = NULL,
|
||||
.config = ess_1688_config
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -163,6 +163,7 @@ static const SOUND_CARD sound_cards[] = {
|
||||
{ &es1371_device },
|
||||
{ &ad1881_device },
|
||||
{ &cs4297a_device },
|
||||
{ &ess_1688_device },
|
||||
{ NULL }
|
||||
// clang-format on
|
||||
};
|
||||
|
Reference in New Issue
Block a user