mirror of
https://github.com/LegitCamper/picocalc-os-rs.git
synced 2025-12-27 15:55:25 +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"]
|
[submodule "picolibc"]
|
||||||
path = picolibc
|
path = picolibc
|
||||||
url = https://github.com/picolibc/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",
|
"tinybmp",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gboy"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"abi",
|
||||||
|
"bindgen",
|
||||||
|
"cc",
|
||||||
|
"embedded-graphics",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -1753,16 +1763,6 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nes"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"abi",
|
|
||||||
"bindgen",
|
|
||||||
"cc",
|
|
||||||
"embedded-graphics",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "new_debug_unreachable"
|
name = "new_debug_unreachable"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ members = [
|
|||||||
"user-apps/snake",
|
"user-apps/snake",
|
||||||
"user-apps/gallery",
|
"user-apps/gallery",
|
||||||
"user-apps/gif",
|
"user-apps/gif",
|
||||||
"user-apps/nes",
|
"user-apps/gboy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|||||||
4
justfile
4
justfile
@@ -1,5 +1,7 @@
|
|||||||
kernel-dev board:
|
kernel-dev board:
|
||||||
cargo run --bin kernel --features {{board}}
|
cargo run --bin kernel --features {{board}}
|
||||||
|
kernel-release-probe board:
|
||||||
|
cargo run --bin kernel --profile release --features {{board}}
|
||||||
kernel-release board:
|
kernel-release board:
|
||||||
cargo build --bin kernel --release --no-default-features --features {{board}}
|
cargo build --bin kernel --release --no-default-features --features {{board}}
|
||||||
elf2uf2-rs -d target/thumbv8m.main-none-eabihf/release/kernel
|
elf2uf2-rs -d target/thumbv8m.main-none-eabihf/release/kernel
|
||||||
@@ -37,4 +39,4 @@ userapps: cbindgen
|
|||||||
just userapp snake
|
just userapp snake
|
||||||
just userapp gallery
|
just userapp gallery
|
||||||
just userapp gif
|
just userapp gif
|
||||||
just userapp nes
|
just userapp gboy
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nes"
|
name = "gboy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@@ -47,6 +47,15 @@ fn bindgen() {
|
|||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.file("peanut_gb_stub.c")
|
.file("peanut_gb_stub.c")
|
||||||
.include("Peanut-GB")
|
.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");
|
.compile("peanut_gb");
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=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