6 Commits
doom ... macros

Author SHA1 Message Date
97639138a1 macros and improvements 2025-11-04 14:49:47 -07:00
dd26e3528b gif responsive 2025-11-03 16:07:14 -07:00
74ec86da8f fix lock 2025-11-03 15:26:22 -07:00
cc463a34be fix alloc panics 2025-11-03 14:09:53 -07:00
355be5cde3 do batching tiles again in fb 2025-11-03 13:45:03 -07:00
1bbb892fcc remove unsafe fb 2025-11-03 10:56:29 -07:00
14 changed files with 209 additions and 259 deletions

53
Cargo.lock generated
View File

@@ -19,6 +19,7 @@ dependencies = [
"abi_sys",
"embedded-graphics",
"embedded-sdmmc",
"main_proc_macro",
"once_cell",
"rand_core 0.9.3",
]
@@ -350,7 +351,7 @@ checksum = "e37549a379a9e0e6e576fd208ee60394ccb8be963889eebba3ffe0980364f472"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -447,7 +448,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim 0.11.1",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -458,7 +459,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -496,7 +497,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -645,7 +646,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -1019,7 +1020,7 @@ checksum = "4f6e621fe4c7e05b695274b722dc0a60bacd1c8696b58191baa0154713d52400"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -1212,7 +1213,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -1648,6 +1649,14 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "main_proc_macro"
version = "0.1.0"
dependencies = [
"quote",
"syn 2.0.108",
]
[[package]]
name = "memchr"
version = "2.7.5"
@@ -1728,7 +1737,7 @@ checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -1920,7 +1929,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -1987,7 +1996,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -2001,9 +2010,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
@@ -2233,7 +2242,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -2400,7 +2409,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -2429,9 +2438,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.104"
version = "2.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
dependencies = [
"proc-macro2",
"quote",
@@ -2528,7 +2537,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -2539,7 +2548,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]
[[package]]
@@ -2607,7 +2616,7 @@ dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
"uuid",
]
@@ -2766,7 +2775,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
"wasm-bindgen-shared",
]
@@ -2788,7 +2797,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2938,5 +2947,5 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.108",
]

View File

@@ -8,6 +8,7 @@ members = [
"user-apps/snake",
"user-apps/gallery",
"user-apps/gif",
"main_proc_macro",
]
[profile.release]

View File

@@ -4,8 +4,9 @@ version = "0.1.0"
edition = "2024"
[dependencies]
embedded-sdmmc = { version = "0.9.0", default-features = false }
embedded-graphics = "0.8.1"
abi_sys = { path = "../abi_sys" }
main_proc_macro = { path = "../main_proc_macro" }
embedded-sdmmc = { version = "0.9", default-features = false }
embedded-graphics = "0.8.1"
once_cell = { version = "1", default-features = false }
rand_core = "0.9.3"

View File

