From 0224d75beaa374c8067bd6183f39de3f93c7eab3 Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:19:49 -0600 Subject: [PATCH] mempak support (#14) * mempak support * fix * read and write * format func * formatting * finish --- src/device.rs | 2 + src/device/controller.rs | 59 ++++++++++++++++++++----- src/device/mempak.rs | 93 ++++++++++++++++++++++++++++++++++++++++ src/device/pif.rs | 7 +++ src/ui.rs | 1 + src/ui/storage.rs | 15 +++++++ 6 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 src/device/mempak.rs diff --git a/src/device.rs b/src/device.rs index a6fa71d..85eb879 100644 --- a/src/device.rs +++ b/src/device.rs @@ -15,6 +15,7 @@ pub mod exceptions; pub mod fpu_instructions; pub mod is_viewer; pub mod memory; +pub mod mempak; pub mod mi; pub mod pi; pub mod pif; @@ -154,6 +155,7 @@ impl Device { rx: None, rx_buf: None, process: None, + pak_handler: None, }; 5], }, cart: cart_rom::Cart { diff --git a/src/device/controller.rs b/src/device/controller.rs index e5c0248..54c3e03 100644 --- a/src/device/controller.rs +++ b/src/device/controller.rs @@ -18,9 +18,16 @@ pub const JDT_JOY_ABS_COUNTERS: u16 = 0x0001; /* joystick with absolute coordina pub const JDT_JOY_PORT: u16 = 0x0004; /* has port for external paks */ //pub const JDT_VRU: u16 = 0x0100; /* VRU */ pub const PAK_CHUNK_SIZE: usize = 0x20; -pub const CONT_STATUS: u8 = 0; +pub const CONT_STATUS_PAK_NOT_PRESENT: u8 = 0; +pub const CONT_STATUS_PAK_PRESENT: u8 = 1; pub const CONT_FLAVOR: u16 = JDT_JOY_ABS_COUNTERS | JDT_JOY_PORT; +#[derive(Copy, Clone)] +pub struct PakHandler { + pub read: fn(&mut device::Device, usize, u16, usize, usize), + pub write: fn(&mut device::Device, usize, u16, usize, usize), +} + pub fn process(device: &mut device::Device, channel: usize) { let cmd = device.pif.ram[device.pif.channels[channel].tx_buf.unwrap()]; @@ -30,7 +37,13 @@ pub fn process(device: &mut device::Device, channel: usize) { device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = CONT_FLAVOR as u8; device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] = (CONT_FLAVOR >> 8) as u8; - device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = CONT_STATUS; + if device.pif.channels[channel].pak_handler.is_none() { + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = + CONT_STATUS_PAK_NOT_PRESENT; + } else { + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = + CONT_STATUS_PAK_PRESENT; + } } JCMD_CONTROLLER_READ => { let offset = device.pif.channels[channel].rx_buf.unwrap(); @@ -42,29 +55,55 @@ pub fn process(device: &mut device::Device, channel: usize) { device.pif.channels[channel].tx_buf.unwrap() + 1, device.pif.channels[channel].rx_buf.unwrap(), device.pif.channels[channel].rx_buf.unwrap() + 32, + channel, ), JCMD_PAK_WRITE => pak_write_block( device, device.pif.channels[channel].tx_buf.unwrap() + 1, device.pif.channels[channel].tx_buf.unwrap() + 3, device.pif.channels[channel].rx_buf.unwrap(), + channel, ), _ => println!("unknown controller command {}", cmd), } } -pub fn pak_read_block(device: &mut device::Device, _addr_acrc: usize, data: usize, dcrc: usize) { - //let address = (device.pif.ram[addr_acrc] << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0); +pub fn pak_read_block( + device: &mut device::Device, + addr_acrc: usize, + data: usize, + dcrc: usize, + channel: usize, +) { + let address = + (device.pif.ram[addr_acrc] as u16) << 8 | (device.pif.ram[addr_acrc + 1] & 0xe0) as u16; + let handler = device.pif.channels[channel].pak_handler; - //TODO: paks - device.pif.ram[dcrc] = !pak_data_crc(device, data, PAK_CHUNK_SIZE) + if !handler.is_none() { + (handler.unwrap().read)(device, channel, address, data, PAK_CHUNK_SIZE); + device.pif.ram[dcrc] = pak_data_crc(device, data, PAK_CHUNK_SIZE) + } else { + device.pif.ram[dcrc] = !pak_data_crc(device, data, PAK_CHUNK_SIZE) + } } -pub fn pak_write_block(device: &mut device::Device, _addr_acrc: usize, data: usize, dcrc: usize) { - //let address = (device.pif.ram[addr_acrc] << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0); +pub fn pak_write_block( + device: &mut device::Device, + addr_acrc: usize, + data: usize, + dcrc: usize, + channel: usize, +) { + let address = + (device.pif.ram[addr_acrc] as u16) << 8 | (device.pif.ram[addr_acrc + 1] & 0xe0) as u16; + let handler = device.pif.channels[channel].pak_handler; - //TODO: paks - device.pif.ram[dcrc] = !pak_data_crc(device, data, PAK_CHUNK_SIZE) + if !handler.is_none() { + (handler.unwrap().write)(device, channel, address, data, PAK_CHUNK_SIZE); + device.pif.ram[dcrc] = pak_data_crc(device, data, PAK_CHUNK_SIZE) + } else { + device.pif.ram[dcrc] = !pak_data_crc(device, data, PAK_CHUNK_SIZE) + } } pub fn pak_data_crc(device: &mut device::Device, data_offset: usize, size: usize) -> u8 { diff --git a/src/device/mempak.rs b/src/device/mempak.rs new file mode 100644 index 0000000..802acee --- /dev/null +++ b/src/device/mempak.rs @@ -0,0 +1,93 @@ +use crate::device; +use crate::ui; + +pub const MEMPAK_SIZE: usize = 0x8000; +pub const MPK_PAGE_SIZE: usize = 256; + +pub fn format_mempak(device: &mut device::Device) { + if device.ui.saves.mempak.len() < MEMPAK_SIZE * 4 { + device.ui.saves.mempak.resize(MEMPAK_SIZE * 4, 0); + + let page_0: [u8; MPK_PAGE_SIZE] = [ + /* Label area */ + 0x81, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, /* Main ID area */ + 0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, + 0x66, 0x25, 0x99, 0xcd, /* Unused */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /* ID area backup #1 */ + 0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, + 0x66, 0x25, 0x99, 0xcd, /* ID area backup #2 */ + 0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, + 0x66, 0x25, 0x99, 0xcd, /* Unused */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, /* ID area backup #3 */ + 0xff, 0xff, 0xff, 0xff, 0x05, 0x1a, 0x5f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, + 0x66, 0x25, 0x99, 0xcd, /* Unused */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + for i in 0..4 { + let offset = i * MEMPAK_SIZE; + + /* Fill Page 0 with pre-initialized content */ + device.ui.saves.mempak[offset..offset + MPK_PAGE_SIZE].copy_from_slice(&page_0); + + /* Fill INODE page 1 and update it's checkum */ + let start_page = 5; + for i in 1 * MPK_PAGE_SIZE..2 * start_page { + device.ui.saves.mempak[offset + i] = 0; + } + for i in (1 * MPK_PAGE_SIZE + 2 * start_page..2 * MPK_PAGE_SIZE).step_by(2) { + device.ui.saves.mempak[offset + i + 0] = 0x00; + device.ui.saves.mempak[offset + i + 1] = 0x03; + } + device.ui.saves.mempak[offset + (1 * MPK_PAGE_SIZE + 1)] = 0x71; + + /* Page 2 is identical to page 1 */ + let page1 = offset + 1 * MPK_PAGE_SIZE; + let page2 = offset + 2 * MPK_PAGE_SIZE; + let page1data = device.ui.saves.mempak[page1..page1 + MPK_PAGE_SIZE].to_vec(); + device.ui.saves.mempak[page2..page2 + MPK_PAGE_SIZE].copy_from_slice(&page1data); + + /* Remaining pages DIR+DATA (3...) are initialized with 0x00 */ + for i in 3 * MPK_PAGE_SIZE..MEMPAK_SIZE - 3 * MPK_PAGE_SIZE { + device.ui.saves.mempak[offset + i] = 0; + } + } + } +} + +pub fn read(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) { + if (address as usize) < MEMPAK_SIZE { + format_mempak(device); + + let offset = (channel * MEMPAK_SIZE) + address as usize; + device.pif.ram[data..data + size] + .copy_from_slice(&device.ui.saves.mempak[offset..offset + size]) + } else { + for i in 0..size { + device.pif.ram[data + i] = 0; + } + } +} + +pub fn write(device: &mut device::Device, channel: usize, address: u16, data: usize, size: usize) { + if (address as usize) < MEMPAK_SIZE { + format_mempak(device); + + let offset = (channel * MEMPAK_SIZE) + address as usize; + device.ui.saves.mempak[offset..offset + size] + .copy_from_slice(&device.pif.ram[data..data + size]); + + ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Mempak); + } +} diff --git a/src/device/pif.rs b/src/device/pif.rs index 865a0e1..da04114 100644 --- a/src/device/pif.rs +++ b/src/device/pif.rs @@ -14,6 +14,7 @@ pub struct PifChannel { pub rx: Option, pub rx_buf: Option, pub process: Option, + pub pak_handler: Option, } pub const PIF_RAM_SIZE: usize = 64; @@ -224,6 +225,12 @@ pub fn init(device: &mut device::Device) { device.pif.ram[0x26] = device.cart.cic_seed; device.pif.ram[0x27] = device.cart.cic_seed; + let mempak_handler = device::controller::PakHandler { + read: device::mempak::read, + write: device::mempak::write, + }; + + device.pif.channels[0].pak_handler = Some(mempak_handler); device.pif.channels[0].process = Some(device::controller::process); device.pif.channels[4].process = Some(device::cart::process) } diff --git a/src/ui.rs b/src/ui.rs index 24d45bc..6ab45f0 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -41,6 +41,7 @@ impl Ui { eeprom: Vec::new(), sram: Vec::new(), flash: Vec::new(), + mempak: Vec::new(), }, sdl_context: Some(sdl_context), video_subsystem: Some(video_subsystem), diff --git a/src/ui/storage.rs b/src/ui/storage.rs index e0abd8e..2d9ef49 100644 --- a/src/ui/storage.rs +++ b/src/ui/storage.rs @@ -6,6 +6,7 @@ pub enum SaveTypes { Eeprom16k, Sram, Flash, + Mempak, } pub struct Paths { @@ -19,6 +20,7 @@ pub struct Saves { pub eeprom: Vec, pub sram: Vec, pub flash: Vec, + pub mempak: Vec, } pub fn get_save_type(game_id: &str) -> Vec { @@ -106,6 +108,11 @@ pub fn init(ui: &mut ui::Ui) { ui.paths .fla_file_path .push(ui.game_name.to_owned() + ".fla"); + + ui.paths.pak_file_path = base_path.clone(); + ui.paths + .pak_file_path + .push(ui.game_name.to_owned() + ".mpk"); } pub fn load_saves(ui: &mut ui::Ui) { @@ -121,6 +128,10 @@ pub fn load_saves(ui: &mut ui::Ui) { if fla.is_ok() { ui.saves.flash = fla.unwrap(); } + let mempak = std::fs::read(&mut ui.paths.pak_file_path); + if mempak.is_ok() { + ui.saves.mempak = mempak.unwrap(); + } } pub fn write_save(ui: &mut ui::Ui, save_type: SaveTypes) { @@ -139,6 +150,10 @@ pub fn write_save(ui: &mut ui::Ui, save_type: SaveTypes) { path = ui.paths.fla_file_path.as_ref(); data = ui.saves.flash.as_ref(); } + SaveTypes::Mempak => { + path = ui.paths.pak_file_path.as_ref(); + data = ui.saves.mempak.as_ref(); + } } let result = std::fs::write(path, data); if result.is_err() {