feat(audio): add single-bank wave channel playback

This commit is contained in:
0xrsydn 2026-10-07 06:49:32 +07:00
commit f496f12ea3
20 changed files with 860 additions and 44 deletions

View file

@ -64,9 +64,13 @@ The repository is a Cargo workspace with three crates:
| `crates/gba-core/src/cpu/fetch_history_tests.rs` | Shared sample consumption, no execution-entry rereads, and failed-step retry |
| `crates/gba-core/src/cpu/fetch_boundary_tests.rs` | Actual fetch-region snapshots, split refills, IWRAM lane requirements, ROM-window boundaries, and nominal timing |
| `crates/gba-core/src/io.rs` | I/O registers, timers, HALT/STOP wake-up, next-event bounds, and IRQ latches |
| `crates/gba-core/src/audio.rs` | Direct Sound state, PSG sequencer, idle channel/wave banks, and instantaneous stereo mixing |
| `crates/gba-core/src/audio.rs` | Direct Sound state, PSG sequencer, wave banks, and instantaneous stereo mixing |
| `crates/gba-core/src/audio/pulse.rs` | Shared pulse oscillator; sweep is mapped only for channel 1 |
| `crates/gba-core/src/audio/modulation.rs` | Shared PSG length, envelope, logical DAC gate, and activity state |
| `crates/gba-core/src/audio/wave.rs` | Single-bank wave samples, rotating RAM, length/gate state, and fractional gain |
| `crates/gba-core/src/audio/wave/tests.rs` | Every sample rate/bank, nibble-order reference, full rotations, batches, volumes, and length edges |
| `crates/gba-core/tests/wave_sound.rs` | Wave masks, bank access, stereo gain, status, HALT/STOP, reset, and explicit unsupported modes |
| `crates/gba-core/src/memory/wave_step_tests.rs` | Wave trigger/status bus phases, rotated RAM transactions, ARM/Thumb/DMA, and rollback |
| `crates/gba-core/src/audio/noise.rs` | Noise channel 4 divider, 7/15-bit counter, and bounded jump-ahead advancement |
| `crates/gba-core/src/audio/noise/tests.rs` | Counter periods, independent bit-array reference, divider fields, and clock batching |
| `crates/gba-core/tests/noise_sound.rs` | Noise registers, modulation, stereo mixing, independent status, HALT/STOP, and capture independence |

View file

