Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Files save on baremetal RPI #137

Merged
merged 41 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
fbd7a57
WIP add file write to the fat32 fs
alloncm Sep 4, 2023
60246e9
Succesfully initing the fat32 driver caches
alloncm Sep 4, 2023
e0d01d4
Improve the fat32 file read performance.
alloncm Sep 4, 2023
c790a88
Trying to arrange all the fat handling
alloncm Sep 5, 2023
f812c76
Refactor all the fat handling
alloncm Sep 5, 2023
5184118
Add optimization comment
alloncm Sep 5, 2023
cc135f1
WIP
alloncm Sep 16, 2023
24530be
Finished the init of the fat table cacheI
alloncm Sep 16, 2023
88db99b
Still trying to implement writing file
alloncm Sep 17, 2023
8d81449
WIP writing files
alloncm Sep 17, 2023
eef0b3f
WIP - Still debugging
alloncm Sep 24, 2023
2ec9255
Writing and reading a file now working
alloncm Sep 24, 2023
3a42a10
Finish syncing all the other fats
alloncm Sep 24, 2023
809dc97
Trying to implement savefiles for rpibm
alloncm Sep 24, 2023
111f63b
Add the support for saves on baremetal
alloncm Sep 25, 2023
a30f2d7
Self CR clean code fixes
alloncm Sep 27, 2023
41aeb6a
WIP
alloncm Sep 30, 2023
d570013
The disk operations now works with any size buffer
alloncm Sep 30, 2023
ec8825e
The disk changes works now
alloncm Oct 3, 2023
22cc146
Refactor write root dir to use the new disk write
alloncm Oct 3, 2023
16c4bfd
For some reason this is working now
alloncm Oct 4, 2023
c735ef3
Extract FatBuffer to its own module
alloncm Oct 4, 2023
75cc2d4
Remove redundant logs
alloncm Oct 4, 2023
bd60b59
Merge branch 'master' into feature/baremetal_save
alloncm Dec 26, 2024
38e0517
Update bitfield-struct
alloncm Dec 26, 2024
3b9a507
Self cr fixes
alloncm Dec 26, 2024
40f9f2c
Add safety comments to main
alloncm Dec 27, 2024
f9ded37
Self CR fixes and add bm feature properly
alloncm Dec 27, 2024
78b9923
Continue to review fat_buffer
alloncm Dec 27, 2024
b4844f7
Some more refactors
alloncm Dec 28, 2024
69f08f8
Some refactors to fat_buffer
alloncm Dec 28, 2024
71f5ebb
Silent invalid reads logs (made them trace)
alloncm Dec 28, 2024
5180f41
This might fix
alloncm Dec 28, 2024
06c2f33
Try fix CI
alloncm Dec 31, 2024
4964f01
revert ci fix
alloncm Dec 31, 2024
d807e36
Update readme with more accurate instructions for instaltion of barem…
alloncm Jan 3, 2025
ebb9bae
More self cr fixes and comments
alloncm Jan 4, 2025
9bce96f
More fixes
alloncm Jan 4, 2025
f22222b
Merge branch 'feature/baremetal_save' of https://github.com/alloncm/M…
alloncm Jan 5, 2025
880a90e
Self cr fixes
alloncm Jan 5, 2025
49084d4
Remove those for now as they are for nightly 1.84
alloncm Jan 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ members = [
version = "4.0.0"
authors = ["alloncm <[email protected]>"]
rust-version = "1.73" # cause of cargo-ndk used to build for android platform
edition = "2021"
edition = "2021"

[profile.release]
alloncm marked this conversation as resolved.
Show resolved Hide resolved
lto = true # Samller binaries and faster code
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,14 @@ Choose a game with the Joypad bindings (Dpad and A to confirm)
### Raspberry Pi Baremetal

Currently only Raspberry Pi 4 is supported using the following instructions:
* Format a sd card and make a single `FAT32` partition called `boot`
* Copy the file `config.txt` to the root dir of the sd card
* Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/master/boot) onto the SD card:
- [fixup4.dat](https://github.com/raspberrypi/firmware/raw/master/boot/fixup4.dat)
- [start4.elf](https://github.com/raspberrypi/firmware/raw/master/boot/start4.elf)
- [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/master/boot/bcm2711-rpi-4-b.dtb)
* Copy `kernel7.img` onto the SD card
* Format a sd card to MBR (not GPT) and create a single `FAT32` partition (On windows you can use Rufus)
* Copy the following files from the [Raspberry Pi firmware repo](https://github.com/raspberrypi/firmware/tree/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot) onto the SD card:
- [fixup4.dat](https://github.com/raspberrypi/firmware/raw/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot/fixup4.dat)
- [start4.elf](https://github.com/raspberrypi/firmware/raw/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot/start4.elf)
- [bcm2711-rpi-4-b.dtb](https://github.com/raspberrypi/firmware/raw/191360eaf2e5933eaa0ed76ac0d62722b6f9a58f/boot/bcm2711-rpi-4-b.dtb)

_**Notice**: This is a specific revision, for some reason it broke on the latest version of those files_
* Copy `kernel7.img` and `config.txt` to the SD card
* Connect all the peripherals (ili9341 display and gpio buttons)
* Insert the SD card to the RPI4 and boot it

Expand Down
8 changes: 4 additions & 4 deletions core/src/mmu/carts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ pub const MBC_RAM_SIZE_LOCATION:usize = 0x149;
pub fn get_ram_size(ram_size_register:u8)->usize{
match ram_size_register{
0x0=>0,
0x1=>0x800,
0x1=>0x800, // Unofficial - Undefined according to official docs
0x2=>0x4000,
0x3=>0x8000,
0x4=>0x20000,
0x5=>0x10000,
0x4=>0x2_0000,
0x5=>0x1_0000,
_=>core::panic!("invalid ram size register {:#X}", ram_size_register)
}
}
Expand All @@ -34,7 +34,7 @@ pub fn init_ram(ram_reg:u8, external_ram:Option<&'static mut[u8]>)->&'static mut
match external_ram{
Some(ram)=>{
if ram.len() != ram_size{
core::panic!("external rom is not in the correct size for the cartridge");
core::panic!("External ram is not in the correct size for the cartridge, the save seems corrupted, either fix or delete it and try again");
}

return ram;
Expand Down
12 changes: 6 additions & 6 deletions core/src/mmu/gb_mmu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G
return self.io_bus.ppu.vram.read_current_bank(address-0x8000);
}
else{
log::warn!("bad vram read");
log::trace!("bad vram read");
return BAD_READ_VALUE;
}
},
Expand All @@ -50,7 +50,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G
return self.io_bus.ppu.oam[(address-0xFE00) as usize];
}
else{
log::warn!("bad oam read");
log::trace!("bad oam read");
return BAD_READ_VALUE;
}
}
Expand Down Expand Up @@ -80,15 +80,15 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G
self.io_bus.ppu.vram.write_current_bank(address-0x8000, value);
}
else{
log::warn!("bad vram write: address - {:#X}, value - {:#X}, bank - {}", address, value, self.io_bus.ppu.vram.get_bank_reg());
log::trace!("bad vram write: address - {:#X}, value - {:#X}, bank - {}", address, value, self.io_bus.ppu.vram.get_bank_reg());
}
},
0xFE00..=0xFE9F=>{
if self.is_oam_ready_for_io(){
self.io_bus.ppu.oam[(address-0xFE00) as usize] = value;
}
else{
log::warn!("bad oam write")
log::trace!("bad oam write")
}
},
_=>self.write_unprotected(address, value)
Expand Down Expand Up @@ -249,12 +249,12 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{
}

fn bad_dma_read(address:u16)->u8{
log::warn!("bad memory read during dma. {:#X}", address);
log::trace!("bad memory read during dma. {:#X}", address);
return BAD_READ_VALUE;
}

fn bad_dma_write(address:u16){
log::warn!("bad memory write during dma. {:#X}", address)
log::trace!("bad memory write during dma. {:#X}", address)
}

fn write_color_ram(&mut self, address:u16, color: Color){
Expand Down
2 changes: 1 addition & 1 deletion core/src/ppu/gb_ppu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ impl<GFX:GfxDevice> GbPpu<GFX>{
color_ram[(*pallete_index_register & 0b11_1111) as usize] = value;
}
else{
log::warn!("bad color ram write: index - {:#X}, value: - {:#X}", pallete_index_register, value);
log::trace!("bad color ram write: index - {:#X}, value: - {:#X}", pallete_index_register, value);
}

// if bit 7 is set inderement the dest adderess after write
Expand Down
4 changes: 2 additions & 2 deletions rpi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ magenboy_core = {path = "../core"}
magenboy_common = {path = "../common"}
log = "0.4"
cfg-if = "1"
bitfield-struct = "0.5"
bitfield-struct = {version = "0.9", optional = true}
libc = {version = "0.2", optional = true}
nix = {version = "0.24", optional = true}
crossbeam-channel = {version = "0.5", optional = true}
Expand All @@ -20,7 +20,7 @@ arrayvec = {version = "0.7.6", default-features = false, optional = true}

[features]
os = ["magenboy_common/std", "libc", "nix/ioctl", "crossbeam-channel", "rppal"]
bm = ["arrayvec"]
bm = ["arrayvec", "bitfield-struct"]

[[bin]]
name = "rpios"
Expand Down
6 changes: 0 additions & 6 deletions rpi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,5 @@ fn main(){
let rpi_version = std::env::var(config::RPI_ENV_VAR_NAME)
.expect(std::format!("{} env must be set", config::RPI_ENV_VAR_NAME).as_str());
println!("cargo:rustc-cfg=rpi=\"{}\"", rpi_version);

// Silent warnings for this cfg
println!("cargo:rustc-check-cfg=cfg(rpi, values(\"4\", \"2\"))");
}

// Silent warnings for this cfg
println!("cargo:rustc-check-cfg=cfg(rpi)");
}
2 changes: 1 addition & 1 deletion rpi/src/bin/baremetal/link.ld
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Place _start procedure at the entry address for RPI */
__rpi_32_phys_binary_load_addr = 0x8000;
__isr_table_addr = 0;
__stack_size = 0x100000; /* 1MB stack */
__stack_size = 0x300000; /* 3MB stack */
alloncm marked this conversation as resolved.
Show resolved Hide resolved
ENTRY(__rpi_32_phys_binary_load_addr) /* enry point */

SECTIONS
Expand Down
95 changes: 71 additions & 24 deletions rpi/src/bin/baremetal/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ use core::panic::PanicInfo;
use arrayvec::ArrayString;

use magenboy_common::{joypad_menu::{joypad_gfx_menu::{self, GfxDeviceMenuRenderer}, JoypadMenu, }, menu::*, VERSION};
use magenboy_core::machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc};
use magenboy_rpi::{drivers::*, peripherals::{PERIPHERALS, GpioPull}, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}, MENU_PIN_BCM, delay};
use magenboy_core::{machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, mmu::carts::Mbc};
use magenboy_rpi::{drivers::*, peripherals::{PERIPHERALS, GpioPull, ResetMode, Power}, configuration::{display::*, joypad::button_to_bcm_pin, emulation::*}, MENU_PIN_BCM, delay};

#[panic_handler]
fn panic(info:&PanicInfo)->!{
log::error!("An error has occurred!: \r\n{}", info);

// SAFETY: Defined in boot asm code, no params passed and this function does not returned so no calling convention is even needed
unsafe{boot::hang_led()};
}

const MAX_ROM_SIZE:usize = 0x80_0000; // 8 MiB, Max size of MBC5 rom
const MAX_RAM_SIZE:usize = 0x2_0000; // 128 KiB

// Allocating as static buffer (on the .bss) because it is a very large buffer and
// I dont want to cause problems in stack making it overflow and shit (I can increase it when needed but I afraid Id forget)
// I don't want to cause problems in stack making it overflow and shit (I can increase it when needed but I afraid Id forget)
static mut ROM_BUFFER:[u8; MAX_ROM_SIZE] = [0;MAX_ROM_SIZE];
static mut RAM_BUFFER:[u8; MAX_RAM_SIZE] = [0;MAX_RAM_SIZE];

// This function is no regular main.
// It will not return and will be jumped to from the _start proc in the boot code
Expand All @@ -28,14 +38,14 @@ 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 power_manager = unsafe{PERIPHERALS.take_power()};

let mut fs = Fat32::new();
let mut fs = Fat32Fs::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");
log::info!("Initialize all drivers successfully");

let menu_renderer = joypad_gfx_menu::GfxDeviceMenuRenderer::new(&mut gfx);

Expand All @@ -46,16 +56,18 @@ pub extern "C" fn main()->!{
let selected_rom = menu.get_menu_selection(&mut joypad_provider);
log::info!("Selected ROM: {}", selected_rom.get_name());

// SAFETY: Only ref to this static mut var
let rom = unsafe{&mut ROM_BUFFER};
fs.read_file(selected_rom, rom);
let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], None);
let save_data = try_read_save_file(selected_rom, &mut fs);
let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], save_data);
let mode = mbc.detect_preferred_mode();

