riscii

An emulator for the RISC II
Log | Files | Refs | LICENSE

commit e31915a5c2d014b57347b388de316ed9d8b5727a
parent 338d24a611c0c8f6be6f04d18b03015b02d1132d
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date:   Mon, 13 Jun 2022 01:58:46 -0700

Docucomments

Diffstat:
Msrc/config.rs | 39+++++++++++++++++++++++++++++++++++++++
Msrc/cpu.rs | 69+++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/instruction.rs | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/util.rs | 45+++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 170 insertions(+), 30 deletions(-)

diff --git a/src/config.rs b/src/config.rs @@ -54,6 +54,7 @@ pub struct Config { // Struct impls. impl Config { + /// Create a new configuration object (with default settings) on success and a string on error. pub fn new() -> Result<Config, String> { let home_dir = util::get_home_nofail(); // Find a configuration path specified on the command line. @@ -75,6 +76,7 @@ impl Config { }) } + /// Create an initialized configuration object on success and a string on error. pub fn init() -> Result<Config, String> { let mut config = Self::new()?; let args: Vec<String> = env::args().collect(); @@ -91,6 +93,9 @@ impl Config { Ok(config) } + /// Read the user's configuration file and update configuration state + /// (default ~/.config/riscii/config.toml). Return void on success and a + /// string on error. fn read_config_file(&mut self) -> Result<(), String> { // TODO do not exit if config.toml does not exist // TODO get ~ in paths to expand @@ -114,6 +119,10 @@ impl Config { Ok(()) } + /// Parse CMD arguments for configuration file path. Return path on + /// success and string on error. + /// # Arguments + /// * `args` - CMD argument vector. fn find_cmd_config_path(&self, args: &Vec<String>) -> Result<Option<String>, String> { for (i, arg) in args.iter().enumerate() { match arg.as_str() { @@ -128,6 +137,10 @@ impl Config { Ok(None) } + /// Parse CMD args and update configuration state. Return void on success + /// and a string on error. + /// # Arguments + /// * `args` - CMD argument vector. fn parse_cmd_args(&mut self, args: &Vec<String>) -> Result<(), String> { let mut skips = 1i32; for (i, arg) in args.iter().enumerate() { @@ -180,21 +193,35 @@ impl Config { // Getters. + /// Get the user's configured window width. pub fn get_win_width(&self) -> u32 { self.win_width } + /// Get the user's configured window height. pub fn get_win_height(&self) -> u32 { self.win_height } + /// Get the user's configured memory size. pub fn get_mem_size(&self) -> u32 { self.mem } + + /// Get the user's configured number of CPUs. + pub fn get_ncpus(&self) -> u32 { + self.ncpu + } } // Local functions. +/// Check the argument vector to make sure it has at least one more string +/// after the current argument. Return void on success and a string on error. +/// # Arguments +/// * `args` - CMD argument vector. +/// * `i` - Index of the current argument. +/// * `what` - String describing the current argument (for error message). fn args_check_size(args: &Vec<String>, i: usize, what: &String) -> Result<(), String> { if i >= args.len() { Err(format!( @@ -206,6 +233,12 @@ fn args_check_size(args: &Vec<String>, i: usize, what: &String) -> Result<(), St } } +/// Get the next argument in the argument vector as a string. Return next +/// string on success and a string on error. +/// # Arguments +/// * `args` - CMD argument vector. +/// * `i` - Index of the current argument. +/// * `what` - String describing the current argument (for error message). fn args_get_next_arg<'a>( args: &'a Vec<String>, i: usize, @@ -215,6 +248,12 @@ fn args_get_next_arg<'a>( Ok(&args[i + 1]) } +/// Get the next argument in the argument vector as a u32. Return +/// u32 on success and a string on error. +/// # Arguments +/// * `args` - CMD argument vector. +/// * `i` - Index of the current argument. +/// * `what` - String describing the current argument (for error message). fn args_get_next_uint(args: &Vec<String>, i: usize, what: &String) -> Result<u32, String> { args_check_size(&args, i, &what)?; Ok(match args[i + 1].parse::<u32>() { diff --git a/src/cpu.rs b/src/cpu.rs @@ -65,15 +65,23 @@ pub struct RegisterFile { window_regs: [Register; NUM_WINDOW_REGISTERS], // TODO test, there should be 138 regs. } -/// +/// Load/Store queries. Specifies the register and the value to set it to (if store). pub enum LoadStore { - NXTPC { val: u32 }, - PC { val: u32 }, - LSTPC { val: u32 }, - CWP { val: u32 }, - SWP { val: u32 }, - Global { which: u32, val: u32 }, - Window { which: u32, val: u32 }, + /// Global register. + Global { + /// Which global register. g0 is special and will always return 0, + /// attempts to store to it are ignored. Values above 10 throw an error. + which: u32, + /// Value to set register to (ignored owhen loading). + val: u32, + }, + /// Window register. + Window { + /// Which window register. Values above 22 throw an error. + which: u32, + /// Value to set register to (ignored owhen loading). + val: u32, + }, } // Struct implementations. @@ -91,7 +99,7 @@ impl RegisterFile { /// Create a register state from a buffer. /// # Arguments - /// * `buffer` - A byte buffer that is the size of the sum of of register::RegisterFile's + /// * `buf` - A byte buffer that is the size of the sum of of register::RegisterFile's /// members (in bytes) (see `SIZEOF_STATE`). /// The registers should appear in the following order: /// - NXTPC @@ -133,6 +141,7 @@ impl RegisterFile { } } + /// Convert self to a byte buffer of all of the register values. pub fn to_buf(&self) -> [u8; SIZEOF_STATE] { let mut result = [0u8; SIZEOF_STATE]; result[0..4].copy_from_slice(&self.cwp.to_be_bytes()); @@ -159,6 +168,8 @@ impl RegisterFile { result } + /// Push the register window stack. Increment CWP by 1 and flush the bottom + /// windows to memory if necessary and change SWP. pub fn push_reg_window(&mut self) { self.cwp += 1; while self.cwp >= self.swp { @@ -167,6 +178,8 @@ impl RegisterFile { } } + /// Pop the register window stack. Decrement CWP by 1 and pull the bottom + /// windows from memory if necessary and change SWP. pub fn pop_reg_window(&mut self) { self.cwp -= 1; while self.swp >= self.cwp { @@ -175,14 +188,13 @@ impl RegisterFile { } } - pub fn load(&self, ls: LoadStore) -> Result<u32, String> { + /// Load from a register (unsigned). Return the register's value + /// on success and a string message on error. + /// # Arguments + /// * `ls` - Load/Store instruction. Will error if `which` is out of range. + pub fn load_u(&self, ls: LoadStore) -> Result<u32, String> { type LS = LoadStore; Ok(match ls { - LS::NXTPC { val: _ } => self.nxtpc, - LS::PC { val: _ } => self.pc, - LS::LSTPC { val: _ } => self.lstpc, - LS::CWP { val: _ } => self.cwp, - LS::SWP { val: _ } => self.swp, LS::Global { which: rd, val: _ } => { if rd <= NUM_GLOBALS { self.globals[rd] @@ -204,24 +216,21 @@ impl RegisterFile { }) } + /// Load from a register (signed). Return the register's value + /// on success and a string message on error. + /// # Arguments + /// * `ls` - Load/Store instruction. `val` is ignored. + pub fn load_s(&self, ls: LoadStore) -> Result<i32, String> { + self.load_u(ls)? as i32 + } + + /// Store to a register. Return void on success and a string message on + /// failure. + /// # Arguments + /// * `ls` - Load/Store instruction. Will error if `which` is out of range. pub fn store(&mut self, ls: LoadStore) -> Result<(), String> { type LS = LoadStore; Ok(match ls { - LS::NXTPC { val: v } => { - self.nxtpc = v; - } - LS::PC { val: v } => { - self.pc = v; - } - LS::LSTPC { val: v } => { - self.lstpc = v; - } - LS::CWP { val: v } => { - self.cwp = v; - } - LS::SWP { val: v } => { - self.swp = v; - } LS::Global { which: rd, val: v } => { if rd <= NUM_GLOBALS && rd > 0 { self.globals[rd] = val; diff --git a/src/instruction.rs b/src/instruction.rs @@ -64,33 +64,51 @@ pub enum ShortSource { SImm13(i32), } +/// Short instruction format data. #[derive(PartialEq, Eq, Copy, Clone)] pub struct ShortInstruction { + /// Update CC bit. scc: bool, + /// Destination register. dest: u8, + /// Source register. rs1: u8, + /// Short source data. short_source: ShortSource, } +/// Long instruction format data. #[derive(PartialEq, Eq, Copy, Clone)] pub struct LongInstruction { + /// Update CC bit. scc: bool, + /// Destination register. dest: u8, + /// 19 bit constant. imm19: u32, } +/// Short conditional instruction format data. #[derive(PartialEq, Eq, Copy, Clone)] pub struct ShortConditional { + /// Update CC bit. scc: bool, + /// Destination register. dest: Conditional, + /// Source register. rs1: u8, + /// Short source data. short_source: ShortSource, } +/// Long conditional instruction format data. #[derive(PartialEq, Eq, Copy, Clone)] pub struct LongConditional { + /// Update CC bit. scc: bool, + /// Destination register. dest: Conditional, + /// 19 bit constant. imm19: u32, } @@ -252,6 +270,11 @@ pub enum Instruction { // Impls. impl ShortSource { + /// Create a new short source. + /// # Arguments + /// * `opcode` - The current opcode being executed. + /// * `signed` - True if `self` is a 13 bit constant and signed. This + /// is ignored if `self` is not a constant. pub fn new(opcode: u32, signed: bool) -> Self { // Short source immediate-mode bottom 13 bits <12-0> or rs1 <4-0>. if opcode & 0x2000 != 0 { @@ -266,6 +289,8 @@ impl ShortSource { } } + /// Create a new short source. If `self` is an unsigned constant, + /// convert it a signed constant. Else, return `self`. pub fn uimm_to_simm(&self) -> Self { match *self { Self::UImm13(u) => { @@ -303,6 +328,11 @@ impl LowerHex for ShortSource { } impl LongInstruction { + /// Create a new long instruction. + /// # Arguments + /// * `scc` - Should update CC's. + /// * `dest` - Destination register. + /// * `imm19` - 19 bit constant. pub fn new(scc: bool, dest: u8, imm19: u32) -> Self { Self { scc: scc, @@ -323,6 +353,11 @@ impl fmt::Display for LongInstruction { } impl LongConditional { + /// Create a new long conditional instruction. + /// # Arguments + /// * `scc` - Should update CC's. + /// * `dest` - Conditional. + /// * `imm19` - 19 bit constant. pub fn new(scc: bool, dest: Conditional, imm19: u32) -> Self { Self { scc: scc, @@ -343,6 +378,12 @@ impl fmt::Display for LongConditional { } impl ShortInstruction { + /// Create a new long conditional instruction. + /// # Arguments + /// * `scc` - Should update CC's. + /// * `dest` - Destination register. + /// * `rs1` - Source register. + /// * `short_source` - Short source. pub fn new(scc: bool, dest: u8, rs1: u8, short_source: ShortSource) -> Self { Self { scc: scc, @@ -364,6 +405,12 @@ impl fmt::Display for ShortInstruction { } impl ShortConditional { + /// Create a new long conditional instruction. + /// # Arguments + /// * `scc` - Should update CC's. + /// * `dest` - Conditional. + /// * `rs1` - Source register. + /// * `short_source` - Short source. pub fn new(scc: bool, dest: Conditional, rs1: u8, short_source: ShortSource) -> Self { Self { scc: scc, diff --git a/src/util.rs b/src/util.rs @@ -23,17 +23,27 @@ use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH}; // Public struct definitions. +/// File object. Wrapper around fs::File but caches more data. pub struct File { + /// Underlying file object. file: fs::File, + /// Path to the object. path: String, } // Public function definitions. +/// Return a file's contents as a byte vector on success and a string on error. +/// # Arguments +/// * `path` - Path to the file. pub fn read_file_path(path: &String) -> Result<Vec<u8>, String> { File::open(&path)?.read_file() } +/// Return two paths concatenated together on success and a string on error. +/// # Arguments +/// * `base` - Base path. +/// * `rest` - Rest of the path. pub fn concat_paths(base: &String, rest: &String) -> Result<String, String> { let p = Path::new(&base).join(&rest); match p.to_str() { @@ -42,6 +52,7 @@ pub fn concat_paths(base: &String, rest: &String) -> Result<String, String> { } } +/// Get the current unix timestamp on success and a string on error. pub fn get_unix_timestamp() -> Result<Duration, String> { match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(r) => Ok(r), @@ -49,6 +60,9 @@ pub fn get_unix_timestamp() -> Result<Duration, String> { } } +/// Convert a `Result<OsString, String>` to a `Result<String, String>`. +/// # Arguments +/// * `r` - Result to convert. pub fn os_string_result_to_strings(r: Result<String, OsString>) -> Result<String, String> { match r { Err(e) => Err(match e.into_string() { @@ -59,6 +73,8 @@ pub fn os_string_result_to_strings(r: Result<String, OsString>) -> Result<String } } +/// Get the user's home directory. If that fails, get the current directory. +/// If that fails, return an empty string. pub fn get_home_nofail() -> String { match env::var("HOME") { Ok(v) => format!("{}", v), @@ -84,6 +100,9 @@ pub fn get_home_nofail() -> String { // Struct impls. impl File { + /// Open a file from a path. Return File on success and a string on error. + /// # Arguments + /// * `path` - Path to file. pub fn open(path: &String) -> Result<Self, String> { match fs::File::open(&path) { Ok(r) => Ok(Self { @@ -94,6 +113,10 @@ impl File { } } + /// Open a file from a path with options. Return File on success and a string on error. + /// # Arguments + /// * `path` - Path to file. + /// * `ops` - File open options. pub fn open_ops(path: &String, ops: &OpenOptions) -> Result<Self, String> { match ops.open(&path) { Ok(r) => Ok(Self { @@ -104,6 +127,11 @@ impl File { } } + /// Read `self`'s contents into a byte vector, reading as many bytes as + /// necessary to fill that vector. Return void on success and + /// a string on error. + /// # Arguments + /// * `buf` - Byte vector to read `self` into. pub fn read_into_vec(&mut self, buf: &mut Vec<u8>) -> Result<(), String> { match self.file.read_exact(&mut buf[..]) { Ok(r) => Ok(()), @@ -111,6 +139,8 @@ impl File { } } + /// Read `self`'s contents into a byte vector. Return byte vector on success and + /// a string on error. pub fn read_file(&mut self) -> Result<Vec<u8>, String> { let metadata = self.get_metadata()?; let mut result = vec![0u8; metadata.len() as usize]; @@ -119,6 +149,8 @@ impl File { Ok(result) } + /// Read `self`'s contents into a byte vector. Return byte vector on success and + /// a string on error. pub fn get_metadata(&mut self) -> Result<Metadata, String> { match self.file.metadata() { Ok(r) => Ok(r), @@ -126,6 +158,11 @@ impl File { } } + /// Read `self`'s contents into a byte buffer, reading as many bytes as + /// necessary to fill that buffer. Return void on success and + /// a string on error. + /// # Arguments + /// * `buf` - Byte buffer to read `self` into. pub fn read(&mut self, buf: &mut [u8]) -> Result<(), String> { match self.file.read_exact(buf) { Ok(r) => Ok(()), @@ -133,6 +170,10 @@ impl File { } } + /// Write `buf`'s contents into a self (as binary data). + /// Return void on success and string on error. + /// # Arguments + /// * `buf` - Byte buffer to write to `self`. pub fn write_buf(&mut self, buf: &[u8]) -> Result<(), String> { match self.file.write_all(buf) { Ok(r) => Ok(()), @@ -143,6 +184,10 @@ impl File { } } + /// Write `buf`'s contents into a self (as binary data). + /// Return void on success and string on error. + /// # Arguments + /// * `buf` - Byte vector to write to `self`. pub fn write_vec(&mut self, buf: &Vec<u8>) -> Result<(), String> { match self.file.write_all(&buf[..]) { Ok(r) => Ok(()),