ch8_isa/data/
add_data.rs

1/*
2 * add_data.rs
3 * Defines a struct that holds data for the ADD instruction
4 * Created on 12/4/2019
5 * Created by Andrew Davis
6 *
7 * Copyright (C) 2019  Andrew Davis
8 *
9 * This program is free software: you can redistribute it and/or modify   
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 * 
19 * You should have received a copy of the GNU General Public License
20 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23//usage statements
24use super::Register;
25use super::super::codegen::CodeGen;
26
27/// Contextual data for the `ADD` instruction
28pub struct AddData {
29    /// The first addend (also the destination register)
30    first: Register,
31
32    /// The second register addend
33    second_reg: Option<Register>,
34
35    /// The second constant addend
36    second_cst: Option<u16>
37}
38
39//implementation
40impl AddData {
41    /// Constructs a new `AddData` instance with a register addend
42    ///
43    /// # Arguments
44    ///
45    /// * `new_first` - The first addend (also the destination)
46    /// * `new_second` - The second addend
47    ///
48    /// # Panics
49    ///
50    /// This method will panic if `new_second` refers to the `I` register.
51    ///
52    /// # Returns
53    ///
54    /// A new `AddData` instance with the given properties
55    pub fn with_register(new_first: Register, new_second: Register)
56        -> AddData {
57        //validate the second register
58        if new_second == Register::I {
59            panic!("Cannot add the index register to a register");
60        }
61
62        //and call the new method
63        return AddData::new(new_first, Some(new_second), None);
64    }
65
66    /// Constructs a new `AddData` instance with a constant addend
67    ///
68    /// # Arguments
69    ///
70    /// * `new_first` - The first addend (also the destination)
71    /// * `new_second` - The second addend
72    ///
73    /// # Panics
74    ///
75    /// This method will panic if `new_first` refers to the `I` register.
76    ///
77    /// # Returns
78    ///
79    /// A new `AddData` instance with the given properties
80    pub fn with_constant(new_first: Register, new_second: u16)
81        -> AddData {
82        //check the register
83        if new_first == Register::I {
84            panic!("Cannot add constant to index register");
85        }
86
87        //mask the second addend
88        let new_second = new_second & 0x00FF;
89
90        //and call the new method
91        return AddData::new(new_first, None, Some(new_second));
92    }
93
94    /// Constructs a new `AddData` instance
95    ///
96    /// # Arguments
97    ///
98    /// * `new_first` - The first addend (also the destination)
99    /// * `new_second_reg` - The second register addend
100    /// * `new_second_cst` - The second constant addend
101    ///
102    /// # Returns
103    ///
104    /// A new `AddData` instance with the given properties
105    fn new(new_first: Register, new_second_reg: Option<Register>,
106           new_second_cst: Option<u16>) -> AddData {
107        return AddData {
108            first: new_first,
109            second_reg: new_second_reg,
110            second_cst: new_second_cst 
111        };
112    }
113}
114
115//CodeGen implementation
116impl CodeGen for AddData {
117    /// Generates an opcode for the `ADD` instruction
118    /// 
119    /// # Returns
120    ///
121    /// An opcode for the `ADD` instruction with the given data
122    fn gen_opcode(&self) -> u16 {
123        //match the first addend
124        match self.first {
125            Register::I => { //index register
126                //handle different second addends
127                match self.second_reg.clone() {
128                    Some(reg) => {  
129                        let mut code = 0xF01E;
130                        code |= (reg.to_id() as u16) << 8;
131                        return code;
132                    },
133                    None => { //should not be reached
134                        panic!("Invalid RHS of add statement");
135                    }
136                };
137            },
138            _ => { //normal register
139                //handle different second addends
140                match self.second_reg.clone() {
141                    Some(reg) => { //VX += VY
142                        let mut code = 0x8004;
143                        code |= (self.first.to_id() as u16) << 8;
144                        code |= (reg.to_id() as u16) << 4;
145                        return code;
146                    },
147                    None => { //VX += NN
148                        match self.second_cst {
149                            Some(cst) => {
150                                let mut code = 0x7000;
151                                code |= (self.first.to_id() as u16) << 8;
152                                code |= cst;
153                                return code;
154                            },
155                            None => { //should never be reached
156                                panic!("Invalid second addend");
157                            }
158                        };
159                    }
160                };
161            }
162        };
163    }
164}
165
166//unit tests
167#[cfg(test)]
168mod tests {
169    //import the AddData struct
170    use super::*;
171
172    //this test checks that constants are masked
173    #[test]
174    fn test_constants_are_masked() {
175        let d = AddData::with_constant(Register::V0, 0xFFFB);
176        assert_eq!(d.second_cst.unwrap(), 0xFB);
177    }
178
179    //this test ensures that the index register cannot be added
180    //to a normal register
181    #[test]
182    #[should_panic]
183    fn test_cannot_add_index_to_normal() {
184        let _d = AddData::with_register(Register::V0, Register::I);
185    }
186
187    //this test ensures that a constant cannot be directly added
188    //to the index register
189    #[test]
190    #[should_panic]
191    fn test_cannot_add_constant_to_index() {
192        let _d = AddData::with_constant(Register::I, 0x0FFF);
193    }
194
195    //this test checks opcode generation
196    #[test]
197    fn test_opcode_gen() {
198        let d1 = AddData::with_register(Register::V1, Register::V2);
199        assert_eq!(d1.gen_opcode(), 0x8124);
200        let d2 = AddData::with_register(Register::I, Register::V1);
201        assert_eq!(d2.gen_opcode(), 0xF11E);
202        let d3 = AddData::with_constant(Register::V1, 0xFC);
203        assert_eq!(d3.gen_opcode(), 0x71FC);
204    }
205}
206
207//end of file