From 116abff1222f2c984616dc7b4c4ed4814d880fdc Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 21 Aug 2023 01:33:08 +0300 Subject: [PATCH] Rpi baremetal pause menu * Add pause menu for baremetal rpi * Add BCM power peripheral * Use cross main image - https://github.com/cross-rs/cross/issues/1320 * Replace the rpi features flags with compile env var --- Cargo.lock | 136 +++++++++++++++++--------- Cargo.toml | 4 +- Cross.toml | 4 +- Makefile.toml | 2 +- README.md | 2 +- common/src/emulation_menu.rs | 98 ------------------- common/src/joypad_menu/mod.rs | 6 +- common/src/lib.rs | 2 +- common/src/menu.rs | 103 +++++++++++++++++++ core/src/utils/stack_string.rs | 21 +++- rpi/Cargo.toml | 3 - rpi/build.rs | 20 ++-- rpi/src/bin/baremetal/main.rs | 62 +++++++++--- rpi/src/bin/rpios/main.rs | 6 +- rpi/src/drivers/gpio_joypad.rs | 7 +- rpi/src/drivers/ili9341_gfx_device.rs | 56 +++++++---- rpi/src/lib.rs | 8 +- rpi/src/peripherals/emmc.rs | 23 ++--- rpi/src/peripherals/gpio.rs | 33 +++++-- rpi/src/peripherals/gpu.rs | 14 +-- rpi/src/peripherals/mailbox.rs | 23 ++++- rpi/src/peripherals/mod.rs | 14 ++- rpi/src/peripherals/power.rs | 92 +++++++++++++++++ rpi/src/peripherals/utils.rs | 21 +++- sdl/src/main.rs | 2 +- 25 files changed, 504 insertions(+), 258 deletions(-) delete mode 100644 common/src/emulation_menu.rs create mode 100644 common/src/menu.rs create mode 100644 rpi/src/peripherals/power.rs diff --git a/Cargo.lock b/Cargo.lock index c5bfbd9b..75704014 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -773,7 +773,7 @@ dependencies = [ [[package]] name = "magenboy_common" -version = "3.0.2" +version = "4.0.0" dependencies = [ "cc", "cfg-if", @@ -788,7 +788,7 @@ dependencies = [ [[package]] name = "magenboy_core" -version = "3.0.2" +version = "4.0.0" dependencies = [ "criterion", "image", @@ -799,7 +799,7 @@ dependencies = [ [[package]] name = "magenboy_rpi" -version = "3.0.2" +version = "4.0.0" dependencies = [ "bitfield-struct", "cfg-if", @@ -814,7 +814,7 @@ dependencies = [ [[package]] name = "magenboy_sdl" -version = "3.0.2" +version = "4.0.0" dependencies = [ "cfg-if", "crossbeam-channel", @@ -873,25 +873,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", - "miow", - "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -933,15 +922,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - [[package]] name = "num-integer" version = "0.1.44" @@ -1564,9 +1544,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.20.4" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb78f30e4b41e98ca4cce5acb51168a033839a7af9e42b380355808e14e98ee0" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -1576,7 +1556,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "socket2", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -1853,13 +1833,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -1868,7 +1848,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -1877,13 +1866,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1892,42 +1896,84 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "winreg" version = "0.10.1" diff --git a/Cargo.toml b/Cargo.toml index 8f1e0656..54dff7b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,6 @@ members = [ ] [workspace.package] -version = "3.0.2" +version = "4.0.0" authors = ["alloncm "] -rust-version = "1.65" # cause of let else feature \ No newline at end of file +rust-version = "1.70" # cause of once cell \ No newline at end of file diff --git a/Cross.toml b/Cross.toml index 51c78fda..3383183e 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,5 +1,7 @@ - [target.armv7-unknown-linux-gnueabihf] +# Using the main image cause I had problems with the image if version 0.2.5, when 0.3.0 will be released this should sort out +# see - https://github.com/cross-rs/cross/issues/1320 +image = "ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:main" pre-build = [ "echo deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi >> /etc/apt/sources.list", "touch /etc/apt/sources.list.d/raspi.list", diff --git a/Makefile.toml b/Makefile.toml index da2f8867..0e1c1e90 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -47,7 +47,7 @@ dependencies = ["build_rpi_baremetal","install_llvm_tools"] toolchain = "nightly" cwd = "./rpi/" command = "cargo" -args = ["build", "--release", "--target", "armv7a-none-eabihf","--package", "magenboy_rpi", "--bin", "baremetal", "-Z", "build-std=core", "--no-default-features", "--features", "rpi4"] +args = ["build", "--release", "--target", "armv7a-none-eabihf","--package", "magenboy_rpi", "--bin", "baremetal", "-Z", "build-std=core"] dependencies = ["install_rust_src"] [tasks.install_llvm_tools] diff --git a/README.md b/README.md index e8cae599..56d1a1a4 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ cargo build --release --package magenboy_sdl --features [optional_features] Edit the relevant settings in `configuration.rs` install [`arm-none-eabi-gcc`](https://developer.arm.com/downloads/-/gnu-rm) and then run: ```sh -cargo make rpibm +cargo make -e [rpi_revision] rpibm ``` This command will do the folowing: diff --git a/common/src/emulation_menu.rs b/common/src/emulation_menu.rs deleted file mode 100644 index 12d66d5b..00000000 --- a/common/src/emulation_menu.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::{sync::{atomic::AtomicBool, Mutex}, path::PathBuf}; -use magenboy_core::{ppu::gfx_device::GfxDevice, keypad::joypad_provider::JoypadProvider}; - -use crate::joypad_menu::MenuRenderer; - -use super::joypad_menu::{MenuOption, MenuJoypadProvider, joypad_gfx_menu, JoypadMenu}; - -enum EmulatorMenuOption{ - Resume, - Restart, - Shutdown -} - -const GAME_MENU_OPTIONS:[MenuOption;3] = [ - MenuOption{prompt:"Resume", value:EmulatorMenuOption::Resume}, - MenuOption{prompt:"Restart", value:EmulatorMenuOption::Restart}, - MenuOption{prompt:"Shutdown", value:EmulatorMenuOption::Shutdown} -]; - -pub struct MagenBoyState{ - // Use atomic bool, normal bool doesnt works on arm (probably cause of the memory model) - pub running:AtomicBool, - pub pause:AtomicBool, - pub exit:AtomicBool, - pub state_mutex:Mutex<()> -} - -impl MagenBoyState{ - pub const fn new() -> Self { - Self { running: AtomicBool::new(true), pause: AtomicBool::new(false), exit: AtomicBool::new(false), state_mutex: Mutex::new(()) } - } -} - -pub struct MagenBoyMenu{ - header:String, - provider:JP, -} - -impl MagenBoyMenu { - pub fn new(provider:JP, header:String)->Self{ - Self { provider, header } - } - - pub fn pop_game_menu(&mut self, state:&MagenBoyState, gfx_device:&mut GFX, receiver:crossbeam_channel::Receiver){ - match self.get_game_menu_selection(state, gfx_device, receiver){ - EmulatorMenuOption::Resume => {}, - EmulatorMenuOption::Restart => state.running.store(false, std::sync::atomic::Ordering::Relaxed), - EmulatorMenuOption::Shutdown => { - state.running.store(false, std::sync::atomic::Ordering::Relaxed); - state.exit.store(true, std::sync::atomic::Ordering::Relaxed); - }, - } - } - - fn get_game_menu_selection(&mut self, state:&MagenBoyState,gfx_device:&mut GFX, emulation_framebuffer_channel:crossbeam_channel::Receiver)->&EmulatorMenuOption{ - let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(gfx_device); - - let mut menu = JoypadMenu::new(&GAME_MENU_OPTIONS, &self.header, menu_renderer); - - // lock the mutex here to sync the 2 threads - state.pause.store(true, std::sync::atomic::Ordering::SeqCst); - loop{ - if let Ok(_lock) = state.state_mutex.try_lock(){ - let selection = menu.get_menu_selection(&mut self.provider); - state.pause.store(false, std::sync::atomic::Ordering::SeqCst); - return selection; - }else{ - // try recv in order to clear frames from the channel - // in order to not block the emualtion thread and allow it to finish the frame - let _ = emulation_framebuffer_channel.try_recv(); - } - } - } - -} - -pub fn get_rom_selection, JP:MenuJoypadProvider + JoypadProvider>(roms_path:&str, menu_renderer:MR, jp:&mut JP)->String{ - let mut menu_options = Vec::new(); - let dir_entries = std::fs::read_dir(roms_path).expect(std::format!("Error openning the roms directory: {}",roms_path).as_str()); - for entry in dir_entries{ - let entry = entry.unwrap(); - let path = entry.path(); - if let Some(extension) = path.as_path().extension().and_then(std::ffi::OsStr::to_str){ - match extension { - "gb" | "gbc"=>{ - let filename = String::from(path.file_name().expect("Error should be a file").to_str().unwrap()); - let option = MenuOption{value: path, prompt: filename}; - menu_options.push(option); - }, - _=>{} - } - } - } - let mut menu = JoypadMenu::new(&menu_options, String::from("Choose ROM"), menu_renderer); - let result = menu.get_menu_selection(jp); - - return String::from(result.to_str().unwrap()); -} \ No newline at end of file diff --git a/common/src/joypad_menu/mod.rs b/common/src/joypad_menu/mod.rs index b9c3e70e..11b7d37b 100644 --- a/common/src/joypad_menu/mod.rs +++ b/common/src/joypad_menu/mod.rs @@ -3,11 +3,7 @@ pub mod joypad_gfx_menu; use magenboy_core::keypad::{button::Button, joypad::Joypad, joypad_provider::JoypadProvider}; -#[derive(Default, Clone, Copy)] -pub struct MenuOption>{ - pub value:T, - pub prompt:S -} +use crate::menu::MenuOption; pub trait MenuRenderer>{ fn render_menu(&mut self,header:&S, menu:&[MenuOption], selection:usize); diff --git a/common/src/lib.rs b/common/src/lib.rs index bdc37ee5..6cef5b31 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,12 +1,12 @@ #![cfg_attr(not(feature = "std"), no_std)] cfg_if::cfg_if!{ if #[cfg(feature = "std")] { - pub mod emulation_menu; pub mod mbc_handler; pub mod mpmc_gfx_device; pub mod logging; }} +pub mod menu; pub mod joypad_menu; pub mod interpolation; diff --git a/common/src/menu.rs b/common/src/menu.rs new file mode 100644 index 00000000..9969456b --- /dev/null +++ b/common/src/menu.rs @@ -0,0 +1,103 @@ +#[derive(Default, Clone, Copy)] +pub struct MenuOption>{ + pub value:T, + pub prompt:S +} + +pub enum EmulatorMenuOption{ + Resume, + Restart, + Shutdown +} + +pub const GAME_MENU_OPTIONS:[MenuOption;3] = [ + MenuOption{prompt:"Resume", value:EmulatorMenuOption::Resume}, + MenuOption{prompt:"Restart", value:EmulatorMenuOption::Restart}, + MenuOption{prompt:"Shutdown", value:EmulatorMenuOption::Shutdown} +]; + +cfg_if::cfg_if!{ if #[cfg(feature = "std")]{ + use std::{sync::{atomic::AtomicBool, Mutex}, path::PathBuf}; + use magenboy_core::{ppu::gfx_device::GfxDevice, keypad::joypad_provider::JoypadProvider}; + use super::joypad_menu::{MenuJoypadProvider, joypad_gfx_menu, JoypadMenu, MenuRenderer}; + + pub struct MagenBoyState{ + // Use atomic bool, normal bool doesnt works on arm (probably cause of the memory model) + pub running:AtomicBool, + pub pause:AtomicBool, + pub exit:AtomicBool, + pub state_mutex:Mutex<()> + } + + impl MagenBoyState{ + pub const fn new() -> Self { + Self { running: AtomicBool::new(true), pause: AtomicBool::new(false), exit: AtomicBool::new(false), state_mutex: Mutex::new(()) } + } + } + + pub struct MagenBoyMenu{ + header:String, + provider:JP, + } + + impl MagenBoyMenu { + pub fn new(provider:JP, header:String)->Self{ + Self { provider, header } + } + + pub fn pop_game_menu(&mut self, state:&MagenBoyState, gfx_device:&mut GFX, receiver:crossbeam_channel::Receiver){ + match self.get_game_menu_selection(state, gfx_device, receiver){ + EmulatorMenuOption::Resume => {}, + EmulatorMenuOption::Restart => state.running.store(false, std::sync::atomic::Ordering::Relaxed), + EmulatorMenuOption::Shutdown => { + state.running.store(false, std::sync::atomic::Ordering::Relaxed); + state.exit.store(true, std::sync::atomic::Ordering::Relaxed); + }, + } + } + + fn get_game_menu_selection(&mut self, state:&MagenBoyState,gfx_device:&mut GFX, emulation_framebuffer_channel:crossbeam_channel::Receiver)->&EmulatorMenuOption{ + let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(gfx_device); + + let mut menu = JoypadMenu::new(&GAME_MENU_OPTIONS, &self.header, menu_renderer); + + // lock the mutex here to sync the 2 threads + state.pause.store(true, std::sync::atomic::Ordering::SeqCst); + loop{ + if let Ok(_lock) = state.state_mutex.try_lock(){ + let selection = menu.get_menu_selection(&mut self.provider); + state.pause.store(false, std::sync::atomic::Ordering::SeqCst); + return selection; + }else{ + // try recv in order to clear frames from the channel + // in order to not block the emualtion thread and allow it to finish the frame + let _ = emulation_framebuffer_channel.try_recv(); + } + } + } + + } + + pub fn get_rom_selection, JP:MenuJoypadProvider + JoypadProvider>(roms_path:&str, menu_renderer:MR, jp:&mut JP)->String{ + let mut menu_options = Vec::new(); + let dir_entries = std::fs::read_dir(roms_path).expect(std::format!("Error openning the roms directory: {}",roms_path).as_str()); + for entry in dir_entries{ + let entry = entry.unwrap(); + let path = entry.path(); + if let Some(extension) = path.as_path().extension().and_then(std::ffi::OsStr::to_str){ + match extension { + "gb" | "gbc"=>{ + let filename = String::from(path.file_name().expect("Error should be a file").to_str().unwrap()); + let option = MenuOption{value: path, prompt: filename}; + menu_options.push(option); + }, + _=>{} + } + } + } + let mut menu = JoypadMenu::new(&menu_options, String::from("Choose ROM"), menu_renderer); + let result = menu.get_menu_selection(jp); + + return String::from(result.to_str().unwrap()); + } +}} \ No newline at end of file diff --git a/core/src/utils/stack_string.rs b/core/src/utils/stack_string.rs index aa88df1a..32d77212 100644 --- a/core/src/utils/stack_string.rs +++ b/core/src/utils/stack_string.rs @@ -1,4 +1,4 @@ -use core::{convert::From, fmt::Write}; +use core::{convert::From, fmt::{Write, Arguments}}; #[derive(Clone, Copy)] pub struct StackString{ @@ -25,6 +25,12 @@ impl From<&str> for StackString{ } impl StackString{ + pub fn from_args(args:Arguments)->Self{ + let mut str = Self::default(); + str.write_fmt(args).unwrap(); + return str; + } + pub fn append(&mut self, data_to_append:&[u8]){ if self.size + data_to_append.len() > SIZE{ core::panic!("Error!, trying to append to stack string with too much data"); @@ -117,4 +123,17 @@ mod tests{ let res: Result<(), core::fmt::Error> = ss.write_str(" fucker djakdjaslkdjskl"); assert_eq!(res, Result::Err(core::fmt::Error)); } + + #[test] + fn test_from_args(){ + let str: StackString<10> = StackString::from_args(format_args!("{},{}", "test", "test1")); + assert_eq!(str.as_str(), "test,test1"); + } + + #[test] + #[should_panic] + fn test_from_args_too_small_size(){ + let str: StackString<5> = StackString::from_args(format_args!("{},{}", "test", "test1")); + assert_eq!(str.as_str(), "test,test1"); + } } \ No newline at end of file diff --git a/rpi/Cargo.toml b/rpi/Cargo.toml index 86d8123f..520f8b12 100644 --- a/rpi/Cargo.toml +++ b/rpi/Cargo.toml @@ -18,9 +18,6 @@ crossbeam-channel = {version = "0.5", optional = true} rppal = {version = "0.14", optional = true} [features] -default = ["rpi4"] -rpi4 = [] -rpi2 = [] os = ["magenboy_common/std", "libc", "nix/ioctl", "crossbeam-channel", "rppal"] [[bin]] diff --git a/rpi/build.rs b/rpi/build.rs index a88c6f2a..77b0b719 100644 --- a/rpi/build.rs +++ b/rpi/build.rs @@ -1,15 +1,16 @@ #[cfg(not(feature = "os"))] mod config{ + pub const RPI_ENV_VAR_NAME:&'static str = "RPI"; pub const LD_SCRIPT_PATH:&str = "src/bin/baremetal/link.ld"; pub const CONFIG_FILE_PATH: &str = "config.txt"; pub const CONFIG_TXT_CONTENT:&str = - "# configuration for the RPI - arm_64bit=0 # boot to 32 bit mode +"# configuration for the RPI +arm_64bit=0 # boot to 32 bit mode - # fast boot - boot_delay=0 - disable_poe_fan=1 - disable_splash=1"; +# fast boot +boot_delay=0 +disable_poe_fan=1 +disable_splash=1"; } fn main(){ @@ -21,6 +22,13 @@ fn main(){ #[cfg(not(feature = "os"))] { println!("cargo:rerun-if-changed={}", config::LD_SCRIPT_PATH); + println!("cargo:rerun-if-env-changed={}", config::RPI_ENV_VAR_NAME); + + // Creates config.txt std::fs::write(config::CONFIG_FILE_PATH, config::CONFIG_TXT_CONTENT).unwrap(); + + // Add the cfg option `rpi` with that value of the env var `RPI` + let rpi_revision = std::env::var(config::RPI_ENV_VAR_NAME).expect("RPI env must be set"); + println!("cargo:rustc-cfg=rpi=\"{}\"", rpi_revision); } } \ No newline at end of file diff --git a/rpi/src/bin/baremetal/main.rs b/rpi/src/bin/baremetal/main.rs index 5ea6900b..b5a1aaa4 100644 --- a/rpi/src/bin/baremetal/main.rs +++ b/rpi/src/bin/baremetal/main.rs @@ -6,9 +6,9 @@ mod logging; use core::panic::PanicInfo; -use magenboy_common::joypad_menu::{joypad_gfx_menu, JoypadMenu, MenuOption}; +use magenboy_common::{joypad_menu::{joypad_gfx_menu::{self, GfxDeviceMenuRenderer}, JoypadMenu, }, menu::*, VERSION}; use magenboy_core::{machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, mmu::external_memory_bus::Bootrom, utils::stack_string::StackString}; -use magenboy_rpi::{drivers::{GpioJoypadProvider, Ili9341GfxDevice, Fat32, FileEntry}, peripherals::PERIPHERALS, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}}; +use magenboy_rpi::{drivers::*, peripherals::{PERIPHERALS, GpioPull}, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}, MENU_PIN_BCM, delay}; const MAX_ROM_SIZE:usize = 0x80_0000; // 8 MiB, Max size of MBC5 rom @@ -26,14 +26,57 @@ pub extern "C" fn main()->!{ log::info!("Initialized logger"); log::info!("running at exec mode: {:#X}", boot::get_cpu_execution_mode()); + let mut power_manager = unsafe{PERIPHERALS.take_power()}; + let mut fs = Fat32::new(); let mut gfx = Ili9341GfxDevice::new(RESET_PIN_BCM, LED_PIN_BCM, TURBO, FRAME_LIMITER); + let mut pause_menu_gfx = gfx.clone(); let mut joypad_provider = GpioJoypadProvider::new(button_to_bcm_pin); + let mut pause_menu_joypad_provider = joypad_provider.clone(); log::info!("Initialize all drivers succesfully"); let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(&mut gfx); let mut menu_options:[MenuOption>; 255] = [Default::default(); 255]; + let menu_options_size = read_menu_options(&mut fs, &mut menu_options); + + let mut menu = JoypadMenu::new(&menu_options[0..menu_options_size], StackString::from("Choose ROM"), menu_renderer); + let selected_rom = menu.get_menu_selection(&mut joypad_provider); + log::info!("Selected ROM: {}", selected_rom.get_name()); + + let rom = unsafe{&mut ROM_BUFFER}; + fs.read_file(selected_rom, rom); + let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], None, None); + + let mut gameboy = GameBoy::new(mbc, joypad_provider, magenboy_rpi::BlankAudioDevice, gfx, Bootrom::None, None); + log::info!("Initialized gameboy!"); + + let menu_pin = unsafe {PERIPHERALS.get_gpio().take_pin(MENU_PIN_BCM).into_input(GpioPull::PullUp)}; + let pause_menu_header:StackString<30> = StackString::from_args(format_args!("MagenBoy bm v{}", VERSION)); + let pause_menu_renderer = GfxDeviceMenuRenderer::new(&mut pause_menu_gfx); + let mut pause_menu = JoypadMenu::new(&GAME_MENU_OPTIONS, pause_menu_header.as_str(), pause_menu_renderer); + loop{ + if !menu_pin.read_state(){ + log::info!("Open pause menu"); + match pause_menu.get_menu_selection(&mut pause_menu_joypad_provider){ + EmulatorMenuOption::Resume => {}, + EmulatorMenuOption::Restart => { + log::info!("Reseting system"); + delay::wait_ms(100); + power_manager.reset(magenboy_rpi::peripherals::ResetMode::Partition0) + } + EmulatorMenuOption::Shutdown => { + log::info!("Shuting down system"); + delay::wait_ms(100); + power_manager.reset(magenboy_rpi::peripherals::ResetMode::Halt) + } + } + } + gameboy.cycle_frame(); + } +} + +fn read_menu_options(fs: &mut Fat32, menu_options: &mut [MenuOption>; 255]) -> usize { let mut menu_options_size = 0; let mut root_dir_offset = 0; const FILES_PER_LIST:usize = 20; @@ -50,20 +93,7 @@ pub extern "C" fn main()->!{ } root_dir_offset += FILES_PER_LIST; } - - let mut menu = JoypadMenu::new(&menu_options[0..menu_options_size], StackString::from("Choose ROM"), menu_renderer); - let selected_rom = menu.get_menu_selection(&mut joypad_provider); - log::info!("Selected ROM: {}", selected_rom.get_name()); - - let rom = unsafe{&mut ROM_BUFFER}; - fs.read_file(selected_rom, rom); - let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], None, None); - - let mut gameboy = GameBoy::new(mbc, joypad_provider, magenboy_rpi::BlankAudioDevice, gfx, Bootrom::None, None); - log::info!("Initialized gameboy!"); - loop{ - gameboy.cycle_frame(); - } + return menu_options_size; } #[panic_handler] diff --git a/rpi/src/bin/rpios/main.rs b/rpi/src/bin/rpios/main.rs index 25724674..140c168c 100644 --- a/rpi/src/bin/rpios/main.rs +++ b/rpi/src/bin/rpios/main.rs @@ -1,11 +1,9 @@ use std::{env, fs}; -use magenboy_common::{emulation_menu::*, joypad_menu::*, mpmc_gfx_device::MpmcGfxDevice, mbc_handler::{initialize_mbc, release_mbc}}; +use magenboy_common::{menu::*, joypad_menu::*, mpmc_gfx_device::MpmcGfxDevice, mbc_handler::{initialize_mbc, release_mbc}}; use magenboy_core::{ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::{GfxDevice, Pixel}}, mmu::{GBC_BOOT_ROM_SIZE, external_memory_bus::Bootrom, GB_BOOT_ROM_SIZE}, machine::{Mode, gameboy::GameBoy}, keypad::joypad_provider::JoypadProvider}; use log::info; -use magenboy_rpi::{configuration::{emulation::*, display::*, joypad::*}, drivers::*, peripherals::PERIPHERALS}; - -const MENU_PIN_BCM:u8 = 3; // This pin is the turn on pin on thr RPI +use magenboy_rpi::{configuration::{emulation::*, display::*, joypad::*}, drivers::*, peripherals::PERIPHERALS, MENU_PIN_BCM}; // This is static and not local for the unix signal handler to access it static EMULATOR_STATE:MagenBoyState = MagenBoyState::new(); diff --git a/rpi/src/drivers/gpio_joypad.rs b/rpi/src/drivers/gpio_joypad.rs index b7a03965..689cc582 100644 --- a/rpi/src/drivers/gpio_joypad.rs +++ b/rpi/src/drivers/gpio_joypad.rs @@ -4,6 +4,7 @@ use crate::peripherals::{PERIPHERALS, GpioPull, Trigger, InputGpioPin}; const READ_THRESHOLD:u32 = 0x1000; +#[derive(Clone)] pub struct GpioJoypadProvider{ input_pins: [InputGpioPin; NUM_OF_KEYS], read_threshold_counter: u32 @@ -52,10 +53,4 @@ impl magenboy_common::joypad_menu::MenuJoypadProvider for GpioJoypadProvider { joypad.buttons[i] = self.input_pins[i].read_state(); } } -} - -impl Clone for GpioJoypadProvider{ - fn clone(&self) -> Self { - Self { input_pins: self.input_pins.clone(), read_threshold_counter: self.read_threshold_counter.clone() } - } } \ No newline at end of file diff --git a/rpi/src/drivers/ili9341_gfx_device.rs b/rpi/src/drivers/ili9341_gfx_device.rs index 40ec8ffc..e490ccac 100644 --- a/rpi/src/drivers/ili9341_gfx_device.rs +++ b/rpi/src/drivers/ili9341_gfx_device.rs @@ -1,3 +1,5 @@ +use core::cell::OnceCell; + use magenboy_core::ppu::{gb_ppu::{SCREEN_WIDTH, SCREEN_HEIGHT}, gfx_device::{GfxDevice, Pixel}}; use crate::peripherals::{Timer, Spi0, PERIPHERALS, OutputGpioPin}; @@ -41,7 +43,6 @@ enum Ili9341Command{ struct Ili9341Contoller{ spi:Spi0, - timer: Timer, led_pin: OutputGpioPin, reset_pin: OutputGpioPin } @@ -57,7 +58,7 @@ impl Ili9341Contoller{ let led_pin = gpio.take_pin(led_pin_bcm).into_output(); let spi = unsafe{PERIPHERALS.take_spi0()}; - let mut controller = Ili9341Contoller { spi, led_pin, reset_pin, timer: unsafe{PERIPHERALS.take_timer()}}; + let mut controller = Ili9341Contoller { spi, led_pin, reset_pin}; // toggling the reset pin to initalize the lcd controller.reset_pin.set_high(); @@ -156,8 +157,12 @@ impl Ili9341Contoller{ } fn sleep_ms(&mut self, milliseconds_to_sleep:u64){ - let target_wait_time = core::time::Duration::from_millis(milliseconds_to_sleep); - self.timer.wait(target_wait_time); + cfg_if::cfg_if!{ if #[cfg(feature = "os")]{ + std::thread::sleep(std::time::Duration::from_millis(milliseconds_to_sleep)); + } + else{ + crate::delay::wait_ms(milliseconds_to_sleep as u32); + }} } } @@ -171,32 +176,42 @@ impl Drop for Ili9341Contoller{ } } +#[derive(Clone)] pub struct Ili9341GfxDevice{ - ili9341_controller:Ili9341Contoller, turbo_mul:u8, turbo_frame_counter:u8, frame_limiter:u32, frames_counter: u32, time_counter:core::time::Duration, + + // This type is here to mark this type as not Send and not Sync + _unsend_unsync_marker: core::marker::PhantomData<*const ()> } +static mut ILI9341_CONTROLLER:OnceCell = OnceCell::new(); +static mut BCM_TIMER:OnceCell = OnceCell::new(); + impl Ili9341GfxDevice{ pub fn new(reset_pin_bcm:u8, led_pin_bcm:u8, turbo_mul:u8, frame_limiter:u32)->Self{ - let mut ili9341_controller = Ili9341Contoller::new(reset_pin_bcm, led_pin_bcm); - // reset the timer - let _ = ili9341_controller.timer.tick(); + unsafe{ + ILI9341_CONTROLLER.set(Ili9341Contoller::new(reset_pin_bcm, led_pin_bcm)).ok().unwrap(); + BCM_TIMER.set(PERIPHERALS.take_timer()).ok().unwrap(); + + // reset the timer + let _ = BCM_TIMER.get_mut().unwrap().tick(); + } Ili9341GfxDevice { - ili9341_controller,frames_counter:0, time_counter: core::time::Duration::ZERO, - turbo_mul, turbo_frame_counter:0, frame_limiter + frames_counter:0, turbo_mul, turbo_frame_counter:0, frame_limiter, + _unsend_unsync_marker: core::marker::PhantomData, } } + + const EXPECTED_FRAME_DURATION: f64 = 1.0f64/60.0f64; } - -const EXPECTED_FRAME_DURATION: f64 = 1.0f64/60.0f64; impl GfxDevice for Ili9341GfxDevice{ fn swap_buffer(&mut self, buffer:&[Pixel; SCREEN_HEIGHT * SCREEN_WIDTH]) { self.turbo_frame_counter = (self.turbo_frame_counter + 1) % self.turbo_mul; @@ -205,17 +220,20 @@ impl GfxDevice for Ili9341GfxDevice{ } if self.frames_counter & self.frame_limiter == 0{ - self.ili9341_controller.write_frame_buffer(&buffer); + unsafe{ILI9341_CONTROLLER.get_mut().unwrap().write_frame_buffer(&buffer)}; } // measure fps self.frames_counter += 1; - let mut duration = self.ili9341_controller.timer.tick().as_secs_f64(); - - // block for the frame duration - while duration < EXPECTED_FRAME_DURATION{ - duration += self.ili9341_controller.timer.tick().as_secs_f64(); - } + let duration = unsafe{ + let timer = BCM_TIMER.get_mut().unwrap(); + let mut duration = timer.tick().as_secs_f64(); + // block for the frame duration + while duration < Self::EXPECTED_FRAME_DURATION{ + duration += timer.tick().as_secs_f64(); + } + duration + }; self.time_counter += core::time::Duration::from_secs_f64(duration); if self.time_counter.as_millis() > 1000{ diff --git a/rpi/src/lib.rs b/rpi/src/lib.rs index cc0c67f4..bb744b58 100644 --- a/rpi/src/lib.rs +++ b/rpi/src/lib.rs @@ -1,18 +1,20 @@ #![cfg_attr(not(feature = "os"), no_std)] -#[cfg(all(feature = "os", any(feature = "rpi4",feature = "rpi2")))] -core::compile_error!("rpiX features cant be combined with the os feature"); +#[cfg(all(feature = "os", rpi))] +core::compile_error!("The os feature and the rpi cfg value cant be set at the same time"); pub mod configuration; pub mod peripherals; pub mod drivers; cfg_if::cfg_if!{ if #[cfg(not(feature = "os"))]{ pub mod syncronization; - mod delay; + pub mod delay; }} use magenboy_core::apu::audio_device::*; +pub const MENU_PIN_BCM:u8 = 3; // This pin is the turn on pin on thr RPI + pub struct BlankAudioDevice; impl AudioDevice for BlankAudioDevice{ fn push_buffer(&mut self, _buffer:&[StereoSample; BUFFER_SIZE]) {} diff --git a/rpi/src/peripherals/emmc.rs b/rpi/src/peripherals/emmc.rs index 727c9dd1..fdee624f 100644 --- a/rpi/src/peripherals/emmc.rs +++ b/rpi/src/peripherals/emmc.rs @@ -5,11 +5,11 @@ use core::mem::size_of; use bitfield_struct::bitfield; -use crate::{delay, peripherals::{InputGpioPin, IoGpioPin}}; +use crate::{delay, peripherals::{InputGpioPin, IoGpioPin, Tag}}; use super::{utils::{MmioReg32, compile_time_size_assert, self, memory_barrier}, PERIPHERALS, Mailbox}; // 0x30_0000 - EMMC1 for other RPI's, 0x34_0000 - EMMC2 for RPI4 -const EMMC_BASE_OFFSET:usize = if cfg!(feature = "rpi4"){0x34_0000} else {0x30_0000}; +const EMMC_BASE_OFFSET:usize = if cfg!(rpi = "4") {0x34_0000}else{0x30_0000}; const SD_INIT_CLOCK:u32 = 400000; const SD_NORMAL_CLOCK:u32 = 25000000; @@ -158,18 +158,15 @@ struct Scr{ version:u32, } -// Mailbox Tags -const SET_GPIO_STATE_TAG:u32 = 0x38041; +// Mailbox params const GPIO_TAG_PIN_1_8V_CONTROL:u32 = 132; -const SET_POWER_STATE_TAG:u32 = 0x28001; const SD_CARD_DEVICE_ID:u32 = 0; const POWER_SET_TAG_ON:u32 = 1; const POWER_SET_TAG_WAIT:u32 = 1 << 1; const POWER_SET_TAG_NO_DEVICE:u32 = 1 << 1; -const GET_CLOCK_RATE_TAG:u32 = 0x30002; // In RPI4 take the EMMC2 clock -const EMMC_CLOCK_ID:u32 = if cfg!(feature = "rpi4") {0xC} else {0x1}; +const EMMC_CLOCK_ID:u32 = if cfg!(rpi = "4") {0xC} else {0x1}; pub struct Emmc{ registers: &'static mut EmmcRegisters, @@ -226,9 +223,8 @@ impl Emmc{ let mbox = unsafe{PERIPHERALS.get_mailbox()}; // this code is only for the RPI4 - #[cfg(feature = "rpi4")] - { - let res = mbox.call(SET_GPIO_STATE_TAG, [GPIO_TAG_PIN_1_8V_CONTROL/* Pin to operate on */, 0/* requested state */]); + if cfg!(rpi = "4"){ + let res = mbox.call(Tag::SetGpioState, [GPIO_TAG_PIN_1_8V_CONTROL/* Pin to operate on */, 0/* requested state */]); // Test for the GPIO pin state set if res[1] != 0{ core::panic!("Failed to disable RPI4 GPIO 1.8v supply"); @@ -245,8 +241,7 @@ impl Emmc{ .expect("Could not reset the SD card"); // this block is only for RPI4 - #[cfg(feature = "rpi4")] - { + if cfg!(rpi = "4"){ let mut control0 = self.registers.control[0].read(); control0 |= 0xF00; // Those bits are not documented in bcm2835 docs, it is copied from Circle self.registers.control[0].write(control0); @@ -544,7 +539,7 @@ impl Emmc{ } fn power_on(&mut self, mbox: &mut Mailbox){ - let res = mbox.call(SET_POWER_STATE_TAG, [SD_CARD_DEVICE_ID, POWER_SET_TAG_ON | POWER_SET_TAG_WAIT]); + let res = mbox.call(Tag::SetPowerState, [SD_CARD_DEVICE_ID, POWER_SET_TAG_ON | POWER_SET_TAG_WAIT]); if res[1] & POWER_SET_TAG_ON == 0 || res[1] & POWER_SET_TAG_NO_DEVICE != 0{ core::panic!("Could not power on the SD card device from the mbox interface"); } @@ -590,7 +585,7 @@ impl Emmc{ } fn get_base_clock(&mut self, mbox:&mut Mailbox)->u32{ - let res = mbox.call(GET_CLOCK_RATE_TAG, [EMMC_CLOCK_ID, 0]); + let res = mbox.call(Tag::GetClockRate, [EMMC_CLOCK_ID, 0]); let clock_rate = res[1]; return clock_rate; } diff --git a/rpi/src/peripherals/gpio.rs b/rpi/src/peripherals/gpio.rs index c52bc214..b202bdf9 100644 --- a/rpi/src/peripherals/gpio.rs +++ b/rpi/src/peripherals/gpio.rs @@ -25,7 +25,7 @@ pub enum Trigger{ #[cfg(not(feature = "os"))] pub mod no_std_impl{ - use crate::{syncronization::Mutex, peripherals::utils::{compile_time_size_assert, MmioReg32, get_static_peripheral, memory_barrier}}; + use crate::{syncronization::Mutex, peripherals::utils::{compile_time_size_assert, MmioReg32, get_static_peripheral, memory_barrier, BulkWrite}}; use super::*; #[repr(C,align(4))] @@ -46,10 +46,7 @@ pub mod no_std_impl{ } compile_time_size_assert!(GpioRegisters, 0xF4); - #[cfg(feature = "rpi4")] - const GPIO_PINS_COUNT:usize = 58; - #[cfg(feature = "rpi2")] - const GPIO_PINS_COUNT:usize = 54; + const GPIO_PINS_COUNT:usize = if cfg!(rpi = "4") {58} else {54}; const BASE_GPIO_OFFSET: usize = 0x20_0000; static mut GPIO_REGISTERS:Option> = None; @@ -99,14 +96,28 @@ pub mod no_std_impl{ r.gpeds[0].write(0xFFFF_FFFF); r.gpeds[1].write(0xFFFF_FFFF); }); + memory_barrier(); return; } } } + + pub fn power_off(&mut self){ + memory_barrier(); + let registers = unsafe{GPIO_REGISTERS.as_mut().unwrap()}; + registers.lock(|r|{ + r.gpfsel.write(0); + cfg_if::cfg_if!{ if #[cfg(rpi = "4")]{ + r.gpio_pup_pdn_cntrl.write(0); + } + else{compile_error!("Power off only support rpi4")}} + }); + memory_barrier(); + } } pub struct GpioPin{ - registers:&'static mut Mutex<&'static mut GpioRegisters>, + registers:&'static Mutex<&'static mut GpioRegisters>, bcm_pin_number:u8, } @@ -143,9 +154,13 @@ pub mod no_std_impl{ let offset = (self.bcm_pin_number % 16) * 2; let mask:u32 = 0b11 << offset; memory_barrier(); - let register_value = self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].read()); - let new_value = (register_value & !mask) | ((pull_mode as u32) << offset as u32); - self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].write(new_value)); + cfg_if::cfg_if!{ if #[cfg(rpi = "4")]{ + let register_value = self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].read()); + let new_value = (register_value & !mask) | ((pull_mode as u32) << offset as u32); + self.registers.lock(|r|r.gpio_pup_pdn_cntrl[register_index as usize].write(new_value)); + } + else{compile_error!("rpi's other than 4 needs proper support in order for set_pull to work")}} + memory_barrier(); } diff --git a/rpi/src/peripherals/gpu.rs b/rpi/src/peripherals/gpu.rs index 9d772686..4cf63cc1 100644 --- a/rpi/src/peripherals/gpu.rs +++ b/rpi/src/peripherals/gpu.rs @@ -1,4 +1,4 @@ -use crate::peripherals::PERIPHERALS; +use crate::peripherals::{PERIPHERALS, Tag}; // using GpuMemory cause I need a memory that is not cached by the cpu caches (L1, L2) pub(super) struct GpuMemory{ @@ -11,10 +11,6 @@ pub(super) struct GpuMemory{ impl GpuMemory{ const MEM_ALLOC_FLAG_DIRECT:usize = 1 << 2; const MEM_ALLOC_FLAG_COHERENT:usize = 1 << 3; - const ALLOCATE_MEMORY_TAG:u32 = 0x3000C; - const LOCK_MEMORY_TAG:u32 = 0x3000D; - const UNLOCK_MEMORY_TAG:u32 = 0x3000E; - const RELEASE_MEMORY_TAG:u32 = 0x3000F; const PAGE_SIZE:u32 = 4096; // This function converts the from the bus address of the SDRAM uncached memory to the arm physical address @@ -28,14 +24,14 @@ impl GpuMemory{ log::debug!("Trying to allocate: {} memory", size); // Result for alloc memory call is in the first u32 of the buffer - let handle = mbox.call(Self::ALLOCATE_MEMORY_TAG, [size, Self::PAGE_SIZE, flags])[0]; + let handle = mbox.call(Tag::AllocateMemory, [size, Self::PAGE_SIZE, flags])[0]; // This is not documented well but after testing - on out of Gpu memory mailbox returns handle = 0 if handle == 0{ core::panic!("Error allocating Gpu memory! perhaps there is not enough free Gpu memory"); } // The result for lock memory call is in the first u32 of the buffer - let bus_address = mbox.call(Self::LOCK_MEMORY_TAG, [handle])[0]; + let bus_address = mbox.call(Tag::LockMemory, [handle])[0]; // This is not documented well but after testing - on invalid handle mailbox returns bus_address = 0 if bus_address == 0{ core::panic!("Error locking Gpu memory!"); @@ -63,10 +59,10 @@ impl GpuMemory{ } } let mbox = unsafe{PERIPHERALS.get_mailbox()}; - if mbox.call(Self::UNLOCK_MEMORY_TAG, [self.mailbox_memory_handle])[0] != 0{ + if mbox.call(Tag::UnlockMemory, [self.mailbox_memory_handle])[0] != 0{ core::panic!("Error while trying to unlock gpu memory using mailbox"); } - if mbox.call(Self::RELEASE_MEMORY_TAG, [self.mailbox_memory_handle])[0] != 0{ + if mbox.call(Tag::ReleaseMemory, [self.mailbox_memory_handle])[0] != 0{ core::panic!("Error while trying to release gpu memory using mailbox"); } } diff --git a/rpi/src/peripherals/mailbox.rs b/rpi/src/peripherals/mailbox.rs index 0a6e3eb2..7972ca55 100644 --- a/rpi/src/peripherals/mailbox.rs +++ b/rpi/src/peripherals/mailbox.rs @@ -14,6 +14,19 @@ pub use std_impl::Mailbox; #[cfg(not(feature = "os"))] pub use no_std_impl::Mailbox; +#[derive(Clone, Copy)] +#[repr(u32)] +pub enum Tag{ + SetPowerState = 0x28001, + GetClockRate = 0x30002, + AllocateMemory = 0x3000C, + LockMemory = 0x3000D, + UnlockMemory = 0x3000E, + ReleaseMemory = 0x3000F, + SetClockRate = 0x38002, + SetGpioState = 0x38041, +} + #[repr(C, align(4))] struct PropertyTagHeader{ tag:u32, @@ -47,12 +60,12 @@ struct Message{ impl Message{ const MBOX_PROPERTY_END:u32 = 0; - fn new(tag:u32, data:[u32;DATA_LEN])->Self{ + fn new(tag:Tag, data:[u32;DATA_LEN])->Self{ Self{ size: size_of::() as u32, status: PropertyStatus::Request, tag_header: PropertyTagHeader { - tag, + tag: tag as u32, buffer_size: (size_of::() * DATA_LEN) as u32, request_response_size: 0 // zero since unused by the firmare }, @@ -103,7 +116,7 @@ mod no_std_impl{ return Mailbox { registers, uncached_buffer }; } - pub fn call(&mut self, tag:u32, data:[u32;DATA_LEN])->[u32;DATA_LEN]{ + pub fn call(&mut self, tag:Tag, data:[u32;DATA_LEN])->[u32;DATA_LEN]{ if size_of::>()> self.uncached_buffer.len() { core::panic!("Error, Message with data len of {} bytes is too large ({}) and cant fit a {} bytes buffer", DATA_LEN, size_of::>(), self.uncached_buffer.len()); @@ -132,7 +145,7 @@ mod no_std_impl{ return uncached_message.data; } core::panic!("Error in mbox call! tag: {:#X}, req_data: {:?}, status: {:#X}, res_data: {:?}", - tag, data, uncached_message.status as u32, uncached_message.data); + tag as u32, data, uncached_message.status as u32, uncached_message.data); } } } @@ -163,7 +176,7 @@ mod std_impl{ Self { mbox_fd: fd } } - pub fn call(&mut self, tag:u32, data:[u32;SIZE])->[u32;SIZE]{ + pub fn call(&mut self, tag:Tag, data:[u32;SIZE])->[u32;SIZE]{ let mut message = Message::::new(tag, data); return self.send_message(&mut message); } diff --git a/rpi/src/peripherals/mod.rs b/rpi/src/peripherals/mod.rs index d07d2b43..47785d3c 100644 --- a/rpi/src/peripherals/mod.rs +++ b/rpi/src/peripherals/mod.rs @@ -10,9 +10,11 @@ mod timer; mod bcm_host; cfg_if::cfg_if!{ if #[cfg(not(feature = "os"))]{ mod emmc; + mod power; pub(crate) use utils::compile_time_size_assert; pub use utils::PERIPHERALS_BASE_ADDRESS; pub use emmc::Emmc; + pub use power::*; }} pub use gpio::*; @@ -32,15 +34,15 @@ pub struct Peripherals{ spi0: Peripheral, #[cfg(not(feature = "os"))] emmc: Peripheral, + #[cfg(not(feature = "os"))] + power: Peripheral } impl Peripherals{ - const SET_CLOCK_RATE_TAG: u32 = 0x38002; - pub fn set_core_clock(&mut self){ const CORE_CLOCK_ID:u32 = 4; let mbox = self.get_mailbox(); - let result = mbox.call(Self::SET_CLOCK_RATE_TAG, [CORE_CLOCK_ID, CORE_FREQ, 0]); + let result = mbox.call(Tag::SetClockRate, [CORE_CLOCK_ID, CORE_FREQ, 0]); if result[0] != CORE_CLOCK_ID || result[1] != CORE_FREQ{ core::panic!("Error, set core clock failed, \nfreq: {}, clock id: {}", result[1], result[0]); } @@ -64,6 +66,10 @@ impl Peripherals{ pub fn take_emmc(&mut self)->emmc::Emmc{ self.emmc.take(||emmc::Emmc::new()) } + #[cfg(not(feature = "os"))] + pub fn take_power(&mut self)->Power{ + self.power.take(||Power::new()) + } } pub static mut PERIPHERALS: Peripherals = Peripherals{ @@ -74,4 +80,6 @@ pub static mut PERIPHERALS: Peripherals = Peripherals{ spi0: Peripheral::Uninit, #[cfg(not(feature = "os"))] emmc: Peripheral::Uninit, + #[cfg(not(feature = "os"))] + power: Peripheral::Uninit }; \ No newline at end of file diff --git a/rpi/src/peripherals/power.rs b/rpi/src/peripherals/power.rs new file mode 100644 index 00000000..3e454800 --- /dev/null +++ b/rpi/src/peripherals/power.rs @@ -0,0 +1,92 @@ +use super::{utils::{MmioReg32, get_static_peripheral, memory_barrier}, PERIPHERALS, Tag}; + +const RPI_DEVICES_COUNT:usize = if cfg!(rpi = "4") {11} else {9}; + +const PM_BASE_OFFSET:usize = 0x10_001C; + +const PM_PASSWORD:u32 = 0x5A00_0000; +const PM_RSTC_WRCFG_CLR: u32 = 0xFFFF_FFCF; +const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020; +const PM_RSTS_PARTITION_MASK:u32 = 0xFFFF_FAAA; // Bits 0,2,4,6,8,10 are cleared + +#[repr(C)] +struct PowerRegisters{ + /// ## RSTC Register bit info: + /// | Bit Index | Meaning | + /// | --------- | ------- | + /// | 0-3 | Unknown | + /// | 4-5 | WRCFG value, 0b10 - Full Reset, 0b11 - Set | + /// |24-31| Password - 0x5A | + rstc: MmioReg32, + + /// ## RSTS Register bits info: + /// | Bit Index | Meaning | + /// | --------- | ------- | + /// | 0 | Bit 0 of boot partition | + /// | 1 | Unknown | + /// | 2 | Bit 1 of boot partition | + /// | 3 | Unknown | + /// | 4 | Bit 2 of boot partition | + /// | 5 | Unknown | + /// | 6 | Bit 3 of boot partition | + /// | 7 | Unknown | + /// | 8 | Bit 4 of boot partition | + /// | 9 | Unknown | + /// | 10 | Bit 5 of boot partition | + /// |11-23| Unknown | + /// |24-31| Password - 0x5A | + rsts: MmioReg32, + + /// ## RSTS Register bits info: + /// | Bit Index | Meaning | + /// | --------- | ------- | + /// |0-19| timeout ticks, 0 - disabled | + /// |20-23| Unknown | + /// |24-31| Password - 0x5A | + wdog: MmioReg32 +} + + +pub struct Power{ + registers: &'static mut PowerRegisters +} + +// The RPI firmware uses the RSTS register to know to which partition to boot from +// The partition value is spread across non sequential bits (see the RSTS register description above) +// The enum is converted to u32 and the value is the value of those non sequential bits in the register +#[repr(u32)] +pub enum ResetMode{ + Partition0 = 0, + Halt = 0x555 +} + +impl Power{ + pub(super) fn new()->Self{ + Self { registers: get_static_peripheral(PM_BASE_OFFSET) } + } + + pub fn reset(&mut self, mode:ResetMode){ + let mbox = unsafe{PERIPHERALS.get_mailbox()}; + for device_id in 0..RPI_DEVICES_COUNT{ + mbox.call(Tag::SetPowerState, [device_id as u32, 0 /* power off, no wait */]); + } + + let gpio = unsafe{PERIPHERALS.get_gpio()}; + gpio.power_off(); + + memory_barrier(); + let mut rsts_reg = self.registers.rsts.read(); + rsts_reg &= PM_RSTS_PARTITION_MASK; + rsts_reg |= PM_PASSWORD | mode as u32; + self.registers.rsts.write(rsts_reg); + + // Setting the watchdog timeout to a non zero value in order to trigger the reset + self.registers.wdog.write(PM_PASSWORD | 1); + + let mut rstc_reg = self.registers.rstc.read(); + rstc_reg &= PM_RSTC_WRCFG_CLR; + rstc_reg |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + self.registers.rstc.write(rstc_reg); + memory_barrier(); + } +} \ No newline at end of file diff --git a/rpi/src/peripherals/utils.rs b/rpi/src/peripherals/utils.rs index 1352acd2..464a81d7 100644 --- a/rpi/src/peripherals/utils.rs +++ b/rpi/src/peripherals/utils.rs @@ -18,6 +18,18 @@ impl MmioReg32 { } } +pub trait BulkWrite{ + fn write(&mut self, value: u32); +} + +impl BulkWrite for [MmioReg32; SIZE]{ + fn write(&mut self, value: u32){ + for reg in self{ + reg.write(value); + } + } +} + // According to the docs the raspberrypi requires memory barrier between reads and writes to differnet peripherals #[inline] pub(super) fn memory_barrier(){ @@ -52,11 +64,10 @@ impl Peripheral{ } } - -#[cfg(feature = "rpi4")] -pub const PERIPHERALS_BASE_ADDRESS:usize = 0xFE00_0000; -#[cfg(feature = "rpi2")] -pub const PERIPHERALS_BASE_ADDRESS:usize = 0x3F00_0000; +#[cfg(not(feature = "os"))] +pub const PERIPHERALS_BASE_ADDRESS:usize = if cfg!(rpi = "4") {0xFE00_0000} + else if cfg!(rpi = "2"){0x3F00_0000} + else{unimplemented!()}; pub(super) fn get_static_peripheral(offset:usize)->&'static mut T{ #[cfg(feature = "os")] diff --git a/sdl/src/main.rs b/sdl/src/main.rs index f14702ec..1c547c3f 100644 --- a/sdl/src/main.rs +++ b/sdl/src/main.rs @@ -28,7 +28,7 @@ cfg_if::cfg_if!{ } use crate::audio::*; -use magenboy_common::{emulation_menu::*, joypad_menu::*, mbc_handler::*, mpmc_gfx_device::*}; +use magenboy_common::{joypad_menu::*, mbc_handler::*, mpmc_gfx_device::*, menu::*}; use magenboy_core::{keypad::button::Button, apu::audio_device::*, machine::{gameboy::GameBoy, Mode}, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::{GfxDevice, Pixel}}, mmu::{GBC_BOOT_ROM_SIZE, external_memory_bus::Bootrom, GB_BOOT_ROM_SIZE}}; use std::{fs, env, result::Result, vec::Vec, convert::TryInto}; use log::info;