Apparently this wasn't committed previously?

This commit is contained in:
Arthur Beck 2025-01-25 18:22:59 -06:00
parent dd168ab5fd
commit 86a85b8b0d
12 changed files with 440 additions and 415 deletions

View file

@ -1,6 +1,7 @@
display_library: x, options="gui_debug"
port_e9_hack: enabled=1
cpu: reset_on_triple_fault=0
cpu: reset_on_triple_fault=0, model=tigerlake
magic_break: enabled=1
ata0-master: type=cdrom, path=../kernel/aphrodite.iso, status=inserted
boot: cdrom

View file

@ -1,26 +0,0 @@
# bx_enh_dbg_ini
SeeReg[0] = TRUE
SeeReg[1] = TRUE
SeeReg[2] = TRUE
SeeReg[3] = TRUE
SeeReg[4] = FALSE
SeeReg[5] = FALSE
SeeReg[6] = FALSE
SeeReg[7] = FALSE
SingleCPU = FALSE
ShowIOWindows = TRUE
ShowButtons = TRUE
SeeRegColors = TRUE
ignoreNxtT = TRUE
ignSSDisasm = TRUE
UprCase = 0
DumpInAsciiMode = 3
isLittleEndian = TRUE
DefaultAsmLines = 512
DumpWSIndex = 0
DockOrder = 0x123
ListWidthPix[0] = 354
ListWidthPix[1] = 411
ListWidthPix[2] = 512
MainWindow = 0, 0, 895, 500
FontName = Normal

View file