@ -1,7 +1,7 @@
# Audio device subset
The core models Direct Sound A/B, bounded sound DMA, and programmable sound generator (PSG) pulse channels 1–2 and noise channel 4.
It does not produce desktop audio. PSG wave channel 3 remains unsupported.
The core models Direct Sound A/B, bounded sound DMA, and programmable sound generator (PSG) channels 1–4.
Wave channel 3 supports single-bank playback only. The core does not produce desktop audio.
`Memory::audio_level()` exposes the current digital stereo level for deterministic inspection, not a continuous sample stream.
## Registers and PSG state
@ -11,12 +11,12 @@ It does not produce desktop audio. PSG wave channel 3 remains unsupported.
| `0x04000060..0x04000065` | Pulse channel 1 sweep, duty, length, envelope, frequency, and trigger |
| `0x04000068..0x04000069` | Pulse channel 2 duty, length, and envelope |
| `0x0400006c..0x0400006d` | Pulse channel 2 frequency and trigger; no sweep unit |
| `0x04000070..0x04000075` | Idle wave channel 3 configuration |
| `0x04000070..0x04000075` | Wave channel 3 gate, bank, length, volume, frequency, and trigger |
| `0x04000078..0x04000079` | Noise channel 4 length and envelope |
| `0x0400007c..0x0400007d` | Noise divider, counter width, shift, length enable, and trigger |
| `0x04000080..0x04000081` | PSG stereo routing and volume |
| `0x04000082` (`SOUNDCNT_H`) | Mixing, routing, timer selection, and FIFO reset strobes; readback mask `0x770f` |
| `0x04000084` (`SOUNDCNT_X`) | Master enable in bit 7; channel 1/2/4 activity in read-only bits 0/1/3; channel 3 remains inactive |
| `0x04000084` (`SOUNDCNT_X`) | Master enable in bit 7; channel 1–4 activity in read-only bits 0–3 |
| `0x04000088` (`SOUNDBIAS`) | Bias/resolution configuration with mask `0xc3fe` |
| `0x04000090..0x0400009f` | CPU wave RAM window, opposite the bank selected by `SOUND3CNT_L` bit 6 |
| `0x040000a0..0x040000a7` | FIFO A/B writes with byte, halfword, and word access boundaries preserved |
@ -31,10 +31,9 @@ The CPU then accesses wave bank 1. Idle bank selection can expose bank 0 after m
The original BIOS reset clears bank 1 and both FIFO queues. It retains bank 0 and any separate in-flight playback word.
This is a functional reset subset, not a claim of complete firmware ordering or sound-reset equivalence.
An enabled wave channel 3 trigger returns `MemoryError::UnsupportedIo` with the exact address and value.
This applies even when the configured channel would be inaudible. All PSG writes are ignored while master sound is disabled.
Unsupported two-bank wave playback and active wave-bank changes return `MemoryError::UnsupportedIo` with the exact address and value.
All PSG writes are ignored while master sound is disabled.
Reserved PSG volume selection 3 is rejected while master sound is enabled, including enable after disabled configuration.
Idle wave channel 3 writes do not imply playback support.
## Pulse channels 1 and 2
@ -122,6 +121,56 @@ An independent bit-array reference checks these jump-ahead operations, including
Both complete sequence periods and maximum clock batches have deterministic regressions.
This is device-state advancement, not a sampled audio stream or hardware recording comparison.
## Wave channel 3: single-bank playback
The selected bank supplies 32 four-bit samples. The CPU reads and writes the opposite bank, including during playback.
SOUND3CNT_L retains gate, bank, and dimension bits with mask `0xe0`.
The volume register retains bits 13–15. Frequency, length-load, and trigger fields are write-only.
The unused halfword at `0x04000076` reads zero and ignores writes.
Each nominal sample period is `8 * (2048 - frequency)` system clocks.
Playback consumes each byte's high nibble before its low nibble, then proceeds to the next byte.
The selected bank rotates by one nibble per sample; it is not immutable storage addressed by a hidden pointer.
Stopping playback and exposing that bank therefore shows rotated data.
Complete 32-sample rotations restore the bank layout but still update the held output sample.
Clock batches compute the final rotation and sample directly, without looping once per sample.
A trigger reloads a full sample period and clears the held output to nominal silence until the first edge.
It cannot restore data already rotated in wave RAM. Retrigger uses the bank's current layout.
Frequency writes affect the next reload, not the remaining sample interval.
Clearing the gate stops activity. Setting the gate again requires a trigger to resume.
A trigger with the gate clear is accepted but cannot activate playback, including with an idle two-bank configuration.
Mute volume and disabled routes do not stop playback or clear activity.
Master disable resets channel configuration/state but preserves both wave RAM banks in their current layouts.
The length counter loads `256 - length_field` and uses the shared 256 Hz length clocks.
It follows the same nominal extra-clock/reload rule as other PSG channels, with maximum length 256 rather than 64.
A nonempty counter survives retrigger. Enabled expiry clears SOUNDCNT_X bit 2.
HALT continues sample and length clocks; STOP freezes them. No timer or IRQ enable is required.
The centered full-volume amplitude is `2 * (sample - 8)`, from -16 through +14.
Volume selects mute, 100%, 50%, or 25%. Force-volume overrides all selections with 75%.
The mixer retains quarter-units until after combining PSG sources, stereo volume, and the overall PSG ratio.
This avoids separately rounding fractional wave amplitudes. It follows the signed NanoBoyAdvance mixer comparison, not an analog recording.
### Evidence limits
Only dimension zero playback is implemented. Idle dimension-one configuration is writable, but triggering it with the gate enabled is diagnostic.
Selecting dimension one or changing the selected bank during active playback also remains diagnostic; clear the gate first.
These failures retain register lanes, RAM rotation, activity, and clocks through ordinary audio transactions.
GBATEK describes rotating wave RAM and selected-bank-first two-bank playback.
The pinned mGBA implementation rotates storage, while NanoBoyAdvance uses a sample pointer and changes bank at wrap.
Their two-bank starts, observable bank state, and retrigger behavior differ.
This increment does not choose an unverified two-bank model merely to accept more register writes.
The single-bank order/rate follows GBATEK; trigger phase, held-sample startup, live frequency/volume edges, and length quirks remain nominal.
There is no Game Boy wave-corruption model or physical GBA waveform validation.
Original tests cover every frequency and both single-bank selections, independent nibble rotation, complete rotations, maximum batches, and volume settings.
Integration checks cover masks, gating, status, stereo mixing/clipping, length, HALT/STOP, master reset, and BIOS reset.
ARM/Thumb/DMA tests verify trigger bus phases, status at length expiry, rotated RAM readback, and failed-step/block-store isolation.
Original terminal and native-window programs activate channel 3 without external game assets or host audio.
## FIFO and timer behavior
Each channel has a seven-word queue, a separate four-byte playback word, and a held signed sample.
@ -167,11 +216,12 @@ Sub-instruction DMA arbitration and exact refill latency are not modeled.
Each routed Direct Sound sample contributes `sample * 2` at 50% volume or `sample * 4` at 100% volume.
Each active pulse or noise channel contributes a centered signed amplitude, `+volume` or `-volume`.
`SOUNDCNT_L` routes channel 1 through bits 8/12, channel 2 through bits 9/13, and noise through bits 11/15, for right/left respectively.
`SOUNDCNT_L` routes channel 1 through bits 8/12, channel 2 through bits 9/13, wave through bits 10/14, and noise through bits 11/15.
Each pair selects right/left respectively.
The mixer sums routed PSG amplitudes on each side, then multiplies by that side's volume field plus one.
`SOUNDCNT_H` applies the PSG ratio: 25%, 50%, or 100%, using one arithmetic right shift after summation.
Rounding each PSG channel separately would produce different low-volume levels and is not used.
At full volume, each supported PSG channel spans `-120..120` before mixing with Direct Sound.
At full stereo volume, each pulse/noise channel spans `-120..120`; wave spans `-128..112`, before mixing with Direct Sound.
This centered mixer follows GBATEK's signed range and the NanoBoyAdvance comparison; exact analog offset is not modeled.
The mixer adds all supported channels and the ten-bit bias, clips each side to `0..1023`, and subtracts 512.
`StereoLevel` therefore contains signed levels in `-512..511`. Master disable returns zero.
@ -201,12 +251,16 @@ CPU/DMA tests include noise register writes at bus completion and failed-step ro
BIOS reset tests verify that selected sound reset stops all supported PSG channels, while unselected sound state remains active.
These tests validate the documented nominal model, not hardware audio fidelity.
Remaining audio work: wave channel 3, a timestamped or fixed-rate output stream, PWM/mixer sampling, and a Darwin host backend.
The local Emerald startup now passes the pulse and noise triggers, then stops at GPIO serial interrupt control.
Remaining audio work: two-bank wave playback, verified live bank changes, a continuous output stream, PWM/mixer sampling, and a Darwin host backend.
A local Emerald input probe exposed a wave trigger with the gate disabled. This is now accepted without activating playback.
The same scheduled-input probe passes that point and completes its extended frame budget; audible output and gameplay remain unverified.
See [the local runtime result](../research/emerald-reset.md).
## References
- [GBATEK wave channel 3](https://problemkaputt.de/gbatek-gba-sound-channel-3-wave-output.htm): bank selection, nibble order, rotating RAM, sample rate, length, and force volume.
- [NanoBoyAdvance wave at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/wave_channel.cc) and [header](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/wave_channel.hh): signed gain and rate comparison; pointer-based RAM and two-bank/retrigger differences are not copied.
- [GBATEK noise channel 4](https://mgba-emu.github.io/gbatek/): length/envelope registers, divider formula, Galois counter seeds, masks, and sequence periods.
- [NanoBoyAdvance noise at 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/noise_channel.cc) and [noise header](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/hw/apu/channel/noise_channel.hh): GBA divider scaling, counter feedback, trigger, and register behavior comparison.
- [GBATEK channels 1 and 2](https://mgba-emu.github.io/gbatek/): register fields, duty ratios, modulation rates, channel 2's register gap, and absence of sweep.

View file

@ -253,7 +253,7 @@ Supported flags can be combined:
| 7 | Reset supported display, DMA, timer, and interrupt registers as described below |
All low-byte flag combinations, including `r0=0xff`, are accepted within the supported device subset.
This is not full audio or serial support. [wave channel 3](audio.md) and [serial transfers](serial.md) still fail explicitly.
This is not full audio or serial support. [Two-bank wave playback](audio.md) and [connected serial transfers](serial.md) remain unsupported.
Sound reset retains wave bank 0 and the separate in-flight playback word; exact firmware sound-reset equivalence is not established.
The firmware always clears the low halfword of SIODATA32, even when bit 5 is clear; its upper halfword is preserved.
@ -544,6 +544,6 @@ Unsupported SWIs, unsupported exception vectors, and null/misaligned IRQ callbac
The CPU reports `CpuError::UnsupportedInstruction` with `bios::UNSUPPORTED_TRAP`, rather than silently treating a service as a no-op.
Invalid arithmetic/decompression arguments use the same CPU error type with the distinct `bios::INVALID_ARGUMENT_TRAP` instruction.
Prior boot/service steps remain committed on failure.
PSG wave channel 3, host audio output, connected serial links, HardReset, and serial/Game Pak STOP wake-up remain unimplemented.
Two-bank wave playback, host audio output, connected serial links, HardReset, and serial/Game Pak STOP wake-up remain unimplemented.
Disconnected normal serial transfers use the [bounded serial model](serial.md); cancel active transfers before BIOS serial reset.
This subset is not sufficient for Pokémon Emerald compatibility.

View file

@ -114,9 +114,9 @@ Workspace debug/release tests, strict Clippy, public ARM/Thumb/memory/BIOS check
Native ROM-window tests and graphics smoke modes pass on Darwin arm64.
The pinned Pong debug/release reports match.
Next, inspect visible startup/menu progress and exercise button presses and releases in a bounded run.
Identify any waiting loop or new diagnostic before selecting another compatibility correction.
Do not treat frame-limit success as a title-screen or gameplay pass.
A subsequent [scheduled-input probe](emerald-wave.md) exposed a wave-channel trigger with its gate disabled.
The general single-bank wave model now passes that point and completes an extended Start/A input schedule.
Visible menu semantics and gameplay still require verification; frame-limit success alone cannot establish either.
Flash programming, erase, busy behavior, and safe persistence remain required before claiming save compatibility.
RTC persistence and cartridge IRQ behavior remain separate missing features.
Ordinary ROM writes remain read-only. Without `--rtc`, the earlier GPIO diagnostic is still expected.
@ -125,7 +125,7 @@ This result does not establish gameplay, linked transfers, or cartridge/serial G
Noise now has deterministic counter clocks and shares tested length/envelope logic with the independent pulse channels.
Sweep applies only to channel 1. Direct Sound has FIFO clocks and nominal DMA requests.
All supported channels use the instantaneous digital-level inspection interface.
Wave channel 3, a continuous sample stream, and desktop output are still missing.
Wave channel 3 now supports single-bank playback. Two-bank playback, a continuous sample stream, and desktop output are still missing.
Do not treat digital-level tests as audible-game validation or suppress remaining diagnostics without implementing their behavior.
Cartridge saves remain a separate required feature. SRAM alone will not support Emerald's Flash save protocol.

View file

@ -0,0 +1,97 @@
# Local Emerald input probe: wave trigger
## Claim and method
The earlier 600-frame native run established execution without a diagnostic, not gameplay.
This probe checks whether scheduled input changes captured output and exposes another unsupported operation.
The game remains a local user-supplied file. No ROM bytes, assets, screenshots, or extracted instructions are committed or submitted to Jev.
A temporary host driver uses public core APIs:
1. Boot the local ROM with `bios::boot` and select `CartridgeHardware::Rtc` and `SaveDevice::Flash128`.
2. Set deterministic RTC components to `[24, 1, 1, 1, 12, 0, 0]`. Do not advance this clock during the probe.
3. Enable scanline rendering and call `Machine::run_until_vblank(400000)` once per requested frame.
4. Apply each input snapshot after the named VBlank, before the next machine step.
5. Require `present_frame` to return a complete image. Stop on any execution/render diagnostic.
6. Compare captured pixels locally against a fresh no-input run with identical hardware/time configuration.
The initial budget is 900 VBlanks. Each frame has its own machine-step bound; this is not the CLI's total step budget.
A follow-up extends the limit to 1,320 VBlanks. The temporary driver rejects requests above 1,500 VBlanks.
These are local diagnostic runs, not committed ROM-suite assertions or a commercial compatibility test.
The current manifest versions cannot select this cartridge hardware, so `--test-suite` cannot reproduce this setup directly.
| VBlank | Buttons held after the event |
| --- | --- |
| 0 | None |
| 600 | Start |
| 610 | None |
| 720 | Start |
| 730 | None |
| 840 | Start |
| 850 | None |
| 960 | A, extended run only |
| 970 | None |
| 1080 | A, extended run only |
| 1090 | None |
| 1200 | A, extended run only |
| 1210 | None |
## Observed failure
Before wave support, the first Start press changed captured output.
Execution then stopped before frame 638:
```text
PC=0x082e104c
successful steps through frame 637: 62597533
unsupported PSG channel trigger (synthesis not implemented): write 0x86 at 0x04000075
SOUND3CNT_L readback: 0
SOUND3CNT_H readback: 0
```
The wave gate was disabled. A trigger in this state must not activate channel 3.
Rejecting every trigger was an overly strict feature limit, not evidence that the game needed audible wave output at that moment.
The correction implements general [single-bank wave behavior](../hardware/audio.md#wave-channel-3-single-bank-playback), including the inactive trigger case.
It does not patch this game's instruction or skip its write.
## Result after the correction
The unchanged initial schedule reaches 900 frames and 89,199,123 successful machine steps without a diagnostic.
The extended schedule reaches 1,320 frames and 128,588,887 successful machine steps, ending at PC `0x080008ca`.
The no-input control also reaches 900 frames without a diagnostic.
Local pixel comparisons against that control show:
| Captured frame | Different pixels out of 38,400 |
| --- | --- |
| 600, before input | 0 |
| 620 | 38,400 |
| 660 | 38,232 |
| 900 | 38,400 |
This establishes input-dependent execution and different captured output under fixed device configuration.
It does not establish readable menus, correct scene rendering, release behavior inside the game, or gameplay.
Local optical character recognition did not identify reliable menu text and supplied no semantic acceptance criterion.
No visual classifier or Jev call was used. No title-screen claim follows from color counts or pixel differences.
The file's cartridge revision was not inferred from its filename.
Original regressions separately test active wave synthesis, nibble rotation, lengths, gains, gating, bus phases, and rollback.
Workspace debug/release tests, public ARM/Thumb/memory/BIOS suites, pinned Pong scenarios, and native Darwin checks pass.
The pinned Pong debug/release reports match. Native ROM-window tests include an original wave-activation program.
A separate 600-frame Emerald native run still reaches its frame limit without input or a diagnostic.
## Remaining requirements
- Verify visible menu text and gameplay through direct window testing or explicit independently justified assertions.
- Implement continuous sample production and a Darwin output backend before claiming audible sound.
- Research two-bank wave playback and live bank changes separately. Both remain diagnostic during active playback.
- Implement Flash programming/erase and safe persistence before claiming saving.
For a manual native check, use:
```sh
direnv exec . cargo run --locked --release -- --rom roms/pokemon-emerald.gba --rtc --save-type flash128 --window
```
Enter maps to Start. Z maps to A. Escape exits. This command does not supply the deterministic probe schedule or fixed RTC.
Hardware sources and implementation comparisons are recorded in [audio references](../hardware/audio.md#references).

View file

@ -89,7 +89,8 @@ Update this file when a feature lands or a limit is removed.
- Direct Sound A/B support timer-driven FIFO playback, stereo digital levels, and DMA1/A or DMA2/B refill blocks.
- PSG pulse channels 1 and 2 support independent nominal frequency/duty playback, length, envelope, status, and stereo mixing. Channel 1 also supports sweep.
- Noise channel 4 supports nominal 7/15-bit generation, divider clocks, shared length/envelope behavior, status, and stereo mixing.
- Wave channel 3 triggers and unsupported serial operations produce explicit address/value diagnostics with whole-access and block-store rollback.
- Wave channel 3 supports single-bank nibble playback, rotating RAM, volume/force-volume, length, activity status, and digital mixing.
- Two-bank wave playback, active bank changes, and unsupported serial operations remain diagnostic with whole-access and block-store rollback.
- ROM windows with scanline capture, keyboard input, focus-loss release, bounded execution slices, and interactive keypad wake from STOP.
- Optional ROM-window frame limits and an original input-test ROM generator.
- Headless JSON ROM suites with PC or VBlank completion, register/CPSR/memory/pixel assertions, scheduled buttons, bounded budgets, and structured failure reports.
@ -151,8 +152,8 @@ Flash programming, erase, busy polling, wider accesses, unverified aliases, and
The optional [cartridge GPIO/RTC subset](hardware/cartridge-gpio.md) supports calendar/control transfers and explicit clock updates, selected with `--rtc` in ROM modes.
RTC persistence, interrupts, invalid-date correction, physical subsecond timing, GPIO aliases, and byte writes remain unsupported or unverified.
The core uses deterministic caller time. Desktop RTC runs use host UTC/elapsed time and are not fully deterministic.
The [audio subset](hardware/audio.md) supports Direct Sound, both PSG pulse channels, noise channel 4, idle wave channel 3 state, and instantaneous digital mixing.
It does not yet provide a continuous sample stream. Wave channel 3 playback and unverified sound-DMA pairings remain diagnostic.
The [audio subset](hardware/audio.md) supports Direct Sound, both PSG pulse channels, single-bank wave channel 3, noise channel 4, and instantaneous digital mixing.
It does not yet provide a continuous sample stream. Two-bank wave playback, active wave-bank changes, and unverified sound-DMA pairings remain diagnostic.
PSG timing and signed mixing are nominal; hardware startup edges and analog behavior remain unverified.
The [serial subset](hardware/serial.md) supports disconnected normal 8/32-bit transfers, external-clock waiting, idle multiplayer configuration, and general-purpose pins.
Internal clocks shift high input at nominal rates, clear busy, and request completion IRQs. External requests retain busy without clock edges.
@ -177,7 +178,10 @@ It now also completes internally clocked normal transfers and selects idle multi
With explicit `--rtc` selection, it also passes Game Pak GPIO configuration and RTC calendar reads.
With `--save-type flash128`, it also passes Flash identification and reaches a later startup check.
It now passes disconnected Joybus setup and completes a bounded 600-frame native run without a diagnostic error.
This run uses no scripted input or pixel assertions; menu progress and gameplay remain unverified.
That run used no scripted input or pixel assertions.
A later [fixed-time input probe](research/emerald-wave.md) found a wave trigger with its gate disabled.
Wave support now passes that point and reaches 1,320 frames with scheduled Start/A presses and releases.
Output differs from a no-input control, but readable menus and gameplay remain unverified.
Flash writing and persistence remain unimplemented.
Without `--rtc`, cartridge GPIO writes still return read-only diagnostics.
No title-screen, gameplay, or audible-game compatibility is established.

View file

@ -79,7 +79,11 @@ ARM/Thumb probes verify RAM byte reads, ROM command writes, all save wait settin
Wide accesses, DMA, instruction fetches, and CPU reads outside work RAM retain explicit diagnostics.
An original desktop ROM installs its Flash byte reader in IWRAM; terminal/native tests check explicit selection and unchanged files.
They verify explicit activation errors and atomic byte/word/block-store/DMA failure behavior.
The optional [local Emerald startup check](research/emerald-reset.md) now reaches sound activation but still fails deliberately there.
Wave tests cover every frequency and bank, independent nibble rotation, full rotations, bounded maximum batches, and fractional gains.
They also check inactive triggers, gates, length, status, master/BIOS reset, HALT/STOP, live-write limits, and CPU/DMA rollback.
Original terminal/native programs activate wave playback without external assets or host audio.
The optional [local Emerald input probe](research/emerald-wave.md) now completes its scheduled Start/A frame budget.
Frame completion and different pixels are not assertions about readable menus, correct graphics, or gameplay.
[Jev evidence review](jev-debugging.md) is optional and never changes deterministic test verdicts.
Its Python tests mock the provider and verify schemas, response validation, credential handling, non-overwriting logs, and failure behavior.
Live classifier smoke probes are separate from offline tests and must not become mandatory CI checks.

View file

@ -44,9 +44,12 @@ It also passes normal serial transfers and idle multiplayer configuration.
With `--rtc --save-type flash128`, it passes Game Pak GPIO setup, RTC reads, and Flash identification.
It also passes disconnected Joybus configuration and completes a bounded 600-frame native run without a diagnostic error.
That run does not verify screen contents, button responses, or gameplay.
The core has tested Direct Sound, both pulse channels, and noise channel 4.
Wave channel 3, a continuous sample stream, and desktop audio output remain missing.
See [the runtime result](research/emerald-reset.md). Next, verify visible menu progress and button responses rather than treating frame counts as playability.
A [fixed-time input probe](research/emerald-wave.md) then exposed an unsupported wave trigger with its gate disabled.
Single-bank wave support now passes that point and completes 1,320 frames with scheduled Start/A input.
Captured output differs from a no-input control; readable menus and gameplay remain unverified.
The core has tested Direct Sound, both pulse channels, single-bank wave channel 3, and noise channel 4.
Two-bank wave playback, a continuous sample stream, and desktop audio output remain missing.
See [the runtime result](research/emerald-reset.md). Next, verify visible menu progress rather than treating frame counts as playability.
The separate save requirement includes Flash programming/erase and safe host persistence. Identification/read support is not successful saving.
SRAM is byte-addressable battery-backed save memory. Flash and EEPROM remain separate protocols.