Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
/// Default DRAM size (128MiB).
pub const DRAM_SIZE: u64 = 1024 * 1024 * 128;
/// The CPU to contain registers, a program coutner, and DRAM.
struct Cpu {
/// 32 64-bit integer registers.
regs: [u64; 32],
/// Program counter to hold the the dram address of the next instruction that would be
/// executed.
pc: u64,
/// Computer dram to store executable instructions.
dram: Vec<u8>,
}
impl Cpu {
/// Create a new `Cpu` object.
fn new(code: Vec<u8>) -> Self {
let mut regs = [0; 32];
regs[2] = DRAM_SIZE;
Self {
regs,
pc: 0,
dram: code,
}
}
/// Print values in all registers (x0-x31).
pub fn dump_registers(&self) {
let mut output = String::from("");
let abi = [
"zero", " ra ", " sp ", " gp ", " tp ", " t0 ", " t1 ", " t2 ", " s0 ", " s1 ", " a0 ",
" a1 ", " a2 ", " a3 ", " a4 ", " a5 ", " a6 ", " a7 ", " s2 ", " s3 ", " s4 ", " s5 ",
" s6 ", " s7 ", " s8 ", " s9 ", " s10", " s11", " t3 ", " t4 ", " t5 ", " t6 ",
];
for i in (0..32).step_by(4) {
output = format!(
"{}\n{}",
output,
format!(
"x{:02}({})={:>#18x} x{:02}({})={:>#18x} x{:02}({})={:>#18x} x{:02}({})={:>#18x}",
i,
abi[i],
self.regs[i],
i + 1,
abi[i + 1],
self.regs[i + 1],
i + 2,
abi[i + 2],
self.regs[i + 2],
i + 3,
abi[i + 3],
self.regs[i + 3],
)
);
}
println!("{}", output);
}
/// Get an instruction from the dram.
fn fetch(&self) -> u32 {
let index = self.pc as usize;
return (self.dram[index] as u32)
| ((self.dram[index + 1] as u32) << 8)
| ((self.dram[index + 2] as u32) << 16)
| ((self.dram[index + 3] as u32) << 24);
}
/// Execute an instruction after decoding.
fn execute(&mut self, inst: u32) {
let opcode = inst & 0x7f;
let rd = ((inst >> 7) & 0x1f) as usize;
let rs1 = ((inst >> 15) & 0x1f) as usize;
let rs2 = ((inst >> 20) & 0x1f) as usize;
// Emulate that register x0 is hardwired with all bits equal to 0.
self.regs[0] = 0;
match opcode {
0x13 => {
// addi
let imm = ((inst & 0xfff00000) as i32 as i64 >> 20) as u64;
self.regs[rd] = self.regs[rs1].wrapping_add(imm);
}
0x33 => {
// add
self.regs[rd] = self.regs[rs1].wrapping_add(self.regs[rs2]);
}
_ => {
dbg!(format!("not implemented yet: opcode {:#x}", opcode));
}
}
}
}
fn main() -> io::Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
panic!("Usage: rvemu-for-book <filename>");
}
let mut file = File::open(&args[1])?;
let mut code = Vec::new();
file.read_to_end(&mut code)?;
let mut cpu = Cpu::new(code);
while cpu.pc < cpu.dram.len() as u64 {
// 1. Fetch.
let inst = cpu.fetch();
// 2. Add 4 to the program counter.
cpu.pc += 4;
// 3. Decode.
// 4. Execute.
cpu.execute(inst);
}
cpu.dump_registers();
Ok(())
}