1use bincode::{Decode, Encode}; 2use jgenesis_common::frontend::{DisplayInfo, MappableInputs}; 3use jgenesis_common::input::Player; 4use nes_config::{NesButton, NesJoypadState}; 5 6pub(crate) trait NesJoypadStateExt { 7 #[must_use] 8 fn latch(self) -> LatchedJoypadState; 9} 10 11impl NesJoypadStateExt for NesJoypadState { 12 fn latch(self) -> LatchedJoypadState { 13 let bitstream = (u8::from(self.right) << 7) 14 | (u8::from(self.left) << 6) 15 | (u8::from(self.down) << 5) 16 | (u8::from(self.up) << 4) 17 | (u8::from(self.start) << 3) 18 | (u8::from(self.select) << 2) 19 | (u8::from(self.b) << 1) 20 | u8::from(self.a); 21 LatchedJoypadState(bitstream) 22 } 23} 24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 25pub struct ZapperState { 26 pub fire: bool, 27 pub force_offscreen: bool, 28 // Position in NES pixels, or None if offscreen 29 // X value should be in the range 0..=255 30 // Y value should be in the range 0..=223 (NTSC) or 0..=239 (PAL) 31 // Out-of-bounds values will be considered offscreen 32 pub position: Option<(u16, u16)>, 33} 34 35impl ZapperState { 36 pub(crate) fn position(self) -> Option<(u16, u16)> { 37 if self.force_offscreen { None } else { self.position } 38 } 39} 40 41#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 42pub enum NesInputDevice { 43 Controller(NesJoypadState), 44 Zapper(ZapperState), 45} 46 47impl Default for NesInputDevice { 48 fn default() -> Self { 49 Self::Controller(NesJoypadState::default()) 50 } 51} 52 53#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 54pub struct NesInputs { 55 pub p1: NesJoypadState, 56 pub p2: NesInputDevice, 57} 58 59impl MappableInputs<NesButton> for NesInputs { 60 #[inline] 61 fn set_field(&mut self, button: NesButton, player: Player, pressed: bool) { 62 match (button, player) { 63 (NesButton::ZapperFire | NesButton::ZapperForceOffscreen, _) => { 64 if let NesInputDevice::Zapper(zapper_state) = &mut self.p2 { 65 match button { 66 NesButton::ZapperFire => zapper_state.fire = pressed, 67 NesButton::ZapperForceOffscreen => zapper_state.force_offscreen = pressed, 68 _ => {} 69 } 70 } 71 } 72 (button, Player::One) => self.p1.set_button(button, pressed), 73 (button, Player::Two) => { 74 if let NesInputDevice::Controller(joypad_state) = &mut self.p2 { 75 joypad_state.set_button(button, pressed); 76 } 77 } 78 _ => {} 79 } 80 } 81 82 #[inline] 83 fn handle_mouse_motion( 84 &mut self, 85 (x, y): (f32, f32), 86 _delta: (f32, f32), 87 display_info: DisplayInfo, 88 ) { 89 if let NesInputDevice::Zapper(zapper_state) = &mut self.p2 { 90 zapper_state.position = 91 jgenesis_common::input::viewport_position_to_frame_position(x, y, display_info); 92 log::debug!("Set Zapper position to {:?}", zapper_state.position); 93 } 94 } 95 96 #[inline] 97 fn handle_mouse_leave(&mut self) { 98 if let NesInputDevice::Zapper(zapper_state) = &mut self.p2 { 99 zapper_state.position = None; 100 } 101 } 102} 103 104#[derive(Debug, Clone, Copy, Encode, Decode)] 105pub(crate) struct LatchedJoypadState(u8); 106 107impl Default for LatchedJoypadState { 108 fn default() -> Self { 109 Self(0xFF) 110 } 111} 112 113impl LatchedJoypadState { 114 pub fn next_bit(self) -> u8 { 115 self.0 & 0x01 116 } 117 118 #[must_use] 119 pub fn shift(self) -> Self { 120 Self((self.0 >> 1) | 0x80) 121 } 122}