diff --git a/examples/noise_triangle_sfx.nes b/examples/noise_triangle_sfx.nes index 00ed762..d96c03a 100644 Binary files a/examples/noise_triangle_sfx.nes and b/examples/noise_triangle_sfx.nes differ diff --git a/src/assets/chr.rs b/src/assets/chr.rs index cc95066..2e1c99c 100644 --- a/src/assets/chr.rs +++ b/src/assets/chr.rs @@ -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 diff --git a/src/codegen/ir_codegen.rs b/src/codegen/ir_codegen.rs index 7b004cb..aa176fe 100644 --- a/src/codegen/ir_codegen.rs +++ b/src/codegen/ir_codegen.rs @@ -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())); diff --git a/tests/emulator/goldens/noise_triangle_sfx.audio.hash b/tests/emulator/goldens/noise_triangle_sfx.audio.hash index 5f988a9..5c09402 100644 --- a/tests/emulator/goldens/noise_triangle_sfx.audio.hash +++ b/tests/emulator/goldens/noise_triangle_sfx.audio.hash @@ -1 +1 @@ -a82b6ff5 132084 +8fd9f7d2 132084