let mut gameboy = GameBoy::new_with_mode(mbc, joypad_provider, magenboy_rpi::BlankAudioDevice, gfx, mode);
log::info!("Initialized gameboy!");

let menu_pin = unsafe {PERIPHERALS.get_gpio().take_pin(MENU_PIN_BCM).into_input(GpioPull::PullUp)};
let pause_menu_header:ArrayString<30> = ArrayString::try_from(format_args!("MagenBoy bm v{}", VERSION)).unwrap();
let pause_menu_header:ArrayString<30> = ArrayString::try_from(format_args!("MagenBoy v{}", VERSION)).unwrap();
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{
Expand All @@ -64,45 +76,80 @@ pub extern "C" fn main()->!{
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)
log::info!("Resetting system");
reset_system(mbc, fs, power_manager, ResetMode::Partition0, selected_rom);
}
EmulatorMenuOption::Shutdown => {
log::info!("Shuting down system");
delay::wait_ms(100);
power_manager.reset(magenboy_rpi::peripherals::ResetMode::Halt)
reset_system(mbc, fs, power_manager, ResetMode::Halt, selected_rom);
}
}
}
gameboy.cycle_frame();
}
}

fn read_menu_options(fs: &mut Fat32, menu_options: &mut [MenuOption<FileEntry, ArrayString<{FileEntry::FILENAME_SIZE}>>; 255]) -> usize {
fn reset_system<'a>(mbc: &'a mut dyn Mbc, mut fs: Fat32Fs, mut power_manager: Power, mode: ResetMode, selected_rom: &FileEntry)->!{
let filename = get_save_filename(selected_rom);
fs.write_file(filename.as_str(), mbc.get_ram());

// delaying the reset operation so other low level tasks will have enough time to finish (like uart transmission)
delay::wait_ms(100);
alloncm marked this conversation as resolved.
Show resolved Hide resolved
power_manager.reset(mode);
}

