diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 90bd7166..9ffcbc00 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -171,6 +171,8 @@ pub mod hash_map; /// Simple random number generator pub mod rng; pub mod save; +/// Link port support +pub mod serial_link; mod single; /// Implements sound output. pub mod sound; diff --git a/agb/src/serial_link/mod.rs b/agb/src/serial_link/mod.rs new file mode 100644 index 00000000..ce1540a3 --- /dev/null +++ b/agb/src/serial_link/mod.rs @@ -0,0 +1,190 @@ +use core::ops::Deref; + +use crate::memory_mapped::MemoryMapped; + +const SIODATA8: MemoryMapped = unsafe { MemoryMapped::new(0x0400_012A) }; +const SIOCNT: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0128) }; +const RCNT: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0134) }; + +pub enum LinkPortError { + GbaErrorBit, + Blocked, +} + +pub struct LinkPortUart; + +impl LinkPortUart { + pub fn init(rate: BaudRate) -> Self { + RCNT.set(0x0); + SIOCNT.set(SioControlReg::default_uart().with_baud(rate).into()); + Self + } + + pub fn read(&mut self) -> Result { + match SioControlReg::from(SIOCNT.get()) { + v if *v.error => Err(LinkPortError::GbaErrorBit), + v if *v.recv_empty => Err(LinkPortError::Blocked), + _ => Ok((SIODATA8.get() & 0xFF) as u8), + } + } + + pub fn write(&mut self, data: u8) -> Result<(), LinkPortError> { + match SioControlReg::from(SIOCNT.get()) { + v if *v.error => Err(LinkPortError::GbaErrorBit), + v if *v.send_full => Err(LinkPortError::Blocked), + _ => { + SIODATA8.set(data as u16); + Ok(()) + } + } + } +} + +pub enum BaudRate { + B9600 = 0b00, + B38400 = 0b01, + B57600 = 0b10, + B115200 = 0b11, +} + +impl From for BaudRate { + fn from(value: u16) -> Self { + match value { + 0b00 => Self::B9600, + 0b01 => Self::B38400, + 0b10 => Self::B57600, + 0b11 => Self::B115200, + _ => panic!("passed invalid value"), + } + } +} + +pub enum SioMode { + Normal8bit = 0b00, + Multiplayer = 0b01, + Normal32bit = 0b10, + Uart = 0b11, +} + +impl From for SioMode { + fn from(value: u16) -> Self { + match value { + 0b00 => Self::Normal8bit, + 0b01 => Self::Multiplayer, + 0b10 => Self::Normal32bit, + 0b11 => Self::Uart, + _ => panic!("passed invalid value"), + } + } +} + +struct SioControlReg { + baud_rate: BaudRate, // 0-1 + flow_control: BoolField, // 2 + parity_odd: BoolField, // 3 + send_full: BoolField, // 4 + recv_empty: BoolField, // 5 + error: BoolField, // 6 + data_8bit: BoolField, // 7 + fifo_enabled: BoolField, // 8 + parity_enabled: BoolField, // 9 + tx_enabled: BoolField, // 10 + rx_enabled: BoolField, // 11 + mode: SioMode, // 12-13 + irq_enable: BoolField, // 14 +} + +impl SioControlReg { + fn default_uart() -> Self { + Self { + baud_rate: BaudRate::B9600, + flow_control: BoolField(false), + parity_odd: BoolField(false), + send_full: BoolField(false), + recv_empty: BoolField(false), + error: BoolField(false), + data_8bit: BoolField(true), + fifo_enabled: BoolField(true), + parity_enabled: BoolField(false), + tx_enabled: BoolField(true), + rx_enabled: BoolField(true), + mode: SioMode::Uart, + irq_enable: BoolField(false), + } + } + + fn with_baud(mut self, rate: BaudRate) -> Self { + self.baud_rate = rate; + self + } +} + +impl From for u16 { + fn from(value: SioControlReg) -> Self { + value.baud_rate as u16 + | u16::from(value.flow_control) << 2 + | u16::from(value.parity_odd) << 3 + | u16::from(value.send_full) << 4 + | u16::from(value.recv_empty) << 5 + | u16::from(value.error) << 6 + | u16::from(value.data_8bit) << 7 + | u16::from(value.fifo_enabled) << 8 + | u16::from(value.parity_enabled) << 9 + | u16::from(value.tx_enabled) << 10 + | u16::from(value.rx_enabled) << 11 + | (value.mode as u16) << 12 + | u16::from(value.irq_enable) << 14 + } +} + +impl From for SioControlReg { + fn from(value: u16) -> Self { + Self { + baud_rate: BaudRate::from(value & 0b11), + flow_control: (value & (1 << 2)).into(), + parity_odd: (value & (1 << 3)).into(), + send_full: (value & (1 << 4)).into(), + recv_empty: (value & (1 << 5)).into(), + error: (value & (1 << 6)).into(), + data_8bit: (value & (1 << 7)).into(), + fifo_enabled: (value & (1 << 8)).into(), + parity_enabled: (value & (1 << 9)).into(), + tx_enabled: (value & (1 << 10)).into(), + rx_enabled: (value & (1 << 11)).into(), + mode: ((value & (0b11 << 12)) >> 12).into(), + irq_enable: (value & (1 << 14)).into(), + } + } +} + +pub struct BoolField(bool); + +impl Deref for BoolField { + type Target = bool; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for u16 { + fn from(value: BoolField) -> Self { + if *value { + 1 + } else { + 0 + } + } +} + +impl From for BoolField { + fn from(value: u16) -> Self { + Self(value != 0) + } +} + +impl From for BoolField { + fn from(value: bool) -> Self { + Self(value) + } +}