1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
 * opcode.rs
 * Defines an enum that represents an opcode
 * Created by Andrew Davis
 * Created on 1/16/2019
 * Licensed under the Lesser GNU Public License, version 3
 */

//crate import statements
extern crate num;

//use statement
use std::str;
use std::cmp;

/// Defines various opcodes
#[derive(Debug, FromPrimitive, Copy, Clone)]
pub enum Opcode {
   /// No operation 
   NOP = 0x00,
   /// Add the values of two registers
   ADD = 0x01,
   /// Subtract the value of one register from another
   SUB = 0x02,
   /// Multiply the values of two registers
   MPY = 0x03,
   /// Divide the value of one register by another
   DIV = 0x04,
   /// Calculates the value of one register modulus another
   MOD = 0x05,
   /// Reads a string from stdin and puts it into memory
   /// pointed to by an argument register
   INS = 0x06,
   /// Prints out a string pointed to by an argument register
   OTS = 0x07,
   /// Reads an integer from stdin and puts it into memory
   /// pointed to by an argument register
   INI = 0x08,
   /// Prints out an integer pointed to by an argument register
   OTI = 0x09,
   /// Stops execution of a program
   HLT = 0x0A,
   /// Loads an immediate value into a register
   LDI = 0x0B,
   /// Copies the value of one register into another
   MOV = 0x0C,
   /// Loads the word at an address into a register
   LDA = 0x0D,
   /// Loads the word pointed to by a register into another register
   MVA = 0x0E,
   /// Loads the word in a register into memory
   LRF = 0x0F,
   /// Copies the value pointed to by one register into memory
   /// pointed to by another
   CPT = 0x10,
   /// Pushes the value of a register onto the stack
   PSH = 0x11,
   /// Pops the value on top of the stack into a register
   POP = 0x12,
   /// Increments the value of a register
   INC = 0x13,
   /// Decrements the value of a register
   DEC = 0x14,
   /// Increments the value pointed to by a register
   ICP = 0x15,
   /// Decrements the value pointed to by a register
   DCP = 0x16,
   /// Unconditional jump
   JMP = 0x17,
   /// Compares the values of two registers and sets the proper flags
   CMP = 0x18,
   /// Jumps if the equals flag is not set
   JNE = 0x19,
   /// Jumps if the equals flag is set
   JE = 0x1A,
   /// Jumps if the less-than flag is set
   JLT = 0x1B,
   /// Jumps if the greater-than flag is set
   JGT = 0x1C,
   /// Checks a register and sets the proper flags
   CHK = 0x1D,
   /// Jumps if the zero flag is not set
   JNZ = 0x1E,
   /// Jumps if the zero flag is set
   JZ = 0x1F,
   /// Calls a subroutine
   CALL = 0x20,
   /// Returns from a subroutine
   RET = 0x21,
   /// Compares two strings and sets the proper flags
   SCMP = 0x22,
   /// Takes the length of a string pointed to by a register
   /// and puts it into another register
   SLEN = 0x23,
   /// Prints the value of a register to stdout
   ORG = 0x24,
   /// Reads a value from stdin and puts it into a register
   IRG = 0x25,
   /// Prints out the values of all registers
   DBG = 0x26,
   /// Exchanges the values of two registers
   EXG = 0x27,
   /// Puts a random integer into the argument register
   RAND = 0x28,
}

//method implementations
impl Opcode {
    /// Returns an Opcode instance corresponding to a one-byte ID
    ///
    /// # Arguments
    ///
    /// * `id` - The ID matching the desired opcode
    ///
    /// # Panics
    ///
    /// This method will panic if the argument ID does not
    /// match any defined opcode. 
    pub fn from_id(id: u8) -> Self {
        //get an Option from the ID
        let element = num::FromPrimitive::from_u8(id);

        //match it to an Opcode
        match element {
            Some(op) => op,
            None => panic!("There is no opcode with ID {}!", id)
        }
    }

    /// Returns an Opcode instance corresponding to a name string
    /// 
    /// # Arguments
    ///
    /// * `name` - The name of the desired opcode
    /// 
    /// # Panics
    ///
    /// This method will panic if the argument name does not
    /// match any defined opcode
    pub fn from_name(name: &str) -> Self {
        //get the opcode from the string
        let as_enum: Opcode = name.parse().unwrap();

        //and return it
        return as_enum;
    }

