VGA Text Mode

This commit is contained in:
2021-12-09 10:34:40 +08:00
parent a5c98f5732
commit aab190baea
6 changed files with 212 additions and 11 deletions

View File

@@ -3,6 +3,8 @@
use core::panic::PanicInfo;
mod vga_buffer;
// 定义一个 byte string 类型的静态变量
static HELLO: &[u8] = b"Hello World!";
@@ -11,16 +13,7 @@ static HELLO: &[u8] = b"Hello World!";
// 函数名为 _start 是因为大多数系统默认用这个名字作为入口点名称
// -> ! 代表这是一个发散函数,不允许有任何返回值,因为它不会被任何函数调用,而是将直接被 bootloader 调用
pub extern "C" fn _start() -> ! {
// VGA Buffer 从 0xb8000 开始
let vga_buffer = 0xb8000 as *mut u8;
// 迭代 HELLO, 通过裸指针 vga_buffer 来定位待写入的缓冲区地址
for (i, &byte) in HELLO.iter().enumerate() {
unsafe {
*vga_buffer.offset(i as isize * 2) = byte; // ASCII 码字节
*vga_buffer.offset(i as isize * 2 + 1) = 0xb; // 颜色字节, 0xb 代表青色
}
}
println!("Hello World{}", "!");
loop {}
}
@@ -28,6 +21,7 @@ pub extern "C" fn _start() -> ! {
#[panic_handler]
// panic! 时将会进行栈展开,执行各种 drop 函数来回收垃圾
// 禁用标准库之后,这些东西就都没有了,我们需要把它重写一遍
fn panic(_info: &PanicInfo) -> ! {
fn panic(info: &PanicInfo) -> ! {
println!("{}", info);
loop {}
}

170
src/vga_buffer.rs Normal file
View File

@@ -0,0 +1,170 @@
// 用 volatile 以防止我们针对内存的操作被优化掉
// 因为我们是通过对内存地址 0xb8000 写入来操作 VGA Buffer 的
// 这实际上是通过内存操作的副作用来实现的
// 但由于我们从未访问过这段内存,所以它可能会在未来的 Rust 编译器中被优化掉
// 为了告诉编译器,这部分针对内存的操作是我们故意的,就需要用到 volatile crate
use volatile::Volatile;
use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
#[allow(dead_code)] // 允许未使用的代码
#[derive(Debug, Clone, Copy, PartialEq, Eq)] // 启用 copy 语义
#[repr(u8)] // 每个 enum variant 都以 u8 存储
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)] // 为了保证 ColorCode 和 u8 有同样的数据布局
struct ColorCode(u8);
impl ColorCode {
fn new(foreground: Color, background: Color) -> ColorCode {
ColorCode((background as u8) << 4 | (foreground as u8))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)] // 使数据结构的布局和 C 一样,这样才能保证每个成员的顺序一致
struct ScreenChar {
ascii_character: u8,
color_code: ColorCode,
}
const BUFFER_HEIGHT: usize = 25;
const BUFFER_WIDTH: usize = 80;
#[repr(transparent)]
struct Buffer {
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
pub struct Writer {
column_position: usize, // 当前光标所在列
color_code: ColorCode, // 当前颜色代码
buffer: &'static mut Buffer, // VGA Buffer 的引用,生命周期是 'static
}
impl Writer {
pub fn write_byte(&mut self, byte: u8) {
match byte {
// 如果碰到 '\n' 则创建新行
b'\n' => self.new_line(),
byte => {
// 如果列数超出限定的阈值,则创建新行
if self.column_position >= BUFFER_WIDTH {
self.new_line();
}
let row = BUFFER_HEIGHT -1;
let col = self.column_position;
let color_code = self.color_code;
// 将 ScreenChar 写入到 Buffer 的相应位置
self.buffer.chars[row][col].write(ScreenChar {
ascii_character: byte,
color_code,
});
self.column_position += 1;
}
}
}
fn new_line(&mut self) {
// 将所有字符上移一行
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let character = self.buffer.chars[row][col].read();
self.buffer.chars[row -1][col].write(character);
}
}
// 清空最下面的一行
self.clear_row(BUFFER_HEIGHT -1);
// 将当前列的位置设为 0
self.column_position = 0;
}
fn clear_row(&mut self, row: usize) {
let blank = ScreenChar {
ascii_character: b' ',
color_code: self.color_code,
};
for col in 0..BUFFER_WIDTH {
self.buffer.chars[row][col].write(blank);
}
}
pub fn write_string(&mut self, s: &str) {
for byte in s.bytes() {
// Rust 字符串是以 UTF-8 编码的,然而 VGA Buffer 只支持 ASCII字符所以有可能存在无法打印的字符
match byte {
// 当在 ASCII 字符范围内时,打印它
0x20..=0x7e | b'\n' => self.write_byte(byte),
// 否则打印 ■
_ => self.write_byte(0xfe),
}
}
}
}
impl fmt::Write for Writer {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write_string(s);
Ok(())
}
}
lazy_static! {
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer {
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }
});
}
pub fn print_something() {
use core::fmt::Write;
let mut writer = Writer {
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
// VGA Buffer 从 0xb8000 开始
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) },
};
writer.write_byte(b'H');
writer.write_string("ello ");
writer.write_string("Wörld!");
write!(writer, "The numbers are {} and {}", 42, 1.0/3.0).unwrap();
}
// 将宏导出到 crate 的根,这样我们就可以用
// use crate::println
// 来使用它
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
// 由于宏需要从外部调用这个函数,所以它必须是公开的
// 用 #[doc(hidden)] 来阻止它生成文档
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
WRITER.lock().write_fmt(args).unwrap();
}