Format
OS in Rust
Recall
- We wrote
str_to_vga. - We handled a bunch of corner cases.
- We had an all-around great time.
Today
- We recall our two main source code files.
- We anticipate our configuration files to be unaltered.
Main
VGA
src/vga.rs
static mut LATEST: usize = 0;
const MMIO: *mut u8 = 0xb8000 as *mut u8;
const COLOR: u8 = 0xF;
fn char_to_vga(a: u8) {
unsafe {
let rel: *mut u8 = ((MMIO as usize) + (LATEST * 2)) as *mut u8;
*rel = a;
*((rel as usize + 1) as *mut u8) = COLOR;
LATEST = LATEST + 1;
}
}
const ROWS: usize = 80;
const COLS: usize = 25;
const MAX: usize = ROWS * COLS;
fn scroll() {
unsafe {
for i in 80..MAX {
let src: *mut u8 = ((MMIO as usize) + (i * 2)) as *mut u8;
let dst: *mut u8 = ((MMIO as usize) + ((i - 80) * 2)) as *mut u8;
*dst = *src;
*((dst as usize + 1) as *mut u8) = COLOR;
}
for i in (MAX-80)..MAX {
let dst: *mut u8 = ((MMIO as usize) + ((i) * 2)) as *mut u8;
*dst = 32;
*((dst as usize + 1) as *mut u8) = COLOR;
}
LATEST = LATEST - 80;
}
}
pub fn str_to_vga(s: &str) {
let v = s.as_bytes();
unsafe {
for i in 0..v.len() {
if LATEST > MAX {
scroll();
}
match v[i] {
10 => LATEST = ((LATEST / 80) + 1) * 80,
_ => char_to_vga(v[i]),
}
}
}
}Format
Formatting Macros
- It would be nice to support Rust’s formatting macros, too.
- We will also discover that Rust is, in point of fact, an object-oriented language.
- Read: bad.
- I was extremely annoyed by how to get this working, but I did it.
Functionality
- We need to implement the [
core::fmt::Write] trait.- I had foolishly assumed we could just use
format! - That would tragically be too reasonable.
- (It also would raise some unanswered questions about memory but whatever).
- I had foolishly assumed we could just use
- The only required method of this trait is
write_str:
Sketch
- Basically we need to write this:
src/vga.rs
- The
Ok(())is just aOkResult containing the()“unit type”.
Workaround
- I already can write text!
Dummy struct
- I just make an arbitrary structure.
src/vga.rs
- Presumably it is clear why someone would find this an annoying way to do things.
This works
- I’m not kidding it actually does.
- I was surprised too.
- I did forget to capitalized the W in write but…
src/main.rs
- It throws an uninteresting warning.
- Make sure you know how to fix and why it happens.
A println Macro
- We can add a
printlnmacro. - Rust’s macro syntax is a bit strange.
- So we won’t try to write a macro from scratch.
- Instead, we look at the source of
println!in the standard library:
Rules
- Macros are defined through one or more rules, similar to
matcharms. - The
printlnmacro has two rules:- The first rule is for invocations without arguments, e.g.,
println!(), which is expanded toprint!("\n")and thus just prints a newline. - The second rule is for invocations with parameters such as
println!("Hello")orprintln!("Number: {}", 4).- It is also expanded to an invocation of the
print!macro. - It passes all arguments and an additional newline
\nat the end.
- It is also expanded to an invocation of the
- The first rule is for invocations without arguments, e.g.,
- The
#[macro_export]attribute makes the macro available to the whole crate. - It also places the macro at the crate root.
- Import the macro through
use std::printlninstead ofstd::macros::println.
- Import the macro through
std/macros.rs
The macro expands to a call of the
_printfunction in theiomodule.The
$cratevariable ensures that the macro also works from outside thestdcrate by expanding tostdwhen it’s used in other crates.The
format_argsmacro builds afmt::Argumentstype from the passed arguments, which is passed to_print.The
_printfunction callsprint_to, which is rather complicated because it supports differentStdoutdevices.- We don’t need that complexity since we just want to print to the VGA buffer.
To print to the VGA buffer, we just copy the
println!andprint!macros, but modify them to use our own_printfunction:
src/vga.rs
- Here’s mine:
Hello World using println
Now we can use println in our _start function:
src/main.rs
- We don’t have to import the macro in the main function, because it already lives in the root namespace.
Printing Panic Messages
Now that we have a println macro, we can use it in our panic function to print the panic message and the location of the panic.
- Using your boundless intellect and and unbreakable work ethic, implement a panic handler that prints out some relevant information.
- It is easy enough to test.