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(&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 pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) -> &mut Self {
125 self.quirks = quirks_mode.into();
126 self
127 }
128
129 pub fn with_custom_quirks(&mut self, quirks: QuirksFields) -> &mut Self {
131 self.quirks = quirks;
132 self
133 }
134
135 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 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 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 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 if overdrawn_x {
547 to_get_index -= self.screen_width as u16;
548 }
549
550 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 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#[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 emulator.memory[0x200] = 0x00;
692 emulator.memory[0x201] = 0xE0;
693
694 emulator.memory[0x202] = 0xDE;
696 emulator.memory[0x203] = 0xF5;
697
698 emulator.memory[0x204] = 0x6E;
700 emulator.memory[0x205] = 0xAB;
701
702 emulator.memory[0x206] = 0x7E;
704 emulator.memory[0x207] = 0xAB;
705
706 emulator.memory[0x208] = 0xA1;
708 emulator.memory[0x209] = 0x23;
709
710 emulator.memory[0x20A] = 0x11;
712 emulator.memory[0x20B] = 0x23;
713
714 emulator.memory[0x20C] = 0x31;
716 emulator.memory[0x20D] = 0x23;
717
718 emulator.memory[0x20E] = 0x41;
720 emulator.memory[0x20F] = 0x23;
721
722 emulator.memory[0x210] = 0x51;
724 emulator.memory[0x211] = 0x20;
725
726 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}