mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
mempak support (#14)
* mempak support * fix * read and write * format func * formatting * finish
This commit is contained in:
@@ -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 {
|
||||
|
||||
+49
-10
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ pub struct PifChannel {
|
||||
pub rx: Option<usize>,
|
||||
pub rx_buf: Option<usize>,
|
||||
pub process: Option<fn(&mut device::Device, usize)>,
|
||||
pub pak_handler: Option<device::controller::PakHandler>,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -6,6 +6,7 @@ pub enum SaveTypes {
|
||||
Eeprom16k,
|
||||
Sram,
|
||||
Flash,
|
||||
Mempak,
|
||||
}
|
||||
|
||||
pub struct Paths {
|
||||
@@ -19,6 +20,7 @@ pub struct Saves {
|
||||
pub eeprom: Vec<u8>,
|
||||
pub sram: Vec<u8>,
|
||||
pub flash: Vec<u8>,
|
||||
pub mempak: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn get_save_type(game_id: &str) -> Vec<SaveTypes> {
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user