Skip to content

Firmware-derived fixes: memory map, Audio Out byte, Start modulation, tracing - #8

Merged
mo0kid merged 8 commits into
mo0kid:mainfrom
xbraindance:firmware-fixes
Oct 3, 2026
Merged

mo0kid merged 8 commits into
mo0kid:mainfrom
xbraindance:firmware-fixes

Conversation

@xbraindance

Copy link
Copy Markdown
Contributor

Based on mo0kid/wave main (c7b0b53, 0.1.12). Contains only changes derived from the OS 1.700 firmware. No tabbed layout, no voice-allocation patch, no reconstruction/DAC or 3-bit level changes; those are separate or held.

Commits

  1. Wave RAM decode and shared SRAM size. Wave RAM is at board 0x040000/0x050000 (write-both alias 0x060000), not 0x600000; the voice bus no longer falls through to master DRAM below 0x100000. The shared SRAM is 512 KB, so the 0x180000 extension decode is dropped on both buses.
  2. Modulation-table typos. Table 0x25E8 follows 8*n^2 except indices 6, 126 and 127; the engine now applies those three entries. Cited oscillator addresses corrected (0xE14/0xECA). This slightly changes the engine at three amounts; easy to drop if you want it separate.
  3. Bundle::load missing-path guard. A nonexistent path fell back to its parent, so the recursive image search walked /.
  4. Research note rewritten with the completed mapping results (firmware, map and oracle stay out of the repo).
  5. WaveVoiceTrace (EXCLUDE_FROM_ALL, no ROMs at build time): runs the real WDV in VoiceFirmwareRuntime from a small script and prints the ASIC/CV/routing-latch write stream, for diffing against an oracle.
  6. Routing latch logged, board selectable in WaveVoiceTrace, board-count limit test.
  7. Audio Out byte. The plugin edited part byte +8; firmware table 0x14BCA and WDV 0xBC6 use +0xC (0 Aux only, 1 main, 2/3 sub). audioOutput now holds the firmware value and only 1 = main reaches the output. Sets with +0xC = 0 leave the main mix.
  8. Start modulation. The route offsets Startphase (sound byte 27/43) at note-on, as in WDV 0xF92/0x1074, instead of the wave position; Startphase 0 is a free start and ignores it. WaldorfEngine::startPhaseCode reproduces the firmware's integer arithmetic.

Start modulation: one correction to the earlier description

The earlier note said the result is "clamped to 1-127". Executing the WDV shows the two oscillators clamp differently, because the sum is formed in 16-bit words:

  • Wave 1 (0xF92) compares signed: no result is ever clamped to 127; an overflowing sum wraps to 8 bits (64 + 127 gives 191, a negative sum gives 128-255).
  • Wave 2 (0x1074) compares unsigned: anything outside 1..126, negative included, becomes 127.

startPhaseCode matches the firmware exactly for a given source word (checked against a transliteration of 0xF92/0x1074/0x246C over every input). Two approximations remain at the engine call site, both marked in the code:

  • The engine's float sources become a 16-bit word at 32768 full scale, but the master stores MIDI controller words as CC << 8 (max 0x7F00, w2sys 0x85A2). Controller-driven start modulation can therefore land one phase step off. This is the engine's general source model and affects every route; per-source word scales would be a separate change.
  • Bit 7 of the 8-bit code is assumed ignored by the ASIC (Startphase is documented 0..127 = one cycle). Not hardware-validated.

