feat: Support basic tty output
This commit is contained in:
parent
3177749dbe
commit
591372c741
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod kernel;
|
||||
pub mod tty;
|
||||
|
||||
// 重新導出由 boot.S 調用的入口點
|
||||
// pub use kernel::{_kernel_init, _kernel_main};
|
||||
@ -1 +1,16 @@
|
||||
pub mod tty;
|
||||
// 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;
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user