parent
7dc0a3902a
commit
b7d133cb37
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -99,7 +99,8 @@ pub fn init(device: &mut device::Device, rom_file: Vec<u8>) {
|
||||
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) {
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
15
src/ui.rs
15
src/ui.rs
@ -4,6 +4,9 @@ pub mod storage;
|
||||
pub mod video;
|
||||
|
||||
pub struct Ui {
|
||||
pub save_type: Vec<storage::SaveTypes>,
|
||||
pub game_name: String,
|
||||
pub game_id: String,
|
||||
pub paths: storage::Paths,
|
||||
pub saves: storage::Saves,
|
||||
pub sdl_context: Option<sdl2::Sdl>,
|
||||
@ -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),
|
||||
|
||||
@ -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<u8>,
|
||||
pub sram: Vec<u8>,
|
||||
pub flash: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn init(ui: &mut ui::Ui, game_name: &str) {
|
||||
pub fn get_save_type(game_id: &str) -> Vec<SaveTypes> {
|
||||
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<u8>;
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user