1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-09 09:18:01 +00:00

linker+runtime: code review cleanup of the seven gates

* Tighten the OAM-DMA gate: the `has_oam` flag now ORs
   `__sprite_cycle_used` in addition to `__oam_used`. A hypothetical
   program that calls `cycle_sprites` without ever drawing would
   otherwise compile to an NMI that advances \$07EF each frame but
   never actually runs the DMA the cycling is meant to perturb.
   The stronger gate keeps the two markers semantically coupled
   (cycling presupposes DMA) and adds a test that verifies the
   DMA trigger is emitted for a cycle-only program.

 * Drop the unused `NmiOptions::any_input()` helper. The only
   consumer (`gen_nmi`) reads the two flags inline and I never
   wired up a second caller.

 * Fix the cycle-count claim in `NmiOptions::has_p2_input` /
   `has_p1_input` docstrings: LDA abs + LSR A + ROL zp is 11
   cycles per port, ×8 loop iterations = ~88 cycles, not the "~30"
   I wrote in the original commit. Also notes the ~12-cycle strobe
   + scaffold overhead that disappears when both ports are unused.

No behavioural change — all 622 Rust tests and 33 emulator
goldens still pass unchanged.

https://claude.ai/code/session_016kM6P7PukktBDqTZexrrAN
This commit is contained in:
Claude 2026-04-16 13:53:50 +00:00
parent 53c454669d
commit e5bc325a71
No known key found for this signature in database
3 changed files with 39 additions and 21 deletions

View file

@ -388,29 +388,19 @@ pub struct NmiOptions {
pub has_oam: bool,
/// When false, drop the three instructions that shift `$4017`
/// (JOY2) into `ZP_INPUT_P2` from the NMI's 8-iteration input
/// loop. Single-player programs save ~6 bytes of code and ~30
/// cycles per frame (a `LDA abs`, an `LSR A`, and a `ROL zp`
/// running 8 times).
/// loop. Single-player programs save ~6 bytes of code and ~88
/// cycles per frame (LDA abs + LSR A + ROL zp = 11 cycles × 8
/// iterations).
pub has_p2_input: bool,
/// When false, drop the three instructions that shift `$4016`
/// (JOY1) into `ZP_INPUT_P1`. If both `has_p1_input` and
/// `has_p2_input` are false the whole strobe-and-loop block
/// disappears — programs that never touch `button.*` pay
/// zero cycles for input sampling.
/// zero cycles for input sampling (~100 cycles: the 88-cycle
/// body plus the ~12-cycle strobe and loop scaffold).
pub has_p1_input: bool,
}
impl NmiOptions {
/// Whether the program reads any controller input — the
/// necessary condition for emitting the strobe write to
/// `$4016` and the 8-iteration shift loop. Skipped entirely
/// when both ports are unused.
#[must_use]
pub fn any_input(&self) -> bool {
self.has_p1_input || self.has_p2_input
}
}
#[must_use]
pub fn gen_nmi(opts: NmiOptions) -> Vec<Instruction> {
let NmiOptions {