diff --git a/bench/programs_rust/fontplate.png b/bench/programs_rust/fontplate.png new file mode 100644 index 0000000..7120f47 Binary files /dev/null and b/bench/programs_rust/fontplate.png differ diff --git a/bench/programs_rust/fpga-lib-macros/Cargo.toml b/bench/programs_rust/fpga-lib-macros/Cargo.toml index e793a03..34a7a98 100644 --- a/bench/programs_rust/fpga-lib-macros/Cargo.toml +++ b/bench/programs_rust/fpga-lib-macros/Cargo.toml @@ -7,6 +7,8 @@ edition = "2024" proc-macro = true [dependencies] +image = "0.25.5" +regex = "1.11.1" proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } diff --git a/bench/programs_rust/fpga-lib-macros/src/image.rs b/bench/programs_rust/fpga-lib-macros/src/image.rs new file mode 100644 index 0000000..63106d2 --- /dev/null +++ b/bench/programs_rust/fpga-lib-macros/src/image.rs @@ -0,0 +1,106 @@ +use image::{ImageBuffer, Luma}; +use proc_macro::{Span, TokenStream}; +use quote::quote; +use regex::Regex; +use syn::parse::Parse; + +fn remove_non_alphanumeric(input: &str) -> String { + let re = Regex::new(r"[^a-zA-Z0-9_]+").unwrap(); + re.replace_all(input, "").to_string() +} + +fn to_format(img: ImageBuffer, Vec>, width: usize, height: usize) -> Vec { + let mut output = Vec::new(); + + let mut bit: u8 = 0; + let mut byte: u8 = 0; + for y in 0..height { + for x in 0..width { + let pixel = img.get_pixel(x as u32, y as u32)[0]; + if pixel >= 127 { + byte |= 1 << bit; + } + bit += 1; + if bit == 8 { + output.push(byte); + byte = 0; + bit = 0; + } + } + } + if bit != 0 { + output.push(byte); + } + output +} + +fn path_to_image(path: &str) -> (Vec, String, usize, usize) { + let img = match image::open(path) { + Ok(img) => img.to_luma8(), + Err(e) => panic!("failed to open image {}: {}", path, e), + }; + + let width = img.width() as usize; + let height = img.height() as usize; + + let bytes = to_format(img, width, height); + + let path = path + .split('/') + .next_back() + .expect("failed to get last part of path"); + let split: Vec<_> = path.split('.').collect(); + let name = remove_non_alphanumeric(&split[0..split.len() - 1].join(".")).to_uppercase(); + + (bytes, name, width, height) +} + +struct ParsedArgs { + path: String, +} + +impl Parse for ParsedArgs { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let path: syn::LitStr = input.parse()?; + let path = path.value(); + Ok(ParsedArgs { path }) + } +} + +pub fn parse_image( + input: TokenStream, +) -> Result< + ( + u8, + u8, + proc_macro2::Ident, + usize, + Vec, + ), + syn::Error, +> { + // parse the input into a comma separated list of arguments + let parsed_args = syn::parse::(input)?; + // let parsed_args = parse_macro_input!(input as ParsedArgs); + let (bytes, name, width, height) = path_to_image(&parsed_args.path); + + let width = width as u8; + let height = height as u8; + + let byte_array = bytes.as_slice(); + let byte_count = byte_array.len(); + + let name_ident = syn::Ident::new(&name, Span::call_site().into()); + + let byte_tokens = bytes.iter().map(|b| quote! { #b }).collect::>(); + Ok((width, height, name_ident, byte_count, byte_tokens)) +} + +pub fn include_font_plate_impl(input: TokenStream) -> TokenStream { + let (_, _, _, _, byte_tokens) = parse_image(input).unwrap(); + + let output = quote! { + [#(#byte_tokens),*] + }; + output.into() +} diff --git a/bench/programs_rust/fpga-lib-macros/src/lib.rs b/bench/programs_rust/fpga-lib-macros/src/lib.rs index 470238d..59ba7d7 100644 --- a/bench/programs_rust/fpga-lib-macros/src/lib.rs +++ b/bench/programs_rust/fpga-lib-macros/src/lib.rs @@ -1,4 +1,5 @@ -extern crate proc_macro; +mod image; + use proc_macro::TokenStream; use quote::{format_ident, quote}; @@ -32,3 +33,8 @@ pub fn define_regs(_input: TokenStream) -> TokenStream { tokens.into() } + +#[proc_macro] +pub fn include_font_plate(input: TokenStream) -> TokenStream { + image::include_font_plate_impl(input) +} diff --git a/bench/programs_rust/fpga-lib/src/lib.rs b/bench/programs_rust/fpga-lib/src/lib.rs index 9622770..7c3af85 100644 --- a/bench/programs_rust/fpga-lib/src/lib.rs +++ b/bench/programs_rust/fpga-lib/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] pub mod register; +pub mod vga; #[macro_export] macro_rules! panic_handler { diff --git a/bench/programs_rust/fpga-lib/src/vga.rs b/bench/programs_rust/fpga-lib/src/vga.rs new file mode 100644 index 0000000..1957f2d --- /dev/null +++ b/bench/programs_rust/fpga-lib/src/vga.rs @@ -0,0 +1,48 @@ +use fpga_lib_macros::include_font_plate; + +pub const VGA_ADDRESS: *mut Color = 0x80000000 as *mut Color; +pub const WIDTH: usize = 320; +pub const HEIGHT: usize = 240; + +#[repr(transparent)] +pub struct Color(pub(crate) u8); + +impl Color { + pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { + Self(((r >> 6) << 6) | ((g >> 5) << 3) | (b >> 5)) + } +} + +pub const WHITE: Color = Color::from_rgb(255, 255, 255); +pub const GRAY: Color = Color::from_rgb(128, 128, 128); +pub const LIGHT_GRAY: Color = Color::from_rgb(128, 64, 64); +pub const BLACK: Color = Color::from_rgb(0, 0, 0); +pub const RED: Color = Color::from_rgb(255, 0, 0); +pub const GREEN: Color = Color::from_rgb(0, 255, 0); +pub const BLUE: Color = Color::from_rgb(0, 0, 255); + +pub struct VGA {} + +impl VGA { + /// # Safety + /// `x` must be less than `WIDTH` and `y` must be less than `HEIGHT` + pub unsafe fn write_pixel_unsafe(x: u16, y: u16, color: Color) { + unsafe { *VGA_ADDRESS.add(x as usize + y as usize * WIDTH) = color } + } + + pub unsafe fn draw_char(x: u16, y: u16, c: char) { + let c = c as u8 - b' '; + for i in 0..FONT_HEIGHT { + for j in 0..FONT_WIDTH { + + } + } + } +} + +pub const FONT_WIDTH: usize = 6; +pub const FONT_HEIGHT: usize = 13; +pub const FONTPLATE_WIDTH: usize = 32 * FONT_WIDTH; +pub const FONTPLATE_HEIGHT: usize = 3 * FONT_HEIGHT; +pub const FONTPLATE_SIZE: usize = FONTPLATE_WIDTH * FONTPLATE_HEIGHT / 8; +pub const FONTPLATE: [u8; FONTPLATE_SIZE] = include_font_plate! {"fontplate.png"}; diff --git a/bench/programs_rust/hello-rust/src/main.rs b/bench/programs_rust/hello-rust/src/main.rs index 3e21312..2e71d73 100644 --- a/bench/programs_rust/hello-rust/src/main.rs +++ b/bench/programs_rust/hello-rust/src/main.rs @@ -3,7 +3,11 @@ use core::arch::asm; -use fpga_lib::{panic_handler, register::X31}; +use fpga_lib::{ + panic_handler, + register::X31, + vga::{VGA, WHITE}, +}; panic_handler! {} @@ -17,9 +21,7 @@ pub extern "C" fn _start() -> ! { "add x31, x0, {}", in(reg) test ); - const VGA: *mut u8 = 0x80000000 as *mut u8; - VGA.write_volatile(255); - // VGA.add(1).write_volatile(255); + VGA::write_pixel_unsafe(0, 0, WHITE); } loop {}