@ -3,113 +3,10 @@
#![allow(unexpected_cfgs)]
#![allow(static_mut_refs)]
use crate::arch::x86::output::*;
use crate::arch::x86::egatext as egatext;
use crate::multiboot2::BootInfo;
use egatext::*;
use crate::output::*;
/// The real entrypoint to the kernel. `internel/arch/*/entry.rs` files eventually call this.
#[allow(non_snake_case)]
pub fn _entry(ega: Option<crate::arch::x86::egatext::FramebufferInfo>, BI: &BootInfo) -> ! {
if ega.is_some() {
let ega = ega.unwrap();
ega.clear_screen(WHITE_ON_BLACK);
sreset();
let extended_functions = crate::arch::x86::cpuid_extended_functions();
if extended_functions {
binfosln("This CPU supports extended functions", ega).unwrap();
let longmode_support = crate::arch::x86::cpuid(0x80000001).1 & (1<<29) > 1;
if longmode_support {
binfosln("This CPU supports long mode", ega).unwrap();
} else {
binfosln("This CPU does NOT support long mode!", ega).unwrap();
bdebugs("Long mode CPUID: ", ega).unwrap();
bdebugbnpln(&crate::u32_as_u8_slice(crate::arch::x86::cpuid(0x80000001).1), ega).unwrap();
}
} else {
binfosln("This CPU does NOT support extended functions or long mode!", ega).unwrap();
}
if BI.bootloader_name.is_some() {
binfos("Kernel booted by ", ega).unwrap();
binfosnpln(BI.bootloader_name.unwrap().into(), ega).unwrap();
}
if BI.cmdline.is_some() {
binfos("Command line passed: \"", ega).unwrap();
binfosnp(BI.cmdline.unwrap().into(), ega).unwrap();
binfosnpln("\"", ega).unwrap();
}
if BI.mem_lower.is_some() {
binfos("Amount of lower memory: ", ega).unwrap();
binfobnpln(&crate::u32_as_u8_slice(BI.mem_lower.unwrap()), ega).unwrap();
}
if BI.mem_upper.is_some() {
binfos("Amount of upper memory: ", ega).unwrap();
binfobnpln(&crate::u32_as_u8_slice(BI.mem_upper.unwrap()), ega).unwrap();
}
if BI.memory_map.is_some() {
binfos("Recieved memory map from bootloader with ", ega).unwrap();
binfobnp(&crate::usize_as_u8_slice(BI.memory_map.unwrap().sections.len()), ega).unwrap();
binfosnpln(" sections", ega).unwrap();
let mut i = 0;
for ele in BI.memory_map.unwrap().sections {
binfos("Section #", ega).unwrap();
binfobnp(&crate::usize_as_u8_slice(i), ega).unwrap();
binfosnp(": ", ega).unwrap();
match ele.mem_type {
1 => {
binfosnp("Available RAM", ega).unwrap();
},
2 => {
binfosnp("Reserved by hardware", ega).unwrap();
}
3 => {
binfosnp("ACPI information", ega).unwrap();
},
4 => {
binfosnp("Reserved memory", ega).unwrap();
},
5 => {
binfosnp("Defective", ega).unwrap();
},
_ => {
binfosnp("Reserved/unknown (type=", ega).unwrap();
binfobnp(&crate::u32_as_u8_slice(ele.mem_type), ega).unwrap();
binfosnp(")", ega).unwrap();
}
}
binfosnp(", starting at ", ega).unwrap();
binfobnp(&crate::u64_as_u8_slice(ele.base_addr), ega).unwrap();
binfosnp(" and running for ", ega).unwrap();
binfobnp(&crate::u64_as_u8_slice(ele.length), ega).unwrap();
binfosnpln(" bytes", ega).unwrap();
i += 1;
}
}
} else {
if BI.bootloader_name.is_some() {
sinfos("Kernel booted by ");
sinfosnpln(BI.bootloader_name.unwrap().into());
}
if BI.cmdline.is_some() {
sinfos("Command line passed: \"");
sinfosnp(BI.cmdline.unwrap().into());
sinfosnpln("\"");
}
if BI.memory_map.is_some() {
sinfosln("Recieved memory map from bootloader");
}
if BI.mem_lower.is_some() {
sinfos("Amount of lower memory: ");
sinfobnpln(&crate::u32_as_u8_slice(BI.mem_lower.unwrap()));
}
if BI.mem_upper.is_some() {
sinfos("Amount of upper memory: ");
sinfobnpln(&crate::u32_as_u8_slice(BI.mem_upper.unwrap()));
}
}
pub fn _entry(display: Option<&dyn crate::TextDisplay>, BI: &crate::boot::BootInfo) -> ! {
loop {}
}

View file

@ -1,3 +1,5 @@
//! Arch-specific code.
pub mod x86;
mod x86;
pub use x86::*;

View file

@ -1,6 +1,8 @@
//! Stuff for writing and reading to the EGA text buffer.
#![cfg(any(target_arch = "x86"))]
use crate::Color;
/// Information about the framebuffer.
#[derive(Clone, Copy)]
pub struct FramebufferInfo {
@ -29,9 +31,9 @@ pub const WHITE_ON_BLACK: u8 = 0b00000111;
/// Black text on a black background.
pub const BLACK_ON_BLACK: u8 = 0b00000000;
impl FramebufferInfo {
impl crate::TextDisplay for FramebufferInfo {
/// Writes a character to the screen.
pub fn write_char(self, mut pos: (u32, u32), char: u8, color: u8) -> Result<(), crate::Error<'static>> {
fn write_char(&self, mut pos: (u32, u32), char: u8, color: Color) -> Result<(), crate::Error<'static>> {
if pos.0>self.width {
return Err(crate::Error::new("Invalid X position", ERR_INVALID_X));
}
@ -51,66 +53,13 @@ impl FramebufferInfo {
}
Ok(())
}
/// Clears the screen.
pub fn clear_screen(self, color: u8) {
for x in 0..self.width {
for y in 0..self.height {
self.write_char((x, y), b' ', color).unwrap();
}
}
}
/// Writes a &str to the screen.
pub fn write_str(mut self, pos: (u32, u32), str: &str, color: u8) -> Result<(u32, u32), crate::Error<'static>> {
let (mut x, mut y) = pos;
let change_cursor = self.change_cursor;
if change_cursor {
self.change_cursor = false;
}
for char in str.as_bytes() {
self.write_char((x, y), *char, color)?;
if *char == 0 {
continue
}
x += 1;
while x>self.width {
x -= self.width;
y += 1;
}
}
if change_cursor {
self.change_cursor = true;
self.set_cursor_location((x, y));
}
Ok((x, y))
}
/// Writes a &\[u8] to the screen.
pub fn write_bytes(mut self, pos: (u32, u32), str: &[u8], color: u8) -> Result<(u32, u32), crate::Error<'static>> {
let (mut x, mut y) = pos;
let change_cursor = self.change_cursor;
if change_cursor {
self.change_cursor = false;
}
for char in str {
self.write_char((x, y), *char, color)?;
if *char == 0 {
continue
}
x += 1;
while x>self.width {
x -= self.width;
y += 1;
}
}
if change_cursor {
self.change_cursor = true;
self.set_cursor_location((x, y));
}
Ok((x, y))
fn get_size(&self) -> (u32, u32) {
(self.width, self.height)
}
}
impl FramebufferInfo {
/// Disables the cursor.
pub fn disable_cursor(self) {
super::ports::outb(0x3D4, 0x0A);

View file

@ -7,10 +7,13 @@ pub mod interrupts;
pub mod ports;
pub mod output;
pub mod egatext;
pub mod paging;
mod constants;
pub use constants::*;
use interrupts::{pop_irq, restore_irq};
use ports::{inb, outb};
/// Returns information from the CPUID command in the form
/// (ebx, edx, ecx).
@ -35,4 +38,136 @@ pub fn cpuid_extended_functions() -> bool {
)
}
out >= 0x80000001
}
}
/// Returns whether the a20 gate is enabled.
pub fn test_a20() -> bool {
let addr0: usize;
let addr1: usize;
unsafe {
asm!(
"mov edi, 0x112345",
"mov esi, 0x012345",
"mov [esi], esi",
"mov [edi], edi",
"mov eax, esi",
out("eax") addr0, out("edi") addr1
)
}
addr0 != addr1
}
/// Waits for a keyboard command to complete.
pub fn wait_for_keyboard_cmd() {
while inb(0x64)&0b10 > 1 {}
}
/// Waits for there to be data to read from the keyboard.
pub fn wait_for_keyboard_data() {
while inb(0x64)&0b1 == 0 {}
}
/// Sends a keyboard command.
pub fn send_keyboard_cmd(byte: u8) {
outb(0x64, byte);
}
/// Gets data from the keyboard.
pub fn get_keyboard_data() -> u8 {
inb(0x60)
}
/// Sends data to the keyboard.
pub fn send_keyboard_data(data: u8) {
outb(0x60, data);
}
/// Tries to enable the a20 gate via the keyboard controller method.
pub fn enable_a20_keyboard() {
let irq = pop_irq();
wait_for_keyboard_cmd();
send_keyboard_cmd(0xAD); // disable keyboard
wait_for_keyboard_cmd();
send_keyboard_cmd(0xD0); // read from input
wait_for_keyboard_cmd();
wait_for_keyboard_data();
let a = get_keyboard_data();
wait_for_keyboard_cmd();
send_keyboard_cmd(0xD1); // write to output
wait_for_keyboard_cmd();
send_keyboard_data(a|2);
wait_for_keyboard_cmd();
send_keyboard_cmd(0xAE); // enable keyboard
restore_irq(irq);
}
/// Tries to enable the a20 gate via fast a20.
/// Note that this may not work or do something unexpected.
pub fn enable_a20_fasta20() {
let mut a = inb(0x92);
if a&0b10 > 0 {
return
}
a |= 0b10;
a &= 0xFE;
outb(0x92, a);
}
/// Tries to enable the a20 gate by reading from port 0xee.
pub fn enable_a20_ee_port() {
inb(0xee);
}
/// Tries to enable the a20 gate by trying many different methods
/// and seeing what sticks.
pub fn enable_a20() -> bool {
if test_a20() {
return true;
}
enable_a20_keyboard();
let mut i = 0u32;
while (!test_a20()) && i<10000 {
i += 1;
}
if test_a20() {
return true;
}
enable_a20_ee_port();
let mut i = 0u32;
while (!test_a20()) && i<10000 {
i += 1;
}
if test_a20() {
return true;
}
enable_a20_fasta20();
let mut i = 0u32;
while (!test_a20()) && i<10000 {
i += 1;
}
return test_a20();
}
/// Disables paging by clearing bit 31 in the cr0 register.
pub fn disable_paging() {
unsafe {
asm!(
"mov eax, cr0",
"and eax, 01111111111111111111111111111111b",
"mov cr0, eax"
)
}
}

