Moved files

This commit is contained in:
Kim-Dewelski
2022-02-08 03:06:01 +01:00
parent 359047d7f3
commit 92e95b0471
10 changed files with 0 additions and 3501 deletions

2454
cpu.rs

File diff suppressed because it is too large Load Diff

View File

@@ -1,45 +0,0 @@
use crate::mmu::MMU;
use crate::cpu::CPU;
pub struct Emulator {
cpu: CPU,
mmu: MMU,
}
impl Emulator {
pub fn new() -> Self {
Self {
cpu: CPU::new(),
mmu: MMU::new(),
}
}
pub fn new_hle() -> Self {
Self {
cpu: CPU::new_hle(),
mmu: MMU::new(),
}
}
pub fn reload(&mut self) {
self.cpu = CPU::new();
self.mmu = MMU::new();
}
pub fn reload_hle(&mut self) {
self.cpu = CPU::new_hle();
self.mmu = MMU::new();
}
pub fn tick(&mut self) {
self.cpu.fetch_and_exec_opcode(&mut self.mmu);
}
pub fn cpu(&self) -> &CPU {
&self.cpu
}
pub fn mut_mmu(&mut self) -> &mut MMU {
&mut self.mmu
}
}

147
gui.rs
View File

@@ -1,147 +0,0 @@
use eframe::{egui, epi};
use std::cell::RefCell;
use std::rc::Rc;
use crate::emulator::Emulator;
#[derive(PartialEq, Eq)]
enum Register {
CPU,
CP0,
}
impl Default for Register {
fn default() -> Self {
Self::CPU
}
}
pub struct EmulatorApp {
core: Emulator,
selected_register: Register,
}
impl Default for EmulatorApp {
fn default() -> Self {
Self {
core: Emulator::new_hle(),
selected_register: Register::CPU,
}
}
}
impl epi::App for EmulatorApp {
fn name(&self) -> &str {
"Rultra64"
}
/// Called once before the first frame.
fn setup(
&mut self,
_ctx: &egui::CtxRef,
_frame: &epi::Frame,
_storage: Option<&dyn epi::Storage>,
) {
// Load previous app state (if any).
// Note that you must enable the `persistence` feature for this to work.
#[cfg(feature = "persistence")]
if let Some(storage) = _storage {
*self = epi::get_value(storage, epi::APP_KEY).unwrap_or_default()
}
}
/// Called by the frame work to save state before shutdown.
/// Note that you must enable the `persistence` feature for this to work.
#[cfg(feature = "persistence")]
fn save(&mut self, storage: &mut dyn epi::Storage) {
epi::set_value(storage, epi::APP_KEY, self);
}
/// Called each time the UI needs repainting, which may be many times per second.
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
fn update(&mut self, ctx: &egui::CtxRef, frame: &epi::Frame) {
let Self { core: emulator_core, selected_register } = self;
let emulator_core = Rc::new(RefCell::new(emulator_core));
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
// The top panel is often a good place for a menu bar:
egui::menu::bar(ui, |ui| {
ui.menu_button("File", |ui| {
if ui.button("Load ROM").clicked() {
if let Some(path) = rfd::FileDialog::new().pick_file() {
let picked_path = path.display().to_string();
if let Ok(rom) = crate::rom::ROM::new_from_filename(&picked_path) {
let mut emulator_core = emulator_core.borrow_mut();
emulator_core.reload_hle();
emulator_core.mut_mmu().set_rom(rom);
emulator_core.mut_mmu().hle_ipl();
println!("ROM loaded!");
}
}
}
if ui.button("Quit").clicked() {
frame.quit();
}
});
});
});
build_registers_window(ctx, selected_register, emulator_core.clone());
build_emulator_controls_window(ctx, emulator_core.clone());
}
}
fn build_registers_window(ctx: &egui::CtxRef, selected_register: &mut Register, emulator_core: Rc<RefCell<&mut Emulator>>) {
egui::Window::new("Registers").vscroll(true).show(ctx, |ui| {
ui.horizontal(|ui| {
ui.selectable_value(selected_register, Register::CPU, "CPU");
ui.selectable_value(selected_register, Register::CP0, "CP0");
});
ui.separator();
match selected_register {
Register::CPU => build_cpu_registers(ui, emulator_core),
Register::CP0 => {ui.label("CP0 registers");},
};
});
}
fn build_cpu_registers(ui: &mut egui::Ui, emulator_core: Rc<RefCell<&mut Emulator>>) {
let emulator_core = emulator_core.borrow();
ui.columns(3, |cols| {
cols[0].label("#");
cols[1].label("Name");
cols[2].label("Value");
});
ui.separator();
ui.columns(3, |cols| {
cols[0].label("-");
cols[1].label("PC");
cols[2].label(format!("{:64X}", emulator_core.cpu().registers().get_program_counter()));
});
ui.columns(3, |cols| {
cols[0].label("-");
cols[1].label("hi");
cols[2].label(format!("{}", emulator_core.cpu().registers().get_hi()));
});
ui.columns(3, |cols| {
cols[0].label("-");
cols[1].label("lo");
cols[2].label(format!("{}", emulator_core.cpu().registers().get_lo()));
});
for (index, name) in crate::registers::CPU_REGISTER_NAMES.into_iter().enumerate() {
let val = emulator_core.cpu().registers().get_by_name(name);
ui.columns(3, |cols| {
cols[0].label(format!("r{}", index));
cols[1].label(format!("{}", name));
cols[2].label(format!("{}", val));
});
}
}
fn build_emulator_controls_window(ctx: &egui::CtxRef, emulator_core: Rc<RefCell<&mut Emulator>>) {
egui::Window::new("Controls").vscroll(true).show(ctx, |ui| {
if ui.button("Tick").clicked() {
emulator_core.borrow_mut().tick();
}
});
}

