rvm_opcode/
opcode.rs

1/*
2 * opcode.rs
3 * Defines an enum that represents an opcode
4 * Created by Andrew Davis
5 * Created on 1/16/2019
6 * Licensed under the Lesser GNU Public License, version 3
7 */
8
9//crate import statements
10extern crate num;
11
12//use statement
13use std::str;
14use std::cmp;
15
16/// Defines various opcodes
17#[derive(Debug, FromPrimitive, Copy, Clone)]
18pub enum Opcode {
19   /// No operation 
20   NOP = 0x00,
21   /// Add the values of two registers
22   ADD = 0x01,
23   /// Subtract the value of one register from another
24   SUB = 0x02,
25   /// Multiply the values of two registers
26   MPY = 0x03,
27   /// Divide the value of one register by another
28   DIV = 0x04,
29   /// Calculates the value of one register modulus another
30   MOD = 0x05,
31   /// Reads a string from stdin and puts it into memory
32   /// pointed to by an argument register
33   INS = 0x06,
34   /// Prints out a string pointed to by an argument register
35   OTS = 0x07,
36   /// Reads an integer from stdin and puts it into memory
37   /// pointed to by an argument register
38   INI = 0x08,
39   /// Prints out an integer pointed to by an argument register
40   OTI = 0x09,
41   /// Stops execution of a program
42   HLT = 0x0A,
43   /// Loads an immediate value into a register
44   LDI = 0x0B,
45   /// Copies the value of one register into another
46   MOV = 0x0C,
47   /// Loads the word at an address into a register
48   LDA = 0x0D,
49   /// Loads the word pointed to by a register into another register
50   MVA = 0x0E,
51   /// Loads the word in a register into memory
52   LRF = 0x0F,
53   /// Copies the value pointed to by one register into memory
54   /// pointed to by another
55   CPT = 0x10,
56   /// Pushes the value of a register onto the stack
57   PSH = 0x11,
58   /// Pops the value on top of the stack into a register
59   POP = 0x12,
60   /// Increments the value of a register
61   INC = 0x13,
62   /// Decrements the value of a register
63   DEC = 0x14,
64   /// Increments the value pointed to by a register
65   ICP = 0x15,
66   /// Decrements the value pointed to by a register
67   DCP = 0x16,
68   /// Unconditional jump
69   JMP = 0x17,
70   /// Compares the values of two registers and sets the proper flags
71   CMP = 0x18,
72   /// Jumps if the equals flag is not set
73   JNE = 0x19,
74   /// Jumps if the equals flag is set
75   JE = 0x1A,
76   /// Jumps if the less-than flag is set
77   JLT = 0x1B,
78   /// Jumps if the greater-than flag is set
79   JGT = 0x1C,
80   /// Checks a register and sets the proper flags
81   CHK = 0x1D,
82   /// Jumps if the zero flag is not set
83   JNZ = 0x1E,
84   /// Jumps if the zero flag is set
85   JZ = 0x1F,
86   /// Calls a subroutine
87   CALL = 0x20,
88   /// Returns from a subroutine
89   RET = 0x21,
90   /// Compares two strings and sets the proper flags
91   SCMP = 0x22,
92   /// Takes the length of a string pointed to by a register
93   /// and puts it into another register
94   SLEN = 0x23,
95   /// Prints the value of a register to stdout
96   ORG = 0x24,
97   /// Reads a value from stdin and puts it into a register
98   IRG = 0x25,
99   /// Prints out the values of all registers
100   DBG = 0x26,
101   /// Exchanges the values of two registers
102   EXG = 0x27,
103   /// Puts a random integer into the argument register
104   RAND = 0x28,
105}
106
107//method implementations
108impl Opcode {
109    /// Returns an Opcode instance corresponding to a one-byte ID
110    ///
111    /// # Arguments
112    ///
113    /// * `id` - The ID matching the desired opcode
114    ///
115    /// # Panics
116    ///
117    /// This method will panic if the argument ID does not
118    /// match any defined opcode. 
119    pub fn from_id(id: u8) -> Self {
120        //get an Option from the ID
121        let element = num::FromPrimitive::from_u8(id);
122
123        //match it to an Opcode
124        match element {
125            Some(op) => op,
126            None => panic!("There is no opcode with ID {}!", id)
127        }
128    }
129
130    /// Returns an Opcode instance corresponding to a name string
131    /// 
132    /// # Arguments
133    ///
134    /// * `name` - The name of the desired opcode
135    /// 
136    /// # Panics
137    ///
138    /// This method will panic if the argument name does not
139    /// match any defined opcode
140    pub fn from_name(name: &str) -> Self {
141        //get the opcode from the string
142        let as_enum: Opcode = name.parse().unwrap();
143
144        //and return it
145        return as_enum;
146    }
147
148    /// Returns the ID of the Opcode
149    pub fn get_id(&self) -> u8 {
150        return *self as u8;
151    }
152}
153
154//Trait implementations
155
156//This trait allows for getting an Opcode from a string
157impl str::FromStr for Opcode {
158    /// Error type for the from_str method
159    type Err = String;
160
161    /// Parses a string and returns an Opcode instance matching the string
162    /// wrapped in a Result instance 
163    /// 
164    /// # Arguments
165    ///
166    /// * `s` - the string to parse
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        match s {
169            "NOP" => Ok(Opcode::NOP),
170            "ADD" => Ok(Opcode::ADD),
171            "SUB" => Ok(Opcode::SUB),
172            "MPY" => Ok(Opcode::MPY),
173            "DIV" => Ok(Opcode::DIV),
174            "MOD" => Ok(Opcode::MOD),
175            "INS" => Ok(Opcode::INS),
176            "OTS" => Ok(Opcode::OTS),
177            "INI" => Ok(Opcode::INI),
178            "OTI" => Ok(Opcode::OTI),
179            "HLT" => Ok(Opcode::HLT),
180            "LDI" => Ok(Opcode::LDI),
181            "MOV" => Ok(Opcode::MOV),
182            "LDA" => Ok(Opcode::LDA),
183            "MVA" => Ok(Opcode::MVA),
184            "LRF" => Ok(Opcode::LRF),
185            "CPT" => Ok(Opcode::CPT),
186            "PSH" => Ok(Opcode::PSH),
187            "POP" => Ok(Opcode::POP),
188            "INC" => Ok(Opcode::INC),
189            "DEC" => Ok(Opcode::DEC),
190            "ICP" => Ok(Opcode::ICP),
191            "DCP" => Ok(Opcode::DCP),
192            "JMP" => Ok(Opcode::JMP),
193            "CMP" => Ok(Opcode::CMP),
194            "JNE" => Ok(Opcode::JNE),
195            "JE" => Ok(Opcode::JE),
196            "JLT" => Ok(Opcode::JLT),
197            "JGT" => Ok(Opcode::JGT),
198            "CHK" => Ok(Opcode::CHK),
199            "JNZ" => Ok(Opcode::JNZ),
200            "JZ" => Ok(Opcode::JZ),
201            "CALL" => Ok(Opcode::CALL),
202            "RET" => Ok(Opcode::RET),
203            "SCMP" => Ok(Opcode::SCMP),
204            "SLEN" => Ok(Opcode::SLEN),
205            "ORG" => Ok(Opcode::ORG),
206            "IRG" => Ok(Opcode::IRG),
207            "DBG" => Ok(Opcode::DBG),
208            "EXG" => Ok(Opcode::EXG),
209            "RAND" => Ok(Opcode::RAND),
210            _ => Err(format!("'{}' is not a valid opcode name", s)),
211        }
212    }
213}
214
215//This trait allows for comparing two Opcodes
216impl cmp::PartialEq<Opcode> for Opcode {
217    /// Compares two Opcodes and returns whether they are the same
218    ///
219    /// # Arguments
220    ///
221    /// * `other` - The opcode to compare `self` to
222    fn eq(&self, other: &Opcode) -> bool {
223        return self.get_id() == other.get_id();
224    }
225
226    /// Compares two Opcodes and returns whether they are different
227    /// 
228    /// # Arguments
229    ///
230    /// * `other` - The opcode to compare `self` to
231    fn ne(&self, other: &Opcode) -> bool {
232        return self.get_id() != other.get_id();
233    }
234}
235
236//end of enum
237
238//start of unit tests
239
240#[cfg(test)]
241mod tests {
242    //this function tests creating an Opcode from an ID
243    #[test]
244    fn from_id() {
245        let op = super::Opcode::from_id(0x00);
246        assert_eq!(op, super::Opcode::NOP);
247    }
248
249    //this function tests creating an Opcode from a name
250    #[test]
251    fn from_name() {
252        let op = super::Opcode::from_name("HLT");
253        assert_eq!(op, super::Opcode::HLT);
254    }
255
256    //this function tests invalid opcode IDs
257    #[test]
258    #[should_panic]
259    fn invalid_id() {
260        super::Opcode::from_id(0xFF);
261    }
262
263    //this function test invalid opcode names
264    #[test]
265    #[should_panic]
266    fn invalid_name() {
267        super::Opcode::from_name("AAA");
268    }
269
270}
271
272//end of unit tests