Commit 042623f5 authored by Arthur Carlsson's avatar Arthur Carlsson

Implemented stuff that was needed in order to get something to run!

parent 6ee700c6
Pipeline #25429299 failed with stage
in 7 minutes and 42 seconds
use super::{reg, Regs};
enum Segment {
// 32-bit Supervisor mode segments
SUSEG,
SSEG,
// 32-bit Kernel mode segments
KUSEG,
KSEG0,
KSEG1,
KSSEG,
KSEG3,
// 64-bit Kernel mode segments
XKUSEG,
XKSSEG,
XKPHYS,
XKSEG,
CKSEG0,
CKSEG1,
CKSSEG,
CKSEG3,
}
#[derive(Default)]
pub struct Cp0 {
regs: Regs,
pub regs: Regs,
}
impl Cp0 {
......@@ -21,23 +44,70 @@ impl Cp0 {
/// Maps a virtual address to a physical address as per section 5.2 in the datasheet
pub fn virt_addr_to_phys_addr(&self, vaddr: u64) -> u32 {
if self.regs.status().is_kernel_mode_address_segment() {
// Kernel mode segment as per "Table 5-4 64-Bit Kernel Mode Segments"
match vaddr >> 62 {
0b11 if (vaddr >> 31) == 0x1_ffff_ffff => match vaddr {
// ckseg0
0xFFFF_FFFF_8000_0000...0xFFFF_FFFF_9FFF_FFFF => (vaddr - 0xFFFF_FFFF_8000_0000) as u32,
// ckseg1
0xFFFF_FFFF_A000_0000...0xFFFF_FFFF_BFFF_FFFF => (vaddr - 0xFFFF_FFFF_A000_0000) as u32,
_ => panic!("Unmapped 0b11 address: 0x{:X}", vaddr),
},
_ => panic!("Unmapped address: 0x{:X}", vaddr),
}
} else if self.regs.status().is_supervisor_mode_address_segments() {
// Supervisor mode segment as per "Table 5-2 32-Bit and 64-Bit Supervisor Mode Segments"
unimplemented!("Supervisor mode")
use self::Segment::*;
match self.segment(vaddr) {
// 32-bit Supervisor mode segments
SUSEG => unimplemented!("KSSEG"),
SSEG => unimplemented!("KSSEG"),
// 32-bit Kernel mode segments
KUSEG => unimplemented!("KUSEG"),
KSEG0 => unimplemented!("KSEG0"),
KSEG1 => (vaddr as u32) - 0xA000_0000,
KSSEG => unimplemented!("KSSEG"),
KSEG3 => unimplemented!("KSEG3"),
// 64-bit Kernel mode segments
XKUSEG => unimplemented!("XKUSEG"),
XKSSEG => unimplemented!("XKSSEG"),
XKPHYS => unimplemented!("XKPHYS"),
XKSEG => unimplemented!("XKSEG"),
CKSEG0 => unimplemented!("CKSEG0"),
CKSEG1 => (vaddr - 0xFFFF_FFFF_A000_0000) as u32,
CKSSEG => unimplemented!("CKSSEG"),
CKSEG3 => unimplemented!("CKSEG3"),
}
}
fn segment(&self, vaddr: u64) -> Segment {
use self::Segment::*;
if self.regs.status().is_32_bit_kernel_mode() {
return match vaddr as u32 {
0x0000_0000...0x7FFF_FFFF => KUSEG,
0x8000_0000...0x9FFF_FFFF => KSEG0,
0xA000_0000...0xBFFF_FFFF => KSEG1,
0xC000_0000...0xDFFF_FFFF => KSSEG,
0xE000_0000...0xFFFF_FFFF => KSEG3,
_ => panic!("is_32_bit_kernel_mode - Should not happen..."),
};
} else if self.regs.status().is_64_bit_kernel_mode() {
return match vaddr {
0x0000_0000_0000_0000...0x0000_00FF_FFFF_FFFF => XKUSEG,
0x4000_0000_0000_0000...0x4000_00FF_FFFF_FFFF => XKSSEG,
0x8000_0000_0000_0000...0xBFFF_FFFF_FFFF_FFFF => XKPHYS,
0xC000_0000_0000_0000...0xC000_00FF_7FFF_FFFF => XKSEG,
0xFFFF_FFFF_8000_0000...0xFFFF_FFFF_9FFF_FFFF => CKSEG0,
0xFFFF_FFFF_A000_0000...0xFFFF_FFFF_BFFF_FFFF => CKSEG1,
0xFFFF_FFFF_C000_0000...0xFFFF_FFFF_DFFF_FFFF => CKSSEG,
0xFFFF_FFFF_E000_0000...0xFFFF_FFFF_FFFF_FFFF => CKSEG3,
_ => panic!("is_64_bit_kernel_mode - Should not happen..."),
};
} else if self.regs.status().is_64_bit_user_mode() {
unimplemented!("64 bit user mode")
} else if self.regs.status().is_32_bit_user_mode() {
unimplemented!("32 bit user mode")
} else if self.regs.status().is_64_bit_supervisor_mode() {
unimplemented!("64 bit supervisor mode")
} else if self.regs.status().is_32_bit_supervisor_mode() {
return match vaddr as u32 {
0x0000_0000...0x7FFF_FFFF => SUSEG,
0xC000_0000...0xDFFF_FFFF => SSEG,
_ => panic!("is_32_bit_supervisor_mode - Should not happen..."),
};
} else {
panic!("Unknown address mode")
panic!("Unknown segment")
}
}
}
pub use self::reg::Regs;
pub use self::cp0::Cp0;
pub use self::reg::Regs;
mod reg;
mod cp0;
mod reg;
......@@ -3,10 +3,18 @@ pub const CONFIG: usize = 16;
/// CP0 system registers
pub struct Regs {
pub gpr: [u32; 32],
gpr: [u32; 32],
}
impl Regs {
pub fn gpr(&self, nr: impl Into<u32>) -> u32 {
self.gpr[nr.into() as usize]
}
pub fn gpr_mut(&mut self, nr: impl Into<u32>) -> &mut u32 {
&mut self.gpr[nr.into() as usize]
}
pub fn status(&self) -> Status {
Status::from_bits_truncate(self.gpr[STATUS])
}
......@@ -77,20 +85,59 @@ bitflags! {
| Self::DS_TS.bits
| Self::DS_BEV.bits
| Self::DS_ITS.bits;
const KSU_USER = 0b10 << 3;
const KSU_SUPERVISOR = 0b01 << 3;
const KSU_KERNEL = 0;
}
}
impl Status {
/// Kernel mode segment as per "Table 5-4 64-Bit Kernel Mode Segments"
pub fn is_kernel_mode_address_segment(&self) -> bool {
(!self.contains(Status::KSU) || self.intersects(Status::EXL | Status::ERL))
&& self.contains(Status::KX)
/// Table 5-1 32-Bit User Mode Segments
pub fn is_32_bit_user_mode(&self) -> bool {
(*self & Status::KSU) == Status::KSU_USER
&& (*self & Status::EXL).bits == 0
&& (*self & Status::ERL).bits == 0
&& (*self & Status::UX).bits == 0
}
/// Table 5-1 64-Bit User Mode Segments
pub fn is_64_bit_user_mode(&self) -> bool {
(*self & Status::KSU) == Status::KSU_USER
&& (*self & Status::EXL).bits == 0
&& (*self & Status::ERL).bits == 0
&& (*self & Status::UX).bits != 0
}
/// Table 5-2 32-Bit Supervisor Mode Segments
pub fn is_32_bit_supervisor_mode(&self) -> bool {
(*self & Status::KSU) == Status::KSU_SUPERVISOR
&& (*self & Status::EXL).bits == 0
&& (*self & Status::ERL).bits == 0
&& (*self & Status::SX).bits == 0
}
/// Table 5-2 64-Bit Supervisor Mode Segments
pub fn is_64_bit_supervisor_mode(&self) -> bool {
(*self & Status::KSU) == Status::KSU_SUPERVISOR
&& (*self & Status::EXL).bits == 0
&& (*self & Status::ERL).bits == 0
&& (*self & Status::SX).bits != 0
}
/// Table 5-3 32-Bit Kernel Mode Segments
pub fn is_32_bit_kernel_mode(&self) -> bool {
((*self & Status::KSU) == Status::KSU_KERNEL
|| (*self & Status::EXL).bits != 0
|| (*self & Status::ERL).bits != 0) && (*self & Status::KX).bits == 0
}
/// Supervisor mode segment as per "Table 5-2 32-Bit and 64-Bit Supervisor Mode Segments"
pub fn is_supervisor_mode_address_segments(&self) -> bool {
(*self & Status::KSU).bits == 0b01000 && !self.intersects(Status::EXL | Status::ERL)
/// Table 5-4 64-Bit Kernel Mode Segments
pub fn is_64_bit_kernel_mode(&self) -> bool {
((*self & Status::KSU) == Status::KSU_KERNEL
|| (*self & Status::EXL).bits != 0
|| (*self & Status::ERL).bits != 0) && (*self & Status::KX).bits != 0
}
}
......@@ -109,29 +156,3 @@ bitflags! {
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_kernel_mode_address_segment() {
assert!((Status::EXL | Status::ERL | Status::KX).is_kernel_mode_address_segment());
}
#[test]
fn test_is_kernel_mode_address_segment_not_matching() {
assert!(!Status::empty().is_kernel_mode_address_segment());
}
#[test]
fn test_is_supervisor_mode_segments_sx_0() {
assert!(Status::from_bits_truncate(0b1000).is_supervisor_mode_address_segments());
}
#[test]
fn test_is_supervisor_mode_segments_sx_1() {
assert!(Status::from_bits_truncate(0b1001000).is_supervisor_mode_address_segments());
}
}
use super::reg;
use cpu;
use super::mem;
use cpu::{self, mem, reg};
/// The main type for emulating the CPU, which is a VR4300 MIPS processor.
pub struct Cpu {
......@@ -10,8 +8,11 @@ pub struct Cpu {
impl Cpu {
/// Creates a blank CPU instance which is in an undefined state.
pub fn new() -> Cpu {
Default::default()
pub fn new() -> Self {
Self {
regs: reg::Regs::new(),
cp0: cpu::Cp0::new(),
}
}
/// Triggers a power-on reset exception as per the section 9.2.1 in the datasheet
......@@ -21,33 +22,161 @@ impl Cpu {
self.cp0.power_on_reset();
}
pub fn virt_addr_to_phys_addr(&self, vaddr: u64) -> u32 {
self.cp0.virt_addr_to_phys_addr(vaddr)
}
/// Fetches the instruction for the current PC
pub fn fetch(&self, mem: &mem::Reader) -> u32 {
mem.readw(self.regs.pc)
pub fn fetch(&self, mem: &impl mem::Read) -> u32 {
mem.readw(self.cp0.virt_addr_to_phys_addr(self.regs.pc))
}
/// Decodes an instruction into an Instruction instance
pub fn decode(&self, word: u32) -> cpu::Instruction {
cpu::decode_instruction(word)
}
/// Executes the instruction in the context of the CPU.
///
/// NOTE: Only handles 32 bit mode.
pub fn execute(&mut self, instr: &cpu::Instruction, mem: &mut (impl mem::Read + mem::Write)) {
use cpu::Instruction::*;
/// Executes the instruction in the context of the CPU
pub fn execute(&mut self, instr: &cpu::Instruction) {
println!("{:?}", instr);
match *instr {
ADDIU(ref a) => *self.regs.gpr_mut(a.rt()) = self.regs.gpr(a.rs()) + a.imm_sign_extended(),
ANDI(ref a) => *self.regs.gpr_mut(a.rt()) = (a.imm() as u64) & self.regs.gpr(a.rs()),
BNEL(ref a) => {
let target = a.imm_sign_extended() << 2;
let condition = self.regs.gpr(a.rs()) != self.regs.gpr(a.rt());
if condition {
self.regs.pc = self.regs.pc.wrapping_add(target - 4);
}
}
BEQL(ref a) => {
let target = a.imm_sign_extended() << 2;
let condition = self.regs.gpr(a.rs()) == self.regs.gpr(a.rt());
if condition {
self.regs.pc = self.regs.pc.wrapping_add(target - 4);
}
}
LUI(ref a) => *self.regs.gpr_mut(a.rt()) = a.imm_sign_extended() << 16,
LW(ref a) => {
let base = a.rs();
let offset = a.imm_sign_extended();
let vaddr = self.regs.gpr(base).wrapping_add(offset);
let paddr = self.cp0.virt_addr_to_phys_addr(vaddr);
let mem = mem.readw(paddr) as u64;
*self.regs.gpr_mut(a.rt()) = mem;
}
MTC0(ref a) => *self.cp0.regs.gpr_mut(a.rd()) = self.regs.gpr(a.rt()) as u32,
ORI(ref a) => *self.regs.gpr_mut(a.rt()) = self.regs.gpr(a.rs()) | (a.imm() as u64),
SW(ref a) => {
let vaddr = a.imm_sign_extended() + self.regs.gpr(a.rs());
let paddr = self.cp0.virt_addr_to_phys_addr(vaddr);
let data = self.regs.gpr(a.rt()) as u32;
mem.writew(paddr, data);
}
REGIMM(..) => unimplemented!("REGIMM"),
ADD(..) => unimplemented!("ADD"),
SRL(..) => unimplemented!("SRL"),
}
self.regs.pc += 4;
}
}
impl Default for Cpu {
fn default() -> Self {
Self {
regs: reg::Regs::new(),
cp0: cpu::Cp0::new(),
#[cfg(test)]
mod tests {
use super::*;
use byteorder::BigEndian;
enum TestMemCtx {}
impl mem::MemCtx for TestMemCtx {
type ByteOrder = BigEndian;
}
mod execute {
use super::*;
use cpu::instruction::{IType, RType};
use cpu::Instruction::*;
#[test]
fn test_mtc0() {
let mut cpu = Cpu::new();
*cpu.regs.gpr_mut(1u32) = 66u64;
*cpu.cp0.regs.gpr_mut(8u32) = 10u32;
cpu.execute(
&MTC0(RType::new(99, 1, 8, 99, 99)),
&mut mem::Mem::<TestMemCtx>::with_capacity(1000),
);
assert_eq!(66, cpu.cp0.regs.gpr(8u32));
}
#[test]
fn test_lui() {
let mut cpu = Cpu::new();
cpu.execute(
&LUI(IType::new(1, 2, 66)),
&mut mem::Mem::<TestMemCtx>::with_capacity(1000),
);
assert_eq!(66 << 16, cpu.regs.gpr(2u32));
}
#[test]
fn test_ori() {
let mut cpu = Cpu::new();
*cpu.regs.gpr_mut(1u32) = 0xABCD_0000u64 | 0b1001;
cpu.execute(
&ORI(IType::new(1, 2, 0b1110)),
&mut mem::Mem::<TestMemCtx>::with_capacity(1000),
);
assert_eq!(0xABCD_0000 | 0b1111, cpu.regs.gpr(2u32));
}
#[test]
fn test_lw() {
let mut cpu = Cpu::new();
*cpu.regs.gpr_mut(1u32) = 0xFFFF_FFFF_A000_0004;
cpu.execute(
&LW(IType::new(1, 2, 3)),
&mut mem::Mem::<TestMemCtx>::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb]),
);
assert_eq!(0x08090a0b, cpu.regs.gpr(2u32));
}
#[test]
fn test_bneq_equals() {
let mut cpu = Cpu::new();
*cpu.regs.gpr_mut(1u32) = 10;
*cpu.regs.gpr_mut(2u32) = 10;
cpu.regs.pc = 99;
cpu.execute(
&BNEL(IType::new(1, 2, 10)),
&mut mem::Mem::<TestMemCtx>::new(vec![]),
);
assert_eq!(103, cpu.regs.pc);
}
#[test]
fn test_bneq_not_equals() {
let mut cpu = Cpu::new();
*cpu.regs.gpr_mut(1u32) = 10;
*cpu.regs.gpr_mut(2u32) = 11;
cpu.regs.pc = 99;
cpu.execute(
&BNEL(IType::new(1, 2, 0b01000)),
&mut mem::Mem::<TestMemCtx>::new(vec![]),
);
assert_eq!(99 + 0b0100000, cpu.regs.pc);
}
}
}
use std::fmt;
/// An I-Type instruction format as per chapter 1.4.3 in the datasheet
/// 31 26 25 21 20 16 15 0
/// op rs rt imm
#[derive(PartialEq)]
pub struct IType(u32);
impl IType {
#[inline]
pub fn new(rs: u8, rt: u8, imm: u16) -> Self {
IType(((rs as u32) & 0b11111) << 21 | ((rt as u32) & 0b11111) << 16 | (imm as u32))
}
#[inline]
pub fn rs(&self) -> u8 {
((self.0 >> 21) & 0b11111) as u8
......@@ -18,6 +25,11 @@ impl IType {
pub fn imm(&self) -> u16 {
(self.0 & 0xFFFF) as u16
}
#[inline]
pub fn imm_sign_extended(&self) -> u64 {
(self.imm() as i16) as u64
}
}
impl fmt::Debug for IType {
......@@ -33,6 +45,8 @@ impl fmt::Debug for IType {
}
/// A J-Type instruction format as per chapter 1.4.3 in the datasheet
/// 31 26 25 0
/// op target
#[derive(PartialEq)]
pub struct JType(u32);
impl JType {
......@@ -49,9 +63,22 @@ impl fmt::Debug for JType {
}
/// A R-Type instruction format as per chapter 1.4.3 in the datasheet
/// 31 26 25 21 20 16 15 11 10 6 5 0
/// op rs rt rd sa funct
#[derive(PartialEq)]
pub struct RType(u32);
impl RType {
#[inline]
pub fn new(rs: u8, rt: u8, rd: u8, sa: u8, funct: u8) -> Self {
RType(
((rs as u32) & 0b11111) << 21
| ((rt as u32) & 0b11111) << 16
| ((rd as u32) & 0b11111) << 11
| ((sa as u32) & 0b11111) << 6
| (funct as u32),
)
}
#[inline]
pub fn rs(&self) -> u8 {
((self.0 >> 21) & 0b11111) as u8
......@@ -95,42 +122,47 @@ impl fmt::Debug for RType {
/// Instruction representations. The instructions are described in in chapter 16 in the datasheet.
#[derive(Debug, PartialEq)]
pub enum Instruction {
REGIMM(IType),
SRL(RType),
LUI(IType),
ADD(RType),
ADDIU(IType),
MTC0(RType),
ORI(IType),
LW(IType),
ANDI(IType),
BEQL(IType),
SW(IType),
BNEL(IType),
REGIMM(IType),
SRL(RType),
ADD(RType),
}
/// Decodes an instruction to an Instruction type. Returns a DecodeError if
/// there was an error.
#[inline]
pub fn decode(word: u32) -> Instruction {
macro_rules! itype ( ($i:ident) => (Instruction::$i(IType(word))) );
macro_rules! rtype ( ($i:ident) => (Instruction::$i(RType(word))) );
use self::Instruction::*;
match word >> 26 {
// SPECIAL
0b000000 => match word & 0b111111 {
0b000010 => rtype!(SRL),
0b100000 => rtype!(ADD),
0b000010 => SRL(RType(word)),
0b100000 => ADD(RType(word)),
w @ _ => panic!("Unknown special instruction: {:X}", w),
},
0b000001 => itype!(REGIMM),
0b001001 => itype!(ADDIU),
0b001100 => itype!(ANDI),
0b001101 => itype!(ORI),
0b001111 => itype!(LUI),
0b000001 => REGIMM(IType(word)),
0b001001 => ADDIU(IType(word)),
0b001100 => ANDI(IType(word)),
0b001101 => ORI(IType(word)),
0b001111 => LUI(IType(word)),
// COP0
0b010000 => match (word >> 21) & 0b11111 {
0b00100 => rtype!(MTC0),
0b00100 => MTC0(RType(word)),
w @ _ => panic!("Unknown CP0 instruction: {:X}", w),
},
0b100011 => itype!(LW),
0b010100 => BEQL(IType(word)),
0b100011 => LW(IType(word)),
0b101011 => SW(IType(word)),
0b010101 => BNEL(IType(word)),
w @ _ => panic!("Unknown instruction: {:01$b}", w, 6),
}
}
......
use byteorder;
use byteorder::ByteOrder;
use std::marker::PhantomData;
pub trait MemCtx {
type ByteOrder: byteorder::ByteOrder;
}
pub struct Mem<T: MemCtx> {
buf: Vec<u8>,
phantom: PhantomData<T>,
}
impl<T: MemCtx> Mem<T> {
/// Creates a new memory buffer with the pre-filled memory content
pub fn new(data: Vec<u8>) -> Self {
Self {
buf: data,
phantom: PhantomData,
}
}
/// Creates a memory buffer with the specified size
pub fn with_capacity(size: usize) -> Self {
Self {
buf: vec![0xab; size],
phantom: PhantomData,
}
}
}
/// Reader for the memory as seen from the CPU's perspective
pub trait Reader {
pub trait Read {
fn readb(&self, addr: u32) -> u8;
/// Reads a word from the memory at the given address.
fn readw(&self, addr: u64) -> u32;
fn readw(&self, addr: u32) -> u32;
}
impl<T: MemCtx> Read for Mem<T> {
fn readb(&self, addr: u32) -> u8 {
self.buf[addr as usize]
}
fn readw(&self, addr: u32) -> u32 {
T::ByteOrder::read_u32(&self.buf[addr as usize..addr as usize + 4])
}
}
/// Writer for the memory as seen from the CPU's perspective
pub trait Writer {
pub trait Write {
/// Writes a byte to the given address
fn write(&mut self, addr: u64, byte: u8);
fn writeb(&mut self, addr: u32, byte: u8);
/// Writes a word to the given address
///
/// TODO: What happens if there is no room for the word? I.e. the address is just at the end of the memory.
fn writew(&mut self, addr: u32, word: u32);
}
impl<T: MemCtx> Write for Mem<T> {
fn writeb(&mut self, addr: u32, byte: u8) {
self.buf[addr as usize] = byte
}
fn writew(&mut self, addr: u32, word: u32) {
T::ByteOrder::write_u32(&mut self.buf[addr as usize..addr as usize + 4], word)
}
}
pub use self::cp0::Cp0;
pub use self::cpu::Cpu;
pub use self::instruction::Instruction;
pub use self::instruction::decode as decode_instruction;
pub use self::cp0::Cp0;
pub use self::instruction::Instruction;
mod cp0;
mod cpu;
mod instruction;
mod reg;
pub mod mem;
mod cp0;
mod reg;
......@@ -2,7 +2,7 @@ pub struct Regs {
/// 64-bit Program Counter, the PC register
pub pc: u64,
/// The general purpose registers
pub gpr: [u64; 32],
gpr: [u64; 32],
/// 32 64-bit floating-point operation registers, FPRs
pub fpr: [u64; 32],
/// 64-bit HI register, containing the integer multiply and divide high- order doubleword result
......@@ -21,6 +21,16 @@ impl Regs {
pub fn new() -> Regs {
Default::default()
}
#[inline]
pub fn gpr(&self, nr: impl Into<u32>) -> u64 {
self.gpr[nr.into() as usize]
}
#[inline]
pub fn gpr_mut(&mut self, nr: impl Into<u32>) -> &mut u64 {
&mut self.gpr[nr.into() as usize]
}
}
impl Default for Regs {
......
......@@ -3,12 +3,14 @@ extern crate byteorder;
#[macro_use]
extern crate bitflags;
mod rom;
mod pif_rom;
mod n64;
mod cpu;
mod cart;
mod cpu;
mod n64;
mod pif_rom;
mod rom;
mod rsp;
pub use pif_rom::PifRom;
pub use n64::N64;
pub use cart::Cart;
pub use n64::N64;
pub use pif_rom::PifRom;
pub use rsp::Rsp;
extern crate clap;
extern crate rustn64;
use clap::{App, Arg};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use clap::{App, Arg};
use rustn64::{Cart, N64, PifRom};
......
use cpu::mem;
use pif_rom::PifRom;
use rsp::Rsp;
pub struct MemoryMap {
pub rsp: mem::Mem<Rsp>,
pub pif_rom: mem::Mem<PifRom>,
}
impl MemoryMap {
pub fn new(pif_rom: mem::Mem<PifRom>, rsp: mem::Mem<Rsp>) -> Self {
Self { pif_rom, rsp }
}