Compare commits

...

8 Commits

Author SHA1 Message Date
Logan McNaughton 920a0323c0 cleanup 2025-02-03 16:49:42 +01:00
Logan McNaughton 14e45be300 SD card emulation (#214)
* SD card emulation

* more

* more

* more

* more

* more

* more

* more

* better save name

* better save name

* better save name

* more
2025-02-03 16:41:46 +01:00
Logan McNaughton fba6a4a6d4 support for Advanced Homebrew ROM Header (#213)
* support for Advanced Homebrew ROM Header

* more

* more
2025-02-03 10:22:21 +01:00
Logan McNaughton 1efdeafc31 bump to 1.0.2 (#212) 2025-02-02 12:54:48 +01:00
Logan McNaughton a7c2c26d78 minor tweak to interrupt system (#211) 2025-02-02 11:23:32 +01:00
Logan McNaughton 7d63a694cc fix compare interrupt (#210) 2025-02-01 20:47:26 +01:00
Logan McNaughton 5c98a08fe4 remove schedule_rcp_interrupt (#208)
* remove schedule_rcp_interrupt

* more

* more

* more
2025-02-01 19:16:38 +01:00
Logan McNaughton 39ffad3af3 Bump to 1.0.1 (#206) 2025-01-30 21:08:05 +01:00
18 changed files with 392 additions and 86 deletions
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "gopher64"
version = "1.0.0"
version = "1.0.2"
edition = "2021"
rust-version = "1.82"
@@ -25,6 +25,7 @@ mimalloc = "0.1"
regex = "1.11"
tungstenite = "0.26"
socket2 = "0.5"
fatfs = "0.3"
postcard = { version = "1.1", features = ["use-std"] }
clap = { version = "4.4", features = ["derive"] }
+20 -12
View File
@@ -19,7 +19,6 @@ use std::io::Read;
pub mod ai;
pub mod cache;
pub mod cart;
pub mod cart_rom;
pub mod controller;
pub mod cop0;
pub mod cop1;
@@ -42,13 +41,12 @@ pub mod rsp_interface;
pub mod rsp_su_instructions;
pub mod rsp_vu_instructions;
pub mod si;
pub mod sram;
pub mod tlb;
pub mod unmapped;
pub mod vi;
pub fn run_game(rom_contents: Vec<u8>, device: &mut Device, fullscreen: bool) {
cart_rom::init(device, rom_contents); // cart needs to come before rdram
cart::rom::init(device, rom_contents); // cart needs to come before rdram
// rdram pointer is shared with parallel-rdp
rdram::init(device);
@@ -68,9 +66,9 @@ pub fn run_game(rom_contents: Vec<u8>, device: &mut Device, fullscreen: bool) {
vi::init(device);
cpu::init(device);
ui::storage::init(&mut device.ui);
ui::storage::init(&mut device.ui, &device.cart.rom);
ui::storage::load_saves(&mut device.ui, &mut device.netplay);
cart_rom::load_rom_save(device);
cart::rom::load_rom_save(device);
cpu::run(device);
@@ -163,18 +161,19 @@ pub struct Device {
byte_swap: usize,
pub cpu: cpu::Cpu,
pif: pif::Pif,
cart: cart_rom::Cart,
cart: cart::rom::Cart,
memory: memory::Memory,
pub rsp: rsp_interface::Rsp,
pub rdp: rdp::Rdp,
pub rdram: rdram::Rdram,
mi: mi::Mi,
pi: pi::Pi,
sc64: cart::sc64::Sc64,
pub vi: vi::Vi,
ai: ai::Ai,
si: si::Si,
ri: ri::Ri,
flashram: sram::Flashram,
flashram: cart::sram::Flashram,
pub vru: controller::vru::Vru,
}
@@ -196,6 +195,8 @@ impl Device {
regs: [0; cop0::COP0_REGS_COUNT as usize],
reg_write_masks: [0; cop0::COP0_REGS_COUNT as usize],
reg_latch: 0,
is_event: false,
pending_compare_interrupt: false,
instrs: [cop0::reserved; 32],
instrs2: [cop0::reserved; 32],
tlb_lut_w: vec![
@@ -288,13 +289,13 @@ impl Device {
change_pak: controller::PakType::None,
}; 5],
},
cart: cart_rom::Cart {
cart: cart::rom::Cart {
rom: Vec::new(),
is_viewer_buffer: [0; 0xFFFF],
pal: false,
latch: 0,
cic_seed: 0,
cic_type: cart_rom::CicType::CicNus6102,
cic_type: cart::rom::CicType::CicNus6102,
rdram_size_offset: 0,
rtc: cart::AfRtc { control: 0x0200 },
},
@@ -387,6 +388,13 @@ impl Device {
pi: pi::Pi {
regs: [0; pi::PI_REGS_COUNT as usize],
},
sc64: cart::sc64::Sc64 {
regs: [0; cart::sc64::SC64_REGS_COUNT as usize],
regs_locked: true,
cfg: [0; cart::sc64::SC64_CFG_COUNT as usize],
sector: 0,
buffer: [0; 8192],
},
ai: ai::Ai {
regs: [0; ai::AI_REGS_COUNT as usize],
last_read: 0,
@@ -414,12 +422,12 @@ impl Device {
limiter: None,
vi_counter: 0,
},
flashram: sram::Flashram {
flashram: cart::sram::Flashram {
status: 0,
erase_page: 0,
page_buf: [0xff; 128],
silicon_id: [sram::FLASHRAM_TYPE_ID, sram::MX29L1100_ID],
mode: sram::FlashramMode::ReadArray,
silicon_id: [cart::sram::FLASHRAM_TYPE_ID, cart::sram::MX29L1100_ID],
mode: cart::sram::FlashramMode::ReadArray,
},
vru: controller::vru::Vru {
status: 0,
+1 -1
View File
@@ -68,7 +68,7 @@ fn do_dma(device: &mut device::Device) {
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.ai.fifo[0].duration,
dma_event,
);
device::mi::schedule_rcp_interrupt(device, device::mi::MI_INTR_AI);
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI);
}
fn fifo_push(device: &mut device::Device) {
+4
View File
@@ -4,6 +4,10 @@ use chrono::Timelike;
use crate::device;
use crate::ui;
pub mod rom;
pub mod sc64;
pub mod sram;
const JCMD_STATUS: u8 = 0x00;
const JCMD_EEPROM_READ: u8 = 0x04;
const JCMD_EEPROM_WRITE: u8 = 0x05;
+233
View File
@@ -0,0 +1,233 @@
use crate::device;
pub const SDCARD_SIZE: usize = 0x4000000;
const SC64_SCR_REG: u32 = 0;
const SC64_DATA0_REG: u32 = 1;
const SC64_DATA1_REG: u32 = 2;
const SC64_IDENTIFIER_REG: u32 = 3;
const SC64_KEY_REG: u32 = 4;
//const SC64_IRQ_REG: u32 = 5;
//const SC64_AUX_REG: u32 = 6;
pub const SC64_REGS_COUNT: u32 = 7;
pub const SC64_CFG_COUNT: u32 = 15;
const SC64_BUFFER_MASK: usize = 0x1FFF;
pub struct Sc64 {
pub buffer: [u8; 8192],
pub regs: [u32; SC64_REGS_COUNT as usize],
pub regs_locked: bool,
pub cfg: [u32; SC64_CFG_COUNT as usize],
pub sector: u32,
}
fn format_sdcard(device: &mut device::Device) {
if device.ui.saves.sdcard.0.is_empty() {
device.ui.saves.sdcard.0.resize(SDCARD_SIZE, 0);
let buf = std::io::Cursor::new(&mut device.ui.saves.sdcard.0);
fatfs::format_volume(buf, fatfs::FormatVolumeOptions::new()).unwrap();
}
}
pub fn read_regs(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
if device.sc64.regs_locked {
return 0;
}
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
SC64_SCR_REG | SC64_DATA0_REG | SC64_DATA1_REG => device.sc64.regs[reg as usize],
SC64_IDENTIFIER_REG => 0x53437632,
_ => panic!("unknown read reg {} address {:x}", reg, address),
}
}
pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let reg = (address & 0xFFFF) >> 2;
match reg as u32 {
SC64_KEY_REG => {
device::memory::masked_write_32(&mut device.sc64.regs[reg as usize], value, mask);
if device.sc64.regs[SC64_KEY_REG as usize] == 0x4F434B5F {
device.sc64.regs_locked = false;
} else if device.sc64.regs[SC64_KEY_REG as usize] == 0xFFFFFFFF {
device.sc64.regs_locked = true;
}
}
SC64_DATA0_REG | SC64_DATA1_REG => {
if !device.sc64.regs_locked {
device::memory::masked_write_32(&mut device.sc64.regs[reg as usize], value, mask);
}
}
SC64_SCR_REG => {
if !device.sc64.regs_locked {
match char::from_u32(value & mask).unwrap() {
'c' => {
// get config
device.sc64.regs[SC64_DATA1_REG as usize] =
device.sc64.cfg[device.sc64.regs[SC64_DATA0_REG as usize] as usize]
}
'C' => {
// set config
std::mem::swap(
&mut device.sc64.cfg
[device.sc64.regs[SC64_DATA0_REG as usize] as usize],
&mut device.sc64.regs[SC64_DATA1_REG as usize],
);
}
'i' => {
// sd card operation
match device.sc64.regs[SC64_DATA1_REG as usize] {
0 => { //Init SD card
}
1 => { //Deinit SD card
}
_ => {
panic!(
"unknown sc64 sd card operation: {}",
device.sc64.regs[SC64_DATA1_REG as usize]
)
}
}
}
'I' => {
// set sd sector
device.sc64.sector = device.sc64.regs[SC64_DATA0_REG as usize];
}
's' => {
format_sdcard(device);
// read sd card
let address = device.sc64.regs[SC64_DATA0_REG as usize] as u64;
let offset = (device.sc64.sector * 512) as usize;
let length = (device.sc64.regs[SC64_DATA1_REG as usize] * 512) as usize;
let mut i = 0;
while i < length {
if offset + i < device.ui.saves.sdcard.0.len() {
let data = u32::from_be_bytes(
device.ui.saves.sdcard.0[(offset + i)..(offset + i + 4)]
.try_into()
.unwrap(),
);
device.memory.memory_map_write[(address >> 16) as usize](
device,
address + i as u64,
data,
0xFFFFFFFF,
);
} else {
panic!("sd card read out of bounds")
}
i += 4;
}
}
'S' => {
format_sdcard(device);
// write sd card
let address = device.sc64.regs[SC64_DATA0_REG as usize] as u64;
let offset = (device.sc64.sector * 512) as usize;
let length = (device.sc64.regs[SC64_DATA1_REG as usize] * 512) as usize;
let mut i = 0;
while i < length {
if offset + i < device.ui.saves.sdcard.0.len() {
let data = device.memory.memory_map_read[(address >> 16) as usize](
device,
address + i as u64,
device::memory::AccessSize::Word,
)
.to_be_bytes();
device.ui.saves.sdcard.0[(offset + i)..(offset + i + 4)]
.copy_from_slice(&data);
} else {
panic!("sd card write out of bounds")
}
i += 4;
}
device.ui.saves.sdcard.1 = true;
}
_ => {
panic!(
"unknown sc64 command: {}",
char::from_u32(value & mask).unwrap()
)
}
}
}
}
_ => panic!(
"unknown write reg {} address {:x} value {}",
reg,
address,
char::from_u32(value & mask).unwrap()
),
}
}
pub fn read_mem(
device: &mut device::Device,
address: u64,
_access_size: device::memory::AccessSize,
) -> u32 {
let masked_address = address as usize & SC64_BUFFER_MASK;
u32::from_be_bytes(
device.sc64.buffer[masked_address..masked_address + 4]
.try_into()
.unwrap(),
)
}
pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) {
let masked_address = address as usize & SC64_BUFFER_MASK;
let mut data = u32::from_be_bytes(
device.sc64.buffer[masked_address..masked_address + 4]
.try_into()
.unwrap(),
);
device::memory::masked_write_32(&mut data, value, mask);
device.sc64.buffer[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes());
}
pub fn dma_read(
device: &mut device::Device,
mut cart_addr: u32,
mut dram_addr: u32,
length: u32,
) -> u64 {
dram_addr &= device::rdram::RDRAM_MASK as u32;
cart_addr &= SC64_BUFFER_MASK as u32;
let mut i = dram_addr;
let mut j = cart_addr;
while i < dram_addr + length && i < device.rdram.size {
device.sc64.buffer[j as usize] = device.rdram.mem[i as usize ^ device.byte_swap];
i += 1;
j += 1;
}
device::pi::calculate_cycles(device, 1, length)
}
pub fn dma_write(
device: &mut device::Device,
mut cart_addr: u32,
mut dram_addr: u32,
length: u32,
) -> u64 {
dram_addr &= device::rdram::RDRAM_MASK as u32;
cart_addr &= SC64_BUFFER_MASK as u32;
let mut i = dram_addr;
let mut j = cart_addr;
while i < dram_addr + length && i < device.rdram.size {
device.rdram.mem[i as usize ^ device.byte_swap] = device.sc64.buffer[j as usize];
i += 1;
j += 1;
}
device::pi::calculate_cycles(device, 1, length)
}
+6 -5
View File
@@ -55,14 +55,14 @@ pub const COP0_CAUSE_EXCCODE_CPU: u64 = 11 << 2;
pub const COP0_CAUSE_EXCCODE_TR: u64 = 13 << 2;
pub const COP0_CAUSE_EXCCODE_FPE: u64 = 15 << 2;
pub const COP0_CAUSE_IP2: u64 = 1 << 10;
const COP0_CAUSE_IP7: u64 = 1 << 15;
pub const COP0_CAUSE_IP7: u64 = 1 << 15;
pub const COP0_CAUSE_BD: u64 = 1 << 31;
pub const COP0_CAUSE_CE1: u64 = 1 << 28;
pub const COP0_CAUSE_CE2: u64 = 1 << 29;
pub const COP0_CAUSE_IP_MASK: u64 = 0b00000000000000001111111100000000;
pub const COP0_CAUSE_EXCCODE_MASK: u64 = 0x1F << 2;
//pub const COP0_CAUSE_EXCCODE_MASK: u64 = 0x1F << 2;
pub const COP0_CONTEXT_BADVPN2_MASK: u64 = 0b00000000011111111111111111110000;
pub const COP0_XCONTEXT_BADVPN2_MASK: u64 = 0b01111111111111111111111111110000;
pub const COP0_XCONTEXT_REGION_MASK: u64 = 0b110000000000000000000000000000000;
@@ -97,6 +97,8 @@ pub struct Cop0 {
pub tlb_lut_r: Vec<device::tlb::TlbLut>,
pub tlb_lut_w: Vec<device::tlb::TlbLut>,
pub tlb_entries: [device::tlb::TlbEntry; 32],
pub is_event: bool,
pub pending_compare_interrupt: bool,
}
fn mfc0(device: &mut device::Device, opcode: u32) {
@@ -214,6 +216,7 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64)
compare_event,
);
device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_IP7;
device.cpu.cop0.pending_compare_interrupt = false;
}
COP0_STATUS_REG => {
if data & COP0_STATUS_FR != device.cpu.cop0.regs[index as usize] & COP0_STATUS_FR {
@@ -231,9 +234,7 @@ fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64)
}
fn compare_event(device: &mut device::Device) {
device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_EXCCODE_MASK;
device.cpu.cop0.regs[COP0_CAUSE_REG as usize] |= COP0_CAUSE_IP7;
device.cpu.cop0.pending_compare_interrupt = true;
device::events::create_event(
device,
device::events::EventType::Compare,
+3 -1
View File
@@ -8,7 +8,7 @@ pub enum EventType {
PI,
DP,
SP,
InterruptCheck,
Interrupt,
SPDma,
Compare,
Vru,
@@ -53,7 +53,9 @@ pub fn trigger_event(device: &mut device::Device) {
device.cpu.events[next_event_name].enabled = false;
let handler = device.cpu.events[next_event_name].handler;
device.cpu.cop0.is_event = true;
handler(device);
device.cpu.cop0.is_event = false;
set_next_event(device);
}
+32 -9
View File
@@ -1,15 +1,6 @@
use crate::device;
pub fn check_pending_interrupts(device: &mut device::Device) {
if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize]
& device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize]
& device::cop0::COP0_CAUSE_IP_MASK)
== 0
{
// interrupt disabled, or no pending interrupts
return;
}
if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize]
& (device::cop0::COP0_STATUS_IE
| device::cop0::COP0_STATUS_EXL
@@ -20,6 +11,38 @@ pub fn check_pending_interrupts(device: &mut device::Device) {
return;
}
if device.mi.regs[device::mi::MI_INTR_REG as usize]
& device.mi.regs[device::mi::MI_INTR_MASK_REG as usize]
!= 0
{
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = device::cop0::COP0_CAUSE_IP2;
} else if device.cpu.cop0.pending_compare_interrupt {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = device::cop0::COP0_CAUSE_IP7;
device.cpu.cop0.pending_compare_interrupt = false;
}
if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize]
& device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize]
& device::cop0::COP0_CAUSE_IP_MASK)
== 0
{
// interrupt disabled, or no pending interrupts
return;
}
if device.cpu.cop0.is_event {
interrupt_exception(device);
} else {
device::events::create_event(
device,
device::events::EventType::Interrupt,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize],
interrupt_exception,
);
}
}
pub fn interrupt_exception(device: &mut device::Device) {
exception_general(device, 0x180);
}
+13 -5
View File
@@ -19,6 +19,8 @@ pub const MM_CART_ROM: usize = 0x10000000;
const MM_PIF_MEM: usize = 0x1fc00000;
//const MM_DOM1_ADDR3: usize = 0x1fd00000;
const MM_IS_VIEWER: usize = 0x13ff0000;
pub const MM_SC64_BUFFER: usize = 0x1ffe0000;
const MM_SC64_REGS: usize = 0x1fff0000;
#[derive(PartialEq)]
pub enum AccessType {
@@ -142,18 +144,24 @@ pub fn init(device: &mut device::Device) {
device.memory.memory_map_read[i] = device::si::read_regs;
device.memory.memory_map_write[i] = device::si::write_regs;
} else if (MM_DOM2_ADDR2 >> 16..=(MM_DOM2_ADDR2 + 0x1FFFF) >> 16).contains(&i) {
device.memory.memory_map_read[i] = device::sram::read_mem;
device.memory.memory_map_write[i] = device::sram::write_mem;
device.memory.memory_map_read[i] = device::cart::sram::read_mem;
device.memory.memory_map_write[i] = device::cart::sram::write_mem;
} else if i >= MM_CART_ROM >> 16 && i <= (MM_CART_ROM + device.cart.rom.len() - 1) >> 16 {
device.memory.fast_read[i] = device::cart_rom::read_mem_fast;
device.memory.memory_map_read[i] = device::cart_rom::read_mem;
device.memory.memory_map_write[i] = device::cart_rom::write_mem;
device.memory.fast_read[i] = device::cart::rom::read_mem_fast;
device.memory.memory_map_read[i] = device::cart::rom::read_mem;
device.memory.memory_map_write[i] = device::cart::rom::write_mem;
} else if i >= MM_IS_VIEWER >> 16 && i <= (MM_IS_VIEWER + 0xFFFF) >> 16 {
device.memory.memory_map_read[i] = device::is_viewer::read_mem;
device.memory.memory_map_write[i] = device::is_viewer::write_mem;
} else if i >= MM_PIF_MEM >> 16 && i <= (MM_PIF_MEM + 0xFFFF) >> 16 {
device.memory.memory_map_read[i] = device::pif::read_mem;
device.memory.memory_map_write[i] = device::pif::write_mem;
} else if i >= MM_SC64_BUFFER >> 16 && i <= (MM_SC64_BUFFER + 0xFFFF) >> 16 {
device.memory.memory_map_read[i] = device::cart::sc64::read_mem;
device.memory.memory_map_write[i] = device::cart::sc64::write_mem;
} else if i >= MM_SC64_REGS >> 16 && i <= (MM_SC64_REGS + 0xFFFF) >> 16 {
device.memory.memory_map_read[i] = device::cart::sc64::read_regs;
device.memory.memory_map_write[i] = device::cart::sc64::write_regs;
}
}
device::cache::init(device)
+3 -31
View File
@@ -2,8 +2,8 @@ use crate::device;
const MI_INIT_MODE_REG: u32 = 0;
const MI_VERSION_REG: u32 = 1;
const MI_INTR_REG: u32 = 2;
const MI_INTR_MASK_REG: u32 = 3;
pub const MI_INTR_REG: u32 = 2;
pub const MI_INTR_MASK_REG: u32 = 3;
pub const MI_REGS_COUNT: u32 = 4;
/* read */
@@ -64,11 +64,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u
_ => device::memory::masked_write_32(&mut device.mi.regs[reg as usize], value, mask),
}
if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_EXCCODE_MASK;
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2;
} else {
if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] == 0 {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_IP2;
}
@@ -149,37 +145,13 @@ pub fn clear_rcp_interrupt(device: &mut device::Device, interrupt: u32) {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_IP2;
}
device::exceptions::check_pending_interrupts(device)
}
pub fn set_rcp_interrupt(device: &mut device::Device, interrupt: u32) {
device.mi.regs[MI_INTR_REG as usize] |= interrupt;
if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_EXCCODE_MASK;
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2;
}
device::exceptions::check_pending_interrupts(device)
}
pub fn schedule_rcp_interrupt(device: &mut device::Device, interrupt: u32) {
device.mi.regs[MI_INTR_REG as usize] |= interrupt;
if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 {
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &=
!device::cop0::COP0_CAUSE_EXCCODE_MASK;
device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2;
}
device::events::create_event(
device,
device::events::EventType::InterruptCheck,
device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize],
device::exceptions::check_pending_interrupts,
)
}
pub fn init(device: &mut device::Device) {
device.mi.regs[MI_VERSION_REG as usize] = 0x02020102
}
+10 -7
View File
@@ -114,15 +114,18 @@ fn dma_write(device: &mut device::Device) {
fn get_handler(address: u32) -> PiHandler {
let mut handler = PiHandler {
read: device::cart_rom::dma_read,
write: device::cart_rom::dma_write,
read: device::cart::rom::dma_read,
write: device::cart::rom::dma_write,
};
if address >= device::memory::MM_CART_ROM as u32 {
handler.read = device::cart_rom::dma_read;
handler.write = device::cart_rom::dma_write;
if address >= device::memory::MM_SC64_BUFFER as u32 {
handler.read = device::cart::sc64::dma_read;
handler.write = device::cart::sc64::dma_write;
} else if address >= device::memory::MM_CART_ROM as u32 {
handler.read = device::cart::rom::dma_read;
handler.write = device::cart::rom::dma_write;
} else if address >= device::memory::MM_DOM2_ADDR2 as u32 {
handler.read = device::sram::dma_read;
handler.write = device::sram::dma_write;
handler.read = device::cart::sram::dma_read;
handler.write = device::cart::sram::dma_write;
} else {
panic!("unknown pi handler")
}
+1 -1
View File
@@ -361,7 +361,7 @@ fn update_sp_status(device: &mut device::Device, w: u32) {
}
/* set SP interrupt */
if (w & SP_SET_INTR) != 0 && (w & SP_CLR_INTR) == 0 {
device::mi::schedule_rcp_interrupt(device, device::mi::MI_INTR_SP);
device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP);
}
/* clear / set single step */
+2
View File
@@ -121,6 +121,7 @@ impl Ui {
fla_file_path: std::path::PathBuf::new(),
sra_file_path: std::path::PathBuf::new(),
pak_file_path: std::path::PathBuf::new(),
sdcard_file_path: std::path::PathBuf::new(),
romsave_file_path: std::path::PathBuf::new(),
},
saves: storage::Saves {
@@ -128,6 +129,7 @@ impl Ui {
sram: (Vec::new(), false),
flash: (Vec::new(), false),
mempak: (Vec::new(), false),
sdcard: (Vec::new(), false),
romsave: (std::collections::HashMap::new(), false),
},
pak_audio: audio::PakAudio {
+1 -1
View File
@@ -116,7 +116,7 @@ pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) {
let min_latency = (device.ui.audio_freq * 0.02) * 4.0;
if audio_queued < min_latency {
let silence_buffer: Vec<u8> = vec![0; (min_latency - audio_queued) as usize & !3];
let silence_buffer: Vec<u8> = vec![0; min_latency as usize & !3];
if !unsafe {
sdl3_sys::audio::SDL_PutAudioStreamData(
device.ui.audio_stream,
+2 -3
View File
@@ -394,12 +394,11 @@ fn get_sessions(app: &mut GopherEguiApp, ctx: &egui::Context) {
fn parse_rom_file(file: std::path::PathBuf, tx: std::sync::mpsc::Sender<GameInfo>) {
let rom_contents = device::get_rom_contents(file.as_path());
if !rom_contents.is_empty() {
let hash = device::cart_rom::calculate_hash(&rom_contents);
let hash = device::cart::rom::calculate_hash(&rom_contents);
let game_name = std::str::from_utf8(&rom_contents[0x20..0x20 + 0x14])
.unwrap()
.trim()
.replace('\0', "")
.to_string();
.replace('\0', "");
tx.send((
hash,
game_name,
+59 -9
View File
@@ -8,6 +8,7 @@ pub enum SaveTypes {
Sram,
Flash,
Mempak,
Sdcard,
Romsave,
}
@@ -16,6 +17,7 @@ pub struct Paths {
pub sra_file_path: std::path::PathBuf,
pub fla_file_path: std::path::PathBuf,
pub pak_file_path: std::path::PathBuf,
pub sdcard_file_path: std::path::PathBuf,
pub romsave_file_path: std::path::PathBuf,
}
@@ -26,10 +28,25 @@ pub struct Saves {
pub sram: (Vec<u8>, bool),
pub flash: (Vec<u8>, bool),
pub mempak: (Vec<u8>, bool),
pub sdcard: (Vec<u8>, bool),
pub romsave: (std::collections::HashMap<u32, u8>, bool),
}
fn get_save_type(game_id: &str) -> Vec<SaveTypes> {
fn get_save_type(rom: &[u8], game_id: &str) -> Vec<SaveTypes> {
let header_type = std::str::from_utf8(rom[0x3C..0x3E].try_into().unwrap());
if header_type.is_ok() && header_type.unwrap() == "ED" {
let save_type = rom[0x3F] >> 4;
match save_type {
0 => return vec![],
1 => return vec![SaveTypes::Eeprom4k],
2 => return vec![SaveTypes::Eeprom16k],
3 => return vec![SaveTypes::Sram],
4 => panic!("Unsupported save type: {:X}", save_type),
5 => return vec![SaveTypes::Flash],
6 => panic!("Unsupported save type: {:X}", save_type),
_ => panic!("Unknown save type: {:X}", save_type),
}
}
match game_id {
"NB7" | // Banjo-Tooie [Banjo to Kazooie no Daiboken 2 (J)]
"NGT" | // City Tour GrandPrix - Zen Nihon GT Senshuken
@@ -87,36 +104,51 @@ fn get_save_type(game_id: &str) -> Vec<SaveTypes> {
}
}
pub fn init(ui: &mut ui::Ui) {
let id = ui.game_id.as_str();
ui.save_type = get_save_type(id);
pub fn init(ui: &mut ui::Ui, rom: &[u8]) {
ui.save_type = get_save_type(rom, &ui.game_id);
let base_path = ui.dirs.data_dir.join("saves");
let game_name = std::str::from_utf8(&rom[0x20..0x20 + 0x14])
.unwrap()
.trim()
.replace('\0', "");
let prefix = if game_name.is_empty() {
&ui.game_id
} else {
&game_name
};
ui.paths.eep_file_path.clone_from(&base_path);
ui.paths
.eep_file_path
.push(ui.game_id.to_owned() + "-" + &ui.game_hash + ".eep");
.push(prefix.to_owned() + "-" + &ui.game_hash + ".eep");
ui.paths.sra_file_path.clone_from(&base_path);
ui.paths
.sra_file_path
.push(ui.game_id.to_owned() + "-" + &ui.game_hash + ".sra");
.push(prefix.to_owned() + "-" + &ui.game_hash + ".sra");
ui.paths.fla_file_path.clone_from(&base_path);
ui.paths
.fla_file_path
.push(ui.game_id.to_owned() + "-" + &ui.game_hash + ".fla");
.push(prefix.to_owned() + "-" + &ui.game_hash + ".fla");
ui.paths.pak_file_path.clone_from(&base_path);
ui.paths
.pak_file_path
.push(ui.game_id.to_owned() + "-" + &ui.game_hash + ".mpk");
.push(prefix.to_owned() + "-" + &ui.game_hash + ".mpk");
ui.paths.sdcard_file_path.clone_from(&base_path);
ui.paths
.sdcard_file_path
.push(prefix.to_owned() + "-" + &ui.game_hash + ".img");
ui.paths.romsave_file_path.clone_from(&base_path);
ui.paths
.romsave_file_path
.push(ui.game_id.to_owned() + "-" + &ui.game_hash + ".romsave");
.push(prefix.to_owned() + "-" + &ui.game_hash + ".romsave");
}
pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
@@ -137,6 +169,10 @@ pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
if mempak.is_ok() {
ui.saves.mempak.0 = mempak.unwrap();
}
let sdcard = std::fs::read(&mut ui.paths.sdcard_file_path);
if sdcard.is_ok() {
ui.saves.sdcard.0 = sdcard.unwrap();
}
let romsave = std::fs::read(&mut ui.paths.romsave_file_path);
if romsave.is_ok() {
ui.saves.romsave.0 = postcard::from_bytes(romsave.unwrap().as_ref()).unwrap();
@@ -169,6 +205,12 @@ pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
&ui.saves.mempak.0,
ui.saves.mempak.0.len(),
);
netplay::send_save(
netplay.as_mut().unwrap(),
"img",
&ui.saves.sdcard.0,
ui.saves.sdcard.0.len(),
);
let mut romsave_bytes: Vec<u8> = vec![];
if !ui.saves.romsave.0.is_empty() {
romsave_bytes = postcard::to_stdvec(&ui.saves.romsave.0).unwrap();
@@ -184,6 +226,7 @@ pub fn load_saves(ui: &mut ui::Ui, netplay: &mut Option<netplay::Netplay>) {
netplay::receive_save(netplay.as_mut().unwrap(), "sra", &mut ui.saves.sram.0);
netplay::receive_save(netplay.as_mut().unwrap(), "fla", &mut ui.saves.flash.0);
netplay::receive_save(netplay.as_mut().unwrap(), "mpk", &mut ui.saves.mempak.0);
netplay::receive_save(netplay.as_mut().unwrap(), "img", &mut ui.saves.sdcard.0);
let mut romsave_bytes: Vec<u8> = vec![];
netplay::receive_save(netplay.as_mut().unwrap(), "rom", &mut romsave_bytes);
if !romsave_bytes.is_empty() {
@@ -212,6 +255,9 @@ pub fn write_saves(ui: &ui::Ui, netplay: &Option<netplay::Netplay>) {
if ui.saves.mempak.1 {
write_save(ui, SaveTypes::Mempak)
}
if ui.saves.sdcard.1 {
write_save(ui, SaveTypes::Sdcard)
}
if ui.saves.romsave.1 {
write_save(ui, SaveTypes::Romsave)
}
@@ -238,6 +284,10 @@ fn write_save(ui: &ui::Ui, save_type: SaveTypes) {
path = ui.paths.pak_file_path.as_ref();
data = ui.saves.mempak.0.as_ref();
}
SaveTypes::Sdcard => {
path = ui.paths.sdcard_file_path.as_ref();
data = ui.saves.sdcard.0.as_ref();
}
SaveTypes::Romsave => {
write_rom_save(ui);
return;