Commit c9464ee1 authored by Arthur Carlsson's avatar Arthur Carlsson

Restructured the instruction handling

* Redid the instruction format
* Improved the decoding
parent c18d951e
Pipeline #15866410 failed with stage
in 2 minutes and 15 seconds
use super::reg;
use super::instruction;
use cpu::{Instruction, decode_instruction};
use super::mem;
/// The main type for emulating the CPU, which is a VR4300 MIPS processor.
......@@ -11,9 +11,7 @@ impl Cpu {
/// Creates a blank CPU instance which is in an undefined state.
pub fn new() -> Cpu {
Cpu {
regs: reg::Regs::new()
}
Default::default()
}
/// Triggers a power-on reset exception as per the section 9.2.1 in the datasheet
......@@ -28,18 +26,18 @@ impl Cpu {
/// Fetches the instruction for the current PC
pub fn fetch(&self, mem: &mem::Reader) -> u32 {
instruction::fetch(self.regs.pc, mem)
mem.readw(self.regs.pc)
}
/// Decodes an instruction into an Instruction instance
pub fn decode(&self, word: u32) -> Result<instruction::Instruction, instruction::DecodeError> {
instruction::decode(word)
pub fn decode(&self, word: u32) -> Instruction {
decode_instruction(word)
}
/// Executes the instruction in the context of the CPU
pub fn execute(&mut self, instr: &instruction::Instruction) {
pub fn execute(&mut self, instr: &Instruction) {
println!("{:?}", instr);
self.regs.pc += 4;
}
......@@ -67,6 +65,10 @@ impl Cpu {
}
#[cfg(test)]
mod tests {
impl Default for Cpu {
fn default() -> Self {
Self {
regs: reg::Regs::new()
}
}
}
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)
use std::fmt;
/// An I-Type instruction format as per chapter 1.4.3 in the datasheet
#[derive(PartialEq)]
pub struct IType(u32);
impl IType {
#[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
}
}
/// 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] }
)
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())
}
}
/// Error types for decoding operation.
#[derive(Debug, PartialEq)]
pub enum DecodeError {
/// An instruction could not be decoded
UnknownInstruction(u8),
/// An instruction of special type (opcode = 0b000000) could not be decoded
UnknownSpecialInstruction(u8)
/// A J-Type instruction format as per chapter 1.4.3 in the datasheet
#[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
#[derive(PartialEq)]
pub struct RType(u32);
impl RType {
#[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 {
ADD { rs: u8, rt: u8, rd: u8 },
ADDU { rs: u8, rt: u8, rd: u8 },
ADDI { rs: u8, rt: u8, immediate: u16 },
ADDIU { rs: u8, rt: u8, immediate: u16 },
DMTC0 { rt: u8, rd: u8 },
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)
REGIMM(IType),
SRL(RType),
LUI(IType),
ADD(RType),
ADDIU(IType),
MTC0(RType),
ORI(IType),
LW(IType),
ANDI(IType)
}
/// 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::DecodeError::*;
#[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))) );
match instr >> 26 {
match word >> 26 {
// SPECIAL
0b000000 => {
match instr & 0b111111 {
0b100000 => Ok(special_instr!(instr; ADD)),
0b100001 => Ok(special_instr!(instr; ADDU)),
i => Err(UnknownSpecialInstruction(i as u8))
}
0b000000 => match word & 0b111111 {
0b000010 => rtype!(SRL),
0b100000 => rtype!(ADD),
w@_ => panic!("Unknown special instruction: {:X}", w)
},
0b001000 => Ok(ADDI { rs: oper![u8; instr; 25, 21], rt: oper![u8; instr; 20, 16], immediate: oper![u16; instr; 15, 0] }),
0b001001 => Ok(ADDIU { rs: oper![u8; instr; 25, 21], rt: oper![u8; instr; 20, 16], immediate: oper![u16; instr; 15, 0] }),
0b010000 => Ok(DMTC0 { rt: oper![u8; instr; 20, 16], rd: oper![u8; instr; 15, 11] }),
0b001111 => Ok(LUI { rt: oper![u8; instr; 20, 16], immediate: oper![u16; instr; 15, 0] }),
i => Err(UnknownInstruction(i as u8))
0b000001 => itype!(REGIMM),
0b001001 => itype!(ADDIU),
0b001100 => itype!(ANDI),
0b001101 => itype!(ORI),
0b001111 => itype!(LUI),
// COP0
0b010000 => match (word >> 21) & 0b11111 {
0b00100 => rtype!(MTC0),
w@_ => panic!("Unknown CP0 instruction: {:X}", w)
},
0b100011 => itype!(LW),
w@_ => panic!("Unknown instruction: {:01$b}", w, 6)
}
}
#[cfg(test)]
mod tests {
use super::*;
use self::Instruction::*;
use self::DecodeError::*;
#[test]
fn test_oper() {
assert_eq!(0b001001, oper!(u8; 0x24ffffff; 31, 26));
assert_eq!(0xdead, oper!(u16; 0x0000dead; 15, 0));
mod itype {
use super::*;
#[test]
fn given_a_word_then_it_should_be_able_to_extract_the_itype_parts_of_the_instruction() {
let subject = IType(0x1234_5678);
assert_eq!(subject.rs(), 0b10001);
assert_eq!(subject.rt(), 0b10100);
assert_eq!(subject.imm(), 0b0101011001111000);
}
}
#[test]
fn test_decode_instr() {
assert_eq!(Ok(ADDIU { rs: 0b10101, rt: 0b00110, immediate: 0b0000111100001111 }), decode(0b001001_10101_00110_0000111100001111));
mod rtype {
use super::*;
#[test]
fn given_a_word_then_it_should_be_able_to_extract_the_rtype_parts_of_the_instruction() {
let subject = RType(0x1234_5678);
assert_eq!(subject.rs(), 0b10001);
assert_eq!(subject.rt(), 0b10100);
assert_eq!(subject.rd(), 0b01010);
assert_eq!(subject.sa(), 0b11001);
assert_eq!(subject.funct(), 0b111000);
}
}
#[test]
fn test_decode_special_instr() {
assert_eq!(Ok(ADD { rs: 0b10101, rt: 0b00110, rd: 0b11001 }), decode(0b000000_10101_00110_11001_00000_100000));
mod jtype {
use super::*;
#[test]
fn given_a_word_then_it_should_be_able_to_extract_the_jtype_parts_of_the_instruction() {
let subject = JType(0x1234_5678);
assert_eq!(subject.target(), 0x1234_5678);
}
}
#[test]
fn test_decode_no_match() {
assert_eq!(Err(UnknownInstruction(0b111111)), decode(0xffffffff));
mod decode {
use super::*;
#[test]
fn given_the_regimm_word_then_it_should_resolve_to_regimm() {
assert_eq!(Instruction::REGIMM(IType(0x0600_0000)), decode(0x0600_0000));
}
#[test]
fn given_the_srl_word_then_it_should_resolve_to_srl() {
assert_eq!(Instruction::SRL(RType(0x0000_0002)), decode(0x0000_0002));
}
#[test]
fn given_an_instruction_word_then_it_should_be_decodable() {
assert_eq!(Instruction::ADDIU(IType(0b001001_10101_00110_0000111100001111)), decode(0b001001_10101_00110_0000111100001111));
}
#[test]
fn given_a_special_instruction_word_then_it_should_be_decodable() {
assert_eq!(Instruction::ADD(RType(0b000000_10101_00110_11001_00000_100000)), decode(0b000000_10101_00110_11001_00000_100000));
}
#[test]
fn given_a_cop0_instruction_word_then_it_should_be_decodable() {
assert_eq!(Instruction::MTC0(RType(0x40800000)), decode(0x40800000));
}
}
}
pub use self::cpu::Cpu;
pub use self::instruction::Instruction;
pub use self::instruction::decode as decode_instruction;
mod cpu;
mod instruction;
mod reg;
pub mod mem;
pub use self::cpu::Cpu;
......@@ -81,14 +81,20 @@ 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 {
Default::default()
}
}
impl Default for Regs {
fn default() -> Self {
Self {
pc: 0xcafebabe,
status: Status::from_bits_truncate(0xcafebabe),
config: Config::from_bits_truncate(0xcafebabe),
gpr: [0xcafebabe; 32]
}
}
}
#[cfg(test)]
......
use cpu::Cpu;
use cpu::{self, Cpu};
use pif_rom::PifRom;
use cpu;
use cart::Cart;
......@@ -30,7 +29,7 @@ impl N64 {
pub fn start(&mut self) {
loop {
let word = self.cpu.fetch(self);
let instr = self.cpu.decode(word).unwrap();
let instr = self.cpu.decode(word);
self.cpu.execute(&instr);
}
}
......
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