ch8_isa/data/mov_data.rs
1/*
2 * mov_data.rs
3 * Defines a struct that holds data for the MOV 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 `MOV` instruction
28pub struct MovData {
29 /// The destination register
30 dest: Register,
31
32 /// The source register
33 src_reg: Option<Register>,
34
35 /// The source constant
36 src_cst: Option<u16>
37}
38
39//implementation
40impl MovData {
41 /// Constructs a new `MovData` instance
42 /// with a source register
43 ///
44 /// # Arguments
45 ///
46 /// * `new_dest` - The destination register
47 /// * `new_src` - The source register
48 ///
49 /// # Panics
50 ///
51 /// This method will panic if `new_dest` or `new_src`
52 /// refers to the `I` register
53 ///
54 /// # Returns
55 ///
56 /// A new `MovData` instance with the given properties
57 pub fn with_register(new_dest: Register, new_src: Register)
58 -> MovData {
59 //validate the registers
60 if new_dest == Register::I {
61 panic!("Index register must be assigned from a constant");
62 }
63 if new_src == Register::I {
64 panic!("Index register cannot be assigned from");
65 }
66
67 //and call the new method
68 return MovData::new(new_dest, Some(new_src), None);
69 }
70
71 /// Constructs a new `MovData` instance with a source constant
72 ///
73 /// # Arguments
74 ///
75 /// * `new_dest` - The destination register
76 /// * `new_src` - The source constant
77 ///
78 /// # Returns
79 ///
80 /// A new `MovData` instance with the given properties
81 pub fn with_constant(new_dest: Register, new_src: u16) -> MovData {
82 //mask the source value
83 let new_src = match new_dest {
84 Register::I => new_src & 0x0FFF,
85 _ => new_src & 0x00FF
86 };
87
88 //call the new method
89 return MovData::new(new_dest, None, Some(new_src));
90 }
91
92 /// Constructs a new `MovData` instance
93 ///
94 /// # Arguments
95 ///
96 /// * `new_dest` - The destination register
97 /// * `new_src_reg` - The source register
98 /// * `new_src_cst` - The source constant
99 ///
100 /// # Returns
101 ///
102 /// A new `MovData` instance with the given properties
103 fn new(new_dest: Register, new_src_reg: Option<Register>,
104 new_src_cst: Option<u16>) -> MovData {
105 return MovData {
106 dest: new_dest,
107 src_reg: new_src_reg,
108 src_cst: new_src_cst
109 };
110 }
111}
112
113//CodeGen implementation
114impl CodeGen for MovData {
115 /// Generates an opcode from the data
116 ///
117 /// # Returns
118 ///
119 /// An opcode generated from the data
120 fn gen_opcode(&self) -> u16 {
121 //handle destination objects
122 match self.dest {
123 Register::I => {
124 match self.src_cst.clone() {
125 Some(cst) => {
126 let mut code = 0xA000;
127 code |= cst;
128 return code;
129 },
130 None => {
131 panic!("Invalid source value");
132 }
133 };
134 },
135 _ => { //normal register
136 //handle different source values
137 match self.src_reg.clone() {
138 Some(reg) => { //source is a register
139 let mut code = 0x8000;
140 code |= (self.dest.to_id() as u16) << 8;
141 code |= (reg.to_id() as u16) << 4;
142 return code;
143 },
144 None => { //source is a constant
145 match self.src_cst {
146 Some(cst) => {
147 let mut code = 0x6000;
148 code |= (self.dest.to_id() as u16) << 8;
149 code |= cst;
150 return code;
151 },
152 None => { //should not be reached
153 panic!("Invalid RHS of MOV instruction");
154 }
155 };
156 }
157 };
158 }
159 };
160 }
161}
162
163//tests
164#[cfg(test)]
165mod tests {
166 //import the MovData structure
167 use super::*;
168
169 //this test checks that constant values are masked
170 #[test]
171 fn test_constants_are_masked() {
172 let d1 = MovData::with_constant(Register::I, 0xFFFF);
173 assert_eq!(d1.src_cst.unwrap(), 0x0FFF);
174 let d2 = MovData::with_constant(Register::V0, 0xFFFF);
175 assert_eq!(d2.src_cst.unwrap(), 0x00FF);
176 }
177
178 //this test checks that the I register cannot be assigned from
179 #[test]
180 #[should_panic]
181 fn test_cannot_assign_from_index() {
182 let _b1 = MovData::with_register(Register::V0, Register::I);
183 }
184
185 //this test checks that the I register cannot have a register
186 //assigned to it
187 #[test]
188 #[should_panic]
189 fn test_cannot_assign_register_to_index() {
190 let _b1 = MovData::with_register(Register::I, Register::V0);
191 }
192
193 //this test checks generating opcodes
194 #[test]
195 fn test_opcode_gen() {
196 let m1 = MovData::with_register(Register::V1, Register::V2);
197 assert_eq!(m1.gen_opcode(), 0x8120);
198 let m2 = MovData::with_constant(Register::V1, 0xFC);
199 assert_eq!(m2.gen_opcode(), 0x61FC);
200 let m3 = MovData::with_constant(Register::I, 0x0FC1);
201 assert_eq!(m3.gen_opcode(), 0xAFC1);
202 }
203}
204
205//end of file