mirror of
https://github.com/imjasonh/nescript
synced 2026-07-09 01:16:12 +00:00
audio: always enable all four tone channels on sfx trigger
Writing $07 in `emit_play_triangle` and $0B in `emit_play_noise` meant that a noise play following an in-progress triangle note would clear bit 2 of $4015 and cut the triangle off mid-envelope (and vice versa). Write $0F from both paths so every trigger keeps pulse1, pulse2, triangle, and noise enabled; channels with no active envelope stay silent via the runtime's per-channel counter gating. Also fixes the attribute-byte packing comment in `png_to_nametable` — the code was correct, the doc string had the quadrant order backwards. The only observable ROM change is `examples/noise_triangle_sfx.nes` (two immediate operands shift) and its audio hash golden; the committed PNG golden is byte-identical. Found in independent code review after the section landed. https://claude.ai/code/session_01MaNVcDmK9gsspRkdxowQAM
This commit is contained in:
parent
359c945c30
commit
9b54ff83c0
4 changed files with 14 additions and 8 deletions
Binary file not shown.
|
|
@ -92,7 +92,7 @@ pub fn png_to_nametable(path: &std::path::Path) -> Result<([u8; 960], [u8; 64]),
|
|||
|
||||
// Attribute table: 8×8 bytes, each covering a 32×32 region made
|
||||
// up of four 16×16 quadrants. Each quadrant gets 2 bits
|
||||
// (0..=3) packed into the byte as `TR<<6 | TL<<4 | BR<<2 | BL`
|
||||
// (0..=3) packed into the byte as `BR<<6 | BL<<4 | TR<<2 | TL`
|
||||
// per the PPU's documented layout. The 15-row nametable only
|
||||
// half-fills the last attribute byte-row (rows 8..10 of the
|
||||
// bottom attribute byte are unused and stay at 0, matching the
|
||||
|
|
|
|||
|
|
@ -1407,10 +1407,15 @@ impl<'a> IrCodeGen<'a> {
|
|||
self.emit(STA, AM::Absolute(0x400A));
|
||||
self.emit(LDA, AM::Immediate(period_hi));
|
||||
self.emit(STA, AM::Absolute(0x400B));
|
||||
// Enable pulse-1 + pulse-2 + triangle in the APU status
|
||||
// register. We don't bother reading $4015 first — overwriting
|
||||
// with $07 keeps all currently-used channels enabled.
|
||||
self.emit(LDA, AM::Immediate(0x07));
|
||||
// Enable all four tone channels in the APU status register.
|
||||
// We always write $0F (pulse1+pulse2+triangle+noise) instead
|
||||
// of just the channel we're triggering, because per-play
|
||||
// writes use immediate values and a later noise play with
|
||||
// $0B would otherwise silence an in-progress triangle note
|
||||
// by clearing bit 2. Channels with no active envelope stay
|
||||
// silent via the runtime's per-channel counter gating, so
|
||||
// enabling them blindly is harmless.
|
||||
self.emit(LDA, AM::Immediate(0x0F));
|
||||
self.emit(STA, AM::Absolute(0x4015));
|
||||
// Main-RAM envelope pointer.
|
||||
self.emit(LDA, AM::SymbolLo(label.to_string()));
|
||||
|
|
@ -1439,8 +1444,9 @@ impl<'a> IrCodeGen<'a> {
|
|||
// $400F: length counter load.
|
||||
self.emit(LDA, AM::Immediate(period_hi));
|
||||
self.emit(STA, AM::Absolute(0x400F));
|
||||
// Enable pulse-1 + pulse-2 + noise channels.
|
||||
self.emit(LDA, AM::Immediate(0x0B));
|
||||
// Enable all four tone channels — see the equivalent write
|
||||
// in `emit_play_triangle` for why $0F rather than $0B.
|
||||
self.emit(LDA, AM::Immediate(0x0F));
|
||||
self.emit(STA, AM::Absolute(0x4015));
|
||||
// Main-RAM envelope pointer.
|
||||
self.emit(LDA, AM::SymbolLo(label.to_string()));
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
a82b6ff5 132084
|
||||
8fd9f7d2 132084
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue