VGA Text Mode
This commit is contained in:
parent
a5c98f5732
commit
aab190baea
@ -4,3 +4,6 @@ target = "x86_64-anos.json"
|
|||||||
[unstable]
|
[unstable]
|
||||||
build-std-features = ["compiler-builtins-mem"]
|
build-std-features = ["compiler-builtins-mem"]
|
||||||
build-std = ["core", "compiler_builtins"]
|
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"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bootloader",
|
"bootloader",
|
||||||
|
"lazy_static",
|
||||||
|
"spin",
|
||||||
|
"volatile",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -14,3 +17,24 @@ name = "bootloader"
|
|||||||
version = "0.9.19"
|
version = "0.9.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7c452074efc3c0bfb241fb7bc87df04741c7c85e926f6a07c05f8fbd6008240"
|
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]
|
[dependencies]
|
||||||
bootloader = "0.9.8"
|
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;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
mod vga_buffer;
|
||||||
|
|
||||||
// 定义一个 byte string 类型的静态变量
|
// 定义一个 byte string 类型的静态变量
|
||||||
static HELLO: &[u8] = b"Hello World!";
|
static HELLO: &[u8] = b"Hello World!";
|
||||||
|
|
||||||
@ -11,16 +13,7 @@ static HELLO: &[u8] = b"Hello World!";
|
|||||||
// 函数名为 _start 是因为大多数系统默认用这个名字作为入口点名称
|
// 函数名为 _start 是因为大多数系统默认用这个名字作为入口点名称
|
||||||
// -> ! 代表这是一个发散函数,不允许有任何返回值,因为它不会被任何函数调用,而是将直接被 bootloader 调用
|
// -> ! 代表这是一个发散函数,不允许有任何返回值,因为它不会被任何函数调用,而是将直接被 bootloader 调用
|
||||||
pub extern "C" fn _start() -> ! {
|
pub extern "C" fn _start() -> ! {
|
||||||
// VGA Buffer 从 0xb8000 开始
|
println!("Hello World{}", "!");
|
||||||
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 代表青色
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
@ -28,6 +21,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
// panic! 时将会进行栈展开,执行各种 drop 函数来回收垃圾
|
// panic! 时将会进行栈展开,执行各种 drop 函数来回收垃圾
|
||||||
// 禁用标准库之后,这些东西就都没有了,我们需要把它重写一遍
|
// 禁用标准库之后,这些东西就都没有了,我们需要把它重写一遍
|
||||||
fn panic(_info: &PanicInfo) -> ! {
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
println!("{}", info);
|
||||||
loop {}
|
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