mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-17 06:47:49 +00:00
Compare commits
4 Commits
ebfc7c5f7e
...
post-02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83e6c0bc00 | ||
|
|
2b11ad8397 | ||
|
|
fa51f3adbf | ||
|
|
94d14d8d62 |
25
.github/workflows/code.yml
vendored
25
.github/workflows/code.yml
vendored
@@ -67,31 +67,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: bootimage
|
command: bootimage
|
||||||
|
|
||||||
# install QEMU
|
|
||||||
- name: Install QEMU (Linux)
|
|
||||||
run: sudo apt update && sudo apt install qemu-system-x86
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
- name: Install QEMU (macOS)
|
|
||||||
run: brew install qemu
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
env:
|
|
||||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
|
||||||
HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: 1
|
|
||||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
|
||||||
- name: Install QEMU (Windows)
|
|
||||||
run: |
|
|
||||||
choco install qemu --version 2021.5.5
|
|
||||||
echo "$Env:Programfiles\qemu" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: pwsh
|
|
||||||
- name: "Print QEMU Version"
|
|
||||||
run: qemu-system-x86_64 --version
|
|
||||||
|
|
||||||
- name: Run `cargo test`
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
|
|
||||||
check_formatting:
|
check_formatting:
|
||||||
name: Check Formatting
|
name: Check Formatting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
69
Cargo.lock
generated
69
Cargo.lock
generated
@@ -2,80 +2,15 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit_field"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blog_os"
|
name = "blog_os"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bootloader",
|
"bootloader",
|
||||||
"lazy_static",
|
|
||||||
"spin",
|
|
||||||
"uart_16550",
|
|
||||||
"volatile 0.2.7",
|
|
||||||
"x86_64",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bootloader"
|
name = "bootloader"
|
||||||
version = "0.9.31"
|
version = "0.9.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "974e79cf1b0b737839f01330fb5393095daf1124d52693696494e32523ae9ef5"
|
checksum = "7bdfddac270bbdd45903296bc1caf29a7fdce6b326aaf0bbab7f04c5f98b7447"
|
||||||
|
|
||||||
[[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 = "uart_16550"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "503a6c0e6d82daa87985e662d120c0176b09587c92a68db22781b28ae95405dd"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"x86_64",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "volatile"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "volatile"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e4c2dbd44eb8b53973357e6e207e370f0c1059990df850aca1eca8947cf464f0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "x86_64"
|
|
||||||
version = "0.14.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb611915c917c6296d11e23f71ff1ecfe49c5766daba92cd3df52df6b58285b6"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field",
|
|
||||||
"bitflags",
|
|
||||||
"volatile 0.4.4",
|
|
||||||
]
|
|
||||||
|
|||||||
24
Cargo.toml
24
Cargo.toml
@@ -4,34 +4,10 @@ version = "0.1.0"
|
|||||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[[test]]
|
|
||||||
name = "should_panic"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bootloader = "0.9"
|
bootloader = "0.9"
|
||||||
volatile = "0.2.6"
|
|
||||||
spin = "0.5.2"
|
|
||||||
x86_64 = "0.14.2"
|
|
||||||
uart_16550 = "0.2.0"
|
|
||||||
|
|
||||||
[dependencies.lazy_static]
|
|
||||||
version = "1.0"
|
|
||||||
features = ["spin_no_std"]
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "blog_os"
|
name = "blog_os"
|
||||||
test = false
|
test = false
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
|
|
||||||
[package.metadata.bootimage]
|
|
||||||
test-args = [
|
|
||||||
"-device",
|
|
||||||
"isa-debug-exit,iobase=0xf4,iosize=0x04",
|
|
||||||
"-serial",
|
|
||||||
"stdio",
|
|
||||||
"-display",
|
|
||||||
"none",
|
|
||||||
]
|
|
||||||
test-success-exit-code = 33 # (0x10 << 1) | 1
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,10 +1,10 @@
|
|||||||
# Blog OS (Testing)
|
# Blog OS (A Minimal Rust Kernel)
|
||||||
|
|
||||||
[](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Code%22+branch%3Apost-04)
|
[](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Code%22+branch%3Apost-02)
|
||||||
|
|
||||||
This repository contains the source code for the [Testing][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.
|
This repository contains the source code for the [A Minimal Rust Kernel][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.
|
||||||
|
|
||||||
[post]: https://os.phil-opp.com/testing/
|
[post]: https://os.phil-opp.com/minimal-rust-kernel/
|
||||||
|
|
||||||
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**
|
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**
|
||||||
|
|
||||||
@@ -56,10 +56,6 @@ dd if=target/x86_64-blog_os/debug/bootimage-blog_os.bin of=/dev/sdX && sync
|
|||||||
|
|
||||||
Where `sdX` is the device name of your USB stick. **Be careful** to choose the correct device name, because everything on that device is overwritten.
|
Where `sdX` is the device name of your USB stick. **Be careful** to choose the correct device name, because everything on that device is overwritten.
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
To run the unit and integration tests, execute `cargo xtest`.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under either of
|
Licensed under either of
|
||||||
|
|||||||
70
src/lib.rs
70
src/lib.rs
@@ -1,70 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![cfg_attr(test, no_main)]
|
|
||||||
#![feature(custom_test_frameworks)]
|
|
||||||
#![test_runner(crate::test_runner)]
|
|
||||||
#![reexport_test_harness_main = "test_main"]
|
|
||||||
|
|
||||||
use core::panic::PanicInfo;
|
|
||||||
|
|
||||||
pub mod serial;
|
|
||||||
pub mod vga_buffer;
|
|
||||||
|
|
||||||
pub trait Testable {
|
|
||||||
fn run(&self) -> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Testable for T
|
|
||||||
where
|
|
||||||
T: Fn(),
|
|
||||||
{
|
|
||||||
fn run(&self) {
|
|
||||||
serial_print!("{}...\t", core::any::type_name::<T>());
|
|
||||||
self();
|
|
||||||
serial_println!("[ok]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_runner(tests: &[&dyn Testable]) {
|
|
||||||
serial_println!("Running {} tests", tests.len());
|
|
||||||
for test in tests {
|
|
||||||
test.run();
|
|
||||||
}
|
|
||||||
exit_qemu(QemuExitCode::Success);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_panic_handler(info: &PanicInfo) -> ! {
|
|
||||||
serial_println!("[failed]\n");
|
|
||||||
serial_println!("Error: {}\n", info);
|
|
||||||
exit_qemu(QemuExitCode::Failed);
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum QemuExitCode {
|
|
||||||
Success = 0x10,
|
|
||||||
Failed = 0x11,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit_qemu(exit_code: QemuExitCode) {
|
|
||||||
use x86_64::instructions::port::Port;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut port = Port::new(0xf4);
|
|
||||||
port.write(exit_code as u32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entry point for `cargo xtest`
|
|
||||||
#[cfg(test)]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn _start() -> ! {
|
|
||||||
test_main();
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
|
||||||
test_panic_handler(info)
|
|
||||||
}
|
|
||||||
31
src/main.rs
31
src/main.rs
@@ -1,37 +1,26 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(custom_test_frameworks)]
|
|
||||||
#![test_runner(blog_os::test_runner)]
|
|
||||||
#![reexport_test_harness_main = "test_main"]
|
|
||||||
|
|
||||||
use blog_os::println;
|
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
static HELLO: &[u8] = b"Hello World!";
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub extern "C" fn _start() -> ! {
|
pub extern "C" fn _start() -> ! {
|
||||||
println!("Hello World{}", "!");
|
let vga_buffer = 0xb8000 as *mut u8;
|
||||||
|
|
||||||
#[cfg(test)]
|
for (i, &byte) in HELLO.iter().enumerate() {
|
||||||
test_main();
|
unsafe {
|
||||||
|
*vga_buffer.offset(i as isize * 2) = byte;
|
||||||
|
*vga_buffer.offset(i as isize * 2 + 1) = 0xb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is called on panic.
|
/// This function is called on panic.
|
||||||
#[cfg(not(test))]
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
println!("{}", info);
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
|
||||||
blog_os::test_panic_handler(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn trivial_assertion() {
|
|
||||||
assert_eq!(1, 1);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
use lazy_static::lazy_static;
|
|
||||||
use spin::Mutex;
|
|
||||||
use uart_16550::SerialPort;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref SERIAL1: Mutex<SerialPort> = {
|
|
||||||
let mut serial_port = unsafe { SerialPort::new(0x3F8) };
|
|
||||||
serial_port.init();
|
|
||||||
Mutex::new(serial_port)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn _print(args: ::core::fmt::Arguments) {
|
|
||||||
use core::fmt::Write;
|
|
||||||
SERIAL1
|
|
||||||
.lock()
|
|
||||||
.write_fmt(args)
|
|
||||||
.expect("Printing to serial failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints to the host through the serial interface.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! serial_print {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
$crate::serial::_print(format_args!($($arg)*));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints to the host through the serial interface, appending a newline.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! serial_println {
|
|
||||||
() => ($crate::serial_print!("\n"));
|
|
||||||
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
|
|
||||||
($fmt:expr, $($arg:tt)*) => ($crate::serial_print!(
|
|
||||||
concat!($fmt, "\n"), $($arg)*));
|
|
||||||
}
|
|
||||||
@@ -1,193 +0,0 @@
|
|||||||
use core::fmt;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use spin::Mutex;
|
|
||||||
use volatile::Volatile;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
/// A global `Writer` instance that can be used for printing to the VGA text buffer.
|
|
||||||
///
|
|
||||||
/// Used by the `print!` and `println!` macros.
|
|
||||||
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) },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The standard color palette in VGA text mode.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[repr(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,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A combination of a foreground and a background color.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
struct ColorCode(u8);
|
|
||||||
|
|
||||||
impl ColorCode {
|
|
||||||
/// Create a new `ColorCode` with the given foreground and background colors.
|
|
||||||
fn new(foreground: Color, background: Color) -> ColorCode {
|
|
||||||
ColorCode((background as u8) << 4 | (foreground as u8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A screen character in the VGA text buffer, consisting of an ASCII character and a `ColorCode`.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct ScreenChar {
|
|
||||||
ascii_character: u8,
|
|
||||||
color_code: ColorCode,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The height of the text buffer (normally 25 lines).
|
|
||||||
const BUFFER_HEIGHT: usize = 25;
|
|
||||||
/// The width of the text buffer (normally 80 columns).
|
|
||||||
const BUFFER_WIDTH: usize = 80;
|
|
||||||
|
|
||||||
/// A structure representing the VGA text buffer.
|
|
||||||
#[repr(transparent)]
|
|
||||||
struct Buffer {
|
|
||||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A writer type that allows writing ASCII bytes and strings to an underlying `Buffer`.
|
|
||||||
///
|
|
||||||
/// Wraps lines at `BUFFER_WIDTH`. Supports newline characters and implements the
|
|
||||||
/// `core::fmt::Write` trait.
|
|
||||||
pub struct Writer {
|
|
||||||
column_position: usize,
|
|
||||||
color_code: ColorCode,
|
|
||||||
buffer: &'static mut Buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Writer {
|
|
||||||
/// Writes an ASCII byte to the buffer.
|
|
||||||
///
|
|
||||||
/// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character.
|
|
||||||
pub fn write_byte(&mut self, byte: u8) {
|
|
||||||
match byte {
|
|
||||||
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;
|
|
||||||
self.buffer.chars[row][col].write(ScreenChar {
|
|
||||||
ascii_character: byte,
|
|
||||||
color_code,
|
|
||||||
});
|
|
||||||
self.column_position += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes the given ASCII string to the buffer.
|
|
||||||
///
|
|
||||||
/// Wraps lines at `BUFFER_WIDTH`. Supports the `\n` newline character. Does **not**
|
|
||||||
/// support strings with non-ASCII characters, since they can't be printed in the VGA text
|
|
||||||
/// mode.
|
|
||||||
fn write_string(&mut self, s: &str) {
|
|
||||||
for byte in s.bytes() {
|
|
||||||
match byte {
|
|
||||||
// printable ASCII byte or newline
|
|
||||||
0x20..=0x7e | b'\n' => self.write_byte(byte),
|
|
||||||
// not part of printable ASCII range
|
|
||||||
_ => self.write_byte(0xfe),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shifts all lines one line up and clears the last row.
|
|
||||||
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);
|
|
||||||
self.column_position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears a row by overwriting it with blank characters.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Write for Writer {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
self.write_string(s);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like the `print!` macro in the standard library, but prints to the VGA text buffer.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! print {
|
|
||||||
($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like the `println!` macro in the standard library, but prints to the VGA text buffer.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! println {
|
|
||||||
() => ($crate::print!("\n"));
|
|
||||||
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prints the given formatted string to the VGA text buffer through the global `WRITER` instance.
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn _print(args: fmt::Arguments) {
|
|
||||||
use core::fmt::Write;
|
|
||||||
WRITER.lock().write_fmt(args).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test_println_simple() {
|
|
||||||
println!("test_println_simple output");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test_println_many() {
|
|
||||||
for _ in 0..200 {
|
|
||||||
println!("test_println_many output");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test_println_output() {
|
|
||||||
let s = "Some test string that fits on a single line";
|
|
||||||
println!("{}", s);
|
|
||||||
for (i, c) in s.chars().enumerate() {
|
|
||||||
let screen_char = WRITER.lock().buffer.chars[BUFFER_HEIGHT - 2][i].read();
|
|
||||||
assert_eq!(char::from(screen_char.ascii_character), c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(custom_test_frameworks)]
|
|
||||||
#![test_runner(blog_os::test_runner)]
|
|
||||||
#![reexport_test_harness_main = "test_main"]
|
|
||||||
|
|
||||||
use blog_os::println;
|
|
||||||
use core::panic::PanicInfo;
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)] // don't mangle the name of this function
|
|
||||||
pub extern "C" fn _start() -> ! {
|
|
||||||
test_main();
|
|
||||||
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
|
||||||
blog_os::test_panic_handler(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn test_println() {
|
|
||||||
println!("test_println output");
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
use blog_os::{QemuExitCode, exit_qemu, serial_print, serial_println};
|
|
||||||
use core::panic::PanicInfo;
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn _start() -> ! {
|
|
||||||
should_fail();
|
|
||||||
serial_println!("[test did not panic]");
|
|
||||||
exit_qemu(QemuExitCode::Failed);
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_fail() {
|
|
||||||
serial_print!("should_panic::should_fail...\t");
|
|
||||||
assert_eq!(0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(_info: &PanicInfo) -> ! {
|
|
||||||
serial_println!("[ok]");
|
|
||||||
exit_qemu(QemuExitCode::Success);
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
|
||||||
"arch": "x86_64",
|
"arch": "x86_64",
|
||||||
"target-endian": "little",
|
"target-endian": "little",
|
||||||
"target-pointer-width": "64",
|
"target-pointer-width": 64,
|
||||||
"target-c-int-width": 32,
|
"target-c-int-width": 32,
|
||||||
"os": "none",
|
"os": "none",
|
||||||
"executables": true,
|
"executables": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user