From ec64d16aad822f95d55017f74bf9a48fe636bed9 Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Thu, 12 Mar 2026 04:31:04 +0700 Subject: [PATCH 1/5] fix ymfm register writes, use linear OPL3 output resampling --- sw/opl/opl_ymfm.cpp | 122 +++++++++++++++++++++++++------------------- sw/sbplay.cpp | 18 +++++-- 2 files changed, 84 insertions(+), 56 deletions(-) diff --git a/sw/opl/opl_ymfm.cpp b/sw/opl/opl_ymfm.cpp index 826a1a1..e8b9a01 100644 --- a/sw/opl/opl_ymfm.cpp +++ b/sw/opl/opl_ymfm.cpp @@ -1,23 +1,23 @@ -/* - * opl_ymfm.cpp — OPL_Pico_* API implementation using ymfm - * - * Chip selection (compile-time, via CMake define): - * USE_YMF3812 — ymfm::ym3812 (OPL2): 9 ch / 18 op / 1 output - * Status 0x06 → games detect OPL2. Mono output. - * (default) — ymfm::ymf262 (OPL3): 18 ch / 36 op / 4 outputs - * Status 0x00 → games detect OPL3. True stereo output. - * - * ymf262 output layout (data[0..3]): - * data[0] = L1, data[1] = R1, data[2] = L2, data[3] = R2 - * Per-channel panning (reg 0xC0 bits 4-7) routes each channel to any - * combination of the four outputs. Stereo: L = data[0]+data[2], - * R = data[1]+data[3]. Mono: sum all four, shift right 2. - * - * Port read behavior: - * Bank 1 (port+0) and Bank 2 (port+2) both return the status register. - * OPL3 detection: port+2 returns valid status (not 0xFF), and status - * base is 0x00 (vs 0x06 for OPL2). - */ +/* + * opl_ymfm.cpp — OPL_Pico_* API implementation using ymfm + * + * Chip selection (compile-time, via CMake define): + * USE_YMF3812 — ymfm::ym3812 (OPL2): 9 ch / 18 op / 1 output + * Status 0x06 → games detect OPL2. Mono output. + * (default) — ymfm::ymf262 (OPL3): 18 ch / 36 op / 4 outputs + * Status 0x00 → games detect OPL3. True stereo output. + * + * ymf262 output layout (data[0..3]): + * data[0] = L1, data[1] = R1, data[2] = L2, data[3] = R2 + * Per-channel panning (reg 0xC0 bits 4-7) routes each channel to any + * combination of the four outputs. Stereo: L = data[0]+data[2], + * R = data[1]+data[3]. Mono: sum all four, shift right 2. + * + * Port read behavior: + * Bank 1 (port+0) and Bank 2 (port+2) both return the status register. + * OPL3 detection: port+2 returns valid status (not 0xFF), and status + * base is 0x00 (vs 0x06 for OPL2). + */ #include "opl.h" #include "hardware/sync.h" @@ -25,6 +25,11 @@ #include "ymfm/src/ymfm_opl.h" +#if OPL_CMD_BUFFER +#include "include/cmd_buffers.h" +extern cms_buffer_t opl_cmd_buffer; +#endif + // --------------------------------------------------------------------------- // Chip selection // --------------------------------------------------------------------------- @@ -74,24 +79,35 @@ static void OPLTimer_CalculateEndTime(opl_timer_t *timer) // Stereo L/R buffers used by both OPL_Pico_simple and OPL_Pico_stereo. // --------------------------------------------------------------------------- static constexpr uint32_t PREBUF_SIZE = 128; -static opl_chip_t::output_data s_gen_buf[PREBUF_SIZE]; +static opl_chip_t::output_data s_gen_buf; static int32_t s_prebuf_l[PREBUF_SIZE]; static int32_t s_prebuf_r[PREBUF_SIZE]; static uint32_t s_prebuf_head = PREBUF_SIZE; // starts empty → triggers fill on first call static void refill_prebuf() { - s_chip.generate(s_gen_buf, PREBUF_SIZE); for (uint32_t j = 0; j < PREBUF_SIZE; j++) { +#if OPL_CMD_BUFFER + // Process any pending OPL commands + while (opl_cmd_buffer.tail != opl_cmd_buffer.head) { + auto cmd = opl_cmd_buffer.cmds[opl_cmd_buffer.tail]; + OPL_Pico_WriteRegister(cmd.addr, cmd.data); + ++opl_cmd_buffer.tail; + if ((cmd.addr < 0x20) || ((cmd.addr & 0xF0) == 0xB0)) break; // break on key on/off retrigs and misc registers + break; + } +#endif + + s_chip.generate(&s_gen_buf, 1); #ifdef USE_YMF3812 // ym3812 is mono — duplicate to both channels - s_prebuf_l[j] = s_prebuf_r[j] = s_gen_buf[j].data[0]; + s_prebuf_l[j] = s_prebuf_r[j] = s_gen_buf.data[0]; #else // ymf262: use L1 (data[0]) and R1 (data[1]) — the standard stereo pair. // data[2]/data[3] are the rarely-used L2/R2 extended outputs, ignored. - s_prebuf_l[j] = s_gen_buf[j].data[0]; - s_prebuf_r[j] = s_gen_buf[j].data[1]; + s_prebuf_l[j] = s_gen_buf.data[0]; + s_prebuf_r[j] = s_gen_buf.data[1]; #endif } s_prebuf_head = 0; @@ -108,23 +124,23 @@ int OPL_Pico_Init(unsigned int port_base) return 1; } -unsigned int OPL_Pico_PortRead(opl_port_t port) -{ - unsigned int result = OPL_STATUS_BASE; - __dmb(); - uint64_t pico_time = time_us_64(); - - if (timer1.enabled && pico_time > timer1.expire_time) - { - result |= 0x80 | 0x40; - } - if (timer2.enabled && pico_time > timer2.expire_time) - { - result |= 0x80 | 0x20; - } - - return result; -} +unsigned int OPL_Pico_PortRead(opl_port_t port) +{ + unsigned int result = OPL_STATUS_BASE; + __dmb(); + uint64_t pico_time = time_us_64(); + + if (timer1.enabled && pico_time > timer1.expire_time) + { + result |= 0x80 | 0x40; + } + if (timer2.enabled && pico_time > timer2.expire_time) + { + result |= 0x80 | 0x20; + } + + return result; +} void OPL_Pico_WriteRegister(unsigned int reg_num, unsigned int value) { @@ -161,17 +177,17 @@ void OPL_Pico_WriteRegister(unsigned int reg_num, unsigned int value) } break; - default: -#ifndef USE_YMF3812 - if (reg_num >= 0x100) - s_chip.write_address_hi(reg_num & 0xFF); - else - s_chip.write_address(reg_num); -#else - s_chip.write_address(reg_num & 0xFF); -#endif - s_chip.write_data(value & 0xFF); - break; + default: +#ifndef USE_YMF3812 + if (reg_num >= 0x100) + s_chip.write_address_hi(reg_num & 0xFF); + else + s_chip.write_address(reg_num); +#else + s_chip.write_address(reg_num & 0xFF); +#endif + s_chip.write_data(value & 0xFF); + break; } } diff --git a/sw/sbplay.cpp b/sw/sbplay.cpp index e261f7f..d51ab2f 100644 --- a/sw/sbplay.cpp +++ b/sw/sbplay.cpp @@ -103,6 +103,7 @@ static audio_fifo_t opl_out_fifo; // For emu8950 (pg-adlib), OPL_Pico_stereo doesn't exist — fall back to // OPL_Pico_simple and duplicate to both channels. static int32_t opl_resamp_l = 0, opl_resamp_r = 0; +static int32_t opl_resamp_buf_l[2] = {0}, opl_resamp_buf_r[2] = {0}; static uint32_t opl_resamp_phase = 0; // Q16.16 phase accumulator static void opl_resample_tick() @@ -110,18 +111,27 @@ static void opl_resample_tick() opl_resamp_phase += opl_ratio; while (opl_resamp_phase >= (1u << 16)) { + opl_resamp_buf_l[1] = opl_resamp_buf_l[0]; + opl_resamp_buf_r[1] = opl_resamp_buf_r[0]; #if defined(USE_YMFM_OPL) || defined(USE_YMF3812) int32_t l, r; OPL_Pico_stereo(&l, &r, 1); - opl_resamp_l = scale_sample(l, opl_volume, 1); - opl_resamp_r = scale_sample(r, opl_volume, 1); + opl_resamp_buf_l[0] = scale_sample(l, opl_volume, 1); + opl_resamp_buf_r[0] = scale_sample(r, opl_volume, 1); #else int32_t mono; OPL_Pico_simple(&mono, 1); - opl_resamp_l = opl_resamp_r = scale_sample(mono, opl_volume, 1); + opl_resamp_buf_l[0] = opl_resamp_buf_r[0] = scale_sample(mono, opl_volume, 1); #endif opl_resamp_phase -= (1u << 16); } + { + // linear resampler - TODO adapt polyphase code from resampler/resampler.hpp + int32_t phase_frac = opl_resamp_phase & ((1u << 16) - 1); + opl_resamp_l = ((opl_resamp_buf_l[0] * phase_frac) + (opl_resamp_buf_l[1] * ((1 << 16) - phase_frac))) >> 16; + opl_resamp_r = ((opl_resamp_buf_r[0] * phase_frac) + (opl_resamp_buf_r[1] * ((1 << 16) - phase_frac))) >> 16; + } + } // Setup values for audio sample clock @@ -238,6 +248,7 @@ void play_adlib() { cdrom_audio_callback(&cdrom, AUDIO_FIFO_SIZE - SAMPLES_PER_SECTOR); #endif +#if 0 #if OPL_CMD_BUFFER // Process any pending OPL commands while (opl_cmd_buffer.tail != opl_cmd_buffer.head) { @@ -245,6 +256,7 @@ void play_adlib() { OPL_Pico_WriteRegister(cmd.addr, cmd.data); ++opl_cmd_buffer.tail; } +#endif #endif // Generate OPL stereo pairs and add to output FIFO (interleaved L, R). From e0996a10cccaf0d3719a5b5815e4540351d9302d Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Sat, 14 Mar 2026 01:29:49 +0700 Subject: [PATCH 2/5] add DOSBox OPL3 core --- sw/CMakeLists.txt | 62 ++ sw/opl/CMakeLists.txt | 13 + sw/opl/dbopl/dbopl.cpp | 1616 +++++++++++++++++++++++++++++++++++++++ sw/opl/dbopl/dbopl.h | 257 +++++++ sw/opl/dbopl/tables.ipp | 287 +++++++ sw/opl/opl_dbopl.cpp | 179 +++++ sw/opl/opl_ymfm.cpp | 25 +- 7 files changed, 2429 insertions(+), 10 deletions(-) create mode 100644 sw/opl/dbopl/dbopl.cpp create mode 100644 sw/opl/dbopl/dbopl.h create mode 100644 sw/opl/dbopl/tables.ipp create mode 100644 sw/opl/opl_dbopl.cpp diff --git a/sw/CMakeLists.txt b/sw/CMakeLists.txt index bc4c829..a7421e7 100644 --- a/sw/CMakeLists.txt +++ b/sw/CMakeLists.txt @@ -182,6 +182,14 @@ function(config_target TARGET_NAME MULTIFW) OPL_CMD_BUFFER=1 ) target_link_libraries(${TARGET_NAME} opl_ymfm) + elseif(USE_DBOPL_OPL) + # DOSBox OPL emulation + target_compile_definitions(${TARGET_NAME} PRIVATE + SOUND_OPL=1 + USE_DBOPL_OPL=1 + OPL_CMD_BUFFER=1 + ) + target_link_libraries(${TARGET_NAME} opl_dbopl) else() # Default: emu8950 (OPL2, assembly-optimized for RP2040) target_compile_definitions(${TARGET_NAME} PRIVATE @@ -327,6 +335,35 @@ function(build_sb_ymf262 TARGET_NAME MULTIFW) pico_generate_pio_header(${TARGET_NAME} ${CMAKE_CURRENT_LIST_DIR}/isa/isa_dma.pio) endfunction() +################################################################################ +# Build SB firmware — DOSBox OPL3 core +function(build_sb_dbopl3 TARGET_NAME MULTIFW) + set(USE_DBOPL_OPL TRUE) + set(USB_JOYSTICK TRUE) + set(SOUND_MPU FALSE) + set(SOUND_OPL TRUE) + set(CDROM TRUE) + config_target(${TARGET_NAME} ${MULTIFW}) + pico_set_program_name(${TARGET_NAME} "picogus-sb-dbopl3") + target_sources(${TARGET_NAME} PRIVATE + audio/volctrl.cpp + sbdsp/sbdsp.cpp + sbplay.cpp + isa/isa_dma.c + audio/audio_fifo.c + audio/audio_i2s_minimal.c + ) + target_compile_definitions(${TARGET_NAME} PRIVATE + SOUND_SB=1 + SOUND_DSP=1 + SB_BUFFERLESS=1 + SB_BUFFERLESS_NG=1 + USE_CD_AUDIO_FIFO=1 + ) + target_link_libraries(${TARGET_NAME} resampler) + pico_generate_pio_header(${TARGET_NAME} ${CMAKE_CURRENT_LIST_DIR}/isa/isa_dma.pio) +endfunction() + ################################################################################ # Build AdLib firmware function(build_adlib TARGET_NAME MULTIFW) @@ -362,6 +399,24 @@ function(build_adlib_ymf262 TARGET_NAME MULTIFW) target_link_libraries(${TARGET_NAME} resampler) endfunction() +################################################################################ +# Build AdLib firmware — DOSBox OPL3 core +function(build_adlib_dbopl3 TARGET_NAME MULTIFW) + set(USE_DBOPL3_OPL TRUE) + set(USB_JOYSTICK TRUE) + set(USB_MOUSE TRUE) + set(SOUND_MPU TRUE) + set(SOUND_OPL TRUE) + config_target(${TARGET_NAME} ${MULTIFW}) + pico_set_program_name(${TARGET_NAME} "picogus-adlib-dbopl3") + target_sources(${TARGET_NAME} PRIVATE + audio/volctrl.cpp + sbplay.cpp + audio/audio_i2s_minimal.c + ) + target_link_libraries(${TARGET_NAME} resampler) +endfunction() + ################################################################################ # Build AdLib firmware — ymfm OPL2 (ym3812) variant function(build_adlib_ym3812 TARGET_NAME MULTIFW) @@ -493,6 +548,7 @@ if(PROJECT_TYPE STREQUAL "MULTIFW") # set(MULTIFW_OPL_VARIANT "emu8950") # original emu8950 — OPL2, assembly-optimized # set(MULTIFW_OPL_VARIANT "ym3812") # ymfm ym3812 — OPL2, ymfm quality set(MULTIFW_OPL_VARIANT "ymf262") # ymfm ymf262 — OPL3, true stereo + #set(MULTIFW_OPL_VARIANT "dbopl") # DOSBox OPL — OPL3, true stereo # --------------------------------------------------------------------------- if(MULTIFW_OPL_VARIANT STREQUAL "ymf262") @@ -627,6 +683,9 @@ elseif(PROJECT_TYPE STREQUAL "SB_YMF262") elseif(PROJECT_TYPE STREQUAL "SB_YM3812") set(FW_TARGET pg-sb-ym3812) build_sb_ym3812(pg-sb-ym3812 FALSE) +elseif(PROJECT_TYPE STREQUAL "SB_DBOPL3") + set(FW_TARGET pg-sb-dbopl3) + build_sb_dbopl3(pg-sb-dbopl3 FALSE) elseif(PROJECT_TYPE STREQUAL "ADLIB") set(FW_TARGET pg-adlib) build_adlib(pg-adlib FALSE) @@ -639,6 +698,9 @@ elseif(PROJECT_TYPE STREQUAL "ADLIB_YMF262") elseif(PROJECT_TYPE STREQUAL "ADLIB_YM3812") set(FW_TARGET pg-adlib-ym3812) build_adlib_ym3812(pg-adlib-ym3812 FALSE) +elseif(PROJECT_TYPE STREQUAL "ADLIB_DBOPL3") + set(FW_TARGET pg-adlib-dbopl3) + build_adlib_dbopl3(pg-adlib-dbopl3 FALSE) elseif(PROJECT_TYPE STREQUAL "MPU") set(FW_TARGET pg-mpu) build_mpu(pg-mpu FALSE) diff --git a/sw/opl/CMakeLists.txt b/sw/opl/CMakeLists.txt index 501e77e..d06811f 100644 --- a/sw/opl/CMakeLists.txt +++ b/sw/opl/CMakeLists.txt @@ -49,3 +49,16 @@ target_include_directories(opl_ym3812 INTERFACE target_compile_definitions(opl_ym3812 INTERFACE USE_YMF3812=1) target_compile_options(opl_ym3812 INTERFACE -O3 -Wno-stringop-overflow) target_link_libraries(opl_ym3812 INTERFACE pico_audio_i2s hardware_gpio hardware_timer) + +# DOSBox OPL3 backend +add_library(opl_dbopl INTERFACE) +target_sources(opl_dbopl INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/opl_dbopl.cpp + ${CMAKE_CURRENT_LIST_DIR}/dbopl/dbopl.cpp +) +target_include_directories(opl_dbopl INTERFACE + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/dbopl +) +target_compile_options(opl_dbopl INTERFACE -O3 -Wno-stringop-overflow) +target_link_libraries(opl_dbopl INTERFACE pico_audio_i2s hardware_gpio hardware_timer) diff --git a/sw/opl/dbopl/dbopl.cpp b/sw/opl/dbopl/dbopl.cpp new file mode 100644 index 0000000..f0c1835 --- /dev/null +++ b/sw/opl/dbopl/dbopl.cpp @@ -0,0 +1,1616 @@ +/* + * Copyright (C) 2002-2021 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator. + Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2 + Except for the table generation it's all integer math + Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms + The generation was based on the MAME implementation but tried to have it use less memory and be faster in general + MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times + + //TODO Don't delay first operator 1 sample in opl3 mode + //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter + //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though? + //TODO Check if having the same accuracy in all frequency multipliers sounds better or not + + //DUNNO Keyon in 4op, switch to 2op without keyoff. +*/ + + +#include +#include +#include +#include "dbopl.h" + +#ifdef DBOPL_DUMP_TABLES +#include +#endif + +#define GCC_UNLIKELY(x) (x) +#define INLINE inline +//#define INLINE __forceinline + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +namespace DBOPL { + +#define OPLRATE ((double)(14318180.0 / 288.0)) +#define TREMOLO_TABLE 52 + +//Try to use most precision for frequencies +//Else try to keep different waves in synch +//#define WAVE_PRECISION 1 +#ifndef WAVE_PRECISION +//Wave bits available in the top of the 32bit range +//Original adlib uses 10.10, we use 10.22 +#define WAVE_BITS 10 +#else +//Need some extra bits at the top to have room for octaves and frequency multiplier +//We support to 8 times lower rate +//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits +#define WAVE_BITS 14 +#endif +#define WAVE_SH ( 32 - WAVE_BITS ) +#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 ) + +//Use the same accuracy as the waves +#define LFO_SH ( WAVE_SH - 10 ) +//LFO is controlled by our tremolo 256 sample limit +#define LFO_MAX ( 256 << ( LFO_SH ) ) + + +//Maximum amount of attenuation bits +//Envelope goes to 511, 9 bits +#if (DBOPL_WAVE == WAVE_TABLEMUL ) +//Uses the value directly +#define ENV_BITS ( 9 ) +#else +//Add 3 bits here for more accuracy and would have to be shifted up either way +#define ENV_BITS ( 9 ) +#endif +//Limits of the envelope with those bits and when the envelope goes silent +#define ENV_MIN 0 +#define ENV_EXTRA ( ENV_BITS - 9 ) +#define ENV_MAX ( 511 << ENV_EXTRA ) +#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) ) +#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT ) + +//Attack/decay/release rate counter shift +#define RATE_SH 24 +#define RATE_MASK ( ( 1 << RATE_SH ) - 1 ) +//Has to fit within 16bit lookuptable +#define MUL_SH 16 + +//Check some ranges +#if ENV_EXTRA > 3 +#error Too many envelope bits +#endif + + +//How much to subtract from the base value for the final attenuation +static const uint8_t KslCreateTable[16] = { + //0 will always be lower than 7 * 8 + 64, 32, 24, 19, + 16, 12, 11, 10, + 8, 6, 5, 4, + 3, 2, 1, 0, +}; + +#define M(_X_) ((uint8_t)( (_X_) * 2)) +static const uint8_t FreqCreateTable[16] = { + M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ), + M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15) +}; +#undef M + +//We're not including the highest attack rate, that gets a special value +static const uint8_t AttackSamplesTable[13] = { + 69, 55, 46, 40, + 35, 29, 23, 20, + 19, 15, 11, 10, + 9 +}; +//On a real opl these values take 8 samples to reach and are based upon larger tables +static const uint8_t EnvelopeIncreaseTable[13] = { + 4, 5, 6, 7, + 8, 10, 12, 14, + 16, 20, 24, 28, + 32, +}; + +#ifdef DBOPL_USE_PRECALCUALTED_TABLES +#include "tables.ipp" +#else + +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) +static uint16_t ExpTable[ 256 ]; +#endif + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +//PI table used by WAVEHANDLER +static uint16_t SinTable[ 512 ]; +#endif + +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) +static uint16_t MulTable[ 384 ]; +#endif + +#if ( DBOPL_WAVE > WAVE_HANDLER ) +//Layout of the waveform table in 512 entry intervals +//With overlapping waves we reduce the table to half its size + +// | |//\\|____|WAV7|//__|/\ |____|/\/\| +// |\\//| | |WAV7| | \/| | | +// |06 |0126|17 |7 |3 |4 |4 5 |5 | + +//6 is just 0 shifted and masked +static int16_t WaveTable[ 8 * 512 ]; +#endif +#endif + +#if ( DBOPL_WAVE > WAVE_HANDLER ) +//Distance into WaveTable the wave starts +static const uint16_t WaveBaseTable[8] = { + 0x000, 0x200, 0x200, 0x800, + 0xa00, 0xc00, 0x100, 0x400, + +}; +//Mask the counter with this +static const uint16_t WaveMaskTable[8] = { + 1023, 1023, 511, 511, + 1023, 1023, 512, 1023, +}; + +//Where to start the counter on at keyon +static const uint16_t WaveStartTable[8] = { + 512, 0, 0, 0, + 0, 512, 512, 256, +}; +#endif + +static uint8_t KslTable[ 8 * 16 ]; +static uint8_t TremoloTable[ TREMOLO_TABLE ]; +//Start of a channel behind the chip struct start +static uint16_t ChanOffsetTable[32]; +//Start of an operator behind the chip struct start +static uint16_t OpOffsetTable[64]; + +//The lower bits are the shift of the operator vibrato value +//The highest bit is right shifted to generate -1 or 0 for negation +//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0 +static const int8_t VibratoTable[ 8 ] = { + 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00, + 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80 +}; + +//Shift strength for the ksl value determined by ksl strength +static const uint8_t KslShiftTable[4] = { + 31,1,2,0 +}; + +//Generate a table index and table shift value using input value from a selected rate +static void EnvelopeSelect( uint8_t val, uint8_t& index, uint8_t& shift ) { + if ( val < 13 * 4 ) { //Rate 0 - 12 + shift = 12 - ( val >> 2 ); + index = val & 3; + } else if ( val < 15 * 4 ) { //rate 13 - 14 + shift = 0; + index = val - 12 * 4; + } else { //rate 15 and up + shift = 0; + index = 12; + } +} + +#if ( DBOPL_WAVE == WAVE_HANDLER ) +/* + Generate the different waveforms out of the sine/exponential table using handlers +*/ +static /*inline*/ Bits MakeVolume( Bitu wave, Bitu volume ) { + Bitu total = wave + volume; + Bitu index = total & 0xff; + Bitu sig = ExpTable[ index ]; + Bitu exp = total >> 8; +#if 0 + //Check if we overflow the 31 shift limit + if ( exp >= 32 ) { + LOG_MSG( "WTF %d %d", total, exp ); + } +#endif + return (sig >> exp); +}; + +static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) { + uint32_t wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 511]; + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) { + Bitu wave = SinTable[i & 255]; + wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return (MakeVolume( wave, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) { + //Twice as fast + i <<= 1; + Bitu wave = SinTable[i & 511]; + wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 ); + return MakeVolume( wave, volume ); +} +static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) { + Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0 + return (MakeVolume( 0, volume ) ^ neg) - neg; +} +static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) { + //Negative is reversed here + Bits neg = (( i >> 9) & 1) - 1; + Bitu wave = (i << 3); + //When negative the volume also runs backwards + wave = ((wave ^ neg) - neg) & 4095; + return (MakeVolume( wave, volume ) ^ neg) - neg; +} + +static const WaveHandler WaveHandlerTable[8] = { + WaveForm0, WaveForm1, WaveForm2, WaveForm3, + WaveForm4, WaveForm5, WaveForm6, WaveForm7 +}; + +#endif + +/* + Operator +*/ + +//We zero out when rate == 0 +/*inline*/ void Operator::UpdateAttack( const Chip* chip ) { + uint8_t rate = reg60 >> 4; + if ( rate ) { + uint8_t val = (rate << 2) + ksr; + attackAdd = chip->attackRates[ val ]; + rateZero &= ~(1 << ATTACK); + } else { + attackAdd = 0; + rateZero |= (1 << ATTACK); + } +} +/*inline*/ void Operator::UpdateDecay( const Chip* chip ) { + uint8_t rate = reg60 & 0xf; + if ( rate ) { + uint8_t val = (rate << 2) + ksr; + decayAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << DECAY); + } else { + decayAdd = 0; + rateZero |= (1 << DECAY); + } +} +/*inline*/ void Operator::UpdateRelease( const Chip* chip ) { + uint8_t rate = reg80 & 0xf; + if ( rate ) { + uint8_t val = (rate << 2) + ksr; + releaseAdd = chip->linearRates[ val ]; + rateZero &= ~(1 << RELEASE); + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero &= ~( 1 << SUSTAIN ); + } + } else { + rateZero |= (1 << RELEASE); + releaseAdd = 0; + if ( !(reg20 & MASK_SUSTAIN ) ) { + rateZero |= ( 1 << SUSTAIN ); + } + } +} + +/*inline*/ void Operator::UpdateAttenuation( ) { + uint8_t kslBase = (uint8_t)((chanData >> SHIFT_KSLBASE) & 0xff); + uint32_t tl = reg40 & 0x3f; + uint8_t kslShift = KslShiftTable[ reg40 >> 6 ]; + //Make sure the attenuation goes to the right bits + totalLevel = (int32_t)(tl << ( ENV_BITS - 7 )); //Total level goes 2 bits below max + totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift; +} + +void Operator::UpdateFrequency( ) { + uint32_t freq = chanData & (( 1 << 10 ) - 1); + uint32_t block = (chanData >> 10) & 0xff; +#ifdef WAVE_PRECISION + block = 7u - block; + waveAdd = ( freq * freqMul ) >> block; +#else + waveAdd = ( freq << block ) * freqMul; +#endif + if ( reg20 & MASK_VIBRATO ) { + vibStrength = (uint8_t)(freq >> 7u); + +#ifdef WAVE_PRECISION + vibrato = ( (Bitu)vibStrength * freqMul ) >> block; +#else + vibrato = ( (Bitu)vibStrength << block ) * freqMul; +#endif + } else { + vibStrength = 0; + vibrato = 0; + } +} + +void Operator::UpdateRates( const Chip* chip ) { + //Mame seems to reverse this where enabling ksr actually lowers + //the rate, but pdf manuals says otherwise? + uint8_t newKsr = (uint8_t)((chanData >> SHIFT_KEYCODE) & 0xff); + if ( !( reg20 & MASK_KSR ) ) { + newKsr >>= 2; + } + if ( ksr == newKsr ) + return; + ksr = newKsr; + UpdateAttack( chip ); + UpdateDecay( chip ); + UpdateRelease( chip ); +} + +INLINE int32_t Operator::RateForward( uint32_t add ) { + rateIndex += add; + int32_t ret = (int32_t)(rateIndex >> RATE_SH); + rateIndex = rateIndex & RATE_MASK; + return ret; +} + +template< Operator::State yes> +Bits Operator::TemplateVolume( ) { + int32_t vol = volume; + int32_t change; + switch ( yes ) { + case OFF: + return ENV_MAX; + case ATTACK: + change = RateForward( attackAdd ); + if ( !change ) + return vol; + vol += ( (~vol) * change ) >> 3; + if ( vol < ENV_MIN ) { + volume = ENV_MIN; + rateIndex = 0; + SetState( DECAY ); + return ENV_MIN; + } + break; + case DECAY: + vol += RateForward( decayAdd ); + if ( GCC_UNLIKELY(vol >= sustainLevel) ) { + //Check if we didn't overshoot max attenuation, then just go off + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + //Continue as sustain + rateIndex = 0; + SetState( SUSTAIN ); + } + break; + case SUSTAIN: + if ( reg20 & MASK_SUSTAIN ) { + return vol; + } + //In sustain phase, but not sustaining, do regular release + /* FALLTHROUGH */ + case RELEASE: + vol += RateForward( releaseAdd ); + if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { + volume = ENV_MAX; + SetState( OFF ); + return ENV_MAX; + } + break; + } + volume = vol; + return vol; +} + +static const VolumeHandler VolumeHandlerTable[5] = { + &Operator::TemplateVolume< Operator::OFF >, + &Operator::TemplateVolume< Operator::RELEASE >, + &Operator::TemplateVolume< Operator::SUSTAIN >, + &Operator::TemplateVolume< Operator::DECAY >, + &Operator::TemplateVolume< Operator::ATTACK > +}; + +INLINE Bitu Operator::ForwardVolume() { + return (Bitu)(currentLevel + (this->*volHandler)()); +} + + +INLINE Bitu Operator::ForwardWave() { + waveIndex += waveCurrent; + return waveIndex >> WAVE_SH; +} + +void Operator::Write20( const Chip* chip, uint8_t val ) { + uint8_t change = (reg20 ^ val ); + if ( !change ) + return; + reg20 = val; + //Shift the tremolo bit over the entire register, saved a branch, YES! + tremoloMask = (int8_t)(val) >> 7; + tremoloMask &= ~(( 1 << ENV_EXTRA ) -1); + //Update specific features based on changes + if ( change & MASK_KSR ) { + UpdateRates( chip ); + } + //With sustain enable the volume doesn't change + if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) { + rateZero |= ( 1 << SUSTAIN ); + } else { + rateZero &= ~( 1 << SUSTAIN ); + } + //Frequency multiplier or vibrato changed + if ( change & (0xf | MASK_VIBRATO) ) { + freqMul = chip->freqMul[ val & 0xf ]; + UpdateFrequency(); + } +} + +void Operator::Write40( const Chip* /*chip*/, uint8_t val ) { + if (!(reg40 ^ val )) + return; + reg40 = val; + UpdateAttenuation( ); +} + +void Operator::Write60( const Chip* chip, uint8_t val ) { + uint8_t change = reg60 ^ val; + reg60 = val; + if ( change & 0x0f ) { + UpdateDecay( chip ); + } + if ( change & 0xf0 ) { + UpdateAttack( chip ); + } +} + +void Operator::Write80( const Chip* chip, uint8_t val ) { + uint8_t change = (reg80 ^ val ); + if ( !change ) + return; + reg80 = val; + uint8_t sustain = val >> 4; + //Turn 0xf into 0x1f + sustain |= ( sustain + 1) & 0x10; + sustainLevel = sustain << ( ENV_BITS - 5 ); + if ( change & 0x0f ) { + UpdateRelease( chip ); + } +} + +void Operator::WriteE0( const Chip* chip, uint8_t val ) { + if ( !(regE0 ^ val) ) + return; + //in opl3 mode you can always select 7 waveforms regardless of waveformselect + const uint8_t waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) ); + regE0 = val; +#if ( DBOPL_WAVE == WAVE_HANDLER ) + waveHandler = WaveHandlerTable[ waveForm ]; +#else + waveBase = WaveTable + WaveBaseTable[ waveForm ]; + waveStart = (Bitu)WaveStartTable[ waveForm ] << WAVE_SH; + waveMask = WaveMaskTable[ waveForm ]; +#endif +} + +INLINE void Operator::SetState( uint8_t s ) { + state = s; + volHandler = VolumeHandlerTable[ s ]; +} + +INLINE bool Operator::Silent() const { + if ( !ENV_SILENT( totalLevel + volume ) ) + return false; + if ( !(rateZero & ( 1 << state ) ) ) + return false; + return true; +} + +INLINE void Operator::Prepare( const Chip* chip ) { + currentLevel = (uint32_t)(totalLevel + (int32_t)(chip->tremoloValue & tremoloMask)); + waveCurrent = waveAdd; + if ( vibStrength >> chip->vibratoShift ) { + int32_t add = (int32_t)(vibrato >> chip->vibratoShift); + //Sign extend over the shift value + int32_t neg = chip->vibratoSign; + //Negate the add with -1 or 0 + add = ( add ^ neg ) - neg; + waveCurrent += (Bitu)add; + } +} + +void Operator::KeyOn( uint8_t mask ) { + if ( !keyOn ) { + //Restart the frequency generator +#if ( DBOPL_WAVE > WAVE_HANDLER ) + waveIndex = waveStart; +#else + waveIndex = 0; +#endif + rateIndex = 0; + SetState( ATTACK ); + } + keyOn |= mask; +} + +void Operator::KeyOff( uint8_t mask ) { + keyOn &= ~mask; + if ( !keyOn ) { + if ( state != OFF ) { + SetState( RELEASE ); + } + } +} + +INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) { +#if ( DBOPL_WAVE == WAVE_HANDLER ) + return waveHandler( index, vol << ( 3 - ENV_EXTRA ) ); +#elif ( DBOPL_WAVE == WAVE_TABLEMUL ) + return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH; +#elif ( DBOPL_WAVE == WAVE_TABLELOG ) + int32_t wave = waveBase[ index & waveMask ]; + uint32_t total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA ); + int32_t sig = ExpTable[ total & 0xff ]; + uint32_t exp = total >> 8; + int32_t neg = wave >> 16; + return ((sig ^ neg) - neg) >> exp; +#else +#error "No valid wave routine" +#endif +} + +Bits INLINE Operator::GetSample( Bits modulation ) { + Bitu vol = ForwardVolume(); + if ( ENV_SILENT( vol ) ) { + //Simply forward the wave + waveIndex += waveCurrent; + return 0; + } else { + Bitu index = ForwardWave(); + index += (unsigned long)modulation; + return GetWave( index, vol ); + } +} + +Operator::Operator() { + chanData = 0; + freqMul = 0; + waveIndex = 0; + waveAdd = 0; + waveCurrent = 0; + keyOn = 0; + ksr = 0; + reg20 = 0; + reg40 = 0; + reg60 = 0; + reg80 = 0; + regE0 = 0; + waveBase = nullptr; + waveMask = 0; + waveStart = 0; + vibrato = 0; + attackAdd = 0; + decayAdd = 0; + rateIndex = 0; + tremoloMask = 0; + vibStrength = 0; + SetState( OFF ); + rateZero = (1 << OFF); + sustainLevel = ENV_MAX; + currentLevel = ENV_MAX; + totalLevel = ENV_MAX; + volume = ENV_MAX; + releaseAdd = 0; +} + +/* + Channel +*/ + +Channel::Channel() { + old[0] = old[1] = 0; + chanData = 0; + regB0 = 0; + regC0 = 0; + maskLeft = -1; + maskRight = -1; + feedback = 31; + fourMask = 0; + synthHandler = &Channel::BlockTemplate< sm2FM >; +} + +void Channel::SetChanData( const Chip* chip, uint32_t data ) { + uint32_t change = chanData ^ data; + chanData = data; + Op( 0 )->chanData = data; + Op( 1 )->chanData = data; + //Since a frequency update triggered this, always update frequency + Op( 0 )->UpdateFrequency(); + Op( 1 )->UpdateFrequency(); + if ( change & ( 0xffu << SHIFT_KSLBASE ) ) { + Op( 0 )->UpdateAttenuation(); + Op( 1 )->UpdateAttenuation(); + } + if ( change & ( 0xffu << SHIFT_KEYCODE ) ) { + Op( 0 )->UpdateRates( chip ); + Op( 1 )->UpdateRates( chip ); + } +} + +void Channel::UpdateFrequency( const Chip* chip, uint8_t fourOp ) { + //Extrace the frequency bits + uint32_t data = chanData & 0xffff; + uint32_t kslBase = KslTable[ data >> 6 ]; + uint32_t keyCode = ( data & 0x1c00) >> 9; + if ( chip->reg08 & 0x40 ) { + keyCode |= ( data & 0x100)>>8; /* notesel == 1 */ + } else { + keyCode |= ( data & 0x200)>>9; /* notesel == 0 */ + } + //Add the keycode and ksl into the highest bits of chanData + data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE ); + ( this + 0 )->SetChanData( chip, data ); + if ( fourOp & 0x3f ) { + ( this + 1 )->SetChanData( chip, data ); + } +} + +void Channel::WriteA0( const Chip* chip, uint8_t val ) { + uint8_t fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + uint32_t change = (chanData ^ val ) & 0xff; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } +} + +void Channel::WriteB0( const Chip* chip, uint8_t val ) { + uint8_t fourOp = chip->reg104 & chip->opl3Active & fourMask; + //Don't handle writes to silent fourop channels + if ( fourOp > 0x80 ) + return; + Bitu change = (chanData ^ ( (unsigned int)val << 8u ) ) & 0x1f00u; + if ( change ) { + chanData ^= change; + UpdateFrequency( chip, fourOp ); + } + //Check for a change in the keyon/off state + if ( !(( val ^ regB0) & 0x20)) + return; + regB0 = val; + if ( val & 0x20 ) { + Op(0)->KeyOn( 0x1 ); + Op(1)->KeyOn( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOn( 1 ); + ( this + 1 )->Op(1)->KeyOn( 1 ); + } + } else { + Op(0)->KeyOff( 0x1 ); + Op(1)->KeyOff( 0x1 ); + if ( fourOp & 0x3f ) { + ( this + 1 )->Op(0)->KeyOff( 1 ); + ( this + 1 )->Op(1)->KeyOff( 1 ); + } + } +} + +void Channel::WriteC0(const Chip* chip, uint8_t val) { + uint8_t change = val ^ regC0; + if (!change) + return; + regC0 = val; + feedback = (regC0 >> 1) & 7; + if (feedback) { + //We shift the input to the right 10 bit wave index value + feedback = 9 - feedback; + } + else { + feedback = 31; + } + UpdateSynth(chip); +} + +void Channel::UpdateSynth( const Chip* chip ) { + //Select the new synth mode + if ( chip->opl3Active ) { + //4-op mode enabled for this channel + if ( (chip->reg104 & fourMask) & 0x3f ) { + Channel* chan0, *chan1; + //Check if it's the 2nd channel in a 4-op + if ( !(fourMask & 0x80 ) ) { + chan0 = this; + chan1 = this + 1; + } else { + chan0 = this - 1; + chan1 = this; + } + + uint8_t synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 ); + switch ( synth ) { + case 0: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >; + break; + case 1: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >; + break; + case 2: + chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >; + break; + case 3: + chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >; + break; + } + //Disable updating percussion channels + } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm3AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm3FM >; + } + maskLeft = (regC0 & 0x10 ) ? -1 : 0; + maskRight = (regC0 & 0x20 ) ? -1 : 0; + //opl2 active + } else { + //Disable updating percussion channels + if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) { + + //Regular dual op, am or fm + } else if (regC0 & 1 ) { + synthHandler = &Channel::BlockTemplate< sm2AM >; + } else { + synthHandler = &Channel::BlockTemplate< sm2FM >; + } + maskLeft = maskRight = -1; + } +} + +template< bool opl3Mode> +/*INLINE*/ void Channel::GeneratePercussion( Chip* chip, int32_t* output ) { + Channel* chan = this; + + //BassDrum + int32_t mod = (int32_t)((uint32_t)(old[0] + old[1]) >> feedback); + old[0] = old[1]; + old[1] = (int32_t)Op(0)->GetSample( mod ); + + //When bassdrum is in AM mode first operator is ignoed + if ( chan->regC0 & 1 ) { + mod = 0; + } else { + mod = old[0]; + } + int32_t sample = (int32_t)Op(1)->GetSample( mod ); + + + //Precalculate stuff used by other outputs + uint32_t noiseBit = chip->ForwardNoise() & 0x1; + uint32_t c2 = (uint32_t)Op(2)->ForwardWave(); + uint32_t c5 = (uint32_t)Op(5)->ForwardWave(); + uint32_t phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00; + + //Hi-Hat + uint32_t hhVol = (uint32_t)Op(2)->ForwardVolume(); + if ( !ENV_SILENT( hhVol ) ) { + uint32_t hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 ))); + sample += (int32_t)Op(2)->GetWave( hhIndex, hhVol ); + } + //Snare Drum + uint32_t sdVol = (uint32_t)Op(3)->ForwardVolume(); + if ( !ENV_SILENT( sdVol ) ) { + uint32_t sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 ); + sample += (int32_t)Op(3)->GetWave( sdIndex, sdVol ); + } + //Tom-tom + sample += (int32_t)Op(4)->GetSample( 0 ); + + //Top-Cymbal + uint32_t tcVol = (uint32_t)Op(5)->ForwardVolume(); + if ( !ENV_SILENT( tcVol ) ) { + uint32_t tcIndex = (1 + phaseBit) << 8; + sample += (int32_t)Op(5)->GetWave( tcIndex, tcVol ); + } + sample <<= 1; + if ( opl3Mode ) { + output[0] += sample; + output[1] += sample; + } else { + output[0] += sample; + } +} + +template +Channel* Channel::BlockTemplate( Chip* chip, uint32_t samples, int32_t* output ) { + switch( mode ) { + case sm2AM: + case sm3AM: + if ( Op(0)->Silent() && Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm2FM: + case sm3FM: + if ( Op(1)->Silent() ) { + old[0] = old[1] = 0; + return (this + 1); + } + break; + case sm3FMFM: + if ( Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMFM: + if ( Op(0)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3FMAM: + if ( Op(1)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + case sm3AMAM: + if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) { + old[0] = old[1] = 0; + return (this + 2); + } + break; + default: + break; + } + //Init the operators with the current vibrato and tremolo values + Op( 0 )->Prepare( chip ); + Op( 1 )->Prepare( chip ); + if ( mode > sm4Start ) { + Op( 2 )->Prepare( chip ); + Op( 3 )->Prepare( chip ); + } + if ( mode > sm6Start ) { + Op( 4 )->Prepare( chip ); + Op( 5 )->Prepare( chip ); + } + for ( Bitu i = 0; i < samples; i++ ) { + //Early out for percussion handlers + if ( mode == sm2Percussion ) { + GeneratePercussion( chip, output + i ); + continue; //Prevent some uninitialized value bitching + } else if ( mode == sm3Percussion ) { + GeneratePercussion( chip, output + i * 2 ); + continue; //Prevent some uninitialized value bitching + } + + //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise + int32_t mod = (int32_t)((uint32_t)((old[0] + old[1])) >> feedback); + old[0] = old[1]; + old[1] = (int32_t)Op(0)->GetSample( mod ); + int32_t sample; + int32_t out0 = old[0]; + if ( mode == sm2AM || mode == sm3AM ) { + sample = (int32_t)(out0 + Op(1)->GetSample( 0 )); + } else if ( mode == sm2FM || mode == sm3FM ) { + sample = (int32_t)Op(1)->GetSample( out0 ); + } else if ( mode == sm3FMFM ) { + Bits next = Op(1)->GetSample( out0 ); + next = Op(2)->GetSample( next ); + sample = (int32_t)Op(3)->GetSample( next ); + } else if ( mode == sm3AMFM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + next = Op(2)->GetSample( next ); + sample += (int32_t)Op(3)->GetSample( next ); + } else if ( mode == sm3FMAM ) { + sample = (int32_t)Op(1)->GetSample( out0 ); + Bits next = Op(2)->GetSample( 0 ); + sample += (int32_t)Op(3)->GetSample( next ); + } else if ( mode == sm3AMAM ) { + sample = out0; + Bits next = Op(1)->GetSample( 0 ); + sample += (int32_t)Op(2)->GetSample( next ); + sample += (int32_t)Op(3)->GetSample( 0 ); + } + switch( mode ) { + case sm2AM: + case sm2FM: + //output[ i ] += sample; + //break; + case sm3AM: + case sm3FM: + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + output[ i * 2 + 0 ] += sample & maskLeft; + output[ i * 2 + 1 ] += sample & maskRight; + break; + default: + break; + } + } + switch( mode ) { + case sm2AM: + case sm2FM: + case sm3AM: + case sm3FM: + return ( this + 1 ); + case sm3FMFM: + case sm3AMFM: + case sm3FMAM: + case sm3AMAM: + return( this + 2 ); + case sm2Percussion: + case sm3Percussion: + return( this + 3 ); + } + return nullptr; +} + +/* + Chip +*/ + +Chip::Chip( bool _opl3Mode ) : opl3Mode( _opl3Mode ) { + reg08 = 0; + reg04 = 0; + regBD = 0; + reg104 = 0; + opl3Active = 0; +} + +INLINE uint32_t Chip::ForwardNoise() { + noiseCounter += noiseAdd; + Bitu count = noiseCounter >> LFO_SH; + noiseCounter &= ((1< 0; --count ) { + //Noise calculation from mame + noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) ); + noiseValue >>= 1; + } + return noiseValue; +} + +INLINE uint32_t Chip::ForwardLFO( uint32_t samples ) { + //Current vibrato value, runs 4x slower than tremolo + vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7; + vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength; + tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength; + + //Check how many samples there can be done before the value changes + uint32_t todo = LFO_MAX - lfoCounter; + uint32_t count = (todo + lfoAdd - 1) / lfoAdd; + if ( count > samples ) { + count = samples; + lfoCounter += count * lfoAdd; + } else { + lfoCounter += count * lfoAdd; + lfoCounter &= (LFO_MAX - 1); + //Maximum of 7 vibrato value * 4 + vibratoIndex = ( vibratoIndex + 1 ) & 31; + //Clip tremolo to the table size + if ( tremoloIndex + 1 < TREMOLO_TABLE ) + ++tremoloIndex; + else + tremoloIndex = 0; + } + return count; +} + + +void Chip::WriteBD( uint8_t val ) { + uint8_t change = regBD ^ val; + if ( !change ) + return; + regBD = val; + //TODO could do this with shift and xor? + vibratoStrength = (val & 0x40) ? 0x00 : 0x01; + tremoloStrength = (val & 0x80) ? 0x00 : 0x02; + if ( val & 0x20 ) { + //Drum was just enabled, make sure channel 6 has the right synth + if ( change & 0x20 ) { + if ( opl3Active ) { + chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >; + } else { + chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >; + } + } + //Bass Drum + if ( val & 0x10 ) { + chan[6].op[0].KeyOn( 0x2 ); + chan[6].op[1].KeyOn( 0x2 ); + } else { + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + } + //Hi-Hat + if ( val & 0x1 ) { + chan[7].op[0].KeyOn( 0x2 ); + } else { + chan[7].op[0].KeyOff( 0x2 ); + } + //Snare + if ( val & 0x8 ) { + chan[7].op[1].KeyOn( 0x2 ); + } else { + chan[7].op[1].KeyOff( 0x2 ); + } + //Tom-Tom + if ( val & 0x4 ) { + chan[8].op[0].KeyOn( 0x2 ); + } else { + chan[8].op[0].KeyOff( 0x2 ); + } + //Top Cymbal + if ( val & 0x2 ) { + chan[8].op[1].KeyOn( 0x2 ); + } else { + chan[8].op[1].KeyOff( 0x2 ); + } + //Toggle keyoffs when we turn off the percussion + } else if ( change & 0x20 ) { + //Trigger a reset to setup the original synth handler + //This makes it call + chan[6].UpdateSynth( this ); + chan[6].op[0].KeyOff( 0x2 ); + chan[6].op[1].KeyOff( 0x2 ); + chan[7].op[0].KeyOff( 0x2 ); + chan[7].op[1].KeyOff( 0x2 ); + chan[8].op[0].KeyOff( 0x2 ); + chan[8].op[1].KeyOff( 0x2 ); + } +} + + +#define REGOP( _FUNC_ ) \ + index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \ + if ( OpOffsetTable[ index ] ) { \ + Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ]-1 ); \ + regOp->_FUNC_( this, val ); \ + } + +#define REGCHAN( _FUNC_ ) \ + index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \ + if ( ChanOffsetTable[ index ] ) { \ + Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ]-1 ); \ + regChan->_FUNC_( this, val ); \ + } + +//Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers +void Chip::UpdateSynths() { + for (int i = 0; i < 18; i++) { + chan[i].UpdateSynth(this); + } +} + +void Chip::WriteReg( uint32_t reg, uint8_t val ) { + Bitu index; + switch ( (reg & 0xf0) >> 4 ) { + case 0x00 >> 4: + if ( reg == 0x01 ) { + //When the chip is running in opl3 compatible mode, you can't actually disable the waveforms + waveFormMask = ( (val & 0x20) || opl3Mode ) ? 0x7 : 0x0; + } else if ( reg == 0x104 ) { + //Only detect changes in lowest 6 bits + if ( !((reg104 ^ val) & 0x3f) ) + return; + //Always keep the highest bit enabled, for checking > 0x80 + reg104 = 0x80 | ( val & 0x3f ); + //Switch synths when changing the 4op combinations + UpdateSynths(); + } else if ( reg == 0x105 ) { + //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register + if ( !((opl3Active ^ val) & 1 ) ) + return; + opl3Active = ( val & 1 ) ? 0xff : 0; + //Just update the synths now that opl3 must have been enabled + //This isn't how the real card handles it but need to switch to stereo generating handlers + UpdateSynths(); + } else if ( reg == 0x08 ) { + reg08 = val; + } + case 0x10 >> 4: + break; + case 0x20 >> 4: + case 0x30 >> 4: + REGOP( Write20 ); + break; + case 0x40 >> 4: + case 0x50 >> 4: + REGOP( Write40 ); + break; + case 0x60 >> 4: + case 0x70 >> 4: + REGOP( Write60 ); + break; + case 0x80 >> 4: + case 0x90 >> 4: + REGOP( Write80 ); + break; + case 0xa0 >> 4: + REGCHAN( WriteA0 ); + break; + case 0xb0 >> 4: + if ( reg == 0xbd ) { + WriteBD( val ); + } else { + REGCHAN( WriteB0 ); + } + break; + case 0xc0 >> 4: + REGCHAN( WriteC0 ); + case 0xd0 >> 4: + break; + case 0xe0 >> 4: + case 0xf0 >> 4: + REGOP( WriteE0 ); + break; + } +} + + +uint32_t Chip::WriteAddr( uint32_t port, uint8_t val ) { + switch ( port & 3 ) { + case 0: + return val; + case 2: + if ( opl3Active || (val == 0x05u) ) + return 0x100u | val; + else + return val; + } + return 0u; +} + +void Chip::GenerateBlock2( Bitu total, int32_t* output ) { + while ( total > 0 ) { + uint32_t samples = ForwardLFO( (uint32_t)total ); + memset(output, 0, sizeof(int32_t) * samples); +// int count = 0; + for( Channel* ch = chan; ch < chan + 9; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples; + } +} + +void Chip::GenerateBlock3( Bitu total, int32_t* output ) { + while ( total > 0 ) { + uint32_t samples = ForwardLFO( (uint32_t)total ); + memset(output, 0, sizeof(int32_t) * samples *2); +// int count = 0; + for( Channel* ch = chan; ch < chan + 18; ) { +// count++; + ch = (ch->*(ch->synthHandler))( this, samples, output ); + } + total -= samples; + output += samples * 2; + } +} + +void InitTables(void); + +void Chip::Setup( uint32_t rate ) { + InitTables(); + +#if 0 + double original = OPLRATE; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + noiseAdd = (uint32_t)( 0.5 + scale * ( 1 << LFO_SH ) ); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (uint32_t)( 0.5 + scale * ( 1 << LFO_SH ) ); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + + //With higher octave this gets shifted up + //-1 since the freqCreateTable = *2 +#ifdef WAVE_PRECISION + double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10)); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = (uint32_t)( 0.5 + freqScale * FreqCreateTable[ i ] ); + } +#else + uint32_t freqScale = (uint32_t)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10))); + for ( int i = 0; i < 16; i++ ) { + freqMul[i] = freqScale * FreqCreateTable[ i ]; + } +#endif +#else + // ignore scale and force 49716 Hz rendering, just like real OPL3 would + int scale = 1; + + //Noise counter is run at the same precision as general waves + noiseAdd = (uint32_t)(scale * (1 << LFO_SH)); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (uint32_t)(scale * (1 << LFO_SH)); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + + //With higher octave this gets shifted up + //-1 since the freqCreateTable = *2 +#ifdef WAVE_PRECISION + double freqScale = (1 << 7) * scale * (1 << (WAVE_SH - 1 - 10)); + for (int i = 0; i < 16; i++) { + freqMul[i] = (uint32_t)(0.5 + freqScale * FreqCreateTable[i]); + } +#else + uint32_t freqScale = (uint32_t)(scale * (1 << (WAVE_SH - 1 - 10))); + for (int i = 0; i < 16; i++) { + freqMul[i] = freqScale * FreqCreateTable[i]; + } +#endif +#endif + + //-3 since the real envelope takes 8 steps to reach the single value we supply + for ( uint8_t i = 0; i < 76; i++ ) { + uint8_t index, shift; + EnvelopeSelect( i, index, shift ); + linearRates[i] = (uint32_t)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); + } +// int32_t attackDiffs[62]; + //Generate the best matching attack rate + for ( uint8_t i = 0; i < 62; i++ ) { + uint8_t index, shift; + EnvelopeSelect( i, index, shift ); + //Original amount of samples the attack would take + int32_t originalAmount = (int32_t)((uint32_t)( (AttackSamplesTable[ index ] << shift) / scale)); + + int32_t guessAdd = (int32_t)((uint32_t)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 )))); + int32_t bestAdd = guessAdd; + uint32_t bestDiff = 1 << 30; + for( uint32_t passes = 0; passes < 16; passes ++ ) { + int32_t volume = ENV_MAX; + int32_t samples = 0; + uint32_t count = 0; + while ( volume > 0 && samples < originalAmount * 2 ) { + count += (uint32_t)guessAdd; + int32_t change = (int32_t)(count >> RATE_SH); + count &= RATE_MASK; + if ( change ) { // less than 1 % + volume += ( ~volume * change ) >> 3; + } + samples++; + + } + int32_t diff = originalAmount - samples; + uint32_t lDiff = labs( diff ); + //Init last on first pass + if ( lDiff < bestDiff ) { + bestDiff = lDiff; + bestAdd = guessAdd; + //We hit an exactly matching sample count + if ( !bestDiff ) + break; + } + //Linear correction factor, not exactly perfect but seems to work + double correct = (originalAmount - diff) / (double)originalAmount; + guessAdd = (int32_t)(guessAdd * correct); + //Below our target + if ( diff < 0 ) { + //Always add one here for rounding, an overshoot will get corrected by another pass decreasing + guessAdd++; + } + } + attackRates[i] = (uint32_t)bestAdd; + //Keep track of the diffs for some debugging +// attackDiffs[i] = bestDiff; + } + for ( uint8_t i = 62; i < 76; i++ ) { + //This should provide instant volume maximizing + attackRates[i] = 8u << RATE_SH; + } + //Setup the channels with the correct four op flags + //Channels are accessed through a table so they appear linear here + chan[ 0].fourMask = 0x00 | ( 1 << 0 ); + chan[ 1].fourMask = 0x80 | ( 1 << 0 ); + chan[ 2].fourMask = 0x00 | ( 1 << 1 ); + chan[ 3].fourMask = 0x80 | ( 1 << 1 ); + chan[ 4].fourMask = 0x00 | ( 1 << 2 ); + chan[ 5].fourMask = 0x80 | ( 1 << 2 ); + + chan[ 9].fourMask = 0x00 | ( 1 << 3 ); + chan[10].fourMask = 0x80 | ( 1 << 3 ); + chan[11].fourMask = 0x00 | ( 1 << 4 ); + chan[12].fourMask = 0x80 | ( 1 << 4 ); + chan[13].fourMask = 0x00 | ( 1 << 5 ); + chan[14].fourMask = 0x80 | ( 1 << 5 ); + + //mark the percussion channels + chan[ 6].fourMask = 0x40; + chan[ 7].fourMask = 0x40; + chan[ 8].fourMask = 0x40; + + //Clear Everything in opl3 mode + WriteReg( 0x105, 0x1 ); + for ( unsigned int i = 0; i < 512; i++ ) { + if ( i == 0x105 ) + continue; + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } + WriteReg( 0x105, 0x0 ); + //Clear everything in opl2 mode + for ( unsigned int i = 0; i < 255; i++ ) { + WriteReg( i, 0xff ); + WriteReg( i, 0x0 ); + } +} + +static bool doneTables = false; +void InitTables( void ) { + if ( doneTables ) + return; + doneTables = true; + +#ifndef DBOPL_USE_PRECALCUALTED_TABLES +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) + //Exponential volume table, same as the real adlib + for ( int i = 0; i < 256; i++ ) { + //Save them in reverse + ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 ); + ExpTable[i] += 1024; //or remove the -1 oh well :) + //Preshift to the left once so the final volume can shift to the right + ExpTable[i] *= 2; + } +#endif +#if ( DBOPL_WAVE == WAVE_HANDLER ) + //Add 0.5 for the trunc rounding of the integer cast + //Do a PI sinetable instead of the original 0.5 PI + for ( int i = 0; i < 512; i++ ) { + SinTable[i] = (int16_t)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) + //Multiplication based tables + for ( int i = 0; i < 384; i++ ) { + int s = i * 8; + //TODO maybe keep some of the precision errors of the original table? + double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH )); + MulTable[i] = (uint16_t)(val); + } + + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (int16_t)(sin( (i + 0.5) * (PI / 512.0) ) * 4084); + WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = (int16_t)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 ); + WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ]; + } +#endif +#if ( DBOPL_WAVE == WAVE_TABLELOG ) + //Sine Wave Base + for ( int i = 0; i < 512; i++ ) { + WaveTable[ 0x0200 + i ] = (int16_t)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 ); + WaveTable[ 0x0000 + i ] = ((int16_t)0x8000) | WaveTable[ 0x200 + i]; + } + //Exponential wave + for ( int i = 0; i < 256; i++ ) { + WaveTable[ 0x700 + i ] = i * 8; + WaveTable[ 0x6ff - i ] = ((int16_t)0x8000) | i * 8; + } +#endif + + // | |//\\|____|WAV7|//__|/\ |____|/\/\| + // |\\//| | |WAV7| | \/| | | + // |06 |0126|27 |7 |3 |4 |4 5 |5 | + +#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL )) + for ( int i = 0; i < 256; i++ ) { + //Fill silence gaps + WaveTable[ 0x400 + i ] = WaveTable[0]; + WaveTable[ 0x500 + i ] = WaveTable[0]; + WaveTable[ 0x900 + i ] = WaveTable[0]; + WaveTable[ 0xc00 + i ] = WaveTable[0]; + WaveTable[ 0xd00 + i ] = WaveTable[0]; + //Replicate sines in other pieces + WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ]; + //double speed sines + WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ]; + WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ]; + WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ]; + } +#endif +#endif + + //Create the ksl table + for ( int oct = 0; oct < 8; oct++ ) { + int base = oct * 8; + for ( int i = 0; i < 16; i++ ) { + int val = base - KslCreateTable[i]; + if ( val < 0 ) + val = 0; + //*4 for the final range to match attenuation range + KslTable[ oct * 16 + i ] = val * 4; + } + } + //Create the Tremolo table, just increase and decrease a triangle wave + for ( uint8_t i = 0; i < TREMOLO_TABLE / 2; i++ ) { + uint8_t val = i << ENV_EXTRA; + TremoloTable[i] = val; + TremoloTable[TREMOLO_TABLE - 1 - i] = val; + } + //Create a table with offsets of the channels from the start of the chip + for ( Bitu i = 0; i < 32; i++ ) { + Bitu index = i & 0xf; + if ( index >= 9 ) { + ChanOffsetTable[i] = 0; + continue; + } + //Make sure the four op channels follow each other + if ( index < 6 ) { + index = (index % 3) * 2 + ( index / 3 ); + } + //Add back the bits for highest ones + if ( i >= 16 ) + index += 9; + ChanOffsetTable[i] = 1+(uint16_t)(index*sizeof(DBOPL::Channel)); + } + //Same for operators + for ( Bitu i = 0; i < 64; i++ ) { + if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) { + OpOffsetTable[i] = 0; + continue; + } + Bitu chNum = (i / 8) * 3 + (i % 8) % 3; + //Make sure we use 16 and up for the 2nd range to match the chanoffset gap + if ( chNum >= 12 ) + chNum += 16 - 12; + Bitu opNum = ( i % 8 ) / 3; + OpOffsetTable[i] = ChanOffsetTable[chNum]+(uint16_t)(opNum*sizeof(DBOPL::Operator)); + } + + // dump tables to be reused later +#ifdef DBOPL_DUMP_TABLES + { + FILE* f = fopen("tables.ipp", "w"); + if (f) { + fprintf(f, "// autogenerated tables\n\n"); + +#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG ) + fprintf(f, "static uint16_t ExpTable[ 256 ] = {\n"); + for (int i = 0; i < 256; i++) { + if ((i & 15) == 0) fprintf(f, " "); + fprintf(f, "%6u, ", ExpTable[i]); + if ((i & 15) == 15) fprintf(f, "\n"); + } + fprintf(f, "};\n\n"); +#endif + +#if ( DBOPL_WAVE == WAVE_HANDLER ) + fprintf(f, "static uint16_t SinTable[ 512 ] = {\n"); + for (int i = 0; i < 512; i++) { + if ((i & 15) == 0) fprintf(f, " "); + fprintf(f, "%6u, ", SinTable[i]); + if ((i & 15) == 15) fprintf(f, "\n"); + } + fprintf(f, "};\n\n"); +#endif + +#if ( DBOPL_WAVE == WAVE_TABLEMUL ) + fprintf(f, "static uint16_t MulTable[ 384 ] = {\n"); + for (int i = 0; i < 384; i++) { + if ((i & 15) == 0) fprintf(f, " "); + fprintf(f, "%6u, ", MulTable[i]); + if ((i & 15) == 15) fprintf(f, "\n"); + } + fprintf(f, "};\n\n"); + + fprintf(f, "static int16_t WaveTable[ 8 * 512 ] = {\n"); + for (int i = 0; i < 8 * 512; i++) { + if ((i & 15) == 0) fprintf(f, " "); + fprintf(f, "%6d, ", WaveTable[i]); + if ((i & 15) == 15) fprintf(f, "\n"); + } + fprintf(f, "};\n"); +#endif + +#if ( DBOPL_WAVE == WAVE_TABLELOG ) + fprintf(f, "static int16_t WaveTable[ 8 * 512 ] = {\n"); + for (int i = 0; i < 8 * 512; i++) { + if ((i & 15) == 0) fprintf(f, " "); + fprintf(f, "%6d, ", WaveTable[i]); + if ((i & 15) == 15) fprintf(f, "\n"); + } + fprintf(f, "};\n\n"); +#endif + + fclose(f); + } + } +#endif + +#if 0 + DBOPL::Chip* chip = 0; + //Stupid checks if table's are correct + for ( Bitu i = 0; i < 18; i++ ) { + uint32_t find = (uint16_t)( &(chip->chan[ i ]) ); + for ( Bitu c = 0; c < 32; c++ ) { + if ( ChanOffsetTable[c] == find+1 ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } + for ( Bitu i = 0; i < 36; i++ ) { + uint32_t find = (uint16_t)( &(chip->chan[ i / 2 ].op[i % 2]) ); + for ( Bitu c = 0; c < 64; c++ ) { + if ( OpOffsetTable[c] == find+1 ) { + find = 0; + break; + } + } + if ( find ) { + find = find; + } + } +#endif +} + +} //Namespace DBOPL \ No newline at end of file diff --git a/sw/opl/dbopl/dbopl.h b/sw/opl/dbopl/dbopl.h new file mode 100644 index 0000000..0021d78 --- /dev/null +++ b/sw/opl/dbopl/dbopl.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2002-2021 The DOSBox Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include + +typedef uint32_t Bitu; +typedef int32_t Bits; + +//Use 8 handlers based on a small logarithmic wavetable and an exponential table for volume +#define WAVE_HANDLER 10 +//Use a logarithmic wavetable with an exponential table for volume +#define WAVE_TABLELOG 11 +//Use a linear wavetable with a multiply table for volume +#define WAVE_TABLEMUL 12 + +//Select the type of wave generator routine +#define DBOPL_WAVE WAVE_TABLEMUL + +//#define DBOPL_DUMP_TABLES +#define DBOPL_USE_PRECALCUALTED_TABLES + +namespace DBOPL { + +struct Chip; +struct Operator; +struct Channel; + +#if (DBOPL_WAVE == WAVE_HANDLER) +typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume ); +#endif + +typedef Bits ( DBOPL::Operator::*VolumeHandler) ( ); +typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, uint32_t samples, int32_t* output ); + +//Different synth modes that can generate blocks of data +typedef enum { + sm2AM, + sm2FM, + sm3AM, + sm3FM, + sm4Start, + sm3FMFM, + sm3AMFM, + sm3FMAM, + sm3AMAM, + sm6Start, + sm2Percussion, + sm3Percussion, +} SynthMode; + +//Shifts for the values contained in chandata variable +enum { + SHIFT_KSLBASE = 16, + SHIFT_KEYCODE = 24, +}; + +struct Operator { +public: + //Masks for operator 20 values + enum { + MASK_KSR = 0x10, + MASK_SUSTAIN = 0x20, + MASK_VIBRATO = 0x40, + MASK_TREMOLO = 0x80, + }; + + typedef enum { + OFF, + RELEASE, + SUSTAIN, + DECAY, + ATTACK, + } State; + + VolumeHandler volHandler; + +#if (DBOPL_WAVE == WAVE_HANDLER) + WaveHandler waveHandler; //Routine that generate a wave +#else + int16_t* waveBase; + uint32_t waveMask; + uint32_t waveStart; +#endif + uint32_t waveIndex; //WAVE_BITS shifted counter of the frequency index + uint32_t waveAdd; //The base frequency without vibrato + uint32_t waveCurrent; //waveAdd + vibrato + + uint32_t chanData; //Frequency/octave and derived data coming from whatever channel controls this + uint32_t freqMul; //Scale channel frequency with this, TODO maybe remove? + uint32_t vibrato; //Scaled up vibrato strength + int32_t sustainLevel; //When stopping at sustain level stop here + int32_t totalLevel; //totalLevel is added to every generated volume + uint32_t currentLevel; //totalLevel + tremolo + int32_t volume; //The currently active volume + + uint32_t attackAdd; //Timers for the different states of the envelope + uint32_t decayAdd; + uint32_t releaseAdd; + uint32_t rateIndex; //Current position of the envelope + + uint8_t rateZero; //Bits for the different states of the envelope having no changes + uint8_t keyOn; //Bitmask of different values that can generate keyon + //Registers, also used to check for changes + uint8_t reg20, reg40, reg60, reg80, regE0; + //Active part of the envelope we're in + uint8_t state; + //0xff when tremolo is enabled + uint8_t tremoloMask; + //Strength of the vibrato + uint8_t vibStrength; + //Keep track of the calculated KSR so we can check for changes + uint8_t ksr; +private: + void SetState( uint8_t s ); + void UpdateAttack( const Chip* chip ); + void UpdateRelease( const Chip* chip ); + void UpdateDecay( const Chip* chip ); +public: + void UpdateAttenuation(); + void UpdateRates( const Chip* chip ); + void UpdateFrequency( ); + + void Write20( const Chip* chip, uint8_t val ); + void Write40( const Chip* chip, uint8_t val ); + void Write60( const Chip* chip, uint8_t val ); + void Write80( const Chip* chip, uint8_t val ); + void WriteE0( const Chip* chip, uint8_t val ); + + bool Silent() const; + void Prepare( const Chip* chip ); + + void KeyOn( uint8_t mask); + void KeyOff( uint8_t mask); + + template< State state> + Bits TemplateVolume( ); + + int32_t RateForward( uint32_t add ); + Bitu ForwardWave(); + Bitu ForwardVolume(); + + Bits GetSample( Bits modulation ); + Bits GetWave( Bitu index, Bitu vol ); +public: + Operator(); +}; + +struct Channel { + Operator op[2]; //Leave on top of struct for simpler pointer math. + inline Operator* Op( Bitu index ) { + return &( ( this + (index >> 1) )->op[ index & 1 ]); + } + SynthHandler synthHandler; + uint32_t chanData; //Frequency/octave and derived values + int32_t old[2]; //Old data for feedback + + uint8_t feedback; //Feedback shift + uint8_t regB0; //Register values to check for changes + uint8_t regC0; + //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel + uint8_t fourMask; + int8_t maskLeft; //Sign extended values for both channel's panning + int8_t maskRight; + + //Forward the channel data to the operators of the channel + void SetChanData( const Chip* chip, uint32_t data ); + //Change in the chandata, check for new values and if we have to forward to operators + void UpdateFrequency( const Chip* chip, uint8_t fourOp ); + void UpdateSynth(const Chip* chip); + void WriteA0( const Chip* chip, uint8_t val ); + void WriteB0( const Chip* chip, uint8_t val ); + void WriteC0( const Chip* chip, uint8_t val ); + + //call this for the first channel + template< bool opl3Mode > + void GeneratePercussion( Chip* chip, int32_t* output ); + + //Generate blocks of data in specific modes + template + Channel* BlockTemplate( Chip* chip, uint32_t samples, int32_t* output ); + Channel(); +}; + +struct Chip { + //18 channels with 2 operators each. Leave on top of struct for simpler pointer math. + Channel chan[18]; + + //This is used as the base counter for vibrato and tremolo + uint32_t lfoCounter = 0; + uint32_t lfoAdd = 0; + + uint32_t noiseCounter = 0; + uint32_t noiseAdd = 0; + uint32_t noiseValue = 0; + + //Frequency scales for the different multiplications + uint32_t freqMul[16] = {}; + //Rates for decay and release for rate of this chip + uint32_t linearRates[76] = {}; + //Best match attack rates for the rate of this chip + uint32_t attackRates[76] = {}; + + uint8_t reg104; + uint8_t reg08; + uint8_t reg04; + uint8_t regBD; + uint8_t vibratoIndex = 0; + uint8_t tremoloIndex = 0; + int8_t vibratoSign = 0; + uint8_t vibratoShift = 0; + uint8_t tremoloValue = 0; + uint8_t vibratoStrength = 0; + uint8_t tremoloStrength = 0; + //Mask for allowed wave forms + uint8_t waveFormMask = 0; + //0 or -1 when enabled + int8_t opl3Active; + //Running in opl3 mode + const bool opl3Mode; + + //Return the maximum amount of samples before and LFO change + uint32_t ForwardLFO( uint32_t samples ); + uint32_t ForwardNoise(); + + void WriteBD( uint8_t val ); + void WriteReg(uint32_t reg, uint8_t val ); + + uint32_t WriteAddr( uint32_t port, uint8_t val ); + + void GenerateBlock2( Bitu total, int32_t* output ); + void GenerateBlock3( Bitu total, int32_t* output ); + + //Update the synth handlers in all channels + void UpdateSynths(); + void Generate( uint32_t samples ); + void Setup( uint32_t rate ); + + Chip( bool opl3Mode ); +}; + +} //Namespace diff --git a/sw/opl/dbopl/tables.ipp b/sw/opl/dbopl/tables.ipp new file mode 100644 index 0000000..6123c8c --- /dev/null +++ b/sw/opl/dbopl/tables.ipp @@ -0,0 +1,287 @@ +// autogenerated tables + +static uint16_t MulTable[ 384 ] = { + 65359, 63958, 62588, 61247, 59934, 58650, 57393, 56163, 54960, 53782, 52630, 51502, 50399, 49319, 48262, 47228, + 46216, 45225, 44256, 43308, 42380, 41472, 40583, 39714, 38863, 38030, 37215, 36417, 35637, 34874, 34126, 33395, + 32679, 31979, 31294, 30623, 29967, 29325, 28697, 28082, 27480, 26891, 26315, 25751, 25199, 24659, 24131, 23614, + 23108, 22613, 22128, 21654, 21190, 20736, 20292, 19857, 19431, 19015, 18607, 18209, 17819, 17437, 17063, 16697, + 16340, 15990, 15647, 15312, 14984, 14663, 14348, 14041, 13740, 13446, 13157, 12876, 12600, 12330, 12065, 11807, + 11554, 11306, 11064, 10827, 10595, 10368, 10146, 9928, 9716, 9507, 9304, 9104, 8909, 8718, 8532, 8349, + 8170, 7995, 7823, 7656, 7492, 7331, 7174, 7020, 6870, 6723, 6579, 6438, 6300, 6165, 6033, 5903, + 5777, 5653, 5532, 5413, 5297, 5184, 5073, 4964, 4858, 4754, 4652, 4552, 4455, 4359, 4266, 4174, + 4085, 3997, 3912, 3828, 3746, 3666, 3587, 3510, 3435, 3361, 3289, 3219, 3150, 3082, 3016, 2952, + 2888, 2827, 2766, 2707, 2649, 2592, 2536, 2482, 2429, 2377, 2326, 2276, 2227, 2180, 2133, 2087, + 2042, 1999, 1956, 1914, 1873, 1833, 1794, 1755, 1717, 1681, 1645, 1609, 1575, 1541, 1508, 1476, + 1444, 1413, 1383, 1353, 1324, 1296, 1268, 1241, 1214, 1188, 1163, 1138, 1114, 1090, 1066, 1044, + 1021, 999, 978, 957, 936, 916, 897, 878, 859, 840, 822, 805, 787, 771, 754, 738, + 722, 707, 692, 677, 662, 648, 634, 621, 607, 594, 581, 569, 557, 545, 533, 522, + 511, 500, 489, 478, 468, 458, 448, 439, 429, 420, 411, 402, 394, 385, 377, 369, + 361, 353, 346, 338, 331, 324, 317, 310, 304, 297, 291, 285, 278, 272, 267, 261, + 255, 250, 244, 239, 234, 229, 224, 219, 215, 210, 206, 201, 197, 193, 189, 184, + 181, 177, 173, 169, 166, 162, 159, 155, 152, 149, 145, 142, 139, 136, 133, 130, + 128, 125, 122, 120, 117, 115, 112, 110, 107, 105, 103, 101, 98, 96, 94, 92, + 90, 88, 86, 85, 83, 81, 79, 78, 76, 74, 73, 71, 70, 68, 67, 65, + 64, 62, 61, 60, 59, 57, 56, 55, 54, 53, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 40, 40, 39, 38, 37, 36, 36, 35, 34, 33, 33, + 32, 31, 31, 30, 29, 29, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, + 23, 22, 22, 21, 21, 20, 20, 19, 19, 19, 18, 18, 17, 17, 17, 16, +}; + +static int16_t WaveTable[ 8 * 512 ] = { + -12, -37, -62, -87, -112, -137, -162, -187, -212, -237, -262, -287, -312, -337, -362, -387, + -412, -437, -462, -487, -512, -537, -562, -586, -611, -636, -661, -685, -710, -735, -759, -784, + -809, -833, -858, -882, -907, -931, -955, -980, -1004, -1028, -1052, -1077, -1101, -1125, -1149, -1173, + -1197, -1221, -1245, -1269, -1292, -1316, -1340, -1364, -1387, -1411, -1434, -1458, -1481, -1504, -1528, -1551, + -1574, -1597, -1620, -1643, -1666, -1689, -1712, -1734, -1757, -1780, -1802, -1825, -1847, -1869, -1891, -1914, + -1936, -1958, -1980, -2002, -2023, -2045, -2067, -2088, -2110, -2131, -2153, -2174, -2195, -2216, -2237, -2258, + -2279, -2300, -2320, -2341, -2361, -2382, -2402, -2422, -2442, -2462, -2482, -2502, -2522, -2542, -2561, -2581, + -2600, -2619, -2638, -2658, -2677, -2695, -2714, -2733, -2751, -2770, -2788, -2806, -2825, -2843, -2861, -2878, + -2896, -2914, -2931, -2949, -2966, -2983, -3000, -3017, -3034, -3051, -3067, -3084, -3100, -3116, -3132, -3149, + -3164, -3180, -3196, -3211, -3227, -3242, -3257, -3272, -3287, -3302, -3317, -3331, -3346, -3360, -3374, -3388, + -3402, -3416, -3430, -3443, -3457, -3470, -3483, -3496, -3509, -3522, -3534, -3547, -3559, -3571, -3583, -3595, + -3607, -3619, -3630, -3642, -3653, -3664, -3675, -3686, -3697, -3707, -3718, -3728, -3738, -3748, -3758, -3768, + -3777, -3787, -3796, -3805, -3814, -3823, -3832, -3841, -3849, -3857, -3865, -3873, -3881, -3889, -3897, -3904, + -3911, -3918, -3925, -3932, -3939, -3945, -3952, -3958, -3964, -3970, -3976, -3982, -3987, -3992, -3998, -4003, + -4007, -4012, -4017, -4021, -4025, -4030, -4034, -4037, -4041, -4045, -4048, -4051, -4054, -4057, -4060, -4063, + -4065, -4067, -4069, -4071, -4073, -4075, -4077, -4078, -4079, -4080, -4081, -4082, -4083, -4083, -4083, -4083, + -4083, -4083, -4083, -4083, -4082, -4081, -4080, -4079, -4078, -4077, -4075, -4073, -4071, -4069, -4067, -4065, + -4063, -4060, -4057, -4054, -4051, -4048, -4045, -4041, -4037, -4034, -4030, -4025, -4021, -4017, -4012, -4007, + -4003, -3998, -3992, -3987, -3982, -3976, -3970, -3964, -3958, -3952, -3945, -3939, -3932, -3925, -3918, -3911, + -3904, -3897, -3889, -3881, -3873, -3865, -3857, -3849, -3841, -3832, -3823, -3814, -3805, -3796, -3787, -3777, + -3768, -3758, -3748, -3738, -3728, -3718, -3707, -3697, -3686, -3675, -3664, -3653, -3642, -3630, -3619, -3607, + -3595, -3583, -3571, -3559, -3547, -3534, -3522, -3509, -3496, -3483, -3470, -3457, -3443, -3430, -3416, -3402, + -3388, -3374, -3360, -3346, -3331, -3317, -3302, -3287, -3272, -3257, -3242, -3227, -3211, -3196, -3180, -3164, + -3149, -3132, -3116, -3100, -3084, -3067, -3051, -3034, -3017, -3000, -2983, -2966, -2949, -2931, -2914, -2896, + -2878, -2861, -2843, -2825, -2806, -2788, -2770, -2751, -2733, -2714, -2695, -2677, -2658, -2638, -2619, -2600, + -2581, -2561, -2542, -2522, -2502, -2482, -2462, -2442, -2422, -2402, -2382, -2361, -2341, -2320, -2300, -2279, + -2258, -2237, -2216, -2195, -2174, -2153, -2131, -2110, -2088, -2067, -2045, -2023, -2002, -1980, -1958, -1936, + -1914, -1891, -1869, -1847, -1825, -1802, -1780, -1757, -1734, -1712, -1689, -1666, -1643, -1620, -1597, -1574, + -1551, -1528, -1504, -1481, -1458, -1434, -1411, -1387, -1364, -1340, -1316, -1292, -1269, -1245, -1221, -1197, + -1173, -1149, -1125, -1101, -1077, -1052, -1028, -1004, -980, -955, -931, -907, -882, -858, -833, -809, + -784, -759, -735, -710, -685, -661, -636, -611, -586, -562, -537, -512, -487, -462, -437, -412, + -387, -362, -337, -312, -287, -262, -237, -212, -187, -162, -137, -112, -87, -62, -37, -12, + 12, 37, 62, 87, 112, 137, 162, 187, 212, 237, 262, 287, 312, 337, 362, 387, + 412, 437, 462, 487, 512, 537, 562, 586, 611, 636, 661, 685, 710, 735, 759, 784, + 809, 833, 858, 882, 907, 931, 955, 980, 1004, 1028, 1052, 1077, 1101, 1125, 1149, 1173, + 1197, 1221, 1245, 1269, 1292, 1316, 1340, 1364, 1387, 1411, 1434, 1458, 1481, 1504, 1528, 1551, + 1574, 1597, 1620, 1643, 1666, 1689, 1712, 1734, 1757, 1780, 1802, 1825, 1847, 1869, 1891, 1914, + 1936, 1958, 1980, 2002, 2023, 2045, 2067, 2088, 2110, 2131, 2153, 2174, 2195, 2216, 2237, 2258, + 2279, 2300, 2320, 2341, 2361, 2382, 2402, 2422, 2442, 2462, 2482, 2502, 2522, 2542, 2561, 2581, + 2600, 2619, 2638, 2658, 2677, 2695, 2714, 2733, 2751, 2770, 2788, 2806, 2825, 2843, 2861, 2878, + 2896, 2914, 2931, 2949, 2966, 2983, 3000, 3017, 3034, 3051, 3067, 3084, 3100, 3116, 3132, 3149, + 3164, 3180, 3196, 3211, 3227, 3242, 3257, 3272, 3287, 3302, 3317, 3331, 3346, 3360, 3374, 3388, + 3402, 3416, 3430, 3443, 3457, 3470, 3483, 3496, 3509, 3522, 3534, 3547, 3559, 3571, 3583, 3595, + 3607, 3619, 3630, 3642, 3653, 3664, 3675, 3686, 3697, 3707, 3718, 3728, 3738, 3748, 3758, 3768, + 3777, 3787, 3796, 3805, 3814, 3823, 3832, 3841, 3849, 3857, 3865, 3873, 3881, 3889, 3897, 3904, + 3911, 3918, 3925, 3932, 3939, 3945, 3952, 3958, 3964, 3970, 3976, 3982, 3987, 3992, 3998, 4003, + 4007, 4012, 4017, 4021, 4025, 4030, 4034, 4037, 4041, 4045, 4048, 4051, 4054, 4057, 4060, 4063, + 4065, 4067, 4069, 4071, 4073, 4075, 4077, 4078, 4079, 4080, 4081, 4082, 4083, 4083, 4083, 4083, + 4083, 4083, 4083, 4083, 4082, 4081, 4080, 4079, 4078, 4077, 4075, 4073, 4071, 4069, 4067, 4065, + 4063, 4060, 4057, 4054, 4051, 4048, 4045, 4041, 4037, 4034, 4030, 4025, 4021, 4017, 4012, 4007, + 4003, 3998, 3992, 3987, 3982, 3976, 3970, 3964, 3958, 3952, 3945, 3939, 3932, 3925, 3918, 3911, + 3904, 3897, 3889, 3881, 3873, 3865, 3857, 3849, 3841, 3832, 3823, 3814, 3805, 3796, 3787, 3777, + 3768, 3758, 3748, 3738, 3728, 3718, 3707, 3697, 3686, 3675, 3664, 3653, 3642, 3630, 3619, 3607, + 3595, 3583, 3571, 3559, 3547, 3534, 3522, 3509, 3496, 3483, 3470, 3457, 3443, 3430, 3416, 3402, + 3388, 3374, 3360, 3346, 3331, 3317, 3302, 3287, 3272, 3257, 3242, 3227, 3211, 3196, 3180, 3164, + 3149, 3132, 3116, 3100, 3084, 3067, 3051, 3034, 3017, 3000, 2983, 2966, 2949, 2931, 2914, 2896, + 2878, 2861, 2843, 2825, 2806, 2788, 2770, 2751, 2733, 2714, 2695, 2677, 2658, 2638, 2619, 2600, + 2581, 2561, 2542, 2522, 2502, 2482, 2462, 2442, 2422, 2402, 2382, 2361, 2341, 2320, 2300, 2279, + 2258, 2237, 2216, 2195, 2174, 2153, 2131, 2110, 2088, 2067, 2045, 2023, 2002, 1980, 1958, 1936, + 1914, 1891, 1869, 1847, 1825, 1802, 1780, 1757, 1734, 1712, 1689, 1666, 1643, 1620, 1597, 1574, + 1551, 1528, 1504, 1481, 1458, 1434, 1411, 1387, 1364, 1340, 1316, 1292, 1269, 1245, 1221, 1197, + 1173, 1149, 1125, 1101, 1077, 1052, 1028, 1004, 980, 955, 931, 907, 882, 858, 833, 809, + 784, 759, 735, 710, 685, 661, 636, 611, 586, 562, 537, 512, 487, 462, 437, 412, + 387, 362, 337, 312, 287, 262, 237, 212, 187, 162, 137, 112, 87, 62, 37, 12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -16, -17, -17, -17, -18, -18, -19, -19, -19, -20, -20, -21, -21, -22, -22, -23, + -23, -24, -24, -25, -25, -26, -26, -27, -27, -28, -29, -29, -30, -30, -31, -32, + -33, -33, -34, -35, -35, -36, -37, -38, -39, -40, -40, -41, -42, -43, -44, -45, + -46, -47, -48, -49, -50, -51, -52, -54, -55, -56, -57, -58, -60, -61, -62, -64, + -65, -66, -68, -69, -71, -72, -74, -76, -77, -79, -81, -83, -84, -86, -88, -90, + -92, -94, -96, -98, -100, -103, -105, -107, -109, -112, -114, -117, -119, -122, -125, -127, + -130, -133, -136, -139, -142, -145, -148, -151, -155, -158, -162, -165, -169, -172, -176, -180, + -184, -188, -192, -196, -201, -205, -210, -214, -219, -224, -228, -233, -239, -244, -249, -255, + -260, -266, -272, -278, -284, -290, -296, -303, -309, -316, -323, -330, -337, -345, -352, -360, + -368, -376, -384, -393, -401, -410, -419, -428, -438, -447, -457, -467, -477, -488, -498, -509, + -520, -532, -543, -555, -567, -580, -593, -606, -619, -632, -646, -660, -675, -690, -705, -720, + -736, -752, -769, -785, -803, -820, -838, -856, -875, -894, -914, -934, -954, -975, -997, -1018, + -1041, -1064, -1087, -1111, -1135, -1160, -1185, -1211, -1238, -1265, -1293, -1321, -1350, -1379, -1409, -1440, + -1472, -1504, -1537, -1571, -1605, -1640, -1676, -1713, -1750, -1789, -1828, -1868, -1909, -1951, -1993, -2037, + -2082, -2127, -2174, -2221, -2270, -2320, -2370, -2422, -2475, -2530, -2585, -2642, -2699, -2759, -2819, -2881, + -2944, -3008, -3074, -3141, -3210, -3281, -3352, -3426, -3501, -3577, -3656, -3736, -3818, -3901, -3987, -4074, + 4074, 3987, 3901, 3818, 3736, 3656, 3577, 3501, 3426, 3352, 3281, 3210, 3141, 3074, 3008, 2944, + 2881, 2819, 2759, 2699, 2642, 2585, 2530, 2475, 2422, 2370, 2320, 2270, 2221, 2174, 2127, 2082, + 2037, 1993, 1951, 1909, 1868, 1828, 1789, 1750, 1713, 1676, 1640, 1605, 1571, 1537, 1504, 1472, + 1440, 1409, 1379, 1350, 1321, 1293, 1265, 1238, 1211, 1185, 1160, 1135, 1111, 1087, 1064, 1041, + 1018, 997, 975, 954, 934, 914, 894, 875, 856, 838, 820, 803, 785, 769, 752, 736, + 720, 705, 690, 675, 660, 646, 632, 619, 606, 593, 580, 567, 555, 543, 532, 520, + 509, 498, 488, 477, 467, 457, 447, 438, 428, 419, 410, 401, 393, 384, 376, 368, + 360, 352, 345, 337, 330, 323, 316, 309, 303, 296, 290, 284, 278, 272, 266, 260, + 255, 249, 244, 239, 233, 228, 224, 219, 214, 210, 205, 201, 196, 192, 188, 184, + 180, 176, 172, 169, 165, 162, 158, 155, 151, 148, 145, 142, 139, 136, 133, 130, + 127, 125, 122, 119, 117, 114, 112, 109, 107, 105, 103, 100, 98, 96, 94, 92, + 90, 88, 86, 84, 83, 81, 79, 77, 76, 74, 72, 71, 69, 68, 66, 65, + 64, 62, 61, 60, 58, 57, 56, 55, 54, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 40, 40, 39, 38, 37, 36, 35, 35, 34, 33, 33, + 32, 31, 30, 30, 29, 29, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, + 23, 22, 22, 21, 21, 20, 20, 19, 19, 19, 18, 18, 17, 17, 17, 16, + 12, 37, 62, 87, 112, 137, 162, 187, 212, 237, 262, 287, 312, 337, 362, 387, + 412, 437, 462, 487, 512, 537, 562, 586, 611, 636, 661, 685, 710, 735, 759, 784, + 809, 833, 858, 882, 907, 931, 955, 980, 1004, 1028, 1052, 1077, 1101, 1125, 1149, 1173, + 1197, 1221, 1245, 1269, 1292, 1316, 1340, 1364, 1387, 1411, 1434, 1458, 1481, 1504, 1528, 1551, + 1574, 1597, 1620, 1643, 1666, 1689, 1712, 1734, 1757, 1780, 1802, 1825, 1847, 1869, 1891, 1914, + 1936, 1958, 1980, 2002, 2023, 2045, 2067, 2088, 2110, 2131, 2153, 2174, 2195, 2216, 2237, 2258, + 2279, 2300, 2320, 2341, 2361, 2382, 2402, 2422, 2442, 2462, 2482, 2502, 2522, 2542, 2561, 2581, + 2600, 2619, 2638, 2658, 2677, 2695, 2714, 2733, 2751, 2770, 2788, 2806, 2825, 2843, 2861, 2878, + 2896, 2914, 2931, 2949, 2966, 2983, 3000, 3017, 3034, 3051, 3067, 3084, 3100, 3116, 3132, 3149, + 3164, 3180, 3196, 3211, 3227, 3242, 3257, 3272, 3287, 3302, 3317, 3331, 3346, 3360, 3374, 3388, + 3402, 3416, 3430, 3443, 3457, 3470, 3483, 3496, 3509, 3522, 3534, 3547, 3559, 3571, 3583, 3595, + 3607, 3619, 3630, 3642, 3653, 3664, 3675, 3686, 3697, 3707, 3718, 3728, 3738, 3748, 3758, 3768, + 3777, 3787, 3796, 3805, 3814, 3823, 3832, 3841, 3849, 3857, 3865, 3873, 3881, 3889, 3897, 3904, + 3911, 3918, 3925, 3932, 3939, 3945, 3952, 3958, 3964, 3970, 3976, 3982, 3987, 3992, 3998, 4003, + 4007, 4012, 4017, 4021, 4025, 4030, 4034, 4037, 4041, 4045, 4048, 4051, 4054, 4057, 4060, 4063, + 4065, 4067, 4069, 4071, 4073, 4075, 4077, 4078, 4079, 4080, 4081, 4082, 4083, 4083, 4083, 4083, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + 12, 62, 112, 162, 212, 262, 312, 362, 412, 462, 512, 562, 611, 661, 710, 759, + 809, 858, 907, 955, 1004, 1052, 1101, 1149, 1197, 1245, 1292, 1340, 1387, 1434, 1481, 1528, + 1574, 1620, 1666, 1712, 1757, 1802, 1847, 1891, 1936, 1980, 2023, 2067, 2110, 2153, 2195, 2237, + 2279, 2320, 2361, 2402, 2442, 2482, 2522, 2561, 2600, 2638, 2677, 2714, 2751, 2788, 2825, 2861, + 2896, 2931, 2966, 3000, 3034, 3067, 3100, 3132, 3164, 3196, 3227, 3257, 3287, 3317, 3346, 3374, + 3402, 3430, 3457, 3483, 3509, 3534, 3559, 3583, 3607, 3630, 3653, 3675, 3697, 3718, 3738, 3758, + 3777, 3796, 3814, 3832, 3849, 3865, 3881, 3897, 3911, 3925, 3939, 3952, 3964, 3976, 3987, 3998, + 4007, 4017, 4025, 4034, 4041, 4048, 4054, 4060, 4065, 4069, 4073, 4077, 4079, 4081, 4083, 4083, + 4083, 4083, 4082, 4080, 4078, 4075, 4071, 4067, 4063, 4057, 4051, 4045, 4037, 4030, 4021, 4012, + 4003, 3992, 3982, 3970, 3958, 3945, 3932, 3918, 3904, 3889, 3873, 3857, 3841, 3823, 3805, 3787, + 3768, 3748, 3728, 3707, 3686, 3664, 3642, 3619, 3595, 3571, 3547, 3522, 3496, 3470, 3443, 3416, + 3388, 3360, 3331, 3302, 3272, 3242, 3211, 3180, 3149, 3116, 3084, 3051, 3017, 2983, 2949, 2914, + 2878, 2843, 2806, 2770, 2733, 2695, 2658, 2619, 2581, 2542, 2502, 2462, 2422, 2382, 2341, 2300, + 2258, 2216, 2174, 2131, 2088, 2045, 2002, 1958, 1914, 1869, 1825, 1780, 1734, 1689, 1643, 1597, + 1551, 1504, 1458, 1411, 1364, 1316, 1269, 1221, 1173, 1125, 1077, 1028, 980, 931, 882, 833, + 784, 735, 685, 636, 586, 537, 487, 437, 387, 337, 287, 237, 187, 137, 87, 37, + -12, -62, -112, -162, -212, -262, -312, -362, -412, -462, -512, -562, -611, -661, -710, -759, + -809, -858, -907, -955, -1004, -1052, -1101, -1149, -1197, -1245, -1292, -1340, -1387, -1434, -1481, -1528, + -1574, -1620, -1666, -1712, -1757, -1802, -1847, -1891, -1936, -1980, -2023, -2067, -2110, -2153, -2195, -2237, + -2279, -2320, -2361, -2402, -2442, -2482, -2522, -2561, -2600, -2638, -2677, -2714, -2751, -2788, -2825, -2861, + -2896, -2931, -2966, -3000, -3034, -3067, -3100, -3132, -3164, -3196, -3227, -3257, -3287, -3317, -3346, -3374, + -3402, -3430, -3457, -3483, -3509, -3534, -3559, -3583, -3607, -3630, -3653, -3675, -3697, -3718, -3738, -3758, + -3777, -3796, -3814, -3832, -3849, -3865, -3881, -3897, -3911, -3925, -3939, -3952, -3964, -3976, -3987, -3998, + -4007, -4017, -4025, -4034, -4041, -4048, -4054, -4060, -4065, -4069, -4073, -4077, -4079, -4081, -4083, -4083, + -4083, -4083, -4082, -4080, -4078, -4075, -4071, -4067, -4063, -4057, -4051, -4045, -4037, -4030, -4021, -4012, + -4003, -3992, -3982, -3970, -3958, -3945, -3932, -3918, -3904, -3889, -3873, -3857, -3841, -3823, -3805, -3787, + -3768, -3748, -3728, -3707, -3686, -3664, -3642, -3619, -3595, -3571, -3547, -3522, -3496, -3470, -3443, -3416, + -3388, -3360, -3331, -3302, -3272, -3242, -3211, -3180, -3149, -3116, -3084, -3051, -3017, -2983, -2949, -2914, + -2878, -2843, -2806, -2770, -2733, -2695, -2658, -2619, -2581, -2542, -2502, -2462, -2422, -2382, -2341, -2300, + -2258, -2216, -2174, -2131, -2088, -2045, -2002, -1958, -1914, -1869, -1825, -1780, -1734, -1689, -1643, -1597, + -1551, -1504, -1458, -1411, -1364, -1316, -1269, -1221, -1173, -1125, -1077, -1028, -980, -931, -882, -833, + -784, -735, -685, -636, -586, -537, -487, -437, -387, -337, -287, -237, -187, -137, -87, -37, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, -12, + 12, 62, 112, 162, 212, 262, 312, 362, 412, 462, 512, 562, 611, 661, 710, 759, + 809, 858, 907, 955, 1004, 1052, 1101, 1149, 1197, 1245, 1292, 1340, 1387, 1434, 1481, 1528, + 1574, 1620, 1666, 1712, 1757, 1802, 1847, 1891, 1936, 1980, 2023, 2067, 2110, 2153, 2195, 2237, + 2279, 2320, 2361, 2402, 2442, 2482, 2522, 2561, 2600, 2638, 2677, 2714, 2751, 2788, 2825, 2861, + 2896, 2931, 2966, 3000, 3034, 3067, 3100, 3132, 3164, 3196, 3227, 3257, 3287, 3317, 3346, 3374, + 3402, 3430, 3457, 3483, 3509, 3534, 3559, 3583, 3607, 3630, 3653, 3675, 3697, 3718, 3738, 3758, + 3777, 3796, 3814, 3832, 3849, 3865, 3881, 3897, 3911, 3925, 3939, 3952, 3964, 3976, 3987, 3998, + 4007, 4017, 4025, 4034, 4041, 4048, 4054, 4060, 4065, 4069, 4073, 4077, 4079, 4081, 4083, 4083, + 4083, 4083, 4082, 4080, 4078, 4075, 4071, 4067, 4063, 4057, 4051, 4045, 4037, 4030, 4021, 4012, + 4003, 3992, 3982, 3970, 3958, 3945, 3932, 3918, 3904, 3889, 3873, 3857, 3841, 3823, 3805, 3787, + 3768, 3748, 3728, 3707, 3686, 3664, 3642, 3619, 3595, 3571, 3547, 3522, 3496, 3470, 3443, 3416, + 3388, 3360, 3331, 3302, 3272, 3242, 3211, 3180, 3149, 3116, 3084, 3051, 3017, 2983, 2949, 2914, + 2878, 2843, 2806, 2770, 2733, 2695, 2658, 2619, 2581, 2542, 2502, 2462, 2422, 2382, 2341, 2300, + 2258, 2216, 2174, 2131, 2088, 2045, 2002, 1958, 1914, 1869, 1825, 1780, 1734, 1689, 1643, 1597, + 1551, 1504, 1458, 1411, 1364, 1316, 1269, 1221, 1173, 1125, 1077, 1028, 980, 931, 882, 833, + 784, 735, 685, 636, 586, 537, 487, 437, 387, 337, 287, 237, 187, 137, 87, 37, + 12, 62, 112, 162, 212, 262, 312, 362, 412, 462, 512, 562, 611, 661, 710, 759, + 809, 858, 907, 955, 1004, 1052, 1101, 1149, 1197, 1245, 1292, 1340, 1387, 1434, 1481, 1528, + 1574, 1620, 1666, 1712, 1757, 1802, 1847, 1891, 1936, 1980, 2023, 2067, 2110, 2153, 2195, 2237, + 2279, 2320, 2361, 2402, 2442, 2482, 2522, 2561, 2600, 2638, 2677, 2714, 2751, 2788, 2825, 2861, + 2896, 2931, 2966, 3000, 3034, 3067, 3100, 3132, 3164, 3196, 3227, 3257, 3287, 3317, 3346, 3374, + 3402, 3430, 3457, 3483, 3509, 3534, 3559, 3583, 3607, 3630, 3653, 3675, 3697, 3718, 3738, 3758, + 3777, 3796, 3814, 3832, 3849, 3865, 3881, 3897, 3911, 3925, 3939, 3952, 3964, 3976, 3987, 3998, + 4007, 4017, 4025, 4034, 4041, 4048, 4054, 4060, 4065, 4069, 4073, 4077, 4079, 4081, 4083, 4083, + 4083, 4083, 4082, 4080, 4078, 4075, 4071, 4067, 4063, 4057, 4051, 4045, 4037, 4030, 4021, 4012, + 4003, 3992, 3982, 3970, 3958, 3945, 3932, 3918, 3904, 3889, 3873, 3857, 3841, 3823, 3805, 3787, + 3768, 3748, 3728, 3707, 3686, 3664, 3642, 3619, 3595, 3571, 3547, 3522, 3496, 3470, 3443, 3416, + 3388, 3360, 3331, 3302, 3272, 3242, 3211, 3180, 3149, 3116, 3084, 3051, 3017, 2983, 2949, 2914, + 2878, 2843, 2806, 2770, 2733, 2695, 2658, 2619, 2581, 2542, 2502, 2462, 2422, 2382, 2341, 2300, + 2258, 2216, 2174, 2131, 2088, 2045, 2002, 1958, 1914, 1869, 1825, 1780, 1734, 1689, 1643, 1597, + 1551, 1504, 1458, 1411, 1364, 1316, 1269, 1221, 1173, 1125, 1077, 1028, 980, 931, 882, 833, + 784, 735, 685, 636, 586, 537, 487, 437, 387, 337, 287, 237, 187, 137, 87, 37, +}; diff --git a/sw/opl/opl_dbopl.cpp b/sw/opl/opl_dbopl.cpp new file mode 100644 index 0000000..9708aa1 --- /dev/null +++ b/sw/opl/opl_dbopl.cpp @@ -0,0 +1,179 @@ +// DOSBox OPL core adaptation for PicoGUS +// mostly just copied from opl_ymfm.cpp + +#include "opl.h" +#include "hardware/sync.h" +#include "hardware/timer.h" + +#include "dbopl/dbopl.h" + +#if OPL_CMD_BUFFER +#include "include/cmd_buffers.h" +extern cms_buffer_t opl_cmd_buffer; +#endif + +// --------------------------------------------------------------------------- +// Chip selection +// --------------------------------------------------------------------------- +#ifdef USE_YMF3812 +static constexpr unsigned int OPL_STATUS_BASE = 0x06; // OPL2 detection +#else +static constexpr unsigned int OPL_STATUS_BASE = 0x00; // OPL3 detection +#endif + +// --------------------------------------------------------------------------- +// Timer state — Core 0 only +// --------------------------------------------------------------------------- +typedef struct +{ + unsigned int rate; + unsigned int enabled; + unsigned int value; + uint64_t expire_time; +} opl_timer_t; + +static opl_timer_t timer1 = { 12500, 0, 0, 0 }; +static opl_timer_t timer2 = { 3125, 0, 0, 0 }; + +static void OPLTimer_CalculateEndTime(opl_timer_t *timer) +{ + if (timer->enabled) + { + int tics = 0x100 - timer->value; + timer->expire_time = time_us_64() + + ((uint64_t)tics * OPL_SECOND) / timer->rate; + } +} + +static DBOPL::Chip dbopl3(true); + +// --------------------------------------------------------------------------- +// Pre-generation buffer — batch PREBUF_SIZE samples per generate() call so +// ymfm's inner operator loop runs with code and ROM tables hot in cache. +// Stereo L/R buffers used by both OPL_Pico_simple and OPL_Pico_stereo. +// --------------------------------------------------------------------------- +static constexpr uint32_t PREBUF_SIZE = 128; +static int32_t s_prebuf[2*PREBUF_SIZE]; // interleaved stereo +static uint32_t s_prebuf_head = PREBUF_SIZE; // starts empty → triggers fill on first call + +static void refill_prebuf() +{ + int samples_to_render = PREBUF_SIZE; + int32_t *pb = s_prebuf; + while (opl_cmd_buffer.tail != opl_cmd_buffer.head) { + auto cmd = opl_cmd_buffer.cmds[opl_cmd_buffer.tail]; + OPL_Pico_WriteRegister(cmd.addr, cmd.data); + ++opl_cmd_buffer.tail; + if ((cmd.addr < 0x20) || ((cmd.addr & 0xF0) == 0xB0)) { + // step 1 OPL clock, then continue draining OPL register queue + dbopl3.GenerateBlock3(1, pb); + pb += 2 * 1; + samples_to_render--; + if (samples_to_render == 0) break; + }; + } + + // render the rest + if (samples_to_render > 0) dbopl3.GenerateBlock3(samples_to_render, pb); + + // reset prebuf head + s_prebuf_head = 0; +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +int OPL_Pico_Init(unsigned int port_base) +{ + dbopl3.Setup(49716); + s_prebuf_head = PREBUF_SIZE; + return 1; +} + +unsigned int OPL_Pico_PortRead(opl_port_t port) +{ + unsigned int result = OPL_STATUS_BASE; + __dmb(); + uint64_t pico_time = time_us_64(); + + if (timer1.enabled && pico_time > timer1.expire_time) + { + result |= 0x80 | 0x40; + } + if (timer2.enabled && pico_time > timer2.expire_time) + { + result |= 0x80 | 0x20; + } + + return result; +} + +void OPL_Pico_WriteRegister(unsigned int reg_num, unsigned int value) +{ + switch (reg_num) + { + case OPL_REG_TIMER1: + timer1.value = value; + OPLTimer_CalculateEndTime(&timer1); + break; + + case OPL_REG_TIMER2: + timer2.value = value; + OPLTimer_CalculateEndTime(&timer2); + break; + + case OPL_REG_TIMER_CTRL: + if (value & 0x80) + { + timer1.enabled = 0; + timer2.enabled = 0; + } + else + { + if ((value & 0x40) == 0) + { + timer1.enabled = (value & 0x01) != 0; + OPLTimer_CalculateEndTime(&timer1); + } + if ((value & 0x20) == 0) + { + timer2.enabled = (value & 0x02) != 0; + OPLTimer_CalculateEndTime(&timer2); + } + } + break; + + default: + dbopl3.WriteReg(reg_num, value); + break; + } +} + +// Mono interface — kept for compatibility with non-ymfm OPL backends. +// Returns the average of L+R so mono output is at the same level as stereo. +void OPL_Pico_simple(int32_t *buffer, uint32_t nsamples) +{ + for (uint32_t i = 0; i < nsamples; i++) + { + if (s_prebuf_head >= PREBUF_SIZE) + refill_prebuf(); + buffer[i] = (s_prebuf[2*s_prebuf_head+0] + s_prebuf[2*s_prebuf_head+1]) >> 1; + s_prebuf_head++; + } +} + +// Stereo interface — fills separate left and right buffers. +// For ym3812 both channels are identical (mono chip). +// For ymf262 L and R reflect the per-channel panning programmed by the game. +void OPL_Pico_stereo(int32_t *left, int32_t *right, uint32_t nsamples) +{ + for (uint32_t i = 0; i < nsamples; i++) + { + if (s_prebuf_head >= PREBUF_SIZE) + refill_prebuf(); + left[i] = s_prebuf[2*s_prebuf_head+0]; + right[i] = s_prebuf[2*s_prebuf_head+1]; + s_prebuf_head++; + } +} diff --git a/sw/opl/opl_ymfm.cpp b/sw/opl/opl_ymfm.cpp index e8b9a01..9d22448 100644 --- a/sw/opl/opl_ymfm.cpp +++ b/sw/opl/opl_ymfm.cpp @@ -78,15 +78,16 @@ static void OPLTimer_CalculateEndTime(opl_timer_t *timer) // ymfm's inner operator loop runs with code and ROM tables hot in cache. // Stereo L/R buffers used by both OPL_Pico_simple and OPL_Pico_stereo. // --------------------------------------------------------------------------- -static constexpr uint32_t PREBUF_SIZE = 128; -static opl_chip_t::output_data s_gen_buf; +static constexpr uint32_t PREBUF_SIZE = 128; // must be evenly divisible by PREBUF_SIZE_INNER! +static constexpr uint32_t PREBUF_SIZE_INNER = 8; +static opl_chip_t::output_data s_gen_buf[PREBUF_SIZE_INNER]; static int32_t s_prebuf_l[PREBUF_SIZE]; static int32_t s_prebuf_r[PREBUF_SIZE]; static uint32_t s_prebuf_head = PREBUF_SIZE; // starts empty → triggers fill on first call static void refill_prebuf() { - for (uint32_t j = 0; j < PREBUF_SIZE; j++) + for (uint32_t j = 0; j < PREBUF_SIZE; j += PREBUF_SIZE_INNER) { #if OPL_CMD_BUFFER // Process any pending OPL commands @@ -99,16 +100,20 @@ static void refill_prebuf() } #endif - s_chip.generate(&s_gen_buf, 1); + // generate a new bunch of samples + s_chip.generate(s_gen_buf, PREBUF_SIZE_INNER); + + for (int i = 0; i < PREBUF_SIZE_INNER; i++) { #ifdef USE_YMF3812 - // ym3812 is mono — duplicate to both channels - s_prebuf_l[j] = s_prebuf_r[j] = s_gen_buf.data[0]; + // ym3812 is mono — duplicate to both channels + s_prebuf_l[j+i] = s_prebuf_r[j+i] = s_gen_buf[i].data[0]; #else - // ymf262: use L1 (data[0]) and R1 (data[1]) — the standard stereo pair. - // data[2]/data[3] are the rarely-used L2/R2 extended outputs, ignored. - s_prebuf_l[j] = s_gen_buf.data[0]; - s_prebuf_r[j] = s_gen_buf.data[1]; + // ymf262: use L1 (data[0]) and R1 (data[1]) — the standard stereo pair. + // data[2]/data[3] are the rarely-used L2/R2 extended outputs, ignored. + s_prebuf_l[j+i] = s_gen_buf[i].data[0]; + s_prebuf_r[j+i] = s_gen_buf[i].data[1]; #endif + } } s_prebuf_head = 0; } From 9f4b0b4e9f623a4356057a5228bf936257f615b3 Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Sat, 14 Mar 2026 01:50:03 +0700 Subject: [PATCH 3/5] fix missing stereo --- sw/sbplay.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sw/sbplay.cpp b/sw/sbplay.cpp index d51ab2f..9eb34f5 100644 --- a/sw/sbplay.cpp +++ b/sw/sbplay.cpp @@ -113,7 +113,7 @@ static void opl_resample_tick() { opl_resamp_buf_l[1] = opl_resamp_buf_l[0]; opl_resamp_buf_r[1] = opl_resamp_buf_r[0]; -#if defined(USE_YMFM_OPL) || defined(USE_YMF3812) +#if defined(USE_YMFM_OPL) || defined(USE_DBOPL_OPL) || defined(USE_YMF3812) int32_t l, r; OPL_Pico_stereo(&l, &r, 1); opl_resamp_buf_l[0] = scale_sample(l, opl_volume, 1); From 4815c1329b6edd39e137e02922246c2149bfc0cd Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Wed, 18 Mar 2026 05:14:30 +0700 Subject: [PATCH 4/5] fix OPL3 register output dispatch --- sw/opl/opl_dbopl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sw/opl/opl_dbopl.cpp b/sw/opl/opl_dbopl.cpp index 9708aa1..cc5aa98 100644 --- a/sw/opl/opl_dbopl.cpp +++ b/sw/opl/opl_dbopl.cpp @@ -64,7 +64,7 @@ static void refill_prebuf() auto cmd = opl_cmd_buffer.cmds[opl_cmd_buffer.tail]; OPL_Pico_WriteRegister(cmd.addr, cmd.data); ++opl_cmd_buffer.tail; - if ((cmd.addr < 0x20) || ((cmd.addr & 0xF0) == 0xB0)) { + if (((cmd.addr & 0xFF) < 0x20) || ((cmd.addr & 0xF0) == 0xB0)) { // step 1 OPL clock, then continue draining OPL register queue dbopl3.GenerateBlock3(1, pb); pb += 2 * 1; From 6a343b35b80f578c6cff185e79c4a5e5373d4236 Mon Sep 17 00:00:00 2001 From: wbcbz7 Date: Wed, 18 Mar 2026 05:18:07 +0700 Subject: [PATCH 5/5] resampler: resolve python path --- sw/resampler/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sw/resampler/CMakeLists.txt b/sw/resampler/CMakeLists.txt index 790c528..ff27cd4 100644 --- a/sw/resampler/CMakeLists.txt +++ b/sw/resampler/CMakeLists.txt @@ -1,9 +1,10 @@ +find_package(Python3 REQUIRED COMPONENTS Interpreter) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/taps.hpp #WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/taps.py ${CMAKE_CURRENT_BINARY_DIR}/taps.hpp + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/taps.py ${CMAKE_CURRENT_BINARY_DIR}/taps.hpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/taps.py )