From 7bc23ce2111d1fd26b06a208351a7a5d76abd42f Mon Sep 17 00:00:00 2001 From: supersurviveur Date: Thu, 13 Nov 2025 10:26:37 +0100 Subject: [PATCH] Starts adding fontplate for VGA --- bench/programs_rust/fontplate.png | Bin 0 -> 1664 bytes .../programs_rust/fpga-lib-macros/Cargo.toml | 2 + .../fpga-lib-macros/src/image.rs | 106 ++++++++++++++++++ .../programs_rust/fpga-lib-macros/src/lib.rs | 8 +- bench/programs_rust/fpga-lib/src/lib.rs | 1 + bench/programs_rust/fpga-lib/src/vga.rs | 48 ++++++++ bench/programs_rust/hello-rust/src/main.rs | 10 +- 7 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 bench/programs_rust/fontplate.png create mode 100644 bench/programs_rust/fpga-lib-macros/src/image.rs create mode 100644 bench/programs_rust/fpga-lib/src/vga.rs diff --git a/bench/programs_rust/fontplate.png b/bench/programs_rust/fontplate.png new file mode 100644 index 0000000000000000000000000000000000000000..7120f476040e9611b95f3297aeba5cb4565e3c83 GIT binary patch literal 1664 zcmV-`27md9P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007 zbV*G`2k8q92@ex=(r>r`00qQJL_t(|+U;HKj@%#&+^q8ckDT;RsVwS`8Dj{0w@6iW z2?1klW1E3JUa!~vBFb?b=DYi*nsVD>+DF=9Y5SJnQ4UW5Ji)rI>-B!W|NY;D4EZK{ z7QI8i>FY)GdCGz+tEZ&>yZgS)ajYsmO{`J+^l~DVJb~+(rzu8j!xB(WG0?s>I@#|l z1&Mv%_|xb{42M>~f37LLsNbTF~3nesONB9*DFm;Ysj+l*5viySi-MO zsK)GQ(3Yk1Xtd~l!dh8liugLRk|Xs-h55QJllLC~k$#cYjdFn)rO>RABnXWI-{LSV--)lm9<0>D-__jFYDhjSFxorV`L5S089l~Dbyq2v!6csyYod`Su(;Z z<0KjIddUMTvm=GS2W$4_j4XPTov@;QtA3QnwOkjgEeneh^{u@_F*Y)*!(ejy)MLa? zw!X(7B%9*V&pdB@@#=eu&8FRDd;PpbS))^nkfQ-;Oh@aZ+x;k*DY7yi0~o+rw9NKx zAQ`C&ej1FiyWjC4jmI09xG8Qci8R}Q!WbK<#b zvn?$vxM)1^bkNN1&@ZPiit06-#iuXtt1w55yO-tjN(`0f>a4(X%|O(?wP2yjYK#Pz zJ1E#L2V+paRnm(j53JxkqGKl+I4#X$n2q?L7AUG`7ILN_6B~Gwl!-A=BcTEN(0*A^ zU?qrcg+gce$!3JY_FfxogndhGcIs)IR;E9>5f%X^<#}q!mwwk6q4hY9;~pLKS6P03 zSq1jfc7mC@Y@Fa?fihbC#RxqsK%?`vBh+FhO=T`nX4%~ZQ94S;PEcs%EJfBm`AM#F z8`C>8iKE#D_84NT-*6s1zGal2=1)rBQoB!Qr>szwFsnK%{gjb);n1kN zlD!MkvYsB?*Sg>-LyvchLUwhqa`Yo`ars=kxj|crBK}zXoCSwQp06@QunU9;fG1=u zBfJSf@gosEk?Z2UV_hTdW($-*T7B!zP=@~+jWY@hdYf@ys5gI}Yd`gJ+?6QKUE_}x zWmhyP%jfJBD%OrULeII&tJ+DCt 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 {}