1use std::{
2 sync::{
3 Arc,
4 atomic::{AtomicU16, Ordering},
5 },
6 time::{Duration, SystemTime, UNIX_EPOCH},
7};
8use 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#[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 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 pub fn run_blocking(&mut self) {
98 loop {
99 let instruction = self.fetch();
100 self.execute(instruction);
101 }
102 }
103
104 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 pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) -> &mut Self {
123 self.quirks = quirks_mode.into();
124 self
125 }
126
127 pub fn with_custom_quirks(&mut self, quirks: QuirksFields) -> &mut Self {
129 self.quirks = quirks;
130 self
131 }
132
133 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 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 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 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 if overdrawn_x {
545 to_get_index -= self.screen_width as u16;
546 }
547
548 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 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#[derive(Debug, Clone)]
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 Default for EmulatorState {
618 fn default() -> Self {
619 Self {
620 memory: [0; 0x1000],
621 last_instruction: Instruction::ClearScreen,
622 stack: Default::default(),
623 variable_registers: Default::default(),
624 screen_buffer: Default::default(),
625 index_register: Default::default(),
626 program_counter: Default::default(),
627 }
628 }
629}
630
631impl<T: Draw, P: ReadInputState> Iterator for Emulator<T, P> {
632 type Item = EmulatorState;
633
634 fn next(&mut self) -> Option<Self::Item> {
635 let instruction = self.fetch();
636 self.execute(instruction.clone());
637 Some(EmulatorState {
638 last_instruction: instruction,
639 memory: self.memory,
640 stack: self.stack.clone(),
641 variable_registers: self.variable_registers,
642 screen_buffer: self.screen_buffer.clone(),
643 index_register: self.index_register,
644 program_counter: self.program_counter,
645 })
646 }
647}
648
649#[cfg(test)]
650mod tests {
651 struct DummyInput;
652 struct DebugDrawer;
653 impl Draw for DebugDrawer {
654 fn draw_buffer(&mut self, _: &[u8], _: usize, _: usize) {}
655 fn clear_screen(&mut self) {}
656 }
657 impl ReadInputState for DummyInput {
658 fn read_keys_state(&self) -> Result<[u8; 16], String> {
659 Ok([0; 16])
660 }
661 fn reset_keys_state(&mut self) {}
662 }
663
664 use super::*;
665
666 #[test]
667 fn it_can_fetch_an_instruction() {
668 let mut emulator =
669 Emulator::<DebugDrawer, DummyInput>::init(vec![], DebugDrawer, DummyInput)
670 .expect("All initial memory is in range");
671
672 emulator
673 .set_mem_block(&FONT, FONT_ADDR)
674 .expect("Should be able to set font");
675
676 emulator.memory[0x200] = 0x00;
678 emulator.memory[0x201] = 0xE0;
679
680 emulator.memory[0x202] = 0xDE;
682 emulator.memory[0x203] = 0xF5;
683
684 emulator.memory[0x204] = 0x6E;
686 emulator.memory[0x205] = 0xAB;
687
688 emulator.memory[0x206] = 0x7E;
690 emulator.memory[0x207] = 0xAB;
691
692 emulator.memory[0x208] = 0xA1;
694 emulator.memory[0x209] = 0x23;
695
696 emulator.memory[0x20A] = 0x11;
698 emulator.memory[0x20B] = 0x23;
699
700 emulator.memory[0x20C] = 0x31;
702 emulator.memory[0x20D] = 0x23;
703
704 emulator.memory[0x20E] = 0x41;
706 emulator.memory[0x20F] = 0x23;
707
708 emulator.memory[0x210] = 0x51;
710 emulator.memory[0x211] = 0x20;
711
712 emulator.memory[0x212] = 0x91;
714 emulator.memory[0x213] = 0x20;
715
716 emulator.program_counter = 0x200;
717
718 let instruction = emulator.fetch();
719 assert!(matches!(instruction, Instruction::ClearScreen));
720
721 let instruction = emulator.fetch();
722 assert!(matches!(
723 instruction,
724 Instruction::Draw {
725 x_register: 0xE,
726 y_register: 0xF,
727 height: 0x5
728 }
729 ));
730
731 let instruction = emulator.fetch();
732 assert!(matches!(
733 instruction,
734 Instruction::SetGeneralRegister {
735 register: 0xE,
736 value: 0xAB
737 }
738 ));
739
740 let instruction = emulator.fetch();
741 assert!(matches!(
742 instruction,
743 Instruction::AddToRegister {
744 register: 0xE,
745 value: 0xAB
746 }
747 ));
748
749 let instruction = emulator.fetch();
750 assert!(matches!(instruction, Instruction::SetIndexRegister(0x123)));
751
752 let instruction = emulator.fetch();
753 assert!(matches!(instruction, Instruction::Jump(0x123)));
754
755 let instruction = emulator.fetch();
756 assert!(matches!(
757 instruction,
758 Instruction::SkipEqValueWithRegisterContents {
759 register: 0x1,
760 value: 0x23
761 }
762 ));
763
764 let instruction = emulator.fetch();
765 assert!(matches!(
766 instruction,
767 Instruction::SkipNotEqValueWithRegisterContents {
768 register: 0x1,
769 value: 0x23
770 }
771 ));
772
773 let instruction = emulator.fetch();
774 assert!(matches!(
775 instruction,
776 Instruction::SkipEqRegisters {
777 register_x: 0x1,
778 register_y: 0x2
779 }
780 ));
781
782 let instruction = emulator.fetch();
783 assert!(matches!(
784 instruction,
785 Instruction::SkipNotEqRegisters {
786 register_x: 0x1,
787 register_y: 0x2
788 }
789 ));
790 }
791}