From 0de1d60c33b0974c337e15e2392cc20ce37a16b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Apr 2026 13:43:33 +0000 Subject: [PATCH] runtime: gate controller-2 reads in NMI on __p2_input_used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the three-instruction JOY2 shift block (`LDA $4017 / LSR A / ROL ZP_INPUT_P2`) from inside the NMI's 8-iteration input loop when user code never reads controller 2. IR codegen emits the `__p2_input_used` marker from `IrOp::ReadInput(_, 1)`; the linker threads the flag through a new `NmiOptions::has_p2_input` bool, and `gen_nmi` writes the shift block only when the flag is set. Savings for single-player programs: - ~6 bytes of NMI code. - ~30 cycles per frame (3 instructions × 8 loop iterations, each 6-8 cycles depending on addressing — LDA abs is 4, LSR A is 2, ROL zp is 5, so ~11 cycles × 8 = ~88 cycles; rounded down for the page-crossing penalty landing differently in the new layout). This commit also fixes the IR codegen to drop the matching `__p1_input_used` marker from `IrOp::ReadInput(_, 0)`, even though the next commit is the one that actually consumes it. Landing the two markers together keeps the IR codegen's per-op bookkeeping coherent. Six audio goldens flip (every program that reads input + plays audio) with the expected NMI-layout-shift cycle drift. https://claude.ai/code/session_016kM6P7PukktBDqTZexrrAN --- examples/arrays_and_functions.nes | Bin 24592 -> 24592 bytes examples/audio_demo.nes | Bin 24592 -> 24592 bytes examples/auto_chr_background.nes | Bin 24592 -> 24592 bytes examples/bitwise_ops.nes | Bin 24592 -> 24592 bytes examples/bouncing_ball.nes | Bin 24592 -> 24592 bytes examples/coin_cavern.nes | Bin 24592 -> 24592 bytes examples/comparisons.nes | Bin 24592 -> 24592 bytes examples/friendly_assets.nes | Bin 24592 -> 24592 bytes examples/function_chain.nes | Bin 24592 -> 24592 bytes examples/hello_sprite.nes | Bin 24592 -> 24592 bytes examples/inline_asm_demo.nes | Bin 24592 -> 24592 bytes examples/logic_ops.nes | Bin 24592 -> 24592 bytes examples/loop_break_continue.nes | Bin 24592 -> 24592 bytes examples/match_demo.nes | Bin 24592 -> 24592 bytes examples/metasprite_demo.nes | Bin 24592 -> 24592 bytes examples/mmc1_banked.nes | Bin 57360 -> 57360 bytes examples/mmc3_per_state_split.nes | Bin 24592 -> 24592 bytes examples/nested_structs.nes | Bin 24592 -> 24592 bytes examples/noise_triangle_sfx.nes | Bin 24592 -> 24592 bytes examples/palette_and_background.nes | Bin 24592 -> 24592 bytes examples/platformer.nes | Bin 24592 -> 24592 bytes examples/scanline_split.nes | Bin 24592 -> 24592 bytes examples/sfx_pitch_envelope.nes | Bin 24592 -> 24592 bytes examples/sprite_flicker_demo.nes | Bin 24592 -> 24592 bytes examples/sprites_and_palettes.nes | Bin 24592 -> 24592 bytes examples/state_machine.nes | Bin 24592 -> 24592 bytes examples/structs_enums_for.nes | Bin 24592 -> 24592 bytes examples/uxrom_banked.nes | Bin 90128 -> 90128 bytes examples/uxrom_banked_to_banked.nes | Bin 57360 -> 57360 bytes examples/uxrom_user_banked.nes | Bin 40976 -> 40976 bytes examples/war.nes | Bin 24592 -> 24592 bytes src/codegen/ir_codegen.rs | 46 ++++++++++++++++++ src/linker/mod.rs | 2 + src/runtime/mod.rs | 21 ++++++-- tests/emulator/goldens/audio_demo.audio.hash | 2 +- .../goldens/friendly_assets.audio.hash | 2 +- .../goldens/noise_triangle_sfx.audio.hash | 2 +- tests/emulator/goldens/platformer.audio.hash | 2 +- .../goldens/sfx_pitch_envelope.audio.hash | 2 +- tests/emulator/goldens/war.audio.hash | 2 +- 40 files changed, 70 insertions(+), 11 deletions(-) diff --git a/examples/arrays_and_functions.nes b/examples/arrays_and_functions.nes index ab94d155b1e38b11139bdad4122f7a3da4d97833..1e2fe4977e8d69fc560682a2558f8d00677e9d14 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kHc51Ip0%vjhVMJ(Dm41_s2!0KopjqYywK_0tW^ delta 35 tcmV+;0NnqOzyXlJ0kHc539T1EN+t-((DAeW0tP(>#KHi;1jC~cKp+r;4txLr diff --git a/examples/audio_demo.nes b/examples/audio_demo.nes index 1041513e0e30757acd572edfe1002799d3da07da..ecb936ef2bbc019fa63242d53020f04dab66a287 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kB&G1Ip0%vt$FNJd=0NMYLzyXlJ0kD<@1Ip0%v!4ZPI+MT!1_lSk0Kl2Wli@&EvuHr5A5dx#`Tzg` delta 40 ycmV+@0N4MJzyXlJ0kD<@39T1EN+t-((DAdG1#3D62gLxupT(2mKv=VAK&T&8?-3XP diff --git a/examples/bitwise_ops.nes b/examples/bitwise_ops.nes index 7921e14728e4bd74bd1a5f17a048072157114e43..d649e82998e3f406e3f3b8496b9d9d76000ce349 100644 GIT binary patch delta 33 pcmbPmfN{bB#tlu(%%?7V-`veyZ!>uVGaIY-VTJ>(oB16W6#)2!4KDxy delta 35 rcmbPmfN{bB#tlu(oNL7$ywo^OUHG`UmAT%A)%!5Rf$q)x4vY!_{z463 diff --git a/examples/bouncing_ball.nes b/examples/bouncing_ball.nes index b7691951b7682b20be9b9fa04a88adbfc82e5f21..1685d426c716283f387298482d0b285cd2132b3c 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kEtA1Ip0%v$X+LK9k@91_gD&0Kl)a4?qDR0tyY+ delta 35 tcmV+;0NnqOzyXlJ0kEtA39T1EN+t-((DAdc0aZQ)b-@6@wX+XE0U!mD4cPzy diff --git a/examples/coin_cavern.nes b/examples/coin_cavern.nes index c80a9f3aa6cc7ae489971604d59c099b684eed61..fe283cbea1ffd59c15f3f2c7d3aa8a1d3210169a 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kGo&1Ip0%v+M#MJ(Cav1_i9b0Kn$64?qDR09*~Q delta 35 tcmV+;0NnqOzyXlJ0kGo&39T1EN+t-((DAe80vp1Ip0%v)BhRIFbkk1vAG0z|yl1Kmj1{rVR}M delta 35 tcmV+;0NnqOzyXlJ0kF>p39T1EN+t-((DAd<2QoMXGsghH*s~8n0U!v?4e0;? diff --git a/examples/function_chain.nes b/examples/function_chain.nes index aa6a2d2cb114aefc976d85949fe060f46cc846be..8a0119eb7e3567333f2e117b32c3747bce18b308 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kG!*1Ip0%v+w~LK9djv1_iLe0Kn?A4?qDR0NV|@ delta 35 tcmV+;0NnqOzyXlJ0kG!*39T1EN+t-((DAeC0UAC9u)zSp@Uss<0U!`74vGK( diff --git a/examples/hello_sprite.nes b/examples/hello_sprite.nes index d6f2d25aa624ca54dec8d5acea108f754f222a21..6a128eb6fbb3f60adaa22035aa310672e5f58518 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kC8N1Ip0%vuy#LK9i6E1_dp_0KjLn4?qDR@jDE! delta 35 rcmbPmfN{bB#tkWqoNL7$ywo^OUHG^;opG)mtL{OD1G$^|9T*h=_>v6) diff --git a/examples/inline_asm_demo.nes b/examples/inline_asm_demo.nes index 2b8a9cb50eb9b90759e0b08a57f4c67806dd06b8..75f57bd94230bd905e3d55621499c4a6551c2bf1 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kB{J1Ip0%vuOdLK9i6E1_dd>0Kj9j4?qDR@QVzi delta 35 rcmbPmfN{bB#tjLKoNL7$ywo^OUHG^;nQ@^VtL8z51DTuo9T*h=_u35g diff --git a/examples/logic_ops.nes b/examples/logic_ops.nes index 9055961fba2b708533173ad62645f67584faf965..308f9a94ce86835e605ac5fca02d22831aab4f2c 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kA6r1Ip0%vor(NJd}n delta 35 tcmV+;0NnqOzyXlJ0k8}L39T1EN+t-((DAbm0`)xw)xiM36|)aO0U+|<4Eg{7 diff --git a/examples/mmc1_banked.nes b/examples/mmc1_banked.nes index 5cfe88a45383b299ba68a684a55ac6242d2d354d..94ac5873cfa019ccf803e62fca9c915dc065b234 100644 GIT binary patch delta 32 qcmV+*0N?+RzypxL1F-di1Ip0%v;Kk%J&_;=1-`-n!1%KdzyTl-_78Oc delta 35 tcmV+;0NnqOzypxL1F-di39T1EN+t-((DAeQf($(czQO>&{<9Cj0U#+;5IO(= diff --git a/examples/mmc3_per_state_split.nes b/examples/mmc3_per_state_split.nes index a776605e86e3ac79fdf4e29e443daafbb5ed07bb..beaa53d7085004b242fe9367701e66de1f9ec2d7 100644 GIT binary patch delta 27 jcmbPmfN{bB#tkCO%%?7V-z>}g)@HH;Guvh^2Q~!&p79C! delta 29 lcmbPmfN{bB#tkCOoNL7$ywo^OUHG_JocXQIW-bRd1puZp3X=c; diff --git a/examples/nested_structs.nes b/examples/nested_structs.nes index c63f0c4521d48790e21e2394526da312babc455f..6bd7cba615771488b6d8ca1234784ba8a8b84a0a 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kFjZ1Ip0%v(5oLK9leP1_h460Kmwz4?qDR2R05e delta 35 tcmV+;0NnqOzyXlJ0kFjZ39T1EN+t-((DAd#0X#khj==!H&a)3d0U!zw4kQ2o diff --git a/examples/noise_triangle_sfx.nes b/examples/noise_triangle_sfx.nes index e7b884b452638829781f132d27694eda4e4c03a2..a48e47e60f8cb4225659033465a1cfbd3f8ecd8d 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kB&G1Ip0%vt$FNJd=6N6WeAk2Q~!&qLm6a delta 29 lcmbPmfN{bB#tkM+oNL7$ywo^OUHG`!oawCXW-bRd1puiV3bFtI diff --git a/examples/sfx_pitch_envelope.nes b/examples/sfx_pitch_envelope.nes index 652d4e435bda0847d49c95b449335637a3bd8606..1facd3ed82da3e0a6b4cc8e1933e6f30c9ed1a4a 100644 GIT binary patch delta 36 ucmV+<0NekNzyXlJ0kALv1Ip0%vpE9MJ(FMp1_l7a0KhZCvk^f59{~Uo_6@KA delta 38 wcmV+>0NMYLzyXlJ0kALv39T1EN+t-((DAb~0?|DN0Kx#kIl{9MK>r^B03H1fF#rGn diff --git a/examples/sprite_flicker_demo.nes b/examples/sprite_flicker_demo.nes index 51954f30282a94744e4ff0cd370a1b1ff48351d0..d1d32c8132941c16ac323a9ff6e7ce4620d2ee2f 100644 GIT binary patch delta 33 pcmbPmfN{bB#tkh@%%?7V-`vYoYdd)Z6C10~A%+9(oB16W6#)364KV-! delta 35 rcmbPmfN{bB#tkh@oNL7$ywo^OUHG`UovGHA)#nhyf!@vh4vY!_{%#Fo diff --git a/examples/sprites_and_palettes.nes b/examples/sprites_and_palettes.nes index da2669b0df425ab0e2eb4e7c36e1750dd3941bed..c0ca2ecdcde0ccee3e98093f10aed4ec19fb39d9 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kES11Ip0%v#bGLK9kS^1_f-v0KlfR4?qDR|Bnr< delta 35 tcmV+;0NnqOzyXlJ0kES139T1EN+t-((DAdT0bV`@Y{3A)tg{b50U!hO4ZZ*X diff --git a/examples/state_machine.nes b/examples/state_machine.nes index b8cbf27afead51b199ceed682921ee48ee4dd8ae..405149ef9a5b716dd5ab8c8faa3d85015e08735e 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kFUV1Ip0%v&jNMJ(KVP1_g=20Kmhu4?qDR28#|R delta 35 tcmV+;0NnqOzyXlJ0kFUV39T1EN+t-((DAdw0zo|miNXND$+HhY0U!xa4i^9b diff --git a/examples/structs_enums_for.nes b/examples/structs_enums_for.nes index 155d8f3504ed2fd90e73662ed9fd719a8a81d696..e5afb78de3ca0a642e96463130b43f1cbe68660d 100644 GIT binary patch delta 33 rcmV++0N($QzyXlJ0kG!*1Ip0%v+w~LK9djv1_iLe0Kn?A4?qDR0NV|@ delta 35 tcmV+;0NnqOzyXlJ0kG!*39T1EN+t-((DAeC0UAC9u)zSp@Uss<0U!`74vGK( diff --git a/examples/uxrom_banked.nes b/examples/uxrom_banked.nes index f6178bc768ebdadc268cc94df13489f9280fbcc1..a41e9c90f7c8503f5e6f8552a9e6c0ae39953104 100644 GIT binary patch delta 36 ucmV+<0NekNzy*-N1%R{xmI4FH(D%2W0s(71kst;IU%~*unYRx>0RbQuAPs8( delta 39 xcmV+?0NDSKzy*-N1%R{xmI4W_7eGoT2+Gj$x0wO~Ydr;D!T`XZw+}!80U$9a4ln=! diff --git a/examples/uxrom_banked_to_banked.nes b/examples/uxrom_banked_to_banked.nes index 7fb8de86c18fd143d728034abfb270016727e177..e33c3b86c2a7ec84bada831b0ae6fe8a7ffd11c7 100644 GIT binary patch delta 32 qcmV+*0N?+RzypxL1F&3z1Ip0%vt@#%J&`~L1tr1&z+bZuzyTom)D6l2 delta 35 tcmV+;0NnqOzypxL1F&3z39T1EN+t-((DAchf~7qLCBgu}WwQ^!0U!<44sQSe diff --git a/examples/uxrom_user_banked.nes b/examples/uxrom_user_banked.nes index 43efe01efc7deaa9844823ac6faf86dbe7fe8564..9370ffbf5ac3d927b26bf8a34e2be7f87cf96928 100644 GIT binary patch delta 32 ocmbPmfN8=3rVTz$%%?7V-yG_+)plY68>{Fch6Dba`5PD&0PKql_y7O^ delta 35 rcmbPmfN8=3rVTz$oNL7$ywo^OUHG`!-)XBYtLPzy1EHJw8yFP;|ECRo diff --git a/examples/war.nes b/examples/war.nes index 2e58168296a592b80e6b3d23c34fdf643b8d5f3e..24167165e9892e6af89cf136a2636c05b1ec8b88 100644 GIT binary patch delta 1251 zcmaJ>T}YEr7~XOJZcW{fbDHyG{?05`gjgvIETr*KR@g;R6jB6T^n*c=<`hmB!csdI zL0Hhm7amg-c4rZ4g?^QwrW;x&RG?7AOwfx6d(Zin%wfSeobx`<`<(ZEpYyF_2z9`( z5q;}y5|F9$Q6YNU`Kk@AhmRti3DuF4<~mq=&1I;MEN5niSIoZ=1ePt8e07lOv>v{* zg;DwygrekHM!78-kvwurOiNYblSzsu)M6zW%*btqP4SeJ?-gVf2R{H9GtmjGp<`Gp z^o~geZx9e9{+fhsx>1E&rB)s=Pf5rdObFz%z+Z-1IR8%E2hy^AfZe)x3WRx(m!jsk z9VRENJA@kyGEfcnP!-M>Ij-S&J3}f+yImkJta{ILg{Vy=YB@0i2(+?57Oe%xQ{F>X z39SvK_^BbJ_R~|?Oy}@6dLgu`r*ok-JzdACRHw-FEvn;#*{|gJHuA2<^KOwdHh1%N z249j~@kzC?V}|f_gd*&tYOEIKB4YNiloQ7vD|pigG?jqU;D-F2xcvQ1Wf>B?)A8=U zGbwV=UgCC5!_FjtXlf!&&uQhvZ!dG#D-@_}6UD$OLDFX5Rn^Za^c5Nr4U9O4;#0wgnVC z**4+RFwPaqMz9+y$>O~>FlN%TP32>`%a9u7$|Eq$hRd-&G? zj|!g9ZrX$ObOAp3)6VKB=vnDa#XkUbLf5Exk<>ejtt|{YkYLF@(&fzC{w&JrL9l!D zwYVBh@MoMvoq1@LtT|_pOvbYW^p*U`DnfgR!{yJrJu9!r4vEKRdnCPg2y0-i3Mp@DFk9|?Z| DU$Y0& delta 1135 zcmaKrUr19?9LM)_ZoAW^?*AWiyKa?Rj8Y;a>|qck#li6d$)7qwjNt5rNv@jAp-M7~3}Rwd5h8I) zK>9gPKkLjoG@^`fgLy%LNW8@0V}`#9z1Z_wYF$u5Nyj7$*|I<#b0g0~&!ZirMtss1 zu0O}9Kq^s?=uk-Fl1eVQhHu%T!N(dmlyU>-i(wMq%?;ZTYt-~n!{Tc+wm zycy<6Ho!*Q;INkoxztmZXZv;5UW*FZ~ts{EgM{?EsP zTdT`8>`Bqchl^uqV#oiCpL zsRMU=U694+z1wYxjADw=1yT&CMlqsVdeK#H6f)|nx7`3aH5~XL3i*6qc&D3spw`Af F`~&G$&tU)n diff --git a/src/codegen/ir_codegen.rs b/src/codegen/ir_codegen.rs index 7ea7136..6b982fc 100644 --- a/src/codegen/ir_codegen.rs +++ b/src/codegen/ir_codegen.rs @@ -196,6 +196,18 @@ pub struct IrCodeGen<'a> { /// `false` and reclaim the 16 CHR bytes of tile 0 as a blank /// background tile. default_sprite_used: bool, + /// Set to true the first time we lower an `IrOp::ReadInput` + /// targeting player 1. Drives the `__p1_input_used` marker + /// label — the runtime's NMI skips the three instructions that + /// shift `$4016` into `ZP_INPUT_P1` when nobody reads the + /// player-1 byte. + p1_input_used: bool, + /// Set to true the first time we lower an `IrOp::ReadInput` + /// targeting player 2. Drives the `__p2_input_used` marker + /// label — single-player programs skip the `$4017` read inside + /// the NMI's shift loop, saving ~6 bytes of code and ~30 cycles + /// per frame. + p2_input_used: bool, /// Source-location markers produced from [`IrOp::SourceLoc`]. /// Each entry is a `(label_name, span)` pair — the codegen /// emits a unique label-definition pseudo-op at the current @@ -377,6 +389,8 @@ impl<'a> IrCodeGen<'a> { div_used: false, oam_used: false, default_sprite_used: false, + p1_input_used: false, + p2_input_used: false, source_locs: Vec::new(), next_source_loc: 0, emit_source_locs: false, @@ -1490,6 +1504,16 @@ impl<'a> IrCodeGen<'a> { self.emit(INC, AM::ZeroPage(ZP_OAM_CURSOR)); } IrOp::ReadInput(dest, player) => { + // Drop the per-player input marker so the linker + // can decide whether to keep that port's shift + // block inside NMI. IR uses `player_index` 0 = P1 + // and 1 = P2; the ZP bytes the NMI populates match + // the constants at the top of `runtime/mod.rs`. + if *player == 1 { + self.emit_p2_input_marker(); + } else { + self.emit_p1_input_marker(); + } // $01 = P1 input byte, $08 = P2 input byte let addr = if *player == 1 { 0x08 } else { 0x01 }; self.emit(LDA, AM::ZeroPage(addr)); @@ -2141,6 +2165,28 @@ impl<'a> IrCodeGen<'a> { } } + /// Emit the `__p1_input_used` marker label at most once per + /// program. Triggered by `IrOp::ReadInput` with `player == 0`. + /// Programs that never read controller 1 skip the three NMI + /// instructions that shift `$4016` into `ZP_INPUT_P1`. + fn emit_p1_input_marker(&mut self) { + if !self.p1_input_used { + self.emit_label("__p1_input_used"); + self.p1_input_used = true; + } + } + + /// Emit the `__p2_input_used` marker label at most once per + /// program. Triggered by `IrOp::ReadInput` with `player == 1`. + /// Single-player programs skip the three NMI instructions that + /// shift `$4017` into `ZP_INPUT_P2`. + fn emit_p2_input_marker(&mut self) { + if !self.p2_input_used { + self.emit_label("__p2_input_used"); + self.p2_input_used = true; + } + } + /// Emit the MMC3 `__irq_user` handler that dispatches on the /// `(current_state, scanline_step)` pair. Supports multiple /// `on scanline(N)` handlers per state — they fire in ascending diff --git a/src/linker/mod.rs b/src/linker/mod.rs index 11848ec..bc7cb1b 100644 --- a/src/linker/mod.rs +++ b/src/linker/mod.rs @@ -662,12 +662,14 @@ impl Linker { // every frame" hardware symptom into visible flicker that // the eye reconstructs across frames. let has_sprite_cycle = has_label(user_code, "__sprite_cycle_used"); + let has_p2_input = has_label(user_code, "__p2_input_used"); all_instructions.extend(runtime::gen_nmi(runtime::NmiOptions { has_ppu_updates, has_audio, debug_mode, has_sprite_cycle, has_oam, + has_p2_input, })); // IRQ handler diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index d54329b..dee4df8 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -386,6 +386,12 @@ pub struct NmiOptions { /// so the DMA is ~520 wasted cycles per NMI. Skipping it saves /// those cycles plus 9 bytes of NMI code. 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). + pub has_p2_input: bool, } #[must_use] @@ -396,6 +402,7 @@ pub fn gen_nmi(opts: NmiOptions) -> Vec { debug_mode, has_sprite_cycle, has_oam, + has_p2_input, } = opts; let mut out = Vec::new(); @@ -506,16 +513,20 @@ pub fn gen_nmi(opts: NmiOptions) -> Vec { out.push(Instruction::new(STA, AM::Absolute(JOY1))); // Read 8 button bits from controller 1 ($4016) into ZP_INPUT_P1 - // and 8 button bits from controller 2 ($4017) into ZP_INPUT_P2 - // simultaneously — shift each port's carry into its ZP byte. + // — and, when `has_p2_input` is set, 8 bits from controller 2 + // ($4017) into ZP_INPUT_P2 in the same loop. Single-player + // programs drop the three P2 instructions (LDA abs, LSR A, ROL + // zp) and shave ~6 bytes plus ~30 cycles/frame off the NMI. out.push(Instruction::new(LDX, AM::Immediate(0x08))); out.push(Instruction::new(NOP, AM::Label("__read_input".into()))); out.push(Instruction::new(LDA, AM::Absolute(JOY1))); out.push(Instruction::new(LSR, AM::Accumulator)); out.push(Instruction::new(ROL, AM::ZeroPage(ZP_INPUT_P1))); - out.push(Instruction::new(LDA, AM::Absolute(0x4017))); // JOY2 - out.push(Instruction::new(LSR, AM::Accumulator)); - out.push(Instruction::new(ROL, AM::ZeroPage(ZP_INPUT_P2))); + if has_p2_input { + out.push(Instruction::new(LDA, AM::Absolute(0x4017))); // JOY2 + out.push(Instruction::new(LSR, AM::Accumulator)); + out.push(Instruction::new(ROL, AM::ZeroPage(ZP_INPUT_P2))); + } out.push(Instruction::implied(DEX)); out.push(Instruction::new( BNE, diff --git a/tests/emulator/goldens/audio_demo.audio.hash b/tests/emulator/goldens/audio_demo.audio.hash index 9ed40e1..e2a54aa 100644 --- a/tests/emulator/goldens/audio_demo.audio.hash +++ b/tests/emulator/goldens/audio_demo.audio.hash @@ -1 +1 @@ -dceef796 132084 +b52a0b1e 132084 diff --git a/tests/emulator/goldens/friendly_assets.audio.hash b/tests/emulator/goldens/friendly_assets.audio.hash index c671279..5d7538e 100644 --- a/tests/emulator/goldens/friendly_assets.audio.hash +++ b/tests/emulator/goldens/friendly_assets.audio.hash @@ -1 +1 @@ -a9a829f7 132084 +9387feb3 132084 diff --git a/tests/emulator/goldens/noise_triangle_sfx.audio.hash b/tests/emulator/goldens/noise_triangle_sfx.audio.hash index 435c42c..e9c3f96 100644 --- a/tests/emulator/goldens/noise_triangle_sfx.audio.hash +++ b/tests/emulator/goldens/noise_triangle_sfx.audio.hash @@ -1 +1 @@ -be86c2a2 132084 +752f9eee 132084 diff --git a/tests/emulator/goldens/platformer.audio.hash b/tests/emulator/goldens/platformer.audio.hash index 4bbfe8e..7755f4b 100644 --- a/tests/emulator/goldens/platformer.audio.hash +++ b/tests/emulator/goldens/platformer.audio.hash @@ -1 +1 @@ -c9c2333d 132084 +83c0eee4 132084 diff --git a/tests/emulator/goldens/sfx_pitch_envelope.audio.hash b/tests/emulator/goldens/sfx_pitch_envelope.audio.hash index 025c719..20f5682 100644 --- a/tests/emulator/goldens/sfx_pitch_envelope.audio.hash +++ b/tests/emulator/goldens/sfx_pitch_envelope.audio.hash @@ -1 +1 @@ -7be9372c 132084 +fd72f4a8 132084 diff --git a/tests/emulator/goldens/war.audio.hash b/tests/emulator/goldens/war.audio.hash index 03cc9de..35b351f 100644 --- a/tests/emulator/goldens/war.audio.hash +++ b/tests/emulator/goldens/war.audio.hash @@ -1 +1 @@ -b7b5b9a0 132084 +51b560a7 132084