Bring in SB16/WSS support (bufferless SB DSP, AD1848, SB mixer, IRQ/DMA configuration) and 0.12 fixed-point volume scaling from wss-sb16, and combine with stereo OPL3 pipeline and lock-free SPSC audio FIFO from sb16_merge.
Conflict resolutions:
- volctrl.h: use 0.12 scale from wss-sb16 (no int64_t)
- sbplay.cpp: keep stereo opl_resample_tick() with clamp16 before resampler buffers; lock-free SPSC FIFO reads with stereo volume; add sbdsp_process() from wss-sb16
- pgusinit.c: use wss-sb16's full BLASTER parser (A/I/D/H/T fields)
Adds ymfm as a new OPL synthesis backend alongside the existing emu8950
implementation, selectable at compile time via USE_YMFM_OPL / USE_YMF3812.
- ymf262 (OPL3): 18-channel true stereo via data[0]/data[1] (L1/R1)
- ym3812 (OPL2): mono, duplicated to both channels
- Pre-generation buffer (128 samples) for cache-efficient synthesis
- Stereo FIFO pipeline in sbplay: interleaved L/R pairs, proper resampling
from 49716 Hz to 44100 Hz via Q16.16 phase accumulator
- OPL3 bank 2 port handling in SOUND_SB path (ports +0xA/+0xB) and
SOUND_OPL path (port mask widened to 0x3fc, case 2/3 added)
- opl_addr widened to uint16_t to carry the 0x100 bank 2 flag
- OPL3 bank 2 status read port (oplBasePort+2) added to IOR handler
- Panning bits default to 0x30 (L1+R1) for 0xC0-0xC8 regs when unset,
preventing channels from going silent after newflag (reg 0x105) is set
- pgusinit: BLASTER T type now accepts T3-T6 (adds SB16 OPL3 / AWE32)
- Removed OPL cmd buffer drain spin-wait from IOR path (timer regs are
handled on core 0, no drain needed)
This was an audible in games like Doom. It would cause an occasional small interruption when picking up items.
Core 0 (bus ISR) was calling OPL_writeReg() directly on the emu8950
OPL struct while Core 1 was simultaneously reading it in OPL_calc_buffer()
to generate audio. With no synchronization between the cores, a register
write landing mid-calculation produces torn state — a mix of old and new
operator fields — causing occasional clicks or pops during OPL music.
The OPL_CMD_BUFFER mechanism was already implemented in picogus.cpp and
sbplay.cpp but never enabled. Enable it in CMakeLists.txt for all OPL
targets (pg-adlib, pg-sb).
With OPL_CMD_BUFFER enabled, Core 0 enqueues raw (addr, data) pairs into
a 256-entry SPSC ring buffer. Core 1 drains the queue at the top of each
main loop iteration, before the resampler fill loop, so OPL_writeReg()
only ever runs on Core 1 and never races with OPL_calc_buffer().
Timer registers (0x02–0x04) are carved out and still handled immediately
on Core 0. This preserves the existing invariant that OPL_Pico_PortRead()
and the timer state are both Core 0 only, avoiding a new cross-core race
on the timer structs.
The TLL (Total Level Lookup) table is 128KB — too large for the RP2040's
256KB RAM when the firmware also runs code from RAM. Previous attempts to
use a `const` array failed because the firmware uses a copy_to_ram binary
type, which copies ALL sections including `.rodata` into RAM at boot.
The fix: place the pre-computed table in the `.flashdata.tll_table` linker
section. Both pico-sdk's `memmap_copy_to_ram.ld` and the local
`fw_memmap_c2ram.ld` explicitly capture `.flashdata*` into the FLASH
`.rodata` region, which is accessed via XIP cache and never copied to RAM.
- gen_tll_table.py: new script that pre-computes makeTllTable() using the
EMU8950_NO_FLOAT integer path, outputting tll_table_flash.c with the
`.flashdata.tll_table` section attribute
- tll_table_flash.c: generated 128KB table (32,768 uint32_t values)
- emu8950.c: support EMU8950_TLL_FLASH — use extern const declaration and
skip makeTllTable() at runtime when table is pre-computed in flash
- CMakeLists: replace EMU8950_NO_TLL=1 with EMU8950_TLL_FLASH=1
TLL improves OPL2 accuracy by enabling the full Key Scale Level (KSL)
attenuation lookup table rather than the inline approximation.
- Fix memory leak when changing images (file handle not freed)
- Longer max file length limit (127 chars)
- Better errors when USB not present
- Fix polling of CD status register when listing images
- Add ability to load image by name
- Many weird edge cases with image loading state fixed