sci.rsannotatedsci.rssource214 lines · 7.1 KB · raw
1//! SH7604 serial communication interface (SCI)
2//!
3//! No 32X games use this but some test cartridges do
4
5use crate::bus::BusInterface;
6use bincode::{Decode, Encode};
7use jgenesis_common::num::GetBit;
8
9#[derive(Debug, Clone, Encode, Decode)]
10pub struct SerialInterface {
11    name: String,
12    tx_enabled: bool,
13    rx_enabled: bool,
14    tx_interrupt_enabled: bool,
15    rx_interrupt_enabled: bool,
16    transfer_data: u8,
17    transfer_shift: u8,
18    transfer_clocks: u64,
19    receive_data: u8,
20    tx_data_empty: bool,
21    rx_data_full: bool,
22    transfer_end: bool,
23    clock_select: u8,
24    bit_rate: u8,
25}
26
27impl SerialInterface {
28    pub fn new(name: String) -> Self {
29        Self {
30            name,
31            tx_enabled: false,
32            rx_enabled: false,
33            tx_interrupt_enabled: false,
34            rx_interrupt_enabled: false,
35            transfer_data: 0xFF,
36            transfer_shift: 0xFF,
37            transfer_clocks: 0,
38            receive_data: 0x00,
39            tx_data_empty: true,
40            rx_data_full: false,
41            transfer_end: true,
42            clock_select: 0,
43            bit_rate: 0,
44        }
45    }
46
47    pub fn process<B: BusInterface>(&mut self, sh2_clocks_elapsed: u64, bus: &mut B) {
48        if self.rx_enabled
49            && !self.rx_data_full
50            && let Some(rx) = bus.serial_rx()
51        {
52            self.receive_data = rx;
53            self.rx_data_full = true;
54        }
55
56        if self.transfer_clocks == 0 {
57            if self.tx_enabled && !self.tx_data_empty {
58                // TODO TX interrupt
59                self.transfer_shift = self.transfer_data;
60                self.transfer_clocks = estimate_tx_clocks(self.clock_select, self.bit_rate);
61                self.tx_data_empty = true;
62                log::debug!("TX clocks: {}", self.transfer_clocks);
63            } else {
64                return;
65            }
66        }
67
68        self.transfer_clocks = self.transfer_clocks.saturating_sub(sh2_clocks_elapsed);
69        if self.transfer_clocks == 0 {
70            bus.serial_tx(self.transfer_shift);
71            if !self.tx_data_empty {
72                // TODO TX interrupt
73                self.transfer_shift = self.transfer_data;
74                self.transfer_clocks = estimate_tx_clocks(self.clock_select, self.bit_rate);
75                self.tx_data_empty = true;
76                log::debug!("TX clocks: {}", self.transfer_clocks);
77            } else {
78                // TODO transfer end interrupt
79                self.transfer_end = true;
80            }
81        }
82    }
83
84    pub fn read_register(&self, address: u32) -> u8 {
85        log::debug!("[{}] SCI read {address:08X}", self.name);
86
87        match address {
88            0xFFFFFE00 => self.read_mode(),
89            0xFFFFFE01 => self.bit_rate,
90            0xFFFFFE02 => self.read_control(),
91            0xFFFFFE03 => self.transfer_data,
92            0xFFFFFE04 => self.read_status(),
93            0xFFFFFE05 => self.read_rx(),
94            _ => panic!("Invalid SCI register address: {address:08X}"),
95        }
96    }
97
98    pub fn write_register(&mut self, address: u32, value: u8) {
99        match address {
100            0xFFFFFE00 => self.write_mode(value),
101            0xFFFFFE01 => self.write_bit_rate(value),
102            0xFFFFFE02 => self.write_control(value),
103            0xFFFFFE03 => self.write_tx(value),
104            0xFFFFFE04 => self.write_status(value),
105            // RX data register, ignore writes
106            0xFFFFFE05 => {}
107            _ => panic!("Invalid SCI register address: {address:08X} {value:02X}"),
108        }
109    }
110
111    // $FFFFFE00: SMR (Serial mode)
112    fn read_mode(&self) -> u8 {
113        self.clock_select
114    }
115
116    // $FFFFFE00: SMR (Serial mode)
117    fn write_mode(&mut self, value: u8) {
118        self.clock_select = value & 3;
119
120        log::debug!("[{}] SMR write: {value:02X}", self.name);
121        log::debug!("  Clocked synchronous mode: {}", value.bit(7));
122        log::debug!("  Character length: {}", if value.bit(6) { "7-bit" } else { "8-bit" });
123        log::debug!("  Parity check enabled: {}", value.bit(5));
124        log::debug!("  Parity mode odd/even flag: {}", value.bit(4));
125        log::debug!("  Stop bit length bit: {}", value.bit(3));
126        log::debug!("  Multiprocessor mode: {}", value.bit(2));
127        log::debug!(
128            "  Clock select: {}",
129            match value & 3 {
130                0 => "sysclk/4",
131                1 => "sysclk/16",
132                2 => "sysclk/64",
133                3 => "sysclk/256",
134                _ => unreachable!(),
135            }
136        );
137    }
138
139    // $FFFFFE01: BRR (Bit rate)
140    fn write_bit_rate(&mut self, value: u8) {
141        self.bit_rate = value;
142        log::debug!("[{}] BRR write: {value:02X}", self.name);
143    }
144
145    // $FFFFFE02: SCR (Serial control)
146    fn read_control(&self) -> u8 {
147        (u8::from(self.tx_interrupt_enabled) << 7)
148            | (u8::from(self.rx_interrupt_enabled) << 6)
149            | (u8::from(self.tx_enabled) << 5)
150            | (u8::from(self.rx_enabled) << 4)
151    }
152
153    // $FFFFFE02: SCR (Serial control)
154    fn write_control(&mut self, value: u8) {
155        self.tx_interrupt_enabled = value.bit(7);
156        self.rx_interrupt_enabled = value.bit(6);
157        self.tx_enabled = value.bit(5);
158        self.rx_enabled = value.bit(4);
159
160        log::debug!("[{}] SCR write: {value:02X}", self.name);
161        log::debug!("  TX interrupt enabled: {}", self.tx_interrupt_enabled);
162        log::debug!("  RX interrupt enabled: {}", self.rx_interrupt_enabled);
163        log::debug!("  TX enabled: {}", self.tx_enabled);
164        log::debug!("  RX enabled: {}", self.rx_enabled);
165        log::debug!("  Multiprocessor interrupt enabled: {}", value.bit(3));
166        log::debug!("  Transfer end interrupt enabled: {}", value.bit(2));
167        log::debug!("  Clock enabled bits: {}", value & 3);
168    }
169
170    // $FFFFFE03: TDR (Transfer data register)
171    fn write_tx(&mut self, value: u8) {
172        self.transfer_data = value;
173
174        log::trace!("[{}] TDR write: {value:02X}", self.name);
175    }
176
177    // $FFFFFE04: SSR (Serial status)
178    fn read_status(&self) -> u8 {
179        (u8::from(self.tx_data_empty) << 7)
180            | (u8::from(self.rx_data_full) << 6)
181            | (u8::from(self.transfer_end) << 2)
182    }
183
184    // $FFFFFE04: SSR (Serial status)
185    fn write_status(&mut self, value: u8) {
186        self.tx_data_empty &= value.bit(7);
187        self.transfer_end &= value.bit(7);
188        self.rx_data_full &= value.bit(6);
189
190        log::debug!("[{}] SSR write: {value:02X}", self.name);
191        log::debug!("  Clear TX data empty: {}", !value.bit(7));
192        log::debug!("  Clear RX data full: {}", !value.bit(6));
193        log::debug!("  Multiprocessor bit: {}", value & 1);
194    }
195
196    // $FFFFFE05: RDR (Receive data register)
197    fn read_rx(&self) -> u8 {
198        self.receive_data
199    }
200
201    pub fn rx_interrupt_pending(&self) -> bool {
202        self.rx_interrupt_enabled && self.rx_data_full
203    }
204}
205
206fn estimate_tx_clocks(clock_select: u8, bit_rate: u8) -> u64 {
207    let clocks_per_bit = if clock_select == 0 {
208        128 * (u64::from(bit_rate) + 1)
209    } else {
210        256 * (1 << (2 * clock_select - 1)) * (u64::from(bit_rate) + 1)
211    };
212
213    8 * clocks_per_bit
214}