9
lib.rs
View File

@@ -1,9 +0,0 @@
pub mod registers;
pub mod cpu;
pub mod mmu;
pub mod rom;
pub mod rdram;
pub mod emulator;
pub mod rcp;
pub mod utils;
pub mod gui;

203
mmu.rs
View File

@@ -1,203 +0,0 @@
use std::ops::RangeInclusive;
use crate::rdram::RDRAM;
use crate::rom::ROM;
use crate::rcp::RCP;
pub const KUSEG: RangeInclusive<i64> = 0x00000000..=0x7FFFFFFF;
pub const KSEG0: RangeInclusive<i64> = 0x80000000..=0x9FFFFFFF;
pub const KSEG1: RangeInclusive<i64> = 0xA0000000..=0xBFFFFFFF;
pub const KSSEG: RangeInclusive<i64> = 0xC0000000..=0xDFFFFFFF;
pub const KSEG3: RangeInclusive<i64> = 0xE0000000..=0xFFFFFFFF;
pub const RDRAM1: RangeInclusive<i64> = 0x00000000..=0x003FFFFF;
pub const RDRAM2: RangeInclusive<i64> = 0x00400000..=0x007FFFFF;
pub const RESERVED1: RangeInclusive<i64> = 0x00800000..=0x03EFFFFF;
pub const RDRAM_REGISTERS: RangeInclusive<i64> = 0x03F00000..=0x03FFFFFF;
pub const RSP_DMEM: RangeInclusive<i64> = 0x04000000..=0x04000FFF;
pub const RSP_IMEM: RangeInclusive<i64> = 0x04001000..=0x04001FFF;
pub const UNKNOWN: RangeInclusive<i64> = 0x04002000..=0x0403FFFF;
pub const RSP_REGISTERS: RangeInclusive<i64> = 0x04040000..=0x040FFFFF;
pub const RDP_COMMAND_REGISTERS: RangeInclusive<i64> = 0x04100000..=0x041FFFFF;
pub const RDP_SPAN_REGISTERS: RangeInclusive<i64> = 0x04200000..=0x042FFFFF;
pub const MIPS_INTERFACE: RangeInclusive<i64> = 0x04300000..=0x043FFFFF;
pub const VIDEO_INTERFACE: RangeInclusive<i64> = 0x04400000..=0x044FFFFF;
pub const AUDIO_INTERFACE: RangeInclusive<i64> = 0x04500000..=0x045FFFFF;
pub const PERIPHERAL_INTERFACE: RangeInclusive<i64> = 0x04600000..=0x046FFFFF;
pub const RDRAM_INTERFACE: RangeInclusive<i64> = 0x04700000..=0x047FFFFF;
pub const SERIAL_INTERFACE: RangeInclusive<i64> = 0x04800000..=0x048FFFFF;
pub const UNUSED: RangeInclusive<i64> = 0x04900000..=0x04FFFFFF;
pub const CARTRIDGE_DOMAIN_2_ADDRESS_1: RangeInclusive<i64> = 0x05000000..=0x05FFFFFF;
pub const CARTRIDGE_DOMAIN_1_ADDRESS_1: RangeInclusive<i64> = 0x06000000..=0x07FFFFFF;
pub const CARTRIDGE_DOMAIN_2_ADDRESS_2: RangeInclusive<i64> = 0x08000000..=0x0FFFFFFF;
pub const CARTRIDGE_DOMAIN_1_ADDRESS_2: RangeInclusive<i64> = 0x10000000..=0x1FBFFFFF;
pub const PIF_ROM: RangeInclusive<i64> = 0x1FC00000..=0x1FC007BF;
pub const PIF_RAM: RangeInclusive<i64> = 0x1FC007C0..=0x1FC007FF;
pub const RESERVED2: RangeInclusive<i64> = 0x1FC00800..=0x1FCFFFFF;
pub const CARTRIDGE_DOMAIN_1_ADDRESS_3: RangeInclusive<i64> = 0x1FD00000..=0x7FFFFFFF;
pub const EXTERNAL_SYSAD_DEVICE_BUS: RangeInclusive<i64> = 0x80000000..=0xFFFFFFFF;
pub struct MMU {
rdram: RDRAM,
rom: ROM,
rcp: RCP,
}
impl MMU {
pub fn new() -> Self {
Self {
rdram: RDRAM::new(),
rcp: RCP::new(),
rom: ROM::new(),
}
}
pub fn hle_ipl(&mut self) {
// Skip IPL1 and IPL2
for i in 0..0x1000 {
let byte = self.read_virtual(0xB0000000 + i, 1);
self.write_virtual(0xA4000000 + i, &byte);
}
// Skip IPL3
for i in 0..0x100000 {
let byte = self.read_physical_byte(0x10001000 + i);
self.write_physical_byte(0x00001000 + i, byte);
}
}
pub fn set_rom(&mut self, rom: ROM) {
self.rom = rom;
}
pub fn convert(address: i64) -> i64 {
let address = address & 0x00000000FFFFFFFF;
if KUSEG.contains(&address) {
return address - KUSEG.min().unwrap();
} else if KSEG0.contains(&address) {
return address - KSEG0.min().unwrap();
} else if KSEG1.contains(&address) {
return address - KSEG1.min().unwrap();
} else if KSSEG.contains(&address) {
return address - KSSEG.min().unwrap();
} else if KSEG3.contains(&address) {
return address - KSEG3.min().unwrap();
}
unreachable!("Invalid virtual memory address {:08X}", address);
}
pub fn read_virtual(&self, address: i64, bytes: usize) -> Vec<u8> {
let converted_address = MMU::convert(address);
self.read_physical(converted_address, bytes)
}
pub fn write_virtual(&mut self, address: i64, data: &[u8]) {
let converted_address = MMU::convert(address);
self.write_physical(converted_address, data)
}
pub fn read_physical(&self, address: i64, bytes: usize) -> Vec<u8> {
let mut data = Vec::new();
for _ in 0..bytes {
data.push(self.read_physical_byte(address));
}
data
}
pub fn write_physical(&mut self, address: i64, data: &[u8]) {
for byte in data {
self.write_physical_byte(address, *byte);
}
}
pub fn read_physical_byte(&self, address: i64) -> u8 {
if RDRAM1.contains(&address) {
return self.rdram.read8(address);
} else if RDRAM2.contains(&address) {
return self.rdram.read8(address);
} else if RESERVED1.contains(&address) {
return 0xFF;
} else if RDRAM_REGISTERS.contains(&address) {
return 0;
} else if RSP_DMEM.contains(&address) {
return 0;
} else if RSP_IMEM.contains(&address) {
return 0;
} else if UNKNOWN.contains(&address) {
return 0;
} else if RSP_REGISTERS.contains(&address) {
return 0;
} else if RDP_COMMAND_REGISTERS.contains(&address) {
return 0;
} else if RDP_SPAN_REGISTERS.contains(&address) {
return 0;
} else if MIPS_INTERFACE.contains(&address) {
return 0;
} else if VIDEO_INTERFACE.contains(&address) {
return self.rcp.video_interface.get_register(address);
} else if AUDIO_INTERFACE.contains(&address) {
return 0;
} else if PERIPHERAL_INTERFACE.contains(&address) {
return 0;
} else if RDRAM_INTERFACE.contains(&address) {
return 0;
} else if SERIAL_INTERFACE.contains(&address) {
return 0;
} else if UNUSED.contains(&address) {
return 0xFF;
} else if CARTRIDGE_DOMAIN_2_ADDRESS_1.contains(&address) {
return 0;
} else if CARTRIDGE_DOMAIN_1_ADDRESS_1.contains(&address) {
return 0;
} else if CARTRIDGE_DOMAIN_2_ADDRESS_2.contains(&address) {
return self.rom.read(address);
} else if CARTRIDGE_DOMAIN_1_ADDRESS_2.contains(&address) {
return self.rom.read(address);
} else if PIF_ROM.contains(&address) {
return 0;
} else if PIF_RAM.contains(&address) {
return 0;
} else if RESERVED2.contains(&address) {
return 0;
} else if CARTRIDGE_DOMAIN_1_ADDRESS_3.contains(&address) {
return 0;
} else if EXTERNAL_SYSAD_DEVICE_BUS.contains(&address) {
return 0;
}
return 0xFF;
}
pub fn write_physical_byte(&mut self, address: i64, data: u8) {
if RDRAM1.contains(&address) {
self.rdram.write8(address, data);
} else if RDRAM2.contains(&address) {
self.rdram.write8(address, data);
} else if RESERVED1.contains(&address) {
} else if RDRAM_REGISTERS.contains(&address) {
} else if RSP_DMEM.contains(&address) {
} else if RSP_IMEM.contains(&address) {
} else if UNKNOWN.contains(&address) {
} else if RSP_REGISTERS.contains(&address) {
} else if RDP_COMMAND_REGISTERS.contains(&address) {
} else if RDP_SPAN_REGISTERS.contains(&address) {
} else if MIPS_INTERFACE.contains(&address) {
} else if VIDEO_INTERFACE.contains(&address) {
self.rcp.video_interface.set_register(address, data);
} else if AUDIO_INTERFACE.contains(&address) {
} else if PERIPHERAL_INTERFACE.contains(&address) {
} else if RDRAM_INTERFACE.contains(&address) {
} else if SERIAL_INTERFACE.contains(&address) {
} else if UNUSED.contains(&address) {
} else if CARTRIDGE_DOMAIN_2_ADDRESS_1.contains(&address) {
} else if CARTRIDGE_DOMAIN_1_ADDRESS_1.contains(&address) {
} else if CARTRIDGE_DOMAIN_2_ADDRESS_2.contains(&address) {
self.rom.write(address, data);
} else if CARTRIDGE_DOMAIN_1_ADDRESS_2.contains(&address) {
self.rom.write(address, data);
} else if PIF_ROM.contains(&address) {
} else if PIF_RAM.contains(&address) {
} else if RESERVED2.contains(&address) {
} else if CARTRIDGE_DOMAIN_1_ADDRESS_3.contains(&address) {
} else if EXTERNAL_SYSAD_DEVICE_BUS.contains(&address) {
}
}
}

