Files
gopher64/src/device.rs
T
Logan McNaughton 97e4921b6a Don't transmute values in RSP code (#132)
* remove transmute

* fix

* more

* more

* fix
2025-01-13 09:13:14 +01:00

438 lines
14 KiB
Rust

#![allow(non_camel_case_types)]
#![allow(improper_ctypes)]
#[cfg(target_arch = "aarch64")]
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub struct __m128i(std::arch::aarch64::int64x2_t);
#[cfg(target_arch = "aarch64")]
include!(concat!(env!("OUT_DIR"), "/simd_bindings.rs"));
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use crate::ui;
use std::collections::HashMap;
use std::fs;
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;
pub mod cop2;
pub mod cpu;
pub mod cpu_instructions;
pub mod events;
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;
pub mod rdp;
pub mod rdram;
pub mod ri;
pub mod rsp_cpu;
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 mod vru;
pub fn run_game(
file_path: &std::path::Path,
data_dir: std::path::PathBuf,
device: &mut Device,
fullscreen: bool,
) {
let rom_contents = get_rom_contents(file_path);
cart_rom::init(device, rom_contents); // cart needs to come before rdram
// rdram pointer is shared with parallel-rdp
rdram::init(device);
ui::audio::init(&mut device.ui, 33600);
ui::video::init(device, fullscreen);
ui::input::init(&mut device.ui);
mi::init(device);
pif::init(device);
if device.ui.config.input.emulate_vru {
vru::init(device);
}
memory::init(device);
rsp_interface::init(device);
rdp::init(device);
vi::init(device);
cpu::init(device);
ui::storage::init(&mut device.ui, data_dir);
ui::storage::load_saves(&mut device.ui);
cart_rom::load_rom_save(device);
cpu::run(device);
ui::video::close();
ui::storage::write_saves(&device.ui);
}
fn swap_rom(contents: Vec<u8>) -> Vec<u8> {
let test = u32::from_be_bytes(contents[0..4].try_into().unwrap());
if test == 0x80371240 {
// z64
contents
} else if test == 0x37804012 {
// v64
let mut data: Vec<u8> = vec![0; contents.len()];
for i in (0..contents.len()).step_by(2) {
let temp = u16::from_ne_bytes(contents[i..i + 2].try_into().unwrap());
data[i..i + 2].copy_from_slice(&temp.to_be_bytes());
}
return data;
} else if test == 0x40123780 {
// n64
let mut data: Vec<u8> = vec![0; contents.len()];
for i in (0..contents.len()).step_by(4) {
let temp = u32::from_ne_bytes(contents[i..i + 4].try_into().unwrap());
data[i..i + 4].copy_from_slice(&temp.to_be_bytes());
}
return data;
} else {
panic!("unknown rom format")
}
}
fn get_rom_contents(file_path: &std::path::Path) -> Vec<u8> {
let mut contents = vec![];
if file_path.extension().unwrap().eq_ignore_ascii_case("zip") {
let zip_file = fs::File::open(file_path).unwrap();
let mut archive = zip::ZipArchive::new(zip_file).unwrap();
for i in 0..archive.len() {
let mut file = archive.by_index(i).unwrap();
let extension = file
.enclosed_name()
.unwrap()
.extension()
.unwrap()
.to_ascii_lowercase();
if extension == "z64" || extension == "n64" || extension == "v64" {
file.read_to_end(&mut contents)
.expect("could not read zip file");
break;
}
}
} else if file_path.extension().unwrap().eq_ignore_ascii_case("7z") {
let mut archive =
sevenz_rust::SevenZReader::open(file_path, sevenz_rust::Password::empty()).unwrap();
let mut found = false;
archive
.for_each_entries(
&mut |entry: &sevenz_rust::SevenZArchiveEntry, reader: &mut dyn std::io::Read| {
let name = entry.name().to_ascii_lowercase();
if !found
&& (name.ends_with("z64") || name.ends_with("n64") || name.ends_with("v64"))
{
reader
.read_to_end(&mut contents)
.expect("could not read zip file");
found = true;
} else {
//skip other files
std::io::copy(reader, &mut std::io::sink())?;
}
Ok(true)
},
)
.expect("ok");
} else {
contents = fs::read(file_path).expect("Should have been able to read the file");
}
swap_rom(contents)
}
pub struct Device {
pub ui: ui::Ui,
byte_swap: usize,
cpu: cpu::Cpu,
pif: pif::Pif,
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,
pub vi: vi::Vi,
ai: ai::Ai,
si: si::Si,
ri: ri::Ri,
flashram: sram::Flashram,
pub vru: vru::Vru,
}
impl Device {
pub fn new(config_dir: std::path::PathBuf) -> Device {
let mut byte_swap: usize = 0;
let test: [u8; 4] = [1, 2, 3, 4];
// if the host computer is little endian, that means the RDRAM will be stored as little endian
// when accessing bytes in RDRAM, we need to swap them by XORing with the byte_swap value (3)
if u32::from_le_bytes(test) == u32::from_ne_bytes(test) {
byte_swap = 3;
}
Device {
ui: ui::Ui::new(config_dir),
byte_swap,
cpu: cpu::Cpu {
cop0: cop0::Cop0 {
regs: [0; cop0::COP0_REGS_COUNT as usize],
reg_write_masks: [0; cop0::COP0_REGS_COUNT as usize],
reg_latch: 0,
instrs: [cop0::reserved; 32],
instrs2: [cop0::reserved; 32],
tlb_lut_w: vec![
tlb::TlbLut {
address: 0,
cached: false,
};
0x100000
],
tlb_lut_r: vec![
tlb::TlbLut {
address: 0,
cached: false,
};
0x100000
],
tlb_entries: [tlb::TlbEntry {
mask: 0,
vpn2: 0,
region: 0,
g: 0,
asid: 0,
pfn_even: 0,
c_even: 0,
d_even: 0,
v_even: 0,
pfn_odd: 0,
c_odd: 0,
d_odd: 0,
v_odd: 0,
start_even: 0,
end_even: 0,
phys_even: 0,
start_odd: 0,
end_odd: 0,
phys_odd: 0,
}; 32],
},
cop1: cop1::Cop1 {
fcr0: 0,
fcr31: 0,
#[cfg(target_arch = "x86_64")]
flush_mode: 0,
fgr32: [[0; 4]; 32],
fgr64: [[0; 8]; 32],
instrs: [cop1::reserved; 32],
b_instrs: [cop1::reserved; 4],
s_instrs: [cop1::reserved; 64],
d_instrs: [cop1::reserved; 64],
w_instrs: [cop1::reserved; 64],
l_instrs: [cop1::reserved; 64],
},
cop2: cop2::Cop2 {
instrs: [cop2::reserved; 32],
reg_latch: 0,
},
branch_state: cpu::BranchState {
state: cpu::State::Step,
pc: 0,
},
gpr: [0; 32],
clock_rate: 0,
pc: 0xBFC00000,
pc_phys: 0,
llbit: false,
lo: 0,
hi: 0,
running: false,
instrs: [cop0::reserved; 64],
special_instrs: [cop0::reserved; 64],
regimm_instrs: [cop0::reserved; 32],
events: [events::Event {
enabled: false,
count: u64::MAX,
handler: events::dummy_event,
}; events::EventType::Count as usize],
next_event_count: u64::MAX,
next_event: 0,
},
pif: pif::Pif {
rom: [0; 1984],
ram: [0; 64],
channels: [pif::PifChannel {
tx: None,
tx_buf: None,
rx: None,
rx_buf: None,
process: None,
pak_handler: None,
}; 5],
},
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,
rdram_size_offset: 0,
rtc: cart::AfRtc { control: 0x0200 },
},
memory: memory::Memory {
fast_read: [unmapped::read_mem; 0x2000],
memory_map_read: [unmapped::read_mem; 0x2000],
memory_map_write: [unmapped::write_mem; 0x2000],
icache: [cache::ICache {
valid: false,
index: 0,
tag: 0,
words: [0; 8],
instruction: [cop0::reserved; 8],
}; 512],
dcache: [cache::DCache {
valid: false,
dirty: false,
tag: 0,
index: 0,
words: [0; 4],
}; 512],
},
rdram: rdram::Rdram {
mem: vec![],
size: 0x800000,
},
rsp: rsp_interface::Rsp {
cpu: rsp_cpu::Cpu {
instructions: [rsp_cpu::Instructions {
func: rsp_su_instructions::reserved,
opcode: 0,
}; 0x1000 / 4],
last_instruction_type: rsp_cpu::InstructionType::Su,
instruction_type: rsp_cpu::InstructionType::Su,
pipeline_full: false,
branch_state: rsp_cpu::BranchState {
state: cpu::State::Step,
pc: 0,
},
broken: false,
halted: false,
sync_point: false,
cycle_counter: 0,
gpr: [0; 32],
vpr: unsafe { [_mm_setzero_si128(); 32] },
reciprocals: [0; 512],
inverse_square_roots: [0; 512],
divdp: false,
divin: 0,
divout: 0,
shuffle: unsafe { [_mm_setzero_si128(); 16] },
vcol: unsafe { _mm_setzero_si128() },
vcoh: unsafe { _mm_setzero_si128() },
vccl: unsafe { _mm_setzero_si128() },
vcch: unsafe { _mm_setzero_si128() },
vce: unsafe { _mm_setzero_si128() },
accl: unsafe { _mm_setzero_si128() },
accm: unsafe { _mm_setzero_si128() },
acch: unsafe { _mm_setzero_si128() },
special_instrs: [rsp_su_instructions::reserved; 64],
regimm_instrs: [rsp_su_instructions::reserved; 32],
cop0_instrs: [rsp_su_instructions::reserved; 32],
cop2_instrs: [rsp_su_instructions::reserved; 32],
lwc2_instrs: [rsp_su_instructions::reserved; 32],
swc2_instrs: [rsp_su_instructions::reserved; 32],
instrs: [rsp_su_instructions::reserved; 64],
vec_instrs: [rsp_vu_instructions::reserved; 64],
},
regs: [0; rsp_interface::SP_REGS_COUNT as usize],
regs2: [0; rsp_interface::SP_REGS2_COUNT as usize],
mem: [0; 0x2000],
fifo: [rsp_interface::RspDma {
dir: rsp_interface::DmaDir::None,
length: 0,
memaddr: 0,
dramaddr: 0,
}; 2],
},
rdp: rdp::Rdp {
regs_dpc: [0; rdp::DPC_REGS_COUNT as usize],
regs_dps: [0; rdp::DPS_REGS_COUNT as usize],
wait_frozen: false,
},
mi: mi::Mi {
regs: [0; mi::MI_REGS_COUNT as usize],
},
pi: pi::Pi {
regs: [0; pi::PI_REGS_COUNT as usize],
},
ai: ai::Ai {
regs: [0; ai::AI_REGS_COUNT as usize],
last_read: 0,
delayed_carry: false,
fifo: [ai::AiDma {
address: 0,
length: 0,
duration: 0,
}; 2],
},
si: si::Si {
regs: [0; si::SI_REGS_COUNT as usize],
dma_dir: si::DmaDir::None,
},
ri: ri::Ri {
regs: [0; ri::RI_REGS_COUNT as usize],
},
vi: vi::Vi {
regs: [0; vi::VI_REGS_COUNT as usize],
clock: 0,
field: 0,
delay: 0,
count_per_scanline: 0,
limiter: None,
vi_counter: 0,
},
flashram: sram::Flashram {
status: 0,
erase_page: 0,
page_buf: [0xff; 128],
silicon_id: [sram::FLASHRAM_TYPE_ID, sram::MX29L1100_ID],
mode: sram::FlashramMode::ReadArray,
},
vru: vru::Vru {
status: 0,
voice_state: 0,
load_offset: 0,
voice_init: 0,
word_buffer: [0; 40],
words: Vec::new(),
talking: false,
word_mappings: HashMap::new(),
window_notifier: None,
word_receiver: None,
gui_ctx: None,
},
}
}
}