summaryrefslogtreecommitdiffstats
path: root/src/serial.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-07-31 02:18:30 +0200
committersalmonik <salmonik@tuta.io>2026-07-31 02:18:30 +0200
commit76103215711d268b4b02f62e900f2be3032cf295 (patch)
treedcd86b5b02ae99c99ca833668055f4f53211be6b /src/serial.rs
downloadrs-dos-76103215711d268b4b02f62e900f2be3032cf295.tar.gz
rs-dos-76103215711d268b4b02f62e900f2be3032cf295.zip
Initial commit: raisernics toy kernel
- Bare-metal x86_64 UEFI kernel (PE32+, no libc) - GOP framebuffer + 8x16 bitmap font - PS/2 keyboard (polling, scancode set 1) - UART 16550 serial driver (COM1, 115200 8N1) - Bump allocator as #[global_allocator] (alloc::String, Vec, format!) - UEFI memory map capture (static buffer, ADR 0001) - Shell with 7 commands (help, echo, clear, hello, info, mem, halt) - Dual output: GOP framebuffer + serial console - Makefile: run, run-gui, debug, release, image targets - tools/: qemu.sh, boot-image.sh, debug-kernel.py
Diffstat (limited to '')
-rw-r--r--src/serial.rs118
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)
2use core::fmt;
3
4const COM1: u16 = 0x3F8;
5
6const DATA_REG: u16 = COM1;
7const INTR_ENABLE: u16 = COM1 + 1;
8const FIFO_CONTROL: u16 = COM1 + 2;
9const LINE_CONTROL: u16 = COM1 + 3;
10const MODEM_CONTROL: u16 = COM1 + 4;
11const LINE_STATUS: u16 = COM1 + 5;
12
13const LSR_TX_EMPTY: u8 = 0x20;
14const LSR_DATA_READY: u8 = 0x01;
15
16/// Initialise the UART with 115200 baud, 8N1.
17pub 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).
36pub 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).
45pub 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.
53pub 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.
64pub 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// ---------------------------------------------------------------------------
76pub struct SerialWriter;
77
78impl 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]
87macro_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]
95macro_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// ---------------------------------------------------------------------------
106unsafe 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
114unsafe 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}