use crate::gba::bus::interrupt::{InterruptController, bits};
use serde::{Deserialize, Serialize};
pub const REG_KEYINPUT: u32 = 0x0400_0130;
pub const REG_KEYCNT: u32 = 0x0400_0132;
pub const KEYS_MASK: u16 = 0x03FF;
pub const KEYCNT_IRQ_ENABLE: u16 = 1 << 14;
pub const KEYCNT_COND_AND: u16 = 1 << 15;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Keypad {
pressed: u16,
keycnt: u16,
irq_active: bool,
}
impl Keypad {
pub fn new() -> Self {
Self::default()
}
pub fn read_keyinput(&self) -> u16 {
(!self.pressed) & KEYS_MASK
}
pub fn read_keycnt(&self) -> u16 {
self.keycnt
}
pub fn write_keycnt(&mut self, value: u16, ic: &mut InterruptController) {
self.keycnt = value;
self.update_irq(ic);
}
pub fn set_button(&mut self, id: u8, pressed: bool, ic: &mut InterruptController) {
if let Some(bit) = button_id_to_keyinput_bit(id) {
let mask = 1u16 << bit;
if pressed {
self.pressed |= mask;
} else {
self.pressed &= !mask;
}
self.update_irq(ic);
}
}
pub fn get_states(&self) -> u8 {
let actions = (self.pressed & 0x000F) as u8; let right = ((self.pressed >> 4) & 1) as u8;
let left = ((self.pressed >> 5) & 1) as u8;
let up = ((self.pressed >> 6) & 1) as u8;
let down = ((self.pressed >> 7) & 1) as u8;
actions | (up << 4) | (down << 5) | (left << 6) | (right << 7)
}
pub(crate) fn pressed_mask(&self) -> u16 {
self.pressed & KEYS_MASK
}
pub(crate) fn set_pressed_mask(&mut self, pressed: u16, ic: &mut InterruptController) {
self.pressed = pressed & KEYS_MASK;
self.update_irq(ic);
}
pub fn set_states(&mut self, state: u8, ic: &mut InterruptController) {
let actions = (state & 0x0F) as u16;
let up = ((state >> 4) & 1) as u16;
let down = ((state >> 5) & 1) as u16;
let left = ((state >> 6) & 1) as u16;
let right = ((state >> 7) & 1) as u16;
let lr = self.pressed & 0x0300;
self.pressed = actions | (right << 4) | (left << 5) | (up << 6) | (down << 7) | lr;
self.update_irq(ic);
}
fn update_irq(&mut self, ic: &mut InterruptController) {
let now = self.irq_condition_met();
if now && !self.irq_active {
ic.raise(bits::KEYPAD);
}
self.irq_active = now;
}
fn irq_condition_met(&self) -> bool {
if self.keycnt & KEYCNT_IRQ_ENABLE == 0 {
return false;
}
let select = self.keycnt & KEYS_MASK;
if select == 0 {
return false;
}
let pressed_selected = self.pressed & select;
if self.keycnt & KEYCNT_COND_AND != 0 {
pressed_selected == select
} else {
pressed_selected != 0
}
}
}
fn button_id_to_keyinput_bit(id: u8) -> Option<u8> {
Some(match id {
0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 6, 5 => 7, 6 => 5, 7 => 4, 8 => 9, 9 => 8, _ => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn ic() -> InterruptController {
InterruptController::new()
}
#[test]
fn keyinput_default_reads_all_released() {
let kp = Keypad::new();
assert_eq!(kp.read_keyinput(), KEYS_MASK);
}
#[test]
fn keyinput_active_low_for_a_button() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(0, true, &mut ic); assert_eq!(kp.read_keyinput(), 0x03FE);
kp.set_button(0, false, &mut ic);
assert_eq!(kp.read_keyinput(), 0x03FF);
}
#[test]
fn keyinput_bit_mapping_for_all_ten_buttons() {
let cases: [(u8, u16); 10] = [
(0, 0), (1, 1), (2, 2), (3, 3), (4, 6), (5, 7), (6, 5), (7, 4), (8, 9), (9, 8), ];
for (id, bit) in cases {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(id, true, &mut ic);
assert_eq!(
kp.read_keyinput(),
KEYS_MASK & !(1u16 << bit),
"button id {id} should clear KEYINPUT bit {bit}"
);
}
}
#[test]
fn keyinput_a_plus_b_pressed() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(0, true, &mut ic);
kp.set_button(1, true, &mut ic);
assert_eq!(kp.read_keyinput(), 0x03FC);
}
#[test]
fn keyinput_all_ten_pressed_reads_zero() {
let mut kp = Keypad::new();
let mut ic = ic();
for id in 0..10u8 {
kp.set_button(id, true, &mut ic);
}
assert_eq!(kp.read_keyinput(), 0x0000);
}
#[test]
fn unknown_button_id_is_silently_ignored() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(42, true, &mut ic);
assert_eq!(kp.read_keyinput(), KEYS_MASK);
}
#[test]
fn keypad_irq_not_raised_when_keycnt_disabled() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(0x0001, &mut ic);
kp.set_button(0, true, &mut ic);
assert_eq!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn keypad_irq_raised_or_mode_when_any_selected_pressed() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
assert_eq!(ic.if_flags & bits::KEYPAD, 0);
kp.set_button(0, true, &mut ic);
assert_ne!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn keypad_irq_or_mode_ignores_unselected_buttons() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
kp.set_button(1, true, &mut ic);
assert_eq!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn keypad_irq_and_mode_requires_all_selected() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | KEYCNT_COND_AND | 0b11, &mut ic);
kp.set_button(0, true, &mut ic);
assert_eq!(
ic.if_flags & bits::KEYPAD,
0,
"AND mode must require ALL selected buttons"
);
kp.set_button(1, true, &mut ic);
assert_ne!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn write_keycnt_re_evaluates_irq_when_button_already_pressed() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(0, true, &mut ic); assert_eq!(ic.if_flags & bits::KEYPAD, 0);
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
assert_ne!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn keypad_irq_disable_after_enable_does_not_re_raise() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
kp.set_button(0, true, &mut ic);
ic.acknowledge(bits::KEYPAD); kp.write_keycnt(0x0001, &mut ic);
kp.set_button(0, false, &mut ic);
kp.set_button(0, true, &mut ic);
assert_eq!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn set_states_and_get_states_round_trip() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_states(0b1010_0101, &mut ic);
assert_eq!(kp.get_states(), 0b1010_0101);
}
#[test]
fn set_states_preserves_l_and_r_buttons() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(8, true, &mut ic); kp.set_button(9, true, &mut ic); kp.set_states(0b0000_0001, &mut ic); assert_eq!(
kp.read_keyinput(),
KEYS_MASK & !((1 << 0) | (1 << 8) | (1 << 9))
);
}
#[test]
fn held_button_does_not_re_raise_irq_via_set_states() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic); kp.set_states(0b0000_0001, &mut ic); assert_ne!(ic.if_flags & bits::KEYPAD, 0);
ic.acknowledge(bits::KEYPAD);
for _ in 0..8 {
kp.set_states(0b0000_0001, &mut ic);
}
assert_eq!(
ic.if_flags & bits::KEYPAD,
0,
"held key must not storm the IRQ flag"
);
}
#[test]
fn redundant_press_does_not_re_raise_irq() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
kp.set_button(0, true, &mut ic);
ic.acknowledge(bits::KEYPAD);
kp.set_button(0, true, &mut ic);
assert_eq!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn release_then_press_re_raises_irq() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
kp.set_button(0, true, &mut ic);
ic.acknowledge(bits::KEYPAD);
kp.set_button(0, false, &mut ic);
kp.set_button(0, true, &mut ic);
assert_ne!(ic.if_flags & bits::KEYPAD, 0);
}
#[test]
fn redundant_keycnt_write_does_not_re_raise_irq() {
let mut kp = Keypad::new();
let mut ic = ic();
kp.set_button(0, true, &mut ic);
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
ic.acknowledge(bits::KEYPAD);
kp.write_keycnt(KEYCNT_IRQ_ENABLE | 0x0001, &mut ic);
assert_eq!(ic.if_flags & bits::KEYPAD, 0);
}
}