73
rcp.rs
View File

@@ -1,73 +0,0 @@
use crate::rdram::RDRAM;
use crate::utils::box_array;
pub struct VideoInterface {
registers: Box<[u8; 0x100000]>,
}
impl VideoInterface {
pub fn new() -> Self {
let mut registers = box_array![0; 0x100000];
// Initialize VI_V_INTR 0x0440 000C: https://n64brew.dev/wiki/Video_Interface#0x0440_000C_-_VI_V_INTR
registers[0x0440000C - 0x04400000] = 0xFF;
registers[0x0440000B - 0x04400000] = 0x03;
// Initialize VI_BURST 0x0440 0014: https://n64brew.dev/wiki/Video_Interface#0x0440_0014_-_VI_BURST
registers[0x04400014 - 0x04400000] = 0x01;
// Initialize VI_H_SYNC 0x0440 001C: https://n64brew.dev/wiki/Video_Interface#0x0440_001C_-_VI_H_SYNC
registers[0x0440001C - 0x04400000] = 0xFF;
registers[0x0440001B - 0x04400000] = 0x07;
Self {
registers,
}
}
pub fn get_register(&self, address: i64) -> u8 {
self.registers[(address - 0x04400000) as usize]
}
pub fn set_register(&mut self, address: i64, data: u8) {
self.registers[(address - 0x04400000) as usize] = data;
}
/*
RDRAM base address of the video output Frame Buffer. This can be changed as needed to implement double or triple buffering.
https://n64brew.dev/wiki/Video_Interface#0x0440_0004_-_VI_ORIGIN
*/
pub fn get_vi_origin(&self) -> u32 {
((self.get_register(0x04400005) as u32) << 16) | ((self.get_register(0x04400006) as u32) << 8) | (self.get_register(0x04400007) as u32)
}
/*
This is the width in pixels of the frame buffer if you draw to the frame buffer based on a different width than what
is given here the image will drift with each line to the left or right. The common values are 320 and 640,
the maximum value is 640. The minimum value depends on the TV set, 160 would probably be a safe minimum but no guarantee.
The same value would also be used on drawing commands for clipping or scissors.
This can also be used with High Res interlacing modes to change the odd and even lines of the frame buffer to be drawn
to screen by doubling the width of this value and changing the VI_ORIGIN register to the odd or even field being displayed.
RDRAM base address of the video output Frame Buffer. This can be changed as needed to implement double or triple buffering.
https://n64brew.dev/wiki/Video_Interface#0x0440_0008_-_VI_WIDTH
*/
pub fn get_vi_width(&self) -> u16 {
(((self.get_register(0x04400010) as u16) << 8) & 0b1111) | (self.get_register(0x04400011) as u16)
}
}
pub struct RCP {
pub video_interface: VideoInterface,
}
impl RCP {
pub fn new() -> Self {
Self {
video_interface: VideoInterface::new(),
}
}
pub fn copy_framebuffer(&self, rdram: &RDRAM, dest: &mut [u8]) {
let mut addr = self.video_interface.get_vi_origin() as i64;
for elem in dest {
*elem = rdram.read8(addr);
addr += 1;
}
}
}

