From 591372c7413c0579ef81db913168f798e0d60e05 Mon Sep 17 00:00:00 2001 From: ParrotXray Date: Mon, 5 May 2025 20:06:18 +0800 Subject: [PATCH] feat: Support basic tty output --- src/hal/io.rs | 28 +++--- src/kernel/kernel.rs | 18 +++- src/kernel/mod.rs | 1 + src/kernel/tty/mod.rs | 17 +++- src/kernel/tty/tty.rs | 203 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 252 insertions(+), 15 deletions(-) diff --git a/src/hal/io.rs b/src/hal/io.rs index 2901d1c..6b5b300 100644 --- a/src/hal/io.rs +++ b/src/hal/io.rs @@ -13,16 +13,17 @@ use core::arch::asm; /// * `port` - 端口號 /// * `value` - 要寫入的值 #[inline] -pub fn io_port_wb(port: u8, value: u8) { +pub fn io_port_wb(port: u16, value: u8) { unsafe { asm!( "movb {0}, %al", "movw {1:x}, %dx", "outb %al, %dx", in(reg_byte) value, - in(reg) port as u16, + in(reg) port, out("al") _, - out("dx") _ + out("dx") _, + options(att_syntax) ); } } @@ -33,16 +34,17 @@ pub fn io_port_wb(port: u8, value: u8) { /// * `port` - 端口號 /// * `value` - 要寫入的值 #[inline] -pub fn io_port_wl(port: u8, value: u32) { +pub fn io_port_wl(port: u16, value: u32) { unsafe { asm!( "movl {0}, %eax", "movw {1:x}, %dx", "outl %eax, %dx", in(reg) value, - in(reg) port as u16, + in(reg) port, out("eax") _, - out("dx") _ + out("dx") _, + options(att_syntax) ); } } @@ -54,7 +56,7 @@ pub fn io_port_wl(port: u8, value: u32) { /// # 返回 /// 讀取到的值 #[inline] -pub fn io_port_rb(port: u8) -> u8 { +pub fn io_port_rb(port: u16) -> u8 { let value: u8; unsafe { asm!( @@ -62,9 +64,10 @@ pub fn io_port_rb(port: u8) -> u8 { "inb %dx, %al", "movb %al, {0}", out(reg_byte) value, - in(reg) port as u16, + in(reg) port, out("al") _, - out("dx") _ + out("dx") _, + options(att_syntax) ); } value @@ -77,7 +80,7 @@ pub fn io_port_rb(port: u8) -> u8 { /// # 返回 /// 讀取到的值 #[inline] -pub fn io_port_rl(port: u8) -> u32 { +pub fn io_port_rl(port: u16) -> u32 { let value: u32; unsafe { asm!( @@ -85,9 +88,10 @@ pub fn io_port_rl(port: u8) -> u32 { "inl %dx, %eax", "movl %eax, {0}", out(reg) value, - in(reg) port as u16, + in(reg) port, out("eax") _, - out("dx") _ + out("dx") _, + options(att_syntax) ); } value diff --git a/src/kernel/kernel.rs b/src/kernel/kernel.rs index 98635c6..dda88dd 100644 --- a/src/kernel/kernel.rs +++ b/src/kernel/kernel.rs @@ -1,5 +1,6 @@ // src/kernel/kernel.rs use core::arch::asm; +use crate::kernel::tty::tty; // boot.S 調用的入口點 #[no_mangle] @@ -7,11 +8,24 @@ pub extern "C" fn _kernel_init() { // TODO: 加載 GDT // TODO: 加載 IDT // TODO: 啟用分頁 + tty::tty_init(tty::VGA_BUFFER_PADDR); + tty::tty_set_theme(tty::VGA_COLOR_WHITE, tty::VGA_COLOR_BLACK); } #[no_mangle] -pub extern "C" fn _kernel_main(_multiboot_info: u32) { - +pub extern "C" fn _kernel_post_init() { + +} + +#[no_mangle] +pub extern "C" fn _kernel_main() { + + tty::tty_put_str("Loading....!\n"); + + // tty::tty_clear(); + + tty::tty_put_str("Welcome to Cure OS!\n"); + unsafe { loop { asm!("hlt"); diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 9c7f721..8264245 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1,4 +1,5 @@ pub mod kernel; +pub mod tty; // 重新導出由 boot.S 調用的入口點 // pub use kernel::{_kernel_init, _kernel_main}; \ No newline at end of file diff --git a/src/kernel/tty/mod.rs b/src/kernel/tty/mod.rs index 4a77f74..67b2091 100644 --- a/src/kernel/tty/mod.rs +++ b/src/kernel/tty/mod.rs @@ -1 +1,16 @@ -pub mod tty; \ No newline at end of file +// src/kernel/tty/mod.rs + +pub mod tty; + +// 重新導出 TTY 公共函數 +pub use tty::tty_init; +pub use tty::tty_set_buffer; +pub use tty::tty_set_theme; +pub use tty::tty_put_char; +pub use tty::tty_put_str; +pub use tty::tty_scroll_up; +pub use tty::tty_clear; +pub use tty::tty_clear_line; +pub use tty::tty_set_cpos; +pub use tty::tty_get_cpos; +pub use tty::tty_get_theme; \ No newline at end of file diff --git a/src/kernel/tty/tty.rs b/src/kernel/tty/tty.rs index e69de29..7e95c9b 100644 --- a/src/kernel/tty/tty.rs +++ b/src/kernel/tty/tty.rs @@ -0,0 +1,203 @@ +// src/kernel/tty/tty.rs + +use crate::hal::io::{io_port_wb}; + +/// VGA 屬性類型 (16位) +pub type VgaAttribute = u16; + +// VGA 顏色常量 +pub const VGA_COLOR_BLACK: u8 = 0; +pub const VGA_COLOR_BLUE: u8 = 1; +pub const VGA_COLOR_GREEN: u8 = 2; +pub const VGA_COLOR_CYAN: u8 = 3; +pub const VGA_COLOR_RED: u8 = 4; +pub const VGA_COLOR_MAGENTA: u8 = 5; +pub const VGA_COLOR_BROWN: u8 = 6; +pub const VGA_COLOR_LIGHT_GREY: u8 = 7; +pub const VGA_COLOR_DARK_GREY: u8 = 8; +pub const VGA_COLOR_LIGHT_BLUE: u8 = 9; +pub const VGA_COLOR_LIGHT_GREEN: u8 = 10; +pub const VGA_COLOR_LIGHT_CYAN: u8 = 11; +pub const VGA_COLOR_LIGHT_RED: u8 = 12; +pub const VGA_COLOR_LIGHT_MAGENTA: u8 = 13; +pub const VGA_COLOR_LIGHT_BROWN: u8 = 14; +pub const VGA_COLOR_WHITE: u8 = 15; +pub const VGA_BUFFER_PADDR: usize = 0xB8000; + +const TTY_WIDTH: usize = 80; +const TTY_HEIGHT: usize = 25; +const VGA_CTRL_PORT: u16 = 0x03D4; +const VGA_DATA_PORT: u16 = 0x03D5; + +// 全局 TTY 狀態 +pub struct TTYState { + vga_buffer: *mut VgaAttribute, + theme_color: VgaAttribute, + x: usize, + y: usize, +} + +impl TTYState { + const fn new() -> Self { + Self { + vga_buffer: VGA_BUFFER_PADDR as *mut VgaAttribute, + theme_color: (VGA_COLOR_BLACK as VgaAttribute) << 8, + x: 0, + y: 0, + } + } +} + +static mut TTY_STATE: TTYState = TTYState::new(); + +fn update_cursor() { + unsafe { + let pos = TTY_STATE.y * TTY_WIDTH + TTY_STATE.x; + + io_port_wb(VGA_CTRL_PORT, 0x0F); + io_port_wb(VGA_DATA_PORT, (pos & 0xFF) as u8); + io_port_wb(VGA_CTRL_PORT, 0x0E); + io_port_wb(VGA_DATA_PORT, ((pos >> 8) & 0xFF) as u8); + } +} + +/// 初始化 TTY +// vga_buf: *mut u8 +pub fn tty_init(vga_buf: usize) { + unsafe { + TTY_STATE.vga_buffer = vga_buf as *mut VgaAttribute; + tty_clear(); + } +} + +/// 設置 VGA 緩衝區 +pub fn tty_set_buffer(vga_buf: usize) { + unsafe { + TTY_STATE.vga_buffer = vga_buf as *mut VgaAttribute; + } +} + +/// 設置主題顏色(前景色和背景色) +pub fn tty_set_theme(fg: u8, bg: u8) { + unsafe { + TTY_STATE.theme_color = ((bg << 4 | fg) as VgaAttribute) << 8; + } +} + +/// 向 TTY 輸出單個字符 +/// +/// # 注意 +/// - 僅支援 ASCII 範圍 (0-255) 的字符 +/// - 多字節 Unicode 字符會被截斷為低 8 位 +/// - '\t' 擴展為 4 個空格 +/// - '\n' 換行並將游標移至行首 +/// - '\r' 將游標移至行首 +pub fn tty_put_char(chr: char) { + unsafe { + match chr { + '\t' => { + TTY_STATE.x += 4; + } + '\n' => { + TTY_STATE.y += 1; + TTY_STATE.x = 0; + } + '\r' => { + TTY_STATE.x = 0; + } + _ => { + let offset = TTY_STATE.x + TTY_STATE.y * TTY_WIDTH; + // VGA 文本模式僅支持 8 位字符 + *TTY_STATE.vga_buffer.add(offset) = TTY_STATE.theme_color | (chr as u8) as VgaAttribute; + TTY_STATE.x += 1; + } + } + + if TTY_STATE.x >= TTY_WIDTH { + TTY_STATE.x = 0; + TTY_STATE.y += 1; + } + + if TTY_STATE.y >= TTY_HEIGHT { + tty_scroll_up(); + } + + update_cursor(); + } +} + +/// 向 TTY 輸出字符串 +/// +/// # 注意 +/// - 字符串中的多字節 Unicode 字符會被截斷為低 8 位 +/// - 支持 Rust 字符串切片 (&str) +pub fn tty_put_str(s: &str) { + for chr in s.chars() { + tty_put_char(chr); + } +} + +/// 向上滾動一行 +pub fn tty_scroll_up() { + unsafe { + let last_line = TTY_WIDTH * (TTY_HEIGHT - 1); + + // 將所有行向上移動一行 + core::ptr::copy( + TTY_STATE.vga_buffer.add(TTY_WIDTH), + TTY_STATE.vga_buffer, + last_line + ); + + // 清空最後一行 + for i in 0..TTY_WIDTH { + *TTY_STATE.vga_buffer.add(i + last_line) = TTY_STATE.theme_color; + } + + TTY_STATE.y = if TTY_STATE.y == 0 { 0 } else { TTY_HEIGHT - 1 }; + } +} + +/// 清空屏幕 +pub fn tty_clear() { + unsafe { + for i in 0..(TTY_WIDTH * TTY_HEIGHT) { + *TTY_STATE.vga_buffer.add(i) = TTY_STATE.theme_color; + } + TTY_STATE.x = 0; + TTY_STATE.y = 0; + update_cursor(); + } +} + +/// 清空指定行 +pub fn tty_clear_line(y: usize) { + unsafe { + for i in 0..TTY_WIDTH { + *TTY_STATE.vga_buffer.add(i + y * TTY_WIDTH) = TTY_STATE.theme_color; + } + } +} + +/// 設置游標位置 +pub fn tty_set_cpos(x: usize, y: usize) { + unsafe { + TTY_STATE.x = x % TTY_WIDTH; + TTY_STATE.y = y % TTY_HEIGHT; + update_cursor(); + } +} + +/// 獲取游標位置 +pub fn tty_get_cpos() -> (usize, usize) { + unsafe { + (TTY_STATE.x, TTY_STATE.y) + } +} + +/// 獲取當前主題顏色 +pub fn tty_get_theme() -> VgaAttribute { + unsafe { + TTY_STATE.theme_color + } +}