Gravis UltraSound emulation - #2310
Conversation
|
@copilot I want the full gravis ultrasound, not a skeleton. https://github.com/dosbox-staging/dosbox-staging see gus.cpp and related files |
There was a problem hiding this comment.
Copilot wasn't able to review any files in this pull request.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Implemented the full GUS GF1 emulation in commit Register protocol – port All GF1 registers – correct byte ordering (control/vol/pan use the high byte), volume start/end in Reset register (0x4C) – full GF1 reset when Mix control / IRQ – Voice IRQ – Timers – corrected base delays (80 µs / 320 µs), mask bit handling, Lookup tables – vol scalars use the GusVoice – |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs:701
- CheckVoiceIrqs sets IrqVolStateBit/IrqWaveStateBit based on the full _voiceIrq bitmasks, but totalMask is computed using activeVoiceMask. If a program raises IRQ bits on an inactive voice index (>= _activeVoices), the status byte can incorrectly report a pending wave/vol IRQ. Mask the individual wave/vol bitmasks before setting the status bits.
if (_voiceIrq.VolState != 0) {
_irqStatus |= IrqVolStateBit;
}
if (_voiceIrq.WaveState != 0) {
_irqStatus |= IrqWaveStateBit;
src/Spice86.Core/Emulator/Devices/Sound/GusVoice.cs:77
- RenderFrames' early-return checks the intersection of CtrlDisabled bits across WaveState and VolState. If WaveState and VolState are both disabled but with different disabled bits set (e.g., Reset vs Stopped), rendering will incorrectly proceed. This should instead check each control state independently for any disabled bit.
public void RenderFrames(byte[] ram, float[] volScalars, AudioFrame[] panScalars, AudioFrame[] frames, int count) {
// Skip rendering only when BOTH wave AND vol controls are independently disabled,
// matching the GF1 hardware behaviour documented in the UltraSound SDK.
if ((WaveState & VolState & CtrlDisabled) != 0) {
return;
src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs:193
- This PR adds a new hardware device with a non-trivial port map, IRQ, and DMA behavior, but there are no integration tests validating GUS detection/probing or basic IRQ/DMA flows. There is already a sound integration test suite (e.g., SoundIntegrationTests) for SoundBlaster; a similar test that runs a small COM fixture which probes 0x240 and/or checks ULTRASND-related behavior would help prevent regressions and ensure the original crash scenario is covered.
InitPortHandlers(ioPortDispatcher, portBase);
// Mixer setup
mixer.RegisterQueueNotifier(this);
mixer.LockMixerThread();
Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
…staging reference - Fix register data accumulation: port 0x304 byte-write latches low byte only; port 0x305 triggers the write with accumulated 16-bit value (matching GF1 protocol) - Fix all voice register byte ordering: control/rate/vol registers use high byte of accumulated register_data, matching dosbox-staging WriteToRegister() semantics - Add volume register format: vol start/end use (data << 4) * VolumeIncScalar, vol pos uses (register_data >> 4) * VolumeIncScalar - Fix wave address MSW/LSW helpers: proper 13-bit MSW masking and 16-bit LSW masking - Add ResetRegister state (0x4C): full GF1 reset on is_running=0; render guard checks is_running && is_dac_enabled before synthesising voices - Add MixControlRegister: latches_enabled gates IRQ activation; irq_control_selected steers port 0x20B between IRQ and DMA address selection - Add port 0x20B IRQ/DMA address selection with GUS SDK lookup tables - Fix CheckVoiceIrqs: sets irq_status bits 0x20/0x40 for wave/vol IRQs and advances voice_irq.Status to the next pending voice - Fix GetVoiceIrqStatus (reg 0x8F): returns voice_irq.Status|0x20 with inverted vol/ wave bits in the high byte, matching dosbox 0x8F read semantics - Fix timer base delays: 80 µs (timer 1) and 320 µs (timer 2) per GUS SDK spec - Fix timer control register (0x45): updates ShouldRaiseIrq per timer, clears IRQ status bits when IRQ is disabled - Fix timer value writes (0x46/0x47): precompute total delay = (256-value)*baseDelay - Fix OnTimerControl: handles 0x80 expired-flag reset and IsMasked bits - Add missing register reads: 0x41 (DMA control with TC IRQ pending), 0x42 (DMA address), 0x45 (timer ctrl), 0x49 (DMA sample ctrl), 0x4C (reset register), 0x82/0x83 (wave start MSW/LSW), 0x8A/0x8B (wave pos MSW/LSW) - Fix active voices register (0x0E): use 1 + ((register_data>>8) & 31), high byte - Fix DMA address registers (0x43/0x44): correct 20-bit DRAM address reconstruction - Add GetDmaOffset/UpdateDmaAddr for 16-bit DMA channel address translation - Fix vol scalars: constant-dB spacing using DELTA_DB=0.002709201 (dosbox formula) - Fix pan scalars: asymmetric normalisation ensures position 7 is exactly at centre - Fix _outputQueue initialisation: field-level init avoids NullReferenceException in NotifyLockMixer during mixer registration - GusVoice: fix ReadCtrlState to check per-voice irqMask instead of fixed bit 0x80 - GusVoice: fix RenderFrames to skip only when both wave AND vol states are disabled - GusVoice: simplify UpdateWaveState/UpdateVolState to directly compare before/after IRQ state, removing redundant helper methods Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…ts can be combined' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…eters' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…arator comments; fix redundant cast Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com>
d6f007b to
8197da5
Compare
Description of Changes
Adds Gravis Ultrasound sound card emulation to spice86.
Rationale Behind Changes
The Gravis UltraSound (GUS) sound card is used by real-mode DOS games and demos. Spice86 had no GUS presence at all, causing programs that probe for it to fail or crash.
Suggested Testing Steps
ULTRASND=240,7,7,5,5and probes port0x240). Confirm it no longer crashes or throws an unhandled I/O port exception.