View file

@ -2,12 +2,8 @@
#![cfg(any(target_arch = "x86"))]
use super::ports;
use super::egatext::*;
use paste::paste;
static mut OUTPUT_TERM_POSITION: (u32, u32) = (0, 0);
macro_rules! message_funcs {
($func_name:ident, $prefix:literal, $level:ident) => {
paste! {
@ -97,215 +93,6 @@ macro_rules! message_funcs {
}
ports::outb(super::DEBUG_PORT, s);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
/// Outputs a $func_name message &str to the terminal.
pub fn [< t $func_name s >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
}
Ok(())
}
/// Outputs a $func_name message &str and a newline to the terminal.
pub fn [< t $func_name sln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] to the terminal.
pub fn [< t $func_name b >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] and a newline to the terminal.
pub fn [< t $func_name bln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a(n) $func_name message u8 to the terminal.
pub fn [< t $func_name u >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, WHITE_ON_BLACK)?;
info.write_char(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION.0 += 1;
while OUTPUT_TERM_POSITION.0 > info.width {
OUTPUT_TERM_POSITION.0 -= info.width;
OUTPUT_TERM_POSITION.1 += 1;
}
}
Ok(())
}
///////////////////////////////////////////////////////////////
/// Outputs a $func_name message &str to the terminal without a prefix.
pub fn [< t $func_name snp >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
}
Ok(())
}
/// Outputs a $func_name message &str and a newline to the terminal without a prefix.
pub fn [< t $func_name snpln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] to the terminal without a prefix.
pub fn [< t $func_name bnp >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] and a newline to the terminal without a prefix.
pub fn [< t $func_name bnpln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a(n) $func_name message u8 to the terminal without a prefix.
pub fn [< t $func_name unp >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
info.write_char(OUTPUT_TERM_POSITION, s, WHITE_ON_BLACK)?;
OUTPUT_TERM_POSITION.0 += 1;
while OUTPUT_TERM_POSITION.0 > info.width {
OUTPUT_TERM_POSITION.0 -= info.width;
OUTPUT_TERM_POSITION.1 += 1;
}
}
Ok(())
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
/// Outputs a $func_name message &str to the terminal.
pub fn [< b $func_name s >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name s >](s);
[< t $func_name s >](s, info)?;
Ok(())
}
/// Outputs a $func_name message &str and a newline to the terminal.
pub fn [< b $func_name sln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name sln >](s);
[< t $func_name sln >](s, info)?;
Ok(())
}
/// Outputs a $func_name message &\[u8] to the terminal.
pub fn [< b $func_name b >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name b >](s);
[< t $func_name b >](s, info)?;
Ok(())
}
/// Outputs a $func_name message &\[u8] and a newline to the terminal.
pub fn [< b $func_name bln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name bln >](s);
[< t $func_name bln >](s, info)?;
Ok(())
}
/// Outputs a(n) $func_name message u8 to the terminal.
pub fn [< b $func_name u >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name u >](s);
[< t $func_name u >](s, info)?;
Ok(())
}
///////////////////////////////////////////////////////////////
/// Outputs a $func_name message &str to the terminal without a prefix.
pub fn [< b $func_name snp >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name snp >](s);
[< t $func_name snp >](s, info)?;
Ok(())
}
/// Outputs a $func_name message &str and a newline to the terminal without a prefix.
pub fn [< b $func_name snpln >](s: &str, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name snpln >](s);
[< t $func_name snpln >](s, info)?;
Ok(())
}
/// Outputs a $func_name message &\[u8] to the terminal without a prefix.
pub fn [< b $func_name bnp >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name bnp >](s);
[< t $func_name bnp >](s, info)?;
Ok(())
}
/// Outputs a $func_name message &\[u8] and a newline to the terminal without a prefix.
pub fn [< b $func_name bnpln >](s: &[u8], info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name bnpln >](s);
[< t $func_name bnpln >](s, info)?;
Ok(())
}
/// Outputs a(n) $func_name message u8 to the terminal without a prefix.
pub fn [< b $func_name unp >](s: u8, info: FramebufferInfo) -> Result<(), crate::Error<'static>> {
[< s $func_name unp >](s);
[< t $func_name unp >](s, info)?;
Ok(())
}
}
}
}
@ -317,10 +104,3 @@ message_funcs!(error, "[ERROR] ", CONFIG_PREUSER_OUTPUT_ERROR);
message_funcs!(fatal, "[FATAL] ", CONFIG_PREUSER_OUTPUT_FATAL);
message_funcs!(output, "", NONE);
/// Resets the position of output to the screen.
pub fn sreset() {
unsafe {
OUTPUT_TERM_POSITION = (0, 0);
}
}

