Skip to main content

chip_eight/emulator/
emulator_core.rs

1use std::{
2    sync::{
3        Arc,
4        atomic::{AtomicU16, Ordering},
5    },
6    time::{Duration, SystemTime, UNIX_EPOCH},
7};
8// NOTE: The test roms were found at https://github.com/Timendus/chip8-test-suite
9
10use crate::{
11    ApplicationError, BASE_SCREEN_HEIGHT, BASE_SCREEN_WIDTH, Draw, ReadInputState,
12    emulator::{
13        fonts::{BIG_FONT, BIG_FONT_ADDR, FONT, FONT_ADDR},
14        instructions::{Instruction, KeyStateToCheck},
15        logical_operator::{Direction, LogicalOperator},
16        quirks::{QuirksFields, QuirksMode},
17        sub_commands::{FontVariant, SubCommand},
18    },
19    twister_rand::MarsenneTwister32,
20    utils::u8_to_arr,
21};
22
23/// This is the main emulator interface, used to configure and run the interpreter.
24#[derive(Debug)]
25pub struct Emulator<T: Draw, P: ReadInputState> {
26    memory: [u8; 0x1000],
27    stack: Vec<usize>,
28    variable_registers: [u8; 16],
29    screen_buffer: Vec<u8>,
30    index_register: usize,
31    program_counter: usize,
32    delay_timer: Arc<AtomicU16>,
33    sound_timer: Arc<AtomicU16>,
34    drawer: T,
35    input_provider: P,
36    max_draw_delay: Duration,
37    last_draw: SystemTime,
38    quirks: QuirksFields,
39    screen_width: usize,
40    screen_height: usize,
41    rng: MarsenneTwister32,
42}
43
44impl<T: Draw, P: ReadInputState> Emulator<T, P> {
45    /// Init could I guess also have been named 'new()', but it just returns an instance of the
46    /// interpreter/emulator.
47    pub fn init(program: Vec<u8>, drawer: T, input_provider: P) -> Result<Self, ApplicationError> {
48        let seed = SystemTime::now()
49            .duration_since(UNIX_EPOCH)
50            .expect("Now is always after UNIX Epoch");
51
52        let mut emulator = Self {
53            memory: [0; 0x1000],
54            stack: vec![],
55            variable_registers: [0; 16],
56            screen_buffer: vec![0; BASE_SCREEN_WIDTH * BASE_SCREEN_HEIGHT],
57            index_register: 0,
58            program_counter: 0x200,
59            delay_timer: Arc::new(AtomicU16::new(0)),
60            sound_timer: Arc::new(AtomicU16::new(0)),
61            drawer,
62            input_provider,
63            max_draw_delay: Duration::from_millis(7),
64            last_draw: SystemTime::now(),
65            quirks: QuirksMode::Chip8.into(),
66            screen_width: 64,
67            screen_height: 32,
68            rng: MarsenneTwister32::new((seed.as_micros() % u32::MAX as u128) as u32),
69        };
70
71        emulator.set_mem_block(&FONT, FONT_ADDR)?;
72        emulator.set_mem_block(&BIG_FONT, BIG_FONT_ADDR)?;
73        emulator.set_mem_block(&program, 0x200)?;
74
75        let delay_timer = emulator.delay_timer.clone();
76        let sound_timer = emulator.sound_timer.clone();
77        std::thread::spawn(move || {
78            loop {
79                let old_val = delay_timer.load(Ordering::Relaxed);
80                if old_val > 0 {
81                    delay_timer.store(old_val - 1, Ordering::Relaxed);
82                }
83
84                let old_val = sound_timer.load(Ordering::Relaxed);
85                if old_val > 0 {
86                    sound_timer.store(old_val - 1, Ordering::Relaxed);
87                }
88                std::thread::sleep(Duration::from_millis(6));
89            }
90        });
91
92        Ok(emulator)
93    }
94
95    /// Run the emulator/interpreter on the current thread in a busy loop.
96    /// The drawing speed is purely determined by the max draw delay
97    pub fn run_blocking(&mut self) {
98        loop {
99            let instruction = self.fetch();
100            self.execute(instruction);
101        }
102    }
103
104    /// Reset the emulator's state to its initial state.
105    pub fn reset(&mut self, program: Vec<u8>) -> Result<(), ApplicationError> {
106        self.memory = [0; 0x1000];
107        self.stack = vec![];
108        self.variable_registers = [0; 16];
109        self.screen_buffer = vec![0; BASE_SCREEN_WIDTH * BASE_SCREEN_HEIGHT];
110        self.index_register = 0;
111        self.program_counter = 0x200;
112        self.max_draw_delay = Duration::from_millis(7);
113        self.last_draw = SystemTime::now();
114        self.quirks = QuirksMode::Chip8.into();
115        self.screen_width = 64;
116        self.screen_height = 32;
117        self.set_mem_block(&program, 0x200)?;
118        Ok(())
119    }
120
121    /// Set the quirks mode to a predefined behaviour (I'm not sure how consistent this is)
122    pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) -> &mut Self {
123        self.quirks = quirks_mode.into();
124        self
125    }
126
127    /// Set only specific flags for quirks to be followed (I'm not sure how consistent this is)
128    pub fn with_custom_quirks(&mut self, quirks: QuirksFields) -> &mut Self {
129        self.quirks = quirks;
130        self
131    }
132
133    /// Set the amount of time the interpreter will wait before drawing to the screen. I.E. draw to
134    /// screen rate.
135    pub fn set_max_draw_delay(&mut self, rate: Duration) -> &mut Self {
136        self.max_draw_delay = rate;
137        self
138    }
139
140    fn fetch(&mut self) -> Instruction {
141        let tophalf = self.memory[self.program_counter];
142        let bothalf = self.memory[self.program_counter + 1];
143        self.program_counter = (self.program_counter + 2) % self.memory.len();
144        (((tophalf as u16) << 8) | bothalf as u16).into()
145    }
146
147    fn set_high_res(&mut self) {
148        self.screen_width = BASE_SCREEN_WIDTH << 1;
149        self.screen_height = BASE_SCREEN_HEIGHT << 1;
150    }
151
152    fn set_low_res(&mut self) {
153        self.screen_width = BASE_SCREEN_WIDTH;
154        self.screen_height = BASE_SCREEN_HEIGHT;
155    }
156
157    fn execute(&mut self, instruction: Instruction) {
158        match instruction {
159            Instruction::ClearScreen => {
160                self.clear_screen();
161            }
162            Instruction::Draw {
163                x_register,
164                y_register,
165                height,
166            } => {
167                self.draw(x_register, y_register, height);
168            }
169            Instruction::Jump(address) => self.program_counter = address as usize,
170            Instruction::SetIndexRegister(address) => self.index_register = address as usize,
171            Instruction::SetGeneralRegister { register, value } => {
172                self.variable_registers[register] = value
173            }
174            Instruction::AddToRegister { register, value } => {
175                let val: u16 = value as u16 + self.variable_registers[register] as u16;
176                self.variable_registers[register] = (val & 0xFF) as u8;
177            }
178            Instruction::SkipEqValueWithRegisterContents { register, value } => {
179                let vx_value = self.variable_registers[register];
180                if vx_value == value {
181                    self.program_counter += 2;
182                }
183            }
184            Instruction::SkipNotEqValueWithRegisterContents { register, value } => {
185                let vx_value = self.variable_registers[register];
186                if vx_value != value {
187                    self.program_counter += 2;
188                }
189            }
190            Instruction::SkipEqRegisters {
191                register_x,
192                register_y,
193            } => {
194                let vx_value = self.variable_registers[register_x];
195                let vy_value = self.variable_registers[register_y];
196                if vx_value == vy_value {
197                    self.program_counter += 2;
198                }
199            }
200            Instruction::SkipNotEqRegisters {
201                register_x,
202                register_y,
203            } => {
204                let vx_value = self.variable_registers[register_x];
205                let vy_value = self.variable_registers[register_y];
206                if vx_value != vy_value {
207                    self.program_counter += 2;
208                }
209            }
210            Instruction::LogicalOperator {
211                operator,
212                register_x,
213                register_y,
214            } => {
215                self.perform_logical_operator(register_x, register_y, operator);
216            }
217            Instruction::JumpWithOffset {
218                register_x,
219                address,
220            } => {
221                if self.quirks.jumping {
222                    self.program_counter = address + self.variable_registers[register_x] as usize;
223                } else {
224                    self.program_counter = address + self.variable_registers[0] as usize;
225                }
226            }
227            Instruction::Random {
228                register_x,
229                val_to_and,
230            } => {
231                let randint = (self.rng.generate() % 255) as u8;
232                self.variable_registers[register_x] = randint & val_to_and;
233            }
234            Instruction::Return => {
235                if let Some(val) = self.stack.pop() {
236                    self.program_counter = val;
237                };
238            }
239            Instruction::Call(memory_addr) => {
240                self.stack.push(self.program_counter);
241                self.program_counter = memory_addr;
242            }
243            Instruction::SubCommand { register, command } => {
244                self.perform_sub_command(command, register)
245            }
246            Instruction::SkipIfKey {
247                register,
248                state_to_check,
249            } => {
250                self.skip_if_key(state_to_check, register);
251            }
252            Instruction::SetHiRes => {
253                self.set_high_res();
254                self.screen_buffer
255                    .resize(self.screen_width * self.screen_height, 0);
256                self.wait_to_display();
257                self.drawer
258                    .draw_buffer(&self.screen_buffer, self.screen_width, self.screen_height);
259            }
260            Instruction::SetLoRes => {
261                self.set_low_res();
262                self.screen_buffer
263                    .resize(self.screen_width * self.screen_height, 0);
264                self.wait_to_display();
265                self.drawer
266                    .draw_buffer(&self.screen_buffer, self.screen_width, self.screen_height);
267            }
268            Instruction::ScrollDown { amount } => {
269                let px_to_shift = self.screen_width * amount as usize;
270                let mut new_buffer = vec![0; px_to_shift];
271                new_buffer.extend(&self.screen_buffer);
272                new_buffer.resize(self.screen_width * self.screen_height, 0);
273                self.screen_buffer = new_buffer;
274            }
275            Instruction::ScrollRight => {
276                let new_buffer: Vec<u8> = self
277                    .screen_buffer
278                    .chunks(self.screen_width)
279                    .flat_map(|x| {
280                        let mut row = vec![0, 0, 0, 0];
281                        row.extend(x);
282                        row.resize(self.screen_width, 0);
283                        row
284                    })
285                    .collect();
286                self.screen_buffer = new_buffer;
287            }
288            Instruction::ScrollLeft => {
289                let new_buffer: Vec<u8> = self
290                    .screen_buffer
291                    .chunks(self.screen_width)
292                    .flat_map(|x| {
293                        let mut row = x[4..].to_vec();
294                        row.resize(self.screen_width, 0);
295                        row
296                    })
297                    .collect();
298                self.screen_buffer = new_buffer;
299            }
300            Instruction::Unimplemented(val) => {
301                eprintln!("UNIMPLEMENTED: {}", val)
302            }
303        }
304    }
305
306    fn skip_if_key(&mut self, state_to_check: KeyStateToCheck, register: usize) {
307        if let Ok(keys) = self.input_provider.read_keys_state() {
308            let current_key_state = keys[(self.variable_registers[register] & 0xF) as usize];
309            match state_to_check {
310                KeyStateToCheck::IsPressed => {
311                    if current_key_state > 0 {
312                        self.program_counter += 2;
313                    }
314                }
315                KeyStateToCheck::NotPressed => {
316                    if current_key_state == 0 {
317                        self.program_counter += 2;
318                    }
319                }
320                KeyStateToCheck::Invalid => {}
321            }
322        };
323    }
324
325    fn perform_sub_command(&mut self, command: SubCommand, register: usize) {
326        match command {
327            SubCommand::ReadDelayTimer => {
328                self.variable_registers[register] = self.delay_timer.load(Ordering::Relaxed) as u8;
329            }
330            SubCommand::SetDelayTimer => {
331                self.delay_timer
332                    .store(self.variable_registers[register] as u16, Ordering::Relaxed);
333            }
334            SubCommand::SetSoundTimer => {
335                self.sound_timer
336                    .store(self.variable_registers[register] as u16, Ordering::Relaxed);
337            }
338            SubCommand::AddToIndexRegister => {
339                self.index_register += self.variable_registers[register] as usize;
340            }
341            SubCommand::GetFontCharacter(FontVariant::Small) => {
342                self.index_register =
343                    FONT_ADDR + (self.variable_registers[register] & 0xF) as usize * 5;
344            }
345            SubCommand::GetFontCharacter(FontVariant::Big) => {
346                self.index_register =
347                    BIG_FONT_ADDR + (self.variable_registers[register]) as usize * 10;
348            }
349            SubCommand::DecimalConversion => {
350                let val = self.variable_registers[register];
351                let val = u8_to_arr(val);
352
353                for (i, val) in val.iter().enumerate() {
354                    let wrapped = (self.index_register + i) % self.memory.len();
355                    if let Some(x) = self.memory.get_mut(wrapped) {
356                        *x = *val;
357                    };
358                }
359            }
360            SubCommand::LoadFrom => {
361                for (i, reg) in self.variable_registers[0..=register].iter_mut().enumerate() {
362                    let wrapped = (self.index_register + i) % self.memory.len();
363                    if let Some(x) = self.memory.get(wrapped) {
364                        *reg = *x;
365                    }
366                }
367
368                if self.quirks.memory {
369                    self.index_register += register + 1;
370                }
371            }
372            SubCommand::StoreTo => {
373                for (i, reg) in self.variable_registers[0..=register].iter().enumerate() {
374                    let wrapped = (self.index_register + i) % self.memory.len();
375                    if let Some(x) = self.memory.get_mut(wrapped) {
376                        *x = *reg;
377                    }
378                }
379                if self.quirks.memory {
380                    self.index_register += register + 1;
381                }
382            }
383            SubCommand::GetKey => {
384                let mut key_pressed = false;
385                if let Ok(keys) = self.input_provider.read_keys_state() {
386                    for (i, key) in keys.iter().enumerate() {
387                        if *key > 0 {
388                            self.variable_registers[register] = i as u8;
389                            key_pressed = true;
390                            break;
391                        }
392                    }
393                }
394                if !key_pressed {
395                    self.program_counter -= 2;
396                } else {
397                    self.input_provider.reset_keys_state();
398                };
399            }
400            SubCommand::Unimplemented(_value) => {}
401        }
402    }
403
404    fn perform_logical_operator(
405        &mut self,
406        register_x: usize,
407        register_y: usize,
408        operator: LogicalOperator,
409    ) {
410        match operator {
411            LogicalOperator::Set => {
412                self.variable_registers[register_x] = self.variable_registers[register_y];
413            }
414            LogicalOperator::BinaryOr => {
415                self.variable_registers[register_x] |= self.variable_registers[register_y];
416                if self.quirks.vf_reset {
417                    self.variable_registers[0xF] = 0;
418                }
419            }
420            LogicalOperator::BinaryAnd => {
421                self.variable_registers[register_x] &= self.variable_registers[register_y];
422                if self.quirks.vf_reset {
423                    self.variable_registers[0xF] = 0;
424                }
425            }
426            LogicalOperator::LogicalXor => {
427                self.variable_registers[register_x] ^= self.variable_registers[register_y];
428                if self.quirks.vf_reset {
429                    self.variable_registers[0xF] = 0;
430                }
431            }
432            LogicalOperator::AddAffectingCarry => {
433                let res = self.variable_registers[register_x] as u16
434                    + self.variable_registers[register_y] as u16;
435                self.variable_registers[register_x] = (res & 0xFF) as u8;
436                self.variable_registers[0xF] = if res > 255 { 1 } else { 0 };
437            }
438            LogicalOperator::Subtract => {
439                let res = self.variable_registers[register_x] as i16
440                    - self.variable_registers[register_y] as i16;
441                self.variable_registers[register_x] = (res & 0xFF) as u8;
442                self.variable_registers[0xF] = if res >= 0 { 1 } else { 0 };
443            }
444            LogicalOperator::SubtractReverse => {
445                let res = self.variable_registers[register_y] as i16
446                    - self.variable_registers[register_x] as i16;
447                self.variable_registers[register_x] = (res & 0xFF) as u8;
448                self.variable_registers[0xF] = if res >= 0 { 1 } else { 0 };
449            }
450            LogicalOperator::Shift(direction) => {
451                // NOTE: Though this quirk is called shifting, to be more in line with the test
452                // suite, this is inverted.
453                if !self.quirks.shifting {
454                    self.variable_registers[register_x] = self.variable_registers[register_y];
455                }
456                match direction {
457                    Direction::Left => {
458                        let top = self.variable_registers[register_x] & 0b1000_0000;
459                        let res = self.variable_registers[register_x] << 1;
460                        self.variable_registers[register_x] = res;
461                        if top > 0 {
462                            self.variable_registers[0xF] = 1
463                        } else {
464                            self.variable_registers[0xF] = 0
465                        };
466                    }
467                    Direction::Right => {
468                        let bot = self.variable_registers[register_x] & 0b1;
469                        let res = self.variable_registers[register_x] >> 1;
470                        self.variable_registers[register_x] = res;
471                        if bot > 0 {
472                            self.variable_registers[0xF] = 1
473                        } else {
474                            self.variable_registers[0xF] = 0
475                        };
476                    }
477                }
478            }
479            LogicalOperator::Invalid => {}
480        }
481    }
482
483    fn draw(&mut self, x_register: usize, y_register: usize, height: u8) {
484        let (x_value, y_value) = (
485            (self.variable_registers[x_register] % self.screen_width as u8) as u16,
486            (self.variable_registers[y_register] % self.screen_height as u8) as u16,
487        );
488        self.variable_registers[0xF] = 0;
489
490        let start_loc = y_value * self.screen_width as u16 + x_value;
491
492        let (width, height) = { if height == 0 { (16, 16) } else { (8, height) } };
493
494        // For each row in the sprite
495        for i in 0..height {
496            let mut overdrawn_y = false;
497            let sprite_row = if width == 8 {
498                let wrapped_mem_index = (self.index_register + i as usize) % self.memory.len();
499                let Some(sprite_row) = self.memory.get(wrapped_mem_index) else {
500                    break;
501                };
502                *sprite_row as u16
503            } else {
504                let wrapped_mem_index_first_half =
505                    (self.index_register + (i * 2) as usize) % self.memory.len();
506                let Some(sprite_left_half) = self.memory.get(wrapped_mem_index_first_half) else {
507                    break;
508                };
509                let wrapped_mem_index_second_half =
510                    (self.index_register + (i * 2 + 1) as usize) % self.memory.len();
511                let Some(sprite_right_half) = self.memory.get(wrapped_mem_index_second_half) else {
512                    break;
513                };
514                (*sprite_left_half as u16) << 8 | *sprite_right_half as u16
515            };
516
517            let current_loc = start_loc + self.screen_width as u16 * i as u16;
518
519            if y_value + i as u16 > (self.screen_height - 1) as u16 {
520                if self.quirks.clipping {
521                    continue;
522                } else {
523                    overdrawn_y = true;
524                }
525            }
526            // For each pixel in the row
527            for j in 0..width {
528                let mut overdrawn_x = false;
529                let mask: u16 = 0b1 << (width - j - 1);
530
531                if (x_value + j) > (self.screen_width - 1) as u16 {
532                    if self.quirks.clipping {
533                        continue;
534                    } else {
535                        overdrawn_x = true;
536                    }
537                }
538
539                let mut to_get_index = current_loc + j;
540
541                // NOTE: Only in Quirks.clipping
542                // Because we working with a flattened array, lol
543                // subract one row to keep alignment of the px
544                if overdrawn_x {
545                    to_get_index -= self.screen_width as u16;
546                }
547
548                // NOTE: Only in Quirks.clipping
549                // If we go off the bottom of the screen, subract an entire screen
550                // to get back to the top
551                if overdrawn_y {
552                    to_get_index -= (self.screen_width * self.screen_height) as u16;
553                }
554
555                if let Some(x) = self.screen_buffer.get_mut((to_get_index) as usize) {
556                    let pixel = if (mask & sprite_row) > 0 { 1 } else { 0 };
557                    // Collision flag if target and px are both on.
558                    if pixel > 0 && *x > 0 {
559                        self.variable_registers[0xF] = 1;
560                    }
561                    *x ^= pixel;
562                }
563            }
564        }
565        self.wait_to_display();
566        self.drawer
567            .draw_buffer(&self.screen_buffer, self.screen_width, self.screen_height);
568    }
569
570    fn wait_to_display(&mut self) {
571        let now = SystemTime::now();
572        let time_since_last_draw = now
573            .duration_since(self.last_draw)
574            .expect("Earlier is before now.");
575
576        if time_since_last_draw < self.max_draw_delay {
577            std::thread::sleep(self.max_draw_delay - time_since_last_draw);
578        }
579        self.last_draw = SystemTime::now();
580    }
581
582    fn clear_screen(&mut self) {
583        for i in self.screen_buffer.iter_mut() {
584            *i = 0;
585        }
586        self.drawer.clear_screen();
587    }
588
589    fn set_mem_block(&mut self, set: &[u8], start_addr: usize) -> Result<(), ApplicationError> {
590        let end_addr = start_addr + set.len();
591        if end_addr > self.memory.len() {
592            return Err(ApplicationError::MemoryLocationOutOfRange {
593                max_addr: self.memory.len() - set.len(),
594            });
595        }
596        let x = &mut self.memory[start_addr..end_addr];
597        for (i, item) in x.iter_mut().enumerate() {
598            *item = set[i];
599        }
600        Ok(())
601    }
602}
603
604/// Data that is returned after each iteration of the interpreter as long as it is running as an
605/// iterator.
606#[derive(Debug)]
607pub struct EmulatorState {
608    pub memory: [u8; 0x1000],
609    pub stack: Vec<usize>,
610    pub variable_registers: [u8; 16],
611    pub screen_buffer: Vec<u8>,
612    pub index_register: usize,
613    pub program_counter: usize,
614    pub last_instruction: Instruction,
615}
616
617impl<T: Draw, P: ReadInputState> Iterator for Emulator<T, P> {
618    type Item = EmulatorState;
619
620    fn next(&mut self) -> Option<Self::Item> {
621        let instruction = self.fetch();
622        self.execute(instruction.clone());
623        Some(EmulatorState {
624            last_instruction: instruction,
625            memory: self.memory,
626            stack: self.stack.clone(),
627            variable_registers: self.variable_registers,
628            screen_buffer: self.screen_buffer.clone(),
629            index_register: self.index_register,
630            program_counter: self.program_counter,
631        })
632    }
633}
634
635#[cfg(test)]
636mod tests {
637    struct DummyInput;
638    struct DebugDrawer;
639    impl Draw for DebugDrawer {
640        fn draw_buffer(&mut self, _: &[u8], _: usize, _: usize) {}
641        fn clear_screen(&mut self) {}
642    }
643    impl ReadInputState for DummyInput {
644        fn read_keys_state(&self) -> Result<[u8; 16], String> {
645            Ok([0; 16])
646        }
647        fn reset_keys_state(&mut self) {}
648    }
649
650    use super::*;
651
652    #[test]
653    fn it_can_fetch_an_instruction() {
654        let mut emulator =
655            Emulator::<DebugDrawer, DummyInput>::init(vec![], DebugDrawer, DummyInput)
656                .expect("All initial memory is in range");
657
658        emulator
659            .set_mem_block(&FONT, FONT_ADDR)
660            .expect("Should be able to set font");
661
662        // Clear
663        emulator.memory[0x200] = 0x00;
664        emulator.memory[0x201] = 0xE0;
665
666        // Draw XYH
667        emulator.memory[0x202] = 0xDE;
668        emulator.memory[0x203] = 0xF5;
669
670        // 6XNN (set register VX)
671        emulator.memory[0x204] = 0x6E;
672        emulator.memory[0x205] = 0xAB;
673
674        // 7XNN (add value to register VX)
675        emulator.memory[0x206] = 0x7E;
676        emulator.memory[0x207] = 0xAB;
677
678        // ANNN (set index register I)
679        emulator.memory[0x208] = 0xA1;
680        emulator.memory[0x209] = 0x23;
681
682        // 1NNN (jump)
683        emulator.memory[0x20A] = 0x11;
684        emulator.memory[0x20B] = 0x23;
685
686        // 3XNN (skip if *reg X eq val NN)
687        emulator.memory[0x20C] = 0x31;
688        emulator.memory[0x20D] = 0x23;
689
690        // 4XNN (skip if *reg X not eq val NN)
691        emulator.memory[0x20E] = 0x41;
692        emulator.memory[0x20F] = 0x23;
693
694        // 5XY0 (skip if *reg X not eq *reg Y)
695        emulator.memory[0x210] = 0x51;
696        emulator.memory[0x211] = 0x20;
697
698        // 9XY0 (skip if *reg X not eq *reg Y)
699        emulator.memory[0x212] = 0x91;
700        emulator.memory[0x213] = 0x20;
701
702        emulator.program_counter = 0x200;
703
704        let instruction = emulator.fetch();
705        assert!(matches!(instruction, Instruction::ClearScreen));
706
707        let instruction = emulator.fetch();
708        assert!(matches!(
709            instruction,
710            Instruction::Draw {
711                x_register: 0xE,
712                y_register: 0xF,
713                height: 0x5
714            }
715        ));
716
717        let instruction = emulator.fetch();
718        assert!(matches!(
719            instruction,
720            Instruction::SetGeneralRegister {
721                register: 0xE,
722                value: 0xAB
723            }
724        ));
725
726        let instruction = emulator.fetch();
727        assert!(matches!(
728            instruction,
729            Instruction::AddToRegister {
730                register: 0xE,
731                value: 0xAB
732            }
733        ));
734
735        let instruction = emulator.fetch();
736        assert!(matches!(instruction, Instruction::SetIndexRegister(0x123)));
737
738        let instruction = emulator.fetch();
739        assert!(matches!(instruction, Instruction::Jump(0x123)));
740
741        let instruction = emulator.fetch();
742        assert!(matches!(
743            instruction,
744            Instruction::SkipEqValueWithRegisterContents {
745                register: 0x1,
746                value: 0x23
747            }
748        ));
749
750        let instruction = emulator.fetch();
751        assert!(matches!(
752            instruction,
753            Instruction::SkipNotEqValueWithRegisterContents {
754                register: 0x1,
755                value: 0x23
756            }
757        ));
758
759        let instruction = emulator.fetch();
760        assert!(matches!(
761            instruction,
762            Instruction::SkipEqRegisters {
763                register_x: 0x1,
764                register_y: 0x2
765            }
766        ));
767
768        let instruction = emulator.fetch();
769        assert!(matches!(
770            instruction,
771            Instruction::SkipNotEqRegisters {
772                register_x: 0x1,
773                register_y: 0x2
774            }
775        ));
776    }
777}