Verification

  • ctest: 4/4 (WaveCoreTests, M68000ThreadTests, WaveHostStateTests, WavePublicEditorTests) at every commit on this base, and WaveCoreTests also passes with WAVE_FIRMWARE_DIR set at every commit.
  • New Start-modulation tests: free start, fixed phase, positive and negative modulation, clamping (golden table of 48 cases, both oscillators, executed on the WDV in a Musashi oracle; the table holds numbers only, no firmware); an engine-level note-on check; and, when WAVE_FIRMWARE_DIR is set, the same cases run through the real WDV in VoiceFirmwareRuntime and compared with the ASIC phase-load write (reg 2 = n&3, reg 4 = n>>2).
  • Mutation check: restoring the old 1..127 clamp fails both the golden table and the firmware-backed test independently.
  • Oracle gate (emulator vs oracle ASIC/CV/latch write streams, boards 0-2, this branch's WaveVoiceTrace): 35,004 checks, 0 failures.
  • Not run: WaveEditorTests, WaveIntegrationTests, WavePerformanceBenchmark (need the embedded private assets, which I do not have in this build). WaveEditorTests was edited: its Audio Out fader check now expects record byte +12 instead of +8. Please run it on your side.

Hypotheses, not hardware-verified

The meaning of the Audio Out values beyond the UI labels; the ASIC's handling of bit 7 of the Startphase code.

🤖 Generated with Claude Code

xbraindance and others added 8 commits October 3, 2026 08:37
Wave RAM is at board 0x040000 (A) / 0x050000 (B) with a write-both alias at
0x060000, not 0x600000. The voice bus also fell through to master DRAM below
0x100000, so WDV wave-RAM stores landed in the master heap. The board now sees
only its private 64 KB, the shared SRAM and its devices.

The shared SRAM is 512 KB (0x100000-0x17FFFF); drop the 0x180000 extension
decode on both buses so accesses above it count as unmapped.

Verified: ctest passes (4/4) at this commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…esses

Table 0x25E8 follows 8*n^2 except index 6 (-29912), 126 (31000) and 127
(32767). The oscillator asr.l #3 sites are 0xE14/0xECA, not 0xE34/0xEEA.

Verified: ctest passes (4/4) at this commit. No test covers the table values.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A nonexistent firmware path fell back to its parent directory, so the
recursive image search walked "/" and effectively never returned. Only step up
to the parent when the path is an existing file.

Verified: ctest passes (4/4) at this commit; the new WaveCoreTests case covers the missing-path load.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The 23 August note predated the firmware mapping. Replace it with the
findings from mapping and running both OS 1.700 images (main OS and the
WDV voice-board program) in a 68000 oracle: corrected hardware map,
master-to-voice-board protocol, modulation-table facts, reproduced
firmware bugs, remaining open questions and next steps. The full map,
firmware and gates stay outside the repository.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
WaveVoiceTrace boots the real WDV.SYS in VoiceFirmwareRuntime from a small
command script (poke, until_pc, scan, run, mark) and prints the logged
ASIC/CV write stream with cycle stamps. The firmware repo's
oracle/gate_e_wdv_note_trace.py runs the same script on the Musashi oracle
and diffs the two: one note on voices 3 and 9 matches in order, value and
cycle across all 11,146 checks. Both sides use the same 68000 core, so this
checks bus decode, shared RAM and IRQ plumbing, not the ROM.

The tool is EXCLUDE_FROM_ALL and needs no ROMs at build time. The research
note's next-step checkbox is ticked with the result and the gaps found: the
routing latch (0x8C0000) and board-control latch (0x800001) are neither
logged nor decoded, and the engine's firmware register shadow is unused.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
WDV writes the write-only routing latch (0x8C0000, 8 bytes per voice) but the
voice board neither logged nor decoded it, so the emulator-vs-oracle diff could
not cover it. VoiceFirmwareRuntime now queues those writes with the ASIC/CV
writes (the engine still ignores them) through one queueHardwareWrite helper.
WaveVoiceTrace takes an optional board argument (0-2) so the gate can run all
three voice boards.

The research note records the routing latch layout, the resolved board limit
(3) and three emulator assumptions that disagree with the firmware map; none
of those are changed here.

Verified: ctest passes (4/4). gate_e_wdv_note_trace.py runs boards 0-2 against
the Musashi oracle: 35,004 checks, 0 failures. Both sides use the same 68000
core, so this checks bus decode and plumbing, not the ROM or the hardware.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The plugin edited Instrument part byte +8 as Audio Out, a byte nothing
consumes. The firmware page table 0x14BCA and WDV 0xBC6 use +0xC (0 Aux only,
1 main, 2 sub 1, 3 sub 2). The engine now keeps the firmware value and only
main reaches the plugin output.

Verified: WaveCoreTests incl. the new Audio Out routing test. WaveEditorTests
needs the private firmware assets and was not run; its Audio Out fader check
now expects +12.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The wave start-modulation routes add to Startphase (sound byte 27/43) in WDV
0xF92 (Wave 1) / 0x1074 (Wave 2); a Startphase of 0 is a free start and ignores
the route. The engine offset the wave position instead.

WaldorfEngine::startPhaseCode reproduces the firmware's key-on phase code:
amount table 0x25E8 x source word through 0x246C, summed in 16-bit words. The
two waves do not clamp alike - Wave 1 compares signed, so its 8-bit result
wraps (64 + 127 gives 191); Wave 2 compares unsigned, so any result outside
1..126 becomes 127. The earlier "clamp to 1-127" description was wrong outside
the nominal range. Bit 7 of the 8-bit code is assumed ignored by the ASIC
(hardware-unvalidated).

Tests: golden table executed on the WDV in the Musashi oracle, free start,
fixed phase, positive/negative modulation, clamping, an engine-level note-on
check, and - with WAVE_FIRMWARE_DIR set - the same cases run through the real
WDV in VoiceFirmwareRuntime and compared with the ASIC phase-load write.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mo0kid
mo0kid merged commit 13d1d57 into mo0kid:main Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants