rust8/chip8/cpu/
execute.rs1use thiserror::Error;
2use crate::Chip8;
3use crate::chip8::Blocked;
4use crate::constants::{FONT_SPRITE_MEMORY_LOCATION, FONT_SPRITE_SIZE, INSTRUCTION_SIZE};
5use crate::data_register::DataRegister;
6use crate::graphic::{PixelView, XorPixelErased};
7use crate::instruction::Instruction;
8use crate::keyboard::{Key, KeyState};
9
10
11#[derive(Error, Debug)]
12pub enum InstructionExecutionError {
13 #[error("invalid return, no address on stack to jump back to")]
14 InvalidReturn,
15 #[error("invalid memory access, attempted to access {0:#04x}")]
16 InvalidMemoryAccess(usize),
17 #[error("invalid key {0:#01x} specified ")]
18 InvalidKey(u8),
19}
20
21impl Chip8 {
22 pub fn execute_instruction(
23 &mut self,
24 instruction: Instruction,
25 ) -> Result<(), InstructionExecutionError> {
26 match instruction {
27 Instruction::ExecuteMachineLanguageSubroutine { .. } => {
28 Ok(())
30 }
31 Instruction::ClearScreen => {
32 self.screen.clear();
33
34 Ok(())
35 }
36 Instruction::ReturnFromSubroutine => {
37 self.program_counter = self
39 .stack
40 .pop()
41 .ok_or(InstructionExecutionError::InvalidReturn)?;
42 self.in_jump = true;
43
44 Ok(())
45 }
46 Instruction::JumpToAddress { address } => {
47 self.program_counter = address;
48 self.in_jump = true;
49
50 Ok(())
51 }
52
53 Instruction::ExecuteSubroutine { address } => {
54 self.stack.push(self.program_counter + INSTRUCTION_SIZE);
56 self.program_counter = address;
57 self.in_jump = true;
58
59 Ok(())
60 }
61
62 Instruction::SkipIfVxEqualsNum { vx, num } => {
63 if self.data_registers[vx] == num {
64 self.program_counter += INSTRUCTION_SIZE;
65 }
66
67 Ok(())
68 }
69
70 Instruction::SkipIfVxNotEqualNum { vx, num } => {
71 if self.data_registers[vx] != num {
72 self.program_counter += INSTRUCTION_SIZE;
73 }
74
75 Ok(())
76 }
77
78 Instruction::SkipIfVxEqualsVy { vx, vy } => {
79 if self.data_registers[vx] == self.data_registers[vy] {
80 self.program_counter += INSTRUCTION_SIZE;
81 }
82
83 Ok(())
84 }
85
86 Instruction::StoreNumInVx { vx, num } => {
87 self.data_registers[vx] = num;
88
89 Ok(())
90 }
91
92 Instruction::AddNumToVx { vx, num } => {
93 self.data_registers[vx] += num;
94
95 Ok(())
96 }
97
98 Instruction::StoreVyInVx { vx, vy } => {
99 self.data_registers[vx] = self.data_registers[vy];
100
101 Ok(())
102 }
103
104 Instruction::SetVxToVxOrVy { vx, vy } => {
105 self.data_registers[vx] = self.data_registers[vx] | self.data_registers[vy];
106
107 Ok(())
108 }
109
110 Instruction::SetVxToVxAndVy { vx, vy } => {
111 self.data_registers[vx] = self.data_registers[vx] & self.data_registers[vy];
112
113 Ok(())
114 }
115
116 Instruction::SetVxToVxXorVy { vx, vy } => {
117 self.data_registers[vx] = self.data_registers[vx] ^ self.data_registers[vy];
118
119 Ok(())
120 }
121
122 Instruction::AddVyToVx { vx, vy } => {
123 let (value, overflow) =
125 self.data_registers[vx].overflowing_add(self.data_registers[vy]);
126
127 self.data_registers[vx] = value;
128
129 self.data_registers[DataRegister::VF] = match overflow {
130 true => 1, false => 0, };
133
134 Ok(())
135 }
136
137 Instruction::SubtractVyFromVx { vx, vy } => {
138 let (value, overflow) =
139 self.data_registers[vx].overflowing_sub(self.data_registers[vy]);
140 self.data_registers[vx] = value;
141 self.data_registers[DataRegister::VF] = match overflow {
142 true => 0, false => 1, };
145
146 Ok(())
147 }
148
149 Instruction::ShiftVyRightStoreInVx { vx, vy } => {
150 self.data_registers[DataRegister::VF] = self.data_registers[vy] & 1;
152
153 self.data_registers[vx] = self.data_registers[vy] >> 1;
155
156 Ok(())
157 }
158
159 Instruction::SetVxToVyMinusVx { vx, vy } => {
160 let (value, overflow) =
161 self.data_registers[vy].overflowing_sub(self.data_registers[vx]);
162 self.data_registers[vx] = value;
163
164 self.data_registers[DataRegister::VF] = match overflow {
165 true => 0, false => 1, };
168
169 Ok(())
170 }
171
172 Instruction::ShiftVyLeftStoreInVx { vx, vy } => {
173 self.data_registers[DataRegister::VF] = self.data_registers[vy] >> 7;
175
176 self.data_registers[vx] = self.data_registers[vy] << 1;
178
179 Ok(())
180 }
181
182 Instruction::SkipIfVxNotEqualVy { vx, vy } => {
183 if self.data_registers[vx] != self.data_registers[vy] {
185 self.program_counter += INSTRUCTION_SIZE;
186 }
187
188 Ok(())
189 }
190
191 Instruction::StoreAddressInAddressRegister { address } => {
192 self.address_register = address as usize;
194
195 Ok(())
196 }
197
198 Instruction::JumpToAddressPlusV0 { address } => {
199 self.program_counter = address + self.data_registers[DataRegister::V0] as usize;
201 self.in_jump = true;
202
203 Ok(())
204 }
205
206 Instruction::SetVxToRandomWithMask { vx, mask } => {
207 self.data_registers[vx] = rand::random::<u8>() & mask;
208
209 Ok(())
210 }
211
212 Instruction::DrawSpriteAtVxVy { vx, vy, byte_count } => {
213 let sprite = self
217 .memory
218 .read_sprite(self.address_register, byte_count as usize)
219 .ok_or_else(|| {
220 InstructionExecutionError::InvalidMemoryAccess(
221 self.address_register + byte_count as usize,
222 )
223 })?;
224
225 let sprite_x_pos = self.data_registers[vx] as usize;
226 let sprite_y_pos = self.data_registers[vy] as usize;
227
228 let mut pixel_erased = XorPixelErased::No;
229
230 for y in 0..sprite.height() {
231 for x in 0..sprite.width() {
232 let current_pixel_erased = self.screen.xor_pixel_wrapped_position(
233 sprite_x_pos + x,
234 sprite_y_pos + y,
235 sprite.get_pixel_unchecked(x, y),
236 );
237
238 if let XorPixelErased::Yes = current_pixel_erased {
239 pixel_erased = XorPixelErased::Yes;
240 }
241 }
242 }
243
244 self.data_registers[DataRegister::VF] = match pixel_erased {
245 XorPixelErased::Yes => 1, XorPixelErased::No => 0, };
248
249 Ok(())
250 }
251
252 Instruction::SkipIfKeyInVxPressed { vx } => {
253 if let KeyState::Pressed =
255 self.keyboard
256 .get_key_state(Key::try_from(self.data_registers[vx]).map_err(|_| {
257 InstructionExecutionError::InvalidKey(self.data_registers[vx])
258 })?)
259 {
260 self.program_counter += INSTRUCTION_SIZE;
261 }
262
263 Ok(())
264 }
265
266 Instruction::SkipIfKeyInVxNotPressed { vx } => {
267 if let KeyState::Released =
269 self.keyboard
270 .get_key_state(Key::try_from(self.data_registers[vx]).map_err(|_| {
271 InstructionExecutionError::InvalidKey(self.data_registers[vx])
272 })?)
273 {
274 self.program_counter += INSTRUCTION_SIZE;
275 }
276
277 Ok(())
278 }
279
280 Instruction::StoreDelayTimerInVx { vx } => {
281 self.data_registers[vx] = self.delay_timer;
283
284 Ok(())
285 }
286
287 Instruction::WaitForKeypressStoreInVx { vx } => {
288 self.blocked = Blocked::WaitingOnKeyUp(vx);
289
290 Ok(())
291 },
292 Instruction::SetDelayTimerToVx { vx } => {
293 self.delay_timer = self.data_registers[vx];
294
295 Ok(())
296 }
297
298 Instruction::SetSoundTimerToVx { vx } => {
299 self.sound_timer = self.data_registers[vx];
301
302 Ok(())
303 }
304
305 Instruction::AddVxToAddressRegister { vx } => {
306 self.address_register += self.data_registers[vx] as usize;
307
308 Ok(())
309 }
310
311 Instruction::SetAddressRegisterToSpriteAddressOfSpriteInVx { vx } => {
312 self.address_register = FONT_SPRITE_MEMORY_LOCATION
314 + (self.data_registers[vx] as usize * FONT_SPRITE_SIZE);
315
316 Ok(())
317 }
318
319 Instruction::StoreBCDOfVx { vx } => {
320 let num = self.data_registers[vx];
322
323 if self.address_register + 2 >= self.memory.raw_data.len() {
324 return Err(InstructionExecutionError::InvalidMemoryAccess(
325 self.address_register + 2,
326 ));
327 }
328
329 self.memory.raw_data[self.address_register] = num / 100;
331
332 self.memory.raw_data[self.address_register + 1] = (num % 100) / 10;
334
335 self.memory.raw_data[self.address_register + 2] = num % 10;
337
338 Ok(())
339 }
340
341 Instruction::StoreRegistersInMemory { vx } => {
342 for register_num in 0..=vx.into() {
344 let memory_address = self.address_register + register_num as usize;
345 let memory_ref =
346 self.memory
347 .raw_data
348 .get_mut(memory_address)
349 .ok_or_else(|| {
350 InstructionExecutionError::InvalidMemoryAccess(memory_address)
351 })?;
352
353 *memory_ref = self.data_registers[register_num.try_into().unwrap()];
354 }
355
356 Ok(())
357 }
358
359 Instruction::FillRegistersFromMemory { vx } => {
360 for register_num in 0..=vx.into() {
363 let memory_address = self.address_register + register_num as usize;
364
365 self.data_registers[register_num.try_into().unwrap()] =
366 *self.memory.raw_data.get(memory_address).ok_or_else(|| {
367 InstructionExecutionError::InvalidMemoryAccess(memory_address)
368 })?
369 }
370
371 Ok(())
372 }
373 }
374 }
375}