Commit Graph
537 Commits
Author SHA1 Message Date
Ian Scott 3be5cd168c SB: add missing DSP_CONTINUE_DMA_16 (0xD6) command and fix names of other DMA pause/continue commands 2026-03-31 18:27:41 -06:00
Kaede dab24789ec Comment: Fix speed + revert to original algorithm 2026-03-30 21:13:19 -06:00
Ian Scott ac49d6c9b5 Fix Miles AIL detection of SB16 2026-03-30 21:10:45 -06:00
Ian Scott 29da06d8ea Fix multifw build 2026-03-30 08:25:53 -06:00
Ian Scott 7dcf583ff6 Boost OPL volume 1.5X 2026-03-29 20:58:27 -06:00
Ian Scott a3a9a2f41c Passthrough SB line-in volume to wt header volume 2026-03-29 20:57:57 -06:00
Ian Scott ac6f3774db Support variable output sample rate w/ new IRQ-driven output for GUS 2026-03-29 20:51:44 -06:00
Ian Scott d0c699fde5 Lock CheckVoiceIrq for GUS 2026-03-29 16:26:23 -06:00
Ian Scott bcd1c76f12 Overclock at runtime instead of at start to help stability on marginal boards 2026-03-29 16:25:55 -06:00
Ian Scott 553a6a889a SB-like audio IRQ for GUS 2026-03-29 15:56:01 -06:00
Ian Scott 8c5e16eadb Bring in some changes from 86box to support default win9x MKE driver 2026-03-29 15:01:03 -06:00
Ian Scott 89ab467f28 OPL3 fixes for SB16 2026-03-29 12:28:04 -06:00
Ian Scott 35caedfd40 Reduce MIDI buffer sizes in non-MPU modes 2026-03-29 12:28:04 -06:00
Ian Scott 6de116068c Clamp with hardware interpolator 2026-03-29 12:28:04 -06:00
Ian Scott cef8e62e44 Refactor polyphase resampler to allow for stereo resampling 2026-03-29 12:28:04 -06:00
Ian Scott 6afc44b2ad Convert audio FIFOs to stereo sample pairs 2026-03-29 09:21:29 -06:00
Ian Scott db355ec4d6 Merge branch 'wss-sb16' into sb16_merge
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)
2026-03-28 23:50:22 -06:00
Ian Scott cee1081a03 Implement audio_fifo as lock-free SPSC queue to not require disabling interrupts 2026-03-28 22:06:18 -06:00
wbcbz7 d0510ad50c resampler: resolve python path 2026-03-28 11:53:01 -06:00
wbcbz7 55733453c4 fix OPL3 register output dispatch 2026-03-28 11:53:01 -06:00
wbcbz7 3bae77c864 fix missing stereo 2026-03-28 11:53:01 -06:00
wbcbz7 328d5c16e0 add DOSBox OPL3 core 2026-03-28 11:53:01 -06:00
wbcbz7 e53696a3a1 fix ymfm register writes, use linear OPL3 output resampling 2026-03-28 11:53:01 -06:00
Daniel Arnold 82de83fe3a Update versioning v3.3.0-beta 2026-03-28 11:53:01 -06:00
Daniel Arnold 82fa438059 feat(opl): add ymfm OPL2/OPL3 backend with true stereo output
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)
2026-03-28 11:53:01 -06:00
Daniel Arnold 2def019127 fix(audio): fix samples_in_fifo race between core 1 main loop and PWM ISR
fifo_add_sample() increments samples_in_fifo with a non-atomic
load-add-store. The PWM ISR (audio consumer) runs on the same core
and can preempt the main loop (OPL producer) between the load and
store, decrementing the counter mid-update. The ISR's decrement is
then overwritten by the main loop's store, inflating samples_in_fifo
by 1. Over time this drift could cause the producer to throttle itself
early or the consumer to read a stale sample.

Wrap the increment and start-threshold check in fifo_add_sample() with
save_and_disable_interrupts()/restore_interrupts(). The IRQ disable
window is minimal (~4 cycles) and only covers the counter update — the
buffer write and write_idx advance are outside the critical section
since write_idx is single-writer (producer only) and the ISR never
reads it.
2026-03-28 11:53:01 -06:00
Daniel Arnold 3365378a77 fix(opl): eliminate cross-core OPL struct race via OPL_CMD_BUFFER
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.
2026-03-28 11:53:01 -06:00
Daniel Arnold 40a0c9b483 fix(opl): fix OPL Timer 2 enable writing to timer1.enabled instead of timer2.enabled
In OPL_Pico_WriteRegister(), the register 0x04 (TIMER_CTRL) handler
had a copy-paste error in the Timer 2 enable path: it wrote the Timer 2
start bit (0x02) into timer1.enabled instead of timer2.enabled.

