diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 9ffcbc00..1015bb5d 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -10,6 +10,8 @@ any(test, feature = "testing"), reexport_test_harness_main = "test_main" )] +#![allow(incomplete_features)] +#![feature(adt_const_params)] #![feature(alloc_error_handler)] #![feature(allocator_api)] #![feature(asm_const)] diff --git a/agb/src/serial_link/mod.rs b/agb/src/serial_link/mod.rs index 9775b2cf..bad7df38 100644 --- a/agb/src/serial_link/mod.rs +++ b/agb/src/serial_link/mod.rs @@ -1,9 +1,16 @@ +use self::uart::UartSioControl; +pub use self::{ + normal::{ClockSource, LinkPortNormal}, + uart::{BaudRate, LinkPortUart}, +}; +use crate::memory_mapped::MemoryMapped; use core::ops::{Deref, DerefMut}; -use embedded_hal::serial::{Read, Write}; - -use crate::{memory_mapped::MemoryMapped, println}; +mod normal; +mod uart; +// const SIODATA32LOW: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0120) }; +// const SIODATA32HIGH: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0122) }; const SIODATA8: MemoryMapped = unsafe { MemoryMapped::new(0x0400_012A) }; const SIOCNT: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0128) }; const RCNT: MemoryMapped = unsafe { MemoryMapped::new(0x0400_0134) }; @@ -13,76 +20,11 @@ pub enum LinkPortError { GbaErrorBit, } -pub struct LinkPortUart; - -impl LinkPortUart { - pub fn init(rate: BaudRate, with_interrupts: bool, clear_to_send: bool) -> Self { - println!("begin init"); - RCNT.set(0x0); - println!("have set rcnt"); - SIOCNT.set(0x0); - let reg: u16 = SioControlReg::default_uart() - .with_baud(rate) - .with_interrupts(with_interrupts) - .with_cts(clear_to_send) - .into(); - SIOCNT.set(reg); - println!("have set siocnt to {reg:#X}/{reg:#b}"); - Self - } -} - -impl Read for LinkPortUart { - type Error = LinkPortError; - - fn read(&mut self) -> Result> { - match SioControlReg::from(SIOCNT.get()) { - v if *v.error => Err(nb::Error::Other(LinkPortError::GbaErrorBit)), - v if *v.recv_empty => Err(nb::Error::WouldBlock), - _ => Ok((SIODATA8.get() & 0xFF) as u8), - } - } -} - -impl Write for LinkPortUart { - type Error = LinkPortError; - - fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { - match self.flush() { - Ok(_) => { - SIODATA8.set(word as u16); - Ok(()) - } - Err(e) => Err(e), - } - } - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - match SioControlReg::from(SIOCNT.get()) { - v if *v.error => Err(nb::Error::Other(LinkPortError::GbaErrorBit)), - v if *v.send_full => Err(nb::Error::WouldBlock), - _ => 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"), - } - } +#[allow(dead_code)] +enum SioControlReg { + Normal, + Multi, + Uart(UartSioControl), } pub enum SioMode { @@ -104,96 +46,6 @@ impl From for SioMode { } } -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), - 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 - } - - fn with_interrupts(mut self, interrupts: bool) -> Self { - *self.irq_enable = interrupts; - self - } - - fn with_cts(mut self, clear_to_send: bool) -> Self { - *self.flow_control = clear_to_send; - 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 // bit start - | 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 { @@ -222,7 +74,7 @@ impl From for u16 { impl From for BoolField { fn from(value: u16) -> Self { - Self(value != 0) + Self((value % 2) != 0) } } diff --git a/agb/src/serial_link/normal.rs b/agb/src/serial_link/normal.rs new file mode 100644 index 00000000..46bb7d8c --- /dev/null +++ b/agb/src/serial_link/normal.rs @@ -0,0 +1,148 @@ +use crate::println; + +use super::{BoolField, RCNT, SIOCNT}; + +pub struct LinkPortNormal; + +impl LinkPortNormal { + pub fn init(clock_source: ClockSource) -> Self { + println!("begin uart init"); + RCNT.set(0x0); + println!("have set rcnt"); + SIOCNT.set(0x0); + let reg: u16 = NormalSioControl::default() + .with_clock_source(clock_source) + .with_transfer_length(S) + .into(); + SIOCNT.set(reg); + println!("have set siocnt to {reg:#X}/{reg:#b}"); + Self + } +} + +pub(super) struct NormalSioControl { + clock_source: ClockSource, // 0 + clock_rate: InternalClock, // 1 + si_state: BoolField, // 2 + so_inactive: BoolField, // 3 + // 4-6 empty + start_bit: StartBit, // 7 + // 8-11 empty + transfer_length: TransferLength, // 12 + // 13 must be 0 + irq_enable: BoolField, // 14 +} + +impl NormalSioControl { + fn with_clock_source(mut self, source: ClockSource) -> Self { + self.clock_source = source; + self + } + + fn with_transfer_length(mut self, length: TransferLength) -> Self { + self.transfer_length = length; + self + } +} + +impl Default for NormalSioControl { + fn default() -> Self { + Self { + clock_source: ClockSource::Internal, + clock_rate: InternalClock::Khz256, + si_state: BoolField(false), + so_inactive: BoolField(false), + start_bit: StartBit::Inactive, + transfer_length: TransferLength::Bits32, + irq_enable: BoolField(false), + } + } +} + +impl From for u16 { + fn from(value: NormalSioControl) -> Self { + value.clock_source as u16 + | (value.clock_rate as u16) << 1 + | u16::from(value.si_state) << 2 + | u16::from(value.so_inactive) << 3 + | (value.start_bit as u16) << 7 + | (value.transfer_length as u16) << 12 + | u16::from(value.irq_enable) << 14 + } +} + +impl From for NormalSioControl { + fn from(value: u16) -> Self { + Self { + clock_source: (value & 1).into(), + clock_rate: (value & (1 << 1)).into(), + si_state: (value & (1 << 2)).into(), + so_inactive: (value & (1 << 3)).into(), + start_bit: (value & (1 << 7)).into(), + transfer_length: (value & (1 << 12)).into(), + irq_enable: (value & (1 << 14)).into(), + } + } +} + +pub enum ClockSource { + External = 0, + Internal = 1, +} + +impl From for ClockSource { + fn from(value: u16) -> Self { + match value % 2 { + 0 => ClockSource::External, + 1 => ClockSource::Internal, + _ => panic!(), + } + } +} + +pub enum InternalClock { + Khz256 = 0, + Mhz2 = 1, +} + +impl From for InternalClock { + fn from(value: u16) -> Self { + match value % 2 { + 0 => InternalClock::Khz256, + 1 => InternalClock::Mhz2, + _ => panic!(), + } + } +} + +// automatically reset when transfer complete +pub enum StartBit { + Inactive = 0, + Active = 1, +} + +impl From for StartBit { + fn from(value: u16) -> Self { + match value % 2 { + 0 => StartBit::Inactive, + 1 => StartBit::Active, + _ => panic!(), + } + } +} + +#[derive(PartialEq, Eq)] +pub enum TransferLength { + Bits8 = 0, + Bits32 = 1, +} + +impl From for TransferLength { + fn from(value: u16) -> Self { + match value % 2 { + 0 => TransferLength::Bits8, + 1 => TransferLength::Bits32, + _ => panic!(), + } + } +} diff --git a/agb/src/serial_link/uart.rs b/agb/src/serial_link/uart.rs new file mode 100644 index 00000000..644b1bb3 --- /dev/null +++ b/agb/src/serial_link/uart.rs @@ -0,0 +1,168 @@ +use embedded_hal::serial::{Read, Write}; + +use crate::println; + +use super::{BoolField, LinkPortError, SioMode, RCNT, SIOCNT, SIODATA8}; + +pub struct LinkPortUart; + +impl LinkPortUart { + pub fn init(rate: BaudRate, with_interrupts: bool, clear_to_send: bool) -> Self { + println!("begin uart init"); + RCNT.set(0x0); + println!("have set rcnt"); + SIOCNT.set(0x0); + let reg: u16 = UartSioControl::default() + .with_baud(rate) + .with_interrupts(with_interrupts) + .with_cts(clear_to_send) + .into(); + SIOCNT.set(reg); + println!("have set siocnt to {reg:#X}/{reg:#b}"); + Self + } +} + +impl Read for LinkPortUart { + type Error = LinkPortError; + + fn read(&mut self) -> Result> { + match UartSioControl::from(SIOCNT.get()) { + v if *v.error => Err(nb::Error::Other(LinkPortError::GbaErrorBit)), + v if *v.recv_empty => Err(nb::Error::WouldBlock), + _ => Ok((SIODATA8.get() & 0xFF) as u8), + } + } +} + +impl Write for LinkPortUart { + type Error = LinkPortError; + + fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> { + match self.flush() { + Ok(_) => { + SIODATA8.set(word as u16); + Ok(()) + } + Err(e) => Err(e), + } + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + match UartSioControl::from(SIOCNT.get()) { + v if *v.error => Err(nb::Error::Other(LinkPortError::GbaErrorBit)), + v if *v.send_full => Err(nb::Error::WouldBlock), + _ => Ok(()), + } + } +} + +pub(super) struct UartSioControl { + 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 Default for UartSioControl { + fn default() -> 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), + } + } +} + +impl UartSioControl { + fn with_baud(mut self, rate: BaudRate) -> Self { + self.baud_rate = rate; + self + } + + fn with_interrupts(mut self, interrupts: bool) -> Self { + *self.irq_enable = interrupts; + self + } + + fn with_cts(mut self, clear_to_send: bool) -> Self { + *self.flow_control = clear_to_send; + self + } +} + +impl From for u16 { + fn from(value: UartSioControl) -> 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 // bit start + | 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 UartSioControl { + 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 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"), + } + } +}