diff --git a/emu/CMakeLists.txt b/emu/CMakeLists.txt index bc7a1005..141d1e9b 100644 --- a/emu/CMakeLists.txt +++ b/emu/CMakeLists.txt @@ -82,6 +82,7 @@ option(SNDEMU_GA20_ALL "Sound Device Irem GA20: all cores" OFF) option(SNDEMU_MIKEY_ALL "Sound Device Mikey: all cores" OFF) option(SNDEMU_K007232_ALL "Sound Device K007232: all cores" OFF) option(SNDEMU_K005289_ALL "Sound Device K005289: all cores" OFF) +option(SNDEMU_BSMT2000_ALL "Sound Device BSMT2000: all cores" OFF) option(SNDEMU_MSM5205_ALL "Sound Device MSM5205: all cores" OFF) option(SNDEMU_MSM5232_ALL "Sound Device MSM5232: all cores" OFF) @@ -147,6 +148,7 @@ if(SNDEMU__ALL) set(SNDEMU_K005289_ALL ON) set(SNDEMU_MSM5205_ALL ON) set(SNDEMU_MSM5232_ALL ON) + set(SNDEMU_BSMT2000_ALL ON) endif() @@ -634,6 +636,11 @@ if(SNDEMU_MSM5232_ALL) set(EMU_FILES ${EMU_FILES} cores/msm5232.c) set(EMU_CORE_HEADERS ${EMU_CORE_HEADERS} cores/msm5232.h) endif() +if(SNDEMU_BSMT2000_ALL) + set(EMU_DEFS ${EMU_DEFS} " SNDDEV_BSMT2000") + set(EMU_FILES ${EMU_FILES} cores/bsmt2000.c) + set(EMU_CORE_HEADERS ${EMU_CORE_HEADERS} cores/bsmt2000.h) +endif() add_library(${PROJECT_NAME} ${LIBRARY_TYPE} ${EMU_FILES}) set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON) diff --git a/emu/SoundDevs.h b/emu/SoundDevs.h index 472a9283..fcea61e9 100644 --- a/emu/SoundDevs.h +++ b/emu/SoundDevs.h @@ -47,4 +47,5 @@ #define DEVID_K005289 0x2B #define DEVID_MSM5205 0x2C // variants: MSM5205, MSM6585 #define DEVID_MSM5232 0x2D +#define DEVID_BSMT2000 0x2E #endif // __SOUNDDEVS_H__ diff --git a/emu/SoundEmu.c b/emu/SoundEmu.c index a601604a..c1ef6dda 100644 --- a/emu/SoundEmu.c +++ b/emu/SoundEmu.c @@ -54,6 +54,7 @@ #define SNDDEV_K005289 #define SNDDEV_MSM5205 #define SNDDEV_MSM5232 +#define SNDDEV_BSMT2000 #endif #ifdef SNDDEV_SN76496 @@ -182,6 +183,9 @@ #ifdef SNDDEV_MSM5232 #include "cores/msm5232.h" #endif +#ifdef SNDDEV_BSMT2000 +#include "cores/bsmt2000.h" +#endif const DEV_DECL* sndEmu_Devices[] = { #ifdef SNDDEV_SN76496 @@ -321,6 +325,9 @@ const DEV_DECL* sndEmu_Devices[] = { #endif #ifdef SNDDEV_MSM5232 &sndDev_MSM5232, +#endif +#ifdef SNDDEV_BSMT2000 + &sndDev_BSMT2000, #endif NULL // list end }; diff --git a/emu/cores/bsmt2000.c b/emu/cores/bsmt2000.c new file mode 100644 index 00000000..0f71b1eb --- /dev/null +++ b/emu/cores/bsmt2000.c @@ -0,0 +1,604 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Paul Leaman, Miguel Angel Horna, Mao, cam900 +/*************************************************************************** + + Data East BSMT2000 + ================== + + Core for BSMT2000 sound chip, adapted for libvgm, with fixes based on M1/MAME/PinMAME source. + + The BSMT2000 is a custom TMS320C15 DSP with internal ROM and external sample ROM. + It supports multiple PCM voices and a single ADPCM channel, with stereo output. + + FIXES: + - Register layout and handling match M1/MAME/PinMAME (9 registers per voice) + - Position, rate, and loop logic use fixed-point math (16.16) + - Linear interpolation for PCM voices + - ADPCM/compressed channel handling is per M1/MAME/PinMAME + - Voice init matches M1/MAME/PinMAME + + Modifications for PINMAME by Steve Ellenoff & Martin Adrian & Carsten Waechter + + References: + https://github.com/vpinball/pinmame/blob/master/src/sound/bsmt2000.c + https://github.com/vpinball/pinmame/blob/master/src/sound/bsmt2000.h + M1's BSMT2000 source code. https://vgmrips.net/forum/viewtopic.php?t=110 + https://www.researchgate.net/publication/291338452_Hacking_a_Sega_Whitestar_Pinball + +***************************************************************************/ + +#include +#include +#include +#include + +#include "../../stdtype.h" +#include "../EmuStructs.h" +#include "../SoundDevs.h" +#include "../EmuCores.h" +#include "../snddef.h" +#include "../EmuHelper.h" +#include "../logging.h" +#include "bsmt2000.h" + +/* ==== Constants ==== */ + +#define BSMT2000_CLOCK 24000000 /* default 24MHz clock */ +#define BSMT2000_CHANNELS 12 /* up to 12 PCM voices, plus ADPCM */ +#define BSMT2000_ADPCM_INDEX 12 /* 0..11 = PCM, 12 = ADPCM/compressed */ +#define BSMT2000_REG_CURRPOS 0 +#define BSMT2000_REG_RATE 1 +#define BSMT2000_REG_LOOPEND 2 +#define BSMT2000_REG_LOOPSTART 3 +#define BSMT2000_REG_BANK 4 +#define BSMT2000_REG_RIGHTVOL 5 +#define BSMT2000_REG_LEFTVOL 6 +#define BSMT2000_REG_TOTAL 7 + +#define BSMT2000_MAX_VOICES (BSMT2000_CHANNELS + 1) /* 12 PCM + 1 ADPCM/compressed */ +#define BSMT2000_SAMPLE_CHUNK 10000 + +#define BSMT2000_ROM_BANKSIZE 0x10000 /* 64k per bank */ + +static const UINT8 regmap[8][7] = { + { 0x00, 0x18, 0x24, 0x30, 0x3c, 0x48, 0xff }, // last one (stereo/leftvol) unused, set to max for mapping + { 0x00, 0x16, 0x21, 0x2c, 0x37, 0x42, 0x4d }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // mode 2 only a testmode left channel + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // mode 3 only a testmode right channel + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // mode 4 only a testmode left channel + { 0x00, 0x18, 0x24, 0x30, 0x3c, 0x54, 0x60 }, + { 0x00, 0x10, 0x18, 0x20, 0x28, 0x38, 0x40 }, + { 0x00, 0x12, 0x1b, 0x24, 0x2d, 0x3f, 0x48 } }; + +/* ==== Internal Voice State ==== */ +typedef struct +{ + UINT16 reg[9]; // 9 registers per voice, match MAME/M1 + UINT32 position; // 16.16 fixed-point + UINT32 loop_start_position; + UINT32 loop_stop_position; + UINT32 adjusted_rate; +} bsmt2000_voice; + +/* ==== Main Chip State ==== */ +typedef struct _bsmt2000_state bsmt2000_state; +struct _bsmt2000_state +{ + DEV_DATA _devData; + DEV_LOGGER logger; + + // Sample ROM + UINT8 *sample_rom; + UINT32 sample_rom_length; + UINT32 sample_rom_mask; + UINT32 total_banks; + + // Voices and ADPCM/compressed + bsmt2000_voice voice[BSMT2000_MAX_VOICES]; + UINT8 right_volume_set; // Right volume is set (from PinMAME) + UINT8 voices; // actual number of voices (usually 11 or 12) + UINT8 stereo; // stereo output enabled? + UINT8 adpcm; // ADPCM/compressed enabled? + UINT8 mode; // current mode (0,1,5,6,7) + UINT8 last_register; // last register written + UINT32 clock; // Chip clock + + // ADPCM/compressed state + INT32 adpcm_current; + INT32 adpcm_delta_n; + + // Output sample rate + double sample_rate; + + // Mute mask + UINT8 Muted[BSMT2000_MAX_VOICES]; + + // Misc + // (can add more fields as needed) + DEVCB_SRATE_CHG SmpRateFunc; + void* SmpRateData; +}; + +/* ==== Prototypes ==== */ +static void bsmt2000_update(void *param, UINT32 samples, DEV_SMPL **outputs); +static UINT8 device_start_bsmt2000(const DEV_GEN_CFG* cfg, DEV_INFO* retDevInf); +static void device_stop_bsmt2000(void *info); +static void device_reset_bsmt2000(void *info); + +static void bsmt2000_w(void *info, UINT8 offset, UINT8 data); +static UINT8 bsmt2000_r(void *info, UINT8 offset); +static void bsmt2000_write_data(void *info, UINT8 address, UINT16 data); + +static void bsmt2000_alloc_rom(void* info, UINT32 memsize); +static void bsmt2000_write_rom(void *info, UINT32 offset, UINT32 length, const UINT8* data); +static void bsmt2000_set_mute_mask(void *info, UINT32 MuteMask); +static UINT32 bsmt2000_get_mute_mask(void *info); +static void bsmt2000_set_log_cb(void* info, DEVCB_LOG func, void* param); +static void bsmt2000_set_srchg_cb(void *info, DEVCB_SRATE_CHG CallbackFunc, void* DataPtr); + +/* ==== Device Function Table ==== */ +static DEVDEF_RWFUNC devFunc[] = +{ + {RWF_REGISTER | RWF_WRITE, DEVRW_A8D8, 0, bsmt2000_w}, + {RWF_REGISTER | RWF_READ, DEVRW_A8D8, 0, bsmt2000_r}, + {RWF_REGISTER | RWF_QUICKWRITE, DEVRW_A8D16, 0, bsmt2000_write_data}, + {RWF_MEMORY | RWF_WRITE, DEVRW_BLOCK, 0, bsmt2000_write_rom}, + {RWF_MEMORY | RWF_WRITE, DEVRW_MEMSIZE, 0, bsmt2000_alloc_rom}, + {RWF_CHN_MUTE | RWF_WRITE, DEVRW_ALL, 0, bsmt2000_set_mute_mask}, + {0x00, 0x00, 0, NULL} +}; + +/* ==== Device Definition ==== */ +DEV_DEF devDef = +{ + "BSMT2000", "MAME", FCC_MAME, + + device_start_bsmt2000, + device_stop_bsmt2000, + device_reset_bsmt2000, + bsmt2000_update, + + NULL, // SetOptionBits + bsmt2000_set_mute_mask, + NULL, // SetPanning + bsmt2000_set_srchg_cb, + bsmt2000_set_log_cb, // SetLoggingCallback + NULL, // LinkDevice + + devFunc, // rwFuncs +}; + +static const char* DeviceName(const DEV_GEN_CFG* devCfg) +{ + return "BSMT2000"; +} + +static UINT16 DeviceChannels(const DEV_GEN_CFG* devCfg) +{ + return BSMT2000_MAX_VOICES; +} + +static const char** DeviceChannelNames(const DEV_GEN_CFG* devCfg) +{ + static const char* names[BSMT2000_MAX_VOICES] = + { + "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", + "PCM 9", "PCM 10", "PCM 11", "PCM 12", + "ADPCM", + }; + return names; +} + +const DEV_DECL sndDev_BSMT2000 = +{ + DEVID_BSMT2000, + DeviceName, + DeviceChannels, + DeviceChannelNames, + { // cores + &devDef, + NULL + } +}; + +/* ==== Utility Macros ==== */ +#define MIN(x,y) ((x)<(y)?(x):(y)) + +/* ==== Internal Functions ==== */ + +static void init_voice(bsmt2000_voice *voice) +{ + memset(voice, 0, sizeof(*voice)); + // M1/MAME uses 0 for volumes on reset/init. + voice->reg[BSMT2000_REG_LEFTVOL] = 0; + voice->reg[BSMT2000_REG_RIGHTVOL] = 0; + voice->position = 0; + voice->adjusted_rate = 0; + voice->loop_start_position = 0; + voice->loop_stop_position = 0; +} +static void init_all_voices(bsmt2000_state *chip) +{ + int i; + for (i = 0; i < BSMT2000_MAX_VOICES; i++) + init_voice(&chip->voice[i]); +} + +/* ==== Device Start ==== */ +static UINT8 device_start_bsmt2000(const DEV_GEN_CFG* cfg, DEV_INFO* retDevInf) +{ + bsmt2000_state *chip; + chip = (bsmt2000_state *)calloc(1, sizeof(bsmt2000_state)); + if (!chip) + return 0xFF; + + chip->sample_rom = NULL; + chip->sample_rom_length = 0x00; + chip->sample_rom_mask = 0x00; + chip->total_banks = 0; + + chip->right_volume_set = 0; + + chip->voices = 12; // default: 12 PCM (may be overridden later) + chip->stereo = 1; // default: stereo enabled + chip->adpcm = 1; // default: ADPCM enabled + + chip->mode = 1; + chip->clock = cfg->clock; + chip->sample_rate = chip->clock / 1000.0; + chip->last_register = 1; // Mode 1 + + chip->adpcm_current = 0; + chip->adpcm_delta_n = 10; + + init_all_voices(chip); + bsmt2000_set_mute_mask(chip, 0x0000); + + chip->_devData.chipInf = chip; + INIT_DEVINF(retDevInf, &chip->_devData, (UINT32)chip->sample_rate, &devDef); + + return 0x00; +} + +/* ==== Device Stop ==== */ +static void device_stop_bsmt2000(void *info) +{ + bsmt2000_state *chip = (bsmt2000_state *)info; + free(chip->sample_rom); + free(chip); +} + +/* ==== Device Reset ==== */ +static void device_reset_bsmt2000(void *info) { + bsmt2000_state *chip = (bsmt2000_state *)info; + UINT32 muteMask; + + muteMask = bsmt2000_get_mute_mask(chip); + init_all_voices(chip); + chip->adpcm_current = 0; + chip->adpcm_delta_n = 10; + switch (chip->last_register) + { + default: + break; + /* mode 0: 24kHz, 12 channel PCM, 1 channel ADPCM, mono; from PinMAME */ + case 0: + chip->sample_rate = chip->clock / 1000.; + chip->stereo = 0; + chip->voices = 12; + chip->adpcm = 1; + chip->mode = 0; + break; + /* mode 1: 24kHz, 11 channel PCM, 1 channel ADPCM, stereo */ + case 1: + chip->sample_rate = chip->clock / 1000.; + chip->stereo = 1; + chip->voices = 11; + chip->adpcm = 1; + chip->mode = 1; + break; + /* mode 5: 24kHz, 12 channel PCM, stereo */ + case 5: + chip->sample_rate = chip->clock / 1000.; + chip->stereo = 1; + chip->voices = 12; + chip->adpcm = 0; + chip->mode = 5; + break; + /* mode 6: 34kHz, 8 channel PCM, stereo */ + case 6: + chip->sample_rate = chip->clock / 706.; + chip->stereo = 1; + chip->voices = 8; + chip->adpcm = 0; + chip->mode = 6; + break; + + /* mode 7: 32kHz, 9 channel PCM, stereo */ + case 7: + chip->sample_rate = chip->clock / 750.; + chip->stereo = 1; + chip->voices = 9; + chip->adpcm = 0; + chip->mode = 7; + break; + } + bsmt2000_set_mute_mask(chip, muteMask); + if (chip->SmpRateFunc != NULL) + chip->SmpRateFunc(chip->SmpRateData, chip->sample_rate); + +} +/* ==== Register/Command Interface ==== */ + +static void bsmt2000_w(void *info, UINT8 offset, UINT8 data) +{ + bsmt2000_state *chip = (bsmt2000_state *)info; + static UINT16 latch = 0; + switch (offset) + { + case 0: + latch = (latch & 0x00ff) | (data << 8); + break; + case 1: + latch = (latch & 0xff00) | data; + break; + case 2: + bsmt2000_write_data(chip, data, latch); + break; + case 3: + chip->last_register = data; + device_reset_bsmt2000(info); + break; + default: + emu_logf(&chip->logger, DEVLOG_DEBUG, "unexpected bsmt2000 write to offset %d == %02X\n", offset, data); + break; + } +} + +static UINT8 bsmt2000_r(void *info, UINT8 offset) +{ + /* Always ready (bit 7 = 1) */ + return 0x80; +} + +static void bsmt2000_write_data(void *info, UINT8 address, UINT16 data) { + bsmt2000_state *chip = (bsmt2000_state *)info; + bsmt2000_voice *voice; + + chip->last_register = address; + + // Standard voices (interleaved register layout) + if (address < 0x6d) { + int voice_index; + int regindex = BSMT2000_REG_TOTAL - 1; + while (address < regmap[chip->mode][regindex]) + --regindex; + + voice_index = address - regmap[chip->mode][regindex]; + if (voice_index >= chip->voices) + return; + + voice = &chip->voice[voice_index]; + voice->reg[regindex] = data; + + switch (regindex) { + case BSMT2000_REG_CURRPOS: // REG_CURRPOS + voice->position = data << 16; + break; + case BSMT2000_REG_RATE: // REG_RATE + voice->adjusted_rate = data << 5; + break; + case BSMT2000_REG_LOOPSTART: // REG_LOOPSTART + voice->loop_start_position = data << 16; + break; + case BSMT2000_REG_LOOPEND: // REG_LOOPEND + voice->loop_stop_position = data << 16; + break; + case BSMT2000_REG_RIGHTVOL: + chip->right_volume_set = 1; + break; + } + } + // Compressed/ADPCM channel (11-voice model only) + else if (chip->adpcm != 0 && address >= 0x6d) { + voice = &chip->voice[BSMT2000_ADPCM_INDEX]; + switch (address) { + case 0x6d: + voice->reg[BSMT2000_REG_LOOPEND] = data; // REG_LOOPEND + voice->loop_stop_position = data << 16; + break; + case 0x6f: + voice->reg[BSMT2000_REG_BANK] = data; // REG_BANK + break; + case 0x6e: // main right channel volume control, used when ADPCM is alreay playing + case 0x74: + voice->reg[BSMT2000_REG_RIGHTVOL] = data; // REG_RIGHTVOL + chip->right_volume_set = 1; + break; + case 0x75: + voice->reg[BSMT2000_REG_CURRPOS] = data; // REG_CURRPOS + voice->position = data << 16; + chip->adpcm_current = 0; + chip->adpcm_delta_n = 10; + break; + case 0x70: // main left channel volume control, used when ADPCM is alreay playing + case 0x78: + voice->reg[BSMT2000_REG_LEFTVOL] = data; // REG_LEFTVOL + break; + } + } +} + +/* ==== Sample ROM Handling ==== */ +static void bsmt2000_alloc_rom(void* info, UINT32 memsize) { + bsmt2000_state* chip = (bsmt2000_state *)info; + memsize = (memsize + BSMT2000_ROM_BANKSIZE - 1) & ~(BSMT2000_ROM_BANKSIZE - 1); + chip->total_banks = memsize / BSMT2000_ROM_BANKSIZE; + if (chip->sample_rom_length == memsize) + return; + chip->sample_rom = (UINT8*)realloc(chip->sample_rom, memsize); + chip->sample_rom_length = memsize; + chip->sample_rom_mask = pow2_mask(memsize); + chip->total_banks = memsize / BSMT2000_ROM_BANKSIZE; + memset(chip->sample_rom, 0, memsize); +} + +static void bsmt2000_write_rom(void *info, UINT32 offset, UINT32 length, const UINT8* data) { + bsmt2000_state* chip = (bsmt2000_state *)info; + if (offset > chip->sample_rom_length) return; + if (offset + length > chip->sample_rom_length) + length = chip->sample_rom_length - offset; + for (UINT32 i = 0; i < length; i++) + chip->sample_rom[offset + i] = data[i]; +} + +/* ==== Mute Mask ==== */ +static void bsmt2000_set_mute_mask(void *info, UINT32 MuteMask) +{ + bsmt2000_state* chip = (bsmt2000_state *)info; + UINT8 CurChn; + for (CurChn = 0; CurChn < BSMT2000_MAX_VOICES; CurChn++) + chip->Muted[CurChn] = (MuteMask >> CurChn) & 0x01; +} + +static UINT32 bsmt2000_get_mute_mask(void *info) +{ + bsmt2000_state* chip = (bsmt2000_state *)info; + UINT32 muteMask = 0; + UINT8 CurChn; + for (CurChn = 0; CurChn < BSMT2000_MAX_VOICES; CurChn++) + muteMask |= (chip->Muted[CurChn] << CurChn); + return muteMask; +} + +/* ==== Logging ==== */ +static void bsmt2000_set_log_cb(void* info, DEVCB_LOG func, void* param) +{ + bsmt2000_state* chip = (bsmt2000_state *)info; + dev_logger_set(&chip->logger, chip, func, param); +} + +/* ==== Interpolation Macro ==== */ +#define INTERPOLATE(s1,s2,frac) (((s1) * (0x10000 - ((frac)&0xffff)) + (s2) * ((frac)&0xffff)) >> 16) + +/* ==== Sound Update ==== */ +static void bsmt2000_update(void *param, UINT32 samples, DEV_SMPL **outputs) +{ + bsmt2000_state *chip = (bsmt2000_state *)param; + INT64 left[BSMT2000_SAMPLE_CHUNK], right[BSMT2000_SAMPLE_CHUNK]; + bsmt2000_voice *voice; + int samp, v, length = MIN(samples, BSMT2000_SAMPLE_CHUNK); + + if (!chip->sample_rom || !chip->sample_rom_length) + { + memset(outputs[0], 0, samples * sizeof(*outputs[0])); + memset(outputs[1], 0, samples * sizeof(*outputs[1])); + return; + } + memset(left, 0, length * sizeof(left[0])); + memset(right, 0, length * sizeof(right[0])); + + // PCM voices + for (v = 0; v < chip->voices; v++) + { + if (chip->Muted[v]) + continue; + voice = &chip->voice[v]; + if (voice->reg[BSMT2000_REG_BANK] >= chip->total_banks) // REG_BANK + continue; + UINT8 *base = chip->sample_rom + voice->reg[BSMT2000_REG_BANK] * BSMT2000_ROM_BANKSIZE; + UINT32 rate = voice->adjusted_rate; + UINT32 pos = voice->position; + INT32 rvol = voice->reg[BSMT2000_REG_RIGHTVOL]; // REG_RIGHTVOL + INT32 lvol = chip->stereo ? voice->reg[BSMT2000_REG_LEFTVOL] : rvol; // REG_LEFTVOL + if (chip->stereo && !chip->right_volume_set) + rvol = lvol; + int remaining = length; + + while (remaining--) { + INT32 idx = pos >> 16; + INT32 s1 = ((INT8)base[idx] << 8); + INT32 s2 = ((INT8)base[idx+1] << 8); + INT32 sample = (s1 * (INT32)(0x10000 - (pos & 0xffff)) + (s2 * (INT32)(pos & 0xffff))) >> 16; + left[length-remaining-1] += sample * lvol; + right[length-remaining-1] += sample * rvol; + pos += rate; + if (pos >= voice->loop_stop_position) + pos += voice->loop_start_position - voice->loop_stop_position; + } + voice->position = pos; + } + + // ADPCM/compressed voice (11-voice model only) + if (chip->adpcm != 0 && !chip->Muted[BSMT2000_ADPCM_INDEX]) + { + voice = &chip->voice[BSMT2000_ADPCM_INDEX]; + if (voice->reg[BSMT2000_REG_BANK] < chip->total_banks) + { + UINT8 *base = chip->sample_rom + voice->reg[BSMT2000_REG_BANK] * BSMT2000_ROM_BANKSIZE; + UINT32 rate = 0x02aa << 4; + UINT32 pos = voice->position; + INT32 rvol = voice->reg[BSMT2000_REG_RIGHTVOL]; + INT32 lvol = chip->stereo ? voice->reg[BSMT2000_REG_LEFTVOL] : rvol; + if (chip->stereo && !chip->right_volume_set) + rvol = lvol; + int remaining = length; + + while (remaining-- && pos < voice->loop_stop_position) + { + UINT32 oldpos = pos; + left[length-remaining-1] += chip->adpcm_current * (lvol * 2); + right[length-remaining-1] += chip->adpcm_current * (rvol * 2); + + pos += rate; + if ((oldpos ^ pos) & 0x8000) + { + static const UINT8 delta_tab[16] = { 154, 154, 128, 102, 77, 58, 58, 58, 58, 58, 58, 58, 77, 102, 128, 154 }; + int nibble = base[oldpos >> 16] >> ((~oldpos >> 13) & 4); + INT32 value = (nibble & 0xF) | ((nibble & 0x8) ? ~0xF : 0); + int temp; + + temp = chip->adpcm_delta_n * value; + if (value > 0) + temp += chip->adpcm_delta_n >> 1; + else + temp -= chip->adpcm_delta_n >> 1; + + chip->adpcm_current += temp; + if (chip->adpcm_current > 32767) + chip->adpcm_current = 32767; + else if (chip->adpcm_current < -32768) + chip->adpcm_current = -32768; + + chip->adpcm_delta_n = (chip->adpcm_delta_n * delta_tab[value + 8]) >> 6; + if (chip->adpcm_delta_n > 2000) + chip->adpcm_delta_n = 2000; + else if (chip->adpcm_delta_n < 1) + chip->adpcm_delta_n = 1; + } + } + voice->position = pos; + } + } + + // Output clamp and write + for (samp = 0; samp < length; samp++) { + INT64 l = left[samp] >> 16; + INT64 r = right[samp] >> 16; + l = (l > 32767) ? 32767 : (l < -32768) ? -32768 : l; + r = (r > 32767) ? 32767 : (r < -32768) ? -32768 : r; + outputs[0][samp] = l; + outputs[1][samp] = r; + } +} + +static void bsmt2000_set_srchg_cb(void *info, DEVCB_SRATE_CHG CallbackFunc, void* DataPtr) +{ + bsmt2000_state *chip = (bsmt2000_state *)info; + + // set Sample Rate Change Callback routine + chip->SmpRateFunc = CallbackFunc; + chip->SmpRateData = DataPtr; + + return; +} diff --git a/emu/cores/bsmt2000.h b/emu/cores/bsmt2000.h new file mode 100644 index 00000000..c87b37e1 --- /dev/null +++ b/emu/cores/bsmt2000.h @@ -0,0 +1,8 @@ +#ifndef __BSMT2000_H__ +#define __BSMT2000_H__ + +#include "../EmuStructs.h" + +extern const DEV_DECL sndDev_BSMT2000; + +#endif // __BSMT2000_H__ \ No newline at end of file diff --git a/emu/dac_control.c b/emu/dac_control.c index 6be67e16..f552bf0c 100644 --- a/emu/dac_control.c +++ b/emu/dac_control.c @@ -255,6 +255,7 @@ INLINE void daccontrol_SendCommand(dac_control* chip) } break; case DEVID_QSOUND: // 8-bit Register, 16-bit Data + case DEVID_BSMT2000: if (chip->Write.A8D8 == NULL) return; Command = (chip->DstCommand & 0x00FF) >> 0; @@ -426,6 +427,7 @@ void daccontrol_setup_chip(void* info, DEV_INFO* devInf, UINT8 ChType, UINT16 Co case DEVID_32X_PWM: case DEVID_QSOUND: case DEVID_K005289: + case DEVID_BSMT2000: chip->CmdSize = 0x02; break; default: diff --git a/libEmu.vcxproj b/libEmu.vcxproj index 33724064..01efd04a 100644 --- a/libEmu.vcxproj +++ b/libEmu.vcxproj @@ -164,6 +164,7 @@ + @@ -251,6 +252,7 @@ + diff --git a/libEmu.vcxproj.filters b/libEmu.vcxproj.filters index f370bee0..f3b2a625 100644 --- a/libEmu.vcxproj.filters +++ b/libEmu.vcxproj.filters @@ -261,6 +261,9 @@ Quelldateien + + Quelldateien + @@ -560,5 +563,8 @@ Headerdateien + + Headerdateien + diff --git a/player/vgmplayer.cpp b/player/vgmplayer.cpp index 002be7c4..5f00e758 100644 --- a/player/vgmplayer.cpp +++ b/player/vgmplayer.cpp @@ -43,7 +43,7 @@ DEVID_32X_PWM, DEVID_AY8910, DEVID_GB_DMG, DEVID_NES_APU, DEVID_YMW258, DEVID_uPD7759, DEVID_MSM6258, DEVID_MSM6295, DEVID_K051649, DEVID_K054539, DEVID_C6280, DEVID_C140, DEVID_C219, DEVID_K053260, DEVID_POKEY, DEVID_QSOUND, DEVID_SCSP, DEVID_WSWAN, DEVID_VBOY_VSU, DEVID_SAA1099, DEVID_ES5503, DEVID_ES5506, DEVID_X1_010, DEVID_C352, - DEVID_GA20, DEVID_MIKEY, DEVID_K007232, DEVID_K005289, DEVID_MSM5205, DEVID_MSM5232, + DEVID_GA20, DEVID_MIKEY, DEVID_K007232, DEVID_K005289, DEVID_MSM5205, DEVID_MSM5232, DEVID_BSMT2000, }; /*static*/ const DEV_ID VGMPlayer::_DEV_LIST[_CHIP_COUNT] = @@ -53,7 +53,7 @@ DEVID_RF5C68, DEVID_32X_PWM, DEVID_AY8910, DEVID_GB_DMG, DEVID_NES_APU, DEVID_YMW258, DEVID_uPD7759, DEVID_MSM6258, DEVID_MSM6295, DEVID_K051649, DEVID_K054539, DEVID_C6280, DEVID_C140, DEVID_K053260, DEVID_POKEY, DEVID_QSOUND, DEVID_SCSP, DEVID_WSWAN, DEVID_VBOY_VSU, DEVID_SAA1099, DEVID_ES5503, DEVID_ES5506, DEVID_X1_010, DEVID_C352, - DEVID_GA20, DEVID_MIKEY, DEVID_K007232, DEVID_K005289, DEVID_MSM5205, DEVID_MSM5232, + DEVID_GA20, DEVID_MIKEY, DEVID_K007232, DEVID_K005289, DEVID_MSM5205, DEVID_MSM5232, DEVID_BSMT2000, }; /*static*/ const UINT32 VGMPlayer::_CHIPCLK_OFS[_CHIP_COUNT] = @@ -71,7 +71,7 @@ 0x80, 0xE0, 0x100, 0xC0, 0x100, 0x40, 0x11E, 0x1C0, 0x100, 0xA0, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x20, 0x100, 0x100, 0x100, 0x40, 0x20, 0x100, 0x40, - 0x280, 0x100, 0x100, 0x100, 0x100, 0x100, + 0x280, 0x100, 0x100, 0x100, 0x100, 0x100, 0x200, }; /*static*/ const UINT16 VGMPlayer::_PB_VOL_AMNT[_CHIP_COUNT] = { 0x100, 0x80, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, @@ -79,7 +79,7 @@ 0x200, 0x100, 0x200, 0x400, 0x200, 0x400, 0x100, 0x200, 0x200, 0x100, 0x100, 0x100, 0x180, 0x100, 0x100, 0x100, 0x800, 0x100, 0x100, 0x100, 0x800, 0x1000, 0x100, 0x800, - 0x100, 0x200, 0x100, 0x100, 0x200, 0x100, + 0x100, 0x200, 0x100, 0x100, 0x200, 0x100, 0x100, }; /*static*/ const char* const VGMPlayer::_TAG_TYPE_LIST[_TAG_COUNT] = @@ -1551,6 +1551,15 @@ void VGMPlayer::InitDevices(void) SndEmu_GetDeviceFunc(devInf->devDef, RWF_REGISTER | RWF_WRITE, DEVRW_A8D16, 0, (void**)&chipDev.writeD16); SndEmu_GetDeviceFunc(devInf->devDef, RWF_MEMORY | RWF_WRITE, DEVRW_BLOCK, 0, (void**)&chipDev.romWrite); break; + case DEVID_BSMT2000: + retVal = SndEmu_Start2(chipType, devCfg, devInf, _userDevList, _devStartOpts); + if (retVal) + break; + SndEmu_GetDeviceFunc(devInf->devDef, RWF_REGISTER | RWF_WRITE, DEVRW_A8D8, 0, (void**)&chipDev.write8); + SndEmu_GetDeviceFunc(devInf->devDef, RWF_REGISTER | RWF_QUICKWRITE, DEVRW_A8D16, 0, (void**)&chipDev.writeD16); + SndEmu_GetDeviceFunc(devInf->devDef, RWF_MEMORY | RWF_WRITE, DEVRW_MEMSIZE, 0, (void**)&chipDev.romSize); + SndEmu_GetDeviceFunc(devInf->devDef, RWF_MEMORY | RWF_WRITE, DEVRW_BLOCK, 0, (void**)&chipDev.romWrite); + break; default: if (chipType == DEVID_YM2612) chipDev.flags |= devCfg->flags; diff --git a/player/vgmplayer.hpp b/player/vgmplayer.hpp index 71755244..fd6f90a2 100644 --- a/player/vgmplayer.hpp +++ b/player/vgmplayer.hpp @@ -271,7 +271,9 @@ class VGMPlayer : public PlayerBase void Cmd_OKIM6295_Reg(void); // command B8 - OKIM6295 register write (Ofs8_Data8 with minor fixes) void Cmd_K007232_Reg(void); // command 41 - K007232 register write (Ofs8_Data8 with minor fixes) void Cmd_AY_Stereo(void); // command 30 - set AY8910 stereo mask - + void Cmd_BSMT2000_Reg(void); // command C9 - BSMT2000 register write (16-bit data, 8-bit offset) + void Cmd_BSMT2000_Mode(void); // command 32 - BSMT2000 mode change (8-bit data) + CPCONV* _cpcUTF16; // UTF-16 LE -> UTF-8 codepage conversion DEV_LOGGER _logger; DATA_LOADER *_dLoad; @@ -282,8 +284,8 @@ class VGMPlayer : public PlayerBase enum { _HDR_BUF_SIZE = 0x100, - _OPT_DEV_COUNT = 0x2e, - _CHIP_COUNT = 0x2e, + _OPT_DEV_COUNT = 0x2f, + _CHIP_COUNT = 0x2f, _PCM_BANK_COUNT = 0x40 }; diff --git a/player/vgmplayer_cmdhandler.cpp b/player/vgmplayer_cmdhandler.cpp index aad0f718..7d09d95e 100644 --- a/player/vgmplayer_cmdhandler.cpp +++ b/player/vgmplayer_cmdhandler.cpp @@ -71,7 +71,7 @@ {0x00, 0x02, &VGMPlayer::Cmd_SN76489}, // 30 SN76489 register write (2nd chip) {0xFF, 0x02, &VGMPlayer::Cmd_AY_Stereo}, // 31 AY8910 stereo mask [chip type depends on data] {0x2C, 0x02, &VGMPlayer::Cmd_MSM5205_Reg}, // 32 MSM5205 register write - {0xFF, 0x02, &VGMPlayer::Cmd_unknown}, // 33 + {0x2F, 0x02, &VGMPlayer::Cmd_BSMT2000_Mode}, // 33 BSMT2000 change mode {0xFF, 0x02, &VGMPlayer::Cmd_unknown}, // 34 {0xFF, 0x02, &VGMPlayer::Cmd_unknown}, // 35 {0xFF, 0x02, &VGMPlayer::Cmd_unknown}, // 36 @@ -221,7 +221,7 @@ {0x21, 0x04, &VGMPlayer::Cmd_Ofs16_Data8}, // C6 WonderSwan memory write {0x22, 0x04, &VGMPlayer::Cmd_Ofs16_Data8}, // C7 VSU-VUE (Virtual Boy) register write {0x26, 0x04, &VGMPlayer::Cmd_Ofs16_Data8}, // C8 X1-010 register write - {0xFF, 0x04, &VGMPlayer::Cmd_unknown}, // C9 + {0x2F, 0x04, &VGMPlayer::Cmd_BSMT2000_Reg}, // C9 BSMT2000 register write {0xFF, 0x04, &VGMPlayer::Cmd_unknown}, // CA {0xFF, 0x04, &VGMPlayer::Cmd_unknown}, // CB {0xFF, 0x04, &VGMPlayer::Cmd_unknown}, // CC @@ -370,7 +370,7 @@ {0x27, 0}, // 92 C352 {0x28, 0}, // 93 GA20 {0x2A, 0}, // 94 K007232 - {0xFF, 0}, // 95 + {0x2F, 0}, // 95 BSMT2000 {0xFF, 0}, // 96 {0xFF, 0}, // 97 {0xFF, 0}, // 98 @@ -1404,3 +1404,27 @@ void VGMPlayer::Cmd_AY_Stereo(void) writeStMask(cDev->base.defInf.dataPtr, fData[0x01] & 0x3F); return; } + +void VGMPlayer::Cmd_BSMT2000_Reg(void) +{ + UINT8 chipType = _CMD_INFO[fData[0x00]].chipType; + UINT8 chipID = (fData[0x01] & 0x80) >> 7; + CHIP_DEVICE* cDev = GetDevicePtr(chipType, chipID); + if (cDev == NULL || cDev->write8 == NULL) + return; + + WriteQSound_B(cDev, fData[0x01] & 0x7f, ReadBE16(&fData[0x02])); + return; +} + +void VGMPlayer::Cmd_BSMT2000_Mode(void) +{ + UINT8 chipType = _CMD_INFO[fData[0x00]].chipType; + UINT8 chipID = (fData[0x01] & 0x80) >> 7; + CHIP_DEVICE* cDev = GetDevicePtr(chipType, chipID); + if (cDev == NULL || cDev->write8 == NULL) + return; + + cDev->write8(cDev->base.defInf.dataPtr, 0x03, fData[0x01] & 0x7f); + return; +} \ No newline at end of file