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.
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}