View file

@ -0,0 +1 @@
//! Functions and types related to paging.

View file

@ -0,0 +1,56 @@
//! General bootloader-independent stuff.
/// A type of memory, for use in [MemoryMapping]s.
/// The memory allocator will ignore all memory
/// except for memory with type [MemoryType::Free]
/// or [MemoryType::HardwareSpecific] memory with
/// the boolean argument set.
#[derive(Clone, Copy)]
pub enum MemoryType {
/// Free RAM with no use.
Free,
/// Reserved by something.
Reserved,
/// Reserved by something on the hardware.
HardwareReserved,
/// Faulty RAM modules.
Faulty,
/// Unknown use.
Unknown,
/// Hardware-specific use. The boolean argument states
/// whether memory can be allocated in this region.
HardwareSpecific(u32, bool)
}
/// A single memory mapping for [MemoryMap].
pub trait MemoryMapping {
/// Returns the type of the memory.
fn get_type(&self) -> MemoryType;
/// Returns the beginning of the memory.
fn get_start(&self) -> usize;
/// Returns the length of the memory.
fn get_length(&self) -> usize;
}
/// Memory mapping.
pub trait MemoryMap<'a>: core::iter::Iterator<Item = &'a dyn MemoryMapping> + core::ops::Index<usize, Output = &'a dyn MemoryMapping> {
/// Returns the number of [MemoryMapping]s in the MemoryMap. This is total, not remainder.
fn len(&self) -> usize;
}
/// Bootloader-independent information.
#[derive(Clone)]
pub struct BootInfo<'a> {
/// The commandline of the kernel.
/// See https://github.com/AverseABFun/aphrodite/wiki/Plan#bootloader (remember to update link later!) for the format.
pub cmdline: Option<&'static str>,
/// The memory map provided by the bootloader. If None, the kernel will attempt to generate it.
pub memory_map: Option<&'a dyn MemoryMap<'a>>,
/// The name of the bootloader(for example, "GRUB 2.12").
pub bootloader_name: Option<&'static str>,
/// Provides a way to display text.
pub output: Option<&'a dyn crate::TextDisplay>,
}

