Commit c18d951e authored by Arthur Carlsson's avatar Arthur Carlsson

Added documentation and general purpose registers

parent 61214e46
Pipeline #7528658 passed with stage
in 3 minutes and 56 seconds
......@@ -2,18 +2,22 @@ use super::reg;
use super::instruction;
use super::mem;
/// The main type for emulating the CPU, which is a VR4300 MIPS processor.
pub struct Cpu {
regs: reg::Regs
}
impl Cpu {
/// Creates a blank CPU instance which is in an undefined state.
pub fn new() -> Cpu {
Cpu {
regs: reg::Regs::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.regs.status -= reg::STATUS_DS_TS | reg::STATUS_DS_SR | reg::STATUS_RP;
......@@ -22,20 +26,24 @@ impl Cpu {
self.regs.config |= reg::CONFIG_BE;
}
/// Fetches the instruction for the current PC
pub fn fetch(&self, mem: &mem::Reader) -> u32 {
instruction::fetch(self.regs.pc, mem)
}
pub fn decode(&self, word: u32) -> Result<instruction::Instruction, instruction::InstructionError> {
/// Decodes an instruction into an Instruction instance
pub fn decode(&self, word: u32) -> Result<instruction::Instruction, instruction::DecodeError> {
instruction::decode(word)
}
/// Executes the instruction in the context of the CPU
pub fn execute(&mut self, instr: &instruction::Instruction) {
println!("{:?}", instr);
self.regs.pc += 4;
}
/// 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"
......
use super::mem;
/// Extracts an operand from the instruction.
///
/// $ub is the upper bound bit index for the opcode found in the datasheet
/// $lb is the lower bound bit index for the opcode found in the datasheet
///
/// # Examples
///
/// ```
/// let rs = oper![u8; instr; 25, 21];
/// ```
macro_rules! oper {
($t:ident; $e:expr; $ub:expr, $lb:expr) => ((((0xffffffffu32 >> (31 - $ub)) & $e) >> $lb) as $t)
}
/// Creates a special instruction instance which always looks the same,
/// i.e. it contains rs, rt and rd operands.
///
/// # Examples
///
/// ```
/// let add = special_instr!(instr; ADD);
/// ```
macro_rules! special_instr {
($e:expr; $n:ident) => (
$n { rs: oper![u8; $e; 25, 21], rt: oper![u8; $e; 20, 16], rd: oper![u8; $e; 15, 11] }
)
}
/// Error types for decoding operation.
#[derive(Debug, PartialEq)]
pub enum InstructionError {
pub enum DecodeError {
/// An instruction could not be decoded
UnknownInstruction(u8),
/// An instruction of special type (opcode = 0b000000) could not be decoded
UnknownSpecialInstruction(u8)
}
/// Instruction representations. The instructions are described in in chapter 16 in the datasheet.
#[derive(Debug, PartialEq)]
pub enum Instruction {
ADD { rs: u8, rt: u8, rd: u8 },
......@@ -26,13 +48,17 @@ pub enum Instruction {
LUI { rt: u8, immediate: u16 }
}
/// Fetches an instruction from the provided memory from the location
/// of the pc.
pub fn fetch(pc: u64, mem: &mem::Reader) -> u32 {
mem.readw(pc)
}
pub fn decode(instr: u32) -> Result<Instruction, InstructionError> {
/// Decodes an instruction to an Instruction type. Returns a DecodeError if
/// there was an error.
pub fn decode(instr: u32) -> Result<Instruction, DecodeError> {
use self::Instruction::*;
use self::InstructionError::*;
use self::DecodeError::*;
match instr >> 26 {
// SPECIAL
......@@ -55,7 +81,7 @@ pub fn decode(instr: u32) -> Result<Instruction, InstructionError> {
mod tests {
use super::*;
use self::Instruction::*;
use self::InstructionError::*;
use self::DecodeError::*;
#[test]
fn test_oper() {
......
/// Reader for the memory as seen from the CPU's perspective
pub trait Reader {
/// Reads a word from the memory at the given address.
fn readw(&self, addr: u64) -> u32;
}
/// Writer for the memory as seen from the CPU's perspective
pub trait Writer {
/// Writes a byte to the given address
fn write(&mut self, addr: u64, byte: u8);
}
bitflags! {
/// Status registers as per "Figure 6-5 Status Register" in the datasheet
pub flags Status: u32 {
const STATUS_IE = 1 << 0,
const STATUS_EXL = 1 << 1,
......@@ -36,12 +37,12 @@ bitflags! {
impl Status {
// Kernel mode segment as per "Table 5-4 64-Bit Kernel Mode Segments"
/// 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)
}
// Supervisor mode segment as per "Table 5-2 32-Bit and 64-Bit Supervisor Mode Segments"
/// 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)
}
......@@ -50,6 +51,7 @@ impl Status {
bitflags! {
/// Config register as per "Figure 5-16 Config Register" in the datasheet
pub flags Config: u32 {
const CONFIG_K0 = 0b111 << 0,
const CONFIG_CU = 1 << 3,
......@@ -64,20 +66,26 @@ bitflags! {
}
pub struct Regs {
/// The program counter
pub pc: u64,
/// The status register
pub status: Status,
pub config: Config
/// The config register
pub config: Config,
/// The general purpose registers
pub gpr: [u64; 32]
}
impl Regs {
/// Creates a new Regs instance and sets the register values to
/// bogus values so that they can be determined if they have been changed or not
pub fn new() -> Regs {
Regs {
pc: 0xcafebabe,
status: Status::from_bits_truncate(0xcafebabe),
config: Config::from_bits_truncate(0xcafebabe)
config: Config::from_bits_truncate(0xcafebabe),
gpr: [0xcafebabe; 32]
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment