Commit 886c45e6 authored by Arthur Carlsson's avatar Arthur Carlsson

Merge branch 'part-3' into 'master'

Part 3

See merge request !2
parents cb9f6277 79a17b95
Pipeline #25776533 passed with stage
in 2 minutes and 26 seconds
# This file is a template, and might need editing before it works on your project.
# Unofficial language image. Look for the different tagged releases at:
# https://hub.docker.com/r/scorpil/rust/tags/
image: "scorpil/rust:stable"
image: "rust:latest"
# Optional: Pick zero or more services to be used on all builds.
# Only needed when using a docker container to run your tests in.
......@@ -11,14 +8,19 @@ image: "scorpil/rust:stable"
# - redis:latest
# - postgres:latest
# Optional: Install a C compiler, cmake and git into the container.
# You will often need this when you (or any of your dependencies) depends on C code.
#before_script:
#- apt-get update -yqq
#- apt-get install -yqq --no-install-recommends build-essential
variables:
CARGO_HOME: $CI_PROJECT_DIR/cargo
before_script:
- rustup component add rustfmt-preview
# Use cargo to test the project
test:cargo:
cache:
paths:
- target/
- cargo/
script:
- rustc --version && cargo --version # Print version info for debugging
- cargo fmt -- --write-mode=diff # Make sure format is OK
- cargo test --verbose --jobs 1 --release # Don't paralize to make errors more readable
......@@ -4,5 +4,6 @@ version = "0.1.0"
authors = ["Arthur Carlsson <arthur@kiron.net>"]
[dependencies]
clap = "2.10"
byteorder = "0.5"
clap = "2"
byteorder = "1.2"
bitflags = "1.0"
......@@ -12,3 +12,4 @@ Nice-to-have Links
* [Hex Viewer Package for Sublime Text](https://github.com/facelessuser/HexViewer)
* [Online Disassembler](https://www.onlinedisassembler.com/odaweb/)
* [VR4300 Datasheet](http://datasheets.chipdb.org/NEC/Vr-Series/Vr43xx/U10504EJ7V0UMJ1.pdf)
* [ROM endianes info](http://jul.rustedlogic.net/thread.php?id=11769)
tab_spaces = 2
use byteorder::BigEndian;
use rom::Rom;
pub struct Cart {
rom: Rom<BigEndian>,
}
impl Cart {
pub fn new(buf: Vec<u8>) -> Cart {
let mut rom = Rom::new(buf);
match rom[0] {
0x37 => rom.byte_swap(),
0x40 => rom.endian_swap(),
_ => (),
}
Cart { rom: rom }
}
pub fn title(&self) -> String {
self.rom.read_ascii_string(0x0020, 20)
}
}
impl Cart {
pub fn read_word(&self, addr: u32) -> u32 {
self.rom.read_word(addr)
}
}
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 {
pub regs: Regs,
}
impl Cp0 {
pub fn new() -> Self {
Default::default()
}
pub fn power_on_reset(&mut self) {
self
.regs
.remove_status_flags(reg::Status::DS_TS | reg::Status::DS_SR | reg::Status::RP)
.remove_config_flags(reg::Config::EP)
.add_status_flags(reg::Status::ERL | reg::Status::DS_BEV)
.add_config_flags(reg::Config::BE);
}
/// 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 {
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 segment")
}
}
}
pub use self::cp0::Cp0;
pub use self::reg::Regs;
mod cp0;
mod reg;
pub const STATUS: usize = 12;
pub const CONFIG: usize = 16;
/// CP0 system registers
pub struct Regs {
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])
}
pub fn config(&self) -> Config {
Config::from_bits_truncate(self.gpr[CONFIG])
}
pub fn remove_status_flags(&mut self, flags: Status) -> &mut Self {
self.gpr[STATUS] = (self.status() - flags).bits;
self
}
pub fn remove_config_flags(&mut self, flags: Config) -> &mut Self {
self.gpr[CONFIG] = (self.config() - flags).bits;
self
}
pub fn add_status_flags(&mut self, flags: Status) -> &mut Self {
self.gpr[STATUS] = (self.status() | flags).bits;
self
}
pub fn add_config_flags(&mut self, flags: Config) -> &mut Self {
self.gpr[CONFIG] = (self.config() | flags).bits;
self
}
}
impl Default for Regs {
fn default() -> Self {
Self {
gpr: [0xcafebabe; 32],
}
}
}
bitflags! {
/// Status registers as per "Figure 6-5 Status Register" in the datasheet
pub struct Status: u32 {
const IE = 1 << 0;
const EXL = 1 << 1;
const ERL = 1 << 2;
const KSU = 0b11 << 3;
const UX = 1 << 5;
const SX = 1 << 6;
const KX = 1 << 7;
const IM = 0b1111_1111 << 8;
const DS_DE = 1 << 15;
const DS_CE = 1 << 16;
const DS_CH = 1 << 17;
// ... 0
const DS_SR = 1 << 19;
const DS_TS = 1 << 20;
const DS_BEV = 1 << 21;
// ... 0
const DS_ITS = 1 << 23;
const RE = 1 << 24;
const FR = 1 << 25;
const RP = 1 << 26;
const CU = 0b1111 << 27;
const DS = Self::DS_DE.bits
| Self::DS_CE.bits
| Self::DS_CH.bits
| Self::DS_SR.bits
| 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 {
/// 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
}
/// 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
}
}
bitflags! {
/// Config register as per "Figure 5-16 Config Register" in the datasheet
pub struct Config: u32 {
const K0 = 0b111 << 0;
const CU = 1 << 3;
// ... 1100100110
const BE = 1 << 15;
// ... 00000110
const EP = 0b1111 << 24;
const EC = 0b111 << 28;
// ... 0
}
}
use cpu::{self, mem, reg};
/// The main type for emulating the CPU, which is a VR4300 MIPS processor.
pub struct Cpu {
regs: reg::Regs,
cp0: cpu::Cp0,
}
impl Cpu {
/// Creates a blank CPU instance which is in an undefined state.
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
/// TODO: Maybe handle this as an exception which sets an exception bit?
pub fn power_on_reset(&mut self) {
self.regs.pc = 0xFFFF_FFFF_BFC0_0000;
self.cp0.power_on_reset();
}
/// Fetches the instruction for the current PC
pub fn fetch(&self, mem: &impl mem::Read) -> u32 {
mem.readw(self.cp0.virt_addr_to_phys_addr(self.regs.pc))
}
/// 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::*;
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;
}
}
#[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
}
#[inline]
pub fn rt(&self) -> u8 {
((self.0 >> 16) & 0b11111) as u8
}
#[inline]
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"rs: 0x{:X}, rt: 0x{:X}, imm: 0x{:X}",
self.rs(),
self.rt(),
self.imm()
)
}
}
/// 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 {
#[inline]
pub fn target(&self) -> u32 {
self.0 & 0x3FFF_FFFF
}
}
impl fmt::Debug for JType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "target: 0x{:X}", self.target())
}
}
/// 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
}
#[inline]
pub fn rt(&self) -> u8 {
((self.0 >> 16) & 0b11111) as u8
}
#[inline]
pub fn rd(&self) -> u8 {
((self.0 >> 11) & 0b11111) as u8
}
#[inline]
pub fn sa(&self) -> u8 {
((self.0 >> 6) & 0b11111) as u8
}
#[inline]
pub fn funct(&self) -> u8 {
(self.0 & 0b111111) as u8
}
}
impl fmt::Debug for RType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"rs: 0x{:X}, rt: 0x{:X}, rd: 0x{:X}, sa: 0x{:X}, funct: 0x{:X}",
self.rs(),
self.rt(),
self.rd(),
self.sa(),
self.funct()
)
}
}
/// Instruction representations. The instructions are described in in chapter 16 in the datasheet.
#[derive(Debug, PartialEq)]
pub enum Instruction {
LUI(IType),
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 {
use self::Instruction::*;
match word >> 26 {
// SPECIAL
0b000000 => match word & 0b111111 {
0b000010 => SRL(RType(word)),
0b100000 => ADD(RType(word)),
w @ _ => panic!("Unknown special instruction: {:X}", w),
},
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 => MTC0(RType(word)),
w @ _ => panic!("Unknown CP0 instruction: {:X}", w),
},
0b010100 => BEQL(IType(word)),
0b100011 => LW(IType(word)),
0b101011 => SW(IType(word)),
0b010101 => BNEL(IType(word)),
w @ _ => panic!("Unknown instruction: {:01$b}", w, 6),
}
}
#[cfg(test)]
mod tests {
use super::*;