1use crate::input::Pins;
2use bincode::{Decode, Encode};
3use genesis_config::GenesisJoypadState;
4
5#[derive(Debug, Clone, Encode, Decode)]
6pub struct ThreeButtonState {
7    pub joypad: GenesisJoypadState,
8}
9
10impl ThreeButtonState {
11    pub fn new(joypad: GenesisJoypadState) -> Self {
12        Self { joypad }
13    }
14
15    pub fn update_pins(&self, pins: &mut Pins) {
16        if pins.th() {
17            // B, C, and directional inputs
18            pins.input_tr(!self.joypad.c);
19            pins.input_tl(!self.joypad.b);
20            pins.input_d3(!self.joypad.right);
21            pins.input_d2(!self.joypad.left);
22            pins.input_d1(!self.joypad.down);
23            pins.input_d0(!self.joypad.up);
24        } else {
25            // A and start (and up/down)
26            pins.input_tr(!self.joypad.start);
27            pins.input_tl(!self.joypad.a);
28            pins.input_d3(false);
29            pins.input_d2(false);
30            pins.input_d1(!self.joypad.down);
31            pins.input_d0(!self.joypad.up);
32        }
33    }
34}
35
36#[derive(Debug, Clone, Encode, Decode)]
37pub struct SixButtonState {
38    pub joypad: GenesisJoypadState,
39    th_flip_count: u8,
40    flip_reset_counter: u32,
41    last_th: bool,
42}
43
44impl SixButtonState {
45    // Produces roughly the expected timeout value in Joystick Test Program (PD), about 1.58ms
46    const FLIP_COUNTER_CYCLES: u32 = 12150;
47
48    pub fn new(joypad: GenesisJoypadState) -> Self {
49        Self { joypad, th_flip_count: 0, flip_reset_counter: 0, last_th: true }
50    }
51
52    pub fn update_pins(&mut self, pins: &mut Pins) {
53        // 6-button controller cycles through 5 different modes whenever TH flips from 0 to 1,
54        // resetting after ~1.5ms have passed without such a flip
55        let th = pins.th();
56        if !self.last_th && th {
57            self.th_flip_count = (self.th_flip_count + 1) % 5;
58            self.flip_reset_counter = Self::FLIP_COUNTER_CYCLES;
59        }
60        self.last_th = th;
61
62        // TR and TL are always set the same way as 3-button
63        if th {
64            pins.input_tr(!self.joypad.c);
65            pins.input_tl(!self.joypad.b);
66        } else {
67            pins.input_tr(!self.joypad.start);
68            pins.input_tl(!self.joypad.a);
69        }
70
71        match (self.th_flip_count, th) {
72            (0..=2 | 4, true) => {
73                // 3-button: B, C, and directional inputs
74                pins.input_d3(!self.joypad.right);
75                pins.input_d2(!self.joypad.left);
76                pins.input_d1(!self.joypad.down);
77                pins.input_d0(!self.joypad.up);
78            }
79            (0 | 1 | 4, false) => {
80                // 3-button: A and Start (and up/down)
81                pins.input_d3(false);
82                pins.input_d2(false);
83                pins.input_d1(!self.joypad.down);
84                pins.input_d0(!self.joypad.up);
85            }
86            (2, false) => {
87                // 6-button: A, Start, and all 0s in the lower bits
88                pins.input_data_nibble(0b0000);
89            }
90            (3, true) => {
91                // 6-button: New buttons (and B and C)
92                pins.input_d3(!self.joypad.mode);
93                pins.input_d2(!self.joypad.x);
94                pins.input_d1(!self.joypad.y);
95                pins.input_d0(!self.joypad.z);
96            }
97            (3, false) => {
98                // 6-button: A, Start, and all 1s in the lower bits
99                pins.input_data_nibble(0b1111);
100            }
101            _ => panic!("th_flip_count should always be <= 4, was {}", self.th_flip_count),
102        }
103    }
104
105    pub fn tick(&mut self, m68k_cycles: u32, pins: &mut Pins) {
106        if self.flip_reset_counter == 0 {
107            return;
108        }
109
110        self.flip_reset_counter = self.flip_reset_counter.saturating_sub(m68k_cycles);
111        if self.flip_reset_counter == 0 {
112            self.th_flip_count = 0;
113            self.update_pins(pins);
114        }
115    }
116}