riscii

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

commit 0c561dfa0990290dd0fd286d1bca610083f27888
parent 7fabdd6a1ec4309862ccd16c01991015cf884180
Author: Ryan Jeffrey <ryan@ryanmj.xyz>
Date:   Sat, 18 Jun 2022 01:11:16 -0700

Dyn error system

Diffstat:
Msrc/config.rs | 41+++++++++++++++++++----------------------
Msrc/cpu.rs | 79+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/decode.rs | 115++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/decode_test.rs | 79++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/execute.rs | 52++++++++++++++++++++++++++--------------------------
Msrc/main.rs | 19+++++++------------
Msrc/memory.rs | 94+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/sdl.rs | 3++-
Msrc/system.rs | 13+++++++------
Msrc/util.rs | 70++++++++++++++++++++++++++++++++++++++++++----------------------------
10 files changed, 312 insertions(+), 253 deletions(-)

diff --git a/src/config.rs b/src/config.rs @@ -23,10 +23,11 @@ use std::env; use std::fmt; use std::fs; use std::path::Path; -use util; -use util::concat_paths; +use util::{concat_paths, get_home_nofail, Result}; -use self::serde_derive::{Deserialize, Serialize}; +use berr; + +use self::serde_derive::Deserialize; /// Configuration of the emulator. #[derive(Deserialize)] @@ -55,8 +56,8 @@ pub struct Config { 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(); + pub fn new() -> Result<Config> { + let home_dir = get_home_nofail(); // Find a configuration path specified on the command line. let config_path = match env::var("XDG_CONFIG_HOME") { Ok(v) => format!("{}", v), @@ -77,7 +78,7 @@ impl Config { } /// Create an initialized configuration object on success and a string on error. - pub fn init() -> Result<Config, String> { + pub fn init() -> Result<Config> { let mut config = Self::new()?; let args: Vec<String> = env::args().collect(); // Look for custom config file location first. Read it, then override with cmd args. @@ -96,17 +97,17 @@ impl 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> { + fn read_config_file(&mut self) -> Result<()> { // TODO do not exit if config.toml does not exist // TODO get ~ in paths to expand // Keep the data we want to survive the assignment. let config_file_path = self.config_file_path.clone(); *self = match toml::from_str(&match fs::read_to_string(Path::new(&config_file_path)) { - Err(e) => return Err(format!("Could not read {}, {}", config_file_path, e)), + Err(e) => return berr!(format!("Could not read {}, {}", config_file_path, e)), Ok(r) => r, }) { Err(e) => { - return Err(format!( + return berr!(format!( "Could not parse config file {}, {}", config_file_path, e )) @@ -123,7 +124,7 @@ impl Config { /// success and string on error. /// # Arguments /// * `args` - CMD argument vector. - fn find_cmd_config_path(&self, args: &Vec<String>) -> Result<Option<String>, String> { + fn find_cmd_config_path(&self, args: &Vec<String>) -> Result<Option<String>> { for (i, arg) in args.iter().enumerate() { match arg.as_str() { "--config_path" => { @@ -141,7 +142,7 @@ impl Config { /// and a string on error. /// # Arguments /// * `args` - CMD argument vector. - fn parse_cmd_args(&mut self, args: &Vec<String>) -> Result<(), String> { + fn parse_cmd_args(&mut self, args: &Vec<String>) -> Result<()> { let mut skips = 1i32; for (i, arg) in args.iter().enumerate() { if skips > 0 { @@ -184,7 +185,7 @@ impl Config { --ncpu Number of cores to emulate (default=1) " ); - return Err(format!("Invalid command line argument: {}", arg)); + return berr!(format!("Invalid command line argument: {}", arg)); } } } @@ -222,9 +223,9 @@ impl Config { /// * `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> { +fn args_check_size(args: &Vec<String>, i: usize, what: &String) -> Result<()> { if i >= args.len() { - Err(format!( + berr!(format!( "Invalid command line argument: {} takes an argument.", what )) @@ -239,11 +240,7 @@ fn args_check_size(args: &Vec<String>, i: usize, what: &String) -> Result<(), St /// * `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, - what: &String, -) -> Result<&'a String, String> { +fn args_get_next_arg<'a>(args: &'a Vec<String>, i: usize, what: &String) -> Result<&'a String> { args_check_size(&args, i, &what)?; Ok(&args[i + 1]) } @@ -254,12 +251,12 @@ fn args_get_next_arg<'a>( /// * `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> { +fn args_get_next_uint(args: &Vec<String>, i: usize, what: &String) -> Result<u32> { args_check_size(&args, i, &what)?; Ok(match args[i + 1].parse::<u32>() { core::result::Result::Ok(u) => u, core::result::Result::Err(e) => { - return Err(format!( + return berr!(format!( "Invalid command line argument for {}: {}, err: {}.", what, args[i + 1], @@ -301,7 +298,7 @@ fn default_ncpu() -> u32 { } fn default_cache() -> String { - let home_dir = util::get_home_nofail(); + let home_dir = get_home_nofail(); let cache_dir = ".cache/riscii".to_string(); match env::var("XDG_CACHE_HOME") { diff --git a/src/cpu.rs b/src/cpu.rs @@ -16,6 +16,9 @@ use instruction::ShortSource; use memory::Memory; use std::convert::TryInto; use std::fmt; +use util::Result; + +use berr; /// The number of register windows the RISCII supports. pub const NUM_REG_WINDOWS: usize = 8; @@ -54,7 +57,7 @@ pub struct ProcessorStatusWord { /// Saved window pointer (MOD 8). swp: u32, /// If interrupts are enabled. - interrupt_enable_bit: bool, + interrupts_enabled: bool, /// System bit, true if running in privileged state. system_mode: bool, /// The previous state of the `system_mode` bit the last time it was changed. @@ -191,11 +194,13 @@ impl RegisterFile { /// Anything outside this [0-31] range is an invalid argument. /// # Arguments /// * `which` - Which register. [0-31] are the only valid values. - pub fn ru(&self, which: u32, psw: &ProcessorStatusWord) -> Result<u32, String> { + /// * `psw` - Processor status object, contains window information. + pub fn ru(&self, which: u32, psw: &ProcessorStatusWord) -> Result<u32> { + let which = which as usize; Ok(match which { 0..=9 => self.globals[which], - 10..=31 => self.window_regs[NUM_ADDED_PER_WINDOW * psw.get_cwp() + rd], - _ => return Err(format!("Register {} is out of range", which)), + 10..=31 => self.window_regs[NUM_ADDED_PER_WINDOW * psw.get_cwp() as usize + which], + _ => return berr!(format!("Register {} is out of range", which)), }) } @@ -208,7 +213,8 @@ impl RegisterFile { /// Anything outside this [0-31] range is an invalid argument. /// # Arguments /// * `which` - Which register. [0-31] are the only valid values. - pub fn rs(&self, which: u32, psw: &ProcessorStatusWord) -> Result<i32, String> { + /// * `psw` - Processor status object, contains window information. + pub fn rs(&self, which: u32, psw: &ProcessorStatusWord) -> Result<i32> { Ok(self.ru(which, psw)? as i32) } @@ -221,11 +227,15 @@ impl RegisterFile { /// Anything outside this [0-31] range is an invalid argument. /// # Arguments /// * `which` - Which register. [0-31] are the only valid values. - pub fn rus(&mut self, which: u32, value: u32, psw: ProcessorStatusWord) -> Result<u32, String> { + /// * `psw` - Processor status object, contains window information. + pub fn rus(&mut self, which: u32, value: u32, psw: ProcessorStatusWord) -> Result<u32> { + let which = which as usize; match which { 0..=9 => self.globals[which] = value, - 10..=31 => self.window_regs[NUM_ADDED_PER_WINDOW * psw.get_cwp() + rd] = value, - _ => return Err(format!("Register {} is out of range", which)), + 10..=31 => { + self.window_regs[NUM_ADDED_PER_WINDOW * psw.get_cwp() as usize + which] = value + } + _ => return berr!(format!("Register {} is out of range", which)), } Ok(value) } @@ -250,14 +260,14 @@ impl RegisterFile { pub fn branch_to(&mut self, to: u32) { self.lstpc = self.pc; - self.pc = nxtpc; + self.pc = self.nxtpc; self.nxtpc = to; } - pub fn get_ss_val(&self, ss: ShortSource) -> Result<u32, String> { + pub fn get_ss_val(&self, ss: ShortSource, psw: &ProcessorStatusWord) -> Result<u32> { type SS = ShortSource; Ok(match ss { - SS::Reg(r) => self.ru(r as u32)?, + SS::Reg(r) => self.ru(r as u32, psw)?, SS::Imm13(u) => u, }) } @@ -283,7 +293,7 @@ impl ProcessorStatusWord { Self { cwp: 0, swp: 0, - interrupt_enable_bit: false, + interrupts_enabled: false, system_mode: false, previous_system_mode: false, cc_zero: false, @@ -297,9 +307,9 @@ impl ProcessorStatusWord { Self { cwp: (v & (0x7 << 7)) >> 7, swp: (v & (0x7 << 10)) >> 10, - interrupt_enabled_bit: v & (0x1 << 6) != 0, - previous_system_bit: v & (0x1 << 5) != 0, - system_bit: v & (0x1 << 4) != 0, + interrupts_enabled: v & (0x1 << 6) != 0, + previous_system_mode: v & (0x1 << 5) != 0, + system_mode: v & (0x1 << 4) != 0, cc_zero: v & (0x1 << 3) != 0, cc_neg: v & (0x1 << 2) != 0, cc_overflow: v & (0x1 << 1) != 0, @@ -310,9 +320,9 @@ impl ProcessorStatusWord { pub fn init( cwp: u32, swp: u32, - interrupt_enable_bit: bool, - previous_system_bit: bool, - system_bit: bool, + interrupts_enabled: bool, + previous_system_mode: bool, + system_mode: bool, cc_zero: bool, cc_neg: bool, cc_overflow: bool, @@ -321,9 +331,9 @@ impl ProcessorStatusWord { Self { cwp: cwp % 8, swp: swp % 8, - interrupt_enable_bit: interrupt_enable_bit, - previous_system_bit: previous_system_bit, - system_bit: system_bit, + interrupts_enabled: interrupts_enabled, + previous_system_mode: previous_system_mode, + system_mode: system_mode, cc_zero: cc_zero, cc_neg: cc_neg, cc_overflow: cc_overflow, @@ -350,7 +360,7 @@ impl ProcessorStatusWord { | (self.cc_zero as u32) << 3 | (self.previous_system_mode as u32) << 4 | (self.system_mode as u32) << 5 - | (self.interrupt_enable_bit as u32) << 6 + | (self.interrupts_enabled as u32) << 6 | (self.cwp) << 7 | (self.swp) << 10) & 0x1fff @@ -359,29 +369,29 @@ impl ProcessorStatusWord { /// Push the register window stack. Set CWP to CWP-1 MOD 8. Push the top /// window to memory and increment SWP if necessary. pub fn push_reg_window(&mut self) { - self.cwp = (self.cwp - 1) % NUM_REG_WINDOWS; + self.cwp = (self.cwp - 1) % NUM_REG_WINDOWS as u32; if self.cwp == self.swp { // TODO save windows to memory. - self.swp = (self.swp + 1) % NUM_REG_WINDOWS; + self.swp = (self.swp + 1) % NUM_REG_WINDOWS as u32; } } /// Pop the register window stack. Set CWP to CWP+1 MOD 8. Pull the bottom /// window from memory and decrement SWP if necessary. pub fn pop_reg_window(&mut self) { - self.cwp = (self.cwp + 1) % NUM_REG_WINDOWS; + self.cwp = (self.cwp + 1) % NUM_REG_WINDOWS as u32; if self.cwp == self.swp { // TODO restore windows from memory. - self.swp = (self.swp - 1) % NUM_REG_WINDOWS; + self.swp = (self.swp - 1) % NUM_REG_WINDOWS as u32; } } pub fn set_cwp(&mut self, v: u32) { - self.cwp = v % NUM_REG_WINDOWS; + self.cwp = v % NUM_REG_WINDOWS as u32; } pub fn set_swp(&mut self, v: u32) { - self.swp = v % NUM_REG_WINDOWS; + self.swp = v % NUM_REG_WINDOWS as u32; } pub fn get_cwp(&self) -> u32 { @@ -424,16 +434,16 @@ impl ProcessorStatusWord { self.cc_neg = value; } - pub fn set_system_bit(&mut self, v: bool) { + pub fn set_system_mode(&mut self, v: bool) { self.system_mode = v; } - pub fn set_previous_system_bit(&mut self, v: bool) { + pub fn set_previous_system_mode(&mut self, v: bool) { self.previous_system_mode = v; } pub fn set_interrupt_enabled(&mut self, v: bool) { - self.interrupt_enable_bit = v; + self.interrupts_enabled = v; } pub fn is_system_mode(&self) -> bool { @@ -445,7 +455,7 @@ impl ProcessorStatusWord { } pub fn is_interrupt_enabled(&self) -> bool { - self.interrupt_enable_bit + self.interrupts_enabled } } @@ -464,6 +474,7 @@ CC Overflow: {} CC Carry: {}", self.cwp, self.swp, + privilege_string(self.interrupts_enabled), privilege_string(self.system_mode), privilege_string(self.previous_system_mode), bool_hl_string(self.cc_zero), @@ -479,7 +490,7 @@ CC Carry: {}", /// Create a descriptive string for the system's privilege state bits. /// # Arguments /// * `s` - Privilege state bit. -fn privilege_string(s: bool) -> &str { +fn privilege_string(s: bool) -> &'static str { if s { "Privileged" } else { @@ -490,7 +501,7 @@ fn privilege_string(s: bool) -> &str { /// Stringify booleans with hardware terminology. /// # Arguments /// * `s` - Boolean. -fn bool_hl_string(s: bool) -> &str { +fn bool_hl_string(s: bool) -> &'static str { if s { "High" } else { diff --git a/src/decode.rs b/src/decode.rs @@ -16,27 +16,60 @@ extern crate core; use core::convert::TryInto; +use std::error::Error; use std::fmt; +use util::Result; use instruction::*; +macro_rules! bdeii { + ( $( $loc:expr, $opcode:expr ),* ) => { + { + Err(Box::new($( DecodeError::InvalidInstruction { loc: $loc, opcode: $opcode } )*)) + } + }; +} + +macro_rules! bdeij { + ( $( $code:expr ),* ) => { + { + Err(Box::new($( DecodeError::InvalidJumpCondition { code: $code } )*)) + } + }; +} + +macro_rules! bdece { + ( $( $descr:expr ),* ) => { + { + Err(Box::new($( DecodeError::CodeError { descr: $descr } )*)) + } + }; +} + // Struct declarations. /// Opcode errors. -#[derive(PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum DecodeError { /// Indicates an invalid instruction. The first u32 indicates which bits are invalid, /// the final u32 is the whole opcode. - InvalidInstruction(u32, u32), - InvalidJumpCondition, + InvalidInstruction { + loc: u32, + opcode: u32, + }, + InvalidJumpCondition { + code: u32, + }, /// Indicates some bug in this program with a string description. - CodeError(String), + CodeError { + descr: String, + }, } // Public function declarations. -pub fn decode(opcode: u32) -> Result<Instruction, DecodeError> { +pub fn decode(opcode: u32) -> Result<Instruction> { type I = Instruction; // SCC flag (<24>). let scc = opcode & 0x1000000 != 0; @@ -48,7 +81,7 @@ pub fn decode(opcode: u32) -> Result<Instruction, DecodeError> { let imm19 = opcode & 0x7FFFF; // Short source immediate-mode bottom 13 bits <12-0> or rs1 <4-0>. let short_source = if opcode & 0x2000 != 0 { - ShortSource::UImm13(opcode & 0x1fff) + ShortSource::Imm13(opcode & 0x1fff) } else { ShortSource::Reg((opcode & 0x1f) as u8) }; // TODO fix ambiguous sign problem. @@ -62,28 +95,24 @@ pub fn decode(opcode: u32) -> Result<Instruction, DecodeError> { Ok(match op >> 4 { // Match the bottom four bytes of the opcode's prefix. 0 => match bottom_nibble { - 0 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 0 => return bdeii!(0xf, opcode), 1 => I::Calli(ShortInstruction::new(scc, dest, rs1, short_source)), 2 => I::GetPSW(ShortInstruction::new(scc, dest, rs1, short_source)), 3 => I::GetLPC(ShortInstruction::new(scc, dest, rs1, short_source)), 4 => I::PutPSW(ShortInstruction::new(scc, dest, rs1, short_source)), - 5..=7 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 5..=7 => return bdeii!(0xf, opcode), 8 => I::Callx(ShortInstruction::new(scc, dest, rs1, short_source)), 9 => I::Callr(LongInstruction::new(scc, dest, imm19)), - 10..=11 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 10..=11 => return bdeii!(0xf, opcode), 12 => I::Jmpx(ShortConditional::new(scc, cond?, rs1, short_source)), 13 => I::Jmpr(LongConditional::new(scc, cond?, imm19)), 14 => I::Ret(ShortConditional::new(scc, cond?, rs1, short_source)), 15 => I::Reti(ShortConditional::new(scc, cond?, rs1, short_source)), // Should never be reached. - _ => { - return Err(DecodeError::CodeError(String::from( - "Match bottom four bytes of opcode prefix", - ))) - } + _ => return bdece!(format!("Match bottom four bytes of opcode prefix")), }, 1 => match bottom_nibble { - 0 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 0 => return bdeii!(0xf, opcode), 1 => I::Sll(ShortInstruction::new(scc, dest, rs1, short_source)), 2 => I::Sra(ShortInstruction::new(scc, dest, rs1, short_source)), 3 => I::Srl(ShortInstruction::new(scc, dest, rs1, short_source)), @@ -93,20 +122,16 @@ pub fn decode(opcode: u32) -> Result<Instruction, DecodeError> { 7 => I::Xor(ShortInstruction::new(scc, dest, rs1, short_source)), 8 => I::Add(ShortInstruction::new(scc, dest, rs1, short_source)), 9 => I::Addc(ShortInstruction::new(scc, dest, rs1, short_source)), - 10..=11 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 10..=11 => return bdeii!(0xf, opcode), 12 => I::Sub(ShortInstruction::new(scc, dest, rs1, short_source)), 13 => I::Subc(ShortInstruction::new(scc, dest, rs1, short_source)), 14 => I::Subi(ShortInstruction::new(scc, dest, rs1, short_source)), 15 => I::Subci(ShortInstruction::new(scc, dest, rs1, short_source)), // Should never be reached. - _ => { - return Err(DecodeError::CodeError(String::from( - "Match bottom four bytes of opcode prefix", - ))) - } + _ => return bdece!(format!("Match bottom four bytes of opcode prefix")), }, 2 => match bottom_nibble { - 0..=5 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 0..=5 => return bdeii!(0xf, opcode), 6 => I::Ldxw(ShortInstruction::new(scc, dest, rs1, short_source)), 7 => I::Ldrw(LongInstruction::new(scc, dest, imm19)), 8 => I::Ldxhu(ShortInstruction::new(scc, dest, rs1, short_source)), @@ -118,39 +143,31 @@ pub fn decode(opcode: u32) -> Result<Instruction, DecodeError> { 14 => I::Ldxbs(ShortInstruction::new(scc, dest, rs1, short_source)), 15 => I::Ldrbs(LongInstruction::new(scc, dest, imm19)), // Should never be reached. - _ => { - return Err(DecodeError::CodeError(String::from( - "Match bottom four bytes of opcode prefix", - ))) - } + _ => return bdece!(format!("Match bottom four bytes of opcode prefix")), }, 3 => match bottom_nibble { - 0..=5 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 0..=5 => return bdeii!(0xf, opcode), 6 => I::Stxw(ShortInstruction::new(scc, dest, rs1, short_source)), 7 => I::Strw(LongInstruction::new(scc, dest, imm19)), - 8..=9 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 8..=9 => return bdeii!(0xf, opcode), 10 => I::Stxh(ShortInstruction::new(scc, dest, rs1, short_source)), 11 => I::Strh(LongInstruction::new(scc, dest, imm19)), - 12..=13 => return Err(DecodeError::InvalidInstruction(0x0f, opcode)), + 12..=13 => return bdeii!(0xf, opcode), 14 => I::Stxb(ShortInstruction::new(scc, dest, rs1, short_source)), 15 => I::Strb(LongInstruction::new(scc, dest, imm19)), // Should never be reached. - _ => { - return Err(DecodeError::CodeError(String::from( - "Match bottom four bytes of opcode prefix", - ))) - } + _ => return bdece!(format!("Match bottom four bytes of opcode prefix")), }, // Top bit is 1, meaning an extension opcode. 4..=8 => match opcode { // TODO - _ => return Err(DecodeError::CodeError(String::from("Not yet implemented!"))), + _ => return bdece!(format!("Not yet implemented!")), }, - _ => return Err(DecodeError::InvalidInstruction(0x8, opcode)), + _ => return bdeii!(0x8, opcode), }) } -pub fn decode_file(file: &Vec<u8>, pos: usize) -> Result<(), DecodeError> { +pub fn decode_file(file: &Vec<u8>, pos: usize) -> Result<()> { let result = 0usize; for i in (0..file.len()).step_by(4) { @@ -164,20 +181,28 @@ pub fn decode_file(file: &Vec<u8>, pos: usize) -> Result<(), DecodeError> { impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::InvalidInstruction(i, op) => write!(f, "Invalid: 0x{:x}, opcode: 0x{:x}", i, op), - Self::InvalidJumpCondition => write!(f, "Invalid jump condition"), - Self::CodeError(s) => write!(f, "Error in RISC II emulator: {}", s), - } + write!( + f, + "Error in decoding instruction: {}", + match self { + Self::InvalidInstruction { loc: i, opcode: op } => + format!("Invalid bits: 0x{:x}, opcode: 0x{:x}", i, op), + Self::InvalidJumpCondition { code: code } => + format!("Invalid jump condition: {} (should be 0-15)", code), + Self::CodeError { descr: s } => format!("Error in RISC II emulator: {}", s), + } + ) } } +impl Error for DecodeError {} + // Private functions. /// Get the RISC-II conditional type from a opcode<22-19>. /// opcode A RISC-II opcode. /// return RISC-II conditional, or DecodeError if 0. -fn get_cond_from_opcode(opcode: u32) -> Result<Conditional, DecodeError> { +fn get_cond_from_opcode(opcode: u32) -> Result<Conditional> { type C = Conditional; Ok(match (opcode & 0x780000) >> 19 { 1 => C::Gt, @@ -195,6 +220,6 @@ fn get_cond_from_opcode(opcode: u32) -> Result<Conditional, DecodeError> { 13 => C::Nv, 14 => C::V, 15 => C::Alw, - _ => return Err(DecodeError::InvalidJumpCondition), + code => return bdeij!(code), }) } diff --git a/src/decode_test.rs b/src/decode_test.rs @@ -17,6 +17,7 @@ #[path = "decode.rs"] mod test { use super::super::*; + use util::Result; use decode::*; use instruction::*; @@ -28,7 +29,7 @@ mod test { // Privileged instructions. #[test] - fn decode_calli() -> Result<(), DecodeError> { + fn decode_calli() -> Result<()> { assert_eq!( decode(0x0329f00f)?, I::Calli(ShortInstruction::new(true, 5, 7, SS::UImm13(4111))) @@ -37,7 +38,7 @@ mod test { } #[test] - fn decode_getpsw() -> Result<(), DecodeError> { + fn decode_getpsw() -> Result<()> { assert_eq!( decode(0x05293fff)?, I::GetPSW(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1fff))) @@ -46,7 +47,7 @@ mod test { } #[test] - fn decode_getipc() -> Result<(), DecodeError> { + fn decode_getipc() -> Result<()> { assert_eq!( decode(0x07293f69)?, I::GetLPC(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -55,7 +56,7 @@ mod test { } #[test] - fn decode_putpsw() -> Result<(), DecodeError> { + fn decode_putpsw() -> Result<()> { assert_eq!( decode(0x09293f69)?, I::PutPSW(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -66,7 +67,7 @@ mod test { // (unpriveleged) Call/jump/ret instructions. #[test] - fn decode_callx() -> Result<(), DecodeError> { + fn decode_callx() -> Result<()> { assert_eq!( decode(0x11293f69)?, I::Callx(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -75,7 +76,7 @@ mod test { } #[test] - fn decode_callr() -> Result<(), DecodeError> { + fn decode_callr() -> Result<()> { assert_eq!( decode(0x132b3420)?, I::Callr(LongInstruction::new(true, 5, 0x33420)) @@ -84,7 +85,7 @@ mod test { } #[test] - fn decode_jmpx() -> Result<(), DecodeError> { + fn decode_jmpx() -> Result<()> { assert_eq!( decode(0x19293f69)?, I::Jmpx(ShortConditional::new( @@ -98,7 +99,7 @@ mod test { } #[test] - fn decode_jmpr() -> Result<(), DecodeError> { + fn decode_jmpr() -> Result<()> { assert_eq!( decode(0x1bfb3420)?, I::Jmpr(LongConditional::new(true, Conditional::Alw, 0x33420)) @@ -107,7 +108,7 @@ mod test { } #[test] - fn decode_ret() -> Result<(), DecodeError> { + fn decode_ret() -> Result<()> { assert_eq!( decode(0x1d293f69)?, I::Ret(ShortConditional::new( @@ -121,7 +122,7 @@ mod test { } #[test] - fn decode_reti() -> Result<(), DecodeError> { + fn decode_reti() -> Result<()> { assert_eq!( decode(0x1f293f69)?, I::Reti(ShortConditional::new( @@ -137,7 +138,7 @@ mod test { // Arithmetic and logic instructions (except ldhi). #[test] - fn decode_sll() -> Result<(), DecodeError> { + fn decode_sll() -> Result<()> { assert_eq!( decode(0x23293f69)?, I::Sll(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -146,7 +147,7 @@ mod test { } #[test] - fn decode_sra() -> Result<(), DecodeError> { + fn decode_sra() -> Result<()> { assert_eq!( decode(0x25293f69)?, I::Sra(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -155,7 +156,7 @@ mod test { } #[test] - fn decode_srl() -> Result<(), DecodeError> { + fn decode_srl() -> Result<()> { assert_eq!( decode(0x27293f69)?, I::Srl(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -164,7 +165,7 @@ mod test { } #[test] - fn decode_ldhi() -> Result<(), DecodeError> { + fn decode_ldhi() -> Result<()> { assert_eq!( decode(0x292b3f69)?, I::Ldhi(LongInstruction::new(true, 5, 0x33f69)) @@ -173,7 +174,7 @@ mod test { } #[test] - fn decode_and() -> Result<(), DecodeError> { + fn decode_and() -> Result<()> { assert_eq!( decode(0x2b293f69)?, I::And(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -182,7 +183,7 @@ mod test { } #[test] - fn decode_or() -> Result<(), DecodeError> { + fn decode_or() -> Result<()> { assert_eq!( decode(0x2d293f69)?, I::Or(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -191,7 +192,7 @@ mod test { } #[test] - fn decode_xor() -> Result<(), DecodeError> { + fn decode_xor() -> Result<()> { assert_eq!( decode(0x2f293f69)?, I::Xor(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -200,7 +201,7 @@ mod test { } #[test] - fn decode_add() -> Result<(), DecodeError> { + fn decode_add() -> Result<()> { assert_eq!( decode(0x31293f69)?, I::Add(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -209,7 +210,7 @@ mod test { } #[test] - fn decode_addc() -> Result<(), DecodeError> { + fn decode_addc() -> Result<()> { assert_eq!( decode(0x33293f69)?, I::Addc(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -218,7 +219,7 @@ mod test { } #[test] - fn decode_sub() -> Result<(), DecodeError> { + fn decode_sub() -> Result<()> { assert_eq!( decode(0x39293f69)?, I::Sub(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -227,7 +228,7 @@ mod test { } #[test] - fn decode_subc() -> Result<(), DecodeError> { + fn decode_subc() -> Result<()> { assert_eq!( decode(0x3b293f69)?, I::Subc(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -236,7 +237,7 @@ mod test { } #[test] - fn decode_subi() -> Result<(), DecodeError> { + fn decode_subi() -> Result<()> { assert_eq!( decode(0x3d293f69)?, I::Subi(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -245,7 +246,7 @@ mod test { } #[test] - fn decode_subci() -> Result<(), DecodeError> { + fn decode_subci() -> Result<()> { assert_eq!( decode(0x3f293f69)?, I::Subci(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -256,7 +257,7 @@ mod test { // Load instructions. #[test] - fn decode_ldxw() -> Result<(), DecodeError> { + fn decode_ldxw() -> Result<()> { assert_eq!( decode(0x4d293f69)?, I::Ldxw(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -265,7 +266,7 @@ mod test { } #[test] - fn decode_ldrw() -> Result<(), DecodeError> { + fn decode_ldrw() -> Result<()> { assert_eq!( decode(0x4f2b3f69)?, I::Ldrw(LongInstruction::new(true, 5, 0x33f69)) @@ -274,7 +275,7 @@ mod test { } #[test] - fn decode_ldxhu() -> Result<(), DecodeError> { + fn decode_ldxhu() -> Result<()> { assert_eq!( decode(0x51293f69)?, I::Ldxhu(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -283,7 +284,7 @@ mod test { } #[test] - fn decode_ldrhu() -> Result<(), DecodeError> { + fn decode_ldrhu() -> Result<()> { assert_eq!( decode(0x532b3f69)?, I::Ldrhu(LongInstruction::new(true, 5, 0x33f69)) @@ -292,7 +293,7 @@ mod test { } #[test] - fn decode_ldxhs() -> Result<(), DecodeError> { + fn decode_ldxhs() -> Result<()> { assert_eq!( decode(0x55293f69)?, I::Ldxhs(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -301,7 +302,7 @@ mod test { } #[test] - fn decode_ldrhs() -> Result<(), DecodeError> { + fn decode_ldrhs() -> Result<()> { assert_eq!( decode(0x572b3f69)?, I::Ldrhs(LongInstruction::new(true, 5, 0x33f69)) @@ -310,7 +311,7 @@ mod test { } #[test] - fn decode_ldxbu() -> Result<(), DecodeError> { + fn decode_ldxbu() -> Result<()> { assert_eq!( decode(0x59293f69)?, I::Ldxbu(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -319,7 +320,7 @@ mod test { } #[test] - fn decode_ldrbu() -> Result<(), DecodeError> { + fn decode_ldrbu() -> Result<()> { assert_eq!( decode(0x5b2b3f69)?, I::Ldrbu(LongInstruction::new(true, 5, 0x33f69)) @@ -328,7 +329,7 @@ mod test { } #[test] - fn decode_ldxbs() -> Result<(), DecodeError> { + fn decode_ldxbs() -> Result<()> { assert_eq!( decode(0x5d293f69)?, I::Ldxbs(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -337,7 +338,7 @@ mod test { } #[test] - fn decode_ldrbs() -> Result<(), DecodeError> { + fn decode_ldrbs() -> Result<()> { assert_eq!( decode(0x5f2b3f69)?, I::Ldrbs(LongInstruction::new(true, 5, 0x33f69)) @@ -348,7 +349,7 @@ mod test { // Store instructions. #[test] - fn decode_stxw() -> Result<(), DecodeError> { + fn decode_stxw() -> Result<()> { assert_eq!( decode(0x6d293f69)?, I::Stxw(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -357,7 +358,7 @@ mod test { } #[test] - fn decode_Strw() -> Result<(), DecodeError> { + fn decode_Strw() -> Result<()> { assert_eq!( decode(0x6f2b3f69)?, I::Strw(LongInstruction::new(true, 5, 0x33f69)) @@ -366,7 +367,7 @@ mod test { } #[test] - fn decode_stxh() -> Result<(), DecodeError> { + fn decode_stxh() -> Result<()> { assert_eq!( decode(0x75293f69)?, I::Stxh(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -375,7 +376,7 @@ mod test { } #[test] - fn decode_Strh() -> Result<(), DecodeError> { + fn decode_Strh() -> Result<()> { assert_eq!( decode(0x772b3f69)?, I::Strh(LongInstruction::new(true, 5, 0x33f69)) @@ -384,7 +385,7 @@ mod test { } #[test] - fn decode_stxb() -> Result<(), DecodeError> { + fn decode_stxb() -> Result<()> { assert_eq!( decode(0x7d293f69)?, I::Stxb(ShortInstruction::new(true, 5, 4, SS::UImm13(0x1f69))) @@ -393,7 +394,7 @@ mod test { } #[test] - fn decode_Strb() -> Result<(), DecodeError> { + fn decode_Strb() -> Result<()> { assert_eq!( decode(0x7f2b3f69)?, I::Strb(LongInstruction::new(true, 5, 0x33f69)) diff --git a/src/execute.rs b/src/execute.rs @@ -31,13 +31,13 @@ pub struct ExecResult { // Public functions. // TODO timing and memory reads/writes. Need to emulate the pipeline and cpu clock. -pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecResult, String> { +pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecResult> { type I = Instruction; let mut result = ExecResult::from_system(system); let mut register_file = result.get_register_file(); let cur_pc = register_file.get_pc(); - + let cur_psw = system.get_psw(); let mut memory = system.get_mem_ref(); match *instruction { @@ -65,7 +65,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes rs1: _, short_source: ss, } => { - let psw = system.get_psw12() & 0xffff7; + let psw = cur_psw & 0xffff7; register_file.rus(dest, psw)?; if scc { let dest_val = register_file.ru(dest)?; @@ -104,7 +104,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes // TODO error } - let val = register_file.get_ss_val(ss)?; + let val = register_file.get_ss_val(ss, cur_psw)?; result.set_psw(register_file.ru(rs1)? + val); } I::Callx { @@ -115,7 +115,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes } => { // TODO test alignment (addr[0] == 1). let rs_val = register_file.ru(rs1)?; - let addr = register_file.get_ss_val(ss)? + rs_val; + let addr = register_file.get_ss_val(ss, cur_psw)? + rs_val; register_file.push_reg_window(); register_file.branch_to(addr); result.rus(cur_pc)?; @@ -140,7 +140,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes if exec_conditional(cond, result.get_psw()) { result.set_branch(true); let rs_val = register_file.ru(rs1)?; - let addr = register_file.get_ss_val(ss)? + rs_val; + let addr = register_file.get_ss_val(ss, cur_psw)? + rs_val; register_file.branch_to(addr); } } @@ -190,7 +190,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, s1_val << s2_val)?; if scc { set_shift_cc(result.get_psw_ref(), d); @@ -203,7 +203,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, s1_val >> s2_val)?; if scc { set_shift_cc(scc, result.get_psw_ref(), d); @@ -216,7 +216,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, s1_val as i32 >> s2_val)?; if scc { set_shift_cc(result.get_psw_ref(), d); @@ -229,7 +229,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, s1_val | s2_val)?; if scc { set_shift_cc(result.get_psw_ref(), d); @@ -242,7 +242,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, s1_val & s2_val)?; if scc { set_shift_cc(result.get_psw_ref(), d); @@ -255,7 +255,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, s1_val ^ s2_val)?; if scc { set_shift_cc(result.get_psw_ref(), d); @@ -268,7 +268,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let (res, o) = s1_val.overflowing_add(s2_val); let d = register_file.rus(dest, res)?; if scc { @@ -285,7 +285,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let mut psw = result.get_psw_ref(); let (r1, o1) = s1_val.overflowing_add(s2_val); let (res, o2) = r1.overflowing_add(psw.get_cc_carry() as u32); @@ -304,7 +304,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let (res, o) = s1_val.overflowing_sub(s2_val); let d = register_file.rus(dest, res)?; if scc { @@ -321,7 +321,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let mut psw = result.get_psw_ref(); let (r1, o1) = s1_val.overflowing_sub(s2_val); let (res, o2) = r1.overflowing_sub(!psw.get_cc_carry() as u32); @@ -341,7 +341,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let (res, o) = s2_val.overflowing_sub(s1_val); let d = register_file.rus(dest, res)?; if scc { @@ -359,7 +359,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { let s1_val = register_file.ru(rs1)?; - let s2_val = register_file.get_ss_val(ss)?; + let s2_val = register_file.get_ss_val(ss, cur_psw)?; let mut psw = result.get_psw_ref(); let (r1, o1) = s2_val.overflowing_sub(s1_val); let (res, o2) = r1.overflowing_sub(!psw.get_cc_carry() as u32); @@ -390,7 +390,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes short_source: ss, } => { // TODO Test alignment - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, memory.get_word(ss_val)?)?; if scc { set_load_cc(result.get_psw_ref(), d); @@ -413,7 +413,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes rs1: rs1, short_source: ss, } => { - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, memory.get_hword(ss_val)? as i32 as u32)?; if scc { set_load_cc(result.get_psw_ref(), d); @@ -436,7 +436,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes rs1: rs1, short_source: ss, } => { - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, memory.get_hword(ss_val)? as u32)?; if scc { set_load_cc(result.get_psw_ref(), d); @@ -459,7 +459,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes rs1: rs1, short_source: ss, } => { - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, memory.get_byte(ss_val)? as i32 as u32)?; if scc { set_load_cc(result.get_psw_ref(), d); @@ -482,7 +482,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes rs1: rs1, short_source: ss, } => { - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let d = register_file.rus(dest, memory.get_byte(ss_val)? as u32)?; if scc { set_load_cc(result.get_psw_ref(), d); @@ -509,7 +509,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes // warn // return Err("Store instructions should be immediate only (not registers)"); } - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let rs1_val = register_file.ru(rs1); let dest_val = register_file.ru(dest); memory.set_word(ss_val + rs1_val, dest_val); @@ -542,7 +542,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes // warn // return Err("Store instructions should be immediate only (not registers)"); } - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let rs1_val = register_file.ru(rs1); let dest_val = register_file.ru(dest); memory.set_hword(ss_val + rs1_val, dest_val as u16); @@ -575,7 +575,7 @@ pub fn execute(instruction: &Instruction, system: &mut System) -> Result<ExecRes // warn // return Err("Store instructions should be immediate only (not registers)"); } - let ss_val = register_file.get_ss_val(ss)?; + let ss_val = register_file.get_ss_val(ss, cur_psw)?; let rs1_val = register_file.ru(rs1); let dest_val = register_file.ru(dest); memory.set_byte(ss_val + rs1_val, dest_val as u8); diff --git a/src/main.rs b/src/main.rs @@ -21,31 +21,25 @@ mod decode_test; mod main_test; mod config; +mod cpu; mod decode; mod instruction; mod memory; -mod register; mod sdl; +mod system; mod util; use decode::decode_file; use std::fs; use config::Config; +use std::boxed::Box; +use std::error::Error; use system::System; // Struct/enum declarations. -fn get_program(path: &String) -> Result<Vec<u8>, String> { - println!("Opening binary file {}.", path); - - match fs::read(path) { - Ok(raw_p) => Ok(raw_p.to_vec()), - Err(raw_e) => Err(raw_e.to_string()), - } -} - -fn main() -> Result<(), String> { +fn main() -> Result<(), Box<dyn Error>> { let config = Config::init()?; let context = sdl::Context::new(&config)?; @@ -54,7 +48,8 @@ fn main() -> Result<(), String> { "Running emulator with the following configuration: \n{}\n", config ); - //let program = get_program(&String::from("test.bin"))?; + //println!("Opening binary file {}.", path); + //let program = fs::read(path)?; Ok(()) } diff --git a/src/memory.rs b/src/memory.rs @@ -16,10 +16,13 @@ // Struct definitions. use config::Config; -use util::{check_hword_alignment, check_word_alignment, File}; +use std::convert::TryInto; +use util::{check_hword_alignment, check_word_alignment, File, Result}; + +use berr; /// The real memory of the RISC II emulator. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub struct Memory(Vec<u8>); // Struct impls. @@ -35,101 +38,112 @@ impl Memory { } } + pub fn from_size(size: u32) -> Self { + Self { + 0: vec![0u8; size as usize], + } + } + pub fn from_vec(memory: &Vec<u8>) -> Self { Self { 0: memory.clone() } } - pub fn write_to_file(&mut self, file: &mut File) -> Result<(), String> { - file.write_vec(&self.0)?; - Ok(()) + pub fn write_to_file(&mut self, file: &mut File) -> Result<()> { + file.write_vec(&self.0) } pub fn write_buf(&mut self, addr: u32, buf: &[u8]) { - self[addr..buf.len()].copy_from_slice(buf); + self.0[addr as usize..buf.len()].copy_from_slice(buf); } - pub fn get_byte(&self, addr: u32) -> Result<u8, String> { - if addr >= self.len() { - Err(format!( + pub fn get_byte(&self, addr: u32) -> Result<u8> { + let addr = addr as usize; + if addr >= self.0.len() { + berr!(format!( "Memory read: address 0x{:x} is out range (memory is of size 0x{:x})", addr, - self.len() + self.0.len() )) } else { Ok(self.0[addr]) } } - pub fn get_hword(&self, addr: u32) -> Result<u16, String> { + pub fn get_hword(&self, addr: u32) -> Result<u16> { check_hword_alignment(addr)?; - if addr >= self.len() { - Err(format!( + let addr = addr as usize; + if addr >= self.0.len() { + berr!(format!( "Memory read: address 0x{:x} is out range (memory is of size 0x{:x})", addr, - self.len() + self.0.len() )) } else { - Ok(u16::from_be_bytes(self.0[addr..addr + 1])) + Ok(u16::from_be_bytes(self.0[addr..addr + 1].try_into()?)) } } - pub fn get_word(&self, addr: u32) -> Result<u32, String> { + pub fn get_word(&self, addr: u32) -> Result<u32> { check_word_alignment(addr)?; - if addr >= self.len() { - Err(format!( + let addr = addr as usize; + if addr >= self.0.len() { + berr!(format!( "Memory read: address 0x{:x} is out range (memory is of size 0x{:x})", addr, - self.len() + self.0.len() )) } else { - Ok(self.0[addr]) + Ok(u32::from_be_bytes(self.0[addr..addr + 4].try_into()?)) } } - pub fn set_word(&mut self, addr: u32, what: u32) -> Result<u32, String> { - check_word_alignment()?; - if addr >= self.len() - 4 { - Err(format!( + pub fn set_word(&mut self, addr: u32, what: u32) -> Result<u32> { + check_word_alignment(addr)?; + let addr = addr as usize; + if addr >= self.0.len() - 4 { + berr!(format!( "Memory write: address 0x{:x} is out range (memory is of size 0x{:x})", addr, - self.len() + self.0.len() )) } else { let what_bytes = if cfg!(target_endian = "little") { - u32::from_ne_bytes(what.swap_bytes()) + u32::to_ne_bytes(what.swap_bytes()) } else { - u32::from_ne_bytes(what) + u32::to_ne_bytes(what) }; - self.0[addr..addr + 4].copy_from_slice(what_bytes); + self.0[addr..addr + 4].copy_from_slice(&what_bytes); Ok(what) } } - pub fn set_hword(&mut self, addr: u32, what: u16) -> Result<u32, String> { - check_word_alignment()?; - if addr >= self.len() - 2 { - Err(format!( + pub fn set_hword(&mut self, addr: u32, what: u16) -> Result<u16> { + check_word_alignment(addr)?; + let addr = addr as usize; + if addr >= self.0.len() - 2 { + berr!(format!( "Memory write: address 0x{:x} is out range (memory is of size 0x{:x})", addr, - self.len() + self.0.len() )) } else { let what_bytes = if cfg!(target_endian = "little") { - u16::from_ne_bytes(what.swap_bytes()) + u16::to_ne_bytes(what.swap_bytes()) } else { - u16::from_ne_bytes(what) + u16::to_ne_bytes(what) }; - self.0[addr..addr + 2].copy_from_slice(what_bytes); + self.0[addr..addr + 2].copy_from_slice(&what_bytes); Ok(what) } } - pub fn set_byte(&mut self, addr: u32, what: u8) -> Result<u32, String> { - if addr >= self.len() { - Err(format!( + pub fn set_byte(&mut self, addr: u32, what: u8) -> Result<u8> { + let addr = addr as usize; + if addr >= self.0.len() { + berr!(format!( "Memory write: address 0x{:x} is out range (memory is of size 0x{:x})", addr, - self.len() + self.0.len() )) } else { self.0[addr] = what; diff --git a/src/sdl.rs b/src/sdl.rs @@ -27,6 +27,7 @@ use sdl2::video::Window; use sdl2::EventPump; use sdl2::Sdl; use sdl2::VideoSubsystem; +use util::Result; // Struct definitions. @@ -47,7 +48,7 @@ pub struct Context { impl Context { /// Create a new SDL window/context. Return context on success and a /// string on error. - pub fn new(config: &Config) -> Result<Self, String> { + pub fn new(config: &Config) -> Result<Self> { let sdl_context = sdl2::init()?; let video_subsystem = sdl_context.video()?; let window = video_subsystem diff --git a/src/system.rs b/src/system.rs @@ -18,9 +18,10 @@ use config::Config; use cpu::{ProcessorStatusWord, RegisterFile}; use memory::Memory; use std::fmt; +use util::Result; /// RISC II emulated system. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct System { /// RISC II register file. regs: RegisterFile, @@ -37,7 +38,7 @@ impl System { /// a string on error. /// # Arguments /// * `config` - Emulator configuration. - pub fn new(config: &Config) -> Result<Self, String> { + pub fn new(config: &Config) -> Result<Self> { Ok(Self { regs: RegisterFile::new(), psw: ProcessorStatusWord::new(), @@ -62,11 +63,11 @@ impl System { } pub fn call(&mut self, addr: u32) { - self.regs.push_reg_window(); + self.psw.push_reg_window(); } pub fn ret(&mut self) { - self.regs.pop_reg_window(); + self.psw.pop_reg_window(); } pub fn get_register_file(&mut self) -> &mut RegisterFile { @@ -106,12 +107,12 @@ impl System { System { regs: self.regs, psw: self.psw, - mem: vec![0; 0], + mem: Memory::from_size(0), } } pub fn get_mem_ref(&mut self) -> &mut Memory { - &mut self.memory + &mut self.mem } } diff --git a/src/util.rs b/src/util.rs @@ -13,13 +13,27 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. +use std::boxed::Box; use std::env; +use std::error::Error; use std::ffi::OsString; use std::fs; use std::fs::{Metadata, OpenOptions}; use std::io::{Read, Write}; use std::path::Path; -use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +pub type Result<T> = std::result::Result<T, Box<dyn Error>>; + +#[macro_export] +macro_rules! berr { + ( $( $x:expr ),* ) => { + { + let b: Box<dyn std::error::Error> = $( $x.into() )*; + Err(b) + } + }; +} // Public constants. @@ -41,7 +55,7 @@ pub struct File { /// 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> { +pub fn read_file_path(path: &String) -> Result<Vec<u8>> { File::open(&path)?.read_file() } @@ -49,28 +63,28 @@ pub fn read_file_path(path: &String) -> Result<Vec<u8>, String> { /// # Arguments /// * `base` - Base path. /// * `rest` - Rest of the path. -pub fn concat_paths(base: &String, rest: &String) -> Result<String, String> { +pub fn concat_paths(base: &String, rest: &String) -> Result<String> { let p = Path::new(&base).join(&rest); match p.to_str() { - None => Err(format!("{} and {} joined is not valid utf8", base, rest)), + None => berr!(format!("{} and {} joined is not valid utf8", base, rest)), Some(s) => Ok(s.to_string()), } } /// Get the current unix timestamp on success and a string on error. -pub fn get_unix_timestamp() -> Result<Duration, String> { +pub fn get_unix_timestamp() -> Result<Duration> { match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(r) => Ok(r), - Err(e) => Err(format!("Could not format unix timestamp: {}", e)), + Err(e) => berr!(format!("Could not format unix timestamp: {}", e)), } } -/// Convert a `Result<OsString, String>` to a `Result<String, String>`. +/// Convert a `Result<OsString, String>` to a `Result<String>`. /// # Arguments /// * `r` - Result to convert. -pub fn os_string_result_to_strings(r: Result<String, OsString>) -> Result<String, String> { +pub fn os_string_result_to_strings(r: std::result::Result<String, OsString>) -> Result<String> { match r { - Err(e) => Err(match e.into_string() { + Err(e) => berr!(match e.into_string() { Ok(s) => s, Err(ee) => "Could not coerce OS string into utf8 string".to_string(), }), @@ -102,17 +116,17 @@ pub fn get_home_nofail() -> String { } } -pub fn check_hword_alignment(addr: u32) -> Result<(), String> { +pub fn check_hword_alignment(addr: u32) -> Result<()> { if addr & 0x1 != 0 { - Err(format!("Bad half word alignment: 0x{:x}", addr)) + berr!(format!("Bad half word alignment: 0x{:x}", addr)) } else { Ok(()) } } -pub fn check_word_alignment(addr: u32) -> Result<(), String> { +pub fn check_word_alignment(addr: u32) -> Result<()> { if addr & 0x3 != 0 { - Err(format!("Bad word alignment: 0x{:x}", addr)) + berr!(format!("Bad word alignment: 0x{:x}", addr)) } else { Ok(()) } @@ -124,13 +138,13 @@ 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> { + pub fn open(path: &String) -> Result<Self> { match fs::File::open(&path) { Ok(r) => Ok(Self { file: r, path: format!("{}", path), }), - Err(e) => Err(format!("Could not open file {}: {}", path, e)), + Err(e) => berr!(format!("Could not open file {}: {}", path, e)), } } @@ -138,13 +152,13 @@ impl File { /// # Arguments /// * `path` - Path to file. /// * `ops` - File open options. - pub fn open_ops(path: &String, ops: &OpenOptions) -> Result<Self, String> { + pub fn open_ops(path: &String, ops: &OpenOptions) -> Result<Self> { match ops.open(&path) { Ok(r) => Ok(Self { file: r, path: format!("{}", path), }), - Err(e) => Err(format!("Could not open file {}: {}", path, e)), + Err(e) => berr!(format!("Could not open file {}: {}", path, e)), } } @@ -153,16 +167,16 @@ impl File { /// 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> { + pub fn read_into_vec(&mut self, buf: &mut Vec<u8>) -> Result<()> { match self.file.read_exact(&mut buf[..]) { Ok(r) => Ok(()), - Err(e) => Err(format!("Failed to read file {}, {}", self.path, e)), + Err(e) => berr!(format!("Failed to read file {}, {}", self.path, e)), } } /// 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> { + pub fn read_file(&mut self) -> Result<Vec<u8>> { let metadata = self.get_metadata()?; let mut result = vec![0u8; metadata.len() as usize]; self.read_into_vec(&mut result)?; @@ -172,10 +186,10 @@ impl File { /// 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> { + pub fn get_metadata(&mut self) -> Result<Metadata> { match self.file.metadata() { Ok(r) => Ok(r), - Err(e) => Err(format!("Could not read metadata for {}: {}", self.path, e)), + Err(e) => berr!(format!("Could not read metadata for {}: {}", self.path, e)), } } @@ -184,10 +198,10 @@ impl File { /// a string on error. /// # Arguments /// * `buf` - Byte buffer to read `self` into. - pub fn read(&mut self, buf: &mut [u8]) -> Result<(), String> { + pub fn read(&mut self, buf: &mut [u8]) -> Result<()> { match self.file.read_exact(buf) { Ok(r) => Ok(()), - Err(e) => Err(format!("Could not read buffer from {}: {}", self.path, e)), + Err(e) => berr!(format!("Could not read buffer from {}: {}", self.path, e)), } } @@ -195,10 +209,10 @@ impl File { /// 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> { + pub fn write_buf(&mut self, buf: &[u8]) -> Result<()> { match self.file.write_all(buf) { Ok(r) => Ok(()), - Err(e) => Err(format!( + Err(e) => berr!(format!( "Could not write byte buffer to {}: {}", self.path, e )), @@ -209,10 +223,10 @@ impl File { /// 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> { + pub fn write_vec(&mut self, buf: &Vec<u8>) -> Result<()> { match self.file.write_all(&buf[..]) { Ok(r) => Ok(()), - Err(e) => Err(format!( + Err(e) => berr!(format!( "Could not write byte buffer to {}: {}", self.path, e )),