More progress; writing to EGA text framebuffer now works.

This commit is contained in:
Arthur Beck 2025-01-25 10:37:46 -06:00
parent 461136c643
commit bc19d1a69d
10 changed files with 148 additions and 35 deletions

View file

@ -1,5 +1,5 @@
# config.aphro for aphrodite devel-b3558c2-out-of-tree
CFG_VERSION=devel-b3558c2-out-of-tree
# config.aphro for aphrodite devel-461136c-out-of-tree
CFG_VERSION=devel-461136c-out-of-tree
CONT_WITH_DIFFERENT_VERSION=false
# Begin metadata
@ -17,9 +17,9 @@ CONFIG_DISABLE_MULTIBOOT2_SUPPORT=false
CONFIG_PREUSER_HALT_ON_PANIC=true
CONFIG_PREUSER_SPIN_ON_PANIC=false
CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH=false
CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH=true
CONFIG_PREUSER_WARN_ON_INVALID_LENGTH=true
CONFIG_PREUSER_EXIT_LOOP_ON_INVALID_LENGTH=true
CONFIG_PREUSER_PANIC_ON_INVALID_LENGTH=false
CONFIG_PREUSER_WARN_ON_INVALID_LENGTH=false
CONFIG_PREUSER_ERROR_ON_INVALID_LENGTH=true
# Whether to output various levels of messages.

10
kernel/get_version Executable file
View file

@ -0,0 +1,10 @@
set -e
DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/functions"
get_version
echo $VERSION

Binary file not shown.

View file

@ -1,8 +1,8 @@
set timeout=15
set default=0
menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-devel-b3558c2-out-of-tree' {
echo 'Loading Aphrodite aphrodite-devel-b3558c2-out-of-tree ...'
menuentry "Aphrodite" --class aphrodite --class kernel --class os $menuentry_id_option 'aphrodite-basic-devel-461136c-out-of-tree' {
echo 'Loading Aphrodite aphrodite-devel-461136c-out-of-tree ...'
multiboot2 /boot/aphrodite.kernel
boot
}

Binary file not shown.

View file

@ -0,0 +1,71 @@
//! Stuff for writing and reading to the EGA text buffer.
#![cfg(any(target_arch = "x86"))]
/// Information about the framebuffer.
#[derive(Clone, Copy)]
pub struct FramebufferInfo {
/// A pointer to the framebuffer.
pub address: u64,
/// The pitch of the framebuffer (i.e. the number of bytes in each row).
pub pitch: u32,
/// The width of the framebuffer.
pub width: u32,
/// The height of the framebuffer.
pub height: u32,
/// Bits per pixel.
pub bpp: u8,
}
/// Returned when the provided position is invalid in the X direction.
pub const ERR_INVALID_X: i16 = -1;
/// Returned when the provided position is invalid in the Y direction.
pub const ERR_INVALID_Y: i16 = -2;
/// White text on a black background.
pub const WHITE_ON_BLACK: u8 = 0b00000111;
/// Black text on a black background.
pub const BLACK_ON_BLACK: u8 = 0b00000000;
impl FramebufferInfo {
/// Writes a character to the screen.
pub fn write_char(self, pos: (u32, u32), char: u8, color: u8) -> Result<(), crate::Error<'static>> {
if pos.0>self.width {
return Err(crate::Error::new("Invalid X position", ERR_INVALID_X));
}
if pos.1>self.height {
return Err(crate::Error::new("Invalid Y position", ERR_INVALID_Y));
}
unsafe {
let mut addr = self.address as usize;
addr += (pos.1*self.pitch) as usize;
addr += (pos.0*(self.bpp as u32/8)) as usize;
let base_ptr = addr as *mut u16;
(*base_ptr) = ((color as u16)<<8) | (char as u16);
}
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(self, pos: (u32, u32), str: &str, color: u8) -> Result<(), crate::Error<'static>> {
let (mut x, mut y) = pos;
for char in str.as_bytes() {
self.write_char((x, y), *char, color)?;
x += 1;
if x>self.width {
x -= self.width;
y += 1;
}
}
Ok(())
}
}

