Permalink
Cannot retrieve contributors at this time
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?
rvemu-for-book/01/src/main.rs
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
127 lines (111 sloc)
3.55 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | |
} |