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