View file

@ -6,6 +6,7 @@ use core::arch::asm;
pub mod interrupts;
pub mod ports;
pub mod output;
pub mod egatext;
mod constants;

View file

@ -6,6 +6,13 @@ pub struct Error<'a> {
code: i16
}
impl<'a> Error<'a> {
/// Creates a new error.
pub fn new(message: &'a str, code: i16) -> Self {
Error { message, code }
}
}
impl core::fmt::Debug for Error<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(core::str::from_utf8(&crate::i16_as_u8_slice(self.code)).unwrap())?;

View file

@ -26,7 +26,6 @@ impl core::ops::Index<usize> for CString {
/// Used for Multiboot2 tags. This shouldn't be used after a [BootInfo] struct has been initalized, but it still can be used.
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone)]
pub struct Tag {
/// The type of the tag.
@ -37,7 +36,6 @@ pub struct Tag {
/// The root tag. The official Multiboot2 name is literally the "fixed part" of the tags, so I made a better name.
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone)]
pub struct RootTag {
/// The total length between the root tag and the terminating tag.
@ -61,7 +59,6 @@ pub struct Module {
/// One memory section provided by a Multiboot2 bootloader.
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone)]
pub struct MemorySection {
/// The base address of the section.
@ -78,7 +75,6 @@ pub struct MemorySection {
/// The raw memory map provided by a Multiboot2 bootloader. This is interpreted
/// into a [MemoryMap].
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
pub struct RawMemoryMap {
/// The type of the tag.
pub tag_type: u32,
@ -107,7 +103,6 @@ pub struct MemoryMap {
/// A color descriptor for [ColorInfo::Palette].
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone, Copy)]
pub struct PaletteColorDescriptor {
/// The red value
@ -120,7 +115,6 @@ pub struct PaletteColorDescriptor {
/// Information about color, for use in [FramebufferInfo].
#[repr(u8)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone, Copy)]
pub enum ColorInfo {
/// The palette for use on the framebuffer.
@ -153,15 +147,10 @@ pub enum ColorInfo {
/// Information about the framebuffer.
#[repr(C)]
#[repr(align(1))] // may or may not be necessary, but I'm not taking chances
#[derive(Clone)]
pub struct FramebufferInfo {
/// The raw pointer to the string.
pub ptr: *const u8,
/// The length of the string, excluding the null byte(\0) at the end.
pub len: usize,
/// A pointer to the framebuffer.
pub address: *mut u8,
pub address: u64,
/// The pitch of the framebuffer (i.e. the number of bytes in each row).
pub pitch: u32,
/// The width of the framebuffer.
@ -175,7 +164,7 @@ pub struct FramebufferInfo {
/// Reserved space. Ignore.
reserved: u8,
// Color info after this; we need separate structs for each colorinfo and
// Color info after this; we need separate structs for each colorinfo as
// we have to understand the format the bootloader gives us.
}

View file

@ -10,6 +10,8 @@
use core::{arch::asm, ffi::CStr, panic::PanicInfo};
use aphrodite::multiboot2::{BootInfo, CString, ColorInfo, FramebufferInfo, MemoryMap, PaletteColorDescriptor, RawMemoryMap, RootTag, Tag};
use aphrodite::arch::x86::output::*;
use aphrodite::arch::x86::egatext as egatext;
use egatext::*;
#[cfg(not(CONFIG_DISABLE_MULTIBOOT2_SUPPORT))]
#[unsafe(link_section = ".multiboot2")]
@ -161,33 +163,33 @@ extern "C" fn _start() -> ! {
// ...before the BootInfo's bootloader_name is set.
},
8 => { // Framebuffer info
if current_tag.tag_len < 31 { // Unexpected size, something is probably up
panic!("size of framebuffer info tag < 31");
if current_tag.tag_len < 32 { // Unexpected size, something is probably up
panic!("size of framebuffer info tag < 32");
}
let framebufferinfo: *const FramebufferInfo = ptr as *const FramebufferInfo;
let framebufferinfo: *const FramebufferInfo = (ptr as usize + size_of::<Tag>()) as *const FramebufferInfo;
let colorinfo: ColorInfo;
match (*framebufferinfo).fb_type {
0 => { // Indexed
colorinfo = ColorInfo::Palette {
num_colors: *((ptr + 40) as *const u32),
palette: (ptr + 44) as *const PaletteColorDescriptor
num_colors: *((ptr + 32) as *const u32),
palette: (ptr + 36) as *const PaletteColorDescriptor
};
},
1 => { // RGB
colorinfo = ColorInfo::RGBColor {
red_field_position: *((ptr + 40) as *const u8),
red_mask_size: *((ptr + 41) as *const u8),
green_field_position: *((ptr + 42) as *const u8),
green_mask_size: *((ptr + 43) as *const u8),
blue_field_position: *((ptr + 44) as *const u8),
blue_mask_size: *((ptr + 45) as *const u8)
red_field_position: *((ptr + 32) as *const u8),
red_mask_size: *((ptr + 33) as *const u8),
green_field_position: *((ptr + 34) as *const u8),
green_mask_size: *((ptr + 35) as *const u8),
blue_field_position: *((ptr + 36) as *const u8),
blue_mask_size: *((ptr + 37) as *const u8)
}
},
2 => { // EGA Text
colorinfo = ColorInfo::EGAText;
},
_ => {
unreachable!();
panic!("unknown color info type")
}
}
BI.framebuffer_info = Some((*framebufferinfo).clone());
@ -233,6 +235,8 @@ extern "C" fn _start() -> ! {
unsafe {
if BI.framebuffer_info.clone().is_some() {
let framebuffer_info = BI.framebuffer_info.clone().unwrap();
let color_info = BI.color_info.clone().unwrap();
sdebugs("Framebuffer width: ");
sdebugbnpln(&aphrodite::u32_as_u8_slice(framebuffer_info.width));
sdebugs("Framebuffer height: ");
@ -244,9 +248,40 @@ extern "C" fn _start() -> ! {
sdebugs("Framebuffer bpp: ");
sdebugbnpln(&aphrodite::u8_as_u8_slice(framebuffer_info.bpp));
sdebugs("Framebuffer type: ");
sdebugbnpln(&aphrodite::u8_as_u8_slice(framebuffer_info.fb_type));
sdebugs("Framebuffer length: ");
sdebugbnpln(&aphrodite::usize_as_u8_slice(framebuffer_info.len));
sdebugbnp(&aphrodite::u8_as_u8_slice(framebuffer_info.fb_type));
match framebuffer_info.fb_type {
0 => { // Indexed
sdebugsnpln("(Indexed)");
let ColorInfo::Palette{num_colors, palette: _} = color_info else { unreachable!() };
sdebugs("Number of palette colors: ");
sdebugbnpln(&aphrodite::u32_as_u8_slice(num_colors));
},
1 => { // RGB
sdebugsnpln("(RGB)");
},
2 => { // EGA Text
sdebugsnpln("(EGA Text)");
sdebugsln("Attempting to output to screen(will then loop for 100000000 cycles)...");
let ega = egatext::FramebufferInfo {
address: framebuffer_info.address,
pitch: framebuffer_info.pitch,
width: framebuffer_info.width,
height: framebuffer_info.height,
bpp: framebuffer_info.bpp
};
ega.clear_screen(BLACK_ON_BLACK);
ega.write_str((0, 0), "Test", WHITE_ON_BLACK).unwrap();
for _ in 0..100000000 {
asm!("nop")
}
},
_ => {
unreachable!();
}
}
}
}