1//! GBA SIO / serial port 2//! 3//! SIO is largely not emulated, only stubbed out enough for games that access it to kind of work 4 5use crate::interrupts::{InterruptRegisters, InterruptType}; 6use bincode::{Decode, Encode}; 7use jgenesis_common::num::GetBit; 8 9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 10enum Mode { 11 #[default] 12 Normal, 13 MultiPlayer, 14 Uart, 15 JoyBus, 16 GeneralPurpose, 17} 18 19#[derive(Debug, Clone, Encode, Decode)] 20pub struct SerialPort { 21 mode: Mode, 22 rcnt: u16, 23 siocnt: u16, 24 siodata32: u32, 25 siodata8: u16, 26 next_irq_cycles: Option<u64>, 27} 28 29impl SerialPort { 30 pub fn new() -> Self { 31 Self { 32 mode: Mode::default(), 33 rcnt: 0x8000, // Sonic Advance boots into multiplayer mode if this defaults to 0 34 siocnt: 0, 35 siodata32: !0, 36 siodata8: !0, 37 next_irq_cycles: None, 38 } 39 } 40 41 pub fn read_register(&mut self, address: u32) -> u16 { 42 log::trace!("SIO read {address:08X}"); 43 44 match address { 45 0x4000120 => self.siodata32 as u16, 46 0x4000122 => (self.siodata32 >> 16) as u16, 47 0x4000128 => self.read_siocnt(), 48 0x400012A => self.siodata8, 49 0x4000134 => self.read_rcnt(), 50 0x4000136 | 0x4000142 | 0x400015A => 0, // Invalid addresses that return 0 51 _ => { 52 log::debug!("Unimplemented SIO read {address:08X}"); 53 !0 54 } 55 } 56 } 57 58 pub fn write_register(&mut self, address: u32, value: u16, cycles: u64) { 59 match address { 60 0x4000120 => { 61 self.siodata32 = (self.siodata32 & !0xFFFF) | u32::from(value); 62 log::trace!("SIODATA32: {:08X}", self.siodata32); 63 } 64 0x4000122 => { 65 self.siodata32 = (self.siodata32 & 0xFFFF) | (u32::from(value) << 16); 66 log::trace!("SIODATA32: {:08X}", self.siodata32); 67 } 68 0x4000128 => self.write_siocnt(value, cycles), 69 0x400012A => { 70 self.siodata8 = value; 71 log::trace!("SIODATA8: {:04X}", self.siodata8); 72 } 73 0x4000134 => self.write_rcnt(value), 74 _ => { 75 log::debug!("Unimplemented SIO write {address:08X} {value:04X}"); 76 } 77 } 78 } 79 80 fn read_rcnt(&self) -> u16 { 81 self.rcnt 82 } 83 84 fn write_rcnt(&mut self, value: u16) { 85 self.rcnt = value; 86 self.update_mode(); 87 88 log::trace!("RCNT write: {value:04X}"); 89 log::trace!(" SIO mode: {:?}", self.mode); 90 } 91 92 fn read_siocnt(&self) -> u16 { 93 self.siocnt 94 } 95 96 fn write_siocnt(&mut self, value: u16, cycles: u64) { 97 let prev_active = self.siocnt.bit(7); 98 99 self.siocnt = value; 100 self.update_mode(); 101 102 let active = self.siocnt.bit(7); 103 if self.mode == Mode::Normal && !prev_active && active { 104 log::trace!("SIO transfer started in Normal mode"); 105 106 let internal_clock = self.siocnt.bit(0); 107 if internal_clock { 108 let clock_rate = if self.siocnt.bit(1) { 109 // 2 MHz 110 2 * 1024 * 1024 111 } else { 112 // 256 KHz 113 256 * 1024 114 }; 115 116 let cycles_per_bit = crate::GBA_CLOCK_SPEED / clock_rate; 117 let bits = if self.siocnt.bit(12) { 32 } else { 8 }; 118 119 self.next_irq_cycles = Some(cycles + bits * cycles_per_bit); 120 } 121 } 122 123 log::trace!("SIOCNT write: {value:04X}"); 124 log::trace!(" SIO mode: {:?}", self.mode); 125 } 126 127 fn update_mode(&mut self) { 128 let bits = ((self.rcnt >> 14) << 2) | ((self.siocnt >> 12) & 3); 129 130 self.mode = if bits & 0b1010 == 0b0000 { 131 Mode::Normal 132 } else if bits & 0b1011 == 0b0010 { 133 Mode::MultiPlayer 134 } else if bits & 0b1011 == 0b0011 { 135 Mode::Uart 136 } else if bits & 0b1100 == 0b1000 { 137 Mode::GeneralPurpose 138 } else { 139 // bits & 0b1100 == 0b1100 140 Mode::JoyBus 141 }; 142 143 if self.mode == Mode::MultiPlayer { 144 // Pretend transfer finished with no GBAs connected 145 self.siocnt &= !(1 << 7); 146 self.siodata32 = u32::from(self.siodata8) | (0xFFFF << 16); 147 } 148 } 149 150 pub fn check_for_interrupt(&mut self, cycles: u64, interrupts: &mut InterruptRegisters) { 151 let Some(next_irq_cycles) = self.next_irq_cycles else { return }; 152 153 if next_irq_cycles >= cycles { 154 if self.siocnt.bit(14) { 155 interrupts.set_flag(InterruptType::Serial, next_irq_cycles); 156 } 157 158 // Act like nothing is connected 159 if self.siocnt.bit(12) { 160 self.siodata32 = !0; 161 } else { 162 self.siodata8 = !0; 163 } 164 165 self.siocnt &= !(1 << 7); 166 self.next_irq_cycles = None; 167 } 168 } 169}