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