VGA Text Mode
This commit is contained in:
parent
a5c98f5732
commit
aab190baea
@ -4,3 +4,6 @@ target = "x86_64-anos.json"
|
||||
[unstable]
|
||||
build-std-features = ["compiler-builtins-mem"]
|
||||
build-std = ["core", "compiler_builtins"]
|
||||
|
||||
[target.'cfg(target_os = "none")']
|
||||
runner = "bootimage runner"
|
||||
|
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -7,6 +7,9 @@ name = "anos"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bootloader",
|
||||
"lazy_static",
|
||||
"spin",
|
||||
"volatile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -14,3 +17,24 @@ name = "bootloader"
|
||||
version = "0.9.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c452074efc3c0bfb241fb7bc87df04741c7c85e926f6a07c05f8fbd6008240"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "volatile"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945"
|
||||
|
@ -12,3 +12,9 @@ panic = "abort" # 禁用 panic 时的栈展开
|
||||
|
||||
[dependencies]
|
||||
bootloader = "0.9.8"
|
||||
volatile = "0.2.6"
|
||||
spin = "0.5.2"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
features = ["spin_no_std"]
|
||||
|
4
setup.sh
Normal file
4
setup.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
rustup component list --installed
|
||||
cargo install bootimage
|
16
src/main.rs
16
src/main.rs
@ -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
170
src/vga_buffer.rs
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user