VGA Text Mode
Today I read another post by phil-opp about implementing VGA Text Mode. Thanks to the previous post, I created bare-metal Rust binary which can run on QEMU, and prints text via VGA. In this post I learned more about VGA and implemented safe interface and a bunch of macros which allow to print the text via VGA.
Vga
First, VGA is accessible via memory-mapped I/O. It means that we can use memory addresses, but the bytes are not written into RAM, but rather directly to the VGA hardware.
VGA on its own is pretty simple in terms of functionalities. We can write CP-437 set of characters to the buffer, control the foreground and background color and turn blinking of the character on.
Useful crates
In order to deliver safe interface for VGA Text Mode, global mutable static was
used. Because mutating global variable is unsafe in Rust, we can't really change
it without using unsafe
blocks. The reason this is unsafe
is that if multiple
threads will start modifying the global, then we'll end up with race condition
which Rust tries to prevent. The simplest solution to get rid of unsafe
is to
use Mutex
. The problem is that I'm implementing OS from scratch and at this
point it doesn't even have the concept of threads so the standard Rust's Mutex
won't work. There is, however, a crate called
spin which allows using spinlock technique to
create Mutex
. Spinlock simply uses busy waiting to repeatedly ask if it can
already use the mutex.
Another useful library I learned about today is
volatile. This crate allows wrapping almost
any type with Volatile
type. This using methods like write
or read
. What's
that for? In OS development scenario, when I'm writing to memory-mapped I/O like
VGA, the compiler has no idea about VGA and it's consequences. From the compiler
point of view, I'm just writing to some memory and I never read values back. It
may happen that compiler will catch that and will optimize this writes (why do we
need writes for if we don't read values back?). To instruct the compiler that this
writes are actually important, we can use Volatile
which basically tells the
compiler that the values in the memory may change in the meantime between two
accesses, even if that's not visible in the code — so simply don't do the
optimizations here. This technique is most often used during accessing
memory-mapped I/O and in multithreading environment.
There is more I learned today by reading this blog post, but putting all this info in TIL is too much. I'll end up writing blog post instead. I encourage you to read phil-opp blog on your own. It's great source of information to the point I supported Phil via GitHub sponsors. The content he creates is worth a lot more than I'm able to give, but it's something :).