View file

@ -10,9 +10,13 @@ pub mod multiboot2;
pub mod arch;
mod errors;
pub mod _entry;
mod traits;
pub mod output;
pub mod boot;
#[allow(unused_imports)] // if there are no constants, then it gives a warning
pub use constants::*;
pub use errors::*;
pub use util::*;
pub use util::*;
pub use traits::*;

View file

@ -0,0 +1,159 @@
//! Architecture-independent output functions.
use paste::paste;
use crate::COLOR_DEFAULT;
static mut OUTPUT_TERM_POSITION: (u32, u32) = (0, 0);
macro_rules! message_funcs {
($func_name:ident, $prefix:literal, $level:ident) => {
paste! {
/// Outputs a $func_name message &str to the terminal.
pub fn [< t $func_name s >](s: &str, info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
}
Ok(())
}
/// Outputs a $func_name message &str and a newline to the terminal.
pub fn [< t $func_name sln >](s: &str, info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] to the terminal.
pub fn [< t $func_name b >](s: &[u8], info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] and a newline to the terminal.
pub fn [< t $func_name bln >](s: &[u8], info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a(n) $func_name message u8 to the terminal.
pub fn [< t $func_name u >](s: u8, info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
let (width, _) = info.get_size();
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, $prefix, COLOR_DEFAULT)?;
info.write_char(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION.0 += 1;
while OUTPUT_TERM_POSITION.0 > width {
OUTPUT_TERM_POSITION.0 -= width;
OUTPUT_TERM_POSITION.1 += 1;
}
}
Ok(())
}
///////////////////////////////////////////////////////////////
/// Outputs a $func_name message &str to the terminal without a prefix.
pub fn [< t $func_name snp >](s: &str, info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
}
Ok(())
}
/// Outputs a $func_name message &str and a newline to the terminal without a prefix.
pub fn [< t $func_name snpln >](s: &str, info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_str(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] to the terminal without a prefix.
pub fn [< t $func_name bnp >](s: &[u8], info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
}
Ok(())
}
/// Outputs a $func_name message &\[u8] and a newline to the terminal without a prefix.
pub fn [< t $func_name bnpln >](s: &[u8], info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
unsafe {
if cfg!($level = "false") {
return Ok(());
}
OUTPUT_TERM_POSITION = info.write_bytes(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION.1 += 1;
OUTPUT_TERM_POSITION.0 = 0;
}
Ok(())
}
/// Outputs a(n) $func_name message u8 to the terminal without a prefix.
pub fn [< t $func_name unp >](s: u8, info: &dyn crate::TextDisplay) -> Result<(), crate::Error<'static>> {
let (width, _) = info.get_size();
unsafe {
if cfg!($level = "false") {
return Ok(());
}
info.write_char(OUTPUT_TERM_POSITION, s, COLOR_DEFAULT)?;
OUTPUT_TERM_POSITION.0 += 1;
while OUTPUT_TERM_POSITION.0 > width {
OUTPUT_TERM_POSITION.0 -= width;
OUTPUT_TERM_POSITION.1 += 1;
}
}
Ok(())
}
}
}
}
message_funcs!(debug, "[DEBUG] ", CONFIG_PREUSER_OUTPUT_DEBUG);
message_funcs!(info, "[INFO] ", CONFIG_PREUSER_OUTPUT_INFO);
message_funcs!(warning, "[WARN] ", CONFIG_PREUSER_OUTPUT_WARN);
message_funcs!(error, "[ERROR] ", CONFIG_PREUSER_OUTPUT_ERROR);
message_funcs!(fatal, "[FATAL] ", CONFIG_PREUSER_OUTPUT_FATAL);
message_funcs!(output, "", NONE);
/// Resets the position of output to the screen.
pub fn sreset() {
unsafe {
OUTPUT_TERM_POSITION = (0, 0);
}
}

