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 23 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
7 changes: 7 additions & 0 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 @@ -9,4 +9,7 @@ members = [
[workspace.package]
version = "4.0.0"
authors = ["alloncm <[email protected]>"]
rust-version = "1.70" # cause of once cell
rust-version = "1.70" # cause of once cell

[profile.release]
lto = true # Samller binaries and faster code
alloncm marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 4 additions & 4 deletions core/src/mmu/carts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,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 @@ -32,7 +32,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
1 change: 1 addition & 0 deletions rpi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ magenboy_common = {path = "../common"}
log = "0.4"
cfg-if = "1"
bitfield-struct = "0.5"
arrayvec = {version = "0.7", default-features = false}
alloncm marked this conversation as resolved.
Show resolved Hide resolved
libc = {version = "0.2", optional = true}
nix = {version = "0.24", optional = true}
crossbeam-channel = {version = "0.5", optional = true}
Expand Down
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
86 changes: 64 additions & 22 deletions rpi/src/bin/baremetal/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ mod logging;
use core::panic::PanicInfo;

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::*, 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::{external_memory_bus::Bootrom, carts::Mbc}, utils::stack_string::StackString};
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);

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 @@ -26,14 +35,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,7 +55,8 @@ pub extern "C" fn main()->!{

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 save_data = try_read_save_file(selected_rom, &mut fs);
let mbc = initialize_mbc(&rom[0..selected_rom.size as usize], save_data, None);

let mut gameboy = GameBoy::new(mbc, joypad_provider, magenboy_rpi::BlankAudioDevice, gfx, Bootrom::None, None);
log::info!("Initialized gameboy!");
Expand All @@ -61,45 +71,77 @@ 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, StackString<{FileEntry::FILENAME_SIZE}>>; 255]) -> usize {
fn reset_system<'a>(mbc: &'a 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())?;
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) -> StackString<11> {
StackString::from_args(format_args!("{}SAV",&selected_rom.get_name()[..8]))
}

fn read_menu_options(fs: &mut Fat32Fs, menu_options: &mut [MenuOption<FileEntry, StackString<{FileEntry::FILENAME_SIZE}>>; 255]) -> usize {
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: StackString::from(entry.get_name()) };
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;
}
}
64 changes: 50 additions & 14 deletions rpi/src/drivers/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::as_mut_buffer;
#[repr(C, packed)]
struct PartitionEntry{
status:u8,
frist_sector_chs_address:[u8;3],
first_sector_chs_address:[u8;3],
partition_type:u8,
last_sector_chs_address:[u8;3],
first_sector_index:u32,
Expand All @@ -13,7 +13,7 @@ struct PartitionEntry{

impl Default for PartitionEntry{
fn default() -> Self {
Self { status: Default::default(), frist_sector_chs_address: Default::default(), partition_type: Default::default(), last_sector_chs_address: Default::default(), first_sector_index: Default::default(), sectors_count: Default::default() }
Self { status: Default::default(), first_sector_chs_address: Default::default(), partition_type: Default::default(), last_sector_chs_address: Default::default(), first_sector_index: Default::default(), sectors_count: Default::default() }
}
}

Expand All @@ -37,12 +37,14 @@ pub struct Disk{
}

impl Disk{
const BLOCK_SIZE:u32 = Emmc::get_block_size();

pub fn new()->Self{
let mut emmc = unsafe{PERIPHERALS.take_emmc()};
emmc.init();

let mut mbr = MasterBootRecord::default();
let buffer = unsafe{as_mut_buffer(&mut mbr)};
let buffer = as_mut_buffer(&mut mbr);

if !emmc.read(buffer){
core::panic!("Cant read MBR from disk");
Expand All @@ -54,24 +56,58 @@ impl Disk{
Self { emmc, mbr }
}

/// Returns the number of blocks the read operation fetched
/// The user knows how much of the buffer is filled
pub fn read(&mut self, block_index:u32, buffer:&mut [u8]) -> u32 {
let block_size = self.emmc.get_block_size();
let buffer_size = buffer.len();
if buffer_size % block_size as usize != 0{
core::panic!("buffer size must be a division of block size: {}", block_size);
pub fn read(&mut self, block_index:u32, buffer:&mut [u8]){
let buffer_len_reminder = buffer.len() % Self::BLOCK_SIZE as usize;
let max_aligned_buffer_len = buffer.len() - buffer_len_reminder;
let aligned_buffer = &mut buffer[..max_aligned_buffer_len];

self.emmc.seek((block_index * Self::BLOCK_SIZE) as u64);
// Verify the buffer is larger than a single block
if aligned_buffer.len() != 0{
self.emmc_read(aligned_buffer);
// early return if the buffer is aligned
if buffer_len_reminder == 0 {return};
}
self.emmc.seek((block_index * block_size) as u64);
// handle the case buffer length is not aligned for block size
let mut temp_buffer:[u8;Self::BLOCK_SIZE as usize] = [0;Self::BLOCK_SIZE as usize];
self.emmc.seek(((block_index + (max_aligned_buffer_len as u32 / Self::BLOCK_SIZE)) * Self::BLOCK_SIZE) as u64);
self.emmc_read(&mut temp_buffer);
buffer[max_aligned_buffer_len..].copy_from_slice(&mut temp_buffer[..buffer_len_reminder]);
}

fn emmc_read(&mut self, buffer: &mut [u8]) {
if !self.emmc.read(buffer){
core::panic!("Error while reading object of size: {}", buffer_size);
core::panic!("Error while reading object of size: {}", buffer.len());
}
}

pub fn write(&mut self, block_index:u32, buffer:&[u8]){
let buffer_len_reminder = buffer.len() % Self::BLOCK_SIZE as usize;
let max_aligned_buffer_len = buffer.len() - buffer_len_reminder;
let aligned_buffer = &buffer[..max_aligned_buffer_len];

self.emmc.seek((block_index * Self::BLOCK_SIZE) as u64);
if aligned_buffer.len() != 0{
self.emmc_write(aligned_buffer);
// early return since the buffer is aligned
if buffer_len_reminder == 0 {return};
}
// handle the case buffer length is not aligned for block size
let mut temp_buffer:[u8;Self::BLOCK_SIZE as usize] = [0;Self::BLOCK_SIZE as usize];
temp_buffer[..buffer_len_reminder].copy_from_slice(&buffer[max_aligned_buffer_len..]);
self.emmc.seek(((block_index + (max_aligned_buffer_len as u32 / Self::BLOCK_SIZE)) * Self::BLOCK_SIZE) as u64);
self.emmc_write(&temp_buffer);
}

fn emmc_write(&mut self, buffer: &[u8]) {
if !self.emmc.write(buffer){
core::panic!("Error while writing object of size: {}", buffer.len());
}
return buffer_size as u32 / block_size;
}

pub fn get_partition_first_sector_index(&self, partition_index:u8)->u32{
self.mbr.partitions[partition_index as usize].first_sector_index
}

pub fn get_block_size(&self)->u32{self.emmc.get_block_size()}
pub const fn get_block_size()->u32{Self::BLOCK_SIZE}
}
Loading