View File

@@ -1,58 +0,0 @@
use crate::utils::box_array;
#[derive(Copy, Clone)]
pub struct Byte {
data: u16,
}
impl Byte {
pub fn new() -> Self {
Self {
data: 0,
}
}
pub fn read(&self) -> u16 {
self.data & 0x1FF
}
pub fn write(&mut self, data: u16) {
self.data = data & 0x1FF;
}
pub fn read8(&self) -> u8 {
self.data.to_be_bytes()[1]
}
pub fn write8(&mut self, data: u8) {
self.data = (self.data & 0x100) | (data as u16);
}
}
pub struct RDRAM {
data: Box<[Byte; 0x400000]>,
}
impl RDRAM {
pub fn new() -> Self {
Self {
data: box_array![Byte::new(); 0x400000],
}
}
pub fn read(&self, address: i64) -> u16 {
self.data[address as usize].read()
}
pub fn write(&mut self, address: i64, data: u16) {
self.data[address as usize].write(data);
}
pub fn read8(&self, address: i64) -> u8 {
self.data[address as usize].read8()
}
pub fn write8(&mut self, address: i64, data: u8) {
self.data[address as usize].write8(data);
}
}

View File

@@ -1,449 +0,0 @@
pub trait Register<T: PartialOrd + Copy> {
fn get(&self) -> T;
fn set(&mut self, val: T);
}
#[derive(Copy, Clone)]
pub struct Fixed<T>(T);
impl<T: PartialOrd + Copy> Register<T> for Fixed<T> {
fn get(&self) -> T {self.0}
fn set(&mut self, _: T) {}
}
#[derive(Copy, Clone)]
pub struct Generic<T>(T);
impl<T: PartialOrd + Copy> Register<T> for Generic<T> {
fn get(&self) -> T {self.0}
fn set(&mut self, val: T) {self.0 = val}
}
pub const CPU_REGISTER_NAMES: [&'static str; 32] = [
"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"
];
pub struct CPURegisters {
registers: [Box<dyn Register<i64>>; 32],
program_counter: Generic<i64>,
next_program_counter: Generic<i64>,
hi: Generic<i64>,
lo: Generic<i64>,
load_link: bool,
}
impl CPURegisters {
pub fn new() -> Self {
Self {
registers: [
Box::new(Fixed(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
Box::new(Generic(0_i64)),
],
program_counter: Generic(0xBFC00000),
next_program_counter: Generic(0xBFC00004),
hi: Generic(0_i64),
lo: Generic(0_i64),
load_link: false,
}
}
pub fn new_hle() -> Self {
let mut registers = Self::new();
registers.set_by_name("t3", 0xFFFFFFFFA4000040_u64 as i64);
registers.set_by_name("s4", 0x0000000000000001);
registers.set_by_name("s6", 0x000000000000003F);
registers.set_by_name("sp", 0xFFFFFFFFA4001FF0_u64 as i64);
registers.set_program_counter(0x80001000);
registers.set_next_program_counter(0x80001000 + 4);
/* registers.set_program_counter(0xA4000040);
registers.set_next_program_counter(0xA4000040 + 4); */
registers
}
pub fn set_load_link(&mut self, val: bool) {
self.load_link = val;
}
pub fn get_load_link(&self) -> bool {
self.load_link
}
fn find_index(name: &'static str) -> usize {
CPU_REGISTER_NAMES.iter().position(|v| *v == name).unwrap()
}
pub fn get_by_number(&self, index: usize) -> i64 {
if index > 31 {
unreachable!("Register number {} not valid", index);
}
self.registers[index].get()
}
pub fn get_by_name(&self, name: &'static str) -> i64 {
let index = CPURegisters::find_index(name);
self.registers[index].get()
}
pub fn set_by_number(&mut self, index: usize, val: i64) {
if index > 31 {
unreachable!("Register number {} not valid", index);
}
self.registers[index].set(val);
}
pub fn set_by_name(&mut self, name: &'static str, val: i64) {
let index = CPURegisters::find_index(name);
self.registers[index].set(val);
}
pub fn get_program_counter(&self) -> i64 {
self.program_counter.get()
}
pub fn set_program_counter(&mut self, val: i64) {
self.program_counter.set(val);
}
pub fn increment_program_counter(&mut self, val: i64) {
let pc: i64 = self.program_counter.get();
self.program_counter.set(pc.wrapping_add(val));
}
pub fn get_next_program_counter(&self) -> i64 {
self.next_program_counter.get()
}
pub fn set_next_program_counter(&mut self, val: i64) {
self.next_program_counter.set(val);
}
pub fn increment_next_program_counter(&mut self, val: i64) {
let pc: i64 = self.next_program_counter.get();
self.next_program_counter.set(pc.wrapping_add(val));
}
pub fn set_hi(&mut self, val: i64) {
self.hi.set(val);
}
pub fn set_lo(&mut self, val: i64) {
self.lo.set(val);
}
pub fn get_hi(&self) -> i64 {
self.hi.get()
}
pub fn get_lo(&self) -> i64 {
self.lo.get()
}
}
pub const CP0_REGISTER_NAMES: [&'static str; 32] = [
"index", "random", "EntryLo0", "EntryLo1", "context", "PageMask", "wired", "7",
"BadVAddr", "count", "EntryHi", "compare", "status", "cause", "epc", "PRId",
"config", "LLAddr", "WatchLo", "WatchHi", "XContext", "21", "22", "23",
"24", "25", "ParityError", "CacheError", "TagLo", "TagHi", "ErrorEPC", "31"
];
pub struct CP0Registers {
index: Generic<i32>,
random: Generic<i32>,
entry_lo_0: Generic<i64>,
entry_lo_1: Generic<i64>,
context: Generic<i64>,
page_mask: Generic<i32>,
wired: Generic<i32>,
r7: Generic<i64>,
bad_v_addr: Generic<i64>,
count: Generic<i32>,
entry_hi: Generic<i64>,
compare: Generic<i32>,
status: Generic<i32>,
cause: Generic<i32>,
epc: Generic<i64>,
prid: Generic<i32>,
config: Generic<i32>,
lladdr: Generic<i32>,
watch_lo: Generic<i32>,
watch_hi: Generic<i32>,
xcontext: Generic<i64>,
r21: Generic<i64>,
r22: Generic<i64>,
r23: Generic<i64>,
r24: Generic<i64>,
r25: Generic<i64>,
parity_error: Generic<i32>,
cache_error: Generic<i32>,
tag_lo: Generic<i32>,
tag_hi: Generic<i32>,
error_epc: Generic<i64>,
r31: Generic<i64>,
}
impl CP0Registers {
pub fn new() -> Self {
Self {
index: Generic(0),
random: Generic(0),
entry_lo_0: Generic(0),
entry_lo_1: Generic(0),
context: Generic(0),
page_mask: Generic(0),
wired: Generic(0),
r7: Generic(0),
bad_v_addr: Generic(0),
count: Generic(0),
entry_hi: Generic(0),
compare: Generic(0),
status: Generic(0),
cause: Generic(0),
epc: Generic(0),
prid: Generic(0),
config: Generic(0),
lladdr: Generic(0),
watch_lo: Generic(0),
watch_hi: Generic(0),
xcontext: Generic(0),
r21: Generic(0),
r22: Generic(0),
r23: Generic(0),
r24: Generic(0),
r25: Generic(0),
parity_error: Generic(0),
cache_error: Generic(0),
tag_lo: Generic(0),
tag_hi: Generic(0),
error_epc: Generic(0),
r31: Generic(0),
}
}
pub fn new_hle() -> Self {
let mut cp0 = Self::new();
cp0.set_by_name_32("random", 0x0000001F);
cp0.set_by_name_32("status", 0x70400004);
cp0.set_by_name_32("PRId", 0x00000B00);
cp0.set_by_name_32("config", 0x0006E463);
cp0
}
fn find_index(name: &'static str) -> usize {
CP0_REGISTER_NAMES.iter().position(|v| *v == name).unwrap()
}
pub fn is_32bits(index: usize) -> bool {
match index {
0 | 1 | 5 | 6 | 9 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 26 | 27 | 28 | 29 => true,
2 | 3 | 4 | 8 | 10 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 30 => false,
_ => unreachable!(),
}
}
pub fn is_64bits(index: usize) -> bool {
!CP0Registers::is_32bits(index)
}
pub fn get_by_number_32(&self, index: usize) -> i32 {
if index > 31 {
unreachable!("Register number {} not valid", index);
}
match index {
0 => self.index.get(),
1 => self.random.get(),
5 => self.page_mask.get(),
6 => self.wired.get(),
9 => self.count.get(),
11 => self.compare.get(),
12 => self.status.get(),
13 => self.cause.get(),
15 => self.prid.get(),
16 => self.config.get(),
17 => self.lladdr.get(),
18 => self.watch_lo.get(),
19 => self.watch_hi.get(),
26 => self.parity_error.get(),
27 => self.cache_error.get(),
28 => self.tag_lo.get(),
29 => self.tag_hi.get(),
_ => unreachable!("Register {} is not 32bit", index),
}
}
pub fn set_by_number_32(&mut self, index: usize, val: i32) {
if index > 31 {
unreachable!("Register number {} not valid", index);
}
match index {
0 => self.index.set(val),
1 => self.random.set(val),
5 => self.page_mask.set(val),
6 => self.wired.set(val),
9 => self.count.set(val),
11 => self.compare.set(val),
12 => self.status.set(val),
13 => self.cause.set(val),
15 => self.prid.set(val),
16 => self.config.set(val),
17 => self.lladdr.set(val),
18 => self.watch_lo.set(val),
19 => self.watch_hi.set(val),
26 => self.parity_error.set(val),
27 => self.cache_error.set(val),
28 => self.tag_lo.set(val),
29 => self.tag_hi.set(val),
_ => unreachable!("Register {} is not 32bit", index),
};
}
pub fn get_by_number_64(&self, index: usize) -> i64 {
if index > 31 {
unreachable!("Register number {} not valid", index);
}
match index {
2 => self.entry_lo_0.get(),
3 => self.entry_lo_1.get(),
4 => self.context.get(),
7 => self.r7.get(),
8 => self.bad_v_addr.get(),
10 => self.entry_hi.get(),
14 => self.epc.get(),
20 => self.xcontext.get(),
21 => self.r21.get(),
22 => self.r22.get(),
23 => self.r23.get(),
24 => self.r24.get(),
25 => self.r25.get(),
30 => self.error_epc.get(),
31 => self.r31.get(),
_ => unreachable!("Register {} is not 64bit", index),
}
}
pub fn set_by_number_64(&mut self, index: usize, val: i64) {
if index > 31 {
unreachable!("Register number {} not valid", index);
}
match index {
2 => self.entry_lo_0.set(val),
3 => self.entry_lo_1.set(val),
4 => self.context.set(val),
7 => self.r7.set(val),
8 => self.bad_v_addr.set(val),
10 => self.entry_hi.set(val),
14 => self.epc.set(val),
20 => self.xcontext.set(val),
21 => self.r21.set(val),
22 => self.r22.set(val),
23 => self.r23.set(val),
24 => self.r24.set(val),
25 => self.r25.set(val),
30 => self.error_epc.set(val),
31 => self.r31.set(val),
_ => unreachable!("Register {} is not 64bit", index),
};
}
pub fn get_by_name_32(&self, name: &'static str) -> i32 {
let index = CP0Registers::find_index(name);
self.get_by_number_32(index)
}
pub fn set_by_name_32(&mut self, name: &'static str, val: i32) {
let index = CP0Registers::find_index(name);
self.set_by_number_32(index, val);
}
pub fn get_by_name_64(&self, name: &'static str) -> i64 {
let index = CP0Registers::find_index(name);
self.get_by_number_64(index)
}
pub fn set_by_name_64(&mut self, name: &'static str, val: i64) {
let index = CP0Registers::find_index(name);
self.set_by_number_64(index, val);
}
}
#[cfg(test)]
mod cpu_registers_tests {
use super::*;
#[test]
fn test_set_by_number() {
let mut registers = CPURegisters::new();
registers.set_by_number(0, 20);
assert_eq!(registers.get_by_number(0), 0);
registers.set_by_number(5, 20);
assert_eq!(registers.get_by_number(5), 20);
}
#[test]
fn test_set_by_name() {
let mut registers = CPURegisters::new();
registers.set_by_name("zero", 20);
assert_eq!(registers.get_by_name("zero"), 0);
registers.set_by_name("a0", 20);
assert_eq!(registers.get_by_name("a0"), 20);
assert_eq!(registers.get_by_number(4), 20);
}
}
#[cfg(test)]
mod cp0_registers_tests {
use super::*;
#[test]
fn test_set_by_number() {
let mut registers = CP0Registers::new();
registers.set_by_number_32(0, 20);
assert_eq!(registers.get_by_number_32(0), 20);
registers.set_by_number_64(4, 20);
assert_eq!(registers.get_by_number_64(4), 20);
}
#[test]
fn test_set_by_name() {
let mut registers = CP0Registers::new();
registers.set_by_name_32("index", 0);
assert_eq!(registers.get_by_name_32("index"), 0);
registers.set_by_name_64("context", 20);
assert_eq!(registers.get_by_name_64("context"), 20);
assert_eq!(registers.get_by_number_64(4), 20);
}
}

50
rom.rs
View File

@@ -1,50 +0,0 @@
use std::fs::File;
use std::io::Read;
use crate::mmu::CARTRIDGE_DOMAIN_2_ADDRESS_2;
use crate::mmu::CARTRIDGE_DOMAIN_1_ADDRESS_2;
pub struct ROM {
data: Vec<u8>,
ram: Vec<u8>,
}
impl ROM {
pub fn new() -> Self {
Self {
data: Vec::new(),
ram: Vec::new(),
}
}
pub fn new_from_filename(filename: &str) -> std::io::Result<Self> {
let mut file = File::open(filename)?;
let mut data = vec![];
file.read_to_end(&mut data)?;
Ok(Self {
data,
ram: vec![0; 0xFC00000],
})
}
pub fn read(&self, address: i64) -> u8 {
if CARTRIDGE_DOMAIN_2_ADDRESS_2.contains(&address) {
return match self.ram.get((address - CARTRIDGE_DOMAIN_2_ADDRESS_2.min().unwrap()) as usize) {
Some(byte) => *byte,
None => 0xFF,
};
} else if CARTRIDGE_DOMAIN_1_ADDRESS_2.contains(&address) {
return match self.data.get((address - CARTRIDGE_DOMAIN_1_ADDRESS_2.min().unwrap()) as usize) {
Some(byte) => *byte,
None => 0xFF,
};
}
unreachable!("Invalid ROM access");
}
pub fn write(&mut self, address: i64, data: u8) {
if let Some(elem) = self.ram.get_mut((address - CARTRIDGE_DOMAIN_2_ADDRESS_2.min().unwrap()) as usize) {
*elem = data;
}
}
}

View File

@@ -1,13 +0,0 @@
#[macro_export]
macro_rules! box_array {
($val:expr ; $len:expr) => {{
fn vec_to_boxed_array<T>(vec: Vec<T>) -> Box<[T; $len]> {
let boxed_slice = vec.into_boxed_slice();
let ptr = ::std::boxed::Box::into_raw(boxed_slice) as *mut [T; $len];
unsafe { Box::from_raw(ptr) }
}
vec_to_boxed_array(vec![$val; $len])
}};
}
pub(crate) use box_array;