diff --git a/Cargo.toml b/Cargo.toml index df18e5f..bc72961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,8 @@ edition = "2018" [dependencies] bootloader = "0.9.19" x86_64 = "0.14.6" -spin = "0.5.2" +spin = "0.9.2" +uart_16550 = "0.2.0" [dependencies.lazy_static] version = "1.0" @@ -15,7 +16,9 @@ features = ["spin_no_std"] [package.metadata.bootimage] build-command = ["build"] - +test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio", + "-display", "none"] run-command = [ "qemu-system-x86_64", "-drive", "format=raw,file={}" ] +test-success-exit-code = 33 # (0x10 << 1) | 1 \ No newline at end of file diff --git a/src/cursor.rs b/src/cursor.rs deleted file mode 100644 index d51b8af..0000000 --- a/src/cursor.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::color::Color; -use core::fmt; - -pub struct Cursor { - pub position: isize, - pub foreground: Color, - pub background: Color, -} - -impl Cursor { - pub fn color(&self) -> u8 { - let fg = self.foreground as u8; - let bg = (self.background as u8) << 4; - fg | bg - } - - pub fn print(&mut self, text: &[u8]) { - let color = self.color(); - - let framebuffer = 0xb8000 as *mut u8; - - for &character in text { - unsafe { - framebuffer.offset(self.position).write_volatile(character); - framebuffer.offset(self.position + 1).write_volatile(color); - } - self.position += 2; - } - } - pub fn clr(&mut self) { - self.position = 0; - for _ in 0..(80 * 25) { - self.print(b" "); - } - self.position = 0; - } -} - -impl Default for Cursor { - fn default() -> Self { - Cursor { - position: 0, - foreground: Color::Green, - background: Color::DarkGray, - } - } -} - -impl fmt::Write for Cursor { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.print(s.as_bytes()); - Ok(()) - } -} diff --git a/src/main.rs b/src/main.rs index 6e00621..d448574 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,48 +1,62 @@ #![feature(core_intrinsics)] #![feature(lang_items)] +#![feature(custom_test_frameworks)] +#![test_runner(crate::test_runner)] +#![reexport_test_harness_main = "test_main"] #![no_std] #![no_main] -use core::fmt::Write; use core::panic::PanicInfo; - use x86_64::instructions::hlt; mod color; -mod cursor; +mod serial; mod vga_buffer; -use color::{Color, ColorCode}; -use cursor::Cursor; -use vga_buffer::*; +mod testable; +mod utils; -pub fn print_something() { - let mut writer = Writer { - column_position: 0, - color_code: ColorCode::new(Color::Yellow, Color::Black), - buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, - }; - - writer.write_byte(b'H'); - writer.write_string("ello "); - writer.write_string("Wörld!\n"); - writer.write_string("Awruk!\n"); +#[cfg(test)] +fn test_runner(tests: &[&dyn Fn()]) { + serial_println!("Running {} tests", tests.len()); + for test in tests { + use testable::Testable; + test.run(); + } + utils::exit_qemu(utils::QemuExitCode::Success); } -#[panic_handler] -#[no_mangle] -pub fn panic(info: &PanicInfo) -> ! { - println!("{}", info); +#[test_case] +fn trivial_assertion() { + assert_eq!(1, 1); +} - loop { - hlt(); - } + +#[cfg(not(test))] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + println!("{}", info); + loop {} +} + +#[cfg(test)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + serial_println!("[failed]\n"); + serial_println!("Error: {}\n", info); + utils::exit_qemu(utils::QemuExitCode::Failed); + loop {} } #[no_mangle] pub extern "C" fn _start() -> ! { - print_something(); - println!("DSADSA"); - println!("Test {}", 5132.31); + utils::print_logo(); + println!(""); + println!("Welcome!"); + println!("Awruk!"); + println!("Test value: {}\n", 5132.31); + + #[cfg(test)] + test_main(); // panic!("help!"); loop { hlt(); diff --git a/src/serial.rs b/src/serial.rs new file mode 100644 index 0000000..e8807df --- /dev/null +++ b/src/serial.rs @@ -0,0 +1,37 @@ +use lazy_static::lazy_static; +use spin::Mutex; +use uart_16550::SerialPort; + +lazy_static! { + pub static ref SERIAL1: Mutex = { + 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)*)); +} diff --git a/src/testable.rs b/src/testable.rs new file mode 100644 index 0000000..83b6d14 --- /dev/null +++ b/src/testable.rs @@ -0,0 +1,16 @@ +use crate::{serial_print,serial_println}; + +pub trait Testable { + fn run(&self) -> (); +} + +impl Testable for T +where + T: Fn(), +{ + fn run(&self) { + serial_print!("{}...\t", core::any::type_name::()); + self(); + serial_println!("[ok]"); + } +} \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..0569e48 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,29 @@ +use crate::println; + +#[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); + } +} + +pub fn print_logo() { + + let text = r###" + _ _ __ + _ |_ |_) _ _|_/ \(_ + (_ | || \|_|_> |_\_/__) +"###; + crate::vga_buffer::change_color(crate::color::Color::BrightRed); + println!("{}", text); + crate::vga_buffer::reset_color(); +} \ No newline at end of file diff --git a/src/vga_buffer.rs b/src/vga_buffer.rs index 9564302..7434b06 100644 --- a/src/vga_buffer.rs +++ b/src/vga_buffer.rs @@ -6,7 +6,7 @@ use spin::Mutex; lazy_static! { pub static ref WRITER: Mutex = Mutex::new(Writer { column_position: 0, - color_code: ColorCode::new(Color::Yellow, Color::Black), + color_code: ColorCode::new(Color::White, Color::Black), buffer: unsafe { &mut *(0xb8000 as *mut Buffer) }, }); } @@ -44,10 +44,9 @@ impl Writer { let row = BUFFER_HEIGHT - 1; let col = self.column_position; - let color_code = self.color_code; let screen_char = ScreenChar { ascii_character: byte, - color_code, + color_code: self.color_code, }; unsafe { core::ptr::write_volatile(&mut self.buffer.chars[row][col], screen_char); @@ -101,6 +100,14 @@ impl fmt::Write for Writer { } } + +pub fn change_color(color : Color) { + WRITER.lock().color_code = ColorCode::new(color, Color::Black); +} +pub fn reset_color() { + WRITER.lock().color_code = ColorCode::new(Color::White, Color::Black); +} + #[macro_export] macro_rules! print { ($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));