Skip to content

Gravis UltraSound emulation - #2310

Draft
maximilien-noal with Copilot wants to merge 7 commits into
masterfrom
copilot/feature-gravis-ultrasound-emulation
Draft

Gravis UltraSound emulation#2310
maximilien-noal with Copilot wants to merge 7 commits into
masterfrom
copilot/feature-gravis-ultrasound-emulation

Conversation

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

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

Copilot AI linked an issue Aug 13, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add Gravis Ultrasound sound card emulation Add Gravis UltraSound stub: register I/O port handlers for GUS detection Aug 13, 2026
Copilot AI requested a review from maximilien-noal August 13, 2026 17:01
@maximilien-noal

Copy link
Copy Markdown
Member

@copilot I want the full gravis ultrasound, not a skeleton.

https://github.com/dosbox-staging/dosbox-staging see gus.cpp and related files

@maximilien-noal
maximilien-noal marked this pull request as ready for review August 13, 2026 17:24
Copilot AI lite review requested due to automatic review settings August 13, 2026 17:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@maximilien-noal maximilien-noal added Music Music emulation related. Can be any form of Music (FM Synth, MT-32, or General MIDI, ...) hardware hardware device (can be virtual) emulation compatibility Emulator compatibility with DOS apps labels Aug 13, 2026

This comment was marked as outdated.

@maximilien-noal maximilien-noal changed the title Add Gravis UltraSound stub: register I/O port handlers for GUS detection Gravis UltraSound emulation Aug 13, 2026
@maximilien-noal
maximilien-noal requested a lite review from Copilot August 13, 2026 19:04
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Fixed
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Fixed
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Fixed
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GusVoice.cs Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();

Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Outdated
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Fixed
@maximilien-noal
maximilien-noal force-pushed the copilot/feature-gravis-ultrasound-emulation branch 2 times, most recently from 2997e4e to 281db70 Compare September 5, 2026 11:55
@maximilien-noal
maximilien-noal marked this pull request as ready for review September 5, 2026 11:55
@maximilien-noal
maximilien-noal marked this pull request as draft September 5, 2026 11:56
@maximilien-noal
maximilien-noal marked this pull request as ready for review September 5, 2026 13:55
@maximilien-noal
maximilien-noal requested a lite review from Copilot September 5, 2026 13:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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

Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Outdated
Comment thread src/Spice86/Spice86DependencyInjection.cs
@maximilien-noal
maximilien-noal marked this pull request as draft September 5, 2026 14:14
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Fixed
@maximilien-noal
maximilien-noal marked this pull request as ready for review September 5, 2026 15:25
@maximilien-noal
maximilien-noal requested a lite review from Copilot September 5, 2026 15:25
@maximilien-noal
maximilien-noal force-pushed the copilot/feature-gravis-ultrasound-emulation branch from 8f4f5bf to 610f241 Compare September 5, 2026 15:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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

Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GusVoice.cs
Comment thread src/Spice86.Core/Emulator/Devices/Sound/GravisUltraSound.cs Outdated
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.
@maximilien-noal
maximilien-noal force-pushed the copilot/feature-gravis-ultrasound-emulation branch from 610f241 to 5905e6a Compare September 5, 2026 15:43
Comment thread src/Spice86/ViewModels/McpStatusViewModel.cs Fixed
@maximilien-noal maximilien-noal added low priority Fixing this is not urgent, or would take too much manpower for too little gain audio Audio emulation or playback related labels Sep 5, 2026
@maximilien-noal
maximilien-noal force-pushed the copilot/feature-gravis-ultrasound-emulation branch 2 times, most recently from bdd975a to 79c900d Compare September 5, 2026 19:27
@maximilien-noal
maximilien-noal removed the request for review from kevinferrare September 5, 2026 19:54
@maximilien-noal
maximilien-noal marked this pull request as draft September 5, 2026 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

audio Audio emulation or playback related compatibility Emulator compatibility with DOS apps hardware hardware device (can be virtual) emulation low priority Fixing this is not urgent, or would take too much manpower for too little gain Music Music emulation related. Can be any form of Music (FM Synth, MT-32, or General MIDI, ...)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Gravis Ultrasound sound card emulation

4 participants