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.
This comment was marked as outdated.
This comment was marked as outdated.
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();
2997e4e to
281db70
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The GUS timer implementation is currently one-shot (does not reschedule), which breaks expected periodic timer behavior and can impact real driver/program operation.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 9/9 changed files
- Comments generated: 3
- Review effort level: Lite
8f4f5bf to
610f241
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces a large new hardware emulation device and scheduler/DMA/timer logic, and the review found correctness and performance issues that should be addressed and re-validated with broader testing.
Review details
- Files reviewed: 15/15 changed files
- Comments generated: 3
- Review effort level: Lite
Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> feat: implement full Gravis UltraSound GF1 emulation based on dosbox-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> Potential fix for pull request finding 'CodeQL / Cast to same type' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Potential fix for pull request finding 'CodeQL / Nested 'if' statements can be combined' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Potential fix for pull request finding 'CodeQL / Too many 'ref' parameters' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> fix: correct DMA word/byte usage, DRAM wrap, invert logic; remove separator comments; fix redundant cast Co-authored-by: maximilien-noal <1087524+maximilien-noal@users.noreply.github.com> refactor;: GusVoice class and update GravisUltraSound integration - Changed GusVoice class from internal to public to allow external access. - Added XML documentation comments for public properties and methods in GusVoice. - Updated the handling of wave and volume control states in GusVoice. - Modified the Machine class to support nullable GravisUltraSound instance. - Adjusted Spice86DependencyInjection to conditionally create GravisUltraSound based on configuration. - Enhanced DOS environment variable setup to include GravisUltraSound details only when enabled. Implement Gravis UltraSound enhancements and OPL3 command mirroring - Added support for dual DMA channels in Gravis UltraSound, allowing for separate playback and recording DMA addresses. - Introduced a command mirroring feature for AdLib commands in the OPL3 FM synthesizer, enabling better integration with the Gravis UltraSound. - Enhanced the rendering logic in Gravis UltraSound to ensure audio frames are processed accurately based on elapsed time. - Updated the SoftwareMixerView to improve the UI layout for audio channel controls, including volume sliders and waveform displays. - Added comprehensive unit tests for Gravis UltraSound to validate functionality, including reset behavior, IRQ handling, and DMA transfers.
610f241 to
5905e6a
Compare
bdd975a to
79c900d
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.
Suggested Testing Steps
Gravis Ultrasound Tests should pass