Supressed clippy errors and add some rust docs
This commit is contained in:
Vendored
+2
@@ -6,4 +6,6 @@
|
|||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"rust-analyzer.check.command": "clippy",
|
"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]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.15"
|
version = "1.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
|
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -75,6 +75,7 @@ name = "foundry_os"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
|
"libm",
|
||||||
"limine",
|
"limine",
|
||||||
"pc-keyboard",
|
"pc-keyboard",
|
||||||
"pic8259",
|
"pic8259",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ x86_64 = "0.15.2"
|
|||||||
spin = "0.9.8"
|
spin = "0.9.8"
|
||||||
pic8259 = "0.11.0"
|
pic8259 = "0.11.0"
|
||||||
pc-keyboard = "0.8.0"
|
pc-keyboard = "0.8.0"
|
||||||
|
libm = { version = "0.1.0", path = "../libm" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.2.14"
|
cc = "1.2.14"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#![expect(unused)]
|
#![expect(unused)]
|
||||||
use core::arch::x86_64::__cpuid;
|
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::{
|
use x86_64::{
|
||||||
PhysAddr, VirtAddr,
|
PhysAddr, VirtAddr,
|
||||||
structures::paging::{Page, PageTableFlags, PhysFrame, Size4KiB},
|
structures::paging::{Page, PageTableFlags, PhysFrame, Size4KiB},
|
||||||
@@ -9,7 +10,7 @@ use x86_64::{
|
|||||||
|
|
||||||
// use crate::serial_print;
|
// 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: u32 = 0x1b;
|
||||||
const IA32_APIC_BASE_MSR_BSP: u64 = 0x100;
|
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.
|
//! Functions for IO using ports.
|
||||||
|
#![expect(unused)]
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
#[inline]
|
/// Take a byte in from a port.
|
||||||
pub fn inb(port: u16) -> u8 {
|
///
|
||||||
|
/// # 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;
|
let value: u8;
|
||||||
unsafe {
|
unsafe {
|
||||||
asm!(
|
asm!(
|
||||||
@@ -16,8 +21,13 @@ pub fn inb(port: u16) -> u8 {
|
|||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
/// Take a byte in from a port.
|
||||||
pub fn outb(port: u16, value: u8) {
|
///
|
||||||
|
/// # 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 {
|
unsafe {
|
||||||
asm!(
|
asm!(
|
||||||
"out dx, al",
|
"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);
|
// serial_println!("Exception: Breakpoint\n{:#?}", stack_frame);
|
||||||
// println_log!("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);
|
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::{HandleControl, Keyboard, ScancodeSet1, layouts};
|
||||||
// use pc_keyboard::DecodedKey;
|
// use pc_keyboard::DecodedKey;
|
||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
// use x86_64::instructions::port::Port;
|
// use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
static KEYBOARD: Lazy<Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>>> = Lazy::new(|| {
|
static KEYBOARD: Lazy<Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>>> =
|
||||||
Mutex::new(Keyboard::new(
|
Lazy::new(|| {
|
||||||
ScancodeSet1::new(),
|
Mutex::new(Keyboard::new(
|
||||||
layouts::Uk105Key,
|
ScancodeSet1::new(),
|
||||||
HandleControl::Ignore,
|
layouts::Uk105Key,
|
||||||
))
|
HandleControl::Ignore,
|
||||||
});
|
))
|
||||||
|
});
|
||||||
|
|
||||||
let _keyboard = KEYBOARD.lock();
|
let _keyboard = KEYBOARD.lock();
|
||||||
// let mut port = Port::new(0x60);
|
// 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 {
|
unsafe {
|
||||||
PICS.lock()
|
PICS.lock()
|
||||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[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 {
|
pub enum Colour {
|
||||||
ARGB(u8, u8, u8, u8),
|
Argb(u8, u8, u8, u8),
|
||||||
RGB(u8, u8, u8),
|
Rgb(u8, u8, u8),
|
||||||
HexARGB(u32),
|
HexARGB(u32),
|
||||||
Black = 0x000000FF,
|
Black = 0x000000FF,
|
||||||
Blue = 0x0000FFFF,
|
Blue = 0x0000FFFF,
|
||||||
@@ -14,14 +17,19 @@ pub enum Colour {
|
|||||||
White = 0xFFFFFFFF,
|
White = 0xFFFFFFFF,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::use_self)]
|
#[expect(
|
||||||
|
clippy::use_self,
|
||||||
|
reason = "Self is uglier and more verbose than `u32`"
|
||||||
|
)]
|
||||||
impl From<Colour> for u32 {
|
impl From<Colour> for u32 {
|
||||||
fn from(val: Colour) -> Self {
|
fn from(val: Colour) -> Self {
|
||||||
match val {
|
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)
|
(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::HexARGB(hex) => hex,
|
||||||
Colour::Black => 0xFF000000,
|
Colour::Black => 0xFF000000,
|
||||||
Colour::Blue => 0xFF0000FF,
|
Colour::Blue => 0xFF0000FF,
|
||||||
@@ -38,8 +46,10 @@ impl From<Colour> for u32 {
|
|||||||
impl core::fmt::Display for Colour {
|
impl core::fmt::Display for Colour {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a),
|
Self::Argb(r, g, b, a) => {
|
||||||
Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
|
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::HexARGB(hex) => write!(f, "Hex(#{:x})", hex),
|
||||||
Self::Black => write!(f, "Black"),
|
Self::Black => write!(f, "Black"),
|
||||||
Self::Blue => write!(f, "Blue"),
|
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 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 keyboard;
|
||||||
pub mod port;
|
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
|
|
||||||
// Re-exported macro definitions.
|
// Re-exported macro definitions.
|
||||||
|
|||||||
+6
-1
@@ -12,6 +12,7 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
use graphics::font::{FONT_SPLEEN_8X16, Font};
|
||||||
use limine::BaseRevision;
|
use limine::BaseRevision;
|
||||||
|
|
||||||
mod arch;
|
mod arch;
|
||||||
@@ -20,12 +21,16 @@ mod io;
|
|||||||
|
|
||||||
/// Sets the base revision to the latest revision supported by the crate.
|
/// Sets the base revision to the latest revision supported by the crate.
|
||||||
/// See specification for further info.
|
/// 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]
|
#[used]
|
||||||
// The .requests section allows limine to find the requests faster and more safely.
|
// The .requests section allows limine to find the requests faster and more safely.
|
||||||
#[unsafe(link_section = ".requests")]
|
#[unsafe(link_section = ".requests")]
|
||||||
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
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]
|
#[panic_handler]
|
||||||
fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
|
fn rust_panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
hcf();
|
hcf();
|
||||||
|
|||||||
+20
-5
@@ -1,3 +1,4 @@
|
|||||||
|
#![feature(proc_macro_span)]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::correctness,
|
clippy::correctness,
|
||||||
clippy::nursery,
|
clippy::nursery,
|
||||||
@@ -12,18 +13,32 @@
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::{Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use std::path::PathBuf;
|
||||||
use syn::{LitStr, parse_macro_input};
|
use syn::{LitStr, parse_macro_input};
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
#[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 {
|
pub fn include_font(item: TokenStream) -> TokenStream {
|
||||||
let filename = parse_macro_input!(item as LitStr);
|
let span = Span::call_site();
|
||||||
let file_path = filename.value();
|
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) {
|
let font_bytes = match load_file(file_path) {
|
||||||
Ok(bytes) => bytes,
|
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