From b7d133cb37aeef745b2c49a7b5893a4a2c30a4fd Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Thu, 5 Oct 2023 11:43:13 -0600 Subject: [PATCH] sram support (#5) * sram support * more --- src/device.rs | 2 - src/device/cart.rs | 43 ++++++++++++----- src/device/cart_rom.rs | 3 +- src/device/pi.rs | 4 +- src/device/sram.rs | 98 +++++++++++++++++++++++++++++++++++-- src/main.rs | 2 +- src/ui.rs | 15 +++++- src/ui/storage.rs | 106 +++++++++++++++++++++++++++++++++++++++-- 8 files changed, 244 insertions(+), 29 deletions(-) diff --git a/src/device.rs b/src/device.rs index d9be928..3ed2e22 100644 --- a/src/device.rs +++ b/src/device.rs @@ -47,7 +47,6 @@ pub struct Device { ai: ai::Ai, si: si::Si, ri: ri::Ri, - pub game: String, } impl Device { @@ -60,7 +59,6 @@ impl Device { byte_swap = 3; } Device { - game: String::new(), ui: ui::Ui::new(), byte_swap: byte_swap, cpu: cpu::Cpu { diff --git a/src/device/cart.rs b/src/device/cart.rs index bfeb53b..5e1762d 100644 --- a/src/device/cart.rs +++ b/src/device/cart.rs @@ -8,8 +8,7 @@ pub const JCMD_RESET: u8 = 0xff; //pub const JDT_AF_RTC: u16 = 0x1000; /* RTC */ pub const JDT_EEPROM_4K: u16 = 0x8000; /* 4k EEPROM */ -//pub const JDT_EEPROM_16K: u16 = 0xc000; /* 16k EEPROM */ -pub const EEPROM_TYPE: u16 = JDT_EEPROM_4K; // todo, support 16k eeprom +pub const JDT_EEPROM_16K: u16 = 0xc000; /* 16k EEPROM */ pub const EEPROM_BLOCK_SIZE: usize = 8; pub fn process(device: &mut device::Device, channel: usize) { @@ -18,9 +17,19 @@ pub fn process(device: &mut device::Device, channel: usize) { match cmd { JCMD_RESET => { /* reset */ } JCMD_STATUS => { - device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = EEPROM_TYPE as u8; + let eeprom_type; + if device + .ui + .save_type + .contains(&ui::storage::SaveTypes::Eeprom16k) + { + eeprom_type = JDT_EEPROM_16K; + } else { + eeprom_type = JDT_EEPROM_4K; + } + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = eeprom_type as u8; device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] = - (EEPROM_TYPE >> 8) as u8; + (eeprom_type >> 8) as u8; device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = 0; } JCMD_EEPROM_READ => { @@ -45,14 +54,16 @@ pub fn process(device: &mut device::Device, channel: usize) { pub fn eeprom_read_block(device: &mut device::Device, block: usize, offset: usize) { let address = device.pif.ram[block as usize] as usize * EEPROM_BLOCK_SIZE; - if address < device.ui.saves.eeprom.len() { - device.pif.ram[offset..offset + EEPROM_BLOCK_SIZE] - .copy_from_slice(&device.ui.saves.eeprom[address..address + EEPROM_BLOCK_SIZE]); - } else { - for i in 0..EEPROM_BLOCK_SIZE { - device.pif.ram[offset + i] = 0; - } + if address + EEPROM_BLOCK_SIZE > device.ui.saves.eeprom.len() { + device + .ui + .saves + .eeprom + .resize(address + EEPROM_BLOCK_SIZE, 0) } + + device.pif.ram[offset..offset + EEPROM_BLOCK_SIZE] + .copy_from_slice(&device.ui.saves.eeprom[address..address + EEPROM_BLOCK_SIZE]); } pub fn eeprom_write_block(device: &mut device::Device, block: usize, offset: usize, status: usize) { @@ -71,5 +82,13 @@ pub fn eeprom_write_block(device: &mut device::Device, block: usize, offset: usi device.pif.ram[status as usize] = 0x00; - ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Eeprom); + let mut save_type = ui::storage::SaveTypes::Eeprom4k; + if device + .ui + .save_type + .contains(&ui::storage::SaveTypes::Eeprom16k) + { + save_type = ui::storage::SaveTypes::Eeprom16k + } + ui::storage::write_save(&mut device.ui, save_type); } diff --git a/src/device/cart_rom.rs b/src/device/cart_rom.rs index 808f610..62c0000 100644 --- a/src/device/cart_rom.rs +++ b/src/device/cart_rom.rs @@ -99,7 +99,8 @@ pub fn init(device: &mut device::Device, rom_file: Vec) { let data = std::str::from_utf8(&device.cart.rom[0x20 as usize..(0x20 + 0x14) as usize]).unwrap(); let hash = calculate_hash(&device.cart.rom); - device.game = format!("{}-{}", data.trim(), hash); + device.ui.game_name = format!("{}-{}", data.trim(), hash); + device.ui.game_id = String::from_utf8(device.cart.rom[0x3B..0x3E].to_vec()).unwrap(); } pub fn set_system_region(device: &mut device::Device, country: u8) { diff --git a/src/device/pi.rs b/src/device/pi.rs index 7406169..4db371d 100644 --- a/src/device/pi.rs +++ b/src/device/pi.rs @@ -94,8 +94,8 @@ pub fn get_handler(address: u32) -> PiHandler { handler.write = device::cart_rom::dma_write; } } else if address >= device::memory::MM_DOM2_ADDR2 as u32 { - panic!("sram handler") - //RW(cart, cart_dom2); + handler.read = device::sram::dma_read; + handler.write = device::sram::dma_write; } else { panic!("unknown pi handler") } diff --git a/src/device/sram.rs b/src/device/sram.rs index 7a06aed..c95f0ca 100644 --- a/src/device/sram.rs +++ b/src/device/sram.rs @@ -1,13 +1,101 @@ use crate::device; +use crate::ui; + +pub const SRAM_MASK: usize = 0xFFFF; pub fn read_mem( - _device: &mut device::Device, - _address: u64, + device: &mut device::Device, + address: u64, _access_size: device::memory::AccessSize, ) -> u32 { - panic!("sream read"); + if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { + let masked_address = address as usize & SRAM_MASK; + + if masked_address + 4 > device.ui.saves.sram.len() { + device.ui.saves.sram.resize(masked_address + 4, 0) + } + + return u32::from_be_bytes( + device.ui.saves.sram[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + } else { + panic!("flash read") + } } -pub fn write_mem(_device: &mut device::Device, _address: u64, _value: u32, _mask: u32) { - panic!("sram write"); +pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) { + if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { + let masked_address = address as usize & SRAM_MASK; + + if masked_address + 4 > device.ui.saves.sram.len() { + device.ui.saves.sram.resize(masked_address + 4, 0) + } + + let mut data = u32::from_be_bytes( + device.ui.saves.sram[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + device::memory::masked_write_32(&mut data, value, mask); + device.ui.saves.sram[masked_address..masked_address + 4] + .copy_from_slice(&data.to_be_bytes()); + + ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Sram); + } else { + panic!("flash write") + } +} + +// cart is big endian, rdram is native endian +pub fn dma_read(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 { + if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { + let mut i = dram_addr & device::rdram::RDRAM_MASK as u32; + let mut j = cart_addr & SRAM_MASK as u32; + + if (cart_addr + length) as usize > device.ui.saves.sram.len() { + device + .ui + .saves + .sram + .resize((cart_addr + length) as usize, 0) + } + + while j < cart_addr + length { + device.ui.saves.sram[j as usize] = device.rdram.mem[i as usize ^ device.byte_swap]; + i += 1; + j += 1; + } + + ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Sram); + return device::pi::calculate_cycles(device, 2, length); + } else { + panic!("flash dma read") + } +} + +// cart is big endian, rdram is native endian +pub fn dma_write(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 { + if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { + let mut i = dram_addr & device::rdram::RDRAM_MASK as u32; + let mut j = cart_addr & SRAM_MASK as u32; + + if (cart_addr + length) as usize > device.ui.saves.sram.len() { + device + .ui + .saves + .sram + .resize((cart_addr + length) as usize, 0) + } + + while i < dram_addr + length { + device.rdram.mem[i as usize ^ device.byte_swap] = device.ui.saves.sram[j as usize]; + i += 1; + j += 1; + } + return device::pi::calculate_cycles(device, 2, length); + } else { + panic!("flash dma write") + } } diff --git a/src/main.rs b/src/main.rs index 5a0af97..033077b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ fn main() { device::vi::init(&mut device); device::cpu::init(&mut device); - ui::storage::init(&mut device.ui, device.game.as_str()); + ui::storage::init(&mut device.ui); ui::storage::load_saves(&mut device.ui); device::cpu::run(&mut device); diff --git a/src/ui.rs b/src/ui.rs index dbcbb6f..24d45bc 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -4,6 +4,9 @@ pub mod storage; pub mod video; pub struct Ui { + pub save_type: Vec, + pub game_name: String, + pub game_id: String, pub paths: storage::Paths, pub saves: storage::Saves, pub sdl_context: Option, @@ -25,10 +28,20 @@ impl Ui { .build() .unwrap(); Ui { + save_type: vec![], + game_name: String::new(), + game_id: String::new(), paths: storage::Paths { eep_file_path: std::path::PathBuf::new(), + fla_file_path: std::path::PathBuf::new(), + sra_file_path: std::path::PathBuf::new(), + pak_file_path: std::path::PathBuf::new(), + }, + saves: storage::Saves { + eeprom: Vec::new(), + sram: Vec::new(), + flash: Vec::new(), }, - saves: storage::Saves { eeprom: Vec::new() }, sdl_context: Some(sdl_context), video_subsystem: Some(video_subsystem), audio_subsystem: Some(audio_subsystem), diff --git a/src/ui/storage.rs b/src/ui/storage.rs index 3cc4db5..caeecee 100644 --- a/src/ui/storage.rs +++ b/src/ui/storage.rs @@ -1,18 +1,86 @@ use crate::ui; +#[derive(PartialEq)] pub enum SaveTypes { - Eeprom, + Eeprom4k, + Eeprom16k, + Sram, + Flash, } pub struct Paths { pub eep_file_path: std::path::PathBuf, + pub sra_file_path: std::path::PathBuf, + pub fla_file_path: std::path::PathBuf, + pub pak_file_path: std::path::PathBuf, } pub struct Saves { pub eeprom: Vec, + pub sram: Vec, + pub flash: Vec, } -pub fn init(ui: &mut ui::Ui, game_name: &str) { +pub fn get_save_type(game_id: &str) -> Vec { + match game_id { + "NB7" | // Banjo-Tooie [Banjo to Kazooie no Daiboken 2 (J)] + "NGT" | // City Tour GrandPrix - Zen Nihon GT Senshuken + "NFU" | // Conker's Bad Fur Day + "NCW" | // Cruis'n World + "NCZ" | // Custom Robo V2 + "ND6" | // Densha de Go! 64 + "NDO" | // Donkey Kong 64 + "ND2" | // Doraemon 2: Nobita to Hikari no Shinden + "N3D" | // Doraemon 3: Nobita no Machi SOS! + "NMX" | // Excitebike 64 + "NGC" | // GT 64: Championship Edition + "NIM" | // Ide Yosuke no Mahjong Juku + "NNB" | // Kobe Bryant in NBA Courtside + "NMV" | // Mario Party 3 + "NM8" | // Mario Tennis + "NEV" | // Neon Genesis Evangelion + "NPP" | // Parlor! Pro 64: Pachinko Jikki Simulation Game + "NUB" | // PD Ultraman Battle Collection 64 + "NPD" | // Perfect Dark + "NRZ" | // Ridge Racer 64 + "NR7" | // Robot Poncots 64: 7tsu no Umi no Caramel + "NEP" | // Star Wars Episode I: Racer + "NYS" // Yoshi's Story + => { + return vec![SaveTypes::Eeprom16k] + } + "NCC" | // Command & Conquer + "NDA" | // Derby Stallion 64 + "NAF" | // Doubutsu no Mori + "NJF" | // Jet Force Gemini [Star Twins (J)] + "NKJ" | // Ken Griffey Jr.'s Slugfest + "NZS" | // Legend of Zelda: Majora's Mask [Zelda no Densetsu - Mujura no Kamen (J)] + "NM6" | // Mega Man 64 + "NCK" | // NBA Courtside 2 featuring Kobe Bryant + "NMQ" | // Paper Mario + "NPN" | // Pokemon Puzzle League + "NPF" | // Pokemon Snap [Pocket Monsters Snap (J)] + "NPO" | // Pokemon Stadium + "CP2" | // Pocket Monsters Stadium 2 (J) + "NP3" | // Pokemon Stadium 2 [Pocket Monsters Stadium - Kin Gin (J)] + "NRH" | // Rockman Dash - Hagane no Boukenshin (J) + "NSQ" | // StarCraft 64 + "NT9" | // Tigger's Honey Hunt + "NW4" | // WWF No Mercy + "NDP" // Dinosaur Planet (Unlicensed) + =>{ + return vec![SaveTypes::Flash] + } + _ => { + return vec![SaveTypes::Eeprom4k, SaveTypes::Sram] + } + } +} + +pub fn init(ui: &mut ui::Ui) { + let id = ui.game_id.as_str(); + ui.save_type = get_save_type(id); + let mut base_path = dirs::data_dir().unwrap(); base_path.push("gopher64"); base_path.push("saves"); @@ -22,8 +90,20 @@ pub fn init(ui: &mut ui::Ui, game_name: &str) { panic!("could not create save dir") } - ui.paths.eep_file_path = base_path; - ui.paths.eep_file_path.push(game_name.to_owned() + ".eep"); + ui.paths.eep_file_path = base_path.clone(); + ui.paths + .eep_file_path + .push(ui.game_name.to_owned() + ".eep"); + + ui.paths.sra_file_path = base_path.clone(); + ui.paths + .sra_file_path + .push(ui.game_name.to_owned() + ".sra"); + + ui.paths.fla_file_path = base_path.clone(); + ui.paths + .fla_file_path + .push(ui.game_name.to_owned() + ".fla"); } pub fn load_saves(ui: &mut ui::Ui) { @@ -31,16 +111,32 @@ pub fn load_saves(ui: &mut ui::Ui) { if eep.is_ok() { ui.saves.eeprom = eep.unwrap(); } + let sra = std::fs::read(&mut ui.paths.sra_file_path); + if sra.is_ok() { + ui.saves.sram = sra.unwrap(); + } + let fla = std::fs::read(&mut ui.paths.fla_file_path); + if fla.is_ok() { + ui.saves.flash = fla.unwrap(); + } } pub fn write_save(ui: &mut ui::Ui, save_type: SaveTypes) { let path: &std::path::Path; let data: &Vec; match save_type { - SaveTypes::Eeprom => { + SaveTypes::Eeprom4k | SaveTypes::Eeprom16k => { path = ui.paths.eep_file_path.as_ref(); data = ui.saves.eeprom.as_ref(); } + SaveTypes::Sram => { + path = ui.paths.sra_file_path.as_ref(); + data = ui.saves.sram.as_ref(); + } + SaveTypes::Flash => { + path = ui.paths.fla_file_path.as_ref(); + data = ui.saves.flash.as_ref(); + } } let result = std::fs::write(path, data); if result.is_err() {