This meant timer2.enabled was always 0, so OPLTimer_CalculateEndTime()
never set timer2.expire_time, and the status register port read never
set bit 5 (Timer 2 expired). The bug also had a secondary effect:
writing 0x22 (enable Timer 2) would accidentally re-enable timer1.

The canonical OPL detection sequence used by the Doom engine and many
other DOS titles tests both timers — it expects bit 6 (Timer 1) and
bit 5 (Timer 2) to be set in sequence. With Timer 2 broken, games
running this detection would see it fail and potentially fall back to
PC speaker or no music. Music drivers that poll Timer 2 for their
playback interrupt would also hang silently.
2026-03-28 11:53:01 -06:00
Daniel Arnold f3220f736a opl: enable EMU8950 TLL via pre-computed table in flash (.flashdata section)
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.
2026-03-28 11:53:01 -06:00
Daniel Arnold 1ceb476dfd Improve OPL synthesis quality 2026-03-28 11:53:01 -06:00
Daniel Arnold 9e03d4893a feat(opl): widen OPL pipeline to int32_t for clean gain and precision
Replace the int16_t intermediate in the OPL output path with int32_t
throughout: emu8950 mix_output_raw, OPL_calc_buffer, OPL_Pico_simple,
and get_opl_sample.

Remove the >> 1 attenuation in _MO() that was required to prevent
int16_t overflow on multi-channel sums. This eliminates 1 bit of
permanent precision loss per channel sample.

Use int64_t intermediate in scale_sample() to prevent int32_t overflow
when opl_volume is boosted above 1.0x (was undefined behaviour at high
volumes with the old approach).

Apply a 6x OPL gain constant in CMD_OPLVOL to compensate for the
removed attenuation and provide comfortable headroom. Per-channel peak
rises from ~25% to ~75% of full scale at 100% oplvol; the recommended
operating point is ~87% oplvol, leaving ~13% of range for boosting
quiet music. The volume setting now correctly attenuates the full-
precision signal rather than scaling an already-clipped intermediate.
2026-03-28 11:53:01 -06:00
Daniel Arnold d047cfe1c7 fix(cdrom): fix audio track seek offsets in CUE images without INDEX 00
When a CUE track has no INDEX 00 (no explicit pregap), prestart was
left at 0, causing skip = cur->start - 0 = full absolute frame position.
This made cdi_add_track compute a negative (wrapping) prev->length and
corrupted skip offsets for all subsequent tracks.

Fix: track whether INDEX 00 was actually seen per-track (has_prestart).
When absent, pass trk.start as prestart so skip computes to 0. Affects
all three cdi_add_track call sites in cdi_load_cue. CUEs with INDEX 00
are unaffected.
2026-03-28 11:53:01 -06:00
Daniel Arnold 96cabd4b7c fix(cdrom): recover from error state on /cdlist; fix image path edge cases
CMD_CDLIST now queues image list when status is ERROR, not just IDLE,
fixing permanent stuck state after a failed /cdload or /cdloadname

isCDImage: fix len<=4 guard so ".iso"/".cue" are rejected; hide all
dot-files instead of only macOS ._-prefixed files

Replace strcpy with strncpy for image_path to prevent buffer overrun
2026-03-28 11:53:01 -06:00
Daniel Arnold bddf6c43fb cdrom: add memory barrier before setting CD_STATUS_PLAYING
Ensures seek_pos, cd_end, and fifo_reset are all visible to core 1 before cd_status is set to PLAYING, preventing core 1 from starting audio consumption against a partially-updated state.
2026-03-28 11:53:01 -06:00
Daniel Arnold 7efd92539d cdrom/mke: DOS protocol correctness fixes
Seven correctness fixes across cdrom.c and mke.c:

cdrom_status(): initialize status to 0 to prevent random stack bits in the status byte sent to DOS

cdrom_disc_capacity(): guard ti.f - 1 against uint8 underflow when frame is 0

CMD1_READ_VER: pad response to 12 bytes as expected by SBPCD driver

CMD1_GETMODE: set data mode byte to 0x01 (Mode 1) instead of 0x00

CMD1_ABORT: clear full command buffer (bytes 0–6), not just byte 0

CMD1_READSUBQ: move cdrom_fifo_clear() to before the subchannel read, not after