fn try_read_save_file(selected_rom: &FileEntry, mut fs: &mut Fat32Fs) -> Option<&'static [u8]> {
let save_filename = get_save_filename(selected_rom);
let file = search_file(&mut fs, save_filename.as_str())?;

// SAFETY: The only reference to this static mut var
let ram = unsafe{&mut RAM_BUFFER[0..file.size as usize]};
fs.read_file(&file, ram);
log::info!("Found save file for selected rom: {}", file.get_name());
return Some(ram);
}

fn get_save_filename(selected_rom: &FileEntry) -> ArrayString<11> {
ArrayString::try_from(format_args!("{}SAV",&selected_rom.get_name()[..8])).unwrap()
}

fn read_menu_options(fs: &mut Fat32Fs, menu_options: &mut [MenuOption<FileEntry, ArrayString<{FileEntry::FILENAME_SIZE}>>; 255]) -> usize {
const FILES_PER_LIST:usize = 20;

let mut menu_options_size = 0;
let mut root_dir_offset = 0;
const FILES_PER_LIST:usize = 20;
'search_dir_loop: loop{
loop{
let dir_entries = fs.root_dir_list::<FILES_PER_LIST>(root_dir_offset);
for entry in dir_entries{
let Some(entry) = entry else {break 'search_dir_loop};
for entry in &dir_entries{
let extension = entry.get_extension();
if extension.eq_ignore_ascii_case("gb") || extension.eq_ignore_ascii_case("gbc"){
menu_options[menu_options_size] = MenuOption{ value: entry.clone(), prompt: ArrayString::from(entry.get_name()).unwrap() };
menu_options_size += 1;
log::debug!("Detected ROM: {}", entry.get_name());
}
}
// The fact that its not completely full indicates that there are no more unread entries left
if dir_entries.remaining_capacity() != 0{
break;
}
root_dir_offset += FILES_PER_LIST;
}
return menu_options_size;
}

#[panic_handler]
fn panic(info:&PanicInfo)->!{
log::error!("An error has occoured!");
log::error!("{}", info);

unsafe{boot::hang_led()};
fn search_file(fs:&mut Fat32Fs, filename: &str)->Option<FileEntry>{
let mut root_dir_offset = 0;
const FILES_PER_LIST:usize = 20;
alloncm marked this conversation as resolved.
Show resolved Hide resolved
loop{
let dir_entries = fs.root_dir_list::<FILES_PER_LIST>(root_dir_offset);
for entry in &dir_entries{
if entry.get_name() == filename{
return Some(entry.clone());
}
}
if dir_entries.remaining_capacity() != 0{
return None;
}
root_dir_offset += FILES_PER_LIST;
}
}
Loading
Loading