Supressed clippy errors and add some rust docs
This commit is contained in:
Vendored
+2
@@ -6,4 +6,6 @@
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"rust-analyzer.check.command": "clippy",
|
||||
"rust-analyzer.procMacro.attributes.enable": true,
|
||||
"rust-analyzer.cargo.buildScripts.enable": true
|
||||
}
|
||||
Generated
+3
-2
@@ -22,9 +22,9 @@ checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.15"
|
||||
version = "1.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -75,6 +75,7 @@ name = "foundry_os"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libm",
|
||||
"limine",
|
||||
"pc-keyboard",
|
||||
"pic8259",
|
||||
|
||||
@@ -11,6 +11,7 @@ x86_64 = "0.15.2"
|
||||
spin = "0.9.8"
|
||||
pic8259 = "0.11.0"
|
||||
pc-keyboard = "0.8.0"
|
||||
libm = { version = "0.1.0", path = "../libm" }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.2.14"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#![expect(unused)]
|
||||
use core::arch::x86_64::__cpuid;
|
||||
|
||||
// use libk::drivers::memory::{FRAME_ALLOCATOR, FoundryOSFrameAllocator, OFFSET_PAGE_TABLE};
|
||||
// use libk::drivers::memory::{FRAME_ALLOCATOR, FoundryOSFrameAllocator,
|
||||
// OFFSET_PAGE_TABLE};
|
||||
use x86_64::{
|
||||
PhysAddr, VirtAddr,
|
||||
structures::paging::{Page, PageTableFlags, PhysFrame, Size4KiB},
|
||||
@@ -9,7 +10,7 @@ use x86_64::{
|
||||
|
||||
// use crate::serial_print;
|
||||
|
||||
use super::{cpu::model_specific_registers::*, memmap::PHYSICAL_MEMORY_OFFSET};
|
||||
use super::{cpu::msr::*, memmap::PHYSICAL_MEMORY_OFFSET};
|
||||
|
||||
const IA32_APIC_BASE_MSR: u32 = 0x1b;
|
||||
const IA32_APIC_BASE_MSR_BSP: u64 = 0x100;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#[expect(unused)]
|
||||
pub mod model_specific_registers {
|
||||
use core::arch::x86_64::__cpuid;
|
||||
use spin::Lazy;
|
||||
use x86_64::registers::model_specific::Msr;
|
||||
|
||||
const CPUID_FLAG_MSR: u32 = 1 << 5;
|
||||
static EDX: Lazy<u32> = Lazy::new(|| unsafe { __cpuid(1).edx });
|
||||
|
||||
pub fn cpu_has_msr() -> bool {
|
||||
*EDX & CPUID_FLAG_MSR != 0
|
||||
}
|
||||
|
||||
pub fn cpu_get_msr(msr: u32, value: &mut u64) {
|
||||
let msr = Msr::new(msr);
|
||||
unsafe {
|
||||
*value = msr.read();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpu_set_msr(msr: u32, value: u64) {
|
||||
let mut msr = Msr::new(msr);
|
||||
unsafe {
|
||||
msr.write(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod msr;
|
||||
pub mod port;
|
||||
@@ -0,0 +1,25 @@
|
||||
#![expect(unused)]
|
||||
use core::arch::x86_64::__cpuid;
|
||||
use spin::Lazy;
|
||||
use x86_64::registers::model_specific::Msr;
|
||||
|
||||
const CPUID_FLAG_MSR: u32 = 1 << 5;
|
||||
static EDX: Lazy<u32> = Lazy::new(|| unsafe { __cpuid(1).edx });
|
||||
|
||||
pub fn cpu_has_msr() -> bool {
|
||||
*EDX & CPUID_FLAG_MSR != 0
|
||||
}
|
||||
|
||||
pub fn cpu_get_msr(msr: u32, value: &mut u64) {
|
||||
let msr = Msr::new(msr);
|
||||
unsafe {
|
||||
*value = msr.read();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpu_set_msr(msr: u32, value: u64) {
|
||||
let mut msr = Msr::new(msr);
|
||||
unsafe {
|
||||
msr.write(value);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
//! Functions for IO using ports.
|
||||
|
||||
#![expect(unused)]
|
||||
use core::arch::asm;
|
||||
|
||||
#[inline]
|
||||
pub fn inb(port: u16) -> u8 {
|
||||
/// Take a byte in from a port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This might have side effects so it is marked unsafe just in case.
|
||||
#[inline(always)]
|
||||
pub unsafe fn inb(port: u16) -> u8 {
|
||||
let value: u8;
|
||||
unsafe {
|
||||
asm!(
|
||||
@@ -16,8 +21,13 @@ pub fn inb(port: u16) -> u8 {
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outb(port: u16, value: u8) {
|
||||
/// Take a byte in from a port.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This might have side effects so it is marked unsafe just in case.
|
||||
#[inline(always)]
|
||||
pub unsafe fn outb(port: u16, value: u8) {
|
||||
unsafe {
|
||||
asm!(
|
||||
"out dx, al",
|
||||
@@ -66,7 +66,9 @@ pub fn disable_pic() {
|
||||
}
|
||||
}
|
||||
|
||||
const extern "x86-interrupt" fn breakpoint_handler(_stack_frame: InterruptStackFrame) {
|
||||
const extern "x86-interrupt" fn breakpoint_handler(
|
||||
_stack_frame: InterruptStackFrame,
|
||||
) {
|
||||
// serial_println!("Exception: Breakpoint\n{:#?}", stack_frame);
|
||||
// println_log!("Exception: Breakpoint\n{:#?}", stack_frame);
|
||||
}
|
||||
@@ -87,19 +89,22 @@ extern "x86-interrupt" fn double_fault_handler(
|
||||
panic!("Exception: Double Fault\n{:#?}", stack_frame);
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
extern "x86-interrupt" fn keyboard_interrupt_handler(
|
||||
_stack_frame: InterruptStackFrame,
|
||||
) {
|
||||
use pc_keyboard::{HandleControl, Keyboard, ScancodeSet1, layouts};
|
||||
// use pc_keyboard::DecodedKey;
|
||||
use spin::Mutex;
|
||||
// use x86_64::instructions::port::Port;
|
||||
|
||||
static KEYBOARD: Lazy<Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>>> = Lazy::new(|| {
|
||||
Mutex::new(Keyboard::new(
|
||||
ScancodeSet1::new(),
|
||||
layouts::Uk105Key,
|
||||
HandleControl::Ignore,
|
||||
))
|
||||
});
|
||||
static KEYBOARD: Lazy<Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>>> =
|
||||
Lazy::new(|| {
|
||||
Mutex::new(Keyboard::new(
|
||||
ScancodeSet1::new(),
|
||||
layouts::Uk105Key,
|
||||
HandleControl::Ignore,
|
||||
))
|
||||
});
|
||||
|
||||
let _keyboard = KEYBOARD.lock();
|
||||
// let mut port = Port::new(0x60);
|
||||
@@ -113,7 +118,9 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStac
|
||||
}
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
extern "x86-interrupt" fn timer_interrupt_handler(
|
||||
_stack_frame: InterruptStackFrame,
|
||||
) {
|
||||
unsafe {
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
// Some predefined colours as well as custom colours in the proper format. This
|
||||
// assumes that the framebuffer uses 32bpp, something which we will assert on
|
||||
// initialisation.
|
||||
pub enum Colour {
|
||||
ARGB(u8, u8, u8, u8),
|
||||
RGB(u8, u8, u8),
|
||||
Argb(u8, u8, u8, u8),
|
||||
Rgb(u8, u8, u8),
|
||||
HexARGB(u32),
|
||||
Black = 0x000000FF,
|
||||
Blue = 0x0000FFFF,
|
||||
@@ -14,14 +17,19 @@ pub enum Colour {
|
||||
White = 0xFFFFFFFF,
|
||||
}
|
||||
|
||||
#[allow(clippy::use_self)]
|
||||
#[expect(
|
||||
clippy::use_self,
|
||||
reason = "Self is uglier and more verbose than `u32`"
|
||||
)]
|
||||
impl From<Colour> for u32 {
|
||||
fn from(val: Colour) -> Self {
|
||||
match val {
|
||||
Colour::ARGB(a, r, g, b) => {
|
||||
Colour::Argb(a, r, g, b) => {
|
||||
(a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32)
|
||||
}
|
||||
Colour::RGB(r, g, b) => ((r as u32) << 16) | (g as u32) << 8 | (b as u32),
|
||||
Colour::Rgb(r, g, b) => {
|
||||
((r as u32) << 16) | (g as u32) << 8 | (b as u32)
|
||||
}
|
||||
Colour::HexARGB(hex) => hex,
|
||||
Colour::Black => 0xFF000000,
|
||||
Colour::Blue => 0xFF0000FF,
|
||||
@@ -38,8 +46,10 @@ impl From<Colour> for u32 {
|
||||
impl core::fmt::Display for Colour {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a),
|
||||
Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
|
||||
Self::Argb(r, g, b, a) => {
|
||||
write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a)
|
||||
}
|
||||
Self::Rgb(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
|
||||
Self::HexARGB(hex) => write!(f, "Hex(#{:x})", hex),
|
||||
Self::Black => write!(f, "Black"),
|
||||
Self::Blue => write!(f, "Blue"),
|
||||
@@ -0,0 +1,46 @@
|
||||
#![allow(unused)]
|
||||
use libm::include_font;
|
||||
|
||||
pub const FONT_SPLEEN_8X16: Font =
|
||||
Font::new(include_font!("../../resources/font/spleen-8x16.psf"));
|
||||
|
||||
pub const FONT_CP850_8X16: Font =
|
||||
Font::new(include_font!("../../resources/font/cp850-8x16.psf"));
|
||||
|
||||
pub struct Font {
|
||||
width: usize,
|
||||
height: usize,
|
||||
length: u16,
|
||||
data: [[u8; 16]; 512],
|
||||
}
|
||||
|
||||
impl Font {
|
||||
pub const fn new(data: [[u8; 16]; 512]) -> Self {
|
||||
Self {
|
||||
width: 8,
|
||||
height: 16,
|
||||
length: data.len() as u16,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn glyph_for(&self, c: u16) -> &[u8] {
|
||||
if c > self.length {
|
||||
return &self.data[0];
|
||||
}
|
||||
|
||||
&self.data[c as usize]
|
||||
}
|
||||
|
||||
pub const fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
pub const fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub const fn default() -> &'static Self {
|
||||
&FONT_SPLEEN_8X16
|
||||
}
|
||||
}
|
||||
@@ -1 +1,158 @@
|
||||
//! Handles
|
||||
//! Handles setting up the framebuffer passed to us by Limine.
|
||||
#![expect(unused)]
|
||||
use crate::DEFAULT_FONT;
|
||||
|
||||
use super::{colour::Colour, font::Font};
|
||||
|
||||
use limine::framebuffer::Framebuffer;
|
||||
use limine::request::FramebufferRequest;
|
||||
use spin::{Lazy, Mutex};
|
||||
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests")]
|
||||
/// The Limine framebuffer request.
|
||||
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||
|
||||
/// A mutex used for writing to the framebuffer, in most cases we can abstract
|
||||
/// this behind calls to println! etc.
|
||||
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::new(
|
||||
|| {
|
||||
Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else(
|
||||
|| {
|
||||
unreachable!("Framebuffer request failed, got None.");
|
||||
},
|
||||
|framebuffer_response| {
|
||||
let framebuffer =
|
||||
framebuffer_response.framebuffers().next()
|
||||
.expect("Expected to find at least one framebuffer in response, got zero.");
|
||||
Some(FramebufferWriter::new(framebuffer, Colour::Black))
|
||||
},
|
||||
))
|
||||
},
|
||||
);
|
||||
|
||||
/// The updated writer stores necessary fields from the [Framebuffer].
|
||||
/// This ensures that the contained types are Send, as Framebuffer was
|
||||
/// not marked as Send.
|
||||
///
|
||||
/// It also avoids the requirement for lifetimes.
|
||||
///
|
||||
/// Note this does not implement Writer as these functions only handle drawing
|
||||
/// pixels.
|
||||
pub struct FramebufferWriter {
|
||||
pitch: u64,
|
||||
/// Number of bits used per pixel, we expect 32 so this will be asserted
|
||||
/// when we initialise the framebuffer.
|
||||
bpp: u16,
|
||||
/// A raw pointer to the framebuffer.
|
||||
addr: *mut u8,
|
||||
/// The width of the screen in pixels.
|
||||
width: u64,
|
||||
/// The height of the screen in pixels.
|
||||
height: u64,
|
||||
/// The default background colour of the Framebuffer.
|
||||
background_colour: Colour,
|
||||
}
|
||||
|
||||
/// Marker trait required for sending the writer across threads, which will
|
||||
/// probably come in handy later.
|
||||
unsafe impl Send for FramebufferWriter {}
|
||||
/// Marker trait required for sharing the writer across threads, which will
|
||||
/// probably come in handy later.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// We wrap this in a Mutex later so this should handle synchronising accesses.
|
||||
unsafe impl Sync for FramebufferWriter {}
|
||||
|
||||
impl FramebufferWriter {
|
||||
/// Creates a new [`FramebufferWriter`] with the specified background
|
||||
/// colour.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the bits per pixel for the framebuffer is not 32 bits per
|
||||
/// pixel, because we didn't exactly handle other cases.
|
||||
pub fn new(framebuffer: Framebuffer, background_colour: Colour) -> Self {
|
||||
// We don't handle other cases, this should not trigger on most newer
|
||||
// hardware.
|
||||
assert!(framebuffer.bpp() == 32);
|
||||
|
||||
Self {
|
||||
pitch: framebuffer.pitch(),
|
||||
bpp: framebuffer.bpp(),
|
||||
addr: framebuffer.addr(),
|
||||
width: framebuffer.width(),
|
||||
height: framebuffer.height(),
|
||||
background_colour,
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a pixel with `colour` to the screen at the given `(x, y)`
|
||||
/// coordinates (in pixels).
|
||||
pub fn write_pixel(&self, x: usize, y: usize, colour: Colour) {
|
||||
let pitch = self.pitch as usize;
|
||||
let bpp = (self.bpp / 8) as usize;
|
||||
let pixel_offset = y * pitch + x * bpp;
|
||||
|
||||
// Safety: We assume the pointer to the framebuffer is valid, which it likely
|
||||
// is because we copy it from the Limine request.
|
||||
unsafe {
|
||||
*(self.addr.add(pixel_offset) as *mut u32) = colour.into();
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a 2D slice `buffer` to the screen. This should be less than the
|
||||
/// size of the screen in dimensions, but does not neccessarily fill the
|
||||
/// whole screen.
|
||||
pub fn render_frame(&self, buffer: &[&[Colour]]) {
|
||||
// TODO: this should return errors.
|
||||
for (y, &row) in buffer.iter().enumerate() {
|
||||
if y >= self.height() as usize {
|
||||
break;
|
||||
}
|
||||
for (x, pixel) in row.iter().enumerate() {
|
||||
if x >= self.width() as usize {
|
||||
break;
|
||||
}
|
||||
self.write_pixel(x, y, *pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the width of the framebuffer in pixels.
|
||||
pub const fn width(&self) -> u32 {
|
||||
self.width as u32
|
||||
}
|
||||
|
||||
/// Returns the height of the framebuffer in pixels.
|
||||
pub const fn height(&self) -> u32 {
|
||||
self.height as u32
|
||||
}
|
||||
|
||||
/// Sets the new default background colour.
|
||||
pub const fn set_default_background_colour(&mut self, bg: Colour) {
|
||||
self.background_colour = bg;
|
||||
}
|
||||
|
||||
/// Clears the screen and sets the background to the default colour.
|
||||
pub fn clear(&self) {
|
||||
let width = self.width as usize;
|
||||
let height = self.height as usize;
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
// Write the background in the preferred background colour.
|
||||
self.write_pixel(x, y, self.background_colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the screen size in pixels or (0, 0) if something went wrong.
|
||||
pub fn screensize_px() -> (u32, u32) {
|
||||
FRAMEBUFFER_WRITER
|
||||
.lock()
|
||||
.as_mut()
|
||||
.map_or_else(|| (0, 0), |writer| (writer.width(), writer.height()))
|
||||
}
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
pub mod ascii;
|
||||
pub mod colour;
|
||||
pub mod font;
|
||||
pub mod framebuffer;
|
||||
pub mod writer;
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
#![expect(dead_code)]
|
||||
use core::fmt;
|
||||
use spin::{Lazy, Mutex};
|
||||
use x86_64::instructions::interrupts;
|
||||
|
||||
use super::{colour::Colour, font::Font, framebuffer::FRAMEBUFFER_WRITER};
|
||||
|
||||
static FONT_WIDTH: u32 = 8;
|
||||
static FONT_HEIGHT: u32 = 16;
|
||||
|
||||
pub static WRITER: Lazy<Mutex<Writer>> = Lazy::new(|| Mutex::new(Writer::new()));
|
||||
|
||||
pub fn screensize_chars() -> (u32, u32) {
|
||||
let writer = WRITER.lock();
|
||||
(writer.screen_width, writer.screen_height)
|
||||
}
|
||||
|
||||
pub struct Writer {
|
||||
font: &'static Font,
|
||||
/// Measured in chars not pixels.
|
||||
screen_width: u32,
|
||||
/// Measured in chars not pixels.
|
||||
screen_height: u32,
|
||||
/// 16 pixels tall.
|
||||
text_line: u32,
|
||||
/// 8 pixels wide.
|
||||
text_col: u32,
|
||||
|
||||
fg_color: Colour,
|
||||
bg_color: Colour,
|
||||
}
|
||||
|
||||
impl Default for Writer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub fn new() -> Self {
|
||||
FRAMEBUFFER_WRITER.lock().as_mut().map_or_else(
|
||||
|| {
|
||||
panic!("Framebuffer writer not initialized.");
|
||||
},
|
||||
|writer| Self {
|
||||
font: &crate::DEFAULT_FONT,
|
||||
screen_width: writer.width() / 8,
|
||||
screen_height: writer.height() / 16,
|
||||
text_line: 0,
|
||||
text_col: 0,
|
||||
fg_color: Colour::White,
|
||||
bg_color: Colour::Black,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub const fn set_font(&mut self, font: &'static Font) {
|
||||
self.font = font;
|
||||
}
|
||||
|
||||
/// This is sent when the user types a backspace.
|
||||
const BACKSPACE: u8 = 8;
|
||||
|
||||
pub fn write_glyph(&mut self, c: u8) {
|
||||
if c == b'\n' {
|
||||
self.newline();
|
||||
return;
|
||||
} else if c == Self::BACKSPACE {
|
||||
self.backspace();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the character data from the font array. -- each byte is a row of
|
||||
// pixels
|
||||
let data: &[u8] = self.font.glyph_for(c as u16);
|
||||
|
||||
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
||||
for (row, line) in data.iter().enumerate().take(16) {
|
||||
for col in 0..8 {
|
||||
let pixel_x: u32 = self.text_col * FONT_WIDTH + col;
|
||||
let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32;
|
||||
|
||||
if line & (0x80 >> col) != 0 {
|
||||
// Write the foreground color
|
||||
writer.write_pixel(
|
||||
pixel_x as usize,
|
||||
pixel_y as usize,
|
||||
self.fg_color,
|
||||
);
|
||||
} else {
|
||||
// Write the background color
|
||||
writer.write_pixel(
|
||||
pixel_x as usize,
|
||||
pixel_y as usize,
|
||||
self.bg_color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Go to next position
|
||||
if self.text_col + 1 >= self.screen_width {
|
||||
self.newline();
|
||||
} else {
|
||||
self.text_col += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn dimensions(&self) -> (u32, u32) {
|
||||
(self.screen_width, self.screen_height)
|
||||
}
|
||||
|
||||
pub const fn next_char(&mut self) {
|
||||
self.text_col += 1;
|
||||
}
|
||||
|
||||
pub const fn newline(&mut self) {
|
||||
self.text_col = 0;
|
||||
|
||||
if self.text_line + 1 >= self.screen_height {
|
||||
self.text_line = 0;
|
||||
} else {
|
||||
self.text_line += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the backspace character. TODO: Implement VT-100 style terminal
|
||||
/// control codes alongside a shell. Not simple.
|
||||
pub fn backspace(&mut self) {
|
||||
if self.text_col > 0 {
|
||||
self.text_col -= 1;
|
||||
// Blank out the previous char.
|
||||
self.write_glyph(b' ');
|
||||
self.text_col -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_string(&mut self, s: &str) {
|
||||
for c in s.chars() {
|
||||
self.write_glyph(c as u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn set_colour(&mut self, fg: Colour, bg: Colour) {
|
||||
self.fg_color = fg;
|
||||
self.bg_color = bg;
|
||||
}
|
||||
|
||||
pub const fn reset_colour(&mut self) {
|
||||
self.fg_color = Colour::White;
|
||||
self.bg_color = Colour::Black;
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
self.write_string(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write(args: fmt::Arguments, fg: Colour, bg: Colour) {
|
||||
use core::fmt::Write;
|
||||
|
||||
interrupts::without_interrupts(|| {
|
||||
let mut writer = WRITER.lock();
|
||||
writer.set_colour(fg, bg);
|
||||
writer.write_fmt(args).unwrap();
|
||||
writer.reset_colour();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn _print(args: fmt::Arguments) {
|
||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||
write(args, Colour::White, Colour::Black);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _print_err(args: fmt::Arguments) {
|
||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||
write(args, Colour::Red, Colour::Black);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn _print_log(args: fmt::Arguments) {
|
||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||
write(args, Colour::Yellow, Colour::Black);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear_screen() {
|
||||
interrupts::without_interrupts(|| {
|
||||
let mut writer = WRITER.lock();
|
||||
writer.text_line = 0;
|
||||
writer.text_col = 0;
|
||||
|
||||
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
||||
writer.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn reset_cursor() {
|
||||
interrupts::without_interrupts(|| {
|
||||
let mut writer = WRITER.lock();
|
||||
writer.text_line = 0;
|
||||
writer.text_col = 0;
|
||||
});
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println_log {
|
||||
() => ($crate::print_log!("\n"));
|
||||
($($arg:tt)*) => ($crate::print_log!("{}\n", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print_log {
|
||||
($($arg:tt)*) => ($crate::_print_log(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! printlnerr {
|
||||
() => ($crate::printerr!("\n"));
|
||||
($($arg:tt)*) => ($crate::printerr!("{}\n", format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! printerr {
|
||||
($($arg:tt)*) => ($crate::_print_err(format_args!($($arg)*)));
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
use limine::request::FramebufferRequest;
|
||||
|
||||
#[used]
|
||||
#[unsafe(link_section = ".requests")]
|
||||
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||
|
||||
use super::colour::Colour;
|
||||
use core::panic;
|
||||
|
||||
use limine::framebuffer::Framebuffer;
|
||||
use spin::{Lazy, Mutex};
|
||||
|
||||
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::new(|| {
|
||||
Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else(
|
||||
|| {
|
||||
panic!("Framebuffer request failed");
|
||||
},
|
||||
|framebuffer_response| {
|
||||
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
|
||||
Some(FramebufferWriter::new(framebuffer))
|
||||
},
|
||||
))
|
||||
});
|
||||
|
||||
/// The updated writer stores necessary fields from the [Framebuffer].
|
||||
/// This ensures that the contained types are Send, as Framebuffer was
|
||||
/// not marked as Send.
|
||||
///
|
||||
/// It also avoids the requirement for lifetimes.
|
||||
///
|
||||
/// Note this does not implement Writer as these functions only handle drawing pixels.
|
||||
pub struct FramebufferWriter {
|
||||
pitch: u64,
|
||||
bpp: u16,
|
||||
addr: *mut u8,
|
||||
width: u64,
|
||||
height: u64,
|
||||
}
|
||||
|
||||
unsafe impl Send for FramebufferWriter {}
|
||||
unsafe impl Sync for FramebufferWriter {}
|
||||
|
||||
impl FramebufferWriter {
|
||||
pub fn new(framebuffer: Framebuffer) -> Self {
|
||||
Self {
|
||||
pitch: framebuffer.pitch(),
|
||||
bpp: framebuffer.bpp(),
|
||||
addr: framebuffer.addr(),
|
||||
width: framebuffer.width(),
|
||||
height: framebuffer.height(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_pixel(&self, x: usize, y: usize, color: Colour) {
|
||||
let pitch = self.pitch as usize;
|
||||
let bpp = (self.bpp / 8) as usize;
|
||||
let pixel_offset = y * pitch + x * bpp;
|
||||
|
||||
unsafe {
|
||||
*(self.addr.add(pixel_offset) as *mut u32) = color.into();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_frame(&self, buffer: &[&[Colour]]) {
|
||||
// TODO: this should return errors
|
||||
for (y, &row) in buffer.iter().enumerate() {
|
||||
if y >= self.height() as usize {
|
||||
break;
|
||||
}
|
||||
for (x, pixel) in row.iter().enumerate() {
|
||||
if x >= self.width() as usize {
|
||||
break;
|
||||
}
|
||||
self.write_pixel(x, y, *pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn width(&self) -> u32 {
|
||||
self.width as u32
|
||||
}
|
||||
|
||||
pub const fn height(&self) -> u32 {
|
||||
self.height as u32
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
let width = self.width as usize;
|
||||
let height = self.height as usize;
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
self.write_pixel(x, y, Colour::Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn screensize_px() -> (u32, u32) {
|
||||
FRAMEBUFFER_WRITER
|
||||
.lock()
|
||||
.as_mut()
|
||||
.map_or_else(|| (0, 0), |writer| (writer.width(), writer.height()))
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub mod colour;
|
||||
pub mod display;
|
||||
@@ -1,7 +1,4 @@
|
||||
pub mod ascii;
|
||||
pub mod framebuffer;
|
||||
pub mod keyboard;
|
||||
pub mod port;
|
||||
pub mod serial;
|
||||
|
||||
// Re-exported macro definitions.
|
||||
|
||||
+6
-1
@@ -12,6 +12,7 @@
|
||||
)]
|
||||
|
||||
use core::arch::asm;
|
||||
use graphics::font::{FONT_SPLEEN_8X16, Font};
|
||||
use limine::BaseRevision;
|
||||
|
||||
mod arch;
|
||||
@@ -20,12 +21,16 @@ mod io;
|
||||
|
||||
/// Sets the base revision to the latest revision supported by the crate.
|
||||
/// See specification for further info.
|
||||
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
|
||||
/// Be sure to mark all limine requests with #[used], otherwise they may be
|
||||
/// removed by the compiler.
|
||||
#[used]
|
||||
// The .requests section allows limine to find the requests faster and more safely.
|
||||
#[unsafe(link_section = ".requests")]
|
||||
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
||||
|
||||
/// The default font used when setting up the framebuffer code.
|
||||
pub const DEFAULT_FONT: Font = FONT_SPLEEN_8X16;
|
||||
|
||||
#[panic_handler]
|
||||
fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
hcf();
|
||||
|
||||
+20
-5
@@ -1,3 +1,4 @@
|
||||
#![feature(proc_macro_span)]
|
||||
#![warn(
|
||||
clippy::correctness,
|
||||
clippy::nursery,
|
||||
@@ -12,18 +13,32 @@
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use std::path::PathBuf;
|
||||
use syn::{LitStr, parse_macro_input};
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
#[proc_macro]
|
||||
/// Expects the file path to be relative to the current file so this works
|
||||
/// similarly to the standard Rust include! macros.
|
||||
pub fn include_font(item: TokenStream) -> TokenStream {
|
||||
let filename = parse_macro_input!(item as LitStr);
|
||||
let file_path = filename.value();
|
||||
let span = Span::call_site();
|
||||
let source_file = span.source_file();
|
||||
|
||||
println!("Loading font: [{}]", file_path);
|
||||
if !source_file.is_real() {
|
||||
panic!(
|
||||
"We can't handle finding files if the source file does not exist. TODO: Can we?"
|
||||
)
|
||||
}
|
||||
|
||||
let filename = parse_macro_input!(item as LitStr);
|
||||
let source_filepath: PathBuf = source_file.path();
|
||||
let file_path = format!(
|
||||
"{}/{}",
|
||||
source_filepath.parent().unwrap_or_else(|| panic!("Expected to find the calling source file in a folder like src! Got: {}", source_filepath.display())).display(),
|
||||
filename.value()
|
||||
);
|
||||
|
||||
let font_bytes = match load_file(file_path) {
|
||||
Ok(bytes) => bytes,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
max_width = 85
|
||||
format_code_in_doc_comments = true
|
||||
comment_width = 80
|
||||
wrap_comments = true
|
||||
inline_attribute_width = 80
|
||||
Reference in New Issue
Block a user