@@ -5,10 +5,20 @@ extern crate alloc;
pub use abi_sys::{self, keyboard};
use abi_sys::{RngRequest, alloc, dealloc, keyboard::KeyEvent};
pub use alloc::format;
use core::alloc::{GlobalAlloc, Layout};
use alloc::format;
use core::{
alloc::{GlobalAlloc, Layout},
panic::PanicInfo,
};
pub use main_proc_macro::main;
use rand_core::RngCore;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
print!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[global_allocator]
static ALLOC: Alloc = Alloc;
@@ -27,7 +37,7 @@ unsafe impl GlobalAlloc for Alloc {
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {{
let s = $crate::format!($($arg)*);
let s = format!($($arg)*);
$crate::abi_sys::print(s.as_ptr(), s.len());
}};
}
@@ -46,7 +56,6 @@ pub fn get_key() -> KeyEvent {
pub mod display {
use abi_sys::CPixel;
use alloc::{vec, vec::Vec};
use embedded_graphics::{
Pixel,
geometry::{Dimensions, Point},
@@ -54,7 +63,6 @@ pub mod display {
prelude::{DrawTarget, Size},
primitives::Rectangle,
};
use once_cell::unsync::Lazy;
pub const SCREEN_WIDTH: usize = 320;
pub const SCREEN_HEIGHT: usize = 320;

View File

@@ -84,7 +84,7 @@ pub async fn display_handler(mut display: DISPLAY) {
FRAMEBUFFER
.as_mut()
.unwrap()
.safe_draw(&mut display)
.partial_draw(&mut display)
.await
.unwrap()
};

View File

@@ -1,6 +1,5 @@
use crate::display::{SCREEN_HEIGHT, SCREEN_WIDTH};
use core::sync::atomic::{AtomicBool, Ordering};
use embassy_sync::lazy_lock::LazyLock;
use embedded_graphics::{
draw_target::DrawTarget,
pixelcolor::{
@@ -14,32 +13,33 @@ use embedded_hal_2::digital::OutputPin;
use embedded_hal_async::{delay::DelayNs, spi::SpiDevice};
use st7365p_lcd::ST7365P;
pub const TILE_SIZE: usize = 16; // 16x16 tile
pub const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE_SIZE); // 400 tiles
const TILE_SIZE: usize = 16; // 16x16 tile
const TILE_COUNT: usize = (SCREEN_WIDTH / TILE_SIZE) * (SCREEN_HEIGHT / TILE_SIZE); // 400 tiles
const NUM_TILE_ROWS: usize = SCREEN_WIDTH / TILE_SIZE;
const NUM_TILE_COLS: usize = SCREEN_WIDTH / TILE_SIZE;
// Group of tiles for batching
pub const MAX_META_TILES: usize = (SCREEN_WIDTH / TILE_SIZE) * 2; // max number of meta tiles in buffer
type MetaTileVec = heapless::Vec<Rectangle, { TILE_COUNT / MAX_META_TILES }>;
const MAX_BATCH_TILES: usize = (SCREEN_WIDTH / TILE_SIZE) * 2;
type BatchTileBuf = [u16; MAX_BATCH_TILES * TILE_SIZE * TILE_SIZE];
pub const SIZE: usize = SCREEN_HEIGHT * SCREEN_WIDTH;
pub static FB_PAUSED: AtomicBool = AtomicBool::new(false);
static mut DIRTY_TILES: LazyLock<heapless::Vec<AtomicBool, TILE_COUNT>> = LazyLock::new(|| {
let mut tiles = heapless::Vec::new();
for _ in 0..TILE_COUNT {
tiles.push(AtomicBool::new(true)).unwrap();
}
tiles
});
#[allow(dead_code)]
pub struct AtomicFrameBuffer<'a>(&'a mut [u16]);
pub struct AtomicFrameBuffer<'a> {
fb: &'a mut [u16],
dirty_tiles: [AtomicBool; TILE_COUNT],
batch_tile_buf: BatchTileBuf,
}
impl<'a> AtomicFrameBuffer<'a> {
pub fn new(buffer: &'a mut [u16]) -> Self {
assert!(buffer.len() == SIZE);
Self(buffer)
Self {
fb: buffer,
dirty_tiles: core::array::from_fn(|_| AtomicBool::new(true)),
batch_tile_buf: [0; MAX_BATCH_TILES * TILE_SIZE * TILE_SIZE],
}
}
fn mark_tiles_dirty(&mut self, rect: Rectangle) {
@@ -52,7 +52,7 @@ impl<'a> AtomicFrameBuffer<'a> {
for ty in start_ty..=end_ty {
for tx in start_tx..=end_tx {
let tile_idx = ty * tiles_x + tx;
unsafe { DIRTY_TILES.get_mut()[tile_idx].store(true, Ordering::Release) };
self.dirty_tiles[tile_idx].store(true, Ordering::Release);
}
}
}
@@ -78,7 +78,7 @@ impl<'a> AtomicFrameBuffer<'a> {
for y in sy..=ey {
for x in sx..=ex {
if let Some(color) = color_iter.next() {
self.0[(y as usize * SCREEN_WIDTH) + x as usize] = color;
self.fb[(y as usize * SCREEN_WIDTH) + x as usize] = color;
} else {
return Err(()); // Not enough data
}
@@ -93,60 +93,17 @@ impl<'a> AtomicFrameBuffer<'a> {
Ok(())
}
// walk the dirty tiles and mark groups of tiles(meta-tiles) for batched updates
fn find_meta_tiles(&mut self, tiles_x: usize, tiles_y: usize) -> MetaTileVec {
let mut meta_tiles: MetaTileVec = heapless::Vec::new();
// Checks if a full draw would be faster than individual tile batches
fn should_full_draw(&self) -> bool {
let threshold_pixels = SIZE * 80 / 100;
let mut dirty_pixels = 0;
for ty in 0..tiles_y {
let mut tx = 0;
while tx < tiles_x {
let idx = ty * tiles_x + tx;
if !unsafe { DIRTY_TILES.get()[idx].load(Ordering::Acquire) } {
tx += 1;
continue;
}
// Start meta-tile at this tile
let mut width_tiles = 1;
let height_tiles = 1;
// Grow horizontally, but keep under MAX_TILES_PER_METATILE
while tx + width_tiles < tiles_x
&& unsafe {
DIRTY_TILES.get()[ty * tiles_x + tx + width_tiles].load(Ordering::Acquire)
}
&& (width_tiles + height_tiles) <= MAX_META_TILES
{
width_tiles += 1;
}
// TODO: for simplicity, skipped vertical growth
for x_off in 0..width_tiles {
unsafe {
DIRTY_TILES.get()[ty * tiles_x + tx + x_off]
.store(false, Ordering::Release);
};
}
// new meta-tile pos
let rect = Rectangle::new(
Point::new((tx * TILE_SIZE) as i32, (ty * TILE_SIZE) as i32),
Size::new(
(width_tiles * TILE_SIZE) as u32,
(height_tiles * TILE_SIZE) as u32,
),
);
if meta_tiles.push(rect).is_err() {
return meta_tiles;
};
tx += width_tiles;
self.dirty_tiles.iter().any(|tile| {
if tile.load(Ordering::Acquire) {
dirty_pixels += TILE_SIZE * TILE_SIZE;
}
}
meta_tiles
dirty_pixels >= threshold_pixels
})
}
/// Sends the entire framebuffer to the display
@@ -165,71 +122,40 @@ impl<'a> AtomicFrameBuffer<'a> {
0,
self.size().width as u16 - 1,
self.size().height as u16 - 1,
&self.0[..],
&self.fb[..],
)
.await?;
unsafe {
for tile in DIRTY_TILES.get_mut().iter() {
tile.store(false, Ordering::Release);
}
};
Ok(())
}
pub async fn safe_draw<SPI, DC, RST, DELAY>(
&mut self,
display: &mut ST7365P<SPI, DC, RST, DELAY>,
) -> Result<(), ()>
where
SPI: SpiDevice,
DC: OutputPin,
RST: OutputPin,
DELAY: DelayNs,
{
let tiles_x = SCREEN_WIDTH / TILE_SIZE;
let _tiles_y = SCREEN_HEIGHT / TILE_SIZE;
let tiles = unsafe { DIRTY_TILES.get_mut() };
let mut pixel_buffer: heapless::Vec<u16, { TILE_SIZE * TILE_SIZE }> = heapless::Vec::new();
for tile_idx in 0..TILE_COUNT {
if tiles[tile_idx].swap(false, Ordering::AcqRel) {
let tx = tile_idx % tiles_x;
let ty = tile_idx / tiles_x;
let x_start = tx * TILE_SIZE;
let y_start = ty * TILE_SIZE;
let x_end = (x_start + TILE_SIZE).min(SCREEN_WIDTH);
let y_end = (y_start + TILE_SIZE).min(SCREEN_HEIGHT);
pixel_buffer.clear();
for y in y_start..y_end {
let start = y * SCREEN_WIDTH + x_start;
let end = y * SCREEN_WIDTH + x_end;
pixel_buffer.extend_from_slice(&self.0[start..end]).unwrap();
}
display
.set_pixels_buffered(
x_start as u16,
y_start as u16,
(x_end - 1) as u16,
(y_end - 1) as u16,
&pixel_buffer,
)
.await
.unwrap();
}
for tile in self.dirty_tiles.iter() {
tile.store(false, Ordering::Release);
}
Ok(())
}
/// Sends only dirty tiles (16x16px) in batches to the display
// copy N tiles horizontally to the right into batch tile buf
fn append_tiles_to_batch(
&mut self,
tile_x: u16,
tile_y: u16,
total_tiles: u16, // number of tiles being written to buf
) {
debug_assert!(total_tiles as usize <= NUM_TILE_COLS);
for batch_row_num in 0..TILE_SIZE {
let batch_row_offset = batch_row_num * total_tiles as usize * TILE_SIZE;
let batch_row = &mut self.batch_tile_buf
[batch_row_offset..batch_row_offset + (total_tiles as usize * TILE_SIZE)];
let fb_row_offset = (tile_y as usize * TILE_SIZE + batch_row_num) * SCREEN_WIDTH
+ tile_x as usize * TILE_SIZE;
let fb_row =
&self.fb[fb_row_offset..fb_row_offset + (total_tiles as usize * TILE_SIZE)];
batch_row.copy_from_slice(fb_row);
}
}
// Pushes tiles to the display in batches to avoid full frame pushes (unless needed)
pub async fn partial_draw<SPI, DC, RST, DELAY>(
&mut self,
display: &mut ST7365P<SPI, DC, RST, DELAY>,
@@ -240,55 +166,53 @@ impl<'a> AtomicFrameBuffer<'a> {
RST: OutputPin,
DELAY: DelayNs,
{
if unsafe { DIRTY_TILES.get().iter().any(|p| p.load(Ordering::Acquire)) } {
let tiles_x = (SCREEN_WIDTH + TILE_SIZE - 1) / TILE_SIZE;
let tiles_y = (SCREEN_HEIGHT + TILE_SIZE - 1) / TILE_SIZE;
if self.should_full_draw() {
return self.draw(display).await;
}
let meta_tiles = self.find_meta_tiles(tiles_x, tiles_y);
for tile_row in 0..NUM_TILE_ROWS {
let row_start_idx = tile_row * NUM_TILE_COLS;
let mut col = 0;
// buffer for copying meta tiles before sending to display
let mut pixel_buffer: heapless::Vec<u16, { MAX_META_TILES * TILE_SIZE * TILE_SIZE }> =
heapless::Vec::new();
while col < NUM_TILE_COLS {
// Check for dirty tile
if self.dirty_tiles[row_start_idx + col].swap(false, Ordering::Acquire) {
let run_start = col;
let mut run_len = 1;
for rect in meta_tiles {
let rect_width = rect.size.width as usize;
let rect_height = rect.size.height as usize;
let rect_x = rect.top_left.x as usize;
let rect_y = rect.top_left.y as usize;
pixel_buffer.clear();
for row in 0..rect_height {
let y = rect_y + row;
let start = y * SCREEN_WIDTH + rect_x;
let end = start + rect_width;
// Safe: we guarantee buffer will not exceed MAX_META_TILE_PIXELS
pixel_buffer.extend_from_slice(&self.0[start..end]).unwrap();
}
display
.set_pixels_buffered(
rect_x as u16,
rect_y as u16,
(rect_x + rect_width - 1) as u16,
(rect_y + rect_height - 1) as u16,
&pixel_buffer,
)
.await?;
// walk the meta-tile and set as clean
let start_tx = rect_x / TILE_SIZE;
let start_ty = rect_y / TILE_SIZE;
let end_tx = (rect_x + rect_width - 1) / TILE_SIZE;
let end_ty = (rect_y + rect_height - 1) / TILE_SIZE;
for ty in start_ty..=end_ty {
for tx in start_tx..=end_tx {
let tile_idx = ty * tiles_x + tx;
unsafe { DIRTY_TILES.get_mut()[tile_idx].store(false, Ordering::Release) };
// Extend run while contiguous dirty tiles and within MAX_BATCH_TILES
while col + 1 < NUM_TILE_COLS
&& self.dirty_tiles[row_start_idx + col + 1].load(Ordering::Acquire)
&& run_len < MAX_BATCH_TILES
{
col += 1;
run_len += 1;
}
// Copy the whole horizontal run into the batch buffer in one call
let tile_x = run_start;
let tile_y = tile_row;
self.append_tiles_to_batch(tile_x as u16, tile_y as u16, run_len as u16);
// Compute coordinates for display write
let start_x = tile_x * TILE_SIZE;
let end_x = start_x + run_len * TILE_SIZE - 1;
let start_y = tile_y * TILE_SIZE;
let end_y = start_y + TILE_SIZE - 1;
// Send batch to display
display
.set_pixels_buffered(
start_x as u16,
start_y as u16,
end_x as u16,
end_y as u16,
&self.batch_tile_buf[..run_len * TILE_SIZE * TILE_SIZE],
)
.await?;
}
col += 1;
}
}
@@ -309,14 +233,14 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
for Pixel(coord, color) in pixels {
if coord.x >= 0 && coord.y >= 0 {
let x = coord.x as i32;
let y = coord.y as i32;
let x = coord.x;
let y = coord.y;
if (x as usize) < SCREEN_WIDTH && (y as usize) < SCREEN_HEIGHT {
let idx = (y as usize) * SCREEN_WIDTH + (x as usize);
let raw_color = RawU16::from(color).into_inner();
if self.0[idx] != raw_color {
self.0[idx] = raw_color;
if self.fb[idx] != raw_color {
self.fb[idx] = raw_color;
changed = true;
}
@@ -365,8 +289,8 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
if let Some(color) = colors.next() {
let idx = (p.y as usize * SCREEN_WIDTH) + (p.x as usize);
let raw_color = RawU16::from(color).into_inner();
if self.0[idx] != raw_color {
self.0[idx] = raw_color;
if self.fb[idx] != raw_color {
self.fb[idx] = raw_color;
changed = true;
}
} else {
@@ -404,7 +328,7 @@ impl<'a> DrawTarget for AtomicFrameBuffer<'a> {
.take((self.size().width * self.size().height) as usize),
)?;
for tile in unsafe { DIRTY_TILES.get_mut() }.iter() {
for tile in self.dirty_tiles.iter() {
tile.store(true, Ordering::Release);
}

View File

@@ -12,8 +12,10 @@ mod abi;
mod display;
mod elf;
mod framebuffer;
#[allow(unused)]
mod heap;
mod peripherals;
#[allow(unused)]
mod psram;
mod scsi;
mod storage;
@@ -225,6 +227,7 @@ struct Sd {
cs: Peri<'static, PIN_17>,
det: Peri<'static, PIN_22>,
}
#[allow(dead_code)]
struct Psram {
pio: Peri<'static, PIO0>,
sclk: Peri<'static, PIN_21>,
@@ -318,7 +321,7 @@ async fn kernel_task(
watchdog: Peri<'static, WATCHDOG>,
display: Display,
sd: Sd,
psram: Psram,
_psram: Psram,
mcu: Mcu,
usb: Peri<'static, USB>,
) {
@@ -328,10 +331,14 @@ async fn kernel_task(
setup_mcu(mcu).await;
defmt::info!("setting up psram");
Timer::after_millis(100).await;
// setup_psram(psram).await;
#[cfg(feature = "pimoroni2w")]
setup_qmi_psram().await;
Timer::after_millis(100).await;
setup_display(display, spawner).await;
setup_sd(sd).await;

View File

@@ -549,13 +549,13 @@ pub fn init_psram_qmi(
let min_deselect: u32 = ((18 * 1_000_000 + (clock_period_fs - 1)) / clock_period_fs
- u64::from(divisor + 1) / 2) as u32;
#[cfg(feature = "defmt")]
defmt::info!(
"clock_period_fs={} max_select={} min_deselect={}",
clock_period_fs,
max_select,
min_deselect
);
// #[cfg(feature = "defmt")]
// defmt::info!(
// "clock_period_fs={} max_select={} min_deselect={}",
// clock_period_fs,
// max_select,
// min_deselect
// );
qmi.direct_csr().write(|w| {
w.set_clkdiv(10);

View File

@@ -0,0 +1,11 @@
[package]
name = "main_proc_macro"
version = "0.1.0"
edition = "2024"
[lib]
proc-macro = true
[dependencies]
quote = "1.0.41"
syn = "2.0.108"

View File

@@ -0,0 +1,21 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, parse_macro_input};
#[proc_macro_attribute]
pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let name = &input.sig.ident;
// ensure we emit _start in the same module as the fn
let expanded = quote! {
#input
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
#name();
}
};
expanded.into()
}

View File

@@ -9,7 +9,6 @@ use abi::{
print,
};
use alloc::{format, string::String, vec, vec::Vec};
use core::panic::PanicInfo;
use embedded_graphics::{
Drawable,
geometry::{Dimensions, Point},
@@ -26,12 +25,6 @@ use embedded_layout::{
prelude::Chain,
};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
print!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()

View File

@@ -8,27 +8,16 @@ use abi::{
fs::{list_dir, read_file},
get_key,
keyboard::{KeyCode, KeyState},
print,
main, print,
};
use alloc::{format, string::ToString, vec};
use core::panic::PanicInfo;
use embedded_graphics::{
Drawable, image::Image, mono_font::MonoTextStyle, mono_font::ascii::FONT_6X10,
pixelcolor::Rgb565, prelude::*, text::Text,
};
use tinybmp::Bmp;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
print!("user panic: {} @ {:?}", info.message(), info.location());
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
#[main]
pub fn main() {
print!("Starting Gallery app");
let mut bmp_buf = vec![0_u8; 100_000];

View File

@@ -7,26 +7,15 @@ use abi::{
fs::{file_len, read_file},
get_key, get_ms,
keyboard::{KeyCode, KeyState},
print, sleep,
main, print, sleep,
};
use alloc::vec;
use core::panic::PanicInfo;
use alloc::{format, vec};
use embedded_graphics::{
image::ImageDrawable, pixelcolor::Rgb565, prelude::Point, transform::Transform,
};
use tinygif::Gif;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
print!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
#[main]
pub fn main() {
print!("Starting Gif app");
let mut display = Display;
@@ -51,11 +40,14 @@ pub fn main() {
.unwrap();
frame_num += 1;
if frame_num % 100 == 0 {
if frame_num % 5 == 0 {
let event = get_key();
if event.state != KeyState::Idle {
match event.key {
KeyCode::Esc => return,
KeyCode::Esc => {
drop(buf);
return;
}
_ => (),
};
};

View File

@@ -9,16 +9,10 @@ use abi::{
keyboard::{KeyCode, KeyState},
print, sleep,
};
use core::panic::PanicInfo;
use alloc::format;
use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor};
use embedded_snake::{Direction, SnakeGame};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
print!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()