1 Commits

Author SHA1 Message Date
nullndvoid 92fe618a99 Megacommit to move some memory code into kernel crate 2025-02-28 02:13:50 +00:00
16 changed files with 106 additions and 323 deletions
+22 -24
View File
@@ -1,19 +1,14 @@
#![allow(unused)] // TODO: Remove this when ready.
use core::arch::x86_64::__cpuid; use core::arch::x86_64::__cpuid;
use libk::drivers::memory::FoundryOSFrameAllocator;
use spin::Lazy;
use x86_64::{ use x86_64::{
PhysAddr, VirtAddr, PhysAddr, VirtAddr,
instructions::port::Port, structures::paging::{Mapper, Size4KiB},
registers::model_specific::Msr,
structures::paging::{
FrameAllocator, Mapper, Page, PageTableFlags, PhysFrame, Size4KiB, Translate,
},
}; };
use crate::serial_print; use crate::serial_print;
use super::{cpu::model_specific_registers::*, memmap::PHYSICAL_MEMORY_OFFSET}; use super::{cpu::model_specific_registers::*, mem::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;
@@ -68,27 +63,30 @@ pub fn check_apic() -> bool {
#[inline(always)] #[inline(always)]
unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr { unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr {
let phys = phys.as_u64(); let phys = phys.as_u64();
match phys.checked_add(*PHYSICAL_MEMORY_OFFSET) { phys.checked_add(*PHYSICAL_MEMORY_OFFSET).map_or_else(
Some(virt) => { || {
serial_print!("map worked!");
VirtAddr::new(virt)
}
None => {
serial_print!("THIS IS A PROBLEM"); serial_print!("THIS IS A PROBLEM");
panic!("overflow") panic!("overflow")
} },
} |virt| {
serial_print!("map worked!");
VirtAddr::new(virt)
},
)
} }
pub fn enable_apic( pub fn enable_apic(
mapper: &mut impl Mapper<Size4KiB>, _mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut FoundryOSFrameAllocator, // TODO: Fix this function.
// frame_allocator: &mut FoundryOSFrameAllocator,
) { ) {
let apic_phys_addr = get_apic_base(); unimplemented!();
set_apic_base_enable(apic_phys_addr);
// let apic_phys_addr = get_apic_base();
// set_apic_base_enable(apic_phys_addr);
// map virt address of apic // map virt address of apic
let apic_virt = unsafe { phys_to_virt(apic_phys_addr) }; // let apic_virt = unsafe { phys_to_virt(apic_phys_addr) };
// let page: Page<Size4KiB> = Page::containing_address(apic_virt); // let page: Page<Size4KiB> = Page::containing_address(apic_virt);
// let frame: PhysFrame<Size4KiB> = PhysFrame::containing_address(apic_phys_addr); // let frame: PhysFrame<Size4KiB> = PhysFrame::containing_address(apic_phys_addr);
// let flags: PageTableFlags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; // let flags: PageTableFlags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
@@ -103,11 +101,11 @@ pub fn enable_apic(
// // FIXME: this causes a page fault // // FIXME: this causes a page fault
// // TODO: map to virtual memor // // TODO: map to virtual memor
let reg = read_apic_register(&apic_virt, 0xF0); // let reg = read_apic_register(&apic_virt, 0xF0);
serial_print!("ok2"); // serial_print!("ok2");
write_apic_register(&apic_virt, 0xF0, reg | 0x100); // write_apic_register(&apic_virt, 0xF0, reg | 0x100);
} }
pub struct Apic {} pub struct Apic {}
+4 -4
View File
@@ -3,11 +3,11 @@ pub mod model_specific_registers {
use spin::Lazy; use spin::Lazy;
use x86_64::registers::model_specific::Msr; use x86_64::registers::model_specific::Msr;
const CPUID_FLAG_MSR: u32 = 1 << 5; const _CPUID_FLAG_MSR: u32 = 1 << 5;
static EDX: Lazy<u32> = Lazy::new(|| unsafe { __cpuid(1).edx }); static _EDX: Lazy<u32> = Lazy::new(|| unsafe { __cpuid(1).edx });
pub fn cpu_has_msr() -> bool { pub fn _cpu_has_msr() -> bool {
*EDX & CPUID_FLAG_MSR != 0 *_EDX & _CPUID_FLAG_MSR != 0
} }
pub fn cpu_get_msr(msr: u32, value: &mut u64) { pub fn cpu_get_msr(msr: u32, value: &mut u64) {
+21 -23
View File
@@ -1,14 +1,10 @@
use libk::drivers::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; // use libk::drivers::mem::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE};
use libk::prelude::*; use libk::prelude::*;
use pic8259::ChainedPics; use pic8259::ChainedPics;
use x86_64::registers::control::Cr2;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
use x86_64::structures::paging::mapper::MapperFlushAll;
use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB};
use spin::{Lazy, Mutex}; use spin::{Lazy, Mutex};
use super::apic::enable_apic;
use super::gdt; use super::gdt;
static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| { static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
@@ -64,6 +60,7 @@ pub fn enable_pic() {
} }
} }
#[expect(unused)]
pub fn disable_pic() { pub fn disable_pic() {
unsafe { unsafe {
PICS.lock().disable(); PICS.lock().disable();
@@ -125,31 +122,32 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr
} }
extern "x86-interrupt" fn page_fault_handler( extern "x86-interrupt" fn page_fault_handler(
stack_frame: InterruptStackFrame, _stack_frame: InterruptStackFrame,
error_code: PageFaultErrorCode, _error_code: PageFaultErrorCode,
) { ) {
todo!("Get this working again.")
// serial_println!("Exception: Page Fault"); // serial_println!("Exception: Page Fault");
// serial_println!("Accessed Address: {:?}", Cr2::read()); // serial_println!("Accessed Address: {:?}", Cr2::read());
// serial_println!("Error Code: {:?}", error_code); // serial_println!("Error Code: {:?}", error_code);
// serial_println!("{:#?}", stack_frame); // serial_println!("{:#?}", stack_frame);
if let Some(frame_allocator) = FRAME_ALLOCATOR.get() { // if let Some(frame_allocator) = FRAME_ALLOCATOR.get() {
let mut f = frame_allocator.lock(); // let mut f = frame_allocator.lock();
let frame = f.allocate_frame().unwrap(); // let frame = f.allocate_frame().unwrap();
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; // let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
let page: Page<Size4KiB> = Page::containing_address(Cr2::read().unwrap()); // let page: Page<Size4KiB> = Page::containing_address(Cr2::read().unwrap());
unsafe { // unsafe {
let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); // let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock();
match mapper.map_to(page, frame, flags, &mut *f) { // match mapper.map_to(page, frame, flags, &mut *f) {
Ok(_) => {} // Ok(_) => {}
Err(why) => panic!("failed to map page: {:?}", why), // Err(why) => panic!("failed to map page: {:?}", why),
} // }
} // }
MapperFlushAll::new().flush_all(); // MapperFlushAll::new().flush_all();
} else { // } else {
panic!("failed to get frame allocator"); // panic!("failed to get frame allocator");
} // }
} }
+2
View File
@@ -0,0 +1,2 @@
pub mod memmap;
pub mod pmm;
View File
+3 -7
View File
@@ -1,9 +1,5 @@
pub mod gdt;
pub mod interrupts;
pub mod memmap;
pub mod apic; pub mod apic;
pub mod cpu; pub mod cpu;
pub mod gdt;
pub mod interrupts;
pub mod mem;
+12 -13
View File
@@ -13,16 +13,14 @@
extern crate alloc; extern crate alloc;
use arch::x86_64::apic::enable_apic; // use arch::x86_64::apic::enable_apic;
use core::arch::asm; use core::arch::asm;
use libk::drivers::memory; // use libk::drivers::mem::pmm;
use limine::BaseRevision; use limine::BaseRevision;
use libk::drivers::kalloc::allocator::init_heap; // use libk::drivers::alloc::allocator::init_heap;
use libk::prelude::*; use libk::prelude::*;
use x86_64::VirtAddr;
mod arch; mod arch;
/// Sets the base revision to the latest revision supported by the crate. /// Sets the base revision to the latest revision supported by the crate.
@@ -58,9 +56,9 @@ pub fn boot() -> Result<(), &'static str> {
return Err("base revision not supported"); return Err("base revision not supported");
} }
use arch::x86_64::{gdt, interrupts, memmap}; use arch::x86_64::{gdt, interrupts, mem::memmap};
let memory_map = memmap::get_memory_map(); let _memory_map = memmap::get_memory_map();
print_log!(" Initialising Serial... "); print_log!(" Initialising Serial... ");
if libk::drivers::io::serial::init().is_err() { if libk::drivers::io::serial::init().is_err() {
@@ -78,18 +76,19 @@ pub fn boot() -> Result<(), &'static str> {
println_log!("[Success]"); println_log!("[Success]");
print_log!(" Initialising Memory Subsystem... "); print_log!(" Initialising Memory Subsystem... ");
let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET); // let physical_memory_offset = VirtAddr::new(*memmap::PHYSICAL_MEMORY_OFFSET);
let mut l4_table = memory::init_page_table(physical_memory_offset); // pmm::init_page_table(physical_memory_offset);
println_log!("[Success]"); println_log!("[Success]");
print_log!(" Setting Up Page Table... "); print_log!(" Setting Up Page Table... ");
memory::init_frame_allocator(memory_map); // pmm::init_frame_allocator(memory_map);
println_log!("[Success]"); println_log!("[Success]");
print_log!(" Initialising Heap... "); print_log!(" Initialising Heap... ");
if init_heap().is_err() { // TODO: Reenable the heap.
return Err("Failed to initialise heap: error"); // if init_heap().is_err() {
} // return Err("Failed to initialise heap: error");
// }
println_log!("[Success]"); println_log!("[Success]");
print_log!(" Enabling PICs... "); print_log!(" Enabling PICs... ");
+1 -1
View File
@@ -72,7 +72,7 @@ impl Writer {
} }
// Get the character data from the font array. -- each byte is a row of pixels // Get the character data from the font array. -- each byte is a row of pixels
let data: &[u8] = &self.font.0[c as usize]; let data: &[u8] = &self.font.glyphs[c as usize];
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() { if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
for (row, line) in data.iter().enumerate().take(16) { for (row, line) in data.iter().enumerate().take(16) {
-73
View File
@@ -1,73 +0,0 @@
use linked_list_allocator::LockedHeap;
use x86_64::{
VirtAddr,
structures::paging::{
FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB,
mapper::{MapToError, MapperFlushAll},
},
};
use crate::{
drivers::memory::{FRAME_ALLOCATOR, OFFSET_PAGE_TABLE},
serial_print, serial_println,
};
/// We are currently using a linked list heap allocator which uses our underlying page allocator.
pub type FoundryAllocator = LockedHeap;
#[global_allocator]
/// This is now Rust's global allocator, so we can use stuff requiring heap allocations.
static ALLOCATOR: FoundryAllocator = FoundryAllocator::empty();
pub const HEAP_START: usize = 0x4444_4444_0000;
pub const HEAP_SIZE: usize = 1024 * 1024 * 1024;
/// Sets up the heap using the backing page frame allocator.
pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
// let mut frame_allocator = if let Some(f) = FRAME_ALLOCATOR.get() {
// f.lock()
// } else {
// return Err(MapToError::FrameAllocationFailed);
// };
// let mut mapper = if let Some(m) = OFFSET_PAGE_TABLE.get() {
// m.lock()
// } else {
// return Err(MapToError::FrameAllocationFailed);
// };
// let range = {
// let heap_start = VirtAddr::new(HEAP_START as u64);
// let heap_end = heap_start + HEAP_SIZE as u64 - 1u64;
// let heap_start_page = Page::<Size4KiB>::containing_address(heap_start);
// let heap_end_page = Page::<Size4KiB>::containing_address(heap_end);
// Page::range_inclusive(heap_start_page, heap_end_page)
// };
// let usable_frames = frame_allocator.count_usable_frames();
// serial_println!("usable frames: {}", usable_frames);
// let mut i = 0;
// for page in range {
// i += 1;
// if i % 128 == 0 {
// serial_println!("allocated {} pages", i);
// }
// let frame = frame_allocator
// .allocate_frame()
// .ok_or(MapToError::FrameAllocationFailed)?;
// let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
// unsafe {
// // IMPORTANT: make sure to flush the mapper!!!!
// let _ = mapper.map_to(page, frame, flags, &mut *frame_allocator)?;
// }
// }
// MapperFlushAll::new().flush_all();
unsafe {
ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE);
}
Ok(())
}
-1
View File
@@ -1 +0,0 @@
pub mod allocator;
-149
View File
@@ -1,149 +0,0 @@
// use lib_alloc::allocator::FoundryAllocator;
use limine::{memory_map::EntryType, response::MemoryMapResponse};
use spin::{Mutex, Once};
use x86_64::{
PhysAddr, VirtAddr,
registers::control::Cr3,
structures::paging::{
FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
page_table::FrameError,
},
};
pub static FRAME_ALLOCATOR: Once<Mutex<FoundryOSFrameAllocator>> = Once::new();
pub static OFFSET_PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
/// Returns a mutable reference to the current level 4 page table.
///
/// # Safety
///
/// The caller must ensure that the level 4 page table is not modified
/// simultaneously. The caller must also ensure that the physical memory offset
/// is correct, to ensure that the correct virtual address is constructed.
unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
let (level_4_frame, _) = Cr3::read();
let phys_addr = level_4_frame.start_address();
let virt = phys_addr.as_u64() + physical_memory_offset.as_u64();
unsafe { &mut *(virt as *mut PageTable) }
}
/// Initializes the `OffsetPageTable` for the current CPU architecture.
///
/// # Safety
///
/// This function must be called only once and should be called before any
/// memory operations are performed that rely on virtual memory management.
/// The provided `physical_memory_offset` must be accurate to ensure correct
/// translation of physical addresses.
///
/// # Parameters
///
/// - `physical_memory_offset`: The offset to convert physical addresses to
/// virtual addresses in the higher-half direct map.
///
/// # Returns
///
/// Returns an `OffsetPageTable` that allows for manipulation of the page
/// tables for the current CPU architecture.
pub fn init_page_table(physical_memory_offset: VirtAddr) {
unsafe {
let l4_table = active_l4_table(physical_memory_offset);
let offset_table = OffsetPageTable::new(l4_table, physical_memory_offset);
OFFSET_PAGE_TABLE.call_once(|| Mutex::new(offset_table));
}
}
pub struct FoundryOSFrameAllocator {
memory_map: &'static MemoryMapResponse,
next: usize,
}
pub fn init_frame_allocator(memory_map: &'static MemoryMapResponse) {
unsafe {
FRAME_ALLOCATOR.call_once(|| Mutex::new(FoundryOSFrameAllocator::init(memory_map)));
}
}
impl FoundryOSFrameAllocator {
/// Creates a new `FoundryOSFrameAllocator` from a memory map.
///
/// This function takes a reference to a `MemoryMapResponse` and initializes a
/// `FoundryOSFrameAllocator` with it. The `next` field is set to 0, indicating that
/// the first frame to be allocated is the first frame in the memory map.
pub const unsafe fn init(memory_map: &'static MemoryMapResponse) -> Self {
Self {
memory_map,
next: 0,
}
}
pub fn count_usable_frames(&self) -> u32 {
self.usable_frames().count() as u32
}
/// An iterator over all usable frames in the memory map.
///
/// Yields one `PhysFrame` for each available 4KiB frame in the memory map.
///
/// This function is used to allocate frames for the pagemap.
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> + use<> {
let regions = self.memory_map.entries().iter();
let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE);
let addr_ranges = usable_regions.map(|region| region.base..region.base + region.length);
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
frame_addresses.map(|addr| PhysFrame::from_start_address(PhysAddr::new(addr)).unwrap())
}
}
unsafe impl FrameAllocator<Size4KiB> for FoundryOSFrameAllocator {
/// Allocates a frame from the list of usable frames.
///
/// This function returns the next available `PhysFrame` from the memory map,
/// if one exists. Once a frame is allocated, the internal counter is incremented
/// to point to the next frame for future allocations.
///
/// # Returns
///
/// - `Some(PhysFrame)`: If a usable frame is available.
/// - `None`: If there are no more usable frames to allocate.
fn allocate_frame(&mut self) -> Option<PhysFrame> {
let frame = self.usable_frames().nth(self.next);
self.next += 1;
frame
}
}
// pub unsafe fn translate_addr(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option<PhysAddr> {
// translate_addr_inner(addr, physical_memory_offset)
// }
// fn translate_addr_inner(addr: VirtAddr, physical_memory_offset: VirtAddr) -> Option<PhysAddr> {
// let (l4_table_frame, _) = Cr3::read();
// let table_indexes = [
// addr.p4_index(),
// addr.p3_index(),
// addr.p2_index(),
// addr.p1_index(),
// ];
// let mut frame = l4_table_frame;
// for &i in &table_indexes {
// let virt = physical_memory_offset + frame.start_address().as_u64();
// let table_ptr: *const PageTable = virt.as_ptr();
// let table = unsafe { &*table_ptr };
// let entry = &table[i];
// frame = match entry.frame() {
// Ok(frame) => frame,
// Err(FrameError::FrameNotPresent) => return None,
// Err(FrameError::HugeFrame) => panic!("huge frames are not supported!"),
// };
// }
// Some(frame.start_address() + u64::from(addr.page_offset()))
// }
+1 -4
View File
@@ -1,6 +1,3 @@
pub mod io;
pub mod kalloc;
pub mod async_io; pub mod async_io;
pub mod memory; pub mod io;
pub mod pic; pub mod pic;
+11 -17
View File
@@ -40,8 +40,8 @@ pub struct ChainedPics {
} }
impl ChainedPics { impl ChainedPics {
pub const unsafe fn new(offset1: u8, offset2: u8) -> Self { pub const fn new(offset1: u8, offset2: u8) -> Self {
ChainedPics { Self {
pics: [ pics: [
Pic { Pic {
offset: offset1, offset: offset1,
@@ -57,20 +57,14 @@ impl ChainedPics {
} }
} }
/// . pub const fn new_contiguous(primary_offset: u8) -> Self {
/// Self::new(primary_offset, primary_offset + 8)
/// # Safety
///
/// .
pub const unsafe fn new_contiguous(primary_offset: u8) -> Self {
unsafe { Self::new(primary_offset, primary_offset + 8) }
} }
/// Returns the initialize of this [`ChainedPics`].
///
/// # Safety /// # Safety
/// ///
/// . /// This should be safe if called just once on initialisation, however
/// sending data to IO ports tends to have side-effects.
pub unsafe fn initialize(&mut self) { pub unsafe fn initialize(&mut self) {
unsafe { unsafe {
let mut wait_port: Port<u8> = Port::new(0x80); let mut wait_port: Port<u8> = Port::new(0x80);
@@ -112,12 +106,12 @@ impl ChainedPics {
} }
/// Reads the interrupt masks of both PICs. /// Reads the interrupt masks of both PICs.
pub unsafe fn read_masks(&mut self) -> [u8; 2] { pub fn read_masks(&mut self) -> [u8; 2] {
unsafe { [self.pics[0].read_mask(), self.pics[1].read_mask()] } unsafe { [self.pics[0].read_mask(), self.pics[1].read_mask()] }
} }
/// Writes the interrupt masks of both PICs. /// Writes the interrupt masks of both PICs.
pub unsafe fn write_masks(&mut self, mask1: u8, mask2: u8) { pub fn write_masks(&mut self, mask1: u8, mask2: u8) {
unsafe { unsafe {
self.pics[0].write_mask(mask1); self.pics[0].write_mask(mask1);
self.pics[1].write_mask(mask2); self.pics[1].write_mask(mask2);
@@ -125,8 +119,8 @@ impl ChainedPics {
} }
/// Disables both PICs by masking all interrupts. /// Disables both PICs by masking all interrupts.
pub unsafe fn disable(&mut self) { pub fn disable(&mut self) {
unsafe { self.write_masks(u8::MAX, u8::MAX) } self.write_masks(u8::MAX, u8::MAX)
} }
/// Do we handle this interrupt? /// Do we handle this interrupt?
@@ -137,7 +131,7 @@ impl ChainedPics {
/// Figure out which (if any) PICs in our chain need to know about this /// Figure out which (if any) PICs in our chain need to know about this
/// interrupt. This is tricky, because all interrupts from `pics[1]` /// interrupt. This is tricky, because all interrupts from `pics[1]`
/// get chained through `pics[0]`. /// get chained through `pics[0]`.
pub unsafe fn notify_end_of_interrupt(&mut self, interrupt_id: u8) { pub fn notify_end_of_interrupt(&mut self, interrupt_id: u8) {
if self.handles_interrupt(interrupt_id) { if self.handles_interrupt(interrupt_id) {
if self.pics[1].handles_interrupt(interrupt_id) { if self.pics[1].handles_interrupt(interrupt_id) {
unsafe { unsafe {
+9
View File
@@ -36,3 +36,12 @@ pub use crate::drivers::io::{
ascii::{_print, _print_log}, ascii::{_print, _print_log},
serial::_serial_write, serial::_serial_write,
}; };
pub type FoundryAllocator = linked_list_allocator::LockedHeap;
#[global_allocator]
/// This is now Rust's global allocator, so we can use stuff requiring heap allocations.
static ALLOCATOR: FoundryAllocator = FoundryAllocator::empty();
pub const HEAP_START: usize = 0x4444_4444_0000;
pub const HEAP_SIZE: usize = 1024 * 1024 * 1024;
+20 -7
View File
@@ -1,13 +1,26 @@
// use libm::include_font;
use libm::include_font; use libm::include_font;
pub mod ibm_vga_8x16; pub mod ibm_vga_8x16;
pub static FONT_SPLEEN_8X16: Font = Font(include_font!( pub static FONT_SPLEEN_8X16: Font =
"./libk/resources/font/spleen-8x16.psf" Font::new(include_font!("./libk/resources/font/spleen-8x16.psf"));
));
pub static FONT_CP850_8X16: Font = Font(include_font!( pub static FONT_CP850_8X16: Font = Font::new(include_font!("./libk/resources/font/cp850-8x16.psf"));
"./libk/resources/font/cp850-8x16.psf"
));
pub struct Font(pub [[u8; 16]; 512]); pub struct Font {
pub glyphs: [[u8; 16]; 512],
}
impl Default for Font {
fn default() -> Self {
Self::new([[0; 16]; 512])
}
}
impl Font {
pub const fn new(glyphs: [[u8; 16]; 512]) -> Self {
Self { glyphs }
}
}