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}