Game Boy serial port

Accessories that use the serial port (e.g. link cable) are not emulated, but some games depend on the serial port responding correctly to reads/writes.

6use crate::HardwareMode;
7use crate::interrupts::InterruptRegisters;
8use crate::sm83::InterruptType;
9use bincode::{Decode, Encode};
10use jgenesis_common::num::GetBit;

Base serial transfer rate is 8192 bits/second == 1024 bytes/second The normal-speed CPU M-cycle clock is 1.048576 MHz (1048576 cycles/second) / (1024 bytes/second) == 1024 cycles/byte == 128 cycles/bit

15const BASE_CYCLES_PER_BIT: u32 = 128;
17#[derive(Debug, Clone, Encode, Decode)]
18pub struct SerialPort {
19    hardware_mode: HardwareMode,
20    transfer_enabled: bool,
21    gbc_high_speed: bool,
22    internal_clock: bool,
23    transfer_cycles_remaining: u32,
24    transfer_bits_remaining: u8,
25    transfer_data: u8,
26}
27
28impl SerialPort {
29    pub fn new(hardware_mode: HardwareMode) -> Self {
30        Self {
31            hardware_mode,
32            transfer_enabled: false,
33            gbc_high_speed: false,
34            internal_clock: false,
35            transfer_cycles_remaining: 0,
36            transfer_bits_remaining: 0,
37            transfer_data: 0,
38        }
39    }
40
41    pub fn tick(&mut self, interrupt_registers: &mut InterruptRegisters) {
42        if !self.transfer_enabled || !self.internal_clock || self.transfer_bits_remaining == 0 {
43            return;
44        }
45
46        self.transfer_cycles_remaining -= 1;
47        if self.transfer_cycles_remaining == 0 {
48            self.transfer_cycles_remaining = self.cycles_per_bit();
49
50            // Shift in 1s to emulate nothing being connected
51            self.transfer_data = (self.transfer_data << 1) | 1;
52
53            self.transfer_bits_remaining -= 1;
54            if self.transfer_bits_remaining == 0 {
55                self.transfer_enabled = false;
56                interrupt_registers.set_flag(InterruptType::Serial);
57            }
58        }
59    }
60
61    // $FF01: SB (Serial transfer data)
62    pub fn read_data(&self) -> u8 {
63        self.transfer_data
64    }
65
66    // $FF01: SB (Serial transfer data)
67    pub fn write_data(&mut self, value: u8) {
68        self.transfer_data = value;
69
70        log::trace!("SB write: {value:02X}");
71    }
72
73    // $FF02: SC (Serial transfer control)
74    pub fn read_control(&self) -> u8 {
75        (u8::from(self.transfer_enabled) << 7)
76            | (u8::from(self.gbc_high_speed) << 1)
77            | u8::from(self.internal_clock)
78    }
79
80    // $FF02: SC (Serial transfer control)
81    pub fn write_control(&mut self, value: u8) {
82        self.transfer_enabled = value.bit(7);
83        self.gbc_high_speed = self.hardware_mode == HardwareMode::Cgb && value.bit(1);
84        self.internal_clock = value.bit(0);
85
86        if self.transfer_enabled && self.internal_clock {
87            self.transfer_cycles_remaining = self.cycles_per_bit();
88            self.transfer_bits_remaining = 8;
89        }
90
91        log::trace!("SC write: {value:02X}");
92        log::trace!("  Transfer enabled: {}", self.transfer_enabled);
93        log::trace!("  GBC high speed: {}", self.gbc_high_speed);
94        log::trace!("  Internal clock: {}", self.internal_clock);
95    }
96
97    fn cycles_per_bit(&self) -> u32 {
98        BASE_CYCLES_PER_BIT >> u8::from(self.gbc_high_speed)
99    }
100}