mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 07:45:28 +00:00
runnintg
This commit is contained in:
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +1,6 @@
|
||||
[submodule "user-apps/nes/Peanut-GB"]
|
||||
path = user-apps/nes/Peanut-GB
|
||||
url = https://github.com/deltabeard/Peanut-GB
|
||||
[submodule "picolibc"]
|
||||
path = picolibc
|
||||
url = https://github.com/picolibc/picolibc
|
||||
[submodule "user-apps/gboy/Peanut-GB"]
|
||||
path = user-apps/gboy/Peanut-GB
|
||||
url = https://github.com/deltabeard/Peanut-GB
|
||||
|
||||
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -1306,6 +1306,16 @@ dependencies = [
|
||||
"tinybmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gboy"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi",
|
||||
"bindgen",
|
||||
"cc",
|
||||
"embedded-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -1753,16 +1763,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||
|
||||
[[package]]
|
||||
name = "nes"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi",
|
||||
"bindgen",
|
||||
"cc",
|
||||
"embedded-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
|
||||
@@ -8,7 +8,7 @@ members = [
|
||||
"user-apps/snake",
|
||||
"user-apps/gallery",
|
||||
"user-apps/gif",
|
||||
"user-apps/nes",
|
||||
"user-apps/gboy",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
||||
4
justfile
4
justfile
@@ -1,5 +1,7 @@
|
||||
kernel-dev board:
|
||||
cargo run --bin kernel --features {{board}}
|
||||
kernel-release-probe board:
|
||||
cargo run --bin kernel --profile release --features {{board}}
|
||||
kernel-release board:
|
||||
cargo build --bin kernel --release --no-default-features --features {{board}}
|
||||
elf2uf2-rs -d target/thumbv8m.main-none-eabihf/release/kernel
|
||||
@@ -37,4 +39,4 @@ userapps: cbindgen
|
||||
just userapp snake
|
||||
just userapp gallery
|
||||
just userapp gif
|
||||
just userapp nes
|
||||
just userapp gboy
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "nes"
|
||||
name = "gboy"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
@@ -47,6 +47,15 @@ fn bindgen() {
|
||||
cc::Build::new()
|
||||
.file("peanut_gb_stub.c")
|
||||
.include("Peanut-GB")
|
||||
// optimization flags
|
||||
.flag("-O3") // max optimization
|
||||
.flag("-ffast-math") // faster floating point, if any
|
||||
.flag("-funroll-loops") // unroll small loops
|
||||
// CPU/architecture flags (example for ARM Cortex-M33)
|
||||
.flag("-mcpu=cortex-m33")
|
||||
.flag("-mthumb")
|
||||
// optional: strip debug info
|
||||
.flag("-g0")
|
||||
.compile("peanut_gb");
|
||||
|
||||
println!("cargo:rustc-link-search=Peanut-GB");
|
||||
116
user-apps/gboy/src/main.rs
Normal file
116
user-apps/gboy/src/main.rs
Normal 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()) };
|
||||
}
|
||||
}
|
||||
85
user-apps/gboy/src/peanut.rs
Normal file
85
user-apps/gboy/src/peanut.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#![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::{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 GAME: &'static str = "/games/nes/game.nes";
|
||||
|
||||
#[repr(C)]
|
||||
struct Priv {
|
||||
rom: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
print!("Starting Nes app");
|
||||
|
||||
let size = file_len(GAME);
|
||||
let mut priv_ = MaybeUninit::<Priv>::uninit();
|
||||
// let read = unsafe {
|
||||
// let priv_ptr = priv_.as_mut_ptr();
|
||||
// (*priv_ptr).rom = Vec::with_capacity(size);
|
||||
|
||||
// read_file(GAME, 0, &mut (*priv_ptr).rom)
|
||||
// };
|
||||
|
||||
// print!("read: {}, file size: {}", read, size);
|
||||
// assert!(read == size);
|
||||
|
||||
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 {
|
||||
let key = match event.key {
|
||||
KeyCode::Esc => return,
|
||||
_ => (),
|
||||
};
|
||||
};
|
||||
|
||||
unsafe { gb_run_frame(gb.as_mut_ptr()) };
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::DISPLAY;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
||||
use abi::{display::Pixel565, fs::read_file};
|
||||
use embedded_graphics::{Drawable, pixelcolor::Rgb565, prelude::Point};
|
||||
|
||||
pub const NES_WIDTH: usize = 256;
|
||||
pub const NES_HEIGHT: usize = 240;
|
||||
|
||||
pub unsafe extern "C" fn gb_rom_read(gb: *mut gb_s, addr: u32) -> u8 {
|
||||
let mut buf = [0_u8; 1];
|
||||
read_file("/games/nes/rom.nes", addr as usize, &mut buf);
|
||||
buf[0]
|
||||
}
|
||||
|
||||
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) {}
|
||||
|
||||
pub unsafe extern "C" fn lcd_draw_line(gb: *mut gb_s, pixels: *const u8, line: u8) {
|
||||
unsafe {
|
||||
if line < NES_HEIGHT as u8 {
|
||||
let pixels = core::slice::from_raw_parts(pixels, NES_WIDTH);
|
||||
let mut x = 0;
|
||||
for p in pixels {
|
||||
let mut pixel = Pixel565::default();
|
||||
pixel.0 = Point::new(x, line.into());
|
||||
pixel.1 = Rgb565::new(*p, 10, 10);
|
||||
|
||||
pixel.draw(&mut DISPLAY).unwrap();
|
||||
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user