This commit is contained in:
2025-11-02 19:29:17 -07:00
parent 957189cd0b
commit e966763fed
12 changed files with 228 additions and 149 deletions

116
user-apps/gboy/src/main.rs Normal file
View File

@@ -0,0 +1,116 @@
#![no_std]
#![no_main]
#![allow(static_mut_refs)]
extern crate alloc;
use abi::{
Rng,
display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH},
fs::{file_len, read_file},
get_key,
keyboard::{KeyCode, KeyState},
print, sleep,
};
use alloc::{vec, vec::Vec};
use core::{cell::LazyCell, ffi::c_void, mem::MaybeUninit, panic::PanicInfo};
use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor};
mod peanut;
use peanut::gb_run_frame;
use crate::peanut::{
gb_cart_ram_read, gb_cart_ram_write, gb_error, gb_init, gb_init_lcd, gb_rom_read, gb_s,
lcd_draw_line,
};
static mut DISPLAY: Display = Display;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
print!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
const PEANUT_A: u8 = 0x01;
const PEANUT_B: u8 = 0x02;
const PEANUT_SELECT: u8 = 0x04;
const PEANUT_START: u8 = 0x08;
const PEANUT_RIGHT: u8 = 0x10;
const PEANUT_LEFT: u8 = 0x20;
const PEANUT_UP: u8 = 0x40;
const PEANUT_DOWN: u8 = 0x80;
const GAME: &'static str = "/games/gameboy/zelda.gb";
static mut GAME_ROM: Option<Vec<u8>> = None;
#[repr(C)]
struct Priv {}
pub fn main() {
print!("Starting Gameboy app");
let size = file_len(GAME);
unsafe { GAME_ROM = Some(vec![0_u8; size]) };
let read = read_file(GAME, 0, unsafe { GAME_ROM.as_mut().unwrap() });
assert!(size == read);
print!("Rom size: {}", read);
let mut priv_ = MaybeUninit::<Priv>::uninit();
let mut gb = MaybeUninit::<gb_s>::uninit();
let init_status = unsafe {
gb_init(
gb.as_mut_ptr(),
Some(gb_rom_read),
Some(gb_cart_ram_read),
Some(gb_cart_ram_write),
Some(gb_error),
priv_.as_mut_ptr() as *mut c_void,
)
};
print!("gb init status: {}", init_status);
unsafe { gb_init_lcd(gb.as_mut_ptr(), Some(lcd_draw_line)) };
loop {
let event = get_key();
if event.state != KeyState::Idle {
match event.key {
KeyCode::Esc => return,
KeyCode::Tab => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_START;
},
KeyCode::Del => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_SELECT;
},
KeyCode::Enter => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_A;
},
KeyCode::Backspace => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_B;
},
KeyCode::JoyUp => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_UP;
},
KeyCode::JoyDown => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_DOWN;
},
KeyCode::JoyLeft => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_LEFT;
},
KeyCode::JoyRight => unsafe {
(*gb.as_mut_ptr()).direct.__bindgen_anon_1.joypad &= !PEANUT_RIGHT;
},
_ => (),
};
};
unsafe { gb_run_frame(gb.as_mut_ptr()) };
}
}

View File

@@ -0,0 +1,85 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::{DISPLAY, GAME_ROM};
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
use abi::{display::Pixel565, fs::read_file};
use embedded_graphics::{Drawable, pixelcolor::Rgb565, prelude::Point};
pub const GBOY_WIDTH: usize = 160;
pub const GBOY_HEIGHT: usize = 144;
pub unsafe extern "C" fn gb_rom_read(gb: *mut gb_s, addr: u32) -> u8 {
unsafe { GAME_ROM.as_ref().unwrap()[addr as usize] }
}
pub unsafe extern "C" fn gb_cart_ram_read(gb: *mut gb_s, addr: u32) -> u8 {
0
}
pub unsafe extern "C" fn gb_cart_ram_write(gb: *mut gb_s, addr: u32, val: u8) {}
pub unsafe extern "C" fn gb_error(gb: *mut gb_s, err: gb_error_e, addr: u16) {}
const NUM_PALETTES: usize = 3;
const SHADES_PER_PALETTE: usize = 4;
const PALETTES: [[Rgb565; SHADES_PER_PALETTE]; NUM_PALETTES] = [
[
Rgb565::new(8, 24, 32),
Rgb565::new(52, 104, 86),
Rgb565::new(136, 192, 112),
Rgb565::new(224, 248, 208),
], // BG
[
Rgb565::new(8, 24, 32),
Rgb565::new(52, 104, 86),
Rgb565::new(136, 192, 112),
Rgb565::new(224, 248, 208),
], // OBJ0
[
Rgb565::new(8, 24, 32),
Rgb565::new(52, 104, 86),
Rgb565::new(136, 192, 112),
Rgb565::new(224, 248, 208),
], // OBJ1
];
pub unsafe extern "C" fn lcd_draw_line(_gb: *mut gb_s, pixels: *const u8, line: u8) {
if line < GBOY_HEIGHT as u8 {
let pixels = unsafe { core::slice::from_raw_parts(pixels, GBOY_WIDTH) };
let y = line as u16;
for (x, &p) in pixels.iter().enumerate() {
let palette_idx = ((p & 0xF0) >> 4) as usize;
let shade_idx = (p & 0x03) as usize;
let color = PALETTES
.get(palette_idx)
.and_then(|pal| pal.get(shade_idx))
.copied()
.unwrap_or(Rgb565::new(0, 0, 0));
let sx = (x as u16) * 2;
let sy = y * 2;
draw_color(color, sx, sy);
draw_color(color, sx + 1, sy);
draw_color(color, sx, sy + 1);
draw_color(color, sx + 1, sy + 1);
}
}
}
fn draw_color(color: Rgb565, x: u16, y: u16) {
let mut pixel = Pixel565::default();
pixel.0 = Point::new(x.into(), y.into());
pixel.1 = color;
unsafe {
pixel.draw(&mut DISPLAY).unwrap();
}
}