diff options
Diffstat (limited to 'src/serial.rs')
| -rw-r--r-- | src/serial.rs | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..03fa570 --- /dev/null +++ b/src/serial.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | /// UART 16550 serial driver — port-mapped I/O on COM1 (0x3F8) | ||
| 2 | use core::fmt; | ||
| 3 | |||
| 4 | const COM1: u16 = 0x3F8; | ||
| 5 | |||
| 6 | const DATA_REG: u16 = COM1; | ||
| 7 | const INTR_ENABLE: u16 = COM1 + 1; | ||
| 8 | const FIFO_CONTROL: u16 = COM1 + 2; | ||
| 9 | const LINE_CONTROL: u16 = COM1 + 3; | ||
| 10 | const MODEM_CONTROL: u16 = COM1 + 4; | ||
| 11 | const LINE_STATUS: u16 = COM1 + 5; | ||
| 12 | |||
| 13 | const LSR_TX_EMPTY: u8 = 0x20; | ||
| 14 | const LSR_DATA_READY: u8 = 0x01; | ||
| 15 | |||
| 16 | /// Initialise the UART with 115200 baud, 8N1. | ||
| 17 | pub fn init() { | ||
| 18 | unsafe { | ||
| 19 | // Disable interrupts | ||
| 20 | port_out(INTR_ENABLE, 0x00); | ||
| 21 | // Set DLAB=1 (baud rate divisor) | ||
| 22 | port_out(LINE_CONTROL, 0x80); | ||
| 23 | // Divisor = 1 → 115200 baud (with 1.8432 MHz clock) | ||
| 24 | port_out(DATA_REG, 0x01); | ||
| 25 | port_out(INTR_ENABLE, 0x00); | ||
| 26 | // 8-bit, 1 stop, no parity, DLAB=0 | ||
| 27 | port_out(LINE_CONTROL, 0x03); | ||
| 28 | // Enable FIFO, clear, 14-byte threshold | ||
| 29 | port_out(FIFO_CONTROL, 0xC7); | ||
| 30 | // RTS/DSR set | ||
| 31 | port_out(MODEM_CONTROL, 0x0B); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Write a single byte to the serial port (blocking poll). | ||
| 36 | pub fn put_byte(b: u8) { | ||
| 37 | unsafe { | ||
| 38 | // Wait for transmitter holding register empty | ||
| 39 | while port_in(LINE_STATUS) & LSR_TX_EMPTY == 0 {} | ||
| 40 | port_out(DATA_REG, b); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Read a single byte from the serial port (blocking poll). | ||
| 45 | pub fn get_byte() -> u8 { | ||
| 46 | unsafe { | ||
| 47 | while port_in(LINE_STATUS) & LSR_DATA_READY == 0 {} | ||
| 48 | port_in(DATA_REG) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Non-blocking read: returns Some(byte) if data ready, None otherwise. | ||
| 53 | pub fn try_get_byte() -> Option<u8> { | ||
| 54 | unsafe { | ||
| 55 | if port_in(LINE_STATUS) & LSR_DATA_READY != 0 { | ||
| 56 | Some(port_in(DATA_REG)) | ||
| 57 | } else { | ||
| 58 | None | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Write a string to serial. | ||
| 64 | pub fn write_str(s: &str) { | ||
| 65 | for &b in s.as_bytes() { | ||
| 66 | if b == b'\n' { | ||
| 67 | put_byte(b'\r'); | ||
| 68 | } | ||
| 69 | put_byte(b); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | // --------------------------------------------------------------------------- | ||
| 74 | // fmt::Write implementation | ||
| 75 | // --------------------------------------------------------------------------- | ||
| 76 | pub struct SerialWriter; | ||
| 77 | |||
| 78 | impl fmt::Write for SerialWriter { | ||
| 79 | fn write_str(&mut self, s: &str) -> fmt::Result { | ||
| 80 | write_str(s); | ||
| 81 | Ok(()) | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Helper macro — prints to serial via format_args!. | ||
| 86 | #[macro_export] | ||
| 87 | macro_rules! serial_print { | ||
| 88 | ($($arg:tt)*) => {{ | ||
| 89 | use core::fmt::Write; | ||
| 90 | let _ = write!(&mut $crate::serial::SerialWriter, $($arg)*); | ||
| 91 | }}; | ||
| 92 | } | ||
| 93 | |||
| 94 | #[macro_export] | ||
| 95 | macro_rules! serial_println { | ||
| 96 | () => { $crate::serial::write_str("\r\n"); }; | ||
| 97 | ($($arg:tt)*) => {{ | ||
| 98 | $crate::serial_print!($($arg)*); | ||
| 99 | $crate::serial::write_str("\r\n"); | ||
| 100 | }}; | ||
| 101 | } | ||
| 102 | |||
| 103 | // --------------------------------------------------------------------------- | ||
| 104 | // Port I/O helpers | ||
| 105 | // --------------------------------------------------------------------------- | ||
| 106 | unsafe fn port_in(port: u16) -> u8 { | ||
| 107 | let val: u8; | ||
| 108 | unsafe { | ||
| 109 | core::arch::asm!("in al, dx", out("al") val, in("dx") port, options(nostack, preserves_flags)); | ||
| 110 | } | ||
| 111 | val | ||
| 112 | } | ||
| 113 | |||
| 114 | unsafe fn port_out(port: u16, val: u8) { | ||
| 115 | unsafe { | ||
| 116 | core::arch::asm!("out dx, al", in("al") val, in("dx") port, options(nostack, preserves_flags)); | ||
| 117 | } | ||
| 118 | } | ||
