ch8_isa/data/skip_data.rs
1/*
2 * skip_data.rs
3 * Defines a struct that holds data for the SKIP instruction
4 * Created on 12/2/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::SkipType;
26use super::super::codegen::CodeGen;
27
28/// Contextual data for the `SKIP` instruction
29pub struct SkipData {
30 /// The LHS side of the comparison
31 vx: Register,
32
33 /// The RHS of the comparison, if using a register
34 vy: Option<Register>,
35
36 /// The RHS of the comparison, if using a constant
37 nn: Option<u8>,
38
39 /// The skip type of the instruction
40 skip_type: SkipType
41}
42
43//implementation
44impl SkipData {
45 /// Constructs a new `SkipData` instance with a
46 /// register-to-register comparison
47 ///
48 /// # Arguments
49 ///
50 /// * `new_vx` - The LHS of the comparison
51 /// * `new_vy` - The RHS of the comparison
52 /// * `new_type` - The skip type of the comparison
53 ///
54 /// # Returns
55 ///
56 /// A new `SkipData` instance with the given properties
57 ///
58 /// # Panics
59 ///
60 /// This method will panic if `new_type` is a key-related variant
61 /// or if either register is the `I` register
62 pub fn with_register(new_vx: Register, new_vy: Register,
63 new_type: SkipType) -> SkipData {
64 //verify the type
65 if (new_type == SkipType::KeyUp) ||
66 (new_type == SkipType::KeyDown) {
67 panic!("Bad skip type: {:?}", new_type);
68 }
69
70 //verify the register
71 if (new_vx == Register::I) || (new_vy == Register::I) {
72 panic!("Index register is disallowed in skips");
73 }
74
75 //and return a new instance
76 return SkipData::new(new_vx, Some(new_vy), None, new_type);
77 }
78
79 /// Constructs a new `SkipData` instance with a
80 /// register-to-constant comparison
81 ///
82 /// # Arguments
83 ///
84 /// * `new_vx` - The LHS of the comparison
85 /// * `new_nn` - The RHS of the comparison
86 /// * `new_type` - The skip type of the comparison
87 ///
88 /// # Returns
89 ///
90 /// A new `SkipData` instance with the given properties
91 ///
92 /// # Panics
93 ///
94 /// This method will panic if `new_type` is a key-related variant
95 /// or if `new_vx` is the `I` register
96 pub fn with_constant(new_vx: Register, new_nn: u8,
97 new_type: SkipType) -> SkipData {
98 //verify the type
99 if (new_type == SkipType::KeyUp) ||
100 (new_type == SkipType::KeyDown) {
101 panic!("Bad skip type: {:?}", new_type);
102 }
103
104 //verify the register
105 if new_vx == Register::I {
106 panic!("Index register is disallowed in skips");
107 }
108
109 //and return a new instance
110 return SkipData::new(new_vx, None, Some(new_nn), new_type);
111 }
112
113 /// Constructs a new `SkipData` instance with a
114 /// key comparison
115 ///
116 /// # Arguments
117 ///
118 /// * `new_vx` - The LHS of the comparison
119 /// * `new_type` - The skip type of the comparison
120 ///
121 /// # Returns
122 ///
123 /// A new `SkipData` instance with the given properties
124 ///
125 /// # Panics
126 ///
127 /// This method will panic if `new_type` is not a key-related variant
128 /// or if `new_vx` is the `I` register
129 pub fn with_key(new_vx: Register,
130 new_type: SkipType) -> SkipData {
131 //verify the type
132 if (new_type != SkipType::KeyUp) &&
133 (new_type != SkipType::KeyDown) {
134 panic!("Bad skip type: {:?}", new_type);
135 }
136
137 //verify the register
138 if new_vx == Register::I {
139 panic!("Index register is disallowed in skips");
140 }
141
142 //and return a new instance
143 return SkipData::new(new_vx, None, None, new_type);
144 }
145
146
147 /// Constructs a new `SkipData` instance
148 ///
149 /// # Arguments
150 ///
151 /// * `new_vx` - The LHS of the comparison
152 /// * `new_vy` - The RHS register
153 /// * `new_nn` - The RHS constant
154 /// * `new_type` - The skip type
155 ///
156 /// # Returns
157 ///
158 /// A new `SkipData` instance with the given properties
159 fn new(new_vx: Register, new_vy: Option<Register>,
160 new_nn: Option<u8>, new_type: SkipType) -> SkipData {
161 return SkipData {
162 vx: new_vx,
163 vy: new_vy,
164 nn: new_nn,
165 skip_type: new_type
166 };
167 }
168}
169
170//CodeGen implementation
171impl CodeGen for SkipData {
172 /// Generates the opcode for the data
173 ///
174 /// # Returns
175 ///
176 /// The numeric opcode for the data
177 fn gen_opcode(&self) -> u16 {
178 //match the skip type
179 match self.skip_type {
180 SkipType::Equals => {
181 //match whether the register field is not None
182 match self.vy.clone() {
183 Some(y) => { //register comparison
184 let mut code = 0x5000;
185 code |= (self.vx.to_id() as u16) << 8;
186 code |= (y.to_id() as u16) << 4;
187 return code;
188 },
189 None => { //constant comparison
190 match self.nn {
191 Some(n) => {
192 let mut code = 0x3000;
193 code |= (self.vx.to_id() as u16) << 8;
194 code |= n as u16;
195 return code;
196 },
197 None => { //should never be reached
198 panic!("Invalid RHS of comparison");
199 }
200 }
201 }
202 }
203 },
204 SkipType::NotEquals => {
205 //match whether the register field is not None
206 match self.vy.clone() {
207 Some(y) => { //register comparison
208 let mut code = 0x9000;
209 code |= (self.vx.to_id() as u16) << 8;
210 code |= (y.to_id() as u16) << 4;
211 return code;
212 },
213 None => { //constant comparison
214 match self.nn {
215 Some(n) => {
216 let mut code = 0x4000;
217 code |= (self.vx.to_id() as u16) << 8;
218 code |= n as u16;
219 return code;
220 },
221 None => { //should never be reached
222 panic!("Invalid RHS of comparison");
223 }
224 }
225 }
226 }
227 },
228 SkipType::KeyDown => {
229 let mut code = 0xE09E;
230 code |= (self.vx.to_id() as u16) << 8;
231 return code;
232 },
233 SkipType::KeyUp => {
234 let mut code = 0xE0A1;
235 code |= (self.vx.to_id() as u16) << 8;
236 return code;
237 }
238 };
239 }
240}
241
242//unit tests
243#[cfg(test)]
244mod tests {
245 //import the SkipData struct
246 use super::*;
247
248 //this test checks that bad skip types panic
249 #[test]
250 #[should_panic]
251 fn test_bad_skiptype_panic() {
252 let _b1 = SkipData::with_register(Register::V0, Register::V1,
253 SkipType::KeyDown);
254 let _b2 = SkipData::with_register(Register::V0, Register::V1,
255 SkipType::KeyUp);
256 let _b3 = SkipData::with_constant(Register::V0, 0xFF,
257 SkipType::KeyDown);
258 let _b4 = SkipData::with_constant(Register::V0, 0xFF,
259 SkipType::KeyUp);
260 let _b5 = SkipData::with_key(Register::V0, SkipType::Equals);
261 let _b6 = SkipData::with_key(Register::V0, SkipType::NotEquals);
262 }
263
264 //this test checks that using the index register panics
265 #[test]
266 #[should_panic]
267 fn test_index_register_panic() {
268 let _b1 = SkipData::with_register(Register::I, Register::V0,
269 SkipType::Equals);
270 let _b2 = SkipData::with_register(Register::V0, Register::I,
271 SkipType::Equals);
272 let _b3 = SkipData::with_constant(Register::I, 0xFF,
273 SkipType::Equals);
274 let _b4 = SkipData::with_key(Register::I, SkipType::KeyDown);
275 }
276
277 //this test checks code generation
278 #[test]
279 fn test_opcode_gen() {
280 let sk1 = SkipData::with_constant(Register::V1, 0xFF,
281 SkipType::Equals);
282 assert_eq!(sk1.gen_opcode(), 0x31FF);
283 let sk2 = SkipData::with_constant(Register::V1, 0xFF,
284 SkipType::NotEquals);
285 assert_eq!(sk2.gen_opcode(), 0x41FF);
286 let sk3 = SkipData::with_register(Register::V1, Register::V2,
287 SkipType::Equals);
288 assert_eq!(sk3.gen_opcode(), 0x5120);
289 let sk4 = SkipData::with_register(Register::V1, Register::V2,
290 SkipType::NotEquals);
291 assert_eq!(sk4.gen_opcode(), 0x9120);
292 let sk5 = SkipData::with_key(Register::V1, SkipType::KeyDown);
293 assert_eq!(sk5.gen_opcode(), 0xE19E);
294 let sk6 = SkipData::with_key(Register::V1, SkipType::KeyUp);
295 assert_eq!(sk6.gen_opcode(), 0xE1A1);
296 }
297}