rust8/chip8/cpu/
execute.rs

1use 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                // This instruction is only used on the old computers on which Chip-8 was originally implemented. It is ignored by modern interpreters.
29                Ok(())
30            }
31            Instruction::ClearScreen => {
32                self.screen.clear();
33
34                Ok(())
35            }
36            Instruction::ReturnFromSubroutine => {
37                //The interpreter sets the program counter to the address at the top of the stack, then subtracts 1 from the stack pointer.
38                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                // The interpreter increments the stack pointer, then puts the current PC on the top of the stack. The PC is then set to nnn.
55                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                // Add the value of register vy to register vx
124                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,  // Set REG_F to 1 if a carry occurs
131                    false => 0, // Set REG_F to 0 if a carry does not occur
132                };
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,  // Set REG_F to 0 if a borrow occurs
143                    false => 1, // Set REG_F to 1 if a borrow does not occur
144                };
145
146                Ok(())
147            }
148
149            Instruction::ShiftVyRightStoreInVx { vx, vy } => {
150                // Set register REG_F to the least significant bit prior to the shift
151                self.data_registers[DataRegister::VF] = self.data_registers[vy] & 1;
152
153                // Store the value of register vy shifted right one bit in register vx
154                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,  // Set REG_F to 0 if a borrow occurs
166                    false => 1, // Set REG_F to 1 if a borrow does not occur
167                };
168
169                Ok(())
170            }
171
172            Instruction::ShiftVyLeftStoreInVx { vx, vy } => {
173                // Set register REG_F to the most significant bit prior to the shift
174                self.data_registers[DataRegister::VF] = self.data_registers[vy] >> 7;
175
176                // Store the value of register vy shifted left one bit in register vx
177                self.data_registers[vx] = self.data_registers[vy] << 1;
178
179                Ok(())
180            }
181
182            Instruction::SkipIfVxNotEqualVy { vx, vy } => {
183                // Skip the following instruction if the value of register VX is not equal to the value of register VY
184                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                // Store memory address NNN in register Address Register
193                self.address_register = address as usize;
194
195                Ok(())
196            }
197
198            Instruction::JumpToAddressPlusV0 { address } => {
199                // Jump to address NNN + V0
200                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                // The interpreter reads n bytes from memory, starting at the address stored in I.
214                // These bytes are then displayed as sprites on screen at coordinates (Vx, Vy).
215
216                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, // If this causes any pixels to be erased, VF is set to 1
246                    XorPixelErased::No => 0,  // Otherwise it is set to 0
247                };
248
249                Ok(())
250            }
251
252            Instruction::SkipIfKeyInVxPressed { vx } => {
253                // Skip next instruction if key with the value of Vx is pressed.
254                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                // Skip next instruction if key with the value of Vx is not pressed.
268                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                // Store the current value of the delay timer in register VX
282                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                // Set the sound timer to the value of register VX
300                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                // The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx.
313                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                // Store the binary-coded decimal equivalent of the value stored in register VX at addresses I, I + 1, and I + 2
321                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                // Hundreds digit in memory at location in I
330                self.memory.raw_data[self.address_register] = num / 100;
331
332                // Tens digit at location I+1
333                self.memory.raw_data[self.address_register + 1] = (num % 100) / 10;
334
335                // Ones digit at location I+2
336                self.memory.raw_data[self.address_register + 2] = num % 10;
337
338                Ok(())
339            }
340
341            Instruction::StoreRegistersInMemory { vx } => {
342                // Store the values of registers V0 to VX inclusive in memory starting at address I
343                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                // Fill registers V0 to VX inclusive with the values stored in memory starting at address I
361                // Store the values of registers V0 to VX inclusive in memory starting at address I
362                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}