input.rsannotatedinput.rssource130 lines · 4.2 KB · raw

Code for handling Sega Master System / Game Gear controller input I/O registers

3use crate::SmsGgEmulatorConfig;
4use crate::vdp::Vdp;
5use bincode::{Decode, Encode};
6use jgenesis_common::num::GetBit;
7use smsgg_config::{SmsGgInputs, SmsGgRegion};
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
10enum PinDirection {
11    Input,
12    Output(bool),
13}
14
15impl PinDirection {
16    fn bit(self, joypad_value: bool) -> bool {
17        match self {
18            Self::Input => joypad_value,
19            Self::Output(output_value) => output_value,
20        }
21    }
22}
23
24#[derive(Debug, Clone, Encode, Decode)]
25pub struct InputState {
26    inputs: SmsGgInputs,
27    port_a_tr: PinDirection,
28    port_a_th: PinDirection,
29    port_b_tr: PinDirection,
30    port_b_th: PinDirection,
31    region: SmsGgRegion,
32    reset: bool,
33    allow_opposing_directions: bool,
34}
35
36impl InputState {
37    pub fn new(region: SmsGgRegion, config: &SmsGgEmulatorConfig) -> Self {
38        Self {
39            inputs: SmsGgInputs::default(),
40            port_a_tr: PinDirection::Input,
41            port_a_th: PinDirection::Input,
42            port_b_tr: PinDirection::Input,
43            port_b_th: PinDirection::Input,
44            region,
45            reset: false,
46            allow_opposing_directions: config.allow_opposing_joypad_directions,
47        }
48    }
49
50    pub fn pause_pressed(&self) -> bool {
51        self.inputs.pause
52    }
53
54    pub fn set_inputs(&mut self, inputs: SmsGgInputs) {
55        self.inputs = inputs;
56    }
57
58    pub fn region(&self) -> SmsGgRegion {
59        self.region
60    }
61
62    pub fn reload_config(&mut self, region: SmsGgRegion, config: &SmsGgEmulatorConfig) {
63        self.region = region;
64        self.allow_opposing_directions = config.allow_opposing_joypad_directions;
65    }
66
67    pub fn set_reset(&mut self, reset: bool) {
68        self.reset = reset;
69    }
70
71    pub fn write_control(&mut self, value: u8, vdp: &mut Vdp) {
72        log::debug!("I/O control write {value:02X}");
73
74        let prev_a_th = self.port_a_th != PinDirection::Output(false);
75        let prev_b_th = self.port_b_th != PinDirection::Output(false);
76
77        self.port_b_th =
78            if value.bit(3) { PinDirection::Input } else { PinDirection::Output(value.bit(7)) };
79        self.port_b_tr =
80            if value.bit(2) { PinDirection::Input } else { PinDirection::Output(value.bit(6)) };
81        self.port_a_th =
82            if value.bit(1) { PinDirection::Input } else { PinDirection::Output(value.bit(5)) };
83        self.port_a_tr =
84            if value.bit(0) { PinDirection::Input } else { PinDirection::Output(value.bit(4)) };
85
86        if (!prev_a_th && self.port_a_th != PinDirection::Output(false))
87            || (!prev_b_th && self.port_b_th != PinDirection::Output(false))
88        {
89            vdp.latch_h_counter_on_th_change();
90        }
91    }
92
93    pub fn port_dc(&self) -> u8 {
94        let p1_inputs =
95            self.inputs.p1.with_allow_opposing_directions(self.allow_opposing_directions);
96        let p2_inputs =
97            self.inputs.p2.with_allow_opposing_directions(self.allow_opposing_directions);
98
99        let port_a_tr_bit = u8::from(self.port_a_tr.bit(!p1_inputs.button2)) << 5;
100
101        (u8::from(!p2_inputs.down) << 7)
102            | (u8::from(!p2_inputs.up) << 6)
103            | port_a_tr_bit
104            | (u8::from(!p1_inputs.button1) << 4)
105            | (u8::from(!p1_inputs.right) << 3)
106            | (u8::from(!p1_inputs.left) << 2)
107            | (u8::from(!p1_inputs.down) << 1)
108            | u8::from(!p1_inputs.up)
109    }
110
111    pub fn port_dd(&self) -> u8 {
112        let p2_inputs =
113            self.inputs.p2.with_allow_opposing_directions(self.allow_opposing_directions);
114
115        let port_b_th_bit =
116            u8::from(self.region == SmsGgRegion::International && self.port_b_th.bit(true)) << 7;
117        let port_a_th_bit =
118            u8::from(self.region == SmsGgRegion::International && self.port_a_th.bit(true)) << 6;
119        let port_b_tr_bit = u8::from(self.port_b_tr.bit(!p2_inputs.button2)) << 3;
120
121        port_b_th_bit
122            | port_a_th_bit
123            | (1 << 5)
124            | (u8::from(!self.reset) << 4)
125            | port_b_tr_bit
126            | (u8::from(!p2_inputs.button1) << 2)
127            | (u8::from(!p2_inputs.right) << 1)
128            | u8::from(!p2_inputs.left)
129    }
130}