View file

@ -0,0 +1,67 @@
//! General traits. Mostly implemented in arch/*.
/// A type used for color in the functions of [TextDisplay].
pub type Color = u8;
/// Black-on-black.
pub const COLOR_BLACK: Color = 0;
/// Should be whatever colors commonly used for status messages.
/// Generally should be white-on-black.
pub const COLOR_DEFAULT: Color = 1;
/// Some form of display that can be written too with text.
pub trait TextDisplay {
/// Writes a single character to the specified position.
fn write_char(&self, pos: (u32, u32), char: u8, color: Color) -> Result<(), crate::Error<'static>>;
/// Gets the size of the screen.
fn get_size(&self) -> (u32, u32);
}
impl dyn TextDisplay + '_ {
/// Clears the screen.
pub fn clear_screen(&self, color: Color) {
let (width, height) = self.get_size();
for x in 0..width {
for y in 0..height {
self.write_char((x, y), b' ', color).unwrap();
}
}
}
/// Writes a &str to the screen.
pub fn write_str(&self, pos: (u32, u32), str: &str, color: Color) -> Result<(u32, u32), crate::Error<'static>> {
let (width, _) = self.get_size();
let (mut x, mut y) = pos;
for char in str.as_bytes() {
self.write_char((x, y), *char, color)?;
if *char == 0 {
continue
}
x += 1;
while x>width {
x -= width;
y += 1;
}
}
Ok((x, y))
}
/// Writes a &\[u8] to the screen.
pub fn write_bytes(&self, pos: (u32, u32), str: &[u8], color: Color) -> Result<(u32, u32), crate::Error<'static>> {
let (width, _) = self.get_size();
let (mut x, mut y) = pos;
for char in str {
self.write_char((x, y), *char, color)?;
if *char == 0 {
continue
}
x += 1;
while x>width {
x -= width;
y += 1;
}
}
Ok((x, y))
}
}