Starts adding fontplate for VGA
This commit is contained in:
BIN
bench/programs_rust/fontplate.png
Normal file
BIN
bench/programs_rust/fontplate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -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"] }
|
||||
|
||||
106
bench/programs_rust/fpga-lib-macros/src/image.rs
Normal file
106
bench/programs_rust/fpga-lib-macros/src/image.rs
Normal file
@@ -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<Luma<u8>, Vec<u8>>, width: usize, height: usize) -> Vec<u8> {
|
||||
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<u8>, 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<Self> {
|
||||
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<proc_macro2::TokenStream>,
|
||||
),
|
||||
syn::Error,
|
||||
> {
|
||||
// parse the input into a comma separated list of arguments
|
||||
let parsed_args = syn::parse::<ParsedArgs>(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::<Vec<_>>();
|
||||
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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod register;
|
||||
pub mod vga;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! panic_handler {
|
||||
|
||||
48
bench/programs_rust/fpga-lib/src/vga.rs
Normal file
48
bench/programs_rust/fpga-lib/src/vga.rs
Normal file
@@ -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"};
|
||||
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user