diff --git a/src/86box.c b/src/86box.c index 67d0bb165..61b81bbf3 100644 --- a/src/86box.c +++ b/src/86box.c @@ -208,6 +208,11 @@ int do_auto_pause = 0; /* (C) Auto-pa loss */ char uuid[MAX_UUID_LEN] = { '\0' }; /* (C) UUID or machine identifier */ +int other_ide_present = 0; /* IDE controllers from non-IDE cards are + present */ +int other_scsi_present = 0; /* SCSI controllers from non-SCSI cards are + present */ + /* Statistics. */ extern int mmuflush; extern int readlnum; @@ -1080,18 +1085,22 @@ pc_reset_hard_close(void) /* Close all the memory mappings. */ mem_close(); + suppress_overscan = 0; + /* Turn off timer processing to avoid potential segmentation faults. */ timer_close(); - suppress_overscan = 0; + lpt_devices_close(); + +#ifdef UNCOMMENT_LATER + lpt_close(); +#endif nvr_save(); nvr_close(); mouse_close(); - lpt_devices_close(); - device_close_all(); scsi_device_close_all(); @@ -1132,6 +1141,9 @@ pc_reset_hard_init(void) * modules that are. */ + /* Reset the IDE and SCSI presences */ + other_ide_present = other_scsi_present = 0; + /* Mark ACPI as unavailable */ acpi_enabled = 0; diff --git a/src/config.c b/src/config.c index df0ae1c35..02c1d276a 100644 --- a/src/config.c +++ b/src/config.c @@ -745,7 +745,6 @@ load_ports(void) char *p; char temp[512]; int c; - int d; memset(temp, 0, sizeof(temp)); @@ -768,14 +767,6 @@ load_ports(void) p = ini_section_get_string(cat, temp, "none"); lpt_ports[c].device = lpt_device_get_from_internal_name(p); } - - /* Legacy config compatibility. */ - d = ini_section_get_int(cat, "lpt_enabled", 2); - if (d < 2) { - for (c = 0; c < PARALLEL_MAX; c++) - lpt_ports[c].enabled = d; - } - ini_section_delete_var(cat, "lpt_enabled"); } /* Load "Storage Controllers" section. */ @@ -790,7 +781,7 @@ load_storage_controllers(void) int min = 0; int free_p = 0; - for (c = min; c < SCSI_BUS_MAX; c++) { + for (c = min; c < SCSI_CARD_MAX; c++) { sprintf(temp, "scsicard_%d", c + 1); p = ini_section_get_string(cat, temp, NULL); @@ -2288,7 +2279,7 @@ save_storage_controllers(void) ini_section_delete_var(cat, "scsicard"); - for (c = 0; c < SCSI_BUS_MAX; c++) { + for (c = 0; c < SCSI_CARD_MAX; c++) { sprintf(temp, "scsicard_%d", c + 1); if (scsi_card_current[c] == 0) diff --git a/src/device.c b/src/device.c index 321f105e5..14380f6c5 100644 --- a/src/device.c +++ b/src/device.c @@ -51,6 +51,8 @@ #include <86box/ini.h> #include <86box/config.h> #include <86box/device.h> +#include <86box/timer.h> +#include <86box/lpt.h> #include <86box/machine.h> #include <86box/mem.h> #include <86box/rom.h> @@ -345,6 +347,10 @@ device_reset_all(uint32_t match_flags) devices[c]->reset(device_priv[c]); } } + + /* TODO: Actually convert the LPT devices to device_t's. */ + if ((match_flags == DEVICE_ALL) || (match_flags == DEVICE_PCI)) + lpt_reset(); } void * diff --git a/src/dma.c b/src/dma.c index ebe75bba6..318c6cda2 100644 --- a/src/dma.c +++ b/src/dma.c @@ -22,6 +22,7 @@ #include #include #include +#define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include "x86.h" diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index 3f5f3f2ab..f76d70797 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -149,6 +149,8 @@ extern int confirm_reset; /* (C) enable reset confirmation */ extern int confirm_exit; /* (C) enable exit confirmation */ extern int confirm_save; /* (C) enable save confirmation */ extern int enable_discord; /* (C) enable Discord integration */ +extern int other_ide_present; /* IDE controllers from non-IDE cards are present */ +extern int other_scsi_present; /* SCSI controllers from non-SCSI cards are present */ extern int fixed_size_x; extern int fixed_size_y; diff --git a/src/include/86box/device.h b/src/include/86box/device.h index bd39a02e4..cc564ceb2 100644 --- a/src/include/86box/device.h +++ b/src/include/86box/device.h @@ -57,6 +57,7 @@ #define CONFIG_MIDI_OUT (3 | CONFIG_TYPE_INT) /* config_get_int() */ #define CONFIG_SPINNER (4 | CONFIG_TYPE_INT) /* config_get_int() */ #define CONFIG_MIDI_IN (5 | CONFIG_TYPE_INT) /* config_get_int() */ +#define CONFIG_MEMORY (6 | CONFIG_TYPE_INT) /* config_get_int() */ #define CONFIG_STRING (0 | CONFIG_TYPE_STRING) /* config_get_string() */ #define CONFIG_FNAME (1 | CONFIG_TYPE_STRING) /* config_get_string() */ diff --git a/src/include/86box/filters.h b/src/include/86box/filters.h index 0aa1c17f1..d11e79512 100644 --- a/src/include/86box/filters.h +++ b/src/include/86box/filters.h @@ -197,8 +197,8 @@ low_iir(int c, int i, double NewSample) 0.93726236021404663000 }; - static double y[4][2][NCoef + 1]; /* output samples */ - static double x[4][2][NCoef + 1]; /* input samples */ + static double y[5][2][NCoef + 1]; /* output samples */ + static double x[5][2][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ @@ -232,8 +232,8 @@ low_cut_iir(int c, int i, double NewSample) 0.93726236021916731000 }; - static double y[4][2][NCoef + 1]; /* output samples */ - static double x[4][2][NCoef + 1]; /* input samples */ + static double y[5][2][NCoef + 1]; /* output samples */ + static double x[5][2][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ @@ -266,8 +266,8 @@ high_iir(int c, int i, double NewSample) -1.36640781670578510000, 0.52352474706139873000 }; - static double y[4][2][NCoef + 1]; /* output samples */ - static double x[4][2][NCoef + 1]; /* input samples */ + static double y[5][2][NCoef + 1]; /* output samples */ + static double x[5][2][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ @@ -300,8 +300,8 @@ high_cut_iir(int c, int i, double NewSample) -1.36640781666419950000, 0.52352474703279628000 }; - static double y[4][2][NCoef + 1]; /* output samples */ - static double x[4][2][NCoef + 1]; /* input samples */ + static double y[5][2][NCoef + 1]; /* output samples */ + static double x[5][2][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ @@ -334,8 +334,8 @@ deemph_iir(int i, double NewSample) -1.05429146278569141337, 0.26412280202756849290 }; - static double y[4][NCoef + 1]; /* output samples */ - static double x[4][NCoef + 1]; /* input samples */ + static double y[5][NCoef + 1]; /* output samples */ + static double x[5][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ @@ -372,8 +372,8 @@ sb_iir(int c, int i, double NewSample) 0.55326988968868285000 }; - static double y[4][2][NCoef + 1]; /* output samples */ - static double x[4][2][NCoef + 1]; /* input samples */ + static double y[5][2][NCoef + 1]; /* output samples */ + static double x[5][2][NCoef + 1]; /* input samples */ int n; /* shift the old samples */ @@ -395,7 +395,7 @@ sb_iir(int c, int i, double NewSample) #define NCoef 1 #define SB16_NCoef 51 -extern double low_fir_sb16_coef[4][SB16_NCoef]; +extern double low_fir_sb16_coef[5][SB16_NCoef]; static inline double low_fir_sb16(int c, int i, double NewSample) @@ -422,28 +422,28 @@ low_fir_sb16(int c, int i, double NewSample) return out; } -extern double low_fir_pas16_coef[4][SB16_NCoef]; +extern double low_fir_pas16_coef[SB16_NCoef]; static inline double -low_fir_pas16(int c, int i, double NewSample) +low_fir_pas16(const int i, const double NewSample) { - static double x[4][2][SB16_NCoef + 1]; // input samples - static int pos[4] = { 0, 0, 0, 0 }; - double out = 0.0; + static double x[2][SB16_NCoef + 1]; // input samples + static int pos = 0; + double out = 0.0; int n; /* Calculate the new output */ - x[c][i][pos[c]] = NewSample; + x[i][pos] = NewSample; - for (n = 0; n < ((SB16_NCoef + 1) - pos[c]) && n < SB16_NCoef; n++) - out += low_fir_pas16_coef[c][n] * x[c][i][n + pos[c]]; + for (n = 0; n < ((SB16_NCoef + 1) - pos) && n < SB16_NCoef; n++) + out += low_fir_pas16_coef[n] * x[i][n + pos]; for (; n < SB16_NCoef; n++) - out += low_fir_pas16_coef[c][n] * x[c][i][(n + pos[c]) - (SB16_NCoef + 1)]; + out += low_fir_pas16_coef[n] * x[i][(n + pos) - (SB16_NCoef + 1)]; if (i == 1) { - pos[c]++; - if (pos[c] > SB16_NCoef) - pos[c] = 0; + pos++; + if (pos > SB16_NCoef) + pos = 0; } return out; diff --git a/src/include/86box/pit.h b/src/include/86box/pit.h index 4129a1093..eb03fd2c9 100644 --- a/src/include/86box/pit.h +++ b/src/include/86box/pit.h @@ -114,6 +114,7 @@ extern double AGPCLK; extern uint64_t PITCONST; extern uint64_t PAS16CONST; extern uint64_t PAS16CONST2; +extern uint64_t PASSCSICONST; extern uint64_t ISACONST; extern uint64_t CGACONST; extern uint64_t MDACONST; diff --git a/src/include/86box/scsi.h b/src/include/86box/scsi.h index 376ac79b9..32ad1f912 100644 --- a/src/include/86box/scsi.h +++ b/src/include/86box/scsi.h @@ -22,12 +22,14 @@ #define EMU_SCSI_H /* Configuration. */ -#define SCSI_BUS_MAX 4 /* currently we support up to 4 controllers */ +#define SCSI_CARD_MAX 4 +#define SCSI_BUS_MAX 9 /* currently we support up to 9 controllers: + up to 1 on-board + up to 4x pas plus/16 + up to 4 scsi controllers */ -#define SCSI_ID_MAX 16 /* 16 on wide buses */ -#define SCSI_LUN_MAX 8 /* always 8 */ +#define SCSI_ID_MAX 16 /* 16 on wide buses */ +#define SCSI_LUN_MAX 8 /* always 8 */ -extern int scsi_card_current[SCSI_BUS_MAX]; +extern int scsi_card_current[SCSI_CARD_MAX]; extern int scsi_card_available(int card); #ifdef EMU_DEVICE_H diff --git a/src/include/86box/scsi_ncr5380.h b/src/include/86box/scsi_ncr5380.h index a213e299d..55c3bf3c5 100644 --- a/src/include/86box/scsi_ncr5380.h +++ b/src/include/86box/scsi_ncr5380.h @@ -88,6 +88,7 @@ typedef struct ncr_t { uint8_t target_id; uint8_t tx_data; uint8_t msglun; + uint8_t irq_state; uint8_t command[20]; uint8_t msgout[4]; @@ -108,10 +109,14 @@ typedef struct ncr_t { int data_pos; int irq; + int simple_pseudo_dma; + + uint32_t block_count; double period; void *priv; + void (*dma_init_ext)(void *priv, void *ext_priv, int send); void (*dma_mode_ext)(void *priv, void *ext_priv); int (*dma_send_ext)(void *priv, void *ext_priv); int (*dma_initiator_receive_ext)(void *priv, void *ext_priv); @@ -121,10 +126,12 @@ typedef struct ncr_t { extern int ncr5380_cmd_len[8]; extern void ncr5380_irq(ncr_t *ncr, int set_irq); +extern void ncr5380_set_irq(ncr_t *ncr, int irq); extern uint32_t ncr5380_get_bus_host(ncr_t *ncr); extern void ncr5380_bus_read(ncr_t *ncr); extern void ncr5380_bus_update(ncr_t *ncr, int bus); extern void ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr); +extern uint8_t ncr5380_drq(ncr_t *ncr); extern uint8_t ncr5380_read(uint16_t port, ncr_t *ncr); #ifdef EMU_DEVICE_H @@ -138,6 +145,7 @@ extern const device_t scsi_ls2000_device; #if defined(DEV_BRANCH) && defined(USE_SUMO) extern const device_t scsi_scsiat_device; #endif +extern const device_t scsi_pas_device; #endif #endif /*SCSI_NCR5380_H*/ diff --git a/src/include/86box/scsi_ncr53c400.h b/src/include/86box/scsi_ncr53c400.h new file mode 100644 index 000000000..9a5315990 --- /dev/null +++ b/src/include/86box/scsi_ncr53c400.h @@ -0,0 +1,62 @@ +/* + * 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. + * + * Implementation of the NCR 53c400 series of SCSI Host Adapters + * made by NCR. These controllers were designed for the ISA and MCA bus. + * + * Authors: Sarah Walker, + * TheCollector1995, + * Fred N. van Kempen, + * + * Copyright 2017-2018 Sarah Walker. + * Copyright 2017-2018 Fred N. van Kempen. + * Copyright 2017-2024 TheCollector1995. + */ + +#ifndef SCSI_NCR53C400_H +#define SCSI_NCR53C400_H + +typedef struct ncr53c400_t { + rom_t bios_rom; + mem_mapping_t mapping; + ncr_t ncr; + uint8_t buffer[512]; + uint8_t int_ram[0x40]; + uint8_t ext_ram[0x600]; + + uint32_t rom_addr; + uint16_t base; + + int8_t type; + uint8_t block_count; + uint8_t status_ctrl; + + int simple_ctrl; + + int block_count_loaded; + + int buffer_pos; + int buffer_host_pos; + + int busy; + uint8_t pos_regs[8]; + + pc_timer_t timer; +} ncr53c400_t; + +#define CTRL_DATA_DIR 0x40 +#define STATUS_BUFFER_NOT_READY 0x04 +#define STATUS_5380_ACCESSIBLE 0x80 + +extern void ncr53c400_simple_write(uint8_t val, void *priv); +extern void ncr53c400_write(uint32_t addr, uint8_t val, void *priv); +extern uint8_t ncr53c400_simple_read(void *priv); +extern uint8_t ncr53c400_read(uint32_t addr, void *priv); +extern void ncr53c400_callback(void *priv); + +#endif /*SCSI_NCR53C400_H*/ diff --git a/src/include/86box/snd_emu8k.h b/src/include/86box/snd_emu8k.h index d226ed8db..4bad23b97 100644 --- a/src/include/86box/snd_emu8k.h +++ b/src/include/86box/snd_emu8k.h @@ -390,12 +390,12 @@ typedef struct emu8k_t { int16_t out_r; emu8k_chorus_eng_t chorus_engine; - int32_t chorus_in_buffer[SOUNDBUFLEN]; + int32_t chorus_in_buffer[WTBUFLEN]; emu8k_reverb_eng_t reverb_engine; - int32_t reverb_in_buffer[SOUNDBUFLEN]; + int32_t reverb_in_buffer[WTBUFLEN]; int pos; - int32_t buffer[SOUNDBUFLEN * 2]; + int32_t buffer[WTBUFLEN * 2]; uint16_t addr; } emu8k_t; diff --git a/src/include/86box/snd_sb_dsp.h b/src/include/86box/snd_sb_dsp.h index a00f512a3..c179fd197 100644 --- a/src/include/86box/snd_sb_dsp.h +++ b/src/include/86box/snd_sb_dsp.h @@ -4,13 +4,16 @@ #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 */ +#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_ES688 3 /* ESS Technology ES688 */ +#define SB_SUBTYPE_ESS_ES1688 4 /* ESS Technology ES1688 */ /* ESS-related */ -#define IS_ESS(dsp) ((dsp)->sb_subtype == SB_SUBTYPE_ESS_ES1688) /* check for future ESS cards here */ +#define IS_ESS(dsp) ((dsp)->sb_subtype >= SB_SUBTYPE_ESS_ES688) /* Check for future ESS cards here */ +#define IS_NOT_ESS(dsp) ((dsp)->sb_subtype < SB_SUBTYPE_ESS_ES688) /* 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 */ @@ -51,6 +54,10 @@ typedef struct sb_dsp_t { void *dma_priv; uint8_t sb_read_data[256]; + + uint8_t dma_ff; + int dma_data; + int sb_read_wp; int sb_read_rp; int sb_speaker; @@ -116,6 +123,8 @@ typedef struct sb_dsp_t { int sbenable; int sb_enable_i; + int state; + pc_timer_t output_timer; pc_timer_t input_timer; diff --git a/src/include/86box/sound.h b/src/include/86box/sound.h index 5f04c10fe..9ede638e5 100644 --- a/src/include/86box/sound.h +++ b/src/include/86box/sound.h @@ -39,6 +39,9 @@ extern int sound_gain; #define CD_FREQ FREQ_44100 #define CD_BUFLEN (CD_FREQ / 10) +#define WT_FREQ FREQ_44100 +#define WTBUFLEN (MUSIC_FREQ / 45) + enum { SOUND_NONE = 0, SOUND_INTERNAL @@ -50,7 +53,9 @@ extern int speakval; extern int speakon; extern int sound_pos_global; + extern int music_pos_global; +extern int wavetable_pos_global; extern int sound_card_current[SOUND_CARD_MAX]; @@ -62,6 +67,10 @@ extern void music_add_handler(void (*get_buffer)(int32_t *buffer, int len, void *priv), void *priv); +extern void wavetable_add_handler(void (*get_buffer)(int32_t *buffer, + int len, void *priv), + void *priv); + extern void sound_set_cd_audio_filter(void (*filter)(int channel, double *buffer, void *priv), void *priv); @@ -94,9 +103,10 @@ extern void sound_cd_thread_reset(void); extern void closeal(void); extern void inital(void); -extern void givealbuffer(void *buf); -extern void givealbuffer_music(void *buf); -extern void givealbuffer_cd(void *buf); +extern void givealbuffer(const void *buf); +extern void givealbuffer_music(const void *buf); +extern void givealbuffer_wt(const void *buf); +extern void givealbuffer_cd(const void *buf); #define sb_vibra16c_onboard_relocate_base sb_vibra16s_onboard_relocate_base extern void sb_vibra16s_onboard_relocate_base(uint16_t new_addr, void *priv); @@ -113,30 +123,16 @@ extern const device_t acermagic_s20_device; extern const device_t mirosound_pcm10_device; extern const device_t azt1605_device; -/* Ensoniq AudioPCI */ -extern const device_t es1371_device; -extern const device_t es1371_onboard_device; +/* C-Media CMI8x38 */ +extern const device_t cmi8338_device; +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; /* Creative Labs Game Blaster */ extern const device_t cms_device; -/* Gravis UltraSound and UltraSound Max */ -extern const device_t gus_device; - -/* Pro Audio Spectrum 16 */ -extern const device_t pasplus_device; -extern const device_t pas16_device; - -/* IBM PS/1 Audio Card */ -extern const device_t ps1snd_device; - -/* Tandy PSSJ */ -extern const device_t pssj_device; -extern const device_t pssj_isa_device; - -/* Tandy PSG */ -extern const device_t tndy_device; - /* Creative Labs Sound Blaster */ extern const device_t sb_1_device; extern const device_t sb_15_device; @@ -163,13 +159,6 @@ extern const device_t sb_awe64_value_device; extern const device_t sb_awe64_device; extern const device_t sb_awe64_gold_device; -/* Innovation SSI-2001 */ -extern const device_t ssi2001_device; - -/* Windows Sound System */ -extern const device_t wss_device; -extern const device_t ncr_business_audio_device; - /* Crystal CS423x */ extern const device_t cs4235_device; extern const device_t cs4235_onboard_device; @@ -177,15 +166,43 @@ extern const device_t cs4236b_device; extern const device_t cs4237b_device; extern const device_t cs4238b_device; -/* C-Media CMI8x38 */ -extern const device_t cmi8338_device; -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_688_device; +extern const device_t ess_ess0100_pnp_device; extern const device_t ess_1688_device; +extern const device_t ess_ess0102_pnp_device; +extern const device_t ess_ess0968_pnp_device; +extern const device_t ess_soundpiper_16_mca_device; +extern const device_t ess_soundpiper_32_mca_device; +extern const device_t ess_chipchat_16_mca_device; + +/* Ensoniq AudioPCI */ +extern const device_t es1371_device; +extern const device_t es1371_onboard_device; + +/* Gravis UltraSound and UltraSound Max */ +extern const device_t gus_device; + +/* IBM PS/1 Audio Card */ +extern const device_t ps1snd_device; + +/* Innovation SSI-2001 */ +extern const device_t ssi2001_device; + +/* Pro Audio Spectrum 16 */ +extern const device_t pasplus_device; +extern const device_t pas16_device; + +/* Tandy PSSJ */ +extern const device_t pssj_device; +extern const device_t pssj_isa_device; + +/* Tandy PSG */ +extern const device_t tndy_device; + +/* Windows Sound System */ +extern const device_t wss_device; +extern const device_t ncr_business_audio_device; #endif diff --git a/src/pit.c b/src/pit.c index 7ae50f413..0816094cb 100644 --- a/src/pit.c +++ b/src/pit.c @@ -49,6 +49,7 @@ double cpuclock; double PITCONSTD; double PAS16CONSTD; double PAS16CONST2D; +double PASSCSICONSTD; double SYSCLK; double isa_timing; double bus_timing; @@ -60,6 +61,7 @@ double AGPCLK; uint64_t PITCONST; uint64_t PAS16CONST; uint64_t PAS16CONST2; +uint64_t PASSCSICONST; uint64_t ISACONST; uint64_t CGACONST; uint64_t MDACONST; @@ -1222,6 +1224,9 @@ pit_set_clock(uint32_t clock) PAS16CONST2D = (cpuclock / 1008000.0); PAS16CONST2 = (uint64_t) (PAS16CONST2D * (double) (1ULL << 32)); + PASSCSICONSTD = (cpuclock / (28224000.0 / 14.0)); + PASSCSICONST = (uint64_t) (PASSCSICONSTD * (double) (1ULL << 32)); + isa_timing = (cpuclock / (double) cpu_isa_speed); if (cpu_64bitbus) bus_timing = (cpuclock / (cpu_busspeed / 2)); diff --git a/src/qt/qt_deviceconfig.cpp b/src/qt/qt_deviceconfig.cpp index d2ae70245..def3a4385 100644 --- a/src/qt/qt_deviceconfig.cpp +++ b/src/qt/qt_deviceconfig.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -29,9 +30,7 @@ #include #include #include -#include #include -#include extern "C" { #include <86box/86box.h> @@ -87,10 +86,15 @@ EnumerateSerialDevices() #ifdef Q_OS_WINDOWS for (int i = 1; i < 256; i++) { devstr[0] = 0; - snprintf(devstr.data(), 1024, "\\\\.\\COM%d", i); - auto handle = CreateFileA(devstr.data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0); - auto dwError = GetLastError(); - if (handle != INVALID_HANDLE_VALUE || (handle == INVALID_HANDLE_VALUE && ((dwError == ERROR_ACCESS_DENIED) || (dwError == ERROR_GEN_FAILURE) || (dwError == ERROR_SHARING_VIOLATION) || (dwError == ERROR_SEM_TIMEOUT)))) { + snprintf(devstr.data(), 1024, R"(\\.\COM%d)", i); + const auto handle = CreateFileA(devstr.data(), + GENERIC_READ | GENERIC_WRITE, 0, + nullptr, OPEN_EXISTING, + 0, nullptr); + const auto dwError = GetLastError(); + if ((handle != INVALID_HANDLE_VALUE) || (dwError == ERROR_ACCESS_DENIED) || + (dwError == ERROR_GEN_FAILURE) || (dwError == ERROR_SHARING_VIOLATION) || + (dwError == ERROR_SEM_TIMEOUT)) { if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle); serialDevices.push_back(QString(devstr)); } @@ -108,233 +112,266 @@ EnumerateSerialDevices() } void -DeviceConfig::ConfigureDevice(const _device_ *device, int instance, Settings *settings) +DeviceConfig::ProcessConfig(void *dc, const void *c, const bool is_dep) { - DeviceConfig dc(settings); - dc.setWindowTitle(QString("%1 Device Configuration").arg(device->name)); - int p; - int q; + auto * device_context = static_cast(dc); + const auto * config = static_cast(c); + const QString blank = ""; + int p; + int q; - device_context_t device_context; - device_set_context(&device_context, device, instance); - - auto device_label = new QLabel(device->name); - device_label->setAlignment(Qt::AlignCenter); - dc.ui->formLayout->addRow(device_label); - auto line = new QFrame; - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - dc.ui->formLayout->addRow(line); - const auto *config = device->config; while (config->type != -1) { + const int config_type = config->type & CONFIG_TYPE_MASK; + + /* Ignore options of the wrong class. */ + if (!!(config->type & CONFIG_DEP) != is_dep) + continue; + + /* If this is a BIOS-dependent option and it's BIOS, ignore it. */ + if (!!(config->type & CONFIG_DEP) && (config_type == CONFIG_BIOS)) + continue; + + const int config_major_type = (config_type >> CONFIG_SHIFT) << CONFIG_SHIFT; + + int value = 0; + auto selected = static_cast(blank.toStdString().c_str()); + + switch (config_major_type) { + default: + break; + case CONFIG_TYPE_INT: + value = config_get_int(device_context->name, const_cast(config->name), + config->default_int); + break; + case CONFIG_TYPE_HEX16: + value = config_get_hex16(device_context->name, const_cast(config->name), + config->default_int); + break; + case CONFIG_TYPE_HEX20: + value = config_get_hex20(device_context->name, const_cast(config->name), + config->default_int); + break; + case CONFIG_TYPE_STRING: + selected = config_get_string(device_context->name, const_cast(config->name), + const_cast(config->default_string)); + break; + } + switch (config->type) { + default: + break; case CONFIG_BINARY: - { - auto value = config_get_int(device_context.name, const_cast(config->name), config->default_int); - auto *cbox = new QCheckBox(); - cbox->setObjectName(config->name); - cbox->setChecked(value > 0); - dc.ui->formLayout->addRow(config->description, cbox); - break; - } + { + auto *cbox = new QCheckBox(); + cbox->setObjectName(config->name); + cbox->setChecked(value > 0); + this->ui->formLayout->addRow(config->description, cbox); + break; + } #ifdef USE_RTMIDI case CONFIG_MIDI_OUT: - { - auto *cbox = new QComboBox(); - cbox->setObjectName(config->name); - cbox->setMaxVisibleItems(30); - auto *model = cbox->model(); - int currentIndex = -1; - int selected = config_get_int(device_context.name, const_cast(config->name), config->default_int); - for (int i = 0; i < rtmidi_out_get_num_devs(); i++) { - char midiName[512] = { 0 }; - rtmidi_out_get_dev_name(i, midiName); + { + auto *cbox = new QComboBox(); + cbox->setObjectName(config->name); + cbox->setMaxVisibleItems(30); + auto *model = cbox->model(); + int currentIndex = -1; + for (int i = 0; i < rtmidi_out_get_num_devs(); i++) { + char midiName[512] = { 0 }; + rtmidi_out_get_dev_name(i, midiName); - Models::AddEntry(model, midiName, i); - if (selected == i) { - currentIndex = i; - } - } - dc.ui->formLayout->addRow(config->description, cbox); - cbox->setCurrentIndex(currentIndex); - break; + Models::AddEntry(model, midiName, i); + if (i == value) + currentIndex = i; } + this->ui->formLayout->addRow(config->description, cbox); + cbox->setCurrentIndex(currentIndex); + break; + } case CONFIG_MIDI_IN: - { - auto *cbox = new QComboBox(); - cbox->setObjectName(config->name); - cbox->setMaxVisibleItems(30); - auto *model = cbox->model(); - int currentIndex = -1; - int selected = config_get_int(device_context.name, const_cast(config->name), config->default_int); - for (int i = 0; i < rtmidi_in_get_num_devs(); i++) { - char midiName[512] = { 0 }; - rtmidi_in_get_dev_name(i, midiName); + { + auto *cbox = new QComboBox(); + cbox->setObjectName(config->name); + cbox->setMaxVisibleItems(30); + auto *model = cbox->model(); + int currentIndex = -1; + for (int i = 0; i < rtmidi_in_get_num_devs(); i++) { + char midiName[512] = { 0 }; + rtmidi_in_get_dev_name(i, midiName); - Models::AddEntry(model, midiName, i); - if (selected == i) { - currentIndex = i; - } - } - dc.ui->formLayout->addRow(config->description, cbox); - cbox->setCurrentIndex(currentIndex); - break; + Models::AddEntry(model, midiName, i); + if (i == value) + currentIndex = i; } + this->ui->formLayout->addRow(config->description, cbox); + cbox->setCurrentIndex(currentIndex); + break; + } #endif + case CONFIG_INT: case CONFIG_SELECTION: case CONFIG_HEX16: case CONFIG_HEX20: - { - auto *cbox = new QComboBox(); - cbox->setObjectName(config->name); - cbox->setMaxVisibleItems(30); - auto *model = cbox->model(); - int currentIndex = -1; - int selected = 0; - switch (config->type) { - case CONFIG_SELECTION: - selected = config_get_int(device_context.name, const_cast(config->name), config->default_int); - break; - case CONFIG_HEX16: - selected = config_get_hex16(device_context.name, const_cast(config->name), config->default_int); - break; - case CONFIG_HEX20: - selected = config_get_hex20(device_context.name, const_cast(config->name), config->default_int); - break; - } + { + auto *cbox = new QComboBox(); + cbox->setObjectName(config->name); + cbox->setMaxVisibleItems(30); + auto *model = cbox->model(); + int currentIndex = -1; - for (auto *sel = config->selection; (sel != nullptr) && (sel->description != nullptr) && (strlen(sel->description) > 0); ++sel) { - int row = Models::AddEntry(model, sel->description, sel->value); - if (selected == sel->value) { - currentIndex = row; - } - } - dc.ui->formLayout->addRow(config->description, cbox); - cbox->setCurrentIndex(currentIndex); - break; + for (auto *sel = config->selection; (sel != nullptr) && (sel->description != nullptr) && + (strlen(sel->description) > 0); ++sel) { + int row = Models::AddEntry(model, sel->description, sel->value); + + if (sel->value == value) + currentIndex = row; } + this->ui->formLayout->addRow(config->description, cbox); + cbox->setCurrentIndex(currentIndex); + break; + } case CONFIG_BIOS: - { - auto *cbox = new QComboBox(); - cbox->setObjectName(config->name); - cbox->setMaxVisibleItems(30); - auto *model = cbox->model(); - int currentIndex = -1; - char *selected; - selected = config_get_string(device_context.name, const_cast(config->name), const_cast(config->default_string)); + { + auto *cbox = new QComboBox(); + cbox->setObjectName(config->name); + cbox->setMaxVisibleItems(30); + auto *model = cbox->model(); + int currentIndex = -1; - q = 0; - for (auto *bios = config->bios; (bios != nullptr) && (bios->name != nullptr) && (strlen(bios->name) > 0); ++bios) { - p = 0; - for (int d = 0; d < bios->files_no; d++) - p += !!rom_present(const_cast(bios->files[d])); - if (p == bios->files_no) { - int row = Models::AddEntry(model, bios->name, q); - if (!strcmp(selected, bios->internal_name)) { - currentIndex = row; - } - } - q++; - } - dc.ui->formLayout->addRow(config->description, cbox); - cbox->setCurrentIndex(currentIndex); - break; - } - case CONFIG_SPINNER: - { - int value = config_get_int(device_context.name, const_cast(config->name), config->default_int); - auto *spinBox = new QSpinBox(); - spinBox->setObjectName(config->name); - spinBox->setMaximum(config->spinner.max); - spinBox->setMinimum(config->spinner.min); - if (config->spinner.step > 0) { - spinBox->setSingleStep(config->spinner.step); - } - spinBox->setValue(value); - dc.ui->formLayout->addRow(config->description, spinBox); - break; - } - case CONFIG_FNAME: - { - auto *fileName = config_get_string(device_context.name, const_cast(config->name), const_cast(config->default_string)); - auto *fileField = new FileField(); - fileField->setObjectName(config->name); - fileField->setFileName(fileName); - fileField->setFilter(QString(config->file_filter).left(strcspn(config->file_filter, "|"))); - dc.ui->formLayout->addRow(config->description, fileField); - break; - } - case CONFIG_STRING: - { - auto lineEdit = new QLineEdit; - lineEdit->setObjectName(config->name); - lineEdit->setCursor(Qt::IBeamCursor); - lineEdit->setText(config_get_string(device_context.name, const_cast(config->name), const_cast(config->default_string))); - dc.ui->formLayout->addRow(config->description, lineEdit); - break; - } - case CONFIG_SERPORT: - { - auto *cbox = new QComboBox(); - cbox->setObjectName(config->name); - cbox->setMaxVisibleItems(30); - auto *model = cbox->model(); - int currentIndex = 0; - auto serialDevices = EnumerateSerialDevices(); - char *selected = config_get_string(device_context.name, const_cast(config->name), const_cast(config->default_string)); - - Models::AddEntry(model, "None", -1); - for (int i = 0; i < serialDevices.size(); i++) { - int row = Models::AddEntry(model, serialDevices[i], i); - if (selected == serialDevices[i]) { + q = 0; + for (auto *bios = config->bios; (bios != nullptr) && (bios->name != nullptr) && + (strlen(bios->name) > 0); ++bios) { + p = 0; + for (int d = 0; d < bios->files_no; d++) + p += !!rom_present(const_cast(bios->files[d])); + if (p == bios->files_no) { + const int row = Models::AddEntry(model, bios->name, q); + if (!strcmp(selected, bios->internal_name)) currentIndex = row; - } } - - dc.ui->formLayout->addRow(config->description, cbox); - cbox->setCurrentIndex(currentIndex); - break; + q++; } + this->ui->formLayout->addRow(config->description, cbox); + cbox->setCurrentIndex(currentIndex); + break; + } + case CONFIG_SPINNER: + { + auto *spinBox = new QSpinBox(); + spinBox->setObjectName(config->name); + spinBox->setMaximum(config->spinner.max); + spinBox->setMinimum(config->spinner.min); + if (config->spinner.step > 0) + spinBox->setSingleStep(config->spinner.step); + spinBox->setValue(value); + this->ui->formLayout->addRow(config->description, spinBox); + break; + } + case CONFIG_FNAME: + { + auto *fileField = new FileField(); + fileField->setObjectName(config->name); + fileField->setFileName(selected); + fileField->setFilter(QString(config->file_filter).left(static_cast(strcspn(config->file_filter, + "|")))); + this->ui->formLayout->addRow(config->description, fileField); + break; + } + case CONFIG_STRING: + { + const auto lineEdit = new QLineEdit; + lineEdit->setObjectName(config->name); + lineEdit->setCursor(Qt::IBeamCursor); + lineEdit->setText(selected); + this->ui->formLayout->addRow(config->description, lineEdit); + break; + } + case CONFIG_SERPORT: + { + auto *cbox = new QComboBox(); + cbox->setObjectName(config->name); + cbox->setMaxVisibleItems(30); + auto *model = cbox->model(); + int currentIndex = 0; + auto serialDevices = EnumerateSerialDevices(); + + Models::AddEntry(model, "None", -1); + for (int i = 0; i < serialDevices.size(); i++) { + const int row = Models::AddEntry(model, serialDevices[i], i); + if (selected == serialDevices[i]) + currentIndex = row; + } + + this->ui->formLayout->addRow(config->description, cbox); + cbox->setCurrentIndex(currentIndex); + break; + } case CONFIG_MAC: { // QHBoxLayout for the line edit widget and the generate button - auto hboxLayout = new QHBoxLayout(); - auto generateButton = new QPushButton(tr("Generate")); - auto lineEdit = new QLineEdit; + const auto hboxLayout = new QHBoxLayout(); + const auto generateButton = new QPushButton(tr("Generate")); + const auto lineEdit = new QLineEdit; // Allow the line edit to expand and fill available space lineEdit->setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::Preferred); lineEdit->setInputMask("HH:HH:HH;0"); lineEdit->setObjectName(config->name); // Display the current or generated MAC in uppercase // When stored it will be converted to lowercase - if (config_get_mac(device_context.name, config->name, config->default_int) & 0xFF000000) { - lineEdit->setText(QString::asprintf("%02X:%02X:%02X", random_generate(), random_generate(), random_generate())); + if (config_get_mac(device_context->name, config->name, + config->default_int) & 0xFF000000) { + lineEdit->setText(QString::asprintf("%02X:%02X:%02X", random_generate(), + random_generate(), random_generate())); } else { - auto current_mac = QString(config_get_string(device_context.name, config->name, const_cast(config->default_string))); + auto current_mac = QString(config_get_string(device_context->name, config->name, + const_cast(config->default_string))); lineEdit->setText(current_mac.toUpper()); } // Action for the generate button connect(generateButton, &QPushButton::clicked, [lineEdit] { - lineEdit->setText(QString::asprintf("%02X:%02X:%02X", random_generate(), random_generate(), random_generate())); + lineEdit->setText(QString::asprintf("%02X:%02X:%02X", random_generate(), + random_generate(), random_generate())); }); hboxLayout->addWidget(lineEdit); hboxLayout->addWidget(generateButton); - dc.ui->formLayout->addRow(config->description, hboxLayout); + this->ui->formLayout->addRow(config->description, hboxLayout); break; } } ++config; } +} + +void +DeviceConfig::ConfigureDevice(const _device_ *device, int instance, Settings *settings) +{ + DeviceConfig dc(settings); + dc.setWindowTitle(QString("%1 Device Configuration").arg(device->name)); + + device_context_t device_context; + device_set_context(&device_context, device, instance); + + const auto device_label = new QLabel(device->name); + device_label->setAlignment(Qt::AlignCenter); + dc.ui->formLayout->addRow(device_label); + const auto line = new QFrame; + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + dc.ui->formLayout->addRow(line); + const _device_config_ *config = device->config; + + dc.ProcessConfig(&device_context, config, false); dc.setFixedSize(dc.minimumSizeHint()); - int res = dc.exec(); - if (res == QDialog::Accepted) { + if (dc.exec() == QDialog::Accepted) { config = device->config; while (config->type != -1) { switch (config->type) { + default: + break; case CONFIG_BINARY: { - auto *cbox = dc.findChild(config->name); + const auto *cbox = dc.findChild(config->name); config_set_int(device_context.name, const_cast(config->name), cbox->isChecked() ? 1 : 0); break; } @@ -408,15 +445,15 @@ DeviceConfig::ConfigureDevice(const _device_ *device, int instance, Settings *se } QString -DeviceConfig::DeviceName(const _device_ *device, const char *internalName, int bus) +DeviceConfig::DeviceName(const _device_ *device, const char *internalName, const int bus) { - if (QStringLiteral("none") == internalName) { + if (QStringLiteral("none") == internalName) return tr("None"); - } else if (QStringLiteral("internal") == internalName) { + else if (QStringLiteral("internal") == internalName) return tr("Internal controller"); - } else if (device == nullptr) { - return QString(); - } else { + else if (device == nullptr) + return ""; + else { char temp[512]; device_get_name(device, bus, temp); return tr(temp, nullptr, 512); diff --git a/src/qt/qt_deviceconfig.hpp b/src/qt/qt_deviceconfig.hpp index 1ed24d618..a16c152a6 100644 --- a/src/qt/qt_deviceconfig.hpp +++ b/src/qt/qt_deviceconfig.hpp @@ -20,13 +20,15 @@ class DeviceConfig : public QDialog { public: explicit DeviceConfig(QWidget *parent = nullptr); - ~DeviceConfig(); + ~DeviceConfig() override; - static void ConfigureDevice(const _device_ *device, int instance = 0, Settings *settings = nullptr); + static void ConfigureDevice(const _device_ *device, int instance = 0, + Settings *settings = nullptr); static QString DeviceName(const _device_ *device, const char *internalName, int bus); private: Ui::DeviceConfig *ui; + void ProcessConfig(void *dc, const void *c, bool is_dep); }; #endif // QT_DEVICECONFIG_HPP diff --git a/src/qt/qt_harddrive_common.cpp b/src/qt/qt_harddrive_common.cpp index 213ebf07b..e0b0233f1 100644 --- a/src/qt/qt_harddrive_common.cpp +++ b/src/qt/qt_harddrive_common.cpp @@ -20,6 +20,7 @@ extern "C" { #include <86box/hdd.h> +#include <86box/scsi.h> #include <86box/cdrom.h> } @@ -109,9 +110,16 @@ Harddrives::populateBusChannels(QAbstractItemModel *model, int bus, SettingsBusT QList channelsInUse; switch (bus) { case HDD_BUS_MFM: + busRows = 2; + busesToCheck.append(HDD_BUS_MFM); + break; case HDD_BUS_XTA: + busRows = 2; + busesToCheck.append(HDD_BUS_XTA); + break; case HDD_BUS_ESDI: busRows = 2; + busesToCheck.append(HDD_BUS_ESDI); break; case HDD_BUS_IDE: busRows = 8; @@ -126,7 +134,7 @@ Harddrives::populateBusChannels(QAbstractItemModel *model, int bus, SettingsBusT case HDD_BUS_SCSI: shifter = 4; orer = 15; - busRows = 64; + busRows = /*64*/ SCSI_BUS_MAX * SCSI_ID_MAX; subChannelWidth = 2; busesToCheck.append(HDD_BUS_SCSI); break; diff --git a/src/qt/qt_machinestatus.cpp b/src/qt/qt_machinestatus.cpp index dee487657..53e875e33 100644 --- a/src/qt/qt_machinestatus.cpp +++ b/src/qt/qt_machinestatus.cpp @@ -272,13 +272,13 @@ MachineStatus::hasCassette() bool MachineStatus::hasIDE() { - return machine_has_flags(machine, MACHINE_IDE_QUAD) > 0; + return (machine_has_flags(machine, MACHINE_IDE_QUAD) > 0) || other_ide_present; } bool MachineStatus::hasSCSI() { - return machine_has_flags(machine, MACHINE_SCSI) > 0; + return (machine_has_flags(machine, MACHINE_SCSI) > 0) || other_scsi_present; } void diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 78eaff5e9..dc8e1970f 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -91,26 +91,23 @@ void qt_set_sequence_auto_mnemonic(bool b); void main_thread_fn() { - uint64_t old_time; - uint64_t new_time; - int drawits; int frames; QThread::currentThread()->setPriority(QThread::HighestPriority); - plat_set_thread_name(NULL, "main_thread_fn"); + plat_set_thread_name(nullptr, "main_thread_fn"); framecountx = 0; // title_update = 1; - old_time = elapsed_timer.elapsed(); - drawits = frames = 0; + uint64_t old_time = elapsed_timer.elapsed(); + int drawits = frames = 0; while (!is_quit && cpu_thread_run) { /* See if it is time to run a frame of code. */ - new_time = elapsed_timer.elapsed(); + const uint64_t new_time = elapsed_timer.elapsed(); #ifdef USE_GDBSTUB if (gdbstub_next_asap && (drawits <= 0)) drawits = 10; else #endif - drawits += (new_time - old_time); + drawits += static_cast(new_time - old_time); old_time = new_time; if (drawits > 0 && !dopause) { /* Yes, so do one frame now. */ @@ -395,7 +392,7 @@ main(int argc, char *argv[]) plat_pause(0); }); - auto ret = app.exec(); + const auto ret = app.exec(); cpu_thread_run = 0; main_thread->join(); pc_close(nullptr); diff --git a/src/qt/qt_mainwindow.cpp b/src/qt/qt_mainwindow.cpp index f4c01b4a2..c48dfd052 100644 --- a/src/qt/qt_mainwindow.cpp +++ b/src/qt/qt_mainwindow.cpp @@ -91,6 +91,7 @@ extern int qt_nvr_save(void); #endif #include +#include #include #include "qt_settings.hpp" @@ -289,14 +290,15 @@ MainWindow::MainWindow(QWidget *parent) resizableonce = true; } if (!QApplication::platformName().contains("eglfs") && vid_resize != 1) { - w = (w / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.)); + w = static_cast(w / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.)); - int modifiedHeight = (h / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.)) + const int modifiedHeight = + static_cast(h / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.)) + menuBar()->height() + (statusBar()->height() * !hide_status_bar) + (ui->toolBar->height() * !hide_tool_bar); - ui->stackedWidget->resize(w, (h / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.))); + ui->stackedWidget->resize(w, static_cast(h / (!dpi_scale ? util::screenOfWidget(this)->devicePixelRatio() : 1.))); setFixedSize(w, modifiedHeight); } }); @@ -306,9 +308,9 @@ MainWindow::MainWindow(QWidget *parent) #ifdef QT_RESIZE_DEBUG qDebug() << "Resize"; #endif - w = (w / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.)); + w = static_cast(w / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.)); - int modifiedHeight = (h / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.)); + int modifiedHeight = static_cast(h / (!dpi_scale ? util::screenOfWidget(renderers[monitor_index].get())->devicePixelRatio() : 1.)); renderers[monitor_index]->setFixedSize(w, modifiedHeight); } @@ -394,9 +396,7 @@ MainWindow::MainWindow(QWidget *parent) ui->actionVulkan->setVisible(false); } - QActionGroup *actGroup = nullptr; - - actGroup = new QActionGroup(this); + auto actGroup = new QActionGroup(this); actGroup->addAction(ui->actionSoftware_Renderer); actGroup->addAction(ui->actionHardware_Renderer_OpenGL); actGroup->addAction(ui->actionHardware_Renderer_OpenGL_ES); @@ -418,6 +418,8 @@ MainWindow::MainWindow(QWidget *parent) #endif RendererStack::Renderer newVidApi = RendererStack::Renderer::Software; switch (vid_api) { + default: + break; case 0: newVidApi = RendererStack::Renderer::Software; break; @@ -457,7 +459,7 @@ MainWindow::MainWindow(QWidget *parent) }); /* Trigger initial renderer switch */ - for (auto action : actGroup->actions()) + for (const auto action : actGroup->actions()) if (action->property("vid_api").toInt() == vid_api) { action->setChecked(true); emit actGroup->triggered(action); @@ -465,6 +467,8 @@ MainWindow::MainWindow(QWidget *parent) } switch (scale) { + default: + break; case 0: ui->action0_5x->setChecked(true); break; @@ -508,6 +512,8 @@ MainWindow::MainWindow(QWidget *parent) actGroup->addAction(ui->action7x); actGroup->addAction(ui->action8x); switch (video_filter_method) { + default: + break; case 0: ui->actionNearest->setChecked(true); break; @@ -519,6 +525,8 @@ MainWindow::MainWindow(QWidget *parent) actGroup->addAction(ui->actionNearest); actGroup->addAction(ui->actionLinear); switch (video_fullscreen_scale) { + default: + break; case FULLSCR_SCALE_FULL: ui->actionFullScreen_stretch->setChecked(true); break; @@ -542,6 +550,8 @@ MainWindow::MainWindow(QWidget *parent) actGroup->addAction(ui->actionFullScreen_int); actGroup->addAction(ui->actionFullScreen_int43); switch (video_grayscale) { + default: + break; case 0: ui->actionRGB_Color->setChecked(true); break; @@ -565,6 +575,8 @@ MainWindow::MainWindow(QWidget *parent) actGroup->addAction(ui->actionWhite_monitor); actGroup->addAction(ui->actionRGB_Color); switch (video_graytype) { + default: + break; case 0: ui->actionBT601_NTSC_PAL->setChecked(true); break; @@ -722,7 +734,7 @@ MainWindow::closeEvent(QCloseEvent *event) if (confirm_exit && confirm_exit_cmdl && cpu_thread_run) { QMessageBox questionbox(QMessageBox::Icon::Question, "86Box", tr("Are you sure you want to exit 86Box?"), QMessageBox::Yes | QMessageBox::No, this); - QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again")); + auto chkbox = new QCheckBox(tr("Don't show this message again")); questionbox.setCheckBox(chkbox); chkbox->setChecked(!confirm_exit); @@ -771,7 +783,7 @@ void MainWindow::initRendererMonitorSlot(int monitor_index) { auto &secondaryRenderer = this->renderers[monitor_index]; - secondaryRenderer.reset(new RendererStack(nullptr, monitor_index)); + secondaryRenderer = std::make_unique(nullptr, monitor_index); if (secondaryRenderer) { connect(secondaryRenderer.get(), &RendererStack::rendererChanged, this, [this, monitor_index] { this->renderers[monitor_index]->show(); @@ -779,9 +791,8 @@ MainWindow::initRendererMonitorSlot(int monitor_index) secondaryRenderer->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); secondaryRenderer->setWindowTitle(QObject::tr("86Box Monitor #") + QString::number(monitor_index + 1)); - if (vid_resize == 2) { + if (vid_resize == 2) secondaryRenderer->setFixedSize(fixed_size_x, fixed_size_y); - } secondaryRenderer->setWindowIcon(this->windowIcon()); if (show_second_monitors) { secondaryRenderer->show(); @@ -791,9 +802,8 @@ MainWindow::initRendererMonitorSlot(int monitor_index) monitor_settings[monitor_index].mon_window_w > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_w, monitor_settings[monitor_index].mon_window_h > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_h); } - if (monitor_settings[monitor_index].mon_window_maximized) { + if (monitor_settings[monitor_index].mon_window_maximized) secondaryRenderer->showMaximized(); - } secondaryRenderer->switchRenderer((RendererStack::Renderer) vid_api); secondaryRenderer->setMouseTracking(true); @@ -877,7 +887,7 @@ MainWindow::on_actionHard_Reset_triggered() QMessageBox questionbox(QMessageBox::Icon::Question, "86Box", tr("Are you sure you want to hard reset the emulated machine?"), QMessageBox::NoButton, this); questionbox.addButton(tr("Reset"), QMessageBox::AcceptRole); questionbox.addButton(tr("Don't reset"), QMessageBox::RejectRole); - QCheckBox *chkbox = new QCheckBox(tr("Don't show this message again")); + const auto chkbox = new QCheckBox(tr("Don't show this message again")); questionbox.setCheckBox(chkbox); chkbox->setChecked(!confirm_reset); @@ -921,7 +931,7 @@ MainWindow::on_actionExit_triggered() void MainWindow::on_actionSettings_triggered() { - int currentPause = dopause; + const int currentPause = dopause; plat_pause(1); Settings settings(this); settings.setModal(true); @@ -932,6 +942,8 @@ MainWindow::on_actionSettings_triggered() settings.exec(); switch (settings.result()) { + default: + break; case QDialog::Accepted: pc_reset_hard_close(); settings.save(); @@ -968,6 +980,9 @@ MainWindow::processKeyboardInput(bool down, uint32_t keycode) /* Apply special cases. */ switch (keycode) { + default: + break; + case 0x54: /* Alt + Print Screen (special case, i.e. evdev SELECTIVE_SCREENSHOT) */ /* Send Alt as well. */ if (down) { @@ -1333,18 +1348,18 @@ MainWindow::checkFullscreenHotkey() { if (!fs_off_signal && video_fullscreen && keyboard_isfsexit()) { /* Signal "exit fullscreen mode". */ - fs_off_signal = 1; + fs_off_signal = true; } else if (fs_off_signal && video_fullscreen && keyboard_isfsexit_up()) { ui->actionFullscreen->trigger(); - fs_off_signal = 0; + fs_off_signal = false; } if (!fs_on_signal && !video_fullscreen && keyboard_isfsenter()) { /* Signal "enter fullscreen mode". */ - fs_on_signal = 1; + fs_on_signal = true; } else if (fs_on_signal && !video_fullscreen && keyboard_isfsenter_up()) { ui->actionFullscreen->trigger(); - fs_on_signal = 0; + fs_on_signal = false; } } @@ -1699,7 +1714,7 @@ MainWindow::on_actionAbout_86Box_triggered() msgBox.setInformativeText(tr("An emulator of old computers\n\nAuthors: Miran Grča (OBattler), RichardG867, Jasmine Iwanek, TC1995, coldbrewed, Teemu Korhonen (Manaatti), Joakim L. Gilje, Adrien Moulin (elyosh), Daniel Balsom (gloriouscow), Cacodemon345, Fred N. van Kempen (waltje), Tiseno100, reenigne, and others.\n\nWith previous core contributions from Sarah Walker, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2 or later. See LICENSE for more information.")); msgBox.setWindowTitle("About 86Box"); msgBox.addButton("OK", QMessageBox::ButtonRole::AcceptRole); - auto webSiteButton = msgBox.addButton(EMU_SITE, QMessageBox::ButtonRole::HelpRole); + const auto webSiteButton = msgBox.addButton(EMU_SITE, QMessageBox::ButtonRole::HelpRole); webSiteButton->connect(webSiteButton, &QPushButton::released, []() { QDesktopServices::openUrl(QUrl("https://" EMU_SITE)); }); @@ -1847,8 +1862,8 @@ void MainWindow::on_actionTake_screenshot_triggered() { startblit(); - for (int i = 0; i < MONITORS_NUM; i++) - monitors[i].mon_screenshots++; + for (auto & monitor : monitors) + ++monitor.mon_screenshots; endblit(); device_force_redraw(); } @@ -1869,8 +1884,10 @@ MainWindow::setSendKeyboardInput(bool enabled) void MainWindow::updateUiPauseState() { - auto pause_icon = dopause ? QIcon(":/menuicons/qt/icons/run.ico") : QIcon(":/menuicons/qt/icons/pause.ico"); - auto tooltip_text = dopause ? QString(tr("Resume execution")) : QString(tr("Pause execution")); + const auto pause_icon = dopause ? QIcon(":/menuicons/qt/icons/run.ico") : + QIcon(":/menuicons/qt/icons/pause.ico"); + const auto tooltip_text = dopause ? QString(tr("Resume execution")) : + QString(tr("Pause execution")); ui->actionPause->setIcon(pause_icon); ui->actionPause->setToolTip(tooltip_text); } @@ -1932,9 +1949,7 @@ MainWindow::changeEvent(QEvent *event) void MainWindow::on_actionRenderer_options_triggered() { - auto dlg = ui->stackedWidget->getOptions(this); - - if (dlg) { + if (const auto dlg = ui->stackedWidget->getOptions(this)) { if (dlg->exec() == QDialog::Accepted) { for (int i = 1; i < MONITORS_NUM; i++) { if (renderers[i] && renderers[i]->hasOptions()) @@ -1947,20 +1962,18 @@ MainWindow::on_actionRenderer_options_triggered() void MainWindow::on_actionMCA_devices_triggered() { - auto dlg = new MCADeviceList(this); - - if (dlg) + if (const auto dlg = new MCADeviceList(this)) dlg->exec(); } void MainWindow::on_actionShow_non_primary_monitors_triggered() { - show_second_monitors = (int) ui->actionShow_non_primary_monitors->isChecked(); + show_second_monitors = static_cast(ui->actionShow_non_primary_monitors->isChecked()); if (show_second_monitors) { for (int monitor_index = 1; monitor_index < MONITORS_NUM; monitor_index++) { - auto &secondaryRenderer = renderers[monitor_index]; + const auto &secondaryRenderer = renderers[monitor_index]; if (!renderers[monitor_index]) continue; secondaryRenderer->show(); @@ -1970,8 +1983,8 @@ MainWindow::on_actionShow_non_primary_monitors_triggered() monitor_settings[monitor_index].mon_window_w > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_w, monitor_settings[monitor_index].mon_window_h > 2048 ? 2048 : monitor_settings[monitor_index].mon_window_h); } - secondaryRenderer->switchRenderer((RendererStack::Renderer) vid_api); - ui->stackedWidget->switchRenderer((RendererStack::Renderer) vid_api); + secondaryRenderer->switchRenderer(static_cast(vid_api)); + ui->stackedWidget->switchRenderer(static_cast(vid_api)); } } else { for (int monitor_index = 1; monitor_index < MONITORS_NUM; monitor_index++) { @@ -1992,7 +2005,7 @@ MainWindow::on_actionShow_non_primary_monitors_triggered() void MainWindow::on_actionOpen_screenshots_folder_triggered() { - QDir(QString(usr_path) + QString("/screenshots/")).mkpath("."); + static_cast(QDir(QString(usr_path) + QString("/screenshots/")).mkpath(".")); QDesktopServices::openUrl(QUrl(QString("file:///") + usr_path + QString("/screenshots/"))); } @@ -2001,7 +2014,7 @@ MainWindow::on_actionApply_fullscreen_stretch_mode_when_maximized_triggered(bool { video_fullscreen_scale_maximized = checked; - auto widget = ui->stackedWidget->currentWidget(); + const auto widget = ui->stackedWidget->currentWidget(); ui->stackedWidget->onResize(widget->width(), widget->height()); for (int i = 1; i < MONITORS_NUM; i++) { diff --git a/src/qt/qt_mainwindow.hpp b/src/qt/qt_mainwindow.hpp index 950d145c1..1fca09231 100644 --- a/src/qt/qt_mainwindow.hpp +++ b/src/qt/qt_mainwindow.hpp @@ -73,7 +73,7 @@ private slots: void on_actionCtrl_Alt_Esc_triggered(); void on_actionHard_Reset_triggered(); void on_actionRight_CTRL_is_left_ALT_triggered(); - void on_actionKeyboard_requires_capture_triggered(); + static void on_actionKeyboard_requires_capture_triggered(); void on_actionResizable_window_triggered(bool checked); void on_actionInverted_VGA_monitor_triggered(); void on_action0_5x_triggered(); diff --git a/src/qt/qt_mediamenu.cpp b/src/qt/qt_mediamenu.cpp index af6d40e79..c4fd50567 100644 --- a/src/qt/qt_mediamenu.cpp +++ b/src/qt/qt_mediamenu.cpp @@ -252,7 +252,7 @@ void MediaMenu::cassetteUpdateMenu() { QString name = cassette_fname; - QString mode = cassette_mode; + const QString mode = cassette_mode; auto childs = cassetteMenu->children(); auto *recordMenu = dynamic_cast(childs[cassetteRecordPos]); auto *playMenu = dynamic_cast(childs[cassettePlayPos]); @@ -266,11 +266,12 @@ MediaMenu::cassetteUpdateMenu() fastFwdMenu->setEnabled(!name.isEmpty()); ejectMenu->setEnabled(!name.isEmpty()); - bool isSaving = mode == QStringLiteral("save"); + const bool isSaving = (mode == QStringLiteral("save")); recordMenu->setChecked(isSaving); playMenu->setChecked(!isSaving); - cassetteMenu->setTitle(QString::asprintf(tr("Cassette: %s").toUtf8().constData(), (name.isEmpty() ? tr("(empty)") : name).toUtf8().constData())); + cassetteMenu->setTitle(QString::asprintf(tr("Cassette: %s").toUtf8().constData(), + (name.isEmpty() ? tr("(empty)") : name).toUtf8().constData())); } void @@ -289,7 +290,7 @@ MediaMenu::cartridgeMount(int i, const QString &filename) void MediaMenu::cartridgeSelectImage(int i) { - auto filename = QFileDialog::getOpenFileName( + const auto filename = QFileDialog::getOpenFileName( parentWidget, QString(), getMediaOpenDirectory(), @@ -314,8 +315,8 @@ MediaMenu::cartridgeEject(int i) void MediaMenu::cartridgeUpdateMenu(int i) { - QString name = cart_fns[i]; - auto *menu = cartridgeMenus[i]; + const QString name = cart_fns[i]; + auto *menu = cartridgeMenus[i]; auto childs = menu->children(); auto *ejectMenu = dynamic_cast(childs[cartridgeEjectPos]); ejectMenu->setEnabled(!name.isEmpty()); @@ -328,8 +329,10 @@ MediaMenu::floppyNewImage(int i) { NewFloppyDialog dialog(NewFloppyDialog::MediaType::Floppy, parentWidget); switch (dialog.exec()) { + default: + break; case QDialog::Accepted: - QByteArray filename = dialog.fileName().toUtf8(); + const QByteArray filename = dialog.fileName().toUtf8(); floppyMount(i, filename, false); break; } @@ -516,7 +519,7 @@ MediaMenu::cdromEject(int i) void MediaMenu::cdromReload(int index, int slot) { - QString filename = mhm.getImageForSlot(index, slot, ui::MediaType::Optical); + const QString filename = mhm.getImageForSlot(index, slot, ui::MediaType::Optical); cdromMount(index, filename.toUtf8().constData()); cdromUpdateMenu(index); ui_sb_update_tip(SB_CDROM | index); @@ -572,7 +575,7 @@ MediaMenu::updateImageHistory(int index, int slot, ui::MediaType type) return; } - QString menu_item_name = fi.fileName().isEmpty() ? tr("previous image").toUtf8().constData() : fi.fileName().toUtf8().constData(); + const QString menu_item_name = fi.fileName().isEmpty() ? tr("previous image").toUtf8().constData() : fi.fileName().toUtf8().constData(); imageHistoryUpdatePos->setText(QString::asprintf(tr("%s").toUtf8().constData(), menu_item_name.toUtf8().constData())); imageHistoryUpdatePos->setVisible(!fi.fileName().isEmpty()); imageHistoryUpdatePos->setVisible(fi.exists()); @@ -602,8 +605,8 @@ MediaMenu::cdromUpdateMenu(int i) auto *imageMenu = dynamic_cast(childs[cdromImagePos]); imageMenu->setEnabled(!name.isEmpty()); - QString menu_item_name = name.isEmpty() ? QString().toUtf8().constData() : fi.fileName().toUtf8().constData(); - auto menu_icon = fi.isDir() ? QApplication::style()->standardIcon(QStyle::SP_DirIcon) : ProgSettings::loadIcon("/cdrom.ico"); + const QString menu_item_name = name.isEmpty() ? QString().toUtf8().constData() : fi.fileName().toUtf8().constData(); + const auto menu_icon = fi.isDir() ? QApplication::style()->standardIcon(QStyle::SP_DirIcon) : ProgSettings::loadIcon("/cdrom.ico"); imageMenu->setIcon(menu_icon); imageMenu->setText(QString::asprintf(tr("Eject %s").toUtf8().constData(), menu_item_name.toUtf8().constData())); @@ -613,15 +616,17 @@ MediaMenu::cdromUpdateMenu(int i) QString busName = tr("Unknown Bus"); switch (cdrom[i].bus_type) { + default: + break; case CDROM_BUS_ATAPI: busName = "ATAPI"; break; case CDROM_BUS_SCSI: busName = "SCSI"; break; - case CDROM_BUS_MITSUMI: - busName = "Mitsumi"; - break; + case CDROM_BUS_MITSUMI: + busName = "Mitsumi"; + break; } // menu->setTitle(tr("CD-ROM %1 (%2): %3").arg(QString::number(i+1), busName, name.isEmpty() ? tr("(empty)") : name)); @@ -633,6 +638,8 @@ MediaMenu::zipNewImage(int i) { NewFloppyDialog dialog(NewFloppyDialog::MediaType::Zip, parentWidget); switch (dialog.exec()) { + default: + break; case QDialog::Accepted: QByteArray filename = dialog.fileName().toUtf8(); zipMount(i, filename, false); @@ -643,7 +650,7 @@ MediaMenu::zipNewImage(int i) void MediaMenu::zipSelectImage(int i, bool wp) { - auto filename = QFileDialog::getOpenFileName( + const auto filename = QFileDialog::getOpenFileName( parentWidget, QString(), QString(), @@ -656,7 +663,7 @@ MediaMenu::zipSelectImage(int i, bool wp) void MediaMenu::zipMount(int i, const QString &filename, bool wp) { - zip_t *dev = (zip_t *) zip_drives[i].priv; + const auto dev = static_cast(zip_drives[i].priv); zip_disk_close(dev); zip_drives[i].read_only = wp; @@ -676,7 +683,7 @@ MediaMenu::zipMount(int i, const QString &filename, bool wp) void MediaMenu::zipEject(int i) { - zip_t *dev = (zip_t *) zip_drives[i].priv; + const auto dev = static_cast(zip_drives[i].priv); zip_disk_close(dev); zip_drives[i].image_path[0] = 0; @@ -694,7 +701,7 @@ MediaMenu::zipEject(int i) void MediaMenu::zipReload(int i) { - zip_t *dev = (zip_t *) zip_drives[i].priv; + const auto dev = static_cast(zip_drives[i].priv); zip_disk_reload(dev); if (strlen(zip_drives[i].image_path) == 0) { @@ -712,8 +719,8 @@ MediaMenu::zipReload(int i) void MediaMenu::zipUpdateMenu(int i) { - QString name = zip_drives[i].image_path; - QString prev_name = zip_drives[i].prev_image_path; + const QString name = zip_drives[i].image_path; + const QString prev_name = zip_drives[i].prev_image_path; if (!zipMenus.contains(i)) return; auto *menu = zipMenus[i]; @@ -726,6 +733,8 @@ MediaMenu::zipUpdateMenu(int i) QString busName = tr("Unknown Bus"); switch (zip_drives[i].bus_type) { + default: + break; case ZIP_BUS_ATAPI: busName = "ATAPI"; break; @@ -743,6 +752,8 @@ MediaMenu::moNewImage(int i) { NewFloppyDialog dialog(NewFloppyDialog::MediaType::Mo, parentWidget); switch (dialog.exec()) { + default: + break; case QDialog::Accepted: QByteArray filename = dialog.fileName().toUtf8(); moMount(i, filename, false); @@ -753,7 +764,7 @@ MediaMenu::moNewImage(int i) void MediaMenu::moSelectImage(int i, bool wp) { - auto filename = QFileDialog::getOpenFileName( + const auto filename = QFileDialog::getOpenFileName( parentWidget, QString(), getMediaOpenDirectory(), @@ -769,7 +780,7 @@ MediaMenu::moSelectImage(int i, bool wp) void MediaMenu::moMount(int i, const QString &filename, bool wp) { - mo_t *dev = (mo_t *) mo_drives[i].priv; + const auto dev = static_cast(mo_drives[i].priv); mo_disk_close(dev); mo_drives[i].read_only = wp; @@ -789,7 +800,7 @@ MediaMenu::moMount(int i, const QString &filename, bool wp) void MediaMenu::moEject(int i) { - mo_t *dev = (mo_t *) mo_drives[i].priv; + const auto dev = static_cast(mo_drives[i].priv); mo_disk_close(dev); mo_drives[i].image_path[0] = 0; @@ -839,6 +850,8 @@ MediaMenu::moUpdateMenu(int i) QString busName = tr("Unknown Bus"); switch (mo_drives[i].bus_type) { + default: + break; case MO_BUS_ATAPI: busName = "ATAPI"; break; @@ -876,6 +889,8 @@ MediaMenu::nicUpdateMenu(int i) QString netType = tr("Null Driver"); switch (net_cards_conf[i].net_type) { + default: + break; case NET_TYPE_SLIRP: netType = "SLiRP"; break; @@ -901,9 +916,10 @@ QString MediaMenu::getMediaOpenDirectory() { QString openDirectory; - if (open_dir_usr_path > 0) { + + if (open_dir_usr_path > 0) openDirectory = QString::fromUtf8(usr_path); - } + return openDirectory; } diff --git a/src/qt/qt_rendererstack.cpp b/src/qt/qt_rendererstack.cpp index 0f86e8ee6..bc4cb9c13 100644 --- a/src/qt/qt_rendererstack.cpp +++ b/src/qt/qt_rendererstack.cpp @@ -404,7 +404,10 @@ RendererStack::createRenderer(Renderer renderer) void RendererStack::blit(int x, int y, int w, int h) { - if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || (w > 2048) || (h > 2048) || (monitors[m_monitor_index].target_buffer == NULL) || imagebufs.empty() || std::get(imagebufs[currentBuf])->test_and_set()) { + if ((x < 0) || (y < 0) || (w <= 0) || (h <= 0) || + (w > 2048) || (h > 2048) || + (monitors[m_monitor_index].target_buffer == NULL) || imagebufs.empty() || + std::get(imagebufs[currentBuf])->test_and_set()) { video_blit_complete_monitor(m_monitor_index); return; } diff --git a/src/qt/qt_settings.cpp b/src/qt/qt_settings.cpp index a7e52c342..e9767083a 100644 --- a/src/qt/qt_settings.cpp +++ b/src/qt/qt_settings.cpp @@ -155,8 +155,28 @@ Settings::Settings(QWidget *parent) &SettingsOtherPeripherals::onCurrentMachineChanged); connect(floppyCdrom, &SettingsFloppyCDROM::cdromChannelChanged, harddisks, &SettingsHarddisks::reloadBusChannels); + connect(floppyCdrom, &SettingsFloppyCDROM::cdromChannelChanged, otherRemovable, + &SettingsOtherRemovable::reloadBusChannels_MO); + connect(floppyCdrom, &SettingsFloppyCDROM::cdromChannelChanged, otherRemovable, + &SettingsOtherRemovable::reloadBusChannels_ZIP); connect(harddisks, &SettingsHarddisks::driveChannelChanged, floppyCdrom, &SettingsFloppyCDROM::reloadBusChannels); + connect(harddisks, &SettingsHarddisks::driveChannelChanged, otherRemovable, + &SettingsOtherRemovable::reloadBusChannels_MO); + connect(harddisks, &SettingsHarddisks::driveChannelChanged, otherRemovable, + &SettingsOtherRemovable::reloadBusChannels_ZIP); + connect(otherRemovable, &SettingsOtherRemovable::moChannelChanged, harddisks, + &SettingsHarddisks::reloadBusChannels); + connect(otherRemovable, &SettingsOtherRemovable::moChannelChanged, floppyCdrom, + &SettingsFloppyCDROM::reloadBusChannels); + connect(otherRemovable, &SettingsOtherRemovable::moChannelChanged, otherRemovable, + &SettingsOtherRemovable::reloadBusChannels_ZIP); + connect(otherRemovable, &SettingsOtherRemovable::zipChannelChanged, harddisks, + &SettingsHarddisks::reloadBusChannels); + connect(otherRemovable, &SettingsOtherRemovable::zipChannelChanged, floppyCdrom, + &SettingsFloppyCDROM::reloadBusChannels); + connect(otherRemovable, &SettingsOtherRemovable::zipChannelChanged, otherRemovable, + &SettingsOtherRemovable::reloadBusChannels_MO); connect(ui->listView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex ¤t, const QModelIndex &previous) { diff --git a/src/qt/qt_settings_bus_tracking.cpp b/src/qt/qt_settings_bus_tracking.cpp index 064fbe0b4..6fb8637da 100644 --- a/src/qt/qt_settings_bus_tracking.cpp +++ b/src/qt/qt_settings_bus_tracking.cpp @@ -22,6 +22,7 @@ #include #include "86box/hdd.h" +#include "86box/scsi.h" #include "qt_settings_bus_tracking.hpp" SettingsBusTracking::SettingsBusTracking() @@ -30,12 +31,11 @@ SettingsBusTracking::SettingsBusTracking() esdi_tracking = 0x0000000000000000ULL; xta_tracking = 0x0000000000000000ULL; - for (uint8_t i = 0; i < 8; i++) { - if (i < 4) - ide_tracking[i] = 0x0000000000000000ULL; + for (uint8_t i = 0; i < 4; i++) + ide_tracking[i] = 0x0000000000000000ULL; + for (uint8_t i = 0; i < 32; i++) scsi_tracking[i] = 0x0000000000000000ULL; - } } uint8_t @@ -101,7 +101,7 @@ SettingsBusTracking::next_free_scsi_id() uint64_t mask; uint8_t ret = CHANNEL_NONE; - for (uint8_t i = 0; i < 64; i++) { + for (uint8_t i = 0; i < (SCSI_BUS_MAX * SCSI_ID_MAX); i++) { element = ((i << 3) >> 6); mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f)); @@ -187,7 +187,7 @@ SettingsBusTracking::scsi_bus_full() uint64_t mask; uint8_t count = 0; - for (uint8_t i = 0; i < 64; i++) { + for (uint8_t i = 0; i < (SCSI_BUS_MAX * SCSI_ID_MAX); i++) { element = ((i << 3) >> 6); mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f)); @@ -204,26 +204,45 @@ QList SettingsBusTracking::busChannelsInUse(const int bus) { int element; uint64_t mask; switch (bus) { + case HDD_BUS_MFM: + for (uint8_t i = 0; i < 32; i++) { + mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f)); + if (mfm_tracking & mask) + channelsInUse.append(i); + } + break; + case HDD_BUS_ESDI: + for (uint8_t i = 0; i < 32; i++) { + mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f)); + if (esdi_tracking & mask) + channelsInUse.append(i); + } + break; + case HDD_BUS_XTA: + for (uint8_t i = 0; i < 32; i++) { + mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f)); + if (xta_tracking & mask) + channelsInUse.append(i); + } + break; case HDD_BUS_IDE: for (uint8_t i = 0; i < 32; i++) { element = ((i << 3) >> 6); - mask = ((uint64_t) DEV_HDD) << ((uint64_t) ((i << 3) & 0x3f)); - if (ide_tracking[element] & mask) { + mask = ((uint64_t) 0xffULL) << ((uint64_t) ((i << 3) & 0x3f)); + if (ide_tracking[element] & mask) channelsInUse.append(i); - } } break; case HDD_BUS_ATAPI: for (uint8_t i = 0; i < 32; i++) { element = ((i << 3) >> 6); - mask = ((uint64_t) DEV_CDROM) << ((uint64_t) ((i << 3) & 0x3f)); - if (ide_tracking[element] & mask) { + mask = ((uint64_t) 0xffULL) << ((uint64_t) ((i << 3) & 0x3f)); + if (ide_tracking[element] & mask) channelsInUse.append(i); - } } break; case HDD_BUS_SCSI: - for (uint8_t i = 0; i < 64; i++) { + for (uint8_t i = 0; i < (SCSI_BUS_MAX * SCSI_ID_MAX); i++) { element = ((i << 3) >> 6); mask = 0xffULL << ((uint64_t) ((i << 3) & 0x3f)); if (scsi_tracking[element] & mask) diff --git a/src/qt/qt_settings_bus_tracking.hpp b/src/qt/qt_settings_bus_tracking.hpp index 279d3247e..917706428 100644 --- a/src/qt/qt_settings_bus_tracking.hpp +++ b/src/qt/qt_settings_bus_tracking.hpp @@ -57,8 +57,10 @@ private: uint64_t xta_tracking { 0 }; /* 16 channels (prepatation for that weird IDE card), 2 devices per channel, 8 bits per device = 256 bits. */ uint64_t ide_tracking[4] { 0, 0, 0, 0 }; - /* 4 buses, 16 devices per bus, 8 bits per device (future-proofing) = 512 bits. */ - uint64_t scsi_tracking[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; + /* 9 buses (rounded upwards to 16for future-proofing), 16 devices per bus, + 8 bits per device (future-proofing) = 2048 bits. */ + uint64_t scsi_tracking[32] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; }; #endif // QT_SETTINGS_BUS_TRACKING_HPP diff --git a/src/qt/qt_settingsfloppycdrom.cpp b/src/qt/qt_settingsfloppycdrom.cpp index bb536e603..8f0ac81a9 100644 --- a/src/qt/qt_settingsfloppycdrom.cpp +++ b/src/qt/qt_settingsfloppycdrom.cpp @@ -368,6 +368,7 @@ SettingsFloppyCDROM::on_comboBoxBus_activated(int) setCDROMType(ui->tableViewCDROM->model(), ui->tableViewCDROM->selectionModel()->currentIndex(), ui->comboBoxCDROMType->currentData().toUInt()); + emit cdromChannelChanged(); } void diff --git a/src/qt/qt_settingsotherremovable.cpp b/src/qt/qt_settingsotherremovable.cpp index 36918ad99..1a6dceacb 100644 --- a/src/qt/qt_settingsotherremovable.cpp +++ b/src/qt/qt_settingsotherremovable.cpp @@ -201,6 +201,7 @@ SettingsOtherRemovable::onMORowChanged(const QModelIndex ¤t) if (!match.isEmpty()) ui->comboBoxMOChannel->setCurrentIndex(match.first().row()); ui->comboBoxMOType->setCurrentIndex(type); + enableCurrentlySelectedChannel_MO(); } void @@ -221,6 +222,16 @@ SettingsOtherRemovable::onZIPRowChanged(const QModelIndex ¤t) if (!match.isEmpty()) ui->comboBoxZIPChannel->setCurrentIndex(match.first().row()); ui->checkBoxZIP250->setChecked(is250); + enableCurrentlySelectedChannel_ZIP(); +} + +void +SettingsOtherRemovable::reloadBusChannels_MO() { + auto selected = ui->comboBoxMOChannel->currentIndex(); + Harddrives::populateBusChannels(ui->comboBoxMOChannel->model(), + ui->comboBoxMOBus->currentData().toInt(), Harddrives::busTrackClass); + ui->comboBoxMOChannel->setCurrentIndex(selected); + enableCurrentlySelectedChannel_MO(); } void @@ -231,7 +242,7 @@ SettingsOtherRemovable::on_comboBoxMOBus_currentIndexChanged(int index) bool enabled = (bus != MO_BUS_DISABLED); ui->comboBoxMOChannel->setEnabled(enabled); ui->comboBoxMOType->setEnabled(enabled); - Harddrives::populateBusChannels(ui->comboBoxMOChannel->model(), bus); + Harddrives::populateBusChannels(ui->comboBoxMOChannel->model(), bus, Harddrives::busTrackClass); } } @@ -258,6 +269,17 @@ SettingsOtherRemovable::on_comboBoxMOBus_activated(int) Harddrives::busTrackClass->device_track(1, DEV_MO, ui->tableViewMO->model()->data(i, Qt::UserRole).toInt(), ui->tableViewMO->model()->data(i, Qt::UserRole + 1).toInt()); + emit moChannelChanged(); +} + +void +SettingsOtherRemovable::enableCurrentlySelectedChannel_MO() +{ + const auto *item_model = qobject_cast(ui->comboBoxMOChannel->model()); + const auto index = ui->comboBoxMOChannel->currentIndex(); + auto *item = item_model->item(index); + if (item) + item->setEnabled(true); } void @@ -274,6 +296,7 @@ SettingsOtherRemovable::on_comboBoxMOChannel_activated(int) Harddrives::busTrackClass->device_track(1, DEV_MO, ui->tableViewMO->model()->data(i, Qt::UserRole).toInt(), ui->tableViewMO->model()->data(i, Qt::UserRole + 1).toInt()); + emit moChannelChanged(); } void @@ -286,6 +309,15 @@ SettingsOtherRemovable::on_comboBoxMOType_activated(int) ui->tableViewMO->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); } +void +SettingsOtherRemovable::reloadBusChannels_ZIP() { + auto selected = ui->comboBoxZIPChannel->currentIndex(); + Harddrives::populateBusChannels(ui->comboBoxZIPChannel->model(), + ui->comboBoxZIPBus->currentData().toInt(), Harddrives::busTrackClass); + ui->comboBoxZIPChannel->setCurrentIndex(selected); + enableCurrentlySelectedChannel_ZIP(); +} + void SettingsOtherRemovable::on_comboBoxZIPBus_currentIndexChanged(int index) { @@ -294,7 +326,7 @@ SettingsOtherRemovable::on_comboBoxZIPBus_currentIndexChanged(int index) bool enabled = (bus != ZIP_BUS_DISABLED); ui->comboBoxZIPChannel->setEnabled(enabled); ui->checkBoxZIP250->setEnabled(enabled); - Harddrives::populateBusChannels(ui->comboBoxZIPChannel->model(), bus); + Harddrives::populateBusChannels(ui->comboBoxZIPChannel->model(), bus, Harddrives::busTrackClass); } } @@ -315,6 +347,17 @@ SettingsOtherRemovable::on_comboBoxZIPBus_activated(int) Harddrives::busTrackClass->device_track(1, DEV_ZIP, ui->tableViewZIP->model()->data(i, Qt::UserRole).toInt(), ui->tableViewZIP->model()->data(i, Qt::UserRole + 1).toInt()); + emit zipChannelChanged(); +} + +void +SettingsOtherRemovable::enableCurrentlySelectedChannel_ZIP() +{ + const auto *item_model = qobject_cast(ui->comboBoxZIPChannel->model()); + const auto index = ui->comboBoxZIPChannel->currentIndex(); + auto *item = item_model->item(index); + if (item) + item->setEnabled(true); } void @@ -331,6 +374,7 @@ SettingsOtherRemovable::on_comboBoxZIPChannel_activated(int) Harddrives::busTrackClass->device_track(1, DEV_ZIP, ui->tableViewZIP->model()->data(i, Qt::UserRole).toInt(), ui->tableViewZIP->model()->data(i, Qt::UserRole + 1).toInt()); + emit zipChannelChanged(); } void diff --git a/src/qt/qt_settingsotherremovable.hpp b/src/qt/qt_settingsotherremovable.hpp index 8b81fb0f0..cea1d202b 100644 --- a/src/qt/qt_settingsotherremovable.hpp +++ b/src/qt/qt_settingsotherremovable.hpp @@ -13,9 +13,14 @@ class SettingsOtherRemovable : public QWidget { public: explicit SettingsOtherRemovable(QWidget *parent = nullptr); ~SettingsOtherRemovable(); + void reloadBusChannels_MO(); + void reloadBusChannels_ZIP(); void save(); +signals: + void moChannelChanged(); + void zipChannelChanged(); private slots: void on_checkBoxZIP250_stateChanged(int arg1); @@ -46,6 +51,8 @@ private slots: private: Ui::SettingsOtherRemovable *ui; + void enableCurrentlySelectedChannel_MO(); + void enableCurrentlySelectedChannel_ZIP(); }; #endif // QT_SETTINGSOTHERREMOVABLE_HPP diff --git a/src/qt/qt_settingssound.cpp b/src/qt/qt_settingssound.cpp index b4df4ff69..e0572c3d8 100644 --- a/src/qt/qt_settingssound.cpp +++ b/src/qt/qt_settingssound.cpp @@ -67,17 +67,17 @@ SettingsSound::save() } void -SettingsSound::onCurrentMachineChanged(int machineId) +SettingsSound::onCurrentMachineChanged(const int machineId) { this->machineId = machineId; - int c = 0; - int selectedRow = 0; + int c; + int selectedRow; for (uint8_t i = 0; i < SOUND_CARD_MAX; ++i) { - auto *cbox = findChild(QString("comboBoxSoundCard%1").arg(i + 1)); - auto *model = cbox->model(); - auto removeRows = model->rowCount(); + auto * cbox = findChild(QString("comboBoxSoundCard%1").arg(i + 1)); + auto * model = cbox->model(); + const auto removeRows = model->rowCount(); c = 0; selectedRow = 0; @@ -207,7 +207,7 @@ SettingsSound::on_pushButtonConfigureSoundCard1_clicked() auto *device = sound_card_getdevice(sndCard); if (sndCard == SOUND_INTERNAL) device = machine_get_snd_device(machineId); - DeviceConfig::ConfigureDevice(device, 0, qobject_cast(Settings::settings)); + DeviceConfig::ConfigureDevice(device, 1, qobject_cast(Settings::settings)); } void @@ -225,7 +225,7 @@ SettingsSound::on_pushButtonConfigureSoundCard2_clicked() { int sndCard = ui->comboBoxSoundCard2->currentData().toInt(); auto *device = sound_card_getdevice(sndCard); - DeviceConfig::ConfigureDevice(device, 0, qobject_cast(Settings::settings)); + DeviceConfig::ConfigureDevice(device, 2, qobject_cast(Settings::settings)); } void @@ -243,7 +243,7 @@ SettingsSound::on_pushButtonConfigureSoundCard3_clicked() { int sndCard = ui->comboBoxSoundCard3->currentData().toInt(); auto *device = sound_card_getdevice(sndCard); - DeviceConfig::ConfigureDevice(device, 0, qobject_cast(Settings::settings)); + DeviceConfig::ConfigureDevice(device, 3, qobject_cast(Settings::settings)); } void @@ -261,7 +261,7 @@ SettingsSound::on_pushButtonConfigureSoundCard4_clicked() { int sndCard = ui->comboBoxSoundCard4->currentData().toInt(); auto *device = sound_card_getdevice(sndCard); - DeviceConfig::ConfigureDevice(device, 0, qobject_cast(Settings::settings)); + DeviceConfig::ConfigureDevice(device, 4, qobject_cast(Settings::settings)); } void @@ -278,7 +278,8 @@ SettingsSound::on_comboBoxMidiOut_currentIndexChanged(int index) void SettingsSound::on_pushButtonConfigureMidiOut_clicked() { - DeviceConfig::ConfigureDevice(midi_out_device_getdevice(ui->comboBoxMidiOut->currentData().toInt()), 0, qobject_cast(Settings::settings)); + DeviceConfig::ConfigureDevice(midi_out_device_getdevice(ui->comboBoxMidiOut->currentData().toInt()), 0, + qobject_cast(Settings::settings)); } void @@ -295,7 +296,8 @@ SettingsSound::on_comboBoxMidiIn_currentIndexChanged(int index) void SettingsSound::on_pushButtonConfigureMidiIn_clicked() { - DeviceConfig::ConfigureDevice(midi_in_device_getdevice(ui->comboBoxMidiIn->currentData().toInt()), 0, qobject_cast(Settings::settings)); + DeviceConfig::ConfigureDevice(midi_in_device_getdevice(ui->comboBoxMidiIn->currentData().toInt()), 0, + qobject_cast(Settings::settings)); } void @@ -307,9 +309,8 @@ SettingsSound::on_checkBoxMPU401_stateChanged(int state) void SettingsSound::on_pushButtonConfigureMPU401_clicked() { - if (machine_has_bus(machineId, MACHINE_BUS_MCA) > 0) { + if (machine_has_bus(machineId, MACHINE_BUS_MCA) > 0) DeviceConfig::ConfigureDevice(&mpu401_mca_device, 0, qobject_cast(Settings::settings)); - } else { + else DeviceConfig::ConfigureDevice(&mpu401_device, 0, qobject_cast(Settings::settings)); - } } diff --git a/src/qt/qt_settingsstoragecontrollers.cpp b/src/qt/qt_settingsstoragecontrollers.cpp index 0f19d46fc..389e22852 100644 --- a/src/qt/qt_settingsstoragecontrollers.cpp +++ b/src/qt/qt_settingsstoragecontrollers.cpp @@ -52,7 +52,7 @@ void SettingsStorageControllers::save() { /* Storage devices category */ - for (int i = 0; i < SCSI_BUS_MAX; ++i) { + for (int i = 0; i < SCSI_CARD_MAX; ++i) { auto *cbox = findChild(QString("comboBoxSCSI%1").arg(i + 1)); scsi_card_current[i] = cbox->currentData().toInt(); } @@ -161,7 +161,7 @@ SettingsStorageControllers::onCurrentMachineChanged(int machineId) ui->comboBoxCDInterface->setCurrentIndex(-1); ui->comboBoxCDInterface->setCurrentIndex(selectedRow); - for (int i = 0; i < SCSI_BUS_MAX; ++i) { + for (int i = 0; i < SCSI_CARD_MAX; ++i) { auto *cbox = findChild(QString("comboBoxSCSI%1").arg(i + 1)); model = cbox->model(); removeRows = model->rowCount(); diff --git a/src/qt/qt_ui.cpp b/src/qt/qt_ui.cpp index 5412a0e4a..fc111e5b4 100644 --- a/src/qt/qt_ui.cpp +++ b/src/qt/qt_ui.cpp @@ -66,13 +66,13 @@ wchar_t * ui_window_title(wchar_t *str) { if (str == nullptr) { - static wchar_t title[512]; - memset(title, 0, sizeof(title)); + static wchar_t title[512] = { 0 }; + main_window->getTitle(title); str = title; - } else { + } else emit main_window->setTitle(QString::fromWCharArray(str)); - } + return str; } @@ -122,8 +122,10 @@ plat_mouse_capture(int on) int ui_msgbox_header(int flags, void *header, void *message) { - auto hdr = (flags & MBX_ANSI) ? QString((char *) header) : QString::fromWCharArray(reinterpret_cast(header)); - auto msg = (flags & MBX_ANSI) ? QString((char *) message) : QString::fromWCharArray(reinterpret_cast(message)); + const auto hdr = (flags & MBX_ANSI) ? QString(static_cast(header)) : + QString::fromWCharArray(static_cast(header)); + const auto msg = (flags & MBX_ANSI) ? QString(static_cast(message)) : + QString::fromWCharArray(static_cast(message)); // any error in early init if (main_window == nullptr) { @@ -220,9 +222,13 @@ ui_sb_set_ready(int ready) void ui_sb_update_icon_state(int tag, int state) { - int category = tag & 0xfffffff0; - int item = tag & 0xf; + const auto temp = static_cast(tag); + const int category = static_cast(temp & 0xfffffff0); + const int item = tag & 0xf; + switch (category) { + default: + break; case SB_CASSETTE: machine_status.cassette.empty = state > 0 ? true : false; break; @@ -247,7 +253,6 @@ ui_sb_update_icon_state(int tag, int state) machine_status.net[item].empty = state > 0 ? true : false; break; case SB_SOUND: - break; case SB_TEXT: break; } @@ -256,11 +261,13 @@ ui_sb_update_icon_state(int tag, int state) void ui_sb_update_icon(int tag, int active) { - int category = tag & 0xfffffff0; - int item = tag & 0xf; + const auto temp = static_cast(tag); + const int category = static_cast(temp & 0xfffffff0); + const int item = tag & 0xf; + switch (category) { + default: case SB_CASSETTE: - break; case SB_CARTRIDGE: break; case SB_FLOPPY: @@ -282,7 +289,6 @@ ui_sb_update_icon(int tag, int active) machine_status.net[item].active = active > 0 ? true : false; break; case SB_SOUND: - break; case SB_TEXT: break; } diff --git a/src/scsi/scsi.c b/src/scsi/scsi.c index 328810a2f..aecbcadec 100644 --- a/src/scsi/scsi.c +++ b/src/scsi/scsi.c @@ -42,7 +42,7 @@ #include <86box/scsi_pcscsi.h> #include <86box/scsi_spock.h> -int scsi_card_current[SCSI_BUS_MAX] = { 0, 0, 0, 0 }; +int scsi_card_current[SCSI_CARD_MAX] = { 0, 0, 0, 0 }; double scsi_bus_speed[SCSI_BUS_MAX] = { 0.0, 0.0, 0.0, 0.0 }; static uint8_t next_scsi_bus = 0; @@ -169,12 +169,12 @@ scsi_card_get_from_internal_name(char *s) void scsi_card_init(void) { - int max = SCSI_BUS_MAX; + int max = SCSI_CARD_MAX; /* On-board SCSI controllers get the first bus, so if one is present, increase our instance number here. */ - if (machine_has_flags(machine, MACHINE_SCSI)) - max--; + // if (machine_has_flags(machine, MACHINE_SCSI)) + // max--; /* Do not initialize any controllers if we have do not have any SCSI bus left. */ diff --git a/src/scsi/scsi_ncr5380.c b/src/scsi/scsi_ncr5380.c index d113d2cb3..d256de764 100644 --- a/src/scsi/scsi_ncr5380.c +++ b/src/scsi/scsi_ncr5380.c @@ -68,16 +68,24 @@ void ncr5380_irq(ncr_t *ncr, int set_irq) { if (set_irq) { + ncr->irq_state = 1; ncr->isr |= STATUS_INT; if (ncr->irq != -1) picint(1 << ncr->irq); } else { + ncr->irq_state = 0; ncr->isr &= ~STATUS_INT; if (ncr->irq != 1) picintc(1 << ncr->irq); } } +void +ncr5380_set_irq(ncr_t *ncr, int irq) +{ + ncr->irq = irq; +} + static int ncr5380_get_dev_id(uint8_t data) { @@ -318,6 +326,19 @@ ncr5380_bus_update(ncr_t *ncr, int bus) ncr5380_log("SCSI ID %i: command 0x%02x for p = %lf, update = %lf, len = %i, dmamode = %x\n", ncr->target_id, ncr->command[0], scsi_device_get_callback(dev), ncr->period, dev->buffer_length, ncr->dma_mode); } } + + if (ncr->simple_pseudo_dma) { + if (dev->phase == SCSI_PHASE_DATA_IN) { + ncr->block_count = dev->buffer_length / 512; + + ncr->dma_init_ext(ncr, ncr->priv, 1); + } else if (dev->phase == SCSI_PHASE_DATA_OUT) { + ncr->block_count = dev->buffer_length / 512; + + ncr->dma_init_ext(ncr, ncr->priv, 0); + } + } + ncr->new_phase = dev->phase; } } @@ -337,7 +358,10 @@ ncr5380_bus_update(ncr_t *ncr, int bus) if (ncr->dma_mode == DMA_IDLE) { /*If a data in command that is not read 6/10 has been issued*/ ncr->data_wait |= 1; ncr5380_log("DMA mode idle in\n"); - ncr->timer(ncr->priv, ncr->period); + if (ncr->simple_pseudo_dma) + ncr->dma_init_ext(ncr, ncr->priv, 1); + else + ncr->timer(ncr->priv, ncr->period); } else { ncr5380_log("DMA mode IN.\n"); ncr->clear_req = 3; @@ -364,7 +388,10 @@ ncr5380_bus_update(ncr_t *ncr, int bus) if (ncr->dma_mode == DMA_IDLE) { /*If a data out command that is not write 6/10 has been issued*/ ncr->data_wait |= 1; ncr5380_log("DMA mode idle out\n"); - ncr->timer(ncr->priv, ncr->period); + if (!ncr->simple_pseudo_dma) + ncr->timer(ncr->priv, ncr->period); + if (ncr->simple_pseudo_dma) + ncr->dma_init_ext(ncr, ncr->priv, 0); } else ncr->clear_req = 3; @@ -490,6 +517,21 @@ ncr5380_write(uint16_t port, uint8_t val, ncr_t *ncr) ncr5380_bus_update(ncr, bus_host); } +uint8_t +ncr5380_drq(ncr_t *ncr) +{ + uint8_t ret = 0; + int bus; + + ncr5380_bus_read(ncr); + bus = ncr->cur_bus; + + if ((bus & BUS_REQ) && (ncr->mode & MODE_DMA)) + ret = 1; + + return ret; +} + uint8_t ncr5380_read(uint16_t port, ncr_t *ncr) { diff --git a/src/scsi/scsi_ncr53c400.c b/src/scsi/scsi_ncr53c400.c index 1ed8e520e..f4bd9de59 100644 --- a/src/scsi/scsi_ncr53c400.c +++ b/src/scsi/scsi_ncr53c400.c @@ -41,6 +41,7 @@ #include <86box/scsi.h> #include <86box/scsi_device.h> #include <86box/scsi_ncr5380.h> +#include <86box/scsi_ncr53c400.h> #define LCS6821N_ROM "roms/scsi/ncr5380/Longshine LCS-6821N - BIOS version 1.04.bin" #define COREL_LS2000_ROM "roms/scsi/ncr5380/Corel LS2000 - BIOS ROM - Ver 1.65.bin" @@ -48,43 +49,14 @@ #define RT1000B_820R_ROM "roms/scsi/ncr5380/RTBIOS82.ROM" #define T130B_ROM "roms/scsi/ncr5380/trantor_t130b_bios_v2.14.bin" -#define CTRL_DATA_DIR 0x40 -#define STATUS_BUFFER_NOT_READY 0x04 -#define STATUS_5380_ACCESSIBLE 0x80 - enum { ROM_LCS6821N = 0, ROM_LS2000, ROM_RT1000B, - ROM_T130B + ROM_T130B, + ROM_PAS }; -typedef struct ncr53c400_t { - rom_t bios_rom; - mem_mapping_t mapping; - ncr_t ncr; - uint8_t buffer[128]; - uint8_t int_ram[0x40]; - uint8_t ext_ram[0x600]; - - uint32_t rom_addr; - uint16_t base; - - int8_t type; - uint8_t block_count; - uint8_t status_ctrl; - - int block_count_loaded; - - int buffer_pos; - int buffer_host_pos; - - int busy; - uint8_t pos_regs[8]; - - pc_timer_t timer; -} ncr53c400_t; - #ifdef ENABLE_NCR53C400_LOG int ncr53c400_do_log = ENABLE_NCR53C400_LOG; @@ -103,8 +75,29 @@ ncr53c400_log(const char *fmt, ...) # define ncr53c400_log(fmt, ...) #endif +void +ncr53c400_simple_write(uint8_t val, void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + ncr_t *ncr = &ncr400->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + + if (ncr400->buffer_host_pos < MIN(512, dev->buffer_length)) { + ncr400->buffer[ncr400->buffer_host_pos++] = val; + + ncr53c400_log("Write host pos = %i, val = %02x\n", ncr400->buffer_host_pos, val); + + if (ncr400->buffer_host_pos == MIN(512, dev->buffer_length)) { + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + ncr400->busy = 1; + + ncr53c400_callback(priv); + } + } +} + /* Memory-mapped I/O WRITE handler. */ -static void +void ncr53c400_write(uint32_t addr, uint8_t val, void *priv) { ncr53c400_t *ncr400 = (ncr53c400_t *) priv; @@ -192,8 +185,30 @@ ncr53c400_write(uint32_t addr, uint8_t val, void *priv) } } +uint8_t +ncr53c400_simple_read(void *priv) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) priv; + ncr_t *ncr = &ncr400->ncr; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + uint8_t ret = 0xff; + + if (ncr400->buffer_host_pos < MIN(512, dev->buffer_length)) { + ret = ncr400->buffer[ncr400->buffer_host_pos++]; + ncr53c400_log("Read host pos = %i, ret = %02x\n", ncr400->buffer_host_pos, ret); + + if (ncr400->buffer_host_pos == MIN(512, dev->buffer_length)) { + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + ncr53c400_log("Transfer busy read, status = %02x\n", ncr400->status_ctrl); + ncr53c400_callback(priv); + } + } + + return ret; +} + /* Memory-mapped I/O READ handler. */ -static uint8_t +uint8_t ncr53c400_read(uint32_t addr, void *priv) { ncr53c400_t *ncr400 = (ncr53c400_t *) priv; @@ -385,6 +400,34 @@ t130b_in(uint16_t port, void *priv) return ret; } +static void +ncr53c400_dma_init_ext(void *priv, void *ext_priv, int send) +{ + ncr53c400_t *ncr400 = (ncr53c400_t *) ext_priv; + ncr_t *ncr = (ncr_t *) priv; + scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; + uint8_t val = send ? CTRL_DATA_DIR : 0x00; + + ncr53c400_log("NCR 53c400 control = %02x, mode = %02x.\n", val, ncr->mode); + ncr400->status_ctrl = (ncr400->status_ctrl & 0x87) | (val & 0x78); + + ncr400->block_count_loaded = 1; + + if (ncr400->status_ctrl & CTRL_DATA_DIR) { + ncr400->buffer_host_pos = MIN(512, dev->buffer_length); + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + } else { + ncr400->buffer_host_pos = 0; + ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; + } + if ((ncr->mode & MODE_DMA) && (dev->buffer_length > 0)) { + memset(ncr400->buffer, 0, MIN(512, dev->buffer_length)); + ncr53c400_log("DMA buffer init\n"); + ncr->timer(ncr->priv, ncr->period); + } + ncr53c400_log("NCR DMA init\n"); +} + static void ncr53c400_dma_mode_ext(void *priv, void *ext_priv) { @@ -412,17 +455,21 @@ ncr53c400_timer_on_auto(void *ext_priv, double period) timer_on_auto(&ncr400->timer, period); } -static void +void ncr53c400_callback(void *priv) { ncr53c400_t *ncr400 = (void *) priv; ncr_t *ncr = &ncr400->ncr; scsi_device_t *dev = &scsi_devices[ncr->bus][ncr->target_id]; int bus; + int blocks_left; uint8_t c; uint8_t temp; + const int buf_len = ncr400->simple_ctrl ? 512 : 128; - if (ncr->dma_mode != DMA_IDLE) + ncr53c400_log("DMA mode = %i\n", ncr->dma_mode); + + if (!ncr400->simple_ctrl && (ncr->dma_mode != DMA_IDLE)) timer_on_auto(&ncr400->timer, 1.0); if (ncr->data_wait & 1) { @@ -441,7 +488,7 @@ ncr53c400_callback(void *priv) switch (ncr->dma_mode) { case DMA_SEND: - if (ncr400->status_ctrl & CTRL_DATA_DIR) { + if (!ncr400->simple_ctrl && (ncr400->status_ctrl & CTRL_DATA_DIR)) { ncr53c400_log("DMA_SEND with DMA direction set wrong\n"); break; } @@ -475,14 +522,20 @@ ncr53c400_callback(void *priv) ncr400->buffer_pos++; ncr53c400_log("NCR 53c400 Buffer pos for writing = %d\n", ncr400->buffer_pos); - if (ncr400->buffer_pos == MIN(128, dev->buffer_length)) { + if (ncr400->buffer_pos == MIN(buf_len, dev->buffer_length)) { ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; ncr400->buffer_pos = 0; ncr400->buffer_host_pos = 0; ncr400->busy = 0; - ncr400->block_count = (ncr400->block_count - 1) & 0xff; - ncr53c400_log("NCR 53c400 Remaining blocks to be written=%d\n", ncr400->block_count); - if (!ncr400->block_count) { + if (ncr400->simple_ctrl) { + ncr->block_count = (ncr->block_count - 1) & 0xffffffff; + blocks_left = ncr->block_count; + } else { + ncr400->block_count = (ncr400->block_count - 1) & 0xff; + blocks_left = ncr400->block_count; + } + ncr53c400_log("NCR 53c400 Remaining blocks to be written=%d\n", blocks_left); + if (blocks_left == 0) { ncr400->block_count_loaded = 0; ncr53c400_log("IO End of write transfer\n"); ncr->tcr |= TCR_LAST_BYTE_SENT; @@ -499,7 +552,7 @@ ncr53c400_callback(void *priv) break; case DMA_INITIATOR_RECEIVE: - if (!(ncr400->status_ctrl & CTRL_DATA_DIR)) { + if (!ncr400->simple_ctrl && !(ncr400->status_ctrl & CTRL_DATA_DIR)) { ncr53c400_log("DMA_INITIATOR_RECEIVE with DMA direction set wrong\n"); break; } @@ -515,8 +568,10 @@ ncr53c400_callback(void *priv) while (1) { for (c = 0; c < 10; c++) { ncr5380_bus_read(ncr); - if (ncr->cur_bus & BUS_REQ) + if (ncr->cur_bus & BUS_REQ) { + ncr53c400_log("ncr->cur_bus & BUS_REQ\n"); break; + } } /* Data ready. */ @@ -531,13 +586,19 @@ ncr53c400_callback(void *priv) ncr400->buffer[ncr400->buffer_pos++] = temp; ncr53c400_log("NCR 53c400 Buffer pos for reading = %d\n", ncr400->buffer_pos); - if (ncr400->buffer_pos == MIN(128, dev->buffer_length)) { + if (ncr400->buffer_pos == MIN(buf_len, dev->buffer_length)) { ncr400->status_ctrl &= ~STATUS_BUFFER_NOT_READY; ncr400->buffer_pos = 0; ncr400->buffer_host_pos = 0; - ncr400->block_count = (ncr400->block_count - 1) & 0xff; - ncr53c400_log("NCR 53c400 Remaining blocks to be read=%d\n", ncr400->block_count); - if (!ncr400->block_count) { + if (ncr400->simple_ctrl) { + ncr->block_count = (ncr->block_count - 1) & 0xffffffff; + blocks_left = ncr->block_count; + } else { + ncr400->block_count = (ncr400->block_count - 1) & 0xff; + blocks_left = ncr400->block_count; + } + ncr53c400_log("NCR 53c400 Remaining blocks to be read=%d\n", blocks_left); + if (blocks_left == 0) { ncr400->block_count_loaded = 0; ncr53c400_log("IO End of read transfer\n"); ncr->isr |= STATUS_END_OF_DMA; @@ -562,6 +623,8 @@ ncr53c400_callback(void *priv) ncr53c400_log("Updating DMA\n"); ncr->mode &= ~MODE_DMA; ncr->dma_mode = DMA_IDLE; + if (ncr400->simple_ctrl) + ncr400->block_count_loaded = 0; } } @@ -656,7 +719,6 @@ ncr53c400_init(const device_t *info) ncr400->bios_rom.rom, MEM_MAPPING_EXTERNAL, ncr400); break; - case ROM_LS2000: /* Corel LS2000 */ ncr400->rom_addr = device_get_config_hex20("bios_addr"); ncr->irq = device_get_config_int("irq"); @@ -715,6 +777,13 @@ ncr53c400_init(const device_t *info) t130b_in, NULL, NULL, t130b_out, NULL, NULL, ncr400); break; + case ROM_PAS: /* Pro Audio Spectrum Plus/16 SCSI controller */ + ncr400->simple_ctrl = 1; + ncr400->status_ctrl |= STATUS_BUFFER_NOT_READY; + ncr->simple_pseudo_dma = 1; + ncr->dma_init_ext = ncr53c400_dma_init_ext; + break; + default: break; } @@ -1007,3 +1076,17 @@ const device_t scsi_ls2000_device = { .force_redraw = NULL, .config = ncr53c400_mmio_config }; + +const device_t scsi_pas_device = { + .name = "Pro Audio Spectrum Plus/16 SCSI", + .internal_name = "scsi_pas", + .flags = DEVICE_ISA, + .local = ROM_PAS, + .init = ncr53c400_init, + .close = ncr53c400_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = NULL, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/sound/openal.c b/src/sound/openal.c index f015205f6..f1ca74182 100644 --- a/src/sound/openal.c +++ b/src/sound/openal.c @@ -21,7 +21,6 @@ #include #include #include -#include #undef AL_API #undef ALC_API #define AL_LIBTYPE_STATIC @@ -40,9 +39,10 @@ ALuint buffers[4]; /* front and back buffers */ ALuint buffers_music[4]; /* front and back buffers */ +ALuint buffers_wt[4]; /* front and back buffers */ ALuint buffers_cd[4]; /* front and back buffers */ ALuint buffers_midi[4]; /* front and back buffers */ -static ALuint source[4]; /* audio source */ +static ALuint source[5]; /* audio source */ static int midi_freq = 44100; static int midi_buf_size = 4410; @@ -52,13 +52,12 @@ static ALCcontext *Context; static ALCdevice *Device; void -al_set_midi(int freq, int buf_size) +al_set_midi(const int freq, const int buf_size) { midi_freq = freq; midi_buf_size = buf_size; } -void closeal(void); ALvoid alutInit(UNUSED(ALint *argc), UNUSED(ALbyte **argv)) { @@ -116,14 +115,15 @@ inital(void) { float *buf = NULL; float *music_buf = NULL; + float *wt_buf = NULL; float *cd_buf = NULL; float *midi_buf = NULL; int16_t *buf_int16 = NULL; int16_t *music_buf_int16 = NULL; + int16_t *wt_buf_int16 = NULL; int16_t *cd_buf_int16 = NULL; int16_t *midi_buf_int16 = NULL; - const char *mdn; int init_midi = 0; if (initialized) @@ -132,21 +132,23 @@ inital(void) alutInit(0, 0); atexit(closeal); - mdn = midi_out_device_get_internal_name(midi_output_device_current); - if (strcmp(mdn, "none") && strcmp(mdn, SYSTEM_MIDI_INTERNAL_NAME)) + const char *mdn = midi_out_device_get_internal_name(midi_output_device_current); + if ((strcmp(mdn, "none") != 0) && (strcmp(mdn, SYSTEM_MIDI_INTERNAL_NAME) != 0)) init_midi = 1; /* If the device is neither none, nor system MIDI, initialize the - MIDI buffer and source, otherwise, do not. */ - sources = 3 + !!init_midi; + MIDI buffer and source, otherwise, do not. */ + sources = 4 + !!init_midi; if (sound_is_float) { buf = (float *) calloc((BUFLEN << 1), sizeof(float)); music_buf = (float *) calloc((MUSICBUFLEN << 1), sizeof(float)); + wt_buf = (float *) calloc((WTBUFLEN << 1), sizeof(float)); cd_buf = (float *) calloc((CD_BUFLEN << 1), sizeof(float)); if (init_midi) midi_buf = (float *) calloc(midi_buf_size, sizeof(float)); } else { buf_int16 = (int16_t *) calloc((BUFLEN << 1), sizeof(int16_t)); music_buf_int16 = (int16_t *) calloc((MUSICBUFLEN << 1), sizeof(int16_t)); + wt_buf_int16 = (int16_t *) calloc((WTBUFLEN << 1), sizeof(int16_t)); cd_buf_int16 = (int16_t *) calloc((CD_BUFLEN << 1), sizeof(int16_t)); if (init_midi) midi_buf_int16 = (int16_t *) calloc(midi_buf_size, sizeof(int16_t)); @@ -155,47 +157,55 @@ inital(void) alGenBuffers(4, buffers); alGenBuffers(4, buffers_cd); alGenBuffers(4, buffers_music); + alGenBuffers(4, buffers_wt); if (init_midi) alGenBuffers(4, buffers_midi); if (init_midi) - alGenSources(4, source); + alGenSources(5, source); else - alGenSources(3, source); + alGenSources(4, source); - alSource3f(source[0], AL_POSITION, 0.0, 0.0, 0.0); - alSource3f(source[0], AL_VELOCITY, 0.0, 0.0, 0.0); - alSource3f(source[0], AL_DIRECTION, 0.0, 0.0, 0.0); - alSourcef(source[0], AL_ROLLOFF_FACTOR, 0.0); + alSource3f(source[0], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source[0], AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSource3f(source[0], AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSourcef(source[0], AL_ROLLOFF_FACTOR, 0.0f); alSourcei(source[0], AL_SOURCE_RELATIVE, AL_TRUE); - alSource3f(source[1], AL_POSITION, 0.0, 0.0, 0.0); - alSource3f(source[1], AL_VELOCITY, 0.0, 0.0, 0.0); - alSource3f(source[1], AL_DIRECTION, 0.0, 0.0, 0.0); - alSourcef(source[1], AL_ROLLOFF_FACTOR, 0.0); + alSource3f(source[1], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source[1], AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSource3f(source[1], AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSourcef(source[1], AL_ROLLOFF_FACTOR, 0.0f); alSourcei(source[1], AL_SOURCE_RELATIVE, AL_TRUE); - alSource3f(source[2], AL_POSITION, 0.0, 0.0, 0.0); - alSource3f(source[2], AL_VELOCITY, 0.0, 0.0, 0.0); - alSource3f(source[2], AL_DIRECTION, 0.0, 0.0, 0.0); - alSourcef(source[2], AL_ROLLOFF_FACTOR, 0.0); + alSource3f(source[2], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source[2], AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSource3f(source[2], AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSourcef(source[2], AL_ROLLOFF_FACTOR, 0.0f); alSourcei(source[2], AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(source[3], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source[3], AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSource3f(source[3], AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSourcef(source[3], AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(source[3], AL_SOURCE_RELATIVE, AL_TRUE); if (init_midi) { - alSource3f(source[3], AL_POSITION, 0.0, 0.0, 0.0); - alSource3f(source[3], AL_VELOCITY, 0.0, 0.0, 0.0); - alSource3f(source[3], AL_DIRECTION, 0.0, 0.0, 0.0); - alSourcef(source[3], AL_ROLLOFF_FACTOR, 0.0); - alSourcei(source[3], AL_SOURCE_RELATIVE, AL_TRUE); + alSource3f(source[4], AL_POSITION, 0.0f, 0.0f, 0.0f); + alSource3f(source[4], AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSource3f(source[4], AL_DIRECTION, 0.0f, 0.0f, 0.0f); + alSourcef(source[4], AL_ROLLOFF_FACTOR, 0.0f); + alSourcei(source[4], AL_SOURCE_RELATIVE, AL_TRUE); } if (sound_is_float) { memset(buf, 0, BUFLEN * 2 * sizeof(float)); memset(cd_buf, 0, CD_BUFLEN * 2 * sizeof(float)); memset(music_buf, 0, MUSICBUFLEN * 2 * sizeof(float)); + memset(wt_buf, 0, WTBUFLEN * 2 * sizeof(float)); if (init_midi) memset(midi_buf, 0, midi_buf_size * sizeof(float)); } else { memset(buf_int16, 0, BUFLEN * 2 * sizeof(int16_t)); memset(cd_buf_int16, 0, CD_BUFLEN * 2 * sizeof(int16_t)); memset(music_buf_int16, 0, MUSICBUFLEN * 2 * sizeof(int16_t)); + memset(wt_buf_int16, 0, WTBUFLEN * 2 * sizeof(int16_t)); if (init_midi) memset(midi_buf_int16, 0, midi_buf_size * sizeof(int16_t)); } @@ -204,39 +214,45 @@ inital(void) if (sound_is_float) { alBufferData(buffers[c], AL_FORMAT_STEREO_FLOAT32, buf, BUFLEN * 2 * sizeof(float), FREQ); alBufferData(buffers_music[c], AL_FORMAT_STEREO_FLOAT32, music_buf, MUSICBUFLEN * 2 * sizeof(float), MUSIC_FREQ); + alBufferData(buffers_wt[c], AL_FORMAT_STEREO_FLOAT32, wt_buf, WTBUFLEN * 2 * sizeof(float), WT_FREQ); alBufferData(buffers_cd[c], AL_FORMAT_STEREO_FLOAT32, cd_buf, CD_BUFLEN * 2 * sizeof(float), CD_FREQ); if (init_midi) - alBufferData(buffers_midi[c], AL_FORMAT_STEREO_FLOAT32, midi_buf, midi_buf_size * sizeof(float), midi_freq); + alBufferData(buffers_midi[c], AL_FORMAT_STEREO_FLOAT32, midi_buf, midi_buf_size * (int) sizeof(float), midi_freq); } else { alBufferData(buffers[c], AL_FORMAT_STEREO16, buf_int16, BUFLEN * 2 * sizeof(int16_t), FREQ); alBufferData(buffers_music[c], AL_FORMAT_STEREO16, music_buf_int16, MUSICBUFLEN * 2 * sizeof(int16_t), MUSIC_FREQ); + alBufferData(buffers_wt[c], AL_FORMAT_STEREO16, wt_buf_int16, WTBUFLEN * 2 * sizeof(int16_t), WT_FREQ); alBufferData(buffers_cd[c], AL_FORMAT_STEREO16, cd_buf_int16, CD_BUFLEN * 2 * sizeof(int16_t), CD_FREQ); if (init_midi) - alBufferData(buffers_midi[c], AL_FORMAT_STEREO16, midi_buf_int16, midi_buf_size * sizeof(int16_t), midi_freq); + alBufferData(buffers_midi[c], AL_FORMAT_STEREO16, midi_buf_int16, midi_buf_size * (int) sizeof(int16_t), midi_freq); } } alSourceQueueBuffers(source[0], 4, buffers); alSourceQueueBuffers(source[1], 4, buffers_music); - alSourceQueueBuffers(source[2], 4, buffers_cd); + alSourceQueueBuffers(source[2], 4, buffers_wt); + alSourceQueueBuffers(source[3], 4, buffers_cd); if (init_midi) - alSourceQueueBuffers(source[3], 4, buffers_midi); + alSourceQueueBuffers(source[4], 4, buffers_midi); alSourcePlay(source[0]); alSourcePlay(source[1]); alSourcePlay(source[2]); + alSourcePlay(source[3]); if (init_midi) - alSourcePlay(source[3]); + alSourcePlay(source[4]); if (sound_is_float) { if (init_midi) free(midi_buf); free(cd_buf); + free(wt_buf); free(music_buf); free(buf); } else { if (init_midi) free(midi_buf_int16); free(cd_buf_int16); + free(wt_buf_int16); free(music_buf_int16); free(buf_int16); } @@ -245,12 +261,11 @@ inital(void) } void -givealbuffer_common(void *buf, uint8_t src, int size, int freq) +givealbuffer_common(const void *buf, const uint8_t src, const int size, const int freq) { int processed; int state; ALuint buffer; - double gain; if (!initialized) return; @@ -263,40 +278,40 @@ givealbuffer_common(void *buf, uint8_t src, int size, int freq) alGetSourcei(source[src], AL_BUFFERS_PROCESSED, &processed); if (processed >= 1) { - gain = pow(10.0, (double) sound_gain / 20.0); - alListenerf(AL_GAIN, gain); + const double gain = pow(10.0, (double) sound_gain / 20.0); + alListenerf(AL_GAIN, (float) gain); alSourceUnqueueBuffers(source[src], 1, &buffer); if (sound_is_float) - alBufferData(buffer, AL_FORMAT_STEREO_FLOAT32, buf, size * sizeof(float), freq); + alBufferData(buffer, AL_FORMAT_STEREO_FLOAT32, buf, size * (int) sizeof(float), freq); else - alBufferData(buffer, AL_FORMAT_STEREO16, buf, size * sizeof(int16_t), freq); + alBufferData(buffer, AL_FORMAT_STEREO16, buf, size * (int) sizeof(int16_t), freq); alSourceQueueBuffers(source[src], 1, &buffer); } } void -givealbuffer(void *buf) +givealbuffer(const void *buf) { givealbuffer_common(buf, 0, BUFLEN << 1, FREQ); } void -givealbuffer_music(void *buf) +givealbuffer_music(const void *buf) { givealbuffer_common(buf, 1, MUSICBUFLEN << 1, MUSIC_FREQ); } void -givealbuffer_cd(void *buf) +givealbuffer_cd(const void *buf) { givealbuffer_common(buf, 2, CD_BUFLEN << 1, CD_FREQ); } void -givealbuffer_midi(void *buf, uint32_t size) +givealbuffer_midi(const void *buf, const uint32_t size) { - givealbuffer_common(buf, 3, size, midi_freq); + givealbuffer_common(buf, 3, (int) size, midi_freq); } diff --git a/src/sound/snd_emu8k.c b/src/sound/snd_emu8k.c index 793f8bcb7..cc01ff6dd 100644 --- a/src/sound/snd_emu8k.c +++ b/src/sound/snd_emu8k.c @@ -1758,8 +1758,7 @@ int32_t old_vol[32] = { 0 }; void emu8k_update(emu8k_t *emu8k) { - int new_pos = (sound_pos_global * FREQ_44100) / SOUND_FREQ; - if (emu8k->pos >= new_pos) + if (emu8k->pos >= wavetable_pos_global) return; int32_t *buf; @@ -1768,16 +1767,16 @@ emu8k_update(emu8k_t *emu8k) /* Clean the buffers since we will accumulate into them. */ buf = &emu8k->buffer[emu8k->pos * 2]; - memset(buf, 0, 2 * (new_pos - emu8k->pos) * sizeof(emu8k->buffer[0])); - memset(&emu8k->chorus_in_buffer[emu8k->pos], 0, (new_pos - emu8k->pos) * sizeof(emu8k->chorus_in_buffer[0])); - memset(&emu8k->reverb_in_buffer[emu8k->pos], 0, (new_pos - emu8k->pos) * sizeof(emu8k->reverb_in_buffer[0])); + memset(buf, 0, 2 * (wavetable_pos_global - emu8k->pos) * sizeof(emu8k->buffer[0])); + memset(&emu8k->chorus_in_buffer[emu8k->pos], 0, (wavetable_pos_global - emu8k->pos) * sizeof(emu8k->chorus_in_buffer[0])); + memset(&emu8k->reverb_in_buffer[emu8k->pos], 0, (wavetable_pos_global - emu8k->pos) * sizeof(emu8k->reverb_in_buffer[0])); /* Voices section */ for (uint8_t c = 0; c < 32; c++) { emu_voice = &emu8k->voice[c]; buf = &emu8k->buffer[emu8k->pos * 2]; - for (pos = emu8k->pos; pos < new_pos; pos++) { + for (pos = emu8k->pos; pos < wavetable_pos_global; pos++) { int32_t dat; if (emu_voice->cvcf_curr_volume) { @@ -2121,12 +2120,12 @@ emu8k_update(emu8k_t *emu8k) } buf = &emu8k->buffer[emu8k->pos * 2]; - emu8k_work_reverb(&emu8k->reverb_in_buffer[emu8k->pos], buf, &emu8k->reverb_engine, new_pos - emu8k->pos); - emu8k_work_chorus(&emu8k->chorus_in_buffer[emu8k->pos], buf, &emu8k->chorus_engine, new_pos - emu8k->pos); - emu8k_work_eq(buf, new_pos - emu8k->pos); + emu8k_work_reverb(&emu8k->reverb_in_buffer[emu8k->pos], buf, &emu8k->reverb_engine, wavetable_pos_global - emu8k->pos); + emu8k_work_chorus(&emu8k->chorus_in_buffer[emu8k->pos], buf, &emu8k->chorus_engine, wavetable_pos_global - emu8k->pos); + emu8k_work_eq(buf, wavetable_pos_global - emu8k->pos); // Clip signal - for (pos = emu8k->pos; pos < new_pos; pos++) { + for (pos = emu8k->pos; pos < wavetable_pos_global; pos++) { if (buf[0] < -32768) buf[0] = -32768; else if (buf[0] > 32767) @@ -2141,9 +2140,9 @@ emu8k_update(emu8k_t *emu8k) } /* Update EMU clock. */ - emu8k->wc += (new_pos - emu8k->pos); + emu8k->wc += (wavetable_pos_global - emu8k->pos); - emu8k->pos = new_pos; + emu8k->pos = wavetable_pos_global; } void diff --git a/src/sound/snd_lpt_dac.c b/src/sound/snd_lpt_dac.c index 8fb526f14..8d7dbba4e 100644 --- a/src/sound/snd_lpt_dac.c +++ b/src/sound/snd_lpt_dac.c @@ -5,12 +5,14 @@ #include #include "cpu.h" + +#include #include <86box/86box.h> #include <86box/filters.h> +#include <86box/timer.h> #include <86box/lpt.h> #include <86box/machine.h> #include <86box/sound.h> -#include <86box/timer.h> #include <86box/plat_unused.h> typedef struct lpt_dac_t { @@ -50,6 +52,14 @@ dac_write_data(uint8_t val, void *priv) dac_update(lpt_dac); } +static void +dac_strobe(uint8_t old, uint8_t val, void *priv) +{ + lpt_dac_t *lpt_dac = (lpt_dac_t *) priv; + + lpt_dac->channel = val; +} + static void dac_write_ctrl(uint8_t val, void *priv) { @@ -110,25 +120,31 @@ dac_close(void *priv) } const lpt_device_t lpt_dac_device = { - .name = "LPT DAC / Covox Speech Thing", - .internal_name = "lpt_dac", - .init = dac_init, - .close = dac_close, - .write_data = dac_write_data, - .write_ctrl = dac_write_ctrl, - .read_data = NULL, - .read_status = dac_read_status, - .read_ctrl = NULL + .name = "LPT DAC / Covox Speech Thing", + .internal_name = "lpt_dac", + .init = dac_init, + .close = dac_close, + .write_data = dac_write_data, + .write_ctrl = dac_write_ctrl, + .autofeed = NULL, + .strobe = dac_strobe, + .read_status = dac_read_status, + .read_ctrl = NULL, + .epp_write_data = NULL, + .epp_request_read = NULL }; const lpt_device_t lpt_dac_stereo_device = { - .name = "Stereo LPT DAC", - .internal_name = "lpt_dac_stereo", - .init = dac_stereo_init, - .close = dac_close, - .write_data = dac_write_data, - .write_ctrl = dac_write_ctrl, - .read_data = NULL, - .read_status = dac_read_status, - .read_ctrl = NULL + .name = "Stereo LPT DAC", + .internal_name = "lpt_dac_stereo", + .init = dac_stereo_init, + .close = dac_close, + .write_data = dac_write_data, + .write_ctrl = dac_write_ctrl, + .autofeed = NULL, + .strobe = dac_strobe, + .read_status = dac_read_status, + .read_ctrl = NULL, + .epp_write_data = NULL, + .epp_request_read = NULL }; diff --git a/src/sound/snd_lpt_dss.c b/src/sound/snd_lpt_dss.c index bd794fffb..5e5191e98 100644 --- a/src/sound/snd_lpt_dss.c +++ b/src/sound/snd_lpt_dss.c @@ -7,10 +7,10 @@ #include "cpu.h" #include <86box/86box.h> #include <86box/filters.h> +#include <86box/timer.h> #include <86box/lpt.h> #include <86box/machine.h> #include <86box/sound.h> -#include <86box/timer.h> #include <86box/plat_unused.h> typedef struct dss_t { @@ -134,13 +134,16 @@ dss_close(void *priv) } const lpt_device_t dss_device = { - .name = "Disney Sound Source", - .internal_name = "dss", - .init = dss_init, - .close = dss_close, - .write_data = dss_write_data, - .write_ctrl = dss_write_ctrl, - .read_data = NULL, - .read_status = dss_read_status, - .read_ctrl = NULL + .name = "Disney Sound Source", + .internal_name = "dss", + .init = dss_init, + .close = dss_close, + .write_data = dss_write_data, + .autofeed = NULL, + .strobe = NULL, + .write_ctrl = dss_write_ctrl, + .read_status = dss_read_status, + .read_ctrl = NULL, + .epp_write_data = NULL, + .epp_request_read = NULL }; diff --git a/src/sound/snd_opl_ymfm.cpp b/src/sound/snd_opl_ymfm.cpp index 55e7f1984..1da97adfa 100644 --- a/src/sound/snd_opl_ymfm.cpp +++ b/src/sound/snd_opl_ymfm.cpp @@ -74,7 +74,6 @@ public: virtual void write(uint16_t addr, uint8_t data) = 0; virtual void generate(int32_t *data, uint32_t num_samples) = 0; - virtual void generate_resampled(int32_t *data, uint32_t num_samples) = 0; virtual int32_t *update() = 0; virtual uint8_t read(uint16_t addr) = 0; virtual void set_clock(uint32_t clock) = 0; @@ -82,6 +81,7 @@ public: protected: int32_t m_buffer[MUSICBUFLEN * 2]; int m_buf_pos; + int *m_buf_pos_global; int8_t m_flags; fm_type m_type; uint32_t m_samplerate; @@ -99,11 +99,12 @@ public: { memset(m_samples, 0, sizeof(m_samples)); memset(m_oldsamples, 0, sizeof(m_oldsamples)); - m_rateratio = (samplerate << RSM_FRAC) / m_chip.sample_rate(m_clock); - m_clock_us = 1000000.0 / (double) m_clock; - m_subtract[0] = 80.0; - m_subtract[1] = 320.0; - m_type = type; + m_rateratio = (samplerate << RSM_FRAC) / m_chip.sample_rate(m_clock); + m_clock_us = 1000000.0 / (double) m_clock; + m_subtract[0] = 80.0; + m_subtract[1] = 320.0; + m_type = type; + m_buf_pos_global = (samplerate == FREQ_49716) ? &music_pos_global : &wavetable_pos_global; if (m_type == FM_YMF278B) { if (rom_load_linear("roms/sound/yamaha/yrw801.rom", 0, 0x200000, 0, m_yrw801) == 0) { @@ -170,9 +171,10 @@ public: } } +#if 0 virtual void generate_resampled(int32_t *data, uint32_t num_samples) override { - if (m_samplerate == FREQ_49716) { + if ((m_samplerate == FREQ_49716) || (m_samplerate == FREQ_44100)) { generate(data, num_samples); return; } @@ -210,29 +212,18 @@ public: m_samplecnt += 1 << RSM_FRAC; } } +#endif virtual int32_t *update() override { - if (m_samplerate == FREQ_49716) { - if (m_buf_pos >= music_pos_global) - return m_buffer; + if (m_buf_pos >= *m_buf_pos_global) + return m_buffer; - generate(&m_buffer[m_buf_pos * 2], music_pos_global - m_buf_pos); + generate(&m_buffer[m_buf_pos * 2], *m_buf_pos_global - m_buf_pos); - for (; m_buf_pos < music_pos_global; m_buf_pos++) { - m_buffer[m_buf_pos * 2] /= 2; - m_buffer[(m_buf_pos * 2) + 1] /= 2; - } - } else { - if (m_buf_pos >= sound_pos_global) - return m_buffer; - - generate_resampled(&m_buffer[m_buf_pos * 2], sound_pos_global - m_buf_pos); - - for (; m_buf_pos < sound_pos_global; m_buf_pos++) { - m_buffer[m_buf_pos * 2] /= 2; - m_buffer[(m_buf_pos * 2) + 1] /= 2; - } + for (; m_buf_pos < *m_buf_pos_global; m_buf_pos++) { + m_buffer[m_buf_pos * 2] /= 2; + m_buffer[(m_buf_pos * 2) + 1] /= 2; } return m_buffer; @@ -343,11 +334,11 @@ ymfm_drv_init(const device_t *info) case FM_YMF289B: /* According to the datasheet, we should be using 33868800, but YMFM appears to cheat and does it using the same values as the YMF262. */ - fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF289B, OPL_FREQ); + fm = (YMFMChipBase *) new YMFMChip(14318181, FM_YMF289B, FREQ_49716); break; case FM_YMF278B: - fm = (YMFMChipBase *) new YMFMChip(33868800, FM_YMF278B, OPL_FREQ); + fm = (YMFMChipBase *) new YMFMChip(33868800, FM_YMF278B, FREQ_44100); break; } @@ -422,7 +413,8 @@ static void ymfm_drv_generate(void *priv, int32_t *data, uint32_t num_samples) { YMFMChipBase *drv = (YMFMChipBase *) priv; - drv->generate_resampled(data, num_samples); + // drv->generate_resampled(data, num_samples); + drv->generate(data, num_samples); } const device_t ym3812_ymfm_device = { diff --git a/src/sound/snd_optimc.c b/src/sound/snd_optimc.c index 245d9590e..3b649639a 100644 --- a/src/sound/snd_optimc.c +++ b/src/sound/snd_optimc.c @@ -414,7 +414,7 @@ optimc_init(const device_t *info) sound_add_handler(optimc_get_buffer, optimc); if (optimc->fm_type == FM_YMF278B) - sound_add_handler(sb_get_music_buffer_sbpro, optimc->sb); + wavetable_add_handler(sb_get_music_buffer_sbpro, optimc->sb); else music_add_handler(sb_get_music_buffer_sbpro, optimc->sb); sound_set_cd_audio_filter(sbpro_filter_cd_audio, optimc->sb); /* CD audio filter for the default context */ diff --git a/src/sound/snd_pas16.c b/src/sound/snd_pas16.c index 4530bda86..80d93dcbf 100644 --- a/src/sound/snd_pas16.c +++ b/src/sound/snd_pas16.c @@ -92,7 +92,6 @@ #include #include #include -#include #define HAVE_STDARG_H #include "cpu.h" @@ -100,24 +99,86 @@ #include <86box/device.h> #include <86box/dma.h> #include <86box/filters.h> +#include <86box/plat_unused.h> #include <86box/io.h> +#include <86box/mem.h> #include <86box/midi.h> #include <86box/pic.h> #include <86box/timer.h> #include <86box/pit.h> #include <86box/pit_fast.h> +#include <86box/rom.h> +#include <86box/scsi_ncr5380.h> +#include <86box/scsi_ncr53c400.h> #include <86box/snd_mpu401.h> #include <86box/sound.h> #include <86box/snd_opl.h> #include <86box/snd_sb.h> #include <86box/snd_sb_dsp.h> -#include <86box/plat_unused.h> +#include <86box/snd_sb_dsp.h> + +typedef struct nsc_mixer_t { + double master_l; + double master_r; + + int bass; + int treble; + + double fm_l; + double fm_r; + double imixer_l; + double imixer_r; + double line_l; + double line_r; + double cd_l; + double cd_r; + double mic_l; + double mic_r; + double pcm_l; + double pcm_r; + double speaker_l; + double speaker_r; + + uint8_t lmc1982_regs[8]; + uint8_t lmc835_regs[32]; + + uint8_t im_state; + uint16_t im_data[4]; +} nsc_mixer_t; + +typedef struct mv508_mixer_t { + double master_l; + double master_r; + + int bass; + int treble; + + double fm_l; + double fm_r; + double imixer_l; + double imixer_r; + double line_l; + double line_r; + double cd_l; + double cd_r; + double mic_l; + double mic_r; + double pcm_l; + double pcm_r; + double speaker_l; + double speaker_r; + double sb_l; + double sb_r; + + uint8_t index; + uint8_t regs[3][128]; +} mv508_mixer_t; typedef struct pas16_t { uint8_t this_id; uint8_t board_id; uint8_t master_ff; - uint8_t irq; + uint8_t has_scsi; uint8_t dma; uint8_t sb_irqdma; uint8_t type; @@ -150,6 +211,10 @@ typedef struct pas16_t { uint8_t midi_data; uint8_t fifo_stat; + uint8_t timeout_count; + uint8_t timeout_status; + uint8_t pad_array[6]; + uint8_t midi_queue[256]; uint16_t base; @@ -170,12 +235,22 @@ typedef struct pas16_t { int midi_uart_in; int sysex; + int irq; + int scsi_irq; + + nsc_mixer_t nsc_mixer; + mv508_mixer_t mv508_mixer; + fm_drv_t opl; sb_dsp_t dsp; mpu_t * mpu; pitf_t * pit; + + ncr53c400_t *scsi; + + pc_timer_t scsi_timer; } pas16_t; static uint8_t pas16_next = 0; @@ -207,45 +282,385 @@ enum { PAS16_FILT_MUTE = 0x20 }; +enum { + STATE_SM_IDLE = 0x00, + STATE_LMC1982_ADDR = 0x10, + STATE_LMC1982_ADDR_0 = 0x10, + STATE_LMC1982_ADDR_1, + STATE_LMC1982_ADDR_2, + STATE_LMC1982_ADDR_3, + STATE_LMC1982_ADDR_4, + STATE_LMC1982_ADDR_5, + STATE_LMC1982_ADDR_6, + STATE_LMC1982_ADDR_7, + STATE_LMC1982_ADDR_OVER, + STATE_LMC1982_DATA = 0x10, + STATE_LMC1982_DATA_0 = 0x20, + STATE_LMC1982_DATA_1, + STATE_LMC1982_DATA_2, + STATE_LMC1982_DATA_3, + STATE_LMC1982_DATA_4, + STATE_LMC1982_DATA_5, + STATE_LMC1982_DATA_6, + STATE_LMC1982_DATA_7, + STATE_LMC1982_DATA_8, + STATE_LMC1982_DATA_9, + STATE_LMC1982_DATA_A, + STATE_LMC1982_DATA_B, + STATE_LMC1982_DATA_C, + STATE_LMC1982_DATA_D, + STATE_LMC1982_DATA_E, + STATE_LMC1982_DATA_F, + STATE_LMC1982_DATA_OVER, + STATE_LMC835_DATA = 0x40, + STATE_LMC835_DATA_0 = 0x40, + STATE_LMC835_DATA_1, + STATE_LMC835_DATA_2, + STATE_LMC835_DATA_3, + STATE_LMC835_DATA_4, + STATE_LMC835_DATA_5, + STATE_LMC835_DATA_6, + STATE_LMC835_DATA_7, + STATE_LMC835_DATA_OVER, +}; + +#define STATE_DATA_MASK 0x07 +#define STATE_DATA_MASK_W 0x0f + +#define LMC1982_ADDR 0x00 +#define LMC1982_DATA 0x01 +#define LMC835_ADDR 0x02 +#define LMC835_DATA 0x03 + +#define LMC835_FLAG_ADDR 0x80 + +#define SERIAL_MIXER_IDENT 0x10 +#define SERIAL_MIXER_STROBE 0x04 +#define SERIAL_MIXER_CLOCK 0x02 +#define SERIAL_MIXER_DATA 0x01 +#define SERIAL_MIXER_RESET_PCM 0x01 + #define PAS16_PCM_AND_DMA_ENA (PAS16_PCM_ENA | PAS16_PCM_DMA_ENA) -double low_fir_pas16_coef[4][SB16_NCoef]; +/* + LMC1982CIN registers (data bits 7 and 6 are always don't care): + - 40 = Mode (0x01 = INPUT2); + - 41 = 0 = Loudness (1 = on, 0 = off), 1 = Stereo Enhance (1 = on, 0 = off) + (0x00); + - 42 = Bass, 00-0c (0x06); + - 43 = Treble, 00-0c (0x06); + - 45 [L] / 44 [R] = Master, 28-00, counting down (0x28, later 0xa8); + - 46 = ???? (0x05 = Stereo). + */ +#define LMC1982_REG_MASK 0xf8 /* LMC1982CIN: Register Mask */ +#define LMC1982_REG_VALID 0x40 /* LMC1982CIN: Register Valid Value */ + +#define LMC1982_REG_ISELECT 0x00 /* LMC1982CIN: Input Select + Mute */ +#define LMC1982_REG_LES 0x01 /* LMC1982CIN: Loudness, Enhanced Stereo */ +#define LMC1982_REG_BASS 0x02 /* LMC1982CIN: Bass */ +#define LMC1982_REG_TREBLE 0x03 /* LMC1982CIN: Treble */ +/* The Windows 95 driver indicates left and right are swapped in the wiring. */ +#define LMC1982_REG_VOL_L 0x04 /* LMC1982CIN: Left Volume */ +#define LMC1982_REG_VOL_R 0x05 /* LMC1982CIN: Right Volume */ +#define LMC1982_REG_MODE 0x06 /* LMC1982CIN: Mode */ +#define LMC1982_REG_DINPUT 0x07 /* LMC1982CIN: Digital Input 1 and 2 */ + +/* Bits 7-2: Don't care. */ +#define LMC1982_ISELECT_MASK 0x03 /* LMC1982CIN: Input Select: Mask */ +#define LMC1982_ISELECT_I1 0x00 /* LMC1982CIN: Input Select: INPUT1 */ +#define LMC1982_ISELECT_I2 0x01 /* LMC1982CIN: Input Select: INPUT2 */ +#define LMC1982_ISELECT_NA 0x02 /* LMC1982CIN: Input Select: N/A */ +#define LMC1982_ISELECT_MUTE 0x03 /* LMC1982CIN: Input Select: MUTE */ + +/* Bits 7-2: Don't care. */ +#define LMC1982_LES_MASK 0x03 /* LMC1982CIN: Loudness, Enhanced Stereo: Mask */ +#define LMC1982_LES_BOTH_OFF 0x00 /* LMC1982CIN: Loudness OFF, Enhanced Stereo OFF */ +#define LMC1982_L_ON_ES_OFF 0x01 /* LMC1982CIN: Loudness ON, Enhanced Stereo OFF */ +#define LMC1982_L_OFF_ES_ON 0x02 /* LMC1982CIN: Loudness OFF, Enhanced Stereo ON */ +#define LMC1982_LES_BOTH_ON 0x03 /* LMC1982CIN: Loudness OFF, Enhanced Stereo ON */ + +/* Bits 7-3: Don't care. */ +#define LMC1982_MODE_MASK 0x07 /* LMC1982CIN: Mode: Mask */ +#define LMC1982_MODE_MONO_L 0x04 /* LMC1982CIN: Mode: Left Mono */ +#define LMC1982_MODE_STEREO 0x05 /* LMC1982CIN: Mode: Stereo */ +#define LMC1982_MODE_MONO_R 0x06 /* LMC1982CIN: Mode: Right Mono */ +#define LMC1982_MODE_MONO_R_2 0x07 /* LMC1982CIN: Mode: Right Mono (Alternate value) */ + +/* Bits 7-2: Don't care. */ +#define LMC1982_DINPUT_MASK 0x03 /* LMC1982CIN: Digital Input 1 and 2: Mask */ +#define LMC1982_DINPUT_DI1 0x01 /* LMC1982CIN: Digital Input 1 */ +#define LMC1982_DINPUT_DI2 0x02 /* LMC1982CIN: Digital Input 2 */ + +/* + LMC835N registers: + - 01 [L] / 08 [R] = FM, 00-2f, bit 6 = clear, but the DOS driver sets + the bit, indicating a volume boost (0x69); + - 02 [L] / 09 [R] = Rec. monitor, 00-2f, bit 6 = clear (0x29); + - 03 [L] / 0A [R] = Line in, 00-2f, bit 6 = clear, except for the DOS + driver (0x69); + - 04 [L] / 0B [R] = CD, 00-2f, bit 6 = clear, except for the DOS driver + (0x69); + - 05 [L] / 0C [R] = Microphone, 00-2f, bit 6 = clear, except for the DOS + driver (0x44); + - 06 [L] / 0D [R] = Wave out, 00-2f, bit 6 = set, except for DOS driver, + which clears the bit (0x29); + - 07 [L] / 0E [R] = PC speaker, 00-2f, bit 6 = clear, except for the DOS + drive (0x06). + The registers for which the DOS driver sets the boost, also have bit 6 + set in the address, despite the fact it should be a don't care - why? + Apparently, no Sound Blaster control. +*/ +#define LMC835_REG_MODE 0x00 /* LMC835N: Mode, not an actual register, but our internal register + to store the mode for Channels A (1-7) and B (8-e). */ + +#define LMC835_REG_FM_L 0x01 /* LMC835N: FM Left */ +#define LMC835_REG_IMIXER_L 0x02 /* LMC835N: Record Monitor Left */ +#define LMC835_REG_LINE_L 0x03 /* LMC835N: Line in Left */ +#define LMC835_REG_CDROM_L 0x04 /* LMC835N: CD Left */ +#define LMC835_REG_MIC_L 0x05 /* LMC835N: Microphone Left */ +#define LMC835_REG_PCM_L 0x06 /* LMC835N: Wave out Left */ +#define LMC835_REG_SPEAKER_L 0x07 /* LMC83N5: PC speaker Left */ + +#define LMC835_REG_FM_R 0x08 /* LMC835N: FM Right */ +#define LMC835_REG_IMIXER_R 0x09 /* LMC835N: Record Monitor Right */ +#define LMC835_REG_LINE_R 0x0a /* LMC835N: Line in Right */ +#define LMC835_REG_CDROM_R 0x0b /* LMC835N: CD Right */ +#define LMC835_REG_MIC_R 0x0c /* LMC835N: Microphone Right */ +#define LMC835_REG_PCM_R 0x0d /* LMC835N: Wave out Right */ +#define LMC835_REG_SPEAKER_R 0x0e /* LMC83N5: PC speaker Right */ + +#define MV508_ADDRESS 0x80 /* Flag indicating it is the address */ +/* + I think this may actually operate as such: + - Bit 6: Mask left channel; + - Bit 5: Mask right channel. + */ +#define MV508_CHANNEL 0x60 +#define MV508_LEFT 0x20 +#define MV508_RIGHT 0x40 +#define MV508_BOTH 0x00 +#define MV508_MIXER 0x10 /* Flag indicating it is a mixer rather than a volume */ + +#define MV508_INPUT_MIX 0x20 /* Flag indicating the selected mixer is input */ + +#define MV508_MASTER_A 0x01 /* Volume: Output */ +#define MV508_MASTER_B 0x02 /* Volume: DSP input */ +#define MV508_BASS 0x03 /* Volume: Bass */ +#define MV508_TREBLE 0x04 /* Volume: Treble */ +#define MV508_MODE 0x05 /* Volume: Mode */ + +#define MV508_LOUDNESS 0x04 /* Mode: Loudness */ +#define MV508_ENH_MASK 0x03 /* Mode: Stereo enhancement bit mask */ +#define MV508_ENH_NONE 0x00 /* Mode: No stereo enhancement */ +#define MV508_ENH_40 0x01 /* Mode: 40% stereo enhancement */ +#define MV508_ENH_60 0x02 /* Mode: 60% stereo enhancement */ +#define MV508_ENH_80 0x03 /* Mode: 80% stereo enhancement */ + +#define MV508_FM 0x00 /* Mixer: FM */ +#define MV508_IMIXER 0x01 /* Mixer: Input mixer (recording monitor) */ +#define MV508_LINE 0x02 /* Mixer: Line in */ +#define MV508_CDROM 0x03 /* Mixer: CD-ROM */ +#define MV508_MIC 0x04 /* Mixer: Microphone */ +#define MV508_PCM 0x05 /* Mixer: PCM */ +#define MV508_SPEAKER 0x06 /* Mixer: PC Speaker */ +#define MV508_SB 0x07 /* Mixer: Sound Blaster DSP */ + +#define MV508_REG_MASTER_A_L (MV508_MASTER_A | MV508_LEFT) +#define MV508_REG_MASTER_A_R (MV508_MASTER_A | MV508_RIGHT) +#define MV508_REG_MASTER_B_L (MV508_MASTER_B | MV508_LEFT) +#define MV508_REG_MASTER_B_R (MV508_MASTER_B | MV508_RIGHT) +#define MV508_REG_BASS (MV508_BASS | MV508_LEFT) +#define MV508_REG_TREBLE (MV508_TREBLE | MV508_LEFT) + +#define MV508_REG_FM_L (MV508_MIXER | MV508_FM | MV508_LEFT) +#define MV508_REG_FM_R (MV508_MIXER | MV508_FM | MV508_RIGHT) +#define MV508_REG_IMIXER_L (MV508_MIXER | MV508_IMIXER | MV508_LEFT) +#define MV508_REG_IMIXER_R (MV508_MIXER | MV508_IMIXER | MV508_RIGHT) +#define MV508_REG_LINE_L (MV508_MIXER | MV508_LINE | MV508_LEFT) +#define MV508_REG_LINE_R (MV508_MIXER | MV508_LINE | MV508_RIGHT) +#define MV508_REG_CDROM_L (MV508_MIXER | MV508_CDROM | MV508_LEFT) +#define MV508_REG_CDROM_R (MV508_MIXER | MV508_CDROM | MV508_RIGHT) +#define MV508_REG_MIC_L (MV508_MIXER | MV508_MIC | MV508_LEFT) +#define MV508_REG_MIC_R (MV508_MIXER | MV508_MIC | MV508_RIGHT) +#define MV508_REG_PCM_L (MV508_MIXER | MV508_PCM | MV508_LEFT) +#define MV508_REG_PCM_R (MV508_MIXER | MV508_PCM | MV508_RIGHT) +#define MV508_REG_SPEAKER_L (MV508_MIXER | MV508_SPEAKER | MV508_LEFT) +#define MV508_REG_SPEAKER_R (MV508_MIXER | MV508_SPEAKER | MV508_RIGHT) +#define MV508_REG_SB_L (MV508_MIXER | MV508_SB | MV508_LEFT) +#define MV508_REG_SB_R (MV508_MIXER | MV508_SB | MV508_RIGHT) + +double low_fir_pas16_coef[SB16_NCoef]; + +/* + Also used for the MVA508. + */ +static double lmc1982_bass_treble_4bits[16]; + +/* + Copied from the Sound Blaster code: -62 dB to 0 dB in 2 dB steps. + Note that these are voltage dB's, so it corresonds in power dB + (formula for conversion to percentage: 10 ^ (dB / 10)) to -31 dB + to 0 dB in 1 dB steps. + + This is used for the MVA508 Volumes. + */ +static const double mva508_att_2dbstep_5bits[] = { + 25.0, 32.0, 41.0, 51.0, 65.0, 82.0, 103.0, 130.0, + 164.0, 206.0, 260.0, 327.0, 412.0, 519.0, 653.0, 822.0, + 1036.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 +}; + +/* + The same but in 1 dB steps, used for the MVA508 Master Volume. + */ +static const double mva508_att_1dbstep_6bits[] = { + 18.0, 25.0, 29.0, 32.0, 36.0, 41.0, 46.0, 51.0, + 58.0, 65.0, 73.0, 82.0, 92.0, 103.0, 116.0, 130.0, + 146.0, 164.0, 184.0, 206.0, 231.0, 260.0, 292.0, 327.0, + 367.0, 412.0, 462.0, 519.0, 582.0, 653.0, 733.0, 822.0, + 923.0, 1036.0, 1162.0, 1304.0, 1463.0, 1641.0, 1842.0, 2067.0, + 2319.0, 2602.0, 2920.0, 3276.0, 3676.0, 4125.0, 4628.0, 5192.0, + 5826.0, 6537.0, 7335.0, 8230.0, 9234.0, 10362.0, 11626.0, 13044.0, + 14636.0, 16422.0, 18426.0, 20674.0, 23197.0, 26027.0, 29204.0, 32767.0 +}; + +/* + In 2 dB steps again, but to -80 dB and counting down (0 = Flat), used + for the LMC1982CIN Master Volume. + */ +static const double lmc1982_att_2dbstep_6bits[] = { + 32767.0, 26027.0, 20674.0, 16422.0, 13044.0, 10362.0, 8230.0, 6537.0, + 5192.0, 4125.0, 3276.0, 2602.0, 2067.0, 1641.0, 1304.0, 1036.0, + 822.0, 653.0, 519.0, 412.0, 327.0, 260.0, 206.0, 164.0, + 130.0, 103.0, 82.0, 65.0, 51.0, 41.0, 32.0, 25.0, + 20.0, 16.0, 13.0, 10.0, 8.0, 6.0, 5.0, 4.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, + 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 +}; + +/* + LMC385N attenuation, both +/- 12 dB and +/- 6 dB. + + Since the DOS and Windows 95 driver diverge on boost vs. cut for + the various inputs, I think it is best to just do a 12 dB cut on + the input, and then apply cut or boost as needed. I have factored + in said cut in the below values. + */ +static const double lmc835_att_1dbstep_7bits[128] = { + 0.0, + /* Flat */ + [0x40] = 8230.0, /* Flat */ + /* Boost */ + [0x60] = 9234.0, /* 1 dB Boost */ + [0x50] = 10362.0, /* 2 dB Boost */ + [0x48] = 11626.0, /* 3 dB Boost */ + [0x44] = 13044.0, /* 4 dB Boost */ + [0x42] = 14636.0, /* 5 dB Boost */ + [0x52] = 16422.0, /* 6 dB Boost */ + [0x6a] = 18426.0, /* 7 dB Boost */ + [0x56] = 20674.0, /* 8 dB Boost */ + [0x41] = 23197.0, /* 9 dB Boost */ + [0x69] = 26027.0, /* 10 dB Boost */ + [0x6d] = 29204.0, /* 11 dB Boost */ + /* The Win95 drivers use D5-D0 = 1D instead of 2D, datasheet erratum? */ + [0x5d] = 29204.0, /* 11 dB Boost */ + [0x6f] = 32767.0, /* 12 dB Boost */ + /* Flat */ + /* The datasheet says this should be Flat (1.0) but the + Windows 95 drivers use this as basically mute (12 dB Cut). */ + [0x00] = 2067.0, /* 12 dB Cut */ + /* Cut - D5-D0 = 2F is minimum cut (0 dB) according to Windows 95 */ + [0x20] = 2319.0, /* 11 dB Cut */ + [0x10] = 2602.0, /* 10 dB Cut */ + [0x08] = 2920.0, /* 9 dB Cut */ + [0x04] = 3276.0, /* 8 dB Cut */ + [0x02] = 3676.0, /* 7 dB Cut */ + [0x12] = 4125.0, /* 6 dB Cut */ + [0x2a] = 4628.0, /* 5 dB Cut */ + [0x16] = 5192.0, /* 4 dB Cut */ + [0x01] = 5826.0, /* 3 dB Cut */ + [0x29] = 6537.0, /* 2 dB Cut */ + [0x2d] = 7335.0, /* 1 dB Cut */ + /* The Win95 drivers use D5-D0 = 1D instead of 2D, datasheet erratum? */ + [0x1d] = 7335.0, /* 1 dB Cut */ + [0x2f] = 8230.0, /* Flat */ +}; + +static const double lmc835_att_05dbstep_7bits[128] = { + 0.0 , + /* Flat */ + [0x40] = 8230.0, /* Flat */ + /* Boost */ + [0x60] = 8718.0, /* 0.5 dB Boost */ + [0x50] = 9234.0, /* 1.0 dB Boost */ + [0x48] = 9782.0, /* 1.5 dB Boost */ + [0x44] = 10362.0, /* 2.0 dB Boost */ + [0x42] = 10975.0, /* 2.5 dB Boost */ + [0x52] = 11626.0, /* 3.0 dB Boost */ + [0x6a] = 12315.0, /* 3.5 dB Boost */ + [0x56] = 13044.0, /* 4.0 dB Boost */ + [0x41] = 13817.0, /* 4.5 dB Boost */ + [0x69] = 14636.0, /* 5.0 dB Boost */ + [0x6d] = 15503.0, /* 5.5 dB Boost */ + /* The Win95 drivers use D5-D0 = 1D instead of 2D, datasheet erratum? */ + [0x5d] = 15503.0, /* 5.5 dB Boost */ + [0x6f] = 16422.0, /* 6.0 dB Boost */ + /* Flat */ + /* The datasheet says this should be Flat (1.0) but the + Windows 95 drivers use this as basically mute (12 dB Cut). */ + [0x00] = 4125.0, /* 6.0 dB Cut */ + /* Cut - D5-D0 = 2F is minimum cut (0 dB) according to Windows 95 */ + [0x20] = 4369.0, /* 5.5 dB Cut */ + [0x10] = 4628.0, /* 5.0 dB Cut */ + [0x08] = 4920.0, /* 4.5 dB Cut */ + [0x04] = 5192.0, /* 4.0 dB Cut */ + [0x02] = 5500.0, /* 3.5 dB Cut */ + [0x12] = 5826.0, /* 3.0 dB Cut */ + [0x2a] = 6172.0, /* 2.5 dB Cut */ + [0x16] = 6537.0, /* 2.0 dB Cut */ + [0x01] = 6925.0, /* 1.5 dB Cut */ + [0x29] = 7335.0, /* 1.0 dB Cut */ + [0x2d] = 7770.0, /* 0.5 dB Cut */ + /* The Win95 drivers use D5-D0 = 1D instead of 2D, datasheet erratum? */ + [0x1d] = 7770.0, /* 0.5 dB Cut */ + [0x2f] = 8230.0, /* Flat */ +}; static __inline double -sinc(double x) +sinc(const double x) { return sin(M_PI * x) / (M_PI * x); } static void -recalc_pas16_filter(int c, int playback_freq) +recalc_pas16_filter(const int playback_freq) { /* Cutoff frequency = playback / 2 */ - int n; - double w; - double h; - double fC = ((double) playback_freq) / (double) FREQ_96000; - double gain; + int n; + const double fC = ((double) playback_freq) / (double) FREQ_96000; + double gain = 0.0; for (n = 0; n < SB16_NCoef; n++) { /* Blackman window */ - w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); + const double w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); /* Sinc filter */ - h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); + const double h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); /* Create windowed-sinc filter */ - low_fir_pas16_coef[c][n] = w * h; + low_fir_pas16_coef[n] = w * h; } - low_fir_pas16_coef[c][(SB16_NCoef - 1) / 2] = 1.0; + low_fir_pas16_coef[(SB16_NCoef - 1) / 2] = 1.0; - gain = 0.0; for (n = 0; n < SB16_NCoef; n++) - gain += low_fir_pas16_coef[c][n]; + gain += low_fir_pas16_coef[n]; /* Normalise filter, to produce unity gain */ for (n = 0; n < SB16_NCoef; n++) - low_fir_pas16_coef[c][n] /= gain; + low_fir_pas16_coef[n] /= gain; } #ifdef ENABLE_PAS16_LOG @@ -276,12 +691,12 @@ pas16_update_irq(pas16_t *pas16) { if (pas16->midi_uart_out && (pas16->midi_stat & 0x18)) { pas16->irq_stat |= PAS16_INT_MIDI; - if (pas16->irq_ena & PAS16_INT_MIDI) + if ((pas16->irq != -1) && (pas16->irq_ena & PAS16_INT_MIDI)) picint(1 << pas16->irq); } if (pas16->midi_uart_in && (pas16->midi_stat & 0x04)) { pas16->irq_stat |= PAS16_INT_MIDI; - if (pas16->irq_ena & PAS16_INT_MIDI) + if ((pas16->irq != -1) && (pas16->irq_ena & PAS16_INT_MIDI)) picint(1 << pas16->irq); } } @@ -291,6 +706,7 @@ pas16_in(uint16_t port, void *priv) { pas16_t *pas16 = (pas16_t *) priv; uint8_t ret = 0xff; + ncr53c400_t *dev = (ncr53c400_t *) pas16->scsi; port -= pas16->base; @@ -300,7 +716,7 @@ pas16_in(uint16_t port, void *priv) break; case 0x0800: - ret = pas16->audio_mixer; + ret = pas16->type ? pas16->audio_mixer : 0xff; break; case 0x0801: ret = pas16->irq_stat & 0xdf; @@ -346,10 +762,47 @@ pas16_in(uint16_t port, void *priv) ret = pas16->fifo_stat; break; + case 0x1c00 ... 0x1c03: /* NCR5380 ports 0 to 3. */ + case 0x3c00 ... 0x3c03: /* NCR5380 ports 4 to 7. */ + if (pas16->has_scsi) { + port = (port & 0x0003) | ((port & 0x2000) >> 11); + ret = ncr5380_read(port, &pas16->scsi->ncr); + } + break; + case 0x2401: /* Board revision */ ret = 0x00; break; + case 0x4000: + ret = pas16->timeout_count; + break; + case 0x4001: + ret = pas16->timeout_status; + break; + + case 0x5c00: + if (pas16->has_scsi) + ret = ncr53c400_simple_read(pas16->scsi); + break; + case 0x5c01: + if (pas16->has_scsi) { + ret = ((dev->ncr.dma_mode != DMA_IDLE) && !(dev->status_ctrl & STATUS_BUFFER_NOT_READY)) << 7; + if (!(ret & 0x80)) { + ncr53c400_callback(pas16->scsi); + if ((dev->ncr.dma_mode != DMA_IDLE) && !(dev->status_ctrl & STATUS_BUFFER_NOT_READY)) { + timer_stop(&pas16->scsi_timer); + pas16->timeout_status &= 0x7f; + } + + } + } + break; + case 0x5c03: + if (pas16->has_scsi) + ret = pas16->scsi->ncr.irq_state << 7; + break; + case 0x7c01: ret = pas16->enhancedscsi & ~0x01; break; @@ -375,11 +828,13 @@ pas16_in(uint16_t port, void *priv) break; case 0xec03: -#ifdef NEWER_PAS16 - ret = pas16->type ? 0x0c : 0x06; -#else - ret = pas16->type ? 0x0f : 0x06; -#endif + /* + Operation mode 1: + - 1,0 = CD-ROM (1,1 = SCSI, 1,0 = Sony, 0,0 = N/A); + - 2 = FM (1 = stereo, 0 = mono); + - 3 = Code (1 = 16-bit, 0 = 8-bit). + */ + ret = pas16->type ? pas16->type : 0x06; break; case 0xf000: @@ -477,17 +932,154 @@ pas16_reset_pcm(void *priv) pas16->irq_stat &= 0xd7; - if (!pas16->irq_stat) + if ((pas16->irq != -1) && !pas16->irq_stat) picintc(1 << pas16->irq); } +static void +lmc1982_recalc(nsc_mixer_t *mixer) +{ + /* LMC1982CIN */ + /* According to the Windows 95 driver, the two volumes are swapped. */ + if ((mixer->lmc1982_regs[LMC1982_REG_ISELECT] & LMC1982_ISELECT_MASK) == LMC1982_ISELECT_I2) { + switch (mixer->lmc1982_regs[LMC1982_REG_MODE] & LMC1982_MODE_MASK) { + case LMC1982_MODE_MONO_L: + mixer->master_l = mixer->master_r = lmc1982_att_2dbstep_6bits[mixer->lmc1982_regs[LMC1982_REG_VOL_R]] / 32767.0; + break; + case LMC1982_MODE_STEREO: + mixer->master_l = lmc1982_att_2dbstep_6bits[mixer->lmc1982_regs[LMC1982_REG_VOL_R]] / 32767.0; + mixer->master_r = lmc1982_att_2dbstep_6bits[mixer->lmc1982_regs[LMC1982_REG_VOL_L]] / 32767.0; + break; + case LMC1982_MODE_MONO_R: + case LMC1982_MODE_MONO_R_2: + mixer->master_l = mixer->master_r = lmc1982_att_2dbstep_6bits[mixer->lmc1982_regs[LMC1982_REG_VOL_L]] / 32767.0; + break; + default: + mixer->master_l = mixer->master_r = 0.0; + break; + } + + mixer->bass = mixer->lmc1982_regs[LMC1982_REG_BASS] & 0x0f; + mixer->treble = mixer->lmc1982_regs[LMC1982_REG_TREBLE] & 0x0f; + } else { + mixer->master_l = mixer->master_r = 0.0; + + mixer->bass = 0x06; + mixer->treble = 0x06; + } +} + +static void +lmc835_recalc(nsc_mixer_t *mixer) +{ + /* LMC835N */ + /* Channel A (1-7) */ + const double *lmc835_att = (const double *) ((mixer->lmc835_regs[LMC835_REG_MODE] & 0x20) ? + lmc835_att_05dbstep_7bits : lmc835_att_1dbstep_7bits); + + mixer->fm_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_FM_L] & 0x7f] / 32767.0; + mixer->imixer_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_IMIXER_L] & 0x7f] / 32767.0; + mixer->line_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_LINE_L] & 0x7f] / 32767.0; + mixer->cd_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_CDROM_L] & 0x7f] / 32767.0; + mixer->mic_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_MIC_L] & 0x7f] / 32767.0; + mixer->pcm_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_PCM_L] & 0x7f] / 32767.0; + mixer->speaker_l = lmc835_att[mixer->lmc835_regs[LMC835_REG_SPEAKER_L] & 0x7f] / 32767.0; + + /* Channel B (8-e) */ + lmc835_att = (const double *) ((mixer->lmc835_regs[LMC835_REG_MODE] & 0x08) ? + lmc835_att_05dbstep_7bits : lmc835_att_1dbstep_7bits); + + mixer->fm_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_FM_R] & 0x7f] / 32767.0; + mixer->imixer_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_IMIXER_R] & 0x7f] / 32767.0; + mixer->line_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_LINE_R] & 0x7f] / 32767.0; + mixer->cd_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_CDROM_R] & 0x7f] / 32767.0; + mixer->mic_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_MIC_R] & 0x7f] / 32767.0; + mixer->pcm_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_PCM_R] & 0x7f] / 32767.0; + mixer->speaker_r = lmc835_att[mixer->lmc835_regs[LMC835_REG_SPEAKER_R] & 0x7f] / 32767.0; +} + +static void +mv508_mixer_recalc(mv508_mixer_t *mixer) +{ + mixer->fm_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_FM_L]] / 32767.0; + mixer->fm_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_FM_R]] / 32767.0; + mixer->imixer_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_IMIXER_L]] / 32767.0; + mixer->imixer_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_IMIXER_R]] / 32767.0; + mixer->line_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_LINE_L]] / 32767.0; + mixer->line_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_LINE_R]] / 32767.0; + mixer->cd_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_CDROM_L]] / 32767.0; + mixer->cd_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_CDROM_R]] / 32767.0; + mixer->mic_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_MIC_L]] / 32767.0; + mixer->mic_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_MIC_R]] / 32767.0; + mixer->pcm_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_PCM_L]] / 32767.0; + mixer->pcm_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_PCM_R]] / 32767.0; + mixer->speaker_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_SPEAKER_L]] / 32767.0; + mixer->speaker_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_SPEAKER_R]] / 32767.0; + mixer->sb_l = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_SB_L]] / 32767.0; + mixer->sb_r = mva508_att_2dbstep_5bits[mixer->regs[0][MV508_REG_SB_R]] / 32767.0; + + mixer->master_l = mva508_att_1dbstep_6bits[mixer->regs[2][MV508_REG_MASTER_A_L]] / 32767.0; + mixer->master_r = mva508_att_1dbstep_6bits[mixer->regs[2][MV508_REG_MASTER_A_R]] / 32767.0; + + mixer->bass = mixer->regs[2][MV508_REG_BASS] & 0x0f; + mixer->treble = mixer->regs[2][MV508_REG_TREBLE] & 0x0f; +} + +static void +pas16_nsc_mixer_reset(nsc_mixer_t *mixer) +{ + mixer->lmc1982_regs[LMC1982_REG_ISELECT] = 0x01; + mixer->lmc1982_regs[LMC1982_REG_LES] = 0x00; + mixer->lmc1982_regs[LMC1982_REG_BASS] = mixer->lmc1982_regs[LMC1982_REG_TREBLE] = 0x06; + mixer->lmc1982_regs[LMC1982_REG_VOL_L] = mixer->lmc1982_regs[LMC1982_REG_VOL_R] = 0x28; + mixer->lmc1982_regs[LMC1982_REG_MODE] = 0x05; + + lmc1982_recalc(mixer); + + mixer->lmc835_regs[LMC835_REG_MODE] = 0x00; + + mixer->lmc835_regs[LMC835_REG_FM_L] = mixer->lmc835_regs[LMC835_REG_FM_R] = 0x69; + mixer->lmc835_regs[LMC835_REG_IMIXER_L] = mixer->lmc835_regs[LMC835_REG_IMIXER_R] = 0x29; + mixer->lmc835_regs[LMC835_REG_LINE_L] = mixer->lmc835_regs[LMC835_REG_LINE_R] = 0x69; + mixer->lmc835_regs[LMC835_REG_CDROM_L] = mixer->lmc835_regs[LMC835_REG_CDROM_R] = 0x69; + mixer->lmc835_regs[LMC835_REG_MIC_L] = mixer->lmc835_regs[LMC835_REG_MIC_R] = 0x44; + mixer->lmc835_regs[LMC835_REG_PCM_L] = mixer->lmc835_regs[LMC835_REG_PCM_R] = 0x29; + mixer->lmc835_regs[LMC835_REG_SPEAKER_L] = mixer->lmc835_regs[LMC835_REG_SPEAKER_R] = 0x06; + + lmc835_recalc(mixer); +} + +static void +pas16_mv508_mixer_reset(mv508_mixer_t *mixer) +{ + /* Based on the Linux driver - TODO: The actual card's defaults. */ + mixer->regs[0][MV508_REG_FM_L] = mixer->regs[0][MV508_REG_FM_R] = 0x18; + mixer->regs[0][MV508_REG_IMIXER_L] = mixer->regs[0][MV508_REG_IMIXER_R] = 0x1f; + mixer->regs[0][MV508_REG_LINE_L] = mixer->regs[0][MV508_REG_LINE_R] = 0x17; + mixer->regs[0][MV508_REG_CDROM_L] = mixer->regs[0][MV508_REG_CDROM_R] = 0x17; + mixer->regs[0][MV508_REG_MIC_L] = mixer->regs[0][MV508_REG_MIC_R] = 0x17; + mixer->regs[0][MV508_REG_PCM_L] = mixer->regs[0][MV508_REG_PCM_R] = 0x17; + mixer->regs[0][MV508_REG_SPEAKER_L] = mixer->regs[0][MV508_REG_SPEAKER_R] = 0x0f; + mixer->regs[0][MV508_REG_SB_L] = mixer->regs[0][MV508_REG_SB_R] = 0x17; + + mixer->regs[2][MV508_REG_MASTER_A_L] = mixer->regs[2][MV508_REG_MASTER_A_R] = 0x1f; + + mixer->regs[2][MV508_REG_BASS] = 0x06; + mixer->regs[2][MV508_REG_TREBLE] = 0x06; + + mv508_mixer_recalc(mixer); +} + static void pas16_reset_regs(void *priv) { pas16_t *pas16 = (pas16_t *) priv; + nsc_mixer_t *nsc_mixer = &pas16->nsc_mixer; + mv508_mixer_t *mv508_mixer = &pas16->mv508_mixer; pitf_t *pit = (pitf_t *) pas16->pit; - picintc(1 << pas16->irq); + if (pas16->irq != -1) + picintc(1 << pas16->irq); pas16->sys_conf_1 &= 0xfd; @@ -509,6 +1101,11 @@ pas16_reset_regs(void *priv) pas16->irq_ena = 0x00; pas16->irq_stat = 0x00; + + if (pas16->type) + pas16_mv508_mixer_reset(mv508_mixer); + else + pas16_nsc_mixer_reset(nsc_mixer); } static void @@ -518,7 +1115,8 @@ pas16_reset_common(void *priv) pas16_reset_regs(pas16); - picintc(1 << pas16->irq); + if (pas16->irq != -1) + picintc(1 << pas16->irq); pas16_io_handler(pas16, 0); pas16->base = 0x0000; @@ -545,10 +1143,70 @@ pas16_reset(void *priv) sb_dsp_setaddr(&pas16->dsp, pas16->sb_compat_base); } +static int +pas16_irq_convert(uint8_t val) +{ + int ret = val; + + if (ret == 0) + ret = -1; + else if (ret <= 6) + ret++; + else if (ret < 0x0b) + ret += 3; + else + ret += 4; + + return ret; +} + +static void +lmc1982_update_reg(nsc_mixer_t *mixer) +{ + pas16_log("LMC1982CIN register %02X = %04X\n", + mixer->im_data[LMC1982_ADDR], mixer->im_data[LMC1982_DATA]); + + if ((mixer->im_data[LMC1982_ADDR] & LMC1982_REG_MASK) == LMC1982_REG_VALID) { + mixer->im_data[LMC1982_ADDR] &= ~LMC1982_REG_MASK; + mixer->lmc1982_regs[mixer->im_data[LMC1982_ADDR]] = mixer->im_data[LMC1982_DATA] & 0xff; + lmc1982_recalc(mixer); + } + + mixer->im_state = STATE_SM_IDLE; +} + +static void +lmc835_update_reg(nsc_mixer_t *mixer) +{ + pas16_log("LMC835N register %02X = %02X\n", + mixer->im_data[LMC835_ADDR], mixer->im_data[LMC835_DATA]); + + mixer->lmc835_regs[LMC835_REG_MODE] = mixer->im_data[LMC835_ADDR] & 0xf0; + mixer->im_data[LMC835_ADDR] &= 0x0f; + if ((mixer->im_data[LMC835_ADDR] >= 0x01) && (mixer->im_data[LMC835_ADDR] <= 0x0e)) + mixer->lmc835_regs[mixer->im_data[LMC835_ADDR] & 0x0f] = mixer->im_data[LMC835_DATA]; + lmc835_recalc(mixer); +} + +static void +pas16_timeout_callback(void *priv) +{ + pas16_t * pas16 = (pas16_t *) priv; + + pas16->timeout_status |= 0x80; + + if ((pas16->timeout_status & 0x08) && (pas16->irq != -1)) + picint(1 << pas16->irq); + + timer_advance_u64(&pas16->scsi_timer, (pas16->timeout_count & 0x3f) * PASSCSICONST); +} + static void pas16_out(uint16_t port, uint8_t val, void *priv) { - pas16_t *pas16 = (pas16_t *) priv; + pas16_t * pas16 = (pas16_t *) priv; + nsc_mixer_t * nsc_mixer = &pas16->nsc_mixer; + mv508_mixer_t *mv508_mixer = &pas16->mv508_mixer; pas16_log("[%04X:%08X] PAS16: [W] %04X (%04X) = %02X\n", CS, cpu_state.pc, port, port - pas16->base, val); @@ -560,14 +1218,178 @@ pas16_out(uint16_t port, uint8_t val, void *priv) pas16->opl.write(port + 0x0388, val, pas16->opl.priv); break; + case 0x0400 ... 0x0402: + break; + case 0x0403: + if (val & MV508_ADDRESS) + mv508_mixer->index = val & ~MV508_ADDRESS; + else { + uint8_t bank; + uint8_t mask; + + pas16_log("MVA508 register %02X = %02X\n", + mv508_mixer->index, val); + + if (mv508_mixer->index & MV508_MIXER) { + bank = !!(val & MV508_INPUT_MIX); + mask = 0x1f; + } else { + bank = 2; + mask = 0x3f; + } + + if (mv508_mixer->index & MV508_CHANNEL) + mv508_mixer->regs[bank][mv508_mixer->index] = val & mask; + else { + mv508_mixer->regs[bank][mv508_mixer->index | MV508_LEFT] = val & mask; + mv508_mixer->regs[bank][mv508_mixer->index | MV508_RIGHT] = val & mask; + } + + mv508_mixer_recalc(mv508_mixer); + } + break; + case 0x0800: - pas16->audio_mixer = val; - if (!(val & 0x01)) + if (pas16->type && !(val & SERIAL_MIXER_RESET_PCM)) { + pas16->audio_mixer = val; pas16_reset_pcm(pas16); + } else if (!pas16->type) { + switch (nsc_mixer->im_state) { + default: + break; + case STATE_SM_IDLE: + /* Transmission initiated. */ + if (val & SERIAL_MIXER_IDENT) { + if (!(val & SERIAL_MIXER_CLOCK) && (pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + /* Prepare for receiving LMC835N data. */ + nsc_mixer->im_data[LMC835_DATA] = 0x0000; + nsc_mixer->im_state |= STATE_LMC835_DATA; + } + } else { + if ((pas16->audio_mixer & SERIAL_MIXER_IDENT)) { + /* + Prepare for receiving the LMC1982CIN address. + */ + nsc_mixer->im_data[LMC1982_ADDR] = 0x0000; + nsc_mixer->im_state |= STATE_LMC1982_ADDR; + } + if ((val & SERIAL_MIXER_CLOCK) && !(pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + /* + Clock the least siginificant bit of the LMC1982CIN address. + */ + nsc_mixer->im_data[LMC1982_ADDR] |= + ((val & SERIAL_MIXER_DATA) << (nsc_mixer->im_state & STATE_DATA_MASK)); + nsc_mixer->im_state++; + } + } + break; + case STATE_LMC1982_ADDR_0: + if (val & SERIAL_MIXER_IDENT) { + /* + IDENT went high in LM1982CIN address state 0, + behave as if we were in the idle state. + */ + if (!(val & SERIAL_MIXER_CLOCK) && (pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + nsc_mixer->im_data[LMC835_DATA] = 0x0000; + nsc_mixer->im_state = STATE_LMC835_DATA_0; + } + } else if ((val & SERIAL_MIXER_CLOCK) && + !(pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + /* + Clock the least siginificant bit of the LMC1982CIN address. + */ + nsc_mixer->im_data[LMC1982_ADDR] |= + ((val & SERIAL_MIXER_DATA) << (nsc_mixer->im_state & STATE_DATA_MASK)); + nsc_mixer->im_state++; + } + break; + case STATE_LMC1982_ADDR_1 ... STATE_LMC1982_ADDR_7: + if ((val & 0x02) && !(pas16->audio_mixer & 0x02)) { + /* + Clock the next bit of the LMC1982CIN address. + */ + nsc_mixer->im_data[LMC1982_ADDR] |= + ((val & SERIAL_MIXER_DATA) << (nsc_mixer->im_state & STATE_DATA_MASK)); + nsc_mixer->im_state++; + } + break; + case STATE_LMC1982_ADDR_OVER: + /* + Prepare for receiving the LMC1982CIN data. + */ + nsc_mixer->im_data[LMC1982_DATA] = 0x0000; + nsc_mixer->im_state = STATE_LMC1982_DATA_0; + break; + case STATE_LMC1982_DATA_0 ... STATE_LMC1982_DATA_7: + case STATE_LMC1982_DATA_9 ... STATE_LMC1982_DATA_F: + if ((val & SERIAL_MIXER_CLOCK) && !(pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + /* + Clock the next bit of the LMC1982CIN data. + */ + nsc_mixer->im_data[LMC1982_DATA] |= + ((val & SERIAL_MIXER_DATA) << (nsc_mixer->im_state & STATE_DATA_MASK_W)); + nsc_mixer->im_state++; + } + break; + case STATE_LMC1982_DATA_8: + if (val & SERIAL_MIXER_IDENT) { + if (!(pas16->audio_mixer & SERIAL_MIXER_IDENT)) + /* + LMC1982CIN data transfer ended after 8 bits, process the data and + reset the state back to idle. + */ + lmc1982_update_reg(nsc_mixer); + else if ((val & SERIAL_MIXER_CLOCK) && + !(pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + /* + Clock the next bit of the LMC1982CIN data. + */ + nsc_mixer->im_data[LMC1982_DATA] |= + ((val & SERIAL_MIXER_DATA) << (nsc_mixer->im_state & STATE_DATA_MASK_W)); + nsc_mixer->im_state++; + } + } + break; + case STATE_LMC1982_DATA_OVER: + if ((val & SERIAL_MIXER_IDENT) && !(pas16->audio_mixer & SERIAL_MIXER_IDENT)) + /* + LMC1982CIN data transfer ended, process the data and reset the state + back to idle. + */ + lmc1982_update_reg(nsc_mixer); + break; + case STATE_LMC835_DATA_0 ... STATE_LMC835_DATA_7: + if ((val & SERIAL_MIXER_CLOCK) && !(pas16->audio_mixer & SERIAL_MIXER_CLOCK)) { + /* + Clock the next bit of the LMC835N data. + */ + nsc_mixer->im_data[LMC835_DATA] |= + ((val & SERIAL_MIXER_DATA) << (nsc_mixer->im_state & STATE_DATA_MASK)); + nsc_mixer->im_state++; + } + break; + case STATE_LMC835_DATA_OVER: + if ((val & SERIAL_MIXER_STROBE) && !(pas16->audio_mixer & SERIAL_MIXER_STROBE)) { + if (nsc_mixer->im_data[LMC835_DATA] & LMC835_FLAG_ADDR) + /* + The LMC835N data is an address, copy it into its own space for usage + when processing it at the end and strip bits 7 (it's the address/data + indicator) and 6 (it's a "don't care" bit). + */ + nsc_mixer->im_data[LMC835_ADDR] = nsc_mixer->im_data[LMC835_DATA] & 0x7f; + else + lmc835_update_reg(nsc_mixer); + nsc_mixer->im_state = STATE_SM_IDLE; + } + break; + } + + pas16->audio_mixer = val; + } break; case 0x0801: pas16->irq_stat &= ~val; - if (!(pas16->irq_stat & 0x1f)) + if ((pas16->irq != -1) && !(pas16->irq_stat & 0x1f)) picintc(1 << pas16->irq); break; case 0x0802: @@ -593,22 +1415,22 @@ pas16_out(uint16_t port, uint8_t val, void *priv) pas16->filter = 0; break; case 0x01: - recalc_pas16_filter(0, 17897); + recalc_pas16_filter(17897); break; case 0x02: - recalc_pas16_filter(0, 15909); + recalc_pas16_filter(15909); break; case 0x04: - recalc_pas16_filter(0, 2982); + recalc_pas16_filter(2982); break; case 0x09: - recalc_pas16_filter(0, 11931); + recalc_pas16_filter(11931); break; case 0x11: - recalc_pas16_filter(0, 8948); + recalc_pas16_filter(8948); break; case 0x19: - recalc_pas16_filter(0, 5965); + recalc_pas16_filter(5965); break; } } else @@ -618,13 +1440,11 @@ pas16_out(uint16_t port, uint8_t val, void *priv) pas16->irq_ena = val & 0x1f; pas16->irq_stat &= ((val & 0x1f) | 0xe0); - if (!(pas16->irq_stat & 0x1f)) + if ((pas16->irq != -1) && !(pas16->irq_stat & 0x1f)) picintc(1 << pas16->irq); break; case 0x0c00: - pas16_update(pas16); - break; case 0x0c01: pas16_update(pas16); break; @@ -647,9 +1467,7 @@ pas16_out(uint16_t port, uint8_t val, void *priv) if ((val & 0x60) == 0x60) { pas16->midi_uart_out = 0; pas16->midi_uart_in = 0; - } else if (val & 0x18) - pas16->midi_uart_out = 1; - else if (val & 0x04) + } else if ((val & 0x1c) == 0x04) pas16->midi_uart_in = 1; else pas16->midi_uart_out = 1; @@ -672,6 +1490,45 @@ pas16_out(uint16_t port, uint8_t val, void *priv) pas16->fifo_stat = val; break; + case 0x1c00 ... 0x1c03: /* NCR5380 ports 0 to 3. */ + case 0x3c00 ... 0x3c03: /* NCR5380 ports 4 to 7. */ + if (pas16->has_scsi) { + port = (port & 0x0003) | ((port & 0x2000) >> 11); + ncr5380_write(port, val, &pas16->scsi->ncr); + } + break; + + case 0x4000: + if (pas16->has_scsi) { + pas16->timeout_count = val; + if (timer_is_enabled(&pas16->scsi_timer)) + timer_disable(&pas16->scsi_timer); + timer_set_delay_u64(&pas16->scsi_timer, (val & 0x3f) * PASSCSICONST); + } + break; + + case 0x4001: + if (pas16->has_scsi) { + pas16->timeout_status = val & 0x7f; + if (pas16->scsi_irq != -1) + picintc(1 << pas16->scsi_irq); + } + break; + + case 0x5c00: + if (pas16->has_scsi) + ncr53c400_simple_write(val, pas16->scsi); + break; + case 0x5c03: + if (pas16->has_scsi) { + if (val & 0x80) { + pas16->scsi->ncr.irq_state = 0; + if (pas16->scsi_irq != -1) + picintc(1 << pas16->scsi_irq); + } + } + break; + case 0x7c01: pas16->enhancedscsi = val; break; @@ -701,6 +1558,8 @@ pas16_out(uint16_t port, uint8_t val, void *priv) break; case 0x8003: pas16->sys_conf_4 = val; + if (pas16->has_scsi && (pas16->scsi_irq != -1) && !(val & 0x20)) + picintc(1 << pas16->scsi_irq); break; case 0xbc00: @@ -723,13 +1582,15 @@ pas16_out(uint16_t port, uint8_t val, void *priv) break; case 0xf002: pas16->io_conf_3 = val; - pas16->irq = val & 0x0f; - if (pas16->irq <= 6) - pas16->irq++; - else if ((pas16->irq > 6) && (pas16->irq < 0x0b)) - pas16->irq += 3; - else - pas16->irq += 4; + if (pas16->irq != -1) + picintc(1 << pas16->irq); + pas16->irq = pas16_irq_convert(val & 0x0f); + if (pas16->has_scsi) { + if (pas16->scsi_irq != -1) + picintc(1 << pas16->scsi_irq); + pas16->scsi_irq = pas16_irq_convert(val >> 4); + ncr5380_set_irq(&pas16->scsi->ncr, pas16->scsi_irq); + } pas16_log("pas16_out : set PAS IRQ %i, val=%02x\n", pas16->irq, val & 0x0f); break; @@ -822,19 +1683,17 @@ pas16_dma_channel_read(pas16_t *pas16, int channel) } static uint16_t -pas16_dma_readb(pas16_t *pas16, uint8_t timer1_ticks) +pas16_dma_readb(pas16_t *pas16) { - uint16_t ret; + const uint16_t ret = pas16_dma_channel_read(pas16, pas16->dma); - ret = pas16_dma_channel_read(pas16, pas16->dma); - - pas16->ticks += timer1_ticks; + pas16->ticks++; return ret; } static uint16_t -pas16_dma_readw(pas16_t *pas16, uint8_t timer1_ticks) +pas16_dma_readw(pas16_t *pas16, const uint8_t timer1_ticks) { uint16_t ret; @@ -853,29 +1712,23 @@ pas16_dma_readw(pas16_t *pas16, uint8_t timer1_ticks) static uint16_t pas16_readdmab(pas16_t *pas16) { - uint16_t ret; - if (pas16->dma >= 5) { if (pas16->dma8_ff) pas16->dma8_dat >>= 8; else - pas16->dma8_dat = pas16_dma_readb(pas16, 1); + pas16->dma8_dat = pas16_dma_readb(pas16); pas16->dma8_ff = !pas16->dma8_ff; } else - pas16->dma8_dat = pas16_dma_readb(pas16, 1); + pas16->dma8_dat = pas16_dma_readb(pas16); - ret = ((pas16->dma8_dat & 0xff) ^ 0x80) << 8; - - return ret; + return ((pas16->dma8_dat & 0xff) ^ 0x80) << 8; } static uint16_t pas16_readdmaw_mono(pas16_t *pas16) { - uint16_t ret; - - ret = pas16_dma_readw(pas16, 1 + (pas16->dma < 5)); + const uint16_t ret = pas16_dma_readw(pas16, 1 + (pas16->dma < 5)); return ret; } @@ -930,11 +1783,10 @@ pas16_readdma_stereo(pas16_t *pas16) } static void -pas16_pit_timer0(int new_out, UNUSED(int old_out), void *priv) +pas16_pit_timer0(const int new_out, UNUSED(int old_out), void *priv) { - pitf_t *pit = (pitf_t *) priv; - pas16_t *pas16 = (pas16_t *) pit->dev_priv; - uint16_t temp; + const pitf_t *pit = (const pitf_t *) priv; + pas16_t * pas16 = (pas16_t *) pit->dev_priv; if (!pas16->pit->counters[0].gate) return; @@ -945,6 +1797,8 @@ pas16_pit_timer0(int new_out, UNUSED(int old_out), void *priv) pas16_update_irq(pas16); if (((pas16->pcm_ctrl & PAS16_PCM_ENA) == PAS16_PCM_ENA) && (pit->counters[1].m & 2) && new_out) { + uint16_t temp; + pas16->ticks = 0; if (pas16->pcm_ctrl & PAS16_PCM_MONO) { @@ -981,7 +1835,8 @@ pas16_pit_timer0(int new_out, UNUSED(int old_out), void *priv) pas16->irq_stat |= PAS16_INT_SAMP; if (pas16->irq_ena & PAS16_INT_SAMP) { pas16_log("INT SAMP.\n"); - picint(1 << pas16->irq); + if (pas16->irq != -1) + picint(1 << pas16->irq); } pas16_update(pas16); @@ -989,10 +1844,10 @@ pas16_pit_timer0(int new_out, UNUSED(int old_out), void *priv) } static void -pas16_pit_timer1(int new_out, UNUSED(int old_out), void *priv) +pas16_pit_timer1(const int new_out, UNUSED(int old_out), void *priv) { - pitf_t *pit = (pitf_t * )priv; - pas16_t *pas16 = (pas16_t *) pit->dev_priv; + const pitf_t *pit = (pitf_t * )priv; + pas16_t * pas16 = (pas16_t *) pit->dev_priv; if (!pas16->pit->counters[1].gate) return; @@ -1002,7 +1857,8 @@ pas16_pit_timer1(int new_out, UNUSED(int old_out), void *priv) if (pas16->irq_ena & PAS16_INT_PCM) { pas16->irq_stat |= PAS16_INT_PCM; pas16_log("pas16_pcm_poll : cause IRQ %i %02X\n", pas16->irq, 1 << pas16->irq); - picint(1 << pas16->irq); + if (pas16->irq != -1) + picint(1 << pas16->irq); } } } @@ -1054,7 +1910,7 @@ pas16_input_sysex(void *priv, uint8_t *buffer, uint32_t len, int abort) pas16->sysex = 1; for (uint32_t i = 0; i < len; i++) { if (pas16->midi_r == pas16->midi_w) - return (len - i); + return (int) (len - i); pas16->midi_queue[pas16->midi_w++] = buffer[i]; pas16->midi_w &= 0xff; } @@ -1072,38 +1928,258 @@ pas16_update(pas16_t *pas16) } } else { for (; pas16->pos < sound_pos_global; pas16->pos++) { - pas16->pcm_buffer[0][pas16->pos] = 0; - pas16->pcm_buffer[1][pas16->pos] = 0; -#ifdef CROSS_CHANNEL - if (pas16->pcm_ctrl & 0x08) - pas16->pcm_buffer[0][pas16->pos] += (int16_t) pas16->pcm_dat_l; - if (pas16->pcm_ctrl & 0x04) - pas16->pcm_buffer[0][pas16->pos] += (int16_t) pas16->pcm_dat_r; - if (pas16->pcm_ctrl & 0x02) - pas16->pcm_buffer[1][pas16->pos] += (int16_t) pas16->pcm_dat_l; - if (pas16->pcm_ctrl & 0x01) - pas16->pcm_buffer[1][pas16->pos] += (int16_t) pas16->pcm_dat_r; -#else - pas16->pcm_buffer[0][pas16->pos] += (int16_t) pas16->pcm_dat_l; - pas16->pcm_buffer[1][pas16->pos] += (int16_t) pas16->pcm_dat_r; -#endif + pas16->pcm_buffer[0][pas16->pos] = (int16_t) pas16->pcm_dat_l; + pas16->pcm_buffer[1][pas16->pos] = (int16_t) pas16->pcm_dat_r; } } } void -pas16_get_buffer(int32_t *buffer, int len, void *priv) +pasplus_get_buffer(int32_t *buffer, int len, void *priv) { - pas16_t *pas16 = (pas16_t *) priv; + pas16_t * pas16 = (pas16_t *) priv; + const nsc_mixer_t *mixer = &pas16->nsc_mixer; + double out_l = 0.0; + double out_r = 0.0; + double bass_treble; sb_dsp_update(&pas16->dsp); pas16_update(pas16); - for (int c = 0; c < len * 2; c++) { - buffer[c] += (int32_t) (sb_iir(0, c & 1, (double) pas16->dsp.buffer[c]) / 1.3) / 2; - if (pas16->filter) - buffer[c] += (low_fir_pas16(0, c & 1, (double) pas16->pcm_buffer[c & 1][c >> 1]) / 1.3) / 2.0; + for (int c = 0; c < len * 2; c += 2) { + out_l = 0.0; + out_r = 0.0; + + out_l += pas16->dsp.buffer[c]; + out_r += pas16->dsp.buffer[c + 1]; + + if (pas16->filter) { + /* We divide by 3 to get the volume down to normal. */ + out_l += low_fir_pas16(0, (double) pas16->pcm_buffer[0][c >> 1]) * mixer->pcm_l; + out_r += low_fir_pas16(1, (double) pas16->pcm_buffer[1][c >> 1]) * mixer->pcm_r; + } else { + out_l += ((double) pas16->pcm_buffer[0][c >> 1]) * mixer->pcm_l; + out_r += ((double) pas16->pcm_buffer[1][c >> 1]) * mixer->pcm_r; + } + + out_l *= mixer->master_l; + out_r *= mixer->master_r; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (mixer->bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->bass]; + + if (mixer->bass > 6) { + out_l += (low_iir(0, 0, out_l) * bass_treble); + out_r += (low_iir(0, 1, out_r) * bass_treble); + } else if (mixer->bass < 6) { + out_l = (out_l *bass_treble + low_cut_iir(0, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + low_cut_iir(0, 1, out_r) * (1.0 - bass_treble)); + } + } + + if (mixer->treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->treble]; + + if (mixer->treble > 6) { + out_l += (high_iir(0, 0, out_l) * bass_treble); + out_r += (high_iir(0, 1, out_r) * bass_treble); + } else if (mixer->treble < 6) { + out_l = (out_l *bass_treble + high_cut_iir(0, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + high_cut_iir(0, 1, out_r) * (1.0 - bass_treble)); + } + } + + buffer[c] += (int32_t) out_l; + buffer[c + 1] += (int32_t) out_r; + } + + pas16->pos = 0; + pas16->dsp.pos = 0; +} + +void +pasplus_get_music_buffer(int32_t *buffer, int len, void *priv) +{ + const pas16_t * pas16 = (const pas16_t *) priv; + const nsc_mixer_t *mixer = &pas16->nsc_mixer; + const int32_t * opl_buf = pas16->opl.update(pas16->opl.priv); + double out_l = 0.0; + double out_r = 0.0; + double bass_treble; + + 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; + + /* TODO: recording CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ + out_l *= mixer->master_l; + out_r *= mixer->master_r; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (mixer->bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->bass]; + + if (mixer->bass > 6) { + out_l += (low_iir(1, 0, out_l) * bass_treble); + out_r += (low_iir(1, 1, out_r) * bass_treble); + } else if (mixer->bass < 6) { + out_l = (out_l *bass_treble + low_cut_iir(1, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + low_cut_iir(1, 1, out_r) * (1.0 - bass_treble)); + } + } + + if (mixer->treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->treble]; + + if (mixer->treble > 6) { + out_l += (high_iir(1, 0, out_l) * bass_treble); + out_r += (high_iir(1, 1, out_r) * bass_treble); + } else if (mixer->treble < 6) { + out_l = (out_l *bass_treble + high_cut_iir(1, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + high_cut_iir(1, 1, out_r) * (1.0 - bass_treble)); + } + } + + buffer[c] += (int32_t) out_l; + buffer[c + 1] += (int32_t) out_r; + } + + pas16->opl.reset_buffer(pas16->opl.priv); +} + +void +pasplus_filter_cd_audio(int channel, double *buffer, void *priv) +{ + const pas16_t * pas16 = (const pas16_t *) priv; + const nsc_mixer_t *mixer = &pas16->nsc_mixer; + const double cd = channel ? mixer->cd_r : mixer->cd_l; + const double master = channel ? mixer->master_r : mixer->master_l; + const int32_t bass = mixer->bass; + const int32_t treble = mixer->treble; + double c = (*buffer) * cd * master; + double bass_treble; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[bass]; + + if (bass > 6) + c += (low_iir(2, channel, c) * bass_treble); else - buffer[c] += ((pas16->pcm_buffer[c & 1][c >> 1] / 1.3) / 2); + c = (c * bass_treble + low_cut_iir(2, channel, c) * (1.0 - bass_treble)); + } + + if (treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[treble]; + + if (treble > 6) + c += (high_iir(2, channel, c) * bass_treble); + else + c = (c * bass_treble + high_cut_iir(2, channel, c) * (1.0 - bass_treble)); + } + + *buffer = c; +} + +void +pasplus_filter_pc_speaker(int channel, double *buffer, void *priv) +{ + const pas16_t * pas16 = (pas16_t *) priv; + const nsc_mixer_t *mixer = &pas16->nsc_mixer; + const double spk = channel ? mixer->speaker_r : mixer->speaker_l; + const double master = channel ? mixer->master_r : mixer->master_l; + const int32_t bass = mixer->bass; + const int32_t treble = mixer->treble; + double c = (*buffer) * spk * master; + double bass_treble; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[bass]; + + if (bass > 6) + c += (low_iir(3, channel, c) * bass_treble); + else + c = (c * bass_treble + low_cut_iir(3, channel, c) * (1.0 - bass_treble)); + } + + if (treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[treble]; + + if (treble > 6) + c += (high_iir(3, channel, c) * bass_treble); + else + c = (c * bass_treble + high_cut_iir(3, channel, c) * (1.0 - bass_treble)); + } + + *buffer = c; +} + +void +pas16_get_buffer(int32_t *buffer, int len, void *priv) +{ + pas16_t * pas16 = (pas16_t *) priv; + const mv508_mixer_t *mixer = &pas16->mv508_mixer; + double out_l = 0.0; + double out_r = 0.0; + double bass_treble; + + sb_dsp_update(&pas16->dsp); + pas16_update(pas16); + for (int c = 0; c < len * 2; c += 2) { + out_l = 0.0; + out_r = 0.0; + + out_l += (pas16->dsp.buffer[c] * mixer->sb_l) / 3.0; + out_r += (pas16->dsp.buffer[c + 1] * mixer->sb_r) / 3.0; + + if (pas16->filter) { + /* We divide by 3 to get the volume down to normal. */ + out_l += (low_fir_pas16(0, (double) pas16->pcm_buffer[0][c >> 1]) * mixer->pcm_l) / 3.0; + out_r += (low_fir_pas16(1, (double) pas16->pcm_buffer[1][c >> 1]) * mixer->pcm_r) / 3.0; + } else { + out_l += (((double) pas16->pcm_buffer[0][c >> 1]) * mixer->pcm_l) / 3.0; + out_r += (((double) pas16->pcm_buffer[1][c >> 1]) * mixer->pcm_r) / 3.0; + } + + out_l *= mixer->master_l; + out_r *= mixer->master_r; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (mixer->bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->bass]; + + if (mixer->bass > 6) { + out_l += (low_iir(0, 0, out_l) * bass_treble); + out_r += (low_iir(0, 1, out_r) * bass_treble); + } else if (mixer->bass < 6) { + out_l = (out_l *bass_treble + low_cut_iir(0, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + low_cut_iir(0, 1, out_r) * (1.0 - bass_treble)); + } + } + + if (mixer->treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->treble]; + + if (mixer->treble > 6) { + out_l += (high_iir(0, 0, out_l) * bass_treble); + out_r += (high_iir(0, 1, out_r) * bass_treble); + } else if (mixer->treble < 6) { + out_l = (out_l *bass_treble + high_cut_iir(0, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + high_cut_iir(0, 1, out_r) * (1.0 - bass_treble)); + } + } + + buffer[c] += (int32_t) out_l; + buffer[c + 1] += (int32_t) out_r; } pas16->pos = 0; @@ -1113,15 +2189,127 @@ pas16_get_buffer(int32_t *buffer, int len, void *priv) void pas16_get_music_buffer(int32_t *buffer, int len, void *priv) { - pas16_t *pas16 = (pas16_t *) priv; + const pas16_t * pas16 = (const pas16_t *) priv; + const mv508_mixer_t *mixer = &pas16->mv508_mixer; + const int32_t * opl_buf = pas16->opl.update(pas16->opl.priv); + double out_l = 0.0; + double out_r = 0.0; + double bass_treble; - const int32_t *opl_buf = pas16->opl.update(pas16->opl.priv); - for (int c = 0; c < len * 2; c++) - buffer[c] += opl_buf[c]; + 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; + + /* TODO: recording CD, Mic with AGC or line in. Note: mic volume does not affect recording. */ + out_l *= mixer->master_l; + out_r *= mixer->master_r; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (mixer->bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->bass]; + + if (mixer->bass > 6) { + out_l += (low_iir(1, 0, out_l) * bass_treble); + out_r += (low_iir(1, 1, out_r) * bass_treble); + } else if (mixer->bass < 6) { + out_l = (out_l *bass_treble + low_cut_iir(1, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + low_cut_iir(1, 1, out_r) * (1.0 - bass_treble)); + } + } + + if (mixer->treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[mixer->treble]; + + if (mixer->treble > 6) { + out_l += (high_iir(1, 0, out_l) * bass_treble); + out_r += (high_iir(1, 1, out_r) * bass_treble); + } else if (mixer->treble < 6) { + out_l = (out_l *bass_treble + high_cut_iir(1, 0, out_l) * (1.0 - bass_treble)); + out_r = (out_r *bass_treble + high_cut_iir(1, 1, out_r) * (1.0 - bass_treble)); + } + } + + buffer[c] += (int32_t) out_l; + buffer[c + 1] += (int32_t) out_r; + } pas16->opl.reset_buffer(pas16->opl.priv); } +void +pas16_filter_cd_audio(int channel, double *buffer, void *priv) +{ + const pas16_t * pas16 = (const pas16_t *) priv; + const mv508_mixer_t *mixer = &pas16->mv508_mixer; + const double cd = channel ? mixer->cd_r : mixer->cd_l; + const double master = channel ? mixer->master_r : mixer->master_l; + const int32_t bass = mixer->bass; + const int32_t treble = mixer->treble; + double c = (((*buffer) * cd) / 3.0) * master; + double bass_treble; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[bass]; + + if (bass > 6) + c += (low_iir(2, channel, c) * bass_treble); + else + c = (c * bass_treble + low_cut_iir(2, channel, c) * (1.0 - bass_treble)); + } + + if (treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[treble]; + + if (treble > 6) + c += (high_iir(2, channel, c) * bass_treble); + else + c = (c * bass_treble + high_cut_iir(2, channel, c) * (1.0 - bass_treble)); + } + + *buffer = c; +} + +void +pas16_filter_pc_speaker(int channel, double *buffer, void *priv) +{ + const pas16_t * pas16 = (const pas16_t *) priv; + const mv508_mixer_t *mixer = &pas16->mv508_mixer; + const double spk = channel ? mixer->speaker_r : mixer->speaker_l; + const double master = channel ? mixer->master_r : mixer->master_l; + const int32_t bass = mixer->bass; + const int32_t treble = mixer->treble; + double c = (((*buffer) * spk) / 3.0) * master; + double bass_treble; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (bass != 6) { + bass_treble = lmc1982_bass_treble_4bits[bass]; + + if (bass > 6) + c += (low_iir(3, channel, c) * bass_treble); + else + c = (c * bass_treble + low_cut_iir(3, channel, c) * (1.0 - bass_treble)); + } + + if (treble != 6) { + bass_treble = lmc1982_bass_treble_4bits[treble]; + + if (treble > 6) + c += (high_iir(3, channel, c) * bass_treble); + else + c = (c * bass_treble + high_cut_iir(3, channel, c) * (1.0 - bass_treble)); + } + + *buffer = c; +} + static void pas16_speed_changed(void *priv) { @@ -1141,12 +2329,13 @@ pas16_init(const device_t *info) } pas16->type = info->local & 0xff; + pas16->has_scsi = (!pas16->type) || (pas16->type & 0x0f); fm_driver_get(FM_YMF262, &pas16->opl); sb_dsp_set_real_opl(&pas16->dsp, 1); sb_dsp_init(&pas16->dsp, SB2, SB_SUBTYPE_DEFAULT, pas16); pas16->mpu = (mpu_t *) malloc(sizeof(mpu_t)); memset(pas16->mpu, 0, sizeof(mpu_t)); - mpu401_init(pas16->mpu, 0, 0, M_INTELLIGENT, device_get_config_int("receive_input401")); + mpu401_init(pas16->mpu, 0, 0, M_UART, device_get_config_int("receive_input401")); sb_dsp_set_mpu(&pas16->dsp, pas16->mpu); pas16->sb_compat_base = 0x0000; @@ -1154,10 +2343,22 @@ pas16_init(const device_t *info) io_sethandler(0x9a01, 0x0001, NULL, NULL, NULL, pas16_out_base, NULL, NULL, pas16); pas16->this_id = 0xbc + pas16_next; + if (pas16->has_scsi) { + pas16->scsi = device_add(&scsi_pas_device); + timer_add(&pas16->scsi_timer, pas16_timeout_callback, pas16, 0); + other_scsi_present++; + } + pas16->pit = device_add(&i8254_ext_io_fast_device); pas16_reset(pas16); pas16->pit->dev_priv = pas16; pas16->irq = pas16->type ? 10 : 5; + pas16->io_conf_3 = pas16->type ? 0x07 : 0x04; + if (pas16->has_scsi) { + pas16->scsi_irq = pas16->type ? 11 : 7; + pas16->io_conf_3 |= (pas16->type ? 0x80 : 0x60); + ncr5380_set_irq(&pas16->scsi->ncr, pas16->scsi_irq); + } pas16->dma = 3; for (uint8_t i = 0; i < 3; i++) pitf_ctr_set_gate(pas16->pit, i, 0); @@ -1168,12 +2369,34 @@ pas16_init(const device_t *info) pitf_ctr_set_using_timer(pas16->pit, 1, 0); pitf_ctr_set_using_timer(pas16->pit, 2, 0); - sound_add_handler(pas16_get_buffer, pas16); - music_add_handler(pas16_get_music_buffer, pas16); + if (pas16->type) { + sound_add_handler(pas16_get_buffer, pas16); + music_add_handler(pas16_get_music_buffer, pas16); + sound_set_cd_audio_filter(pas16_filter_cd_audio, pas16); + if (device_get_config_int("control_pc_speaker")) + sound_set_pc_speaker_filter(pas16_filter_pc_speaker, pas16); + } else { + sound_add_handler(pasplus_get_buffer, pas16); + music_add_handler(pasplus_get_music_buffer, pas16); + sound_set_cd_audio_filter(pasplus_filter_cd_audio, pas16); + if (device_get_config_int("control_pc_speaker")) + sound_set_pc_speaker_filter(pasplus_filter_pc_speaker, pas16); + } if (device_get_config_int("receive_input")) midi_in_handler(1, pas16_input_msg, pas16_input_sysex, pas16); + for (uint8_t i = 0; i < 16; i++) { + if (i < 6) + lmc1982_bass_treble_4bits[i] = pow(10.0, (-((double) (12 - (i << 1))) / 10.0)); + else if (i == 6) + lmc1982_bass_treble_4bits[i] = 0.0; + else if ((i > 6) && (i <= 12)) + lmc1982_bass_treble_4bits[i] = 1.0 - pow(10.0, ((double) ((i - 6) << 1) / 10.0)); + else + lmc1982_bass_treble_4bits[i] = 1.0 - pow(10.0, 1.2); + } + pas16_next++; return pas16; @@ -1190,6 +2413,13 @@ pas16_close(void *priv) } static const device_config_t pas16_config[] = { + { + .name = "control_pc_speaker", + .description = "Control PC speaker", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 0 + }, { .name = "receive_input401", .description = "Receive input (MPU-401)", @@ -1221,12 +2451,25 @@ const device_t pasplus_device = { .config = pas16_config }; - const device_t pas16_device = { .name = "Pro Audio Spectrum 16", .internal_name = "pas16", .flags = DEVICE_ISA | DEVICE_AT, - .local = 1, + .local = 0x0f, + .init = pas16_init, + .close = pas16_close, + .reset = pas16_reset, + { .available = NULL }, + .speed_changed = pas16_speed_changed, + .force_redraw = NULL, + .config = pas16_config +}; + +const device_t pas16d_device = { + .name = "Pro Audio Spectrum 16", + .internal_name = "pas16", + .flags = DEVICE_ISA | DEVICE_AT, + .local = 0x0c, .init = pas16_init, .close = pas16_close, .reset = pas16_reset, diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 13baa3621..9dc7724a4 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -43,6 +43,7 @@ #include <86box/snd_sb.h> #include <86box/plat_unused.h> +#define PNP_ROM_SB_16_PNP "roms/sound/creative/CTL0024A.BIN" #define PNP_ROM_SB_VIBRA16XV "roms/sound/creative/CT4170 PnP.BIN" #define PNP_ROM_SB_VIBRA16C "roms/sound/creative/CT4180 PnP.BIN" #define PNP_ROM_SB_32_PNP "roms/sound/creative/CT3600 PnP.BIN" @@ -50,6 +51,10 @@ #define PNP_ROM_SB_AWE64_VALUE "roms/sound/creative/CT4520 PnP.BIN" #define PNP_ROM_SB_AWE64 "roms/sound/creative/CTL009DA.BIN" #define PNP_ROM_SB_AWE64_GOLD "roms/sound/creative/CT4540 PnP.BIN" +/* TODO: Find real ESS PnP ROM dumps. */ +#define PNP_ROM_ESS0100 "roms/sound/ess/ESS0100.BIN" +#define PNP_ROM_ESS0102 "roms/sound/ess/ESS0102.BIN" +#define PNP_ROM_ESS0968 "roms/sound/ess/ESS0968.BIN" /* 0 to 7 -> -14dB to 0dB i 2dB steps. 8 to 15 -> 0 to +14dB in 2dB steps. Note that for positive dB values, this is not amplitude, it is amplitude - 1. */ @@ -99,92 +104,6 @@ static const double sb_att_3dbstep_3bits[] = { static const uint16_t sb_mcv_addr[8] = { 0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270 }; static const int sb_pro_mcv_irqs[4] = { 7, 5, 3, 3 }; -/* Each card in the SB16 family has a million variants, and it shows in the large variety of device IDs for the PnP models. - This ROM was reconstructed in a best-effort basis around a pnpdump output log found in a forum. */ -static uint8_t sb_16_pnp_rom[] = { - // clang-format off - 0x0e, 0x8c, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, /* CTL0024, dummy checksum (filled in by isapnp_add_card) */ - 0x0a, 0x10, 0x10, /* PnP version 1.0, vendor version 1.0 */ - 0x82, 0x11, 0x00, 'C', 'r', 'e', 'a', 't', 'i', 'v', 'e', ' ', 'S', 'B', '1', '6', ' ', 'P', 'n', 'P', /* ANSI identifier */ - - 0x16, 0x0e, 0x8c, 0x00, 0x31, 0x00, 0x65, /* logical device CTL0031, supports vendor-specific registers 0x39/0x3A/0x3D/0x3F */ - 0x82, 0x05, 0x00, 'A', 'u', 'd', 'i', 'o', /* ANSI identifier */ - 0x31, 0x00, /* start dependent functions, preferred */ - 0x22, 0x20, 0x00, /* IRQ 5 */ - 0x2a, 0x02, 0x08, /* DMA 1, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x2a, 0x20, 0x12, /* DMA 5, compatibility, count by word, no count by byte, not bus master, 16-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x20, 0x02, 0x01, 0x10, /* I/O 0x220, decodes 16-bit, 1-byte alignment, 16 addresses */ - 0x47, 0x01, 0x30, 0x03, 0x30, 0x03, 0x01, 0x02, /* I/O 0x330, decodes 16-bit, 1-byte alignment, 2 addresses */ - 0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */ - 0x31, 0x01, /* start dependent functions, acceptable */ - 0x22, 0xa0, 0x04, /* IRQ 5/7/10 */ - 0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x2a, 0xe0, 0x12, /* DMA 5/6/7, compatibility, count by word, no count by byte, not bus master, 16-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */ - 0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */ - 0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */ - 0x31, 0x01, /* start dependent functions, acceptable */ - 0x22, 0xa0, 0x04, /* IRQ 5/7/10 */ - 0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x2a, 0xe0, 0x12, /* DMA 5/6/7, compatibility, count by word, no count by byte, not bus master, 16-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */ - 0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */ - 0x31, 0x02, /* start dependent functions, functional */ - 0x22, 0xa0, 0x04, /* IRQ 5/7/10 */ - 0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x2a, 0xe0, 0x12, /* DMA 5/6/7, compatibility, count by word, no count by byte, not bus master, 16-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */ - 0x31, 0x02, /* start dependent functions, functional */ - 0x22, 0xa0, 0x04, /* IRQ 5/7/10 */ - 0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */ - 0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */ - 0x47, 0x01, 0x88, 0x03, 0x88, 0x03, 0x01, 0x04, /* I/O 0x388, decodes 16-bit, 1-byte alignment, 4 addresses */ - 0x31, 0x02, /* start dependent functions, functional */ - 0x22, 0xa0, 0x04, /* IRQ 5/7/10 */ - 0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */ - 0x47, 0x01, 0x00, 0x03, 0x30, 0x03, 0x30, 0x02, /* I/O 0x300-0x330, decodes 16-bit, 48-byte alignment, 2 addresses */ - 0x31, 0x02, /* start dependent functions, functional */ - 0x22, 0xa0, 0x04, /* IRQ 5/7/10 */ - 0x2a, 0x0b, 0x08, /* DMA 0/1/3, compatibility, no count by word, count by byte, not bus master, 8-bit only */ - 0x47, 0x01, 0x20, 0x02, 0x80, 0x02, 0x20, 0x10, /* I/O 0x220-0x280, decodes 16-bit, 32-byte alignment, 16 addresses */ - 0x38, /* end dependent functions */ - - 0x16, 0x0e, 0x8c, 0x20, 0x11, 0x00, 0x5a, /* logical device CTL2011, supports vendor-specific registers 0x39/0x3B/0x3C/0x3E */ - 0x1c, 0x41, 0xd0, 0x06, 0x00, /* compatible device PNP0600 */ - 0x82, 0x03, 0x00, 'I', 'D', 'E', /* ANSI identifier */ - 0x31, 0x00, /* start dependent functions, preferred */ - 0x22, 0x00, 0x04, /* IRQ 10 */ - 0x47, 0x01, 0x68, 0x01, 0x68, 0x01, 0x01, 0x08, /* I/O 0x168, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0x6e, 0x03, 0x6e, 0x03, 0x01, 0x02, /* I/O 0x36E, decodes 16-bit, 1-byte alignment, 2 addresses */ - 0x31, 0x01, /* start dependent functions, acceptable */ - 0x22, 0x00, 0x08, /* IRQ 11 */ - 0x47, 0x01, 0xe8, 0x01, 0xe8, 0x01, 0x01, 0x08, /* I/O 0x1E8, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0xee, 0x03, 0xee, 0x03, 0x01, 0x02, /* I/O 0x3EE, decodes 16-bit, 1-byte alignment, 2 addresses */ - 0x31, 0x01, /* start dependent functions, acceptable */ - 0x22, 0x00, 0x8c, /* IRQ 10/11/15 */ - 0x47, 0x01, 0x00, 0x01, 0xf8, 0x01, 0x08, 0x08, /* I/O 0x100-0x1F8, decodes 16-bit, 8-byte alignment, 8 addresses */ - 0x47, 0x01, 0x00, 0x03, 0xfe, 0x03, 0x02, 0x02, /* I/O 0x300-0x3FE, decodes 16-bit, 2-byte alignment, 2 addresses */ - 0x31, 0x02, /* start dependent functions, functional */ - 0x22, 0x00, 0x80, /* IRQ 15 */ - 0x47, 0x01, 0x70, 0x01, 0x70, 0x01, 0x01, 0x08, /* I/O 0x170, decodes 16-bit, 1-byte alignment, 8 addresses */ - 0x47, 0x01, 0x76, 0x03, 0x76, 0x03, 0x01, 0x02, /* I/O 0x376, decodes 16-bit, 1-byte alignment, 1 addresses */ - 0x38, /* end dependent functions */ - - 0x16, 0x41, 0xd0, 0xff, 0xff, 0x00, 0xda, /* logical device PNPFFFF, supports vendor-specific registers 0x38/0x39/0x3B/0x3C/0x3E */ - 0x82, 0x08, 0x00, 'R', 'e', 's', 'e', 'r', 'v', 'e', 'd', /* ANSI identifier */ - 0x47, 0x01, 0x00, 0x01, 0xf8, 0x03, 0x08, 0x01, /* I/O 0x100-0x3F8, decodes 16-bit, 8-byte alignment, 1 address */ - - 0x15, 0x0e, 0x8c, 0x70, 0x01, 0x00, /* logical device CTL7001 */ - 0x1c, 0x41, 0xd0, 0xb0, 0x2f, /* compatible device PNPB02F */ - 0x82, 0x04, 0x00, 'G', 'a', 'm', 'e', /* ANSI identifier */ - 0x47, 0x01, 0x00, 0x02, 0x00, 0x02, 0x01, 0x08, /* I/O 0x200, decodes 16-bit, 1-byte alignment, 8 addresses */ - - 0x79, 0x00 /* end tag, dummy checksum (filled in by isapnp_add_card) */ - // clang-format on -}; - #ifdef ENABLE_SB_LOG int sb_do_log = ENABLE_SB_LOG; @@ -207,11 +126,9 @@ sb_log(const char *fmt, ...) static void sb_get_buffer_sb2(int32_t *buffer, int len, void *priv) { - sb_t *sb = (sb_t *) priv; - const sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; - double out_mono = 0.0; - double out_l = 0.0; - double out_r = 0.0; + sb_t *sb = (sb_t *) priv; + const sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; + double out_mono; sb_dsp_update(&sb->dsp); @@ -219,9 +136,8 @@ sb_get_buffer_sb2(int32_t *buffer, int len, void *priv) cms_update(&sb->cms); for (int c = 0; c < len * 2; c += 2) { - out_mono = 0.0; - out_l = 0.0; - out_r = 0.0; + double out_l = 0.0; + double out_r = 0.0; if (sb->cms_enabled) { out_l += sb->cms.buffer[c]; @@ -261,25 +177,17 @@ sb_get_buffer_sb2(int32_t *buffer, int len, void *priv) static void sb_get_music_buffer_sb2(int32_t *buffer, int len, void *priv) { - sb_t *sb = (sb_t *) priv; - const sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; - double out_mono = 0.0; - double out_l = 0.0; - double out_r = 0.0; - const int32_t *opl_buf = NULL; - - if (!sb->opl_enabled) - return; + const sb_t *sb = (const sb_t *) priv; + const sb_ct1335_mixer_t *mixer = &sb->mixer_sb2; + const int32_t *opl_buf = NULL; opl_buf = sb->opl.update(sb->opl.priv); for (int c = 0; c < len * 2; c += 2) { - out_mono = 0.0; - out_l = 0.0; - out_r = 0.0; + double out_l = 0.0; + double out_r = 0.0; - if (sb->opl_enabled) - out_mono = ((double) opl_buf[c]) * 0.7171630859375; + const double out_mono = ((double) opl_buf[c]) * 0.7171630859375; out_l += out_mono; out_r += out_mono; @@ -318,18 +226,16 @@ sb2_filter_cd_audio(UNUSED(int channel), double *buffer, void *priv) } void -sb_get_buffer_sbpro(int32_t *buffer, int len, void *priv) +sb_get_buffer_sbpro(int32_t *buffer, const int len, void *priv) { sb_t *sb = (sb_t *) priv; const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; - double out_l = 0.0; - double out_r = 0.0; sb_dsp_update(&sb->dsp); for (int c = 0; c < len * 2; c += 2) { - out_l = 0.0; - out_r = 0.0; + double out_l = 0.0; + double out_r = 0.0; /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */ if (mixer->output_filter) { @@ -354,11 +260,11 @@ sb_get_buffer_sbpro(int32_t *buffer, int len, void *priv) void sb_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) { - sb_t *sb = (sb_t *) priv; - const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; - double out_l = 0.0; - double out_r = 0.0; - const int32_t *opl_buf = NULL; + sb_t *sb = (sb_t *) priv; + const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + double out_l = 0.0; + double out_r = 0.0; + const int32_t *opl_buf = NULL; const int32_t *opl2_buf = NULL; if (!sb->opl_enabled) @@ -380,7 +286,8 @@ sb_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) /* Two chips for LEFT and RIGHT channels. Each chip stores data into the LEFT channel only (no sample alternating.) */ out_l = (((double) opl_buf[c]) * mixer->fm_l) * 0.7171630859375; - out_r = (((double) opl2_buf[c]) * mixer->fm_r) * 0.7171630859375; + if (opl2_buf != NULL) + out_r = (((double) opl2_buf[c]) * mixer->fm_r) * 0.7171630859375; } else { out_l = (((double) opl_buf[c]) * mixer->fm_l) * 0.7171630859375; out_r = (((double) opl_buf[c + 1]) * mixer->fm_r) * 0.7171630859375; @@ -404,42 +311,27 @@ sb_get_music_buffer_sbpro(int32_t *buffer, int len, void *priv) void sbpro_filter_cd_audio(int channel, double *buffer, void *priv) { - const sb_t *sb = (sb_t *) priv; - const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; - double c; - double cd = channel ? mixer->cd_r : mixer->cd_l; - double master = channel ? mixer->master_r : mixer->master_l; + const sb_t *sb = (sb_t *) priv; + const sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + const double cd = channel ? mixer->cd_r : mixer->cd_l; + const double master = channel ? mixer->master_r : mixer->master_l; + double c = ((*buffer * cd) / 3.0) * master; - c = (*buffer * cd) / 3.0; - *buffer = c * master; + *buffer = c; } static void sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) { - sb_t *sb = (sb_t *) priv; - const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; - int c_emu8k = 0; - double out_l = 0.0; - double out_r = 0.0; + sb_t *sb = (sb_t *) priv; + const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; double bass_treble; sb_dsp_update(&sb->dsp); - if (sb->dsp.sb_type > SB16) - emu8k_update(&sb->emu8k); - for (int c = 0; c < len * 2; c += 2) { - out_l = 0.0; - out_r = 0.0; - - if (sb->dsp.sb_type > SB16) - c_emu8k = ((((c / 2) * FREQ_44100) / SOUND_FREQ) * 2); - - if (sb->dsp.sb_type > SB16) { - out_l += (((double) sb->emu8k.buffer[c_emu8k]) * mixer->fm_l); - out_r += (((double) sb->emu8k.buffer[c_emu8k + 1]) * mixer->fm_r); - } + double out_l = 0.0; + double out_r = 0.0; if (mixer->output_filter) { /* We divide by 3 to get the volume down to normal. */ @@ -460,7 +352,7 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->bass_l > 8) out_l += (low_iir(0, 0, out_l) * bass_treble); - else if (mixer->bass_l < 8) + else out_l = (out_l *bass_treble + low_cut_iir(0, 0, out_l) * (1.0 - bass_treble)); } @@ -469,7 +361,7 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->bass_r > 8) out_r += (low_iir(0, 1, out_r) * bass_treble); - else if (mixer->bass_r < 8) + else out_r = (out_r *bass_treble + low_cut_iir(0, 1, out_r) * (1.0 - bass_treble)); } @@ -478,7 +370,7 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->treble_l > 8) out_l += (high_iir(0, 0, out_l) * bass_treble); - else if (mixer->treble_l < 8) + else out_l = (out_l *bass_treble + high_cut_iir(0, 0, out_l) * (1.0 - bass_treble)); } @@ -487,7 +379,7 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->treble_r > 8) out_r += (high_iir(0, 1, out_r) * bass_treble); - else if (mixer->treble_r < 8) + else out_r = (out_l *bass_treble + high_cut_iir(0, 1, out_r) * (1.0 - bass_treble)); } @@ -496,31 +388,23 @@ sb_get_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) } sb->dsp.pos = 0; - - if (sb->dsp.sb_type > SB16) - sb->emu8k.pos = 0; } static void -sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) +sb_get_music_buffer_sb16_awe32(int32_t *buffer, const int len, void *priv) { - sb_t *sb = (sb_t *) priv; - const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; - int dsp_rec_pos = sb->dsp.record_pos_write; - int c_record; - int32_t in_l; - int32_t in_r; - double out_l = 0.0; - double out_r = 0.0; + sb_t *sb = (sb_t *) priv; + const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + const int dsp_rec_pos = sb->dsp.record_pos_write; double bass_treble; - const int32_t *opl_buf = NULL; + const int32_t *opl_buf = NULL; if (sb->opl_enabled) opl_buf = sb->opl.update(sb->opl.priv); for (int c = 0; c < len * 2; c += 2) { - out_l = 0.0; - out_r = 0.0; + double out_l = 0.0; + double out_r = 0.0; if (sb->opl_enabled) { out_l = ((double) opl_buf[c]) * mixer->fm_l * 0.7171630859375; @@ -528,10 +412,10 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) } /* TODO: Multi-recording mic with agc/+20db, CD, and line in with channel inversion */ - in_l = (mixer->input_selector_left & INPUT_MIDI_L) ? ((int32_t) out_l) : 0 + (mixer->input_selector_left & INPUT_MIDI_R) ? ((int32_t) out_r) - : 0; - in_r = (mixer->input_selector_right & INPUT_MIDI_L) ? ((int32_t) out_l) : 0 + (mixer->input_selector_right & INPUT_MIDI_R) ? ((int32_t) out_r) - : 0; + int32_t in_l = (mixer->input_selector_left & INPUT_MIDI_L) ? + ((int32_t) out_l) : 0 + (mixer->input_selector_left & INPUT_MIDI_R) ? ((int32_t) out_r) : 0; + int32_t in_r = (mixer->input_selector_right & INPUT_MIDI_L) ? + ((int32_t) out_l) : 0 + (mixer->input_selector_right & INPUT_MIDI_R) ? ((int32_t) out_r) : 0; out_l *= mixer->master_l; out_r *= mixer->master_r; @@ -543,7 +427,7 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->bass_l > 8) out_l += (low_iir(1, 0, out_l) * bass_treble); - else if (mixer->bass_l < 8) + else out_l = (out_l *bass_treble + low_cut_iir(1, 0, out_l) * (1.0 - bass_treble)); } @@ -552,7 +436,7 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->bass_r > 8) out_r += (low_iir(1, 1, out_r) * bass_treble); - else if (mixer->bass_r < 8) + else out_r = (out_r *bass_treble + low_cut_iir(1, 1, out_r) * (1.0 - bass_treble)); } @@ -561,7 +445,7 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->treble_l > 8) out_l += (high_iir(1, 0, out_l) * bass_treble); - else if (mixer->treble_l < 8) + else out_l = (out_l *bass_treble + high_cut_iir(1, 0, out_l) * (1.0 - bass_treble)); } @@ -570,12 +454,13 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) if (mixer->treble_r > 8) out_r += (high_iir(1, 1, out_r) * bass_treble); - else if (mixer->treble_r < 8) + else out_r = (out_l *bass_treble + high_cut_iir(1, 1, out_r) * (1.0 - bass_treble)); } if (sb->dsp.sb_enable_i) { - c_record = dsp_rec_pos + ((c * sb->dsp.sb_freq) / MUSIC_FREQ); + const int c_record = dsp_rec_pos + ((c * sb->dsp.sb_freq) / MUSIC_FREQ); + in_l <<= mixer->input_gain_L; in_r <<= mixer->input_gain_R; @@ -590,8 +475,8 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) else if (in_r > 32767) in_r = 32767; - sb->dsp.record_buffer[c_record & 0xffff] = in_l; - sb->dsp.record_buffer[(c_record + 1) & 0xffff] = in_r; + sb->dsp.record_buffer[c_record & 0xffff] = (int16_t) in_l; + sb->dsp.record_buffer[(c_record + 1) & 0xffff] = (int16_t) in_r; } buffer[c] += (int32_t) (out_l * mixer->output_gain_L); @@ -605,21 +490,82 @@ sb_get_music_buffer_sb16_awe32(int32_t *buffer, int len, void *priv) sb->opl.reset_buffer(sb->opl.priv); } +static void +sb_get_wavetable_buffer_sb16_awe32(int32_t *buffer, const int len, void *priv) +{ + sb_t *sb = (sb_t *) priv; + const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + double bass_treble; + + emu8k_update(&sb->emu8k); + + for (int c = 0; c < len * 2; c += 2) { + double out_l = 0.0; + double out_r = 0.0; + + out_l += (((double) sb->emu8k.buffer[c]) * mixer->fm_l); + out_r += (((double) sb->emu8k.buffer[c + 1]) * mixer->fm_r); + + out_l *= mixer->master_l; + out_r *= mixer->master_r; + + /* This is not exactly how one does bass/treble controls, but the end result is like it. + A better implementation would reduce the CPU usage. */ + if (mixer->bass_l != 8) { + bass_treble = sb_bass_treble_4bits[mixer->bass_l]; + + if (mixer->bass_l > 8) + out_l += (low_iir(4, 0, out_l) * bass_treble); + else + out_l = (out_l *bass_treble + low_cut_iir(4, 0, out_l) * (1.0 - bass_treble)); + } + + if (mixer->bass_r != 8) { + bass_treble = sb_bass_treble_4bits[mixer->bass_r]; + + if (mixer->bass_r > 8) + out_r += (low_iir(4, 1, out_r) * bass_treble); + else + out_r = (out_r *bass_treble + low_cut_iir(4, 1, out_r) * (1.0 - bass_treble)); + } + + if (mixer->treble_l != 8) { + bass_treble = sb_bass_treble_4bits[mixer->treble_l]; + + if (mixer->treble_l > 8) + out_l += (high_iir(4, 0, out_l) * bass_treble); + else + out_l = (out_l *bass_treble + high_cut_iir(4, 0, out_l) * (1.0 - bass_treble)); + } + + if (mixer->treble_r != 8) { + bass_treble = sb_bass_treble_4bits[mixer->treble_r]; + + if (mixer->treble_r > 8) + out_r += (high_iir(4, 1, out_r) * bass_treble); + else + out_r = (out_l *bass_treble + high_cut_iir(4, 1, out_r) * (1.0 - bass_treble)); + } + + buffer[c] += (int32_t) (out_l * mixer->output_gain_L); + buffer[c + 1] += (int32_t) (out_r * mixer->output_gain_R); + } + + sb->emu8k.pos = 0; +} + void sb16_awe32_filter_cd_audio(int channel, double *buffer, void *priv) { - const sb_t *sb = (sb_t *) priv; - const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; - double c; - double cd = channel ? mixer->cd_r : mixer->cd_l /* / 3.0 */; - double master = channel ? mixer->master_r : mixer->master_l; - int32_t bass = channel ? mixer->bass_r : mixer->bass_l; - int32_t treble = channel ? mixer->treble_r : mixer->treble_l; + const sb_t *sb = (sb_t *) priv; + const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + const double cd = channel ? mixer->cd_r : mixer->cd_l /* / 3.0 */; + const double master = channel ? mixer->master_r : mixer->master_l; + const int32_t bass = channel ? mixer->bass_r : mixer->bass_l; + const int32_t treble = channel ? mixer->treble_r : mixer->treble_l; + const double output_gain = (channel ? mixer->output_gain_R : mixer->output_gain_L); double bass_treble; - double output_gain = (channel ? mixer->output_gain_R : mixer->output_gain_L); - - c = ((*buffer) * cd) / 3.0; - c *= master; + double c = (((*buffer) * cd) / 3.0) * master; /* This is not exactly how one does bass/treble controls, but the end result is like it. A better implementation would reduce the CPU usage. */ @@ -628,7 +574,7 @@ sb16_awe32_filter_cd_audio(int channel, double *buffer, void *priv) if (bass > 8) c += (low_iir(2, channel, c) * bass_treble); - else if (bass < 8) + else c = (c * bass_treble + low_cut_iir(2, channel, c) * (1.0 - bass_treble)); } @@ -637,7 +583,7 @@ sb16_awe32_filter_cd_audio(int channel, double *buffer, void *priv) if (treble > 8) c += (high_iir(2, channel, c) * bass_treble); - else if (treble < 8) + else c = (c * bass_treble + high_cut_iir(2, channel, c) * (1.0 - bass_treble)); } @@ -647,15 +593,15 @@ sb16_awe32_filter_cd_audio(int channel, double *buffer, void *priv) void sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv) { - const sb_t *sb = (sb_t *) priv; - const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; - double c; - double spk = mixer->speaker; - double master = channel ? mixer->master_r : mixer->master_l; - int32_t bass = channel ? mixer->bass_r : mixer->bass_l; - int32_t treble = channel ? mixer->treble_r : mixer->treble_l; + const sb_t *sb = (sb_t *) priv; + const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + const double spk = mixer->speaker; + const double master = channel ? mixer->master_r : mixer->master_l; + const int32_t bass = channel ? mixer->bass_r : mixer->bass_l; + const int32_t treble = channel ? mixer->treble_r : mixer->treble_l; + const double output_gain = (channel ? mixer->output_gain_R : mixer->output_gain_L); double bass_treble; - double output_gain = (channel ? mixer->output_gain_R : mixer->output_gain_L); + double c; if (mixer->output_filter) c = (low_fir_sb16(3, channel, *buffer) * spk) / 3.0; @@ -670,7 +616,7 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv) if (bass > 8) c += (low_iir(3, channel, c) * bass_treble); - else if (bass < 8) + else c = (c * bass_treble + low_cut_iir(3, channel, c) * (1.0 - bass_treble)); } @@ -679,7 +625,7 @@ sb16_awe32_filter_pc_speaker(int channel, double *buffer, void *priv) if (treble > 8) c += (high_iir(3, channel, c) * bass_treble); - else if (treble < 8) + else c = (c * bass_treble + high_cut_iir(3, channel, c) * (1.0 - bass_treble)); } @@ -691,14 +637,12 @@ 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; + double out_l = 0.0; + double out_r = 0.0; /* TODO: Implement the stereo switch on the mixer instead of on the dsp? */ if (mixer->output_filter) { @@ -765,6 +709,24 @@ ess_filter_cd_audio(int channel, double *buffer, void *priv) *buffer = c * master; } +void +ess_filter_pc_speaker(int channel, double *buffer, void *priv) +{ + const sb_t *ess = (sb_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_ess; + double c; + double spk = mixer->speaker; + double master = channel ? mixer->master_r : mixer->master_l; + + if (mixer->output_filter) + c = (low_fir_sb16(3, channel, *buffer) * spk) / 3.0; + else + c = ((*buffer) * spk) / 3.0; + c *= master; + + *buffer = c; +} + void sb_ct1335_mixer_write(uint16_t addr, uint8_t val, void *priv) { @@ -839,7 +801,10 @@ void sb_ct1345_mixer_write(uint16_t addr, uint8_t val, void *priv) { sb_t *sb = (sb_t *) priv; - sb_ct1345_mixer_t *mixer = &sb->mixer_sbpro; + sb_ct1345_mixer_t *mixer = (sb == NULL) ? NULL : &sb->mixer_sbpro; + + if (mixer == NULL) + return; if (!(addr & 1)) { mixer->index = val; @@ -977,7 +942,10 @@ void sb_ct1745_mixer_write(uint16_t addr, uint8_t val, void *priv) { sb_t *sb = (sb_t *) priv; - sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; + sb_ct1745_mixer_t *mixer = (sb == NULL) ? NULL : &sb->mixer_sb16; + + if (mixer == NULL) + return; if (!(addr & 1)) mixer->index = val; @@ -1207,7 +1175,6 @@ sb_ct1745_mixer_read(uint16_t addr, void *priv) { const sb_t *sb = (sb_t *) priv; const sb_ct1745_mixer_t *mixer = &sb->mixer_sb16; - uint8_t temp; uint8_t ret = 0xff; if (!(addr & 1)) @@ -1310,6 +1277,8 @@ sb_ct1745_mixer_read(uint16_t addr, void *priv) break; } switch (sb->dsp.sb_16_dmanum) { + default: + break; case 5: ret |= 0x20; break; @@ -1331,8 +1300,8 @@ sb_ct1745_mixer_read(uint16_t addr, void *priv) I haven't seen this making any difference, but I'm keeping it for now. */ /* If QEMU is any indication, then the values are actually 0x20, 0x40, and 0x80. */ /* http://the.earth.li/~tfm/oldpage/sb_mixer.html - 0x10, 0x20, 0x80. */ - temp = ((sb->dsp.sb_irq8) ? 1 : 0) | ((sb->dsp.sb_irq16) ? 2 : 0) | - ((sb->dsp.sb_irq401) ? 4 : 0); + const uint8_t temp = ((sb->dsp.sb_irq8) ? 1 : 0) | ((sb->dsp.sb_irq16) ? 2 : 0) | + ((sb->dsp.sb_irq401) ? 4 : 0); if (sb->dsp.sb_type >= SBAWE32) ret = temp | 0x80; else @@ -1398,15 +1367,22 @@ sb_ct1745_mixer_read(uint16_t addr, void *priv) void sb_ct1745_mixer_reset(sb_t *sb) { - sb_ct1745_mixer_write(4, 0, sb); - sb_ct1745_mixer_write(5, 0, sb); + if (sb != NULL) { + sb_ct1745_mixer_write(4, 0, 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; + ess_mixer_t *mixer = (ess == NULL) ? NULL : &ess->mixer_ess; + + sb_log("[%04X:%08X] [W] %04X = %02X\n", CS, cpu_state.pc, addr, val); + + if (mixer == NULL) + return; if (!(addr & 1)) { mixer->index = val; @@ -1506,20 +1482,12 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) /* 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 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: + case 0x00: case 0x04: case 0x3a: case 0x3c: break; case 0x64: @@ -1529,6 +1497,7 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) case 0x40: { uint16_t mpu401_base_addr = 0x300 | ((mixer->regs[0x40] << 1) & 0x30); + sb_log("mpu401_base_addr = %04X\n", mpu401_base_addr); gameport_remap(ess->gameport, !(mixer->regs[0x40] & 0x2) ? 0x00 : 0x200); if (ess->dsp.sb_subtype != SB_SUBTYPE_ESS_ES1688) { @@ -1545,6 +1514,8 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) } } switch ((mixer->regs[0x40] >> 5) & 0x7) { + default: + break; case 0: mpu401_change_addr(ess->mpu, 0x00); mpu401_setirq(ess->mpu, -1); @@ -1600,31 +1571,24 @@ ess_mixer_write(uint16_t addr, uint8_t val, void *priv) 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; + const sb_t * ess = (sb_t *) priv; + const ess_mixer_t *mixer = &ess->mixer_ess; + uint8_t ret = 0x0a; if (!(addr & 1)) - return mixer->index; - - switch (mixer->index) { + ret = mixer->index; + else 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: @@ -1632,25 +1596,37 @@ ess_mixer_read(uint16_t addr, void *priv) case 0x36: case 0x38: case 0x3e: - return mixer->regs[mixer->index]; + ret = mixer->regs[mixer->index]; + break; + + case 0x04: + case 0x22: + case 0x26: + case 0x28: + case 0x2e: + ret = mixer->regs[mixer->index] | 0x11; + break; 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]; - } + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + ret = mixer->regs[mixer->index]; + else + ret = 0x0a; + break; + + /* Return 0x00 so it has bit 3 clear, so NT 5.x drivers don't misdetect it as ES1788. */ + case 0x64: + ret = 0x00; + break; default: sb_log("ess: Unknown mixer register READ: %02X\t%02X\n", mixer->index, mixer->regs[mixer->index]); break; } - return 0x0a; + sb_log("[%04X:%08X] [R] %04X = %02X\n", CS, cpu_state.pc, addr, ret); + + return ret; } void @@ -1806,12 +1782,9 @@ sb_16_reply_mca_read(int port, void *priv) } static void -sb_16_reply_mca_write(int port, uint8_t val, void *priv) +sb_16_reply_mca_write(const int port, const uint8_t val, void *priv) { uint16_t addr; - uint16_t mpu401_addr; - int low_dma; - int high_dma; sb_t *sb = (sb_t *) priv; if (port < 0x102) @@ -1865,6 +1838,8 @@ sb_16_reply_mca_write(int port, uint8_t val, void *priv) sb->pos_regs[port & 7] = val; if (sb->pos_regs[2] & 1) { + uint16_t mpu401_addr; + switch (sb->pos_regs[2] & 0xc4) { case 4: addr = 0x220; @@ -1883,6 +1858,7 @@ sb_16_reply_mca_write(int port, uint8_t val, void *priv) addr = 0; break; } + switch (sb->pos_regs[2] & 0x18) { case 8: mpu401_addr = 0x330; @@ -1935,8 +1911,8 @@ sb_16_reply_mca_write(int port, uint8_t val, void *priv) break; } - low_dma = sb->pos_regs[3] & 3; - high_dma = (sb->pos_regs[3] >> 4) & 7; + const int low_dma = sb->pos_regs[3] & 3; + int high_dma = (sb->pos_regs[3] >> 4) & 7; if (!high_dma) high_dma = low_dma; @@ -1984,13 +1960,16 @@ sb_vibra16s_onboard_relocate_base(uint16_t new_addr, void *priv) } static void -sb_16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +sb_16_pnp_config_changed(const uint8_t ld, isapnp_device_config_t *config, void *priv) { sb_t *sb = (sb_t *) priv; uint16_t addr = sb->dsp.sb_addr; - uint8_t val; switch (ld) { + default: + case 4: /* StereoEnhance (32) */ + break; + case 0: /* Audio */ io_removehandler(addr, 0x0004, sb->opl.read, NULL, NULL, @@ -2022,6 +2001,8 @@ sb_16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) mpu401_change_addr(sb->mpu, 0); if (config->activate) { + uint8_t val = config->irq[0].irq; + addr = config->io[0].base; if (addr != ISAPNP_IO_DISABLED) { io_sethandler(addr, 0x0004, @@ -2053,7 +2034,6 @@ sb_16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) sb->opl.priv); } - val = config->irq[0].irq; if (val != ISAPNP_IRQ_DISABLED) sb_dsp_setirq(&sb->dsp, val); @@ -2086,17 +2066,11 @@ sb_16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) case 3: /* Game */ gameport_remap(sb->gameport, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0); break; - - case 4: /* StereoEnhance (32) */ - break; - - default: - break; } } static void -sb_vibra16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +sb_vibra16_pnp_config_changed(const uint8_t ld, isapnp_device_config_t *config, void *priv) { sb_t *sb = (sb_t *) priv; @@ -2112,7 +2086,7 @@ sb_vibra16_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void * } static void -sb_awe32_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +sb_awe32_pnp_config_changed(const uint8_t ld, isapnp_device_config_t *config, void *priv) { sb_t *sb = (sb_t *) priv; @@ -2133,7 +2107,7 @@ sb_awe32_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pr } static void -sb_awe64_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +sb_awe64_pnp_config_changed(const uint8_t ld, isapnp_device_config_t *config, void *priv) { sb_t *sb = (sb_t *) priv; @@ -2154,7 +2128,7 @@ sb_awe64_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *pr } static void -sb_awe64_gold_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv) +sb_awe64_gold_pnp_config_changed(const uint8_t ld, isapnp_device_config_t *config, void *priv) { sb_t *sb = (sb_t *) priv; @@ -2173,6 +2147,333 @@ sb_awe64_gold_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, voi } } +static void +ess_x688_pnp_config_changed(UNUSED(const uint8_t ld), isapnp_device_config_t *config, void *priv) +{ + sb_t *ess = (sb_t *) priv; + uint16_t addr = ess->dsp.sb_addr; + uint8_t val; + + switch (ld) { + case 0: /* Audio */ + io_removehandler(addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(addr + 8, 0x0002, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(addr + 4, 0x0002, + ess_mixer_read, NULL, NULL, + ess_mixer_write, NULL, NULL, + ess); + + ess->mixer_ess.ess_id_str[2] = 0x00; + ess->mixer_ess.ess_id_str[3] = 0x00; + + addr = ess->opl_pnp_addr; + if (addr) { + ess->opl_pnp_addr = 0; + io_removehandler(addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + } + + if (ess->pnp == 3) + mpu401_change_addr(ess->mpu, 0); + + sb_dsp_setaddr(&ess->dsp, 0); + sb_dsp_setirq(&ess->dsp, 0); + if (ess->pnp == 3) + mpu401_setirq(ess->mpu, -1); + sb_dsp_setdma8(&ess->dsp, ISAPNP_DMA_DISABLED); + sb_dsp_setdma16_8(&ess->dsp, ISAPNP_DMA_DISABLED); + + if (config->activate) { + addr = config->io[0].base; + if (addr != ISAPNP_IO_DISABLED) { + 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(addr + 4, 0x0002, + ess_mixer_read, NULL, NULL, + ess_mixer_write, NULL, NULL, + ess); + + sb_dsp_setaddr(&ess->dsp, addr); + + ess->mixer_ess.ess_id_str[2] = (addr >> 8) & 0xff; + ess->mixer_ess.ess_id_str[3] = addr & 0xff; + } + + addr = config->io[1].base; + if (addr != ISAPNP_IO_DISABLED) { + ess->opl_pnp_addr = addr; + io_sethandler(addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + } + + if (ess->pnp == 3) { + addr = config->io[2].base; + if (addr != ISAPNP_IO_DISABLED) + mpu401_change_addr(ess->mpu, addr); + } + + val = config->irq[0].irq; + if (val != ISAPNP_IRQ_DISABLED) { + sb_dsp_setirq(&ess->dsp, val); + if (ess->pnp == 3) + mpu401_setirq(ess->mpu, val); + } + + val = config->dma[0].dma; + if (val != ISAPNP_DMA_DISABLED) { + sb_dsp_setdma8(&ess->dsp, val); + sb_dsp_setdma16_8(&ess->dsp, val); + } + } + break; + + case 1: + if (ess->pnp == 3) { /* Game */ + gameport_remap(ess->gameport, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0); + } else { /* MPU-401 */ + mpu401_change_addr(ess->mpu, 0); + mpu401_setirq(ess->mpu, -1); + + if (config->activate) { + addr = config->io[0].base; + if (addr != ISAPNP_IO_DISABLED) + mpu401_change_addr(ess->mpu, addr); + + val = config->irq[0].irq; + if (val != ISAPNP_IRQ_DISABLED) + mpu401_setirq(ess->mpu, val); + } + } + break; + + case 2: + if (ess->pnp == 3) /* IDE */ + ide_pnp_config_changed_1addr(0, config, (void *) 3); + else /* Game */ + gameport_remap(ess->gameport, (config->activate && (config->io[0].base != ISAPNP_IO_DISABLED)) ? config->io[0].base : 0); + break; + + case 3: + if (ess->pnp <= 2) /* IDE */ + ide_pnp_config_changed_1addr(0, config, (void *) 3); + break; + + default: + break; + } +} + +/* This function is common to all the ESS MCA cards. */ +static uint8_t +ess_x688_mca_read(const int port, void *priv) +{ + const sb_t *ess = (sb_t *) priv; + const uint8_t ret = ess->pos_regs[port & 7]; + + sb_log("ess_mca_read: port=%04x ret=%02x\n", port, ret); + + return ret; +} + +static void +ess_soundpiper_mca_write(const int port, const uint8_t val, void *priv) +{ + sb_t *ess = (sb_t *) priv; + + if (port < 0x102) + return; + + sb_log("ess_soundpiper_mca_write: port=%04x val=%02x\n", port, val); + + if (ess->dsp.sb_addr != 0x0000) { + io_removehandler(ess->dsp.sb_addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(ess->dsp.sb_addr + 8, 0x0002, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(0x0388, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(ess->dsp.sb_addr + 4, 0x0002, + ess_mixer_read, NULL, NULL, + ess_mixer_write, NULL, NULL, + ess); + } + + /* DSP I/O handler is activated in sb_dsp_setaddr */ + sb_dsp_setaddr(&ess->dsp, 0); + gameport_remap(ess->gameport, 0); + + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_change_addr(ess->mpu, 0); + + ess->pos_regs[port & 7] = val; + + if (ess->pos_regs[2] & 1) { + switch (ess->pos_regs[2] & 0x0e) { + default: + ess->dsp.sb_addr = 0x0000; + break; + case 0x08: + ess->dsp.sb_addr = 0x0240; + break; + case 0x0c: + ess->dsp.sb_addr = 0x0220; + break; + } + + if (ess->dsp.sb_addr != 0x0000) { + io_sethandler(ess->dsp.sb_addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_sethandler(ess->dsp.sb_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); + io_sethandler(ess->dsp.sb_addr + 4, 0x0002, + ess_mixer_read, NULL, NULL, + ess_mixer_write, NULL, NULL, + ess); + + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_change_addr(ess->mpu, ess->pos_regs[3] & 0x02 ? 0x0330 : 0); + } + + /* DSP I/O handler is activated in sb_dsp_setaddr */ + sb_dsp_setaddr(&ess->dsp, ess->dsp.sb_addr); + gameport_remap(ess->gameport, (ess->pos_regs[3] & 0x01) ? 0x200 : 0); + } + + switch (ess->pos_regs[3] & 0xc0) { + default: + break; + case 0x80: + sb_dsp_setirq(&ess->dsp, 9); + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_setirq(ess->mpu, 9); + break; + case 0xa0: + sb_dsp_setirq(&ess->dsp, 5); + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_setirq(ess->mpu, 5); + break; + case 0xc0: + sb_dsp_setirq(&ess->dsp, 7); + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_setirq(ess->mpu, 7); + break; + case 0xe0: + sb_dsp_setirq(&ess->dsp, 10); + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_setirq(ess->mpu, 10); + break; + } + + if (ess->pos_regs[3] & 0x04) { + sb_dsp_setdma8(&ess->dsp, ess->pos_regs[2] >> 4); + sb_dsp_setdma16_8(&ess->dsp, ess->pos_regs[2] >> 4); + } +} + +static void +ess_chipchat_mca_write(int port, uint8_t val, void *priv) +{ + sb_t *ess = (sb_t *) priv; + + if (port < 0x102) + return; + + sb_log("ess_chipchat_mca_write: port=%04x val=%02x\n", port, val); + + if (ess->dsp.sb_addr != 0x0000) { + io_removehandler(ess->dsp.sb_addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(ess->dsp.sb_addr + 8, 0x0002, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(0x0388, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_removehandler(ess->dsp.sb_addr + 4, 0x0002, + ess_mixer_read, NULL, NULL, + ess_mixer_write, NULL, NULL, + ess); + } + + /* DSP I/O handler is activated in sb_dsp_setaddr */ + sb_dsp_setaddr(&ess->dsp, 0); + gameport_remap(ess->gameport, 0); + + mpu401_change_addr(ess->mpu, 0); + + ess->pos_regs[port & 7] = val; + + if (ess->pos_regs[2] & 1) { + ess->dsp.sb_addr = (ess->pos_regs[2] == 0x51) ? 0x0220 : 0x0000; + + if (ess->dsp.sb_addr != 0x0000) { + io_sethandler(ess->dsp.sb_addr, 0x0004, + ess->opl.read, NULL, NULL, + ess->opl.write, NULL, NULL, + ess->opl.priv); + io_sethandler(ess->dsp.sb_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); + io_sethandler(ess->dsp.sb_addr + 4, 0x0002, + ess_mixer_read, NULL, NULL, + ess_mixer_write, NULL, NULL, + ess); + + if (ess->dsp.sb_subtype == SB_SUBTYPE_ESS_ES1688) + mpu401_change_addr(ess->mpu, (ess->pos_regs[2] == 0x51) ? 0x0330 : 0); + } + + /* DSP I/O handler is activated in sb_dsp_setaddr */ + sb_dsp_setaddr(&ess->dsp, ess->dsp.sb_addr); + gameport_remap(ess->gameport, (ess->pos_regs[2] == 0x51) ? 0x200 : 0); + } + + if (ess->pos_regs[2] == 0x51) { + sb_dsp_setirq(&ess->dsp, 7); + mpu401_setirq(ess->mpu, 7); + + sb_dsp_setdma8(&ess->dsp, 1); + sb_dsp_setdma16_8(&ess->dsp, 1); + } +} + void * sb_1_init(UNUSED(const device_t *info)) { @@ -2180,8 +2481,8 @@ sb_1_init(UNUSED(const device_t *info)) 2x0 to 2x3 -> CMS chip 2x6, 2xA, 2xC, 2xE -> DSP chip 2x8, 2x9, 388 and 389 FM chip */ - sb_t *sb = malloc(sizeof(sb_t)); - uint16_t addr = device_get_config_hex16("base"); + sb_t * sb = malloc(sizeof(sb_t)); + const uint16_t addr = device_get_config_hex16("base"); memset(sb, 0, sizeof(sb_t)); sb->opl_enabled = device_get_config_int("opl"); @@ -2231,8 +2532,8 @@ sb_15_init(UNUSED(const device_t *info)) 2x0 to 2x3 -> CMS chip 2x6, 2xA, 2xC, 2xE -> DSP chip 2x8, 2x9, 388 and 389 FM chip */ - sb_t *sb = malloc(sizeof(sb_t)); - uint16_t addr = device_get_config_hex16("base"); + sb_t * sb = malloc(sizeof(sb_t)); + const uint16_t addr = device_get_config_hex16("base"); memset(sb, 0, sizeof(sb_t)); sb->opl_enabled = device_get_config_int("opl"); @@ -2550,8 +2851,7 @@ sb_pro_mcv_init(UNUSED(const device_t *info)) sb->mixer_enabled = 1; sound_add_handler(sb_get_buffer_sbpro, sb); - if (sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sbpro, sb); + music_add_handler(sb_get_music_buffer_sbpro, sb); sound_set_cd_audio_filter(sbpro_filter_cd_audio, sb); /* I/O handlers activated in sb_pro_mcv_write */ @@ -2593,17 +2893,17 @@ sb_pro_compat_init(UNUSED(const device_t *info)) static void * sb_16_init(UNUSED(const device_t *info)) { - sb_t *sb = malloc(sizeof(sb_t)); - uint16_t addr = device_get_config_hex16("base"); - uint16_t mpu_addr = device_get_config_hex16("base401"); + sb_t *sb = malloc(sizeof(sb_t)); + const uint16_t addr = device_get_config_hex16("base"); + const uint16_t mpu_addr = device_get_config_hex16("base401"); memset(sb, 0x00, sizeof(sb_t)); sb->opl_enabled = device_get_config_int("opl"); if (sb->opl_enabled) - fm_driver_get(info->local, &sb->opl); + fm_driver_get((int) (intptr_t) info->local, &sb->opl); - sb_dsp_set_real_opl(&sb->dsp, (info->local != FM_YMF289B)); + sb_dsp_set_real_opl(&sb->dsp, 1); sb_dsp_init(&sb->dsp, (info->local == FM_YMF289B) ? SBAWE32PNP : SB16, SB_SUBTYPE_DEFAULT, sb); sb_dsp_setaddr(&sb->dsp, addr); sb_dsp_setirq(&sb->dsp, device_get_config_int("irq")); @@ -2633,12 +2933,8 @@ sb_16_init(UNUSED(const device_t *info)) io_sethandler(addr + 4, 0x0002, sb_ct1745_mixer_read, NULL, NULL, sb_ct1745_mixer_write, NULL, NULL, sb); sound_add_handler(sb_get_buffer_sb16_awe32, sb); - if (sb->opl_enabled) { - if (info->local == FM_YMF289B) - sound_add_handler(sb_get_music_buffer_sb16_awe32, sb); - else - music_add_handler(sb_get_music_buffer_sb16_awe32, sb); - } + if (sb->opl_enabled) + music_add_handler(sb_get_music_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (device_get_config_int("control_pc_speaker")) sound_set_pc_speaker_filter(sb16_awe32_filter_pc_speaker, sb); @@ -2679,8 +2975,7 @@ sb_16_reply_mca_init(UNUSED(const device_t *info)) sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); - if (sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + music_add_handler(sb_get_music_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (device_get_config_int("control_pc_speaker")) sound_set_pc_speaker_filter(sb16_awe32_filter_pc_speaker, sb); @@ -2705,6 +3000,12 @@ sb_16_reply_mca_init(UNUSED(const device_t *info)) return sb; } +static int +sb_16_pnp_available(void) +{ + return rom_present(PNP_ROM_SB_16_PNP); +} + static void * sb_16_pnp_init(UNUSED(const device_t *info)) { @@ -2723,8 +3024,7 @@ sb_16_pnp_init(UNUSED(const device_t *info)) sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); - if (sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + music_add_handler(sb_get_music_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (device_get_config_int("control_pc_speaker")) sound_set_pc_speaker_filter(sb16_awe32_filter_pc_speaker, sb); @@ -2740,8 +3040,19 @@ sb_16_pnp_init(UNUSED(const device_t *info)) sb->gameport = gameport_add(&gameport_pnp_device); device_add(&ide_qua_pnp_device); + other_ide_present++; - isapnp_add_card(sb_16_pnp_rom, sizeof(sb_16_pnp_rom), sb_16_pnp_config_changed, NULL, NULL, NULL, sb); + uint8_t *pnp_rom = NULL; + + FILE *fp = rom_fopen(PNP_ROM_SB_16_PNP, "rb"); + if (fp) { + if (fread(sb->pnp_rom, 1, 390, fp) == 390) + pnp_rom = sb->pnp_rom; + fclose(fp); + } + + isapnp_add_card(pnp_rom, 390, sb_16_pnp_config_changed, + NULL, NULL, NULL, sb); sb_dsp_set_real_opl(&sb->dsp, 1); sb_dsp_setaddr(&sb->dsp, 0); @@ -2790,8 +3101,7 @@ sb_vibra16_pnp_init(UNUSED(const device_t *info)) sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); - if (sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + music_add_handler(sb_get_music_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (device_get_config_int("control_pc_speaker")) sound_set_pc_speaker_filter(sb16_awe32_filter_pc_speaker, sb); @@ -2833,7 +3143,7 @@ sb_vibra16_pnp_init(UNUSED(const device_t *info)) switch (info->local) { case 0: case 1: - isapnp_add_card(pnp_rom, sizeof(sb->pnp_rom), sb_vibra16_pnp_config_changed, + isapnp_add_card(pnp_rom, 512, sb_vibra16_pnp_config_changed, NULL, NULL, NULL, sb); break; @@ -2868,14 +3178,14 @@ sb_16_compat_init(const device_t *info) sb_dsp_setdma16_enabled(&sb->dsp, 1); sb_ct1745_mixer_reset(sb); + sb->opl_enabled = 1; sb->mixer_enabled = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); - if (sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + music_add_handler(sb_get_music_buffer_sb16_awe32, sb); sb->mpu = (mpu_t *) malloc(sizeof(mpu_t)); memset(sb->mpu, 0, sizeof(mpu_t)); - mpu401_init(sb->mpu, 0, 0, M_UART, info->local); + mpu401_init(sb->mpu, 0, 0, M_UART, (int) (intptr_t) info->local); sb_dsp_set_mpu(&sb->dsp, sb->mpu); sb->gameport = gameport_add(&gameport_pnp_device); @@ -2968,6 +3278,7 @@ sb_awe32_init(UNUSED(const device_t *info)) sound_add_handler(sb_get_buffer_sb16_awe32, sb); if (sb->opl_enabled) music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + wavetable_add_handler(sb_get_wavetable_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (device_get_config_int("control_pc_speaker")) sound_set_pc_speaker_filter(sb16_awe32_filter_pc_speaker, sb); @@ -3014,8 +3325,8 @@ sb_awe32_pnp_init(const device_t *info) sb->mixer_enabled = 1; sb->mixer_sb16.output_filter = 1; sound_add_handler(sb_get_buffer_sb16_awe32, sb); - if (sb->opl_enabled) - music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + music_add_handler(sb_get_music_buffer_sb16_awe32, sb); + wavetable_add_handler(sb_get_wavetable_buffer_sb16_awe32, sb); sound_set_cd_audio_filter(sb16_awe32_filter_cd_audio, sb); if (device_get_config_int("control_pc_speaker")) sound_set_pc_speaker_filter(sb16_awe32_filter_pc_speaker, sb); @@ -3032,8 +3343,10 @@ sb_awe32_pnp_init(const device_t *info) sb->gameport = gameport_add(&gameport_pnp_device); - if ((info->local != 2) && (info->local != 4)) + if ((info->local != 2) && (info->local != 4)) { device_add(&ide_qua_pnp_device); + other_ide_present++; + } const char *pnp_rom_file = NULL; switch (info->local) { @@ -3112,15 +3425,19 @@ sb_awe32_pnp_init(const device_t *info) } static void * -ess_1688_init(UNUSED(const device_t *info)) +ess_x688_init(UNUSED(const device_t *info)) { sb_t *ess = calloc(sizeof(sb_t), 1); - uint16_t addr = device_get_config_hex16("base"); + const uint16_t addr = device_get_config_hex16("base"); + const uint16_t ide_ctrl = (const uint16_t) device_get_config_int("ide_ctrl"); + const uint16_t ide_base = ide_ctrl & 0x0fff; + const uint16_t ide_side = ide_base + 0x0206; + const uint16_t ide_irq = ide_ctrl >> 12; - fm_driver_get(FM_ESFM, &ess->opl); + fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); sb_dsp_set_real_opl(&ess->dsp, 1); - sb_dsp_init(&ess->dsp, SBPRO2, SB_SUBTYPE_ESS_ES1688, ess); + sb_dsp_init(&ess->dsp, SBPRO2, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, 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")); @@ -3152,26 +3469,196 @@ ess_1688_init(UNUSED(const device_t *info)) 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 (info->local && device_get_config_int("control_pc_speaker")) + sound_set_pc_speaker_filter(ess_filter_pc_speaker, ess); - if (device_get_config_int("receive_input")) { + if (device_get_config_int("receive_input")) midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); + + if (info->local) { + 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, device_get_config_int("receive_input401")); + sb_dsp_set_mpu(&ess->dsp, ess->mpu); } - 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); + if (ide_base > 0x0000) { + device_add(&ide_qua_pnp_device); + ide_set_base(4, ide_base); + ide_set_side(4, ide_side); + ide_set_irq(4, ide_irq); + other_ide_present++; + } + + return ess; +} + +static int +ess_688_pnp_available(void) +{ + return rom_present(PNP_ROM_ESS0100); +} + +static int +ess_1688_pnp_available(void) +{ + return rom_present(PNP_ROM_ESS0102); +} + +static int +ess_1688_968_pnp_available(void) +{ + return rom_present(PNP_ROM_ESS0968); +} + +static void * +ess_x688_pnp_init(UNUSED(const device_t *info)) +{ + sb_t *ess = calloc(sizeof(sb_t), 1); + int len = 512; + + ess->pnp = 1 + (int) info->local; + + fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); + + sb_dsp_set_real_opl(&ess->dsp, 1); + sb_dsp_init(&ess->dsp, SBPRO2, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_setdma16_supported(&ess->dsp, 0); + ess_mixer_reset(ess); + + ess->mixer_enabled = 1; + 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 (info->local && device_get_config_int("control_pc_speaker")) + sound_set_pc_speaker_filter(ess_filter_pc_speaker, ess); + + if (device_get_config_int("receive_input")) + midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); + + /* Not on ES688. */ + ess->mixer_ess.ess_id_str[0] = 0x16; + ess->mixer_ess.ess_id_str[1] = 0x88; + ess->mixer_ess.ess_id_str[2] = 0x00; + ess->mixer_ess.ess_id_str[3] = 0x00; + + 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, device_get_config_int("receive_input401")); + sb_dsp_set_mpu(&ess->dsp, ess->mpu); + + ess->gameport = gameport_add(&gameport_pnp_device); + + device_add(&ide_qua_pnp_device); + other_ide_present++; + + const char *pnp_rom_file = NULL; + switch (info->local) { + case 0: + pnp_rom_file = PNP_ROM_ESS0100; + len = 145; + break; + + case 1: + pnp_rom_file = PNP_ROM_ESS0102; + len = 145; + break; + + case 2: + pnp_rom_file = PNP_ROM_ESS0968; + len = 135; + break; + + default: + break; + } + + uint8_t *pnp_rom = NULL; + if (pnp_rom_file) { + FILE *fp = rom_fopen(pnp_rom_file, "rb"); + if (fp) { + if (fread(ess->pnp_rom, 1, len, fp) == len) + pnp_rom = ess->pnp_rom; + fclose(fp); + } + } + + isapnp_add_card(pnp_rom, len, ess_x688_pnp_config_changed, + NULL, NULL, NULL, ess); + + sb_dsp_setaddr(&ess->dsp, 0); + sb_dsp_setirq(&ess->dsp, 0); + sb_dsp_setdma8(&ess->dsp, ISAPNP_DMA_DISABLED); + sb_dsp_setdma16_8(&ess->dsp, ISAPNP_DMA_DISABLED); + + mpu401_change_addr(ess->mpu, 0); + + ess->gameport_addr = 0; + gameport_remap(ess->gameport, 0); + + ide_remove_handlers(3); + + return ess; +} + +static void * +ess_x688_mca_init(UNUSED(const device_t *info)) +{ + sb_t *ess = calloc(1, sizeof(sb_t)); + + ess->opl_enabled = 1; + fm_driver_get(info->local ? FM_ESFM : FM_YMF262, &ess->opl); + + sb_dsp_set_real_opl(&ess->dsp, 1); + sb_dsp_init(&ess->dsp, SBPRO2, info->local ? SB_SUBTYPE_ESS_ES1688 : SB_SUBTYPE_ESS_ES688, ess); + sb_dsp_setdma16_supported(&ess->dsp, 0); + ess_mixer_reset(ess); + + ess->mixer_enabled = 1; + 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 (info->local && device_get_config_int("control_pc_speaker")) + sound_set_pc_speaker_filter(ess_filter_pc_speaker, ess); + + if (info->local) { + ess->mpu = (mpu_t *) calloc(1, sizeof(mpu_t)); + mpu401_init(ess->mpu, 0, -1, M_UART, device_get_config_int("receive_input401")); + sb_dsp_set_mpu(&ess->dsp, ess->mpu); + } + + ess->gameport = gameport_add(&gameport_device); + + mpu401_change_addr(ess->mpu, 0); + + if (device_get_config_int("receive_input")) + midi_in_handler(1, sb_dsp_input_msg, sb_dsp_input_sysex, &ess->dsp); + + ess->gameport_addr = 0; + gameport_remap(ess->gameport, 0); + + /* I/O handlers activated in sb_pro_mcv_write */ + if (info->local == 2) { + mca_add(ess_x688_mca_read, ess_chipchat_mca_write, sb_mcv_feedb, NULL, ess); + ess->pos_regs[0] = 0x50; + ess->pos_regs[1] = 0x51; + } else { + mca_add(ess_x688_mca_read, ess_soundpiper_mca_write, sb_mcv_feedb, NULL, ess); + ess->pos_regs[0] = 0x30; + ess->pos_regs[1] = 0x51; + } + return ess; } @@ -4444,6 +4931,130 @@ static const device_config_t sb_awe64_gold_config[] = { { .name = "", .description = "", .type = CONFIG_END } }; +static const device_config_t ess_688_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 = "ide_ctrl", + .description = "IDE Controller", + .type = CONFIG_HEX16, + .default_string = "", + .default_int = 0x0000, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { + .description = "Disabled", + .value = 0x0000 + }, + { + .description = "0x170, IRQ 15", + .value = 0xf170 + }, + { + .description = "0x1E8, IRQ 11", + .value = 0xb1e8 + }, + { + .description = "0x168, IRQ 9", + .value = 0x9168 + }, + { + .description = "0x168, IRQ 10", + .value = 0xa168 + }, + { .description = "" } + } + }, + { + .name = "receive_input", + .description = "Receive input (SB MIDI)", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 1 + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + static const device_config_t ess_1688_config[] = { { .name = "base", @@ -4526,18 +5137,95 @@ static const device_config_t ess_1688_config[] = { } }, { - .name = "opl", - .description = "Enable OPL", - .type = CONFIG_BINARY, + .name = "ide_ctrl", + .description = "IDE Controller", + .type = CONFIG_HEX16, .default_string = "", - .default_int = 1 + .default_int = 0x0000, + .file_filter = "", + .spinner = { 0 }, + .selection = { + { + .description = "Disabled", + .value = 0x0000 + }, + { + .description = "0x170, IRQ 15", + .value = 0xf170 + }, + { + .description = "0x1E8, IRQ 11", + .value = 0xb1e8 + }, + { + .description = "0x168, IRQ 9", + .value = 0x9168 + }, + { + .description = "0x168, IRQ 10", + .value = 0xa168 + }, + { .description = "" } + } }, { - .name = "receive_input", - .description = "Receive input (SB MIDI)", - .type = CONFIG_BINARY, + .name = "control_pc_speaker", + .description = "Control PC speaker", + .type = CONFIG_BINARY, .default_string = "", - .default_int = 1 + .default_int = 0 + }, + { + .name = "receive_input", + .description = "Receive input (SB MIDI)", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 1 + }, + { + .name = "receive_input401", + .description = "Receive input (MPU-401)", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 0 + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +static const device_config_t ess_688_pnp_config[] = { + { + .name = "receive_input", + .description = "Receive input (SB MIDI)", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 1 + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +static const device_config_t ess_1688_pnp_config[] = { + { + .name = "control_pc_speaker", + .description = "Control PC speaker", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 0 + }, + { + .name = "receive_input", + .description = "Receive input (SB MIDI)", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 1 + }, + { + .name = "receive_input401", + .description = "Receive input (MPU-401)", + .type = CONFIG_BINARY, + .default_string = "", + .default_int = 0 }, { .name = "", .description = "", .type = CONFIG_END } }; @@ -4761,7 +5449,7 @@ const device_t sb_16_pnp_device = { .init = sb_16_pnp_init, .close = sb_close, .reset = NULL, - { .available = NULL }, + { .available = sb_16_pnp_available }, .speed_changed = sb_speed_changed, .force_redraw = NULL, .config = sb_16_pnp_config @@ -4879,12 +5567,40 @@ const device_t sb_awe64_gold_device = { .config = sb_awe64_gold_config }; +const device_t ess_688_device = { + .name = "ESS AudioDrive ES688", + .internal_name = "ess_es688", + .flags = DEVICE_ISA, + .local = 0, + .init = ess_x688_init, + .close = sb_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_688_config +}; + +const device_t ess_ess0100_pnp_device = { + .name = "ESS AudioDrive ES688 (ESS0100) PnP", + .internal_name = "ess_ess0100_pnp", + .flags = DEVICE_ISA, + .local = 0, + .init = ess_x688_pnp_init, + .close = sb_close, + .reset = NULL, + { .available = ess_688_pnp_available }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_688_pnp_config +}; + const device_t ess_1688_device = { .name = "ESS AudioDrive ES1688", .internal_name = "ess_es1688", .flags = DEVICE_ISA, - .local = 0, - .init = ess_1688_init, + .local = 1, + .init = ess_x688_init, .close = sb_close, .reset = NULL, { .available = NULL }, @@ -4892,3 +5608,75 @@ const device_t ess_1688_device = { .force_redraw = NULL, .config = ess_1688_config }; + +const device_t ess_ess0102_pnp_device = { + .name = "ESS AudioDrive ES1688 (ESS0102) PnP", + .internal_name = "ess_ess0102_pnp", + .flags = DEVICE_ISA, + .local = 1, + .init = ess_x688_pnp_init, + .close = sb_close, + .reset = NULL, + { .available = ess_1688_pnp_available }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_1688_pnp_config +}; + +const device_t ess_ess0968_pnp_device = { + .name = "ESS AudioDrive ES1688 (ESS0968) PnP", + .internal_name = "ess_ess0968_pnp", + .flags = DEVICE_ISA, + .local = 2, + .init = ess_x688_pnp_init, + .close = sb_close, + .reset = NULL, + { .available = ess_1688_968_pnp_available }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_1688_pnp_config +}; + +const device_t ess_soundpiper_16_mca_device = { + .name = "SoundPiper 16 (ESS AudioDrive ES688) MCA", + .internal_name = "soundpiper_16_mca", + .flags = DEVICE_MCA, + .local = 0, + .init = ess_x688_mca_init, + .close = sb_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_688_pnp_config +}; + +const device_t ess_soundpiper_32_mca_device = { + .name = "SoundPiper 32 (ESS AudioDrive ES1688) MCA", + .internal_name = "soundpiper_32_mca", + .flags = DEVICE_MCA, + .local = 1, + .init = ess_x688_mca_init, + .close = sb_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_1688_pnp_config +}; + +const device_t ess_chipchat_16_mca_device = { + .name = "ChipChat 16 (ESS AudioDrive ES1688) MCA", + .internal_name = "chipchat_16_mca", + .flags = DEVICE_MCA, + .local = 2, + .init = ess_x688_mca_init, + .close = sb_close, + .reset = NULL, + { .available = NULL }, + .speed_changed = sb_speed_changed, + .force_redraw = NULL, + .config = ess_1688_pnp_config +}; + + diff --git a/src/sound/snd_sb_dsp.c b/src/sound/snd_sb_dsp.c index 927a06d8c..9c148339d 100644 --- a/src/sound/snd_sb_dsp.c +++ b/src/sound/snd_sb_dsp.c @@ -37,38 +37,44 @@ #define ESPCM_3 5 /* ESPCM_2? */ #define ESPCM_1 7 -#define ESPCM_4E 8 // for differentiating between 4-bit encoding and decoding modes +#define ESPCM_4E 8 /* For differentiating between 4-bit encoding and decoding modes. */ -/*The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb*/ +/* The recording safety margin is intended for uneven "len" calls to the get_buffer mixer calls on sound_sb. */ #define SB_DSP_REC_SAFEFTY_MARGIN 4096 +enum { + DSP_S_NORMAL = 0, + DSP_S_RESET, + DSP_S_RESET_WAIT +}; + void pollsb(void *priv); void sb_poll_i(void *priv); static int sbe2dat[4][9] = { - {0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106}, - { -0x01, 0x02, -0x04, 0x08, 0x10, -0x20, 0x40, -0x80, 165 }, - { -0x01, 0x02, 0x04, -0x08, 0x10, -0x20, -0x40, 0x80, -151}, - { 0x01, -0x02, 0x04, -0x08, -0x10, 0x20, -0x40, 0x80, 90 } + { 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 }, + { -0x01, 0x02, -0x04, 0x08, 0x10, -0x20, 0x40, -0x80, 165 }, + { -0x01, 0x02, 0x04, -0x08, 0x10, -0x20, -0x40, 0x80, -151 }, + { 0x01, -0x02, 0x04, -0x08, -0x10, 0x20, -0x40, 0x80, 90 } }; static int sb_commands[256] = { - -1, 2, -1, 0, 1, 2, -1, 0, 1, -1, -1, -1, -1, -1, 2, 1, - 1, -1, -1, -1, 2, -1, 2, 2, -1, -1, -1, -1, 0, -1, -1, 0, - 0, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, + -1, 2, -1, 0, 1, 2, -1, 0, 1, -1, -1, -1, -1, -1, 2, 1, + 1, -1, -1, -1, 2, -1, 2, 2, -1, -1, -1, -1, 0, -1, -1, 0, + 0, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 1, 2, 2, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, + 1, 2, 2, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 2, 2, 2, 2, -1, -1, -1, -1, -1, 0, -1, 0, - 2, 2, -1, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, - 0, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 2, 2, 2, 2, -1, -1, -1, -1, -1, 0, -1, 0, + 2, 2, -1, -1, -1, -1, -1, -1, 2, 2, -1, -1, -1, -1, -1, -1, + 0, -1, -1, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1, -1, -1, -1, - 1, 0, 1, 0, 1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 0, 0, -1, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, 0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, -1, -1, -1, -1, -1, + 1, 0, 1, 0, 1, -1, -1, 0, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 0, 0, -1, -1, -1, -1, -1, 1, 2, -1, -1, -1, -1, 0 }; char sb16_copyright[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; @@ -76,33 +82,33 @@ uint16_t sb_dsp_versions[] = { 0, 0, 0x105, 0x200, 0x201, 0x300, 0x302, 0x405, 0 /*These tables were 'borrowed' from DOSBox*/ int8_t scaleMap4[64] = { - 0, 1, 2, 3, 4, 5, 6, 7, 0, -1, -2, -3, -4, -5, -6, -7, - 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, - 2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30, + 0, 1, 2, 3, 4, 5, 6, 7, 0, -1, -2, -3, -4, -5, -6, -7, + 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15, + 2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30, 4, 12, 20, 28, 36, 44, 52, 60, -4, -12, -20, -28, -36, -44, -52, -60 }; uint8_t adjustMap4[64] = { - 0, 0, 0, 0, 0, 16, 16, 16, - 0, 0, 0, 0, 0, 16, 16, 16, + 0, 0, 0, 0, 0, 16, 16, 16, + 0, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, 240, 0, 0, 0, 0, 16, 16, 16, - 240, 0, 0, 0, 0, 0, 0, 0, - 240, 0, 0, 0, 0, 0, 0, 0 + 240, 0, 0, 0, 0, 0, 0, 0, + 240, 0, 0, 0, 0, 0, 0, 0 }; int8_t scaleMap26[40] = { - 0, 1, 2, 3, 0, -1, -2, -3, - 1, 3, 5, 7, -1, -3, -5, -7, - 2, 6, 10, 14, -2, -6, -10, -14, + 0, 1, 2, 3, 0, -1, -2, -3, + 1, 3, 5, 7, -1, -3, -5, -7, + 2, 6, 10, 14, -2, -6, -10, -14, 4, 12, 20, 28, -4, -12, -20, -28, 5, 15, 25, 35, -5, -15, -25, -35 }; uint8_t adjustMap26[40] = { - 0, 0, 0, 8, 0, 0, 0, 8, + 0, 0, 0, 8, 0, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, 248, 0, 0, 8, @@ -110,15 +116,17 @@ uint8_t adjustMap26[40] = { }; int8_t scaleMap2[24] = { - 0, 1, 0, -1, 1, 3, -1, -3, - 2, 6, -2, -6, 4, 12, -4, -12, + 0, 1, 0, -1, 1, 3, -1, -3, + 2, 6, -2, -6, 4, 12, -4, -12, 8, 24, -8, -24, 6, 48, -16, -48 }; uint8_t adjustMap2[24] = { - 0, 4, 0, 4, - 252, 4, 252, 4, 252, 4, 252, 4, - 252, 4, 252, 4, 252, 4, 252, 4, + 0, 4, 0, 4, + 252, 4, 252, 4, + 252, 4, 252, 4, + 252, 4, 252, 4, + 252, 4, 252, 4, 252, 0, 252, 0 }; @@ -240,7 +248,7 @@ uint16_t espcm3_dpcm_tables[1024] = }; // clang-format on -double low_fir_sb16_coef[4][SB16_NCoef]; +double low_fir_sb16_coef[5][SB16_NCoef]; #ifdef ENABLE_SB_DSP_LOG int sb_dsp_do_log = ENABLE_SB_DSP_LOG; @@ -269,20 +277,18 @@ sinc(double x) } static void -recalc_sb16_filter(int c, int playback_freq) +recalc_sb16_filter(const int c, const int playback_freq) { /* Cutoff frequency = playback / 2 */ - int n; - double w; - double h; - double fC = ((double) playback_freq) / (double) FREQ_96000; - double gain; + int n; + const double fC = ((double) playback_freq) / (double) FREQ_96000; for (n = 0; n < SB16_NCoef; n++) { /* Blackman window */ - w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); + const double w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); /* Sinc filter */ - h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); + const double h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); /* Create windowed-sinc filter */ low_fir_sb16_coef[c][n] = w * h; @@ -290,7 +296,7 @@ recalc_sb16_filter(int c, int playback_freq) low_fir_sb16_coef[c][(SB16_NCoef - 1) / 2] = 1.0; - gain = 0.0; + double gain = 0.0; for (n = 0; n < SB16_NCoef; n++) gain += low_fir_sb16_coef[c][n]; @@ -300,38 +306,36 @@ recalc_sb16_filter(int c, int playback_freq) } static void -recalc_opl_filter(int c, int playback_freq) +recalc_opl_filter(const int playback_freq) { /* Cutoff frequency = playback / 2 */ - int n; - double w; - double h; - double fC = ((double) playback_freq) / (double) (FREQ_49716 * 2); - double gain; + int n; + const double fC = ((double) playback_freq) / (double) (FREQ_49716 * 2); for (n = 0; n < SB16_NCoef; n++) { /* Blackman window */ - w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); + const double w = 0.42 - (0.5 * cos((2.0 * n * M_PI) / (double) (SB16_NCoef - 1))) + + (0.08 * cos((4.0 * n * M_PI) / (double) (SB16_NCoef - 1))); /* Sinc filter */ - h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); + const double h = sinc(2.0 * fC * ((double) n - ((double) (SB16_NCoef - 1) / 2.0))); /* Create windowed-sinc filter */ - low_fir_sb16_coef[c][n] = w * h; + low_fir_sb16_coef[1][n] = w * h; } - low_fir_sb16_coef[c][(SB16_NCoef - 1) / 2] = 1.0; + low_fir_sb16_coef[1][(SB16_NCoef - 1) / 2] = 1.0; - gain = 0.0; + double gain = 0.0; for (n = 0; n < SB16_NCoef; n++) - gain += low_fir_sb16_coef[c][n]; + gain += low_fir_sb16_coef[1][n]; /* Normalise filter, to produce unity gain */ for (n = 0; n < SB16_NCoef; n++) - low_fir_sb16_coef[c][n] /= gain; + low_fir_sb16_coef[1][n] /= gain; } static void -sb_irq_update_pic(void *priv, int set) +sb_irq_update_pic(void *priv, const int set) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; if (set) @@ -368,11 +372,9 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) return; /* NOTE: not on ES1688 or ES1868 */ - if (IS_ESS(dsp) && dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688 - && !(ESSreg(0xB1) & 0x10)) // if ESS playback, and IRQ disabled, do not fire - { + if (IS_ESS(dsp) && (dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688) && !(ESSreg(0xB1) & 0x10)) + /* If ESS playback, and IRQ disabled, do not fire. */ return; - } switch (bit) { default: @@ -391,7 +393,7 @@ sb_update_status(sb_dsp_t *dsp, int bit, int set) } /* NOTE: not on ES1688, apparently; investigate on ES1868 */ - if (IS_ESS(dsp) && dsp->sb_subtype != SB_SUBTYPE_ESS_ES1688) { + if (IS_ESS(dsp) && (dsp->sb_subtype > SB_SUBTYPE_ESS_ES1688)) { /* TODO: Investigate real hardware for this (the ES1887 datasheet documents this bit somewhat oddly.) */ if (dsp->ess_playback_mode && bit <= 1 && set && !masked) { if (!(ESSreg(0xB1) & 0x40)) // if ESS playback, and IRQ disabled, do not fire @@ -420,7 +422,7 @@ sb_irqc(sb_dsp_t *dsp, int irq8) } static void -sb_dsp_irq_update(void *priv, int set) +sb_dsp_irq_update(void *priv, const int set) { sb_dsp_t *dsp = (sb_dsp_t *) priv; @@ -509,14 +511,14 @@ void sb_dsp_speed_changed(sb_dsp_t *dsp) { if (dsp->sb_timeo < 256) - dsp->sblatcho = TIMER_USEC * (256 - dsp->sb_timeo); + dsp->sblatcho = (double) (TIMER_USEC * (256 - dsp->sb_timeo)); else - dsp->sblatcho = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_timeo - 256))); + dsp->sblatcho = ((double) TIMER_USEC * (1000000.0 / (double) (dsp->sb_timeo - 256))); if (dsp->sb_timei < 256) - dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_timei); + dsp->sblatchi = (double) (TIMER_USEC * (256 - dsp->sb_timei)); else - dsp->sblatchi = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_timei - 256))); + dsp->sblatchi = ((double) TIMER_USEC * (1000000.0 / (double) (dsp->sb_timei - 256))); } void @@ -527,18 +529,16 @@ sb_add_data(sb_dsp_t *dsp, uint8_t v) } static unsigned int -sb_ess_get_dma_counter(sb_dsp_t *dsp) +sb_ess_get_dma_counter(const sb_dsp_t *dsp) { - unsigned int c; - - c = (unsigned int) ESSreg(0xA5) << 8U; + unsigned int c = (unsigned int) ESSreg(0xA5) << 8U; c |= (unsigned int) ESSreg(0xA4); return c; } static unsigned int -sb_ess_get_dma_len(sb_dsp_t *dsp) +sb_ess_get_dma_len(const sb_dsp_t *dsp) { return 0x10000U - sb_ess_get_dma_counter(dsp); } @@ -554,12 +554,13 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_8_autoinit = autoinit; dsp->sb_8_pause = 0; dsp->sb_8_enable = 1; + dsp->dma_ff = 0; if (dsp->sb_16_enable && dsp->sb_16_output) dsp->sb_16_enable = 0; dsp->sb_8_output = 1; if (!timer_is_enabled(&dsp->output_timer)) - timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); + timer_set_delay_u64(&dsp->output_timer, (uint64_t) dsp->sblatcho); dsp->sbleftright = dsp->sbleftright_default; dsp->sbdacpos = 0; } else { @@ -572,7 +573,7 @@ sb_start_dma(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_8_enable = 0; dsp->sb_16_output = 1; if (!timer_is_enabled(&dsp->output_timer)) - timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); + timer_set_delay_u64(&dsp->output_timer, (uint64_t) dsp->sblatcho); } /* This will be set later for ESS playback/record modes. */ @@ -592,7 +593,7 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_16_enable = 0; dsp->sb_8_output = 0; if (!timer_is_enabled(&dsp->input_timer)) - timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); + timer_set_delay_u64(&dsp->input_timer, (uint64_t) dsp->sblatchi); } else { dsp->sb_16_length = dsp->sb_16_origlength = len; dsp->sb_16_format = format; @@ -603,7 +604,7 @@ sb_start_dma_i(sb_dsp_t *dsp, int dma8, int autoinit, uint8_t format, int len) dsp->sb_8_enable = 0; dsp->sb_16_output = 0; if (!timer_is_enabled(&dsp->input_timer)) - timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); + timer_set_delay_u64(&dsp->input_timer, (uint64_t) dsp->sblatchi); } memset(dsp->record_buffer, 0, sizeof(dsp->record_buffer)); @@ -623,9 +624,9 @@ sb_start_dma_ess(sb_dsp_t *dsp) 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, len); + sb_start_dma_i(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, (int) len); else - sb_start_dma(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, len); + sb_start_dma(dsp, !(ESSreg(0xB7) & 4), (ESSreg(0xB8) >> 2) & 1, real_format, (int) len); dsp->ess_playback_mode = 1; dma_set_drq(dsp->sb_8_dmanum, 1); dma_set_drq(dsp->sb_16_8_dmanum, 1); @@ -644,7 +645,7 @@ 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 the DRQ is disabled, do not start. */ if (!(ESSreg(0xB2) & 0x40)) dma_en = false; @@ -660,9 +661,28 @@ sb_ess_update_dma_status(sb_dsp_t *dsp) int sb_8_read_dma(void *priv) { - const sb_dsp_t *dsp = (sb_dsp_t *) priv; + sb_dsp_t *dsp = (sb_dsp_t *) priv; + int ret; - return dma_channel_read(dsp->sb_8_dmanum); + if (dsp->sb_8_dmanum >= 4) { + if (dsp->dma_ff) { + uint32_t temp = (dsp->dma_data & 0xff00) >> 8; + temp |= (dsp->dma_data & 0xffff0000); + ret = (int) temp; + } else { + dsp->dma_data = dma_channel_read(dsp->sb_8_dmanum); + + if (dsp->dma_data == DMA_NODATA) + return DMA_NODATA; + + ret = dsp->dma_data & 0xff; + } + + dsp->dma_ff = !dsp->dma_ff; + } else + ret = dma_channel_read(dsp->sb_8_dmanum); + + return ret; } int @@ -690,8 +710,8 @@ sb_16_read_dma(void *priv) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; - int temp, ret = 0; - int dma_flags, dma_ch = dsp->sb_16_dmanum; + int ret; + int dma_ch = dsp->sb_16_dmanum; if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate) ret = dma_channel_read(dma_ch); @@ -699,19 +719,18 @@ sb_16_read_dma(void *priv) if (dsp->sb_16_dma_enabled) { /* High DMA channel enabled, either translation is enabled or 16-bit transfers are not supported. */ - if (dsp->sb_16_dma_translate || !dsp->sb_16_dma_supported) - dma_ch = dsp->sb_16_8_dmanum; + dma_ch = dsp->sb_16_8_dmanum; } else /* High DMA channel disabled, always use the first 8-bit channel. */ dma_ch = dsp->sb_8_dmanum; - temp = dma_channel_read(dma_ch); + int temp = dma_channel_read(dma_ch); ret = temp; if ((temp != DMA_NODATA) && !(temp & DMA_OVER)) { temp = dma_channel_read(dma_ch); if (temp == DMA_NODATA) ret = DMA_NODATA; else { - dma_flags = temp & DMA_OVER; + const int dma_flags = temp & DMA_OVER; temp &= ~DMA_OVER; ret |= (temp << 8) | dma_flags; } @@ -725,9 +744,8 @@ int sb_16_write_dma(void *priv, uint16_t val) { const sb_dsp_t *dsp = (sb_dsp_t *) priv; - - int temp, ret = 0; int dma_ch = dsp->sb_16_dmanum; + int ret; if (dsp->sb_16_dma_enabled && dsp->sb_16_dma_supported && !dsp->sb_16_dma_translate) ret = dma_channel_write(dma_ch, val) == DMA_NODATA; @@ -735,12 +753,11 @@ sb_16_write_dma(void *priv, uint16_t val) if (dsp->sb_16_dma_enabled) { /* High DMA channel enabled, either translation is enabled or 16-bit transfers are not supported. */ - if (dsp->sb_16_dma_translate || !dsp->sb_16_dma_supported) - dma_ch = dsp->sb_16_8_dmanum; + dma_ch = dsp->sb_16_8_dmanum; } else /* High DMA channel disabled, always use the first 8-bit channel. */ dma_ch = dsp->sb_8_dmanum; - temp = dma_channel_write(dma_ch, val & 0xff); + int temp = dma_channel_write(dma_ch, val & 0xff); ret = temp; if ((temp != DMA_NODATA) && (temp != DMA_OVER)) { temp = dma_channel_write(dma_ch, val >> 8); @@ -760,6 +777,8 @@ sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy) t |= 0x80; } switch (dsp->sb_irqnum) { + default: + break; case 9: t |= 0x0; break; @@ -781,6 +800,8 @@ sb_ess_update_irq_drq_readback_regs(sb_dsp_t *dsp, bool legacy) t |= 0x80; } switch (dsp->sb_8_dmanum) { + default: + break; case 0: t |= 0x5; break; @@ -843,17 +864,17 @@ sb_dsp_setdma16_supported(sb_dsp_t *dsp, int supported) } void -sb_dsp_setdma16_translate(sb_dsp_t *dsp, int translate) +sb_dsp_setdma16_translate(sb_dsp_t *dsp, const int translate) { sb_dsp_log("16-bit to 8-bit translation now: %sabled\n", translate ? "en" : "dis"); dsp->sb_16_dma_translate = translate; } static void -sb_ess_update_reg_a2(sb_dsp_t *dsp, uint8_t val) +sb_ess_update_reg_a2(sb_dsp_t *dsp, const uint8_t val) { - double freq = (7160000.0 / (256.0 - ((double) val))) * 41.0; - int temp = (int) freq; + const double freq = (7160000.0 / (256.0 - ((double) val))) * 41.0; + const int temp = (int) freq; ESSreg(0xA2) = val; if (dsp->sb_freq != temp) @@ -866,7 +887,7 @@ sb_ess_update_reg_a2(sb_dsp_t *dsp, uint8_t val) static void sb_ess_update_filter_freq(sb_dsp_t *dsp) { - double temp = (7160000.0 / (((((double) dsp->sb_freq) / 2.0) * 0.80) * 82.0)) - 256.0; + const double temp = (7160000.0 / (((((double) dsp->sb_freq) / 2.0) * 0.80) * 82.0)) - 256.0; if (dsp->sb_freq >= 22050) ESSreg(0xA1) = 256 - (795500UL / dsp->sb_freq); @@ -877,36 +898,32 @@ sb_ess_update_filter_freq(sb_dsp_t *dsp) } static uint8_t -sb_ess_read_reg(sb_dsp_t *dsp, uint8_t reg) +sb_ess_read_reg(const sb_dsp_t *dsp, const uint8_t reg) { - switch (reg) { - default: - return ESSreg(reg); - } + return ESSreg(reg); } static void sb_ess_update_autolen(sb_dsp_t *dsp) { - dsp->sb_8_autolen = dsp->sb_16_autolen = sb_ess_get_dma_len(dsp); + dsp->sb_8_autolen = dsp->sb_16_autolen = (int) sb_ess_get_dma_len(dsp); } static void -sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) +sb_ess_write_reg(sb_dsp_t *dsp, const uint8_t reg, uint8_t data) { - uint8_t chg = 0x00; + uint8_t chg; switch (reg) { case 0xA1: /* Extended Mode Sample Rate Generator */ { - double temp; ESSreg(reg) = data; if (data & 0x80) - dsp->sb_freq = 795500UL / (256ul - data); + dsp->sb_freq = (int) (795500UL / (256ul - data)); else - dsp->sb_freq = 397700UL / (128ul - data); - temp = 1000000.0 / dsp->sb_freq; - dsp->sblatchi = dsp->sblatcho = TIMER_USEC * temp; + dsp->sb_freq = (int) (397700UL / (128ul - data)); + const double temp = 1000000.0 / dsp->sb_freq; + dsp->sblatchi = dsp->sblatcho = ((double) TIMER_USEC * temp); dsp->sb_timei = dsp->sb_timeo; break; @@ -953,6 +970,8 @@ sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) case 0xB1: /* Legacy Audio Interrupt Control */ ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable switch (data & 0x0C) { + default: + break; case 0x00: dsp->sb_irqnum = 2; break; @@ -972,6 +991,8 @@ sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) chg = ESSreg(reg) ^ data; ESSreg(reg) = (ESSreg(reg) & 0x0F) + (data & 0xF0); // lower 4 bits not writeable switch (data & 0x0C) { + default: + break; case 0x00: dsp->sb_8_dmanum = -1; break; @@ -1033,9 +1054,9 @@ sb_ess_write_reg(sb_dsp_t *dsp, uint8_t reg, uint8_t data) if (chg & 1) { if (dsp->sb_16_enable || dsp->sb_8_enable) { if (dsp->sb_16_enable) - dsp->sb_16_length = sb_ess_get_dma_len(dsp); + dsp->sb_16_length = (int) sb_ess_get_dma_len(dsp); if (dsp->sb_8_enable) - dsp->sb_8_length = sb_ess_get_dma_len(dsp); + dsp->sb_8_length = (int) sb_ess_get_dma_len(dsp); } else dsp->ess_reload_len = 1; } @@ -1122,8 +1143,9 @@ sb_exec_command(sb_dsp_t *dsp) } /* else DSP Status (Obsolete) */ break; case 0x05: /* ASP set codec parameter */ - if (dsp->sb_type >= SB16) + if (dsp->sb_type >= SB16) { sb_dsp_log("SB16 ASP unknown codec params %02X, %02X\n", dsp->sb_data[0], dsp->sb_data[1]); + } break; case 0x07: break; @@ -1209,7 +1231,7 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0x10: /* 8-bit direct mode */ sb_dsp_update(dsp); - dsp->sbdat = dsp->sbdatl = dsp->sbdatr = (dsp->sb_data[0] ^ 0x80) << 8; + dsp->sbdat = dsp->sbdatl = dsp->sbdatr = (int16_t) ((dsp->sb_data[0] ^ 0x80) << 8); // FIXME: What does the ESS AudioDrive do to its filter/sample rate divider registers when emulating this Sound Blaster command? ESSreg(0xA1) = 128 - (397700 / 22050); ESSreg(0xA2) = 256 - (7160000 / (82 * ((4 * 22050) / 10))); @@ -1249,10 +1271,10 @@ sb_exec_command(sb_dsp_t *dsp) mode does not imply such samplerate. Position is increased in sb_poll_i(). */ if (!timer_is_enabled(&dsp->input_timer)) { dsp->sb_timei = 256 - 22; - dsp->sblatchi = TIMER_USEC * 22; + dsp->sblatchi = (double) ((double) TIMER_USEC * 22.0); temp = 1000000 / 22; dsp->sb_freq = temp; - timer_set_delay_u64(&dsp->input_timer, dsp->sblatchi); + timer_set_delay_u64(&dsp->input_timer, (uint64_t) dsp->sblatchi); } break; case 0x24: /* 8-bit single cycle DMA input */ @@ -1275,7 +1297,6 @@ sb_exec_command(sb_dsp_t *dsp) dsp->uart_irq = 1; break; case 0x32: /* MIDI Read Timestamp Poll */ - break; case 0x33: /* MIDI Read Timestamp Interrupt */ break; case 0x34: /* MIDI In poll */ @@ -1302,7 +1323,7 @@ sb_exec_command(sb_dsp_t *dsp) break; case 0x40: /* Set time constant */ dsp->sb_timei = dsp->sb_timeo = dsp->sb_data[0]; - dsp->sblatcho = dsp->sblatchi = TIMER_USEC * (256 - dsp->sb_data[0]); + dsp->sblatcho = dsp->sblatchi = (double) (TIMER_USEC * (256 - dsp->sb_data[0])); temp = 256 - dsp->sb_data[0]; temp = 1000000 / temp; sb_dsp_log("Sample rate - %ihz (%f)\n", temp, dsp->sblatcho); @@ -1316,21 +1337,20 @@ sb_exec_command(sb_dsp_t *dsp) case 0x41: /* Set output sampling rate */ case 0x42: /* Set input sampling rate */ if (dsp->sb_type >= SB16) { - dsp->sblatcho = (uint64_t) (TIMER_USEC * (1000000.0f / (float) (dsp->sb_data[1] + (dsp->sb_data[0] << 8)))); + dsp->sblatcho = (double) ((double) TIMER_USEC * (1000000.0 / (double) (dsp->sb_data[1] + (dsp->sb_data[0] << 8)))); sb_dsp_log("Sample rate - %ihz (%f)\n", dsp->sb_data[1] + (dsp->sb_data[0] << 8), dsp->sblatcho); temp = dsp->sb_freq; dsp->sb_freq = dsp->sb_data[1] + (dsp->sb_data[0] << 8); - dsp->sb_timeo = 256LL + dsp->sb_freq; + dsp->sb_timeo = 256 + dsp->sb_freq; dsp->sblatchi = dsp->sblatcho; dsp->sb_timei = dsp->sb_timeo; - if ((dsp->sb_freq != temp) && (dsp->sb_type >= SB16)) + if (dsp->sb_freq != temp) recalc_sb16_filter(0, dsp->sb_freq); dsp->sb_8051_ram[0x13] = dsp->sb_freq & 0xff; dsp->sb_8051_ram[0x14] = (dsp->sb_freq >> 8) & 0xff; } break; case 0x45: /* Continue Auto-Initialize DMA, 8-bit */ - break; case 0x47: /* Continue Auto-Initialize DMA, 16-bit */ break; case 0x48: /* Set DSP block transfer size */ @@ -1427,7 +1447,7 @@ sb_exec_command(sb_dsp_t *dsp) case 0x80: /* Pause DAC */ dsp->sb_pausetime = dsp->sb_data[0] + (dsp->sb_data[1] << 8); if (!timer_is_enabled(&dsp->output_timer)) - timer_set_delay_u64(&dsp->output_timer, dsp->sblatcho); + timer_set_delay_u64(&dsp->output_timer, (uint64_t) trunc(dsp->sblatcho)); break; case 0x90: /* High speed 8-bit autoinit DMA output */ if (dsp->sb_type >= SB2) @@ -1511,7 +1531,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_8_pause = 1; break; case 0xD1: /* Speaker on */ - if (!IS_ESS(dsp)) { + if (IS_NOT_ESS(dsp)) { if (dsp->sb_type < SB15) dsp->sb_8_pause = 1; else if (dsp->sb_type < SB16) @@ -1520,7 +1540,7 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_speaker = 1; break; case 0xD3: /* Speaker off */ - if (!IS_ESS(dsp)) { + if (IS_NOT_ESS(dsp)) { if (dsp->sb_type < SB15) dsp->sb_8_pause = 1; else if (dsp->sb_type < SB16) @@ -1578,7 +1598,10 @@ sb_exec_command(sb_dsp_t *dsp) } dsp->sbe2 += sbe2dat[dsp->sbe2count & 3][8]; dsp->sbe2count++; - dsp->dma_writeb(dsp->dma_priv, dsp->sbe2); + if (dsp->dma_writeb(dsp->dma_priv, dsp->sbe2)) + /* Undocumented behavior: If write to DMA fails, the byte is written + to the CPU instead. The NT 3.1 Sound Blaster Pro driver relies on this. */ + sb_add_data(dsp, dsp->sbe2); break; case 0xE3: /* DSP copyright */ if (dsp->sb_type >= SB16) { @@ -1598,6 +1621,10 @@ sb_exec_command(sb_dsp_t *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); @@ -1664,24 +1691,34 @@ sb_exec_command(sb_dsp_t *dsp) dsp->sb_8051_ram[0x30] = dsp->sb_command; } +static void +sb_do_reset(sb_dsp_t *dsp, const uint8_t v) +{ + if (((v & 1) != 0) && (dsp->state != DSP_S_RESET)) { + sb_dsp_reset(dsp); + dsp->sb_read_rp = dsp->sb_read_wp = 0; + dsp->state = DSP_S_RESET; + } else if (((v & 1) == 0) && (dsp->state == DSP_S_RESET)) { + dsp->state = DSP_S_RESET_WAIT; + dsp->sb_read_rp = dsp->sb_read_wp = 0; + sb_add_data(dsp, 0xaa); + } +} + void sb_write(uint16_t a, uint8_t v, void *priv) { sb_dsp_t *dsp = (sb_dsp_t *) priv; + sb_dsp_log("[%04X:%08X] DSP: [W] %04X = %02X\n", CS, cpu_state.pc, a, v); + /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ - if (dsp->sb_type < SB16 && (!IS_ESS(dsp) || (IS_ESS(dsp) && ((a & 0xF) != 0xE)))) + if ((dsp->sb_type < SB16) && (IS_NOT_ESS(dsp) || ((a & 0xF) != 0xE))) a &= 0xfffe; switch (a & 0xF) { case 6: /* Reset */ - if (!dsp->uart_midi) { - if (!(v & 1) && (dsp->sbreset & 1)) { - sb_dsp_reset(dsp); - sb_add_data(dsp, 0xAA); - } - dsp->sbreset = v; - } + sb_do_reset(dsp, v); if (!(v & 2) && (dsp->espcm_fifo_reset & 2)) { fifo_reset(dsp->espcm_fifo); @@ -1759,79 +1796,101 @@ sb_read(uint16_t a, void *priv) uint8_t ret = 0x00; /* Sound Blasters prior to Sound Blaster 16 alias the I/O ports. */ - if (dsp->sb_type < SB16) { + if ((dsp->sb_type < SB16) && (IS_NOT_ESS(dsp) || ((a & 0xF) != 0xF))) /* Exception: ESS AudioDrive does not alias port base+0xf */ - if (!IS_ESS(dsp) || !((a & 0xF) == 0xF)) { a &= 0xfffe; - } - } switch (a & 0xf) { + case 0x6: + ret = 0xff; + break; case 0xA: /* Read data */ - if (dsp->mpu && dsp->uart_midi) { + if (dsp->mpu && dsp->uart_midi) ret = MPU401_ReadData(dsp->mpu); - } else { - dsp->sbreaddat = dsp->sb_read_data[dsp->sb_read_rp]; + else { if (dsp->sb_read_rp != dsp->sb_read_wp) { + dsp->sbreaddat = dsp->sb_read_data[dsp->sb_read_rp]; dsp->sb_read_rp++; dsp->sb_read_rp &= 0xff; } - return dsp->sbreaddat; + ret = dsp->sbreaddat; } + /* Advance the state just in case something reads from here + without reading the status first. */ + if (dsp->state == DSP_S_RESET_WAIT) + dsp->state = DSP_S_NORMAL; break; case 0xC: /* Write data ready */ - if (dsp->sb_8_enable || dsp->sb_type >= SB16) - dsp->busy_count = (dsp->busy_count + 1) & 3; - else - dsp->busy_count = 0; - if (IS_ESS(dsp)) { - if (dsp->wb_full || (dsp->busy_count & 2)) { - dsp->wb_full = timer_is_enabled(&dsp->wb_timer); - } - uint8_t busy_flag = dsp->wb_full ? 0x80 : 0x00; - uint8_t data_rdy = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x40; - uint8_t fifo_full = 0; /* Unimplemented */ - uint8_t fifo_empty = 0; /* (this is for the 256-byte extended mode FIFO, */ - uint8_t fifo_half = 0; /* not the standard 64-byte FIFO) */ - uint8_t irq_generic = dsp->ess_irq_generic ? 0x04 : 0x00; - uint8_t irq_fifohe = 0; /* Unimplemented (ditto) */ - uint8_t irq_dmactr = dsp->ess_irq_dmactr ? 0x01 : 0x00; + if (dsp->state == DSP_S_NORMAL) { + if (dsp->sb_8_enable || dsp->sb_type >= SB16) + dsp->busy_count = (dsp->busy_count + 1) & 3; + else + dsp->busy_count = 0; + if (IS_ESS(dsp)) { + if (dsp->wb_full || (dsp->busy_count & 2)) + dsp->wb_full = timer_is_enabled(&dsp->wb_timer); - return busy_flag | data_rdy | fifo_full | fifo_empty | fifo_half | irq_generic | irq_fifohe | irq_dmactr; - } - if (dsp->wb_full || (dsp->busy_count & 2)) { - dsp->wb_full = timer_is_enabled(&dsp->wb_timer); - if (IS_AZTECH(dsp)) { - sb_dsp_log("SB Write Data Aztech read 0x80\n"); - return 0x80; + const uint8_t busy_flag = dsp->wb_full ? 0x80 : 0x00; + const uint8_t data_rdy = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x40; + const uint8_t fifo_full = 0; /* Unimplemented */ + const uint8_t fifo_empty = 0; /* (this is for the 256-byte extended mode FIFO, */ + const uint8_t fifo_half = 0; /* not the standard 64-byte FIFO) */ + const uint8_t irq_generic = dsp->ess_irq_generic ? 0x04 : 0x00; + const uint8_t irq_fifohe = 0; /* Unimplemented (ditto) */ + const uint8_t irq_dmactr = dsp->ess_irq_dmactr ? 0x01 : 0x00; + + ret = busy_flag | data_rdy | fifo_full | fifo_empty | fifo_half | irq_generic | irq_fifohe | irq_dmactr; + } else if (dsp->wb_full || (dsp->busy_count & 2)) { + dsp->wb_full = timer_is_enabled(&dsp->wb_timer); + if (IS_AZTECH(dsp)) { + sb_dsp_log("SB Write Data Aztech read 0x80\n"); + ret = 0x80; + } else { + sb_dsp_log("SB Write Data Creative read 0xff\n"); + if ((dsp->sb_type >= SB2) && (dsp->sb_type < SB16) && IS_NOT_ESS(dsp)) + ret = 0xaa; + else + ret = 0xff; + } + } else if (IS_AZTECH(dsp)) { + sb_dsp_log("SB Write Data Aztech read 0x00\n"); + ret = 0x00; } else { - sb_dsp_log("SB Write Data Creative read 0xff\n"); - return 0xff; + sb_dsp_log("SB Write Data Creative read 0x7f\n"); + if ((dsp->sb_type >= SB2) && (dsp->sb_type < SB16) && IS_NOT_ESS(dsp)) + ret = 0x2a; + else + ret = 0x7f; } - } - if (IS_AZTECH(dsp)) { - sb_dsp_log("SB Write Data Aztech read 0x00\n"); - ret = 0x00; - } else { - sb_dsp_log("SB Write Data Creative read 0x7f\n"); - ret = 0x7f; - } + } else + ret = 0xff; break; case 0xE: /* Read data ready */ dsp->irq_update(dsp->irq_priv, 0); dsp->sb_irq8 = dsp->sb_irq16 = 0; dsp->ess_irq_generic = dsp->ess_irq_dmactr = false; - /* Only bit 7 is defined but aztech diagnostics fail if the others are set. Keep the original behavior to not interfere with what's already working. */ + /* + Only bit 7 is defined but aztech diagnostics fail if the others are set. + Keep the original behavior to not interfere with what's already working. + */ if (IS_AZTECH(dsp)) { - sb_dsp_log("SB Read Data Aztech read %02X, Read RP = %d, Read WP = %d\n", (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80, dsp->sb_read_rp, dsp->sb_read_wp); + sb_dsp_log("SB Read Data Aztech read %02X, Read RP = %d, Read WP = %d\n", + (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80, dsp->sb_read_rp, dsp->sb_read_wp); ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x00 : 0x80; } else { sb_dsp_log("SB Read Data Creative read %02X\n", (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7f : 0xff); - ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7f : 0xff; + if ((dsp->sb_type < SB16) && IS_NOT_ESS(dsp)) + ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x2a : 0xaa; + else + ret = (dsp->sb_read_rp == dsp->sb_read_wp) ? 0x7f : 0xff; + } + if (dsp->state == DSP_S_RESET_WAIT) { + ret &= 0x7f; + dsp->state = DSP_S_NORMAL; } break; case 0xF: /* 16-bit ack */ - if (!IS_ESS(dsp)) { + if (IS_NOT_ESS(dsp)) { dsp->sb_irq16 = 0; if (!dsp->sb_irq8) dsp->irq_update(dsp->irq_priv, 0); @@ -1844,6 +1903,8 @@ sb_read(uint16_t a, void *priv) break; } + sb_dsp_log("[%04X:%08X] DSP: [R] %04X = %02X\n", CS, cpu_state.pc, a, ret); + return ret; } @@ -1891,7 +1952,7 @@ sb_dsp_input_sysex(void *priv, uint8_t *buffer, uint32_t len, int abort) for (uint32_t i = 0; i < len; i++) { if (dsp->sb_read_rp == dsp->sb_read_wp) { sb_dsp_log("Length sysex SB = %d\n", len - i); - return (len - i); + return (int) (len - i); } sb_add_data(dsp, buffer[i]); @@ -1952,13 +2013,13 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) if (IS_ESS(dsp) || (dsp->sb_type >= SBPRO2)) { /* OPL3 or dual OPL2 is stereo. */ if (dsp->sb_has_real_opl) - recalc_opl_filter(1, FREQ_49716 * 2); + recalc_opl_filter(FREQ_49716 * 2); else recalc_sb16_filter(1, FREQ_48000 * 2); } else { /* OPL2 is mono. */ if (dsp->sb_has_real_opl) - recalc_opl_filter(1, FREQ_49716); + recalc_opl_filter(FREQ_49716); else recalc_sb16_filter(1, FREQ_48000); } @@ -1966,6 +2027,8 @@ sb_dsp_init(sb_dsp_t *dsp, int type, int subtype, void *parent) recalc_sb16_filter(2, FREQ_44100 * 2); /* PC speaker is mono. */ recalc_sb16_filter(3, 18939); + /* E-MU 8000 is stereo. */ + recalc_sb16_filter(4, FREQ_44100 * 2); /* Initialize SB16 8051 RAM and ASP internal RAM */ memset(dsp->sb_8051_ram, 0x00, sizeof(dsp->sb_8051_ram)); @@ -2061,7 +2124,7 @@ pollsb(void *priv) int ref; int data[2]; - timer_advance_u64(&dsp->output_timer, dsp->sblatcho); + timer_advance_u64(&dsp->output_timer, (uint64_t) dsp->sblatcho); if (dsp->sb_8_enable && dsp->sb_pausetime < 0 && dsp->sb_8_output) { sb_dsp_update(dsp); @@ -2073,7 +2136,7 @@ pollsb(void *priv) auto-init DMA but programs the DMA controller to single cycle */ if (data[0] == DMA_NODATA) break; - dsp->sbdat = (data[0] ^ 0x80) << 8; + dsp->sbdat = (int16_t) ((data[0] ^ 0x80) << 8); if (dsp->stereo) { sb_dsp_log("pollsb: Mono unsigned, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); @@ -2093,7 +2156,7 @@ pollsb(void *priv) data[0] = dsp->dma_readb(dsp->dma_priv); if (data[0] == DMA_NODATA) break; - dsp->sbdat = data[0] << 8; + dsp->sbdat = (int16_t) (data[0] << 8); if (dsp->stereo) { sb_dsp_log("pollsb: Mono signed, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", data[0], dsp->sbdat); @@ -2114,8 +2177,8 @@ pollsb(void *priv) data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; - dsp->sbdatl = (data[0] ^ 0x80) << 8; - dsp->sbdatr = (data[1] ^ 0x80) << 8; + dsp->sbdatl = (int16_t) ((data[0] ^ 0x80) << 8); + dsp->sbdatr = (int16_t) ((data[1] ^ 0x80) << 8); dsp->sb_8_length -= 2; dsp->ess_dma_counter += 2; } @@ -2126,8 +2189,8 @@ pollsb(void *priv) data[1] = dsp->dma_readb(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; - dsp->sbdatl = data[0] << 8; - dsp->sbdatr = data[1] << 8; + dsp->sbdatl = (int16_t) (data[0] << 8); + dsp->sbdatr = (int16_t) (data[1] << 8); dsp->sb_8_length -= 2; dsp->ess_dma_counter += 2; } @@ -2152,8 +2215,8 @@ pollsb(void *priv) else dsp->sbref = ref; - dsp->sbstep = (dsp->sbstep + adjustMap4[tempi]) & 0xff; - dsp->sbdat = (dsp->sbref ^ 0x80) << 8; + dsp->sbstep = (int8_t) ((dsp->sbstep + adjustMap4[tempi]) & 0xff); + dsp->sbdat = (int16_t) ((dsp->sbref ^ 0x80) << 8); dsp->sbdacpos++; @@ -2198,9 +2261,9 @@ pollsb(void *priv) dsp->sbref = 0x00; else dsp->sbref = ref; - dsp->sbstep = (dsp->sbstep + adjustMap26[tempi]) & 0xff; + dsp->sbstep = (int8_t) ((dsp->sbstep + adjustMap26[tempi]) & 0xff); - dsp->sbdat = (dsp->sbref ^ 0x80) << 8; + dsp->sbdat = (int16_t) ((dsp->sbref ^ 0x80) << 8); dsp->sbdacpos++; if (dsp->sbdacpos >= 3) { @@ -2238,9 +2301,9 @@ pollsb(void *priv) dsp->sbref = 0x00; else dsp->sbref = ref; - dsp->sbstep = (dsp->sbstep + adjustMap2[tempi]) & 0xff; + dsp->sbstep = (int8_t) ((dsp->sbstep + adjustMap2[tempi]) & 0xff); - dsp->sbdat = (dsp->sbref ^ 0x80) << 8; + dsp->sbdat = (int16_t) ((dsp->sbref ^ 0x80) << 8); dsp->sbdacpos++; if (dsp->sbdacpos >= 4) { @@ -2296,8 +2359,8 @@ pollsb(void *priv) dsp->espcm_sample_idx++; tempi |= (dsp->espcm_range << 4); - data[0] = espcm_range_map[tempi]; - dsp->sbdat = data[0] << 8; + data[0] = (int) espcm_range_map[tempi]; + dsp->sbdat = (int16_t) (data[0] << 8); if (dsp->stereo) { sb_dsp_log("pollsb: ESPCM 4, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); @@ -2392,8 +2455,8 @@ pollsb(void *priv) dsp->espcm_sample_idx++; tempi |= (dsp->espcm_range << 4); - data[0] = espcm_range_map[tempi]; - dsp->sbdat = data[0] << 8; + data[0] = (int) (espcm_range_map[tempi]); + dsp->sbdat = (int16_t) (data[0] << 8); if (dsp->stereo) { sb_dsp_log("pollsb: ESPCM 3, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); @@ -2444,8 +2507,8 @@ pollsb(void *priv) dsp->espcm_sample_idx++; tempi |= (dsp->espcm_range << 4); - data[0] = espcm_range_map[tempi]; - dsp->sbdat = data[0] << 8; + data[0] = (int) espcm_range_map[tempi]; + dsp->sbdat = (int16_t) (data[0] << 8); if (dsp->stereo) { sb_dsp_log("pollsb: ESPCM 1, dsp->stereo, %s channel, %04X\n", dsp->sbleftright ? "left" : "right", dsp->sbdat); @@ -2499,7 +2562,7 @@ pollsb(void *priv) data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; - dsp->sbdatl = dsp->sbdatr = data[0] ^ 0x8000; + dsp->sbdatl = dsp->sbdatr = (int16_t) ((data[0] & 0xffff) ^ 0x8000); dsp->sb_16_length--; dsp->ess_dma_counter += 2; break; @@ -2507,7 +2570,7 @@ pollsb(void *priv) data[0] = dsp->dma_readw(dsp->dma_priv); if (data[0] == DMA_NODATA) break; - dsp->sbdatl = dsp->sbdatr = data[0]; + dsp->sbdatl = dsp->sbdatr = (int16_t) (data[0] & 0xffff); dsp->sb_16_length--; dsp->ess_dma_counter += 2; break; @@ -2516,8 +2579,8 @@ pollsb(void *priv) data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; - dsp->sbdatl = data[0] ^ 0x8000; - dsp->sbdatr = data[1] ^ 0x8000; + dsp->sbdatl = (int16_t) ((data[0] & 0xffff) ^ 0x8000); + dsp->sbdatr = (int16_t) ((data[1] & 0xffff) ^ 0x8000); dsp->sb_16_length -= 2; dsp->ess_dma_counter += 4; break; @@ -2526,8 +2589,8 @@ pollsb(void *priv) data[1] = dsp->dma_readw(dsp->dma_priv); if ((data[0] == DMA_NODATA) || (data[1] == DMA_NODATA)) break; - dsp->sbdatl = data[0]; - dsp->sbdatr = data[1]; + dsp->sbdatl = (int16_t) (data[0] & 0xffff); + dsp->sbdatr = (int16_t) (data[1] & 0xffff); dsp->sb_16_length -= 2; dsp->ess_dma_counter += 4; break; @@ -2583,7 +2646,7 @@ sb_poll_i(void *priv) sb_dsp_t *dsp = (sb_dsp_t *) priv; int processed = 0; - timer_advance_u64(&dsp->input_timer, dsp->sblatchi); + timer_advance_u64(&dsp->input_timer, (uint64_t) dsp->sblatchi); if (dsp->sb_8_enable && !dsp->sb_8_pause && dsp->sb_pausetime < 0 && !dsp->sb_8_output) { switch (dsp->sb_8_format) { @@ -2618,16 +2681,17 @@ sb_poll_i(void *priv) dsp->record_pos_read &= 0xFFFF; break; case ESPCM_4E: - // I assume the real hardware double-buffers the blocks or something like that. - // We're not gonna do that here. - dsp->espcm_sample_buffer[dsp->espcm_sample_idx] = dsp->record_buffer[dsp->record_pos_read] >> 8; + /* + I assume the real hardware double-buffers the blocks or something like that. + We're not gonna do that here. + */ + dsp->espcm_sample_buffer[dsp->espcm_sample_idx] = (int8_t) (dsp->record_buffer[dsp->record_pos_read] >> 8); dsp->espcm_sample_idx++; dsp->record_pos_read += 2; dsp->record_pos_read &= 0xFFFF; if (dsp->espcm_sample_idx >= 19) { - int i, table_addr, sigma, last_sigma; + int i, table_addr; int8_t min_sample = 127, max_sample = -128, s; - uint8_t b; for (i = 0; i < 19; i++) { s = dsp->espcm_sample_buffer[i]; @@ -2639,14 +2703,19 @@ sb_poll_i(void *priv) } } if (min_sample < 0) { - min_sample = -min_sample; + if (min_sample == -128) + min_sample = 127; /* Clip it to make it fit into int8_t. */ + else + min_sample = (int8_t) -min_sample; } if (max_sample < 0) { - max_sample = -max_sample; + if (max_sample == -128) + max_sample = 127; /* Clip it to make it fit into int8_t. */ + else + max_sample = (int8_t) -max_sample; } - if (min_sample > max_sample) { + if (min_sample > max_sample) max_sample = min_sample; - } for (table_addr = 15; table_addr < 256; table_addr += 16) { if (max_sample <= espcm_range_map[table_addr]) { @@ -2656,11 +2725,11 @@ sb_poll_i(void *priv) dsp->espcm_range = table_addr >> 4; for (i = 0; i < 19; i++) { + int last_sigma = 9999; table_addr = dsp->espcm_range << 4; - last_sigma = 9999; s = dsp->espcm_sample_buffer[i]; for (; (table_addr >> 4) == dsp->espcm_range; table_addr++) { - sigma = espcm_range_map[table_addr] - s; + int sigma = espcm_range_map[table_addr] - s; if (sigma < 0) { sigma = -sigma; } @@ -2673,7 +2742,7 @@ sb_poll_i(void *priv) dsp->espcm_code_buffer[i] = table_addr & 0x0F; } - b = dsp->espcm_range | (dsp->espcm_code_buffer[0] << 4); + uint8_t b = dsp->espcm_range | (dsp->espcm_code_buffer[0] << 4); dsp->dma_writeb(dsp->dma_priv, b); dsp->sb_8_length--; dsp->ess_dma_counter++; diff --git a/src/sound/sound.c b/src/sound/sound.c index cf7a7a33c..0611909d8 100644 --- a/src/sound/sound.c +++ b/src/sound/sound.c @@ -22,25 +22,20 @@ #include #include #include -#include #define HAVE_STDARG_H #include <86box/86box.h> #include <86box/cdrom.h> #include <86box/device.h> #include <86box/filters.h> -#include <86box/hdc_ide.h> #include <86box/machine.h> #include <86box/midi.h> #include <86box/plat.h> #include <86box/thread.h> #include <86box/snd_ac97.h> -#include <86box/snd_azt2316a.h> #include <86box/timer.h> #include <86box/snd_mpu401.h> #include <86box/sound.h> -#include <86box/snd_opl.h> -#include <86box/snd_sb_dsp.h> typedef struct { const device_t *device; @@ -54,10 +49,15 @@ typedef struct { int sound_card_current[SOUND_CARD_MAX] = { 0, 0, 0, 0 }; int sound_pos_global = 0; int music_pos_global = 0; +int wavetable_pos_global = 0; int sound_gain = 0; static sound_handler_t sound_handlers[8]; + static sound_handler_t music_handlers[8]; +static sound_handler_t wavetable_handlers[8]; + +static double cd_audio_volume_lut[256]; static thread_t *sound_cd_thread_h; static event_t *sound_cd_event; @@ -68,12 +68,18 @@ static int16_t *outbuffer_ex_int16; static int32_t *outbuffer_m; static float *outbuffer_m_ex; static int16_t *outbuffer_m_ex_int16; +static int32_t *outbuffer_w; +static float *outbuffer_w_ex; +static int16_t *outbuffer_w_ex_int16; static int sound_handlers_num; static int music_handlers_num; +static int wavetable_handlers_num; static pc_timer_t sound_poll_timer; static uint64_t sound_poll_latch; static pc_timer_t music_poll_timer; static uint64_t music_poll_latch; +static pc_timer_t wavetable_poll_timer; +static uint64_t wavetable_poll_latch; static int16_t cd_buffer[CDROM_NUM][CD_BUFLEN * 2]; static float cd_out_buffer[CD_BUFLEN * 2]; @@ -120,52 +126,59 @@ static const device_t sound_internal_device = { static const SOUND_CARD sound_cards[] = { // clang-format off - { &sound_none_device }, - { &sound_internal_device }, - { &acermagic_s20_device }, - { &mirosound_pcm10_device }, - { &adlib_device }, - { &adgold_device }, - { &azt2316a_device }, - { &azt1605_device }, - { &cms_device }, - { &cs4235_device }, - { &cs4236b_device }, - { &gus_device }, - { &sb_1_device }, - { &sb_15_device }, - { &sb_2_device }, - { &sb_pro_v1_device }, - { &sb_pro_v2_device }, - { &sb_16_device }, - { &sb_16_pnp_device }, - { &sb_32_pnp_device }, - { &sb_awe32_device }, - { &sb_awe32_pnp_device }, - { &sb_awe64_value_device }, - { &sb_awe64_device }, - { &sb_awe64_gold_device }, - { &sb_vibra16c_device }, - { &sb_vibra16s_device }, - { &sb_vibra16xv_device }, - { &ssi2001_device }, - { &pasplus_device }, - { &pas16_device }, - { &pssj_isa_device }, - { &tndy_device }, - { &wss_device }, - { &adlib_mca_device }, - { &ncr_business_audio_device }, - { &sb_mcv_device }, - { &sb_pro_mcv_device }, - { &sb_16_reply_mca_device }, - { &cmi8338_device }, - { &cmi8738_device }, - { &es1371_device }, - { &ad1881_device }, - { &cs4297a_device }, - { &ess_1688_device }, - { NULL } + { &sound_none_device }, + { &sound_internal_device }, + { &acermagic_s20_device }, + { &mirosound_pcm10_device }, + { &adlib_device }, + { &adgold_device }, + { &azt2316a_device }, + { &azt1605_device }, + { &cms_device }, + { &cs4235_device }, + { &cs4236b_device }, + { &gus_device }, + { &sb_1_device }, + { &sb_15_device }, + { &sb_2_device }, + { &sb_pro_v1_device }, + { &sb_pro_v2_device }, + { &sb_16_device }, + { &sb_16_pnp_device }, + { &sb_32_pnp_device }, + { &sb_awe32_device }, + { &sb_awe32_pnp_device }, + { &sb_awe64_value_device }, + { &sb_awe64_device }, + { &sb_awe64_gold_device }, + { &sb_vibra16c_device }, + { &sb_vibra16s_device }, + { &sb_vibra16xv_device }, + { &ssi2001_device }, + { &pasplus_device }, + { &pas16_device }, + { &pssj_isa_device }, + { &tndy_device }, + { &wss_device }, + { &adlib_mca_device }, + { &ess_chipchat_16_mca_device }, + { &ncr_business_audio_device }, + { &sb_mcv_device }, + { &sb_pro_mcv_device }, + { &sb_16_reply_mca_device }, + { &ess_soundpiper_16_mca_device }, + { &ess_soundpiper_32_mca_device }, + { &cmi8338_device }, + { &cmi8738_device }, + { &es1371_device }, + { &ad1881_device }, + { &cs4297a_device }, + { &ess_688_device }, + { &ess_ess0100_pnp_device }, + { &ess_1688_device }, + { &ess_ess0102_pnp_device }, + { &ess_ess0968_pnp_device }, + { NULL } // clang-format on }; @@ -205,7 +218,7 @@ sound_card_getdevice(int card) int sound_card_has_config(int card) { - if (!sound_cards[card].device) + if (sound_cards[card].device == NULL) return 0; return device_has_config(sound_cards[card].device) ? 1 : 0; } @@ -234,13 +247,13 @@ void sound_card_init(void) { if ((sound_card_current[0] > SOUND_INTERNAL) && (sound_cards[sound_card_current[0]].device)) - device_add(sound_cards[sound_card_current[0]].device); + device_add_inst(sound_cards[sound_card_current[0]].device, 1); if ((sound_card_current[1] > SOUND_INTERNAL) && (sound_cards[sound_card_current[1]].device)) - device_add(sound_cards[sound_card_current[1]].device); + device_add_inst(sound_cards[sound_card_current[1]].device, 2); if ((sound_card_current[2] > SOUND_INTERNAL) && (sound_cards[sound_card_current[2]].device)) - device_add(sound_cards[sound_card_current[2]].device); + device_add_inst(sound_cards[sound_card_current[2]].device, 3); if ((sound_card_current[3] > SOUND_INTERNAL) && (sound_cards[sound_card_current[3]].device)) - device_add(sound_cards[sound_card_current[3]].device); + device_add_inst(sound_cards[sound_card_current[3]].device, 4); } void @@ -262,9 +275,7 @@ sound_cd_clean_buffers(void) static void sound_cd_thread(UNUSED(void *param)) { - uint32_t lba; - int r; - int pre; + int temp_buffer[2]; int channel_select[2]; double audio_vol_l; double audio_vol_r; @@ -281,47 +292,35 @@ sound_cd_thread(UNUSED(void *param)) sound_cd_clean_buffers(); + temp_buffer[0] = temp_buffer[1] = 0; + for (uint8_t i = 0; i < CDROM_NUM; i++) { if ((cdrom[i].bus_type == CDROM_BUS_DISABLED) || (cdrom[i].cd_status == CD_STATUS_EMPTY)) continue; - lba = cdrom[i].seek_pos; - r = cdrom_audio_callback(&(cdrom[i]), cd_buffer[i], CD_BUFLEN * 2); - if (!cdrom[i].bus_type || !cdrom[i].sound_on || !r) + const uint32_t lba = cdrom[i].seek_pos; + const int r = cdrom_audio_callback(&(cdrom[i]), cd_buffer[i], CD_BUFLEN * 2); + if (!cdrom[i].sound_on || !r) continue; - pre = cdrom_is_pre(&(cdrom[i]), lba); + const int pre = cdrom_is_pre(&(cdrom[i]), lba); + if (cdrom[i].get_volume) { - audio_vol_l = (float) (cdrom[i].get_volume(cdrom[i].priv, 0)); - audio_vol_r = (float) (cdrom[i].get_volume(cdrom[i].priv, 1)); + audio_vol_l = cd_audio_volume_lut[cdrom[i].get_volume(cdrom[i].priv, 0)]; + audio_vol_r = cd_audio_volume_lut[cdrom[i].get_volume(cdrom[i].priv, 1)]; } else { - audio_vol_l = 255.0; - audio_vol_r = 255.0; + audio_vol_l = cd_audio_volume_lut[255]; + audio_vol_r = cd_audio_volume_lut[255]; } - /* Calculate attenuation per the specification. */ - if (audio_vol_l >= 255.0) - audio_vol_l = 1.0; - else if (audio_vol_l > 0.0) - audio_vol_l = (48.0 + (20.0 * log(audio_vol_l / 256.0))) / 48.0; - else - audio_vol_l = 0.0; - - if (audio_vol_r >= 255.0) - audio_vol_r = 1.0; - else if (audio_vol_r > 0.0) - audio_vol_r = (48.0 + (20.0 * log(audio_vol_r / 256.0))) / 48.0; - else - audio_vol_r = 0.0; - if (cdrom[i].get_channel) { - channel_select[0] = cdrom[i].get_channel(cdrom[i].priv, 0); - channel_select[1] = cdrom[i].get_channel(cdrom[i].priv, 1); + channel_select[0] = (int) cdrom[i].get_channel(cdrom[i].priv, 0); + channel_select[1] = (int) cdrom[i].get_channel(cdrom[i].priv, 1); } else { channel_select[0] = 1; channel_select[1] = 2; } - for (uint16_t c = 0; c < CD_BUFLEN * 2; c += 2) { + for (int c = 0; c < CD_BUFLEN * 2; c += 2) { /*Apply ATAPI channel select*/ cd_buffer_temp[0] = cd_buffer_temp[1] = 0.0; @@ -359,17 +358,20 @@ sound_cd_thread(UNUSED(void *param)) cd_out_buffer[c] += (float) (cd_buffer_temp[0] / 32768.0); cd_out_buffer[c + 1] += (float) (cd_buffer_temp[1] / 32768.0); } else { - if (cd_buffer_temp[0] > 32767) - cd_buffer_temp[0] = 32767; - if (cd_buffer_temp[0] < -32768) - cd_buffer_temp[0] = -32768; - if (cd_buffer_temp[1] > 32767) - cd_buffer_temp[1] = 32767; - if (cd_buffer_temp[1] < -32768) - cd_buffer_temp[1] = -32768; + temp_buffer[0] += (int) trunc(cd_buffer_temp[0]); + temp_buffer[1] += (int) trunc(cd_buffer_temp[1]); - cd_out_buffer_int16[c] += (int16_t) cd_buffer_temp[0]; - cd_out_buffer_int16[c + 1] += (int16_t) cd_buffer_temp[1]; + if (temp_buffer[0] > 32767) + temp_buffer[0] = 32767; + if (temp_buffer[0] < -32768) + temp_buffer[0] = -32768; + if (temp_buffer[1] > 32767) + temp_buffer[1] = 32767; + if (temp_buffer[1] < -32768) + temp_buffer[1] = -32768; + + cd_out_buffer_int16[c] = (int16_t) temp_buffer[0]; + cd_out_buffer_int16[c + 1] = (int16_t) temp_buffer[1]; } } } @@ -425,6 +427,28 @@ music_realloc_buffers(void) } } +static void +wavetable_realloc_buffers(void) +{ + if (outbuffer_w_ex != NULL) { + free(outbuffer_w_ex); + outbuffer_w_ex = NULL; + } + + if (outbuffer_w_ex_int16 != NULL) { + free(outbuffer_w_ex_int16); + outbuffer_w_ex_int16 = NULL; + } + + if (sound_is_float) { + outbuffer_w_ex = calloc(WTBUFLEN * 2, sizeof(float)); + memset(outbuffer_w_ex, 0x00, WTBUFLEN * 2 * sizeof(float)); + } else { + outbuffer_w_ex_int16 = calloc(WTBUFLEN * 2, sizeof(int16_t)); + memset(outbuffer_w_ex_int16, 0x00, WTBUFLEN * 2 * sizeof(int16_t)); + } +} + void sound_init(void) { @@ -436,6 +460,9 @@ sound_init(void) outbuffer_m_ex = NULL; outbuffer_m_ex_int16 = NULL; + outbuffer_w_ex = NULL; + outbuffer_w_ex_int16 = NULL; + outbuffer = NULL; outbuffer = calloc(SOUNDBUFLEN * 2, sizeof(int32_t)); memset(outbuffer, 0x00, SOUNDBUFLEN * 2 * sizeof(int32_t)); @@ -444,6 +471,22 @@ sound_init(void) outbuffer_m = calloc(MUSICBUFLEN * 2, sizeof(int32_t)); memset(outbuffer_m, 0x00, MUSICBUFLEN * 2 * sizeof(int32_t)); + outbuffer_w = NULL; + outbuffer_w = calloc(WTBUFLEN * 2, sizeof(int32_t)); + memset(outbuffer_w, 0x00, WTBUFLEN * 2 * sizeof(int32_t)); + + for (uint16_t i = 0; i < 256; i++) { + double di = (double) i; + + if (di >= 255.0) + di = 1.0; + else if (di > 0.0) + di = (48.0 + (20.0 * log(di / 256.0))) / 48.0; + else + di = 0.0; + + cd_audio_volume_lut[i] = di; + } for (uint8_t i = 0; i < CDROM_NUM; i++) { if (cdrom[i].bus_type != CDROM_BUS_DISABLED) @@ -484,6 +527,14 @@ music_add_handler(void (*get_buffer)(int32_t *buffer, int len, void *priv), void music_handlers_num++; } +void +wavetable_add_handler(void (*get_buffer)(int32_t *buffer, int len, void *priv), void *priv) +{ + wavetable_handlers[wavetable_handlers_num].get_buffer = get_buffer; + wavetable_handlers[wavetable_handlers_num].priv = priv; + wavetable_handlers_num++; +} + void sound_set_cd_audio_filter(void (*filter)(int channel, double *buffer, void *priv), void *priv) { @@ -527,7 +578,7 @@ sound_poll(UNUSED(void *priv)) if (outbuffer[c] < -32768) outbuffer[c] = -32768; - outbuffer_ex_int16[c] = outbuffer[c]; + outbuffer_ex_int16[c] = (int16_t) outbuffer[c]; } } @@ -571,7 +622,7 @@ music_poll(UNUSED(void *priv)) if (outbuffer_m[c] < -32768) outbuffer_m[c] = -32768; - outbuffer_m_ex_int16[c] = outbuffer_m[c]; + outbuffer_m_ex_int16[c] = (int16_t) outbuffer_m[c]; } } @@ -584,12 +635,50 @@ music_poll(UNUSED(void *priv)) } } +void +wavetable_poll(UNUSED(void *priv)) +{ + timer_advance_u64(&wavetable_poll_timer, wavetable_poll_latch); + + wavetable_pos_global++; + if (wavetable_pos_global == WTBUFLEN) { + int c; + + memset(outbuffer_w, 0x00, WTBUFLEN * 2 * sizeof(int32_t)); + + for (c = 0; c < wavetable_handlers_num; c++) + wavetable_handlers[c].get_buffer(outbuffer_w, WTBUFLEN, wavetable_handlers[c].priv); + + for (c = 0; c < WTBUFLEN * 2; c++) { + if (sound_is_float) + outbuffer_w_ex[c] = ((float) outbuffer_w[c]) / (float) 32768.0; + else { + if (outbuffer_w[c] > 32767) + outbuffer_w[c] = 32767; + if (outbuffer_w[c] < -32768) + outbuffer_w[c] = -32768; + + outbuffer_w_ex_int16[c] = (int16_t) outbuffer_w[c]; + } + } + + if (sound_is_float) + givealbuffer_wt(outbuffer_w_ex); + else + givealbuffer_wt(outbuffer_w_ex_int16); + + wavetable_pos_global = 0; + } +} + void sound_speed_changed(void) { sound_poll_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / (double) SOUND_FREQ)); music_poll_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / (double) MUSIC_FREQ)); + + wavetable_poll_latch = (uint64_t) ((double) TIMER_USEC * (1000000.0 / (double) WT_FREQ)); } void @@ -599,6 +688,8 @@ sound_reset(void) music_realloc_buffers(); + wavetable_realloc_buffers(); + midi_out_device_init(); midi_in_device_init(); @@ -614,6 +705,11 @@ sound_reset(void) music_handlers_num = 0; memset(music_handlers, 0x00, 8 * sizeof(sound_handler_t)); + timer_add(&wavetable_poll_timer, wavetable_poll, NULL, 1); + + wavetable_handlers_num = 0; + memset(wavetable_handlers, 0x00, 8 * sizeof(sound_handler_t)); + filter_cd_audio = NULL; filter_cd_audio_p = NULL; diff --git a/src/sound/xaudio2.c b/src/sound/xaudio2.c index 78c3e2d35..9b341f574 100644 --- a/src/sound/xaudio2.c +++ b/src/sound/xaudio2.c @@ -52,6 +52,7 @@ static IXAudio2 *xaudio2 = NULL; static IXAudio2MasteringVoice *mastervoice = NULL; static IXAudio2SourceVoice *srcvoice = NULL; static IXAudio2SourceVoice *srcvoicemusic = NULL; +static IXAudio2SourceVoice *srcvoicewt = NULL; static IXAudio2SourceVoice *srcvoicemidi = NULL; static IXAudio2SourceVoice *srcvoicecd = NULL; @@ -169,27 +170,34 @@ inital(void) fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; - IXAudio2_CreateSourceVoice(xaudio2, &srcvoicemusic, &fmt, 0, 2.0f, &callbacks, NULL, NULL); + (void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicemusic, &fmt, 0, 2.0f, &callbacks, NULL, NULL); + + fmt.nSamplesPerSec = WT_FREQ; + fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; + fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; + + (void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicewt, &fmt, 0, 2.0f, &callbacks, NULL, NULL); fmt.nSamplesPerSec = CD_FREQ; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; - IXAudio2_CreateSourceVoice(xaudio2, &srcvoicecd, &fmt, 0, 2.0f, &callbacks, NULL, NULL); + (void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicecd, &fmt, 0, 2.0f, &callbacks, NULL, NULL); - IXAudio2SourceVoice_SetVolume(srcvoice, 1, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_Start(srcvoice, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_Start(srcvoicecd, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_Start(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_SetVolume(srcvoice, 1, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_Start(srcvoice, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_Start(srcvoicecd, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_Start(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_Start(srcvoicewt, 0, XAUDIO2_COMMIT_NOW); const char *mdn = midi_out_device_get_internal_name(midi_output_device_current); - if (strcmp(mdn, "none") && strcmp(mdn, SYSTEM_MIDI_INTERNAL_NAME)) { + if ((strcmp(mdn, "none") != 0) && (strcmp(mdn, SYSTEM_MIDI_INTERNAL_NAME) != 0)) { fmt.nSamplesPerSec = midi_freq; fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; - IXAudio2_CreateSourceVoice(xaudio2, &srcvoicemidi, &fmt, 0, 2.0f, &callbacks, NULL, NULL); - IXAudio2SourceVoice_Start(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicemidi, &fmt, 0, 2.0f, &callbacks, NULL, NULL); + (void) IXAudio2SourceVoice_Start(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); } initialized = 1; @@ -202,17 +210,20 @@ closeal(void) if (!initialized) return; initialized = 0; - IXAudio2SourceVoice_Stop(srcvoice, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_FlushSourceBuffers(srcvoice); - IXAudio2SourceVoice_Stop(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemusic); - IXAudio2SourceVoice_Stop(srcvoicecd, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_FlushSourceBuffers(srcvoicecd); + (void) IXAudio2SourceVoice_Stop(srcvoice, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoice); + (void) IXAudio2SourceVoice_Stop(srcvoicemusic, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemusic); + (void) IXAudio2SourceVoice_Stop(srcvoicewt, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicewt); + (void) IXAudio2SourceVoice_Stop(srcvoicecd, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicecd); if (srcvoicemidi) { - IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi); + (void) IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi); IXAudio2SourceVoice_DestroyVoice(srcvoicemidi); } + IXAudio2SourceVoice_DestroyVoice(srcvoicewt); IXAudio2SourceVoice_DestroyVoice(srcvoicecd); IXAudio2SourceVoice_DestroyVoice(srcvoicemusic); IXAudio2SourceVoice_DestroyVoice(srcvoice); @@ -229,12 +240,13 @@ closeal(void) } void -givealbuffer_common(void *buf, IXAudio2SourceVoice *sourcevoice, size_t buflen) +givealbuffer_common(const void *buf, IXAudio2SourceVoice *sourcevoice, const size_t buflen) { if (!initialized) return; - IXAudio2MasteringVoice_SetVolume(mastervoice, pow(10.0, (double) sound_gain / 20.0), XAUDIO2_COMMIT_NOW); + (void) IXAudio2MasteringVoice_SetVolume(mastervoice, pow(10.0, (double) sound_gain / 20.0), + XAUDIO2_COMMIT_NOW); XAUDIO2_BUFFER buffer = { 0 }; buffer.Flags = 0; if (sound_is_float) { @@ -251,37 +263,43 @@ givealbuffer_common(void *buf, IXAudio2SourceVoice *sourcevoice, size_t buflen) buffer.PlayBegin = buffer.PlayLength = 0; buffer.PlayLength = buflen >> 1; buffer.pContext = (void *) buffer.pAudioData; - IXAudio2SourceVoice_SubmitSourceBuffer(sourcevoice, &buffer, NULL); + (void) IXAudio2SourceVoice_SubmitSourceBuffer(sourcevoice, &buffer, NULL); } void -givealbuffer(void *buf) +givealbuffer(const void *buf) { givealbuffer_common(buf, srcvoice, BUFLEN << 1); } void -givealbuffer_music(void *buf) +givealbuffer_music(const void *buf) { givealbuffer_common(buf, srcvoicemusic, MUSICBUFLEN << 1); } void -givealbuffer_cd(void *buf) +givealbuffer_wt(const void *buf) +{ + givealbuffer_common(buf, srcvoicewt, WTBUFLEN << 1); +} + +void +givealbuffer_cd(const void *buf) { if (srcvoicecd) givealbuffer_common(buf, srcvoicecd, CD_BUFLEN << 1); } void -al_set_midi(int freq, int buf_size) +al_set_midi(const int freq, const int buf_size) { midi_freq = freq; midi_buf_size = buf_size; if (initialized && srcvoicemidi) { - IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); - IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi); + (void) IXAudio2SourceVoice_Stop(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2SourceVoice_FlushSourceBuffers(srcvoicemidi); IXAudio2SourceVoice_DestroyVoice(srcvoicemidi); srcvoicemidi = NULL; WAVEFORMATEX fmt; @@ -297,13 +315,13 @@ al_set_midi(int freq, int buf_size) fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; fmt.cbSize = 0; - IXAudio2_CreateSourceVoice(xaudio2, &srcvoicemidi, &fmt, 0, 2.0f, &callbacks, NULL, NULL); - IXAudio2SourceVoice_Start(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); + (void) IXAudio2_CreateSourceVoice(xaudio2, &srcvoicemidi, &fmt, 0, 2.0f, &callbacks, NULL, NULL); + (void) IXAudio2SourceVoice_Start(srcvoicemidi, 0, XAUDIO2_COMMIT_NOW); } } void -givealbuffer_midi(void *buf, uint32_t size) +givealbuffer_midi(const void *buf, const uint32_t size) { givealbuffer_common(buf, srcvoicemidi, size); }