CMD1_READTOC: check return value of cdrom_read_toc() before writing to FIFO
2026-03-28 11:53:01 -06:00
Daniel Arnold 223c5f966e sbplay: cap OPL fill loop to 256 samples per iteration
Prevents cdrom_tasks() from being starved when the OPL FIFO is nearly empty. The 4096-sample FIFO (~93ms) provides sufficient headroom to absorb the capped fill rate without risk of OPL underrun.
2026-03-28 11:53:01 -06:00
Daniel Arnold 7935975408 cdrom: USB/FAT stability fixes
Fixes three reliability issues in msc_app.c:

Add 2-second timeout to wait_for_disk_io() to prevent firmware lockup when a USB drive stops responding mid-transfer

Propagate SCSI errors from disk_io_complete() — disk_read()/disk_write() now return RES_ERROR on failure instead of always returning RES_OK

Guard tuh_msc_umount_cb() against a second MSC device disconnect tearing down the active drive's filesystem
2026-03-28 11:53:01 -06:00
Daniel Arnold 1507446976 Fix CD-ROM Red Book audio distortion and buffer gaps
Several bugs caused CD audio to crackle, distort, and stutter when a USB HID device was connected.

usbplay.cpp, psgplay.cpp: Switch take_audio_buffer() from blocking to non-blocking with a tuh_task() poll loop, matching the existing pattern in gusplay.cpp. Add 500ms boot-time USB pump. Prevents TinyUSB keep-alive timeouts from colliding with audio DMA fills.

sbplay.cpp: Remove private cd_index and opl_out_index static cursors in the PWM IRQ handler. Both were decoupled from their FIFO's read_idx and drifted after any fifo_reset() (seek/pause/stop), causing stale/out-of-order sample reads. Now reads directly from fifo->buffer[fifo->read_idx] before advancing.

audio_fifo.h, sbplay.cpp: Increase AUDIO_FIFO_SIZE from 1024 to 4096 samples (~93ms). One CD sector is 1176 samples; the old buffer was smaller than a single sector, so slow FAT/USB reads caused underruns. Fill target updated from hardcoded 1024 to AUDIO_FIFO_SIZE - SAMPLES_PER_SECTOR.

cdrom.c: Don't call fifo_reset() on resume in cdrom_audio_pause_resume() — only flush on pause/stop. Flushing on resume discarded buffered audio and caused a gap.

cdrom.c: Fix cdrom_get_subq() to report the currently-audible position rather than the next-to-read sector. Subtracts buffered FIFO sectors from seek_pos before calling get_subchannel().

audio_fifo.h: Lower AUDIO_FIFO_START_THRESHOLD from 2048 (~46ms) to 512 (~11ms) to reduce post-seek silence.
2026-03-28 11:53:01 -06:00
wbcbz7 5e8dec0c89 pgusinit: add option to init GUS/SB mode reosurces (base port, IRQ, DMA) from ULTRASND/BLASTER environment variable. 2026-03-28 23:05:35 +07:00
wbcbz7 38d485ef01 Revert "temp overclock UART to 2 Mbps"
This reverts commit 0e88976b60.
2026-03-28 22:02:00 +07:00
wbcbz7 6014def2f9 SB: comment out DSP debug output 2026-03-28 21:59:40 +07:00
wbcbz7 af97e8fd34 SB: fix reentrancy issues 2026-03-26 05:46:15 +07:00
wbcbz7 0e5d95c125 fix DMA multi PIO state machine 2026-03-26 02:48:27 +07:00
wbcbz7 fa9c38e139 SB: remove wait for DACK deassertion in dma_write_multi
(makes it work in DMA controller demand mode - fixes Unreal/FC in SBPro mode)
2026-03-24 02:46:24 +07:00
wbcbz7 0e88976b60 temp overclock UART to 2 Mbps 2026-03-23 18:54:29 +07:00
wbcbz7 aaf36749f2 SB: move dsp_process() to second core, tweak DSP reset procedure 2026-03-23 17:58:08 +07:00
wbcbz7 71434e2c2c SB: implement DSP commands 0xE2 and 0x20 2026-03-23 04:39:25 +07:00
wbcbz7 fadce640cc SB: reset more stuff, fix TC-related oversight 2026-03-22 05:25:28 +07:00
wbcbz7 c02e695950 SB: clock down DMA PIO, minor sbdsp_set_type() fix 2026-03-22 04:26:34 +07:00
wbcbz7 ba3e2edaa4 fix SB types and erratic fallthrough in CMD_SBDMA 2026-03-22 01:26:03 +07:00