input.rsannotatedinput.rssource116 lines · 3.5 KB · raw

GBA input state and registers

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;
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}