anos/src/vga_buffer.rs

193 lines
5.9 KiB
Rust
Raw Normal View History

2021-12-09 02:34:40 +00:00
// 用 volatile 以防止我们针对内存的操作被优化掉
// 因为我们是通过对内存地址 0xb8000 写入来操作 VGA Buffer 的
// 这实际上是通过内存操作的副作用来实现的
// 但由于我们从未访问过这段内存,所以它可能会在未来的 Rust 编译器中被优化掉
// 为了告诉编译器,这部分针对内存的操作是我们故意的,就需要用到 volatile crate
use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
2022-01-13 04:11:03 +00:00
use volatile::Volatile;
2021-12-09 02:34:40 +00:00
#[allow(dead_code)] // 允许未使用的代码
2022-01-13 04:11:03 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)] // 启用 copy 语义
#[repr(u8)] // 每个 enum variant 都以 u8 存储
2021-12-09 02:34:40 +00:00
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)]
2022-01-13 04:11:03 +00:00
#[repr(transparent)] // 为了保证 ColorCode 和 u8 有同样的数据布局
2021-12-09 02:34:40 +00:00
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)]
2022-01-13 04:11:03 +00:00
#[repr(C)] // 使数据结构的布局和 C 一样,这样才能保证每个成员的顺序一致
2021-12-09 02:34:40 +00:00
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 {
2022-01-13 04:11:03 +00:00
column_position: usize, // 当前光标所在列
color_code: ColorCode, // 当前颜色代码
buffer: &'static mut Buffer, // VGA Buffer 的引用,生命周期是 'static
2021-12-09 02:34:40 +00:00
}
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();
}
2022-01-13 04:11:03 +00:00
let row = BUFFER_HEIGHT - 1;
2021-12-09 02:34:40 +00:00
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();
2022-01-13 04:11:03 +00:00
self.buffer.chars[row - 1][col].write(character);
2021-12-09 02:34:40 +00:00
}
}
// 清空最下面的一行
2022-01-13 04:11:03 +00:00
self.clear_row(BUFFER_HEIGHT - 1);
2021-12-09 02:34:40 +00:00
// 将当前列的位置设为 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(())
}
}
2021-12-09 11:51:38 +00:00
// 将 Writer 设置为静态以便调用
lazy_static! { // 内置的 static 会用到标准库里的一些东西,所以这里用 lazy_static
pub static ref WRITER: Mutex<Writer> = Mutex::new(Writer { // 用互斥锁来防止冲突
2021-12-09 02:34:40 +00:00
column_position: 0,
color_code: ColorCode::new(Color::Yellow, Color::Black),
buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }
});
}
// 将宏导出到 crate 的根,这样我们就可以用
// use crate::println
// 来使用它
#[macro_export]
2021-12-09 11:51:38 +00:00
macro_rules! print { // print!()
2021-12-09 02:34:40 +00:00
($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
}
#[macro_export]
2021-12-09 11:51:38 +00:00
macro_rules! println { // println!()
2021-12-09 02:34:40 +00:00
() => ($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;
2022-01-11 09:09:13 +00:00
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
WRITER.lock().write_fmt(args).unwrap();
});
2021-12-09 02:34:40 +00:00
}
2021-12-09 11:51:38 +00:00
#[test_case]
2022-01-13 04:11:03 +00:00
fn test_println_simple() {
// 测试单行 println!
2021-12-09 11:51:38 +00:00
println!("test_println_simple output");
}
#[test_case]
2022-01-13 04:11:03 +00:00
fn test_println_many() {
// 测试多行 println!
for _ in 0..10 {
2021-12-09 11:51:38 +00:00
println!("test_println_many output");
}
}
#[test_case]
2022-01-13 04:11:03 +00:00
fn test_println_output() {
// 测试字符是否真的打印到了屏幕上
2022-01-11 09:09:13 +00:00
use core::fmt::Write;
use x86_64::instructions::interrupts;
2021-12-09 11:51:38 +00:00
let s = "Some test string that fits on a single line";
2022-01-11 09:09:13 +00:00
interrupts::without_interrupts(|| {
let mut writer = WRITER.lock();
writeln!(writer, "\n{}", s).expect("writeln failed");
for (i, c) in s.chars().enumerate() {
let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
assert_eq!(char::from(screen_char.ascii_character), c);
}
});
2021-12-09 11:51:38 +00:00
}