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