Game Boy input handling
10#[derive(Debug, Clone, Encode, Decode)] 11pub(crate) struct InputState { 12 inputs: GameBoyInputs, 13 d_pad_selected: bool, 14 buttons_selected: bool, 15 prev_joyp: u8, 16 allow_opposing_directions: bool, 17} 18 19impl InputState { 20 pub(crate) fn new(config: &GameBoyEmulatorConfig) -> Self { 21 Self { 22 inputs: GameBoyInputs::default(), 23 d_pad_selected: false, 24 buttons_selected: false, 25 prev_joyp: 0xFF, 26 allow_opposing_directions: config.allow_opposing_joypad_directions, 27 } 28 } 29 30 pub(crate) fn set_inputs( 31 &mut self, 32 inputs: GameBoyInputs, 33 interrupts: &mut InterruptRegisters, 34 ) { 35 if self.inputs == inputs { 36 return; 37 } 38 39 self.inputs = inputs; 40 self.check_for_joypad_interrupt(interrupts); 41 } 42 43 pub(crate) fn write_joyp(&mut self, value: u8, interrupts: &mut InterruptRegisters) { 44 self.buttons_selected = !value.bit(5); 45 self.d_pad_selected = !value.bit(4); 46 47 self.check_for_joypad_interrupt(interrupts); 48 49 log::trace!("JOYP write: {value:02X}"); 50 } 51 52 fn check_for_joypad_interrupt(&mut self, interrupts: &mut InterruptRegisters) { 53 // Joypad interrupt triggers when any of JOYP bits 0-3 change from 1 to 0 54 let new_joyp = self.read_joyp(); 55 if self.prev_joyp & 0x0F & !new_joyp != 0 { 56 interrupts.set_flag(InterruptType::Joypad); 57 } 58 self.prev_joyp = new_joyp; 59 } 60 61 pub(crate) fn read_joyp(&self) -> u8 { 62 let inputs = self.inputs.with_allow_opposing_directions(self.allow_opposing_directions); 63 64 let bit_3_inverted = 65 (self.buttons_selected && inputs.start) || (self.d_pad_selected && inputs.down); 66 let bit_2_inverted = 67 (self.buttons_selected && inputs.select) || (self.d_pad_selected && inputs.up); 68 let bit_1_inverted = 69 (self.buttons_selected && inputs.b) || (self.d_pad_selected && inputs.left); 70 let bit_0_inverted = 71 (self.buttons_selected && inputs.a) || (self.d_pad_selected && inputs.right); 72 73 0xC0 | (u8::from(!self.buttons_selected) << 5) 74 | (u8::from(!self.d_pad_selected) << 4) 75 | (u8::from(!bit_3_inverted) << 3) 76 | (u8::from(!bit_2_inverted) << 2) 77 | (u8::from(!bit_1_inverted) << 1) 78 | u8::from(!bit_0_inverted) 79 } 80 81 pub fn reload_config(&mut self, config: &GameBoyEmulatorConfig) { 82 self.allow_opposing_directions = config.allow_opposing_joypad_directions; 83 } 84}