1use crate::api::SnesEmulatorConfig; 2use crate::input::{ 3 SnesController, SnesInputs, SnesJoypadStateExt, SnesMouseState, SuperScopeState, 4}; 5use bincode::{Decode, Encode}; 6use jgenesis_common::num::GetBit; 7use snes_config::SnesJoypadState; 8 9const AUTO_JOYPAD_DURATION_MCLK: u64 = 4224; 10 11#[derive(Debug, Clone, Copy, Encode, Decode)] 12struct MouseRegister { 13 sensitivity: u8, 14 last_x: f64, 15 last_y: f64, 16 x_direction: bool, 17 x_magnitude: u8, 18 y_direction: bool, 19 y_magnitude: u8, 20 left: bool, 21 right: bool, 22} 23 24impl Default for MouseRegister { 25 fn default() -> Self { 26 Self { 27 sensitivity: 0, 28 last_x: 0.0, 29 last_y: 0.0, 30 x_direction: false, 31 x_magnitude: 0, 32 y_direction: false, 33 y_magnitude: 0, 34 left: false, 35 right: false, 36 } 37 } 38} 39 40impl MouseRegister { 41 fn update(&mut self, mouse: SnesMouseState, config_sensitivity: f64) { 42 // According to the manual, 0 is "slow", 1 is "normal", 2 is "fast" 43 // Normal and fast speeds have an exponential curve where the reported movement values 44 // increase more rapidly at higher speeds, but presumably any half-decent PC mouse already 45 // works that way, so instead of trying to emulate that just apply a fixed multiplier to dx/dy values 46 const SENSITIVITY_MULTIPLIERS: [f64; 3] = [0.75, 1.0, 1.5]; 47 48 fn mouse_axis_to_magnitude(value: f64) -> u8 { 49 // Mouse magnitude values are unsigned 7-bit 50 // Multiplying by 0.2 is arbitrary, mouse feels way too sensitive without that 51 (0.2 * value).abs().round().clamp(0.0, 127.0) as u8 52 } 53 54 let sensitivity = config_sensitivity * SENSITIVITY_MULTIPLIERS[self.sensitivity as usize]; 55 56 let dx = sensitivity * (mouse.x_position.get() - self.last_x); 57 let dy = sensitivity * (mouse.y_position.get() - self.last_y); 58 self.last_x = mouse.x_position.get(); 59 self.last_y = mouse.y_position.get(); 60 61 self.left = mouse.left; 62 self.right = mouse.right; 63 64 self.x_magnitude = mouse_axis_to_magnitude(dx); 65 self.y_magnitude = mouse_axis_to_magnitude(dy); 66 67 // Supposedly the direction bits are sticky and only change when magnitude is non-zero 68 if self.x_magnitude != 0 { 69 self.x_direction = dx < 0.0; // 0 = right, 1 = left 70 } 71 if self.y_magnitude != 0 { 72 self.y_direction = dy < 0.0; // 0 = down, 1 = up 73 } 74 } 75 76 fn increment_sensitivity(&mut self) { 77 // Sensitivity is always 0-2 78 self.sensitivity = (self.sensitivity + 1) % 3; 79 log::debug!("SNES mouse sensitivity set to {}", self.sensitivity); 80 } 81 82 fn to_register_bits(self) -> u32 { 83 fn reverse_bits_u2(value: u8) -> u8 { 84 ((value & 1) << 1) | ((value & 2) >> 1) 85 } 86 87 fn reverse_bits_u7(value: u8) -> u8 { 88 value.reverse_bits() >> 1 89 } 90 91 // First 8 bits are always 0 92 (u32::from(self.right) << 8) 93 | (u32::from(self.left) << 9) 94 | (u32::from(reverse_bits_u2(self.sensitivity)) << 10) 95 | (1 << 15) // Bits 12-15 are always mouse ID (0, 0, 0, 1) 96 | (u32::from(self.y_direction) << 16) 97 | (u32::from(reverse_bits_u7(self.y_magnitude)) << 17) 98 | (u32::from(self.x_direction) << 24) 99 | (u32::from(reverse_bits_u7(self.x_magnitude)) << 25) 100 } 101} 102 103#[derive(Debug, Clone, Copy, Encode, Decode)] 104struct SuperScopeRegister { 105 fire: bool, 106 cursor: bool, 107 pause: bool, 108 turbo: bool, 109 offscreen: bool, 110 position: Option<(u16, u16)>, 111} 112 113impl Default for SuperScopeRegister { 114 fn default() -> Self { 115 Self { 116 fire: false, 117 cursor: false, 118 pause: false, 119 turbo: false, 120 offscreen: true, 121 position: None, 122 } 123 } 124} 125 126impl SuperScopeRegister { 127 fn update(&mut self, current_state: SuperScopeState, last_strobe_state: SuperScopeState) { 128 if current_state.fire && !last_strobe_state.fire { 129 // Turbo bit updates only when fire is pressed 130 self.turbo = current_state.turbo; 131 } 132 133 // If turbo is off, fire bit is only set for one strobe after the button is pressed 134 self.fire = if current_state.turbo { 135 current_state.fire 136 } else { 137 current_state.fire && !last_strobe_state.fire 138 }; 139 140 // Cursor bit is always set to button press state 141 self.cursor = current_state.cursor; 142 143 // Pause is only set for one strobe after the button is pressed 144 self.pause = current_state.pause && !last_strobe_state.pause; 145 146 // Offscreen bit is only updated when fire or cursor bit is set 147 if self.fire || self.cursor { 148 self.offscreen = current_state.position.is_none(); 149 } 150 151 // Position is only used for latching, which only occurs when fire or cursor bit is set 152 self.position = current_state.position; 153 } 154 155 fn to_register_word(self) -> u16 { 156 u16::from(self.fire) 157 | (u16::from(self.cursor) << 1) 158 | (u16::from(self.turbo) << 2) 159 | (u16::from(self.pause) << 3) 160 | (u16::from(self.offscreen) << 6) 161 | 0xFF00 // ID bits, always 1 162 } 163} 164 165fn register_word_to_u32(word: u16) -> u32 { 166 // Controllers with 16 bits always read out constant 1s afterwards 167 u32::from(word) | (0xFFFF << 16) 168} 169 170#[derive(Debug, Clone, Encode, Decode)] 171struct ControllerPort { 172 auto_joypad_inputs: u16, 173 manual_joypad_inputs: u32, 174 mouse_register: MouseRegister, 175 mouse_sensitivity: f64, 176 super_scope_register: SuperScopeRegister, 177 last_strobe_inputs: SnesController, 178} 179 180impl ControllerPort { 181 fn new(config: &SnesEmulatorConfig) -> Self { 182 Self { 183 auto_joypad_inputs: 0, 184 manual_joypad_inputs: 0, 185 mouse_register: MouseRegister::default(), 186 mouse_sensitivity: config.mouse_sensitivity, 187 super_scope_register: SuperScopeRegister::default(), 188 last_strobe_inputs: SnesController::None, 189 } 190 } 191 192 fn update_strobe(&mut self, strobe: bool, current_inputs: SnesController) { 193 if !matches!(current_inputs, SnesController::Mouse(_)) { 194 self.mouse_register = MouseRegister::default(); 195 } 196 197 if !matches!(current_inputs, SnesController::SuperScope(_)) { 198 self.super_scope_register = SuperScopeRegister::default(); 199 } 200 201 match current_inputs { 202 SnesController::Gamepad(joypad) => { 203 // Gamepads only reset on strobe 1->0 transitions 204 if !strobe { 205 self.manual_joypad_inputs = register_word_to_u32(joypad.to_register_word()); 206 } 207 } 208 SnesController::Mouse(mouse) => { 209 // Not sure the mouse actually works this way, but only update mouse state 210 // on strobe 1->0 transitions because of how mouse movement is tracked and latched 211 if !strobe { 212 self.mouse_register.update(mouse, self.mouse_sensitivity); 213 self.manual_joypad_inputs = self.mouse_register.to_register_bits(); 214 } 215 } 216 SnesController::SuperScope(super_scope) => { 217 self.manual_joypad_inputs = 218 register_word_to_u32(self.super_scope_register.to_register_word()); 219 220 let last_strobe_state = match self.last_strobe_inputs { 221 SnesController::SuperScope(state) => state, 222 _ => SuperScopeState::default(), 223 }; 224 225 self.super_scope_register.update(super_scope, last_strobe_state); 226 } 227 SnesController::None => { 228 // All bits read 0 when no controller is connected 229 // Some games use this for controller detection (e.g. Donkey Kong Country) 230 self.manual_joypad_inputs = 0; 231 } 232 } 233 234 self.last_strobe_inputs = current_inputs; 235 } 236 237 fn next_manual_bit(&mut self, strobe: bool) -> bool { 238 if strobe && matches!(self.last_strobe_inputs, SnesController::Mouse(_)) { 239 // Reading from the mouse while strobe=1 increments sensitivity 240 self.mouse_register.increment_sensitivity(); 241 return false; 242 } 243 244 let next_bit = self.manual_joypad_inputs.bit(0); 245 self.manual_joypad_inputs = 246 (self.manual_joypad_inputs >> 1) | (u32::from(self.end_of_input_bit()) << 31); 247 next_bit 248 } 249 250 fn end_of_input_bit(&self) -> bool { 251 // Reading past end of inputs produces 1s if a controller is connected, 0s if not 252 !matches!(self.last_strobe_inputs, SnesController::None) 253 } 254 255 fn do_auto_read(&mut self) -> u16 { 256 // Auto joypad read always reads out 16 bits serially, earliest in most significant bits 257 let mut auto_read_inputs = 0; 258 for _ in 0..16 { 259 auto_read_inputs = (auto_read_inputs << 1) | u16::from(self.next_manual_bit(false)); 260 } 261 auto_read_inputs 262 } 263} 264 265#[derive(Debug, Clone, Encode, Decode)] 266pub struct InputState { 267 auto_read_cycles_remaining: u64, 268 auto_joypad_p1_inputs: u16, 269 auto_joypad_p2_inputs: u16, 270 strobe: bool, 271 p1: ControllerPort, 272 p2: ControllerPort, 273 inputs: SnesInputs, 274 allow_opposing_directions: bool, 275} 276 277impl InputState { 278 pub fn new(config: &SnesEmulatorConfig) -> Self { 279 Self { 280 auto_read_cycles_remaining: 0, 281 auto_joypad_p1_inputs: SnesJoypadState::default().to_register_word(), 282 auto_joypad_p2_inputs: SnesJoypadState::default().to_register_word(), 283 strobe: false, 284 p1: ControllerPort::new(config), 285 p2: ControllerPort::new(config), 286 inputs: SnesInputs::default(), 287 allow_opposing_directions: config.allow_opposing_joypad_directions, 288 } 289 } 290 291 pub fn set_strobe(&mut self, strobe: bool) { 292 if strobe != self.strobe { 293 self.p1.update_strobe( 294 strobe, 295 self.inputs.p1.with_allow_opposing_directions(self.allow_opposing_directions), 296 ); 297 self.p2.update_strobe( 298 strobe, 299 self.inputs.p2.with_allow_opposing_directions(self.allow_opposing_directions), 300 ); 301 } 302 303 self.strobe = strobe; 304 } 305 306 pub fn auto_joypad_read_in_progress(&self) -> bool { 307 self.auto_read_cycles_remaining != 0 308 } 309 310 pub fn auto_joypad_p1_inputs(&self) -> u16 { 311 self.auto_joypad_p1_inputs 312 } 313 314 pub fn auto_joypad_p2_inputs(&self) -> u16 { 315 self.auto_joypad_p2_inputs 316 } 317 318 pub fn next_manual_p1_bit(&mut self) -> bool { 319 self.p1.next_manual_bit(self.strobe) 320 } 321 322 pub fn next_manual_p2_bit(&mut self) -> bool { 323 self.p2.next_manual_bit(self.strobe) 324 } 325 326 pub fn start_auto_joypad_read(&mut self) { 327 self.auto_read_cycles_remaining = AUTO_JOYPAD_DURATION_MCLK; 328 } 329 330 pub fn tick(&mut self, master_cycles_elapsed: u64, inputs: SnesInputs) { 331 self.inputs = inputs; 332 333 if self.auto_read_cycles_remaining != 0 { 334 self.progress_auto_joypad_read(master_cycles_elapsed); 335 } 336 } 337 338 fn progress_auto_joypad_read(&mut self, master_cycles_elapsed: u64) { 339 self.auto_read_cycles_remaining = 340 self.auto_read_cycles_remaining.saturating_sub(master_cycles_elapsed); 341 342 if self.auto_read_cycles_remaining == 0 { 343 // Auto joypad read strobes the joypad while reading inputs; this populates the manual 344 // joypad read registers 345 self.set_strobe(true); 346 self.set_strobe(false); 347 348 self.auto_joypad_p1_inputs = self.p1.do_auto_read(); 349 self.auto_joypad_p2_inputs = self.p2.do_auto_read(); 350 } 351 } 352 353 pub fn hv_latch(&self) -> Option<(u16, u16)> { 354 // Super Scope can only trigger HV latching behavior when plugged into port 2 355 let super_scope_register = self.p2.super_scope_register; 356 357 // Super Scope latches the PPU at H=X+40, V=Y+1 when Fire or Cursor is set 358 (super_scope_register.fire || super_scope_register.cursor) 359 .then(|| super_scope_register.position.map(|(x, y)| (x + 40, y + 1))) 360 .flatten() 361 } 362 363 pub fn reload_config(&mut self, config: &SnesEmulatorConfig) { 364 self.allow_opposing_directions = config.allow_opposing_joypad_directions; 365 self.p1.mouse_sensitivity = config.mouse_sensitivity; 366 self.p2.mouse_sensitivity = config.mouse_sensitivity; 367 } 368}