    /// Returns the ID of the Opcode
    pub fn get_id(&self) -> u8 {
        return *self as u8;
    }
}

//Trait implementations

//This trait allows for getting an Opcode from a string
impl str::FromStr for Opcode {
    /// Error type for the from_str method
    type Err = String;

    /// Parses a string and returns an Opcode instance matching the string
    /// wrapped in a Result instance 
    /// 
    /// # Arguments
    ///
    /// * `s` - the string to parse
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "NOP" => Ok(Opcode::NOP),
            "ADD" => Ok(Opcode::ADD),
            "SUB" => Ok(Opcode::SUB),
            "MPY" => Ok(Opcode::MPY),
            "DIV" => Ok(Opcode::DIV),
            "MOD" => Ok(Opcode::MOD),
            "INS" => Ok(Opcode::INS),
            "OTS" => Ok(Opcode::OTS),
            "INI" => Ok(Opcode::INI),
            "OTI" => Ok(Opcode::OTI),
            "HLT" => Ok(Opcode::HLT),
            "LDI" => Ok(Opcode::LDI),
            "MOV" => Ok(Opcode::MOV),
            "LDA" => Ok(Opcode::LDA),
            "MVA" => Ok(Opcode::MVA),
            "LRF" => Ok(Opcode::LRF),
            "CPT" => Ok(Opcode::CPT),
            "PSH" => Ok(Opcode::PSH),
            "POP" => Ok(Opcode::POP),
            "INC" => Ok(Opcode::INC),
            "DEC" => Ok(Opcode::DEC),
            "ICP" => Ok(Opcode::ICP),
            "DCP" => Ok(Opcode::DCP),
            "JMP" => Ok(Opcode::JMP),
            "CMP" => Ok(Opcode::CMP),
            "JNE" => Ok(Opcode::JNE),
            "JE" => Ok(Opcode::JE),
            "JLT" => Ok(Opcode::JLT),
            "JGT" => Ok(Opcode::JGT),
            "CHK" => Ok(Opcode::CHK),
            "JNZ" => Ok(Opcode::JNZ),
            "JZ" => Ok(Opcode::JZ),
            "CALL" => Ok(Opcode::CALL),
            "RET" => Ok(Opcode::RET),
            "SCMP" => Ok(Opcode::SCMP),
            "SLEN" => Ok(Opcode::SLEN),
            "ORG" => Ok(Opcode::ORG),
            "IRG" => Ok(Opcode::IRG),
            "DBG" => Ok(Opcode::DBG),
            "EXG" => Ok(Opcode::EXG),
            "RAND" => Ok(Opcode::RAND),
            _ => Err(format!("'{}' is not a valid opcode name", s)),
        }
    }
}

//This trait allows for comparing two Opcodes
impl cmp::PartialEq<Opcode> for Opcode {
    /// Compares two Opcodes and returns whether they are the same
    ///
    /// # Arguments
    ///
    /// * `other` - The opcode to compare `self` to
    fn eq(&self, other: &Opcode) -> bool {
        return self.get_id() == other.get_id();
    }

    /// Compares two Opcodes and returns whether they are different
    /// 
    /// # Arguments
    ///
    /// * `other` - The opcode to compare `self` to
    fn ne(&self, other: &Opcode) -> bool {
        return self.get_id() != other.get_id();
    }
}

//end of enum

//start of unit tests

#[cfg(test)]
mod tests {
    //this function tests creating an Opcode from an ID
    #[test]
    fn from_id() {
        let op = super::Opcode::from_id(0x00);
        assert_eq!(op, super::Opcode::NOP);
    }

    //this function tests creating an Opcode from a name
    #[test]
    fn from_name() {
        let op = super::Opcode::from_name("HLT");
        assert_eq!(op, super::Opcode::HLT);
    }

    //this function tests invalid opcode IDs
    #[test]
    #[should_panic]
    fn invalid_id() {
        super::Opcode::from_id(0xFF);
    }

    //this function test invalid opcode names
    #[test]
    #[should_panic]
    fn invalid_name() {
        super::Opcode::from_name("AAA");
    }

}

//end of unit tests