1//! GBA input state and registers 2 3use crate::api::GbaEmulatorConfig; 4use crate::interrupts::{InterruptRegisters, InterruptType}; 5use bincode::{Decode, Encode}; 6use gba_config::GbaInputs; 7use jgenesis_common::define_bit_enum; 8use jgenesis_common::num::GetBit; 9 10define_bit_enum!(IrqLogic, [Or, And]); 11 12#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 13pub struct InputState { 14 inputs: GbaInputs, 15 irq_enabled: bool, 16 irq_logic: IrqLogic, 17 irq_mask: u16, 18 prev_irq_line: bool, 19 allow_opposing_directions: bool, 20} 21 22impl InputState { 23 pub fn new(config: &GbaEmulatorConfig) -> Self { 24 Self { 25 inputs: GbaInputs::default(), 26 irq_enabled: false, 27 irq_logic: IrqLogic::default(), 28 irq_mask: 0, 29 prev_irq_line: false, 30 allow_opposing_directions: config.allow_opposing_joypad_directions, 31 } 32 } 33 34 pub fn update_inputs( 35 &mut self, 36 inputs: GbaInputs, 37 cycles: u64, 38 interrupts: &mut InterruptRegisters, 39 ) { 40 self.inputs = inputs; 41 self.check_for_interrupt(cycles, interrupts); 42 } 43 44 // $4000130: KEYINPUT (Keypad input) 45 pub fn read_keyinput(&self) -> u16 { 46 let joypad = 47 self.inputs.joypad.with_allow_opposing_directions(self.allow_opposing_directions); 48 49 [ 50 (joypad.a, 0), 51 (joypad.b, 1), 52 (joypad.select, 2), 53 (joypad.start, 3), 54 (joypad.right, 4), 55 (joypad.left, 5), 56 (joypad.up, 6), 57 (joypad.down, 7), 58 (joypad.r, 8), 59 (joypad.l, 9), 60 ] 61 .into_iter() 62 .map(|(pressed, bit)| u16::from(!pressed) << bit) 63 .reduce(|a, b| a | b) 64 .unwrap() 65 } 66 67 // $4000132: KEYCNT (Keypad interrupt control) 68 pub fn write_keycnt(&mut self, value: u16, cycles: u64, interrupts: &mut InterruptRegisters) { 69 self.irq_enabled = value.bit(14); 70 self.irq_logic = IrqLogic::from_bit(value.bit(15)); 71 self.irq_mask = value & 0x3FF; 72 73 self.check_for_interrupt(cycles, interrupts); 74 75 log::trace!("KEYCNT write: {value:04X}"); 76 log::trace!(" IRQ enabled: {}", self.irq_enabled); 77 log::trace!(" IRQ logic: {:?}", self.irq_logic); 78 log::trace!(" IRQ mask: {:04X}", self.irq_mask); 79 } 80 81 // $4000132: KEYCNT (Keypad interrupt control) 82 pub fn read_keycnt(&self) -> u16 { 83 self.irq_mask | (u16::from(self.irq_enabled) << 14) | ((self.irq_logic as u16) << 15) 84 } 85 86 fn check_for_interrupt(&mut self, cycles: u64, interrupts: &mut InterruptRegisters) { 87 if !self.irq_enabled && !interrupts.stopped() { 88 self.prev_irq_line = false; 89 return; 90 } 91 92 let pressed = !self.read_keyinput(); 93 94 let irq_line = match self.irq_logic { 95 IrqLogic::Or => pressed & self.irq_mask != 0, 96 IrqLogic::And => pressed & self.irq_mask == self.irq_mask, 97 }; 98 99 if interrupts.stopped() { 100 // Keypad IRQ state doesn't update during stop, but the IRQ logic can end stop 101 if irq_line { 102 interrupts.set_flag(InterruptType::Keypad, cycles); 103 } 104 return; 105 } 106 107 if !self.prev_irq_line && irq_line { 108 interrupts.set_flag(InterruptType::Keypad, cycles); 109 } 110 self.prev_irq_line = irq_line; 111 } 112 113 pub fn reload_config(&mut self, config: &GbaEmulatorConfig) { 114 self.allow_opposing_directions = config.allow_opposing_joypad_directions; 115 } 116}