xref: /cloud-hypervisor/hypervisor/src/arch/x86/emulator/instructions/mov.rs (revision 2571e59438597f53aa4993cd70d6462fe1364ba7)
1 //
2 // Copyright © 2020 Intel Corporation
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 
7 #![allow(non_camel_case_types)]
8 
9 //
10 // MOV-Move
11 // SDM Volume 1, Chapter 4.3
12 //   Copies the second operand (source operand) to the first operand (destination operand).
13 //
14 
15 use crate::arch::emulator::{EmulationError, PlatformEmulator};
16 use crate::arch::x86::emulator::instructions::*;
17 use crate::arch::x86::Exception;
18 
19 macro_rules! mov_rm_r {
20     ($bound:ty) => {
21         fn emulate(
22             &self,
23             insn: &Instruction,
24             state: &mut T,
25             platform: &mut dyn PlatformEmulator<CpuState = T>,
26         ) -> Result<(), EmulationError<Exception>> {
27             let src_reg_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
28                 .map_err(EmulationError::PlatformEmulationError)?;
29 
30             set_op(
31                 &insn,
32                 0,
33                 std::mem::size_of::<$bound>(),
34                 state,
35                 platform,
36                 src_reg_value,
37             )
38             .map_err(EmulationError::PlatformEmulationError)?;
39 
40             Ok(())
41         }
42     };
43 }
44 
45 macro_rules! mov_rm_imm {
46     ($bound:ty) => {
47         fn emulate(
48             &self,
49             insn: &Instruction,
50             state: &mut T,
51             platform: &mut dyn PlatformEmulator<CpuState = T>,
52         ) -> Result<(), EmulationError<Exception>> {
53             let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
54                 .map_err(EmulationError::PlatformEmulationError)?;
55 
56             set_op(
57                 &insn,
58                 0,
59                 std::mem::size_of::<$bound>(),
60                 state,
61                 platform,
62                 imm,
63             )
64             .map_err(EmulationError::PlatformEmulationError)?;
65 
66             Ok(())
67         }
68     };
69 }
70 
71 macro_rules! movzx {
72     ($dest_op_size:ty, $src_op_size:ty) => {
73         fn emulate(
74             &self,
75             insn: &Instruction,
76             state: &mut T,
77             platform: &mut dyn PlatformEmulator<CpuState = T>,
78         ) -> Result<(), EmulationError<Exception>> {
79             let src_value = get_op(
80                 &insn,
81                 1,
82                 std::mem::size_of::<$src_op_size>(),
83                 state,
84                 platform,
85             )
86             .map_err(EmulationError::PlatformEmulationError)?;
87 
88             set_op(
89                 &insn,
90                 0,
91                 std::mem::size_of::<$dest_op_size>(),
92                 state,
93                 platform,
94                 src_value,
95             )
96             .map_err(EmulationError::PlatformEmulationError)?;
97 
98             Ok(())
99         }
100     };
101 }
102 
103 // MOV r/rm is a special case of MOVZX, where both operands have the same size.
104 macro_rules! mov_r_rm {
105     ($op_size:ty) => {
106         movzx!($op_size, $op_size);
107     };
108 }
109 
110 macro_rules! mov_r_imm {
111     ($bound:ty) => {
112         fn emulate(
113             &self,
114             insn: &Instruction,
115             state: &mut T,
116             platform: &mut dyn PlatformEmulator<CpuState = T>,
117         ) -> Result<(), EmulationError<Exception>> {
118             let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
119                 .map_err(EmulationError::PlatformEmulationError)?;
120 
121             set_op(
122                 &insn,
123                 0,
124                 std::mem::size_of::<$bound>(),
125                 state,
126                 platform,
127                 imm,
128             )
129             .map_err(EmulationError::PlatformEmulationError)?;
130 
131             Ok(())
132         }
133     };
134 }
135 
136 pub struct Mov_r8_rm8;
137 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_rm8 {
138     mov_r_rm!(u8);
139 }
140 
141 pub struct Mov_r8_imm8;
142 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_imm8 {
143     mov_r_imm!(u8);
144 }
145 
146 pub struct Mov_r16_rm16;
147 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_rm16 {
148     mov_r_rm!(u16);
149 }
150 
151 pub struct Mov_r16_imm16;
152 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_imm16 {
153     mov_r_imm!(u16);
154 }
155 
156 pub struct Mov_r32_rm32;
157 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_rm32 {
158     mov_r_rm!(u32);
159 }
160 
161 pub struct Mov_r32_imm32;
162 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_imm32 {
163     mov_r_imm!(u32);
164 }
165 
166 pub struct Mov_r64_rm64;
167 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_rm64 {
168     mov_r_rm!(u64);
169 }
170 
171 pub struct Mov_r64_imm64;
172 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_imm64 {
173     mov_r_imm!(u64);
174 }
175 
176 pub struct Mov_rm8_imm8;
177 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_imm8 {
178     mov_rm_imm!(u8);
179 }
180 
181 pub struct Mov_rm8_r8;
182 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_r8 {
183     mov_rm_r!(u8);
184 }
185 
186 pub struct Mov_rm16_imm16;
187 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_imm16 {
188     mov_rm_imm!(u16);
189 }
190 
191 pub struct Mov_rm16_r16;
192 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_r16 {
193     mov_rm_r!(u16);
194 }
195 
196 pub struct Mov_rm32_imm32;
197 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_imm32 {
198     mov_rm_imm!(u32);
199 }
200 
201 pub struct Mov_rm32_r32;
202 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_r32 {
203     mov_rm_r!(u32);
204 }
205 
206 pub struct Mov_rm64_imm32;
207 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_imm32 {
208     mov_rm_imm!(u32);
209 }
210 
211 pub struct Mov_rm64_r64;
212 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_r64 {
213     mov_rm_r!(u64);
214 }
215 
216 // MOVZX
217 pub struct Movzx_r16_rm8;
218 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r16_rm8 {
219     movzx!(u16, u8);
220 }
221 
222 pub struct Movzx_r32_rm8;
223 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm8 {
224     movzx!(u32, u8);
225 }
226 
227 pub struct Movzx_r64_rm8;
228 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm8 {
229     movzx!(u64, u8);
230 }
231 
232 pub struct Movzx_r32_rm16;
233 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm16 {
234     movzx!(u32, u16);
235 }
236 
237 pub struct Movzx_r64_rm16;
238 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm16 {
239     movzx!(u64, u16);
240 }
241 
242 pub struct Mov_moffs16_AX;
243 impl<T: CpuStateManager> InstructionHandler<T> for Mov_moffs16_AX {
244     movzx!(u16, u16);
245 }
246 
247 pub struct Mov_AX_moffs16;
248 impl<T: CpuStateManager> InstructionHandler<T> for Mov_AX_moffs16 {
249     movzx!(u16, u16);
250 }
251 
252 pub struct Mov_moffs32_EAX;
253 impl<T: CpuStateManager> InstructionHandler<T> for Mov_moffs32_EAX {
254     movzx!(u32, u32);
255 }
256 
257 pub struct Mov_EAX_moffs32;
258 impl<T: CpuStateManager> InstructionHandler<T> for Mov_EAX_moffs32 {
259     movzx!(u32, u32);
260 }
261 
262 pub struct Mov_moffs64_RAX;
263 impl<T: CpuStateManager> InstructionHandler<T> for Mov_moffs64_RAX {
264     movzx!(u64, u64);
265 }
266 
267 pub struct Mov_RAX_moffs64;
268 impl<T: CpuStateManager> InstructionHandler<T> for Mov_RAX_moffs64 {
269     movzx!(u64, u64);
270 }
271 
272 #[cfg(test)]
273 mod tests {
274     #![allow(unused_mut)]
275     use super::*;
276     use crate::arch::x86::emulator::mock_vmm::*;
277 
278     #[test]
279     // mov rax,rbx
280     fn test_mov_r64_r64() {
281         let rbx: u64 = 0x8899aabbccddeeff;
282         let ip: u64 = 0x1000;
283         let cpu_id = 0;
284         let insn = [0x48, 0x89, 0xd8];
285         let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], None);
286         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
287 
288         let rax: u64 = vmm
289             .cpu_state(cpu_id)
290             .unwrap()
291             .read_reg(Register::RAX)
292             .unwrap();
293         assert_eq!(rax, rbx);
294     }
295 
296     #[test]
297     // mov rax,0x1122334411223344
298     fn test_mov_r64_imm64() {
299         let imm64: u64 = 0x1122334411223344;
300         let ip: u64 = 0x1000;
301         let cpu_id = 0;
302         let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
303         let mut vmm = MockVmm::new(ip, vec![], None);
304         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
305 
306         let rax: u64 = vmm
307             .cpu_state(cpu_id)
308             .unwrap()
309             .read_reg(Register::RAX)
310             .unwrap();
311         assert_eq!(rax, imm64);
312     }
313 
314     #[test]
315     // mov rax, [rax+rax]
316     fn test_mov_r64_m64() {
317         let target_rax: u64 = 0x1234567812345678;
318         let mut rax: u64 = 0x100;
319         let ip: u64 = 0x1000;
320         let cpu_id = 0;
321         let memory: [u8; 8] = target_rax.to_le_bytes();
322         let insn = [0x48, 0x8b, 0x04, 0x00];
323         let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], Some((rax + rax, &memory)));
324         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
325 
326         rax = vmm
327             .cpu_state(cpu_id)
328             .unwrap()
329             .read_reg(Register::RAX)
330             .unwrap();
331         assert_eq!(rax, target_rax);
332     }
333 
334     #[test]
335     // mov al,0x11
336     fn test_mov_r8_imm8() {
337         let imm8: u8 = 0x11;
338         let ip: u64 = 0x1000;
339         let cpu_id = 0;
340         let insn = [0xb0, 0x11];
341         let mut vmm = MockVmm::new(ip, vec![], None);
342         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
343 
344         let al = vmm
345             .cpu_state(cpu_id)
346             .unwrap()
347             .read_reg(Register::AL)
348             .unwrap();
349         assert_eq!(al as u8, imm8);
350     }
351 
352     #[test]
353     // mov eax,0x11
354     fn test_mov_r32_imm8() {
355         let imm8: u8 = 0x11;
356         let ip: u64 = 0x1000;
357         let cpu_id = 0;
358         let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
359         let mut vmm = MockVmm::new(ip, vec![], None);
360         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
361 
362         let eax = vmm
363             .cpu_state(cpu_id)
364             .unwrap()
365             .read_reg(Register::EAX)
366             .unwrap();
367         assert_eq!(eax as u8, imm8);
368     }
369 
370     #[test]
371     // mov rax,0x11223344
372     fn test_mov_r64_imm32() {
373         let imm32: u32 = 0x11223344;
374         let ip: u64 = 0x1000;
375         let cpu_id = 0;
376         let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
377         let mut vmm = MockVmm::new(ip, vec![], None);
378         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
379 
380         let rax: u64 = vmm
381             .cpu_state(cpu_id)
382             .unwrap()
383             .read_reg(Register::RAX)
384             .unwrap();
385         assert_eq!(rax, imm32 as u64);
386     }
387 
388     #[test]
389     // mov byte ptr [rax],dh
390     fn test_mov_m8_r8() {
391         let rax: u64 = 0x100;
392         let dh: u8 = 0x99;
393         let ip: u64 = 0x1000;
394         let cpu_id = 0;
395         let insn = [0x88, 0x30];
396         let mut vmm = MockVmm::new(
397             ip,
398             vec![(Register::RAX, rax), (Register::DH, dh.into())],
399             None,
400         );
401         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
402 
403         let mut memory: [u8; 1] = [0; 1];
404         vmm.read_memory(rax, &mut memory).unwrap();
405 
406         assert_eq!(u8::from_le_bytes(memory), dh);
407     }
408 
409     #[test]
410     // mov dword ptr [rax],esi
411     fn test_mov_m32_r32() {
412         let rax: u64 = 0x100;
413         let esi: u32 = 0x8899;
414         let ip: u64 = 0x1000;
415         let cpu_id = 0;
416         let insn = [0x89, 0x30];
417         let mut vmm = MockVmm::new(
418             ip,
419             vec![(Register::RAX, rax), (Register::ESI, esi.into())],
420             None,
421         );
422         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
423 
424         let mut memory: [u8; 4] = [0; 4];
425         vmm.read_memory(rax, &mut memory).unwrap();
426 
427         assert_eq!(u32::from_le_bytes(memory), esi);
428     }
429 
430     #[test]
431     // mov dword ptr [rax+0x00000001],edi
432     fn test_mov_m32imm32_r32() {
433         let rax: u64 = 0x100;
434         let displacement: u64 = 0x1;
435         let edi: u32 = 0x8899;
436         let ip: u64 = 0x1000;
437         let cpu_id = 0;
438         let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
439         let mut vmm = MockVmm::new(
440             ip,
441             vec![(Register::RAX, rax), (Register::EDI, edi.into())],
442             None,
443         );
444         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
445 
446         let mut memory: [u8; 4] = [0; 4];
447         vmm.read_memory(rax + displacement, &mut memory).unwrap();
448 
449         assert_eq!(u32::from_le_bytes(memory), edi);
450     }
451 
452     #[test]
453     // mov eax,dword ptr [rax+10h]
454     fn test_mov_r32_m32imm32() {
455         let rax: u64 = 0x100;
456         let displacement: u64 = 0x10;
457         let eax: u32 = 0xaabbccdd;
458         let memory: [u8; 4] = eax.to_le_bytes();
459         let ip: u64 = 0x1000;
460         let cpu_id = 0;
461         let insn = [0x8b, 0x40, 0x10];
462         let mut vmm = MockVmm::new(
463             ip,
464             vec![(Register::RAX, rax)],
465             Some((rax + displacement, &memory)),
466         );
467         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
468 
469         let new_eax = vmm
470             .cpu_state(cpu_id)
471             .unwrap()
472             .read_reg(Register::EAX)
473             .unwrap();
474         assert_eq!(new_eax, eax as u64);
475     }
476 
477     #[test]
478     // mov al,byte ptr [rax+10h]
479     fn test_mov_r8_m32imm32() {
480         let rax: u64 = 0x100;
481         let displacement: u64 = 0x10;
482         let al: u8 = 0xaa;
483         let ip: u64 = 0x1000;
484         let cpu_id = 0;
485         let insn = [0x8a, 0x40, 0x10];
486         let memory: [u8; 1] = al.to_le_bytes();
487         let mut vmm = MockVmm::new(
488             ip,
489             vec![(Register::RAX, rax)],
490             Some((rax + displacement, &memory)),
491         );
492         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
493 
494         let new_al = vmm
495             .cpu_state(cpu_id)
496             .unwrap()
497             .read_reg(Register::AL)
498             .unwrap();
499         assert_eq!(new_al, al as u64);
500     }
501 
502     #[test]
503     // mov rax, 0x100
504     // mov rbx, qword ptr [rax+10h]
505     fn test_mov_r64_imm64_and_r64_m64() {
506         let target_rax: u64 = 0x1234567812345678;
507         let rax: u64 = 0x100;
508         let displacement: u64 = 0x10;
509         let ip: u64 = 0x1000;
510         let cpu_id = 0;
511         let memory: [u8; 8] = target_rax.to_le_bytes();
512         let insn = [
513             0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
514             0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
515         ];
516         let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
517         assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
518 
519         let rbx: u64 = vmm
520             .cpu_state(cpu_id)
521             .unwrap()
522             .read_reg(Register::RBX)
523             .unwrap();
524         assert_eq!(rbx, target_rax);
525     }
526 
527     #[test]
528     // mov rax, 0x100
529     // mov rbx, qword ptr [rax+10h]
530     fn test_mov_r64_imm64_and_r64_m64_first_insn() {
531         let target_rax: u64 = 0x1234567812345678;
532         let rax: u64 = 0x100;
533         let displacement: u64 = 0x10;
534         let ip: u64 = 0x1000;
535         let cpu_id = 0;
536         let memory: [u8; 8] = target_rax.to_le_bytes();
537         let insn = [
538             0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
539             0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
540         ];
541 
542         let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
543         // Only run the first instruction.
544         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
545 
546         assert_eq!(ip + 7, vmm.cpu_state(cpu_id).unwrap().ip());
547 
548         let new_rax: u64 = vmm
549             .cpu_state(cpu_id)
550             .unwrap()
551             .read_reg(Register::RAX)
552             .unwrap();
553         assert_eq!(rax, new_rax);
554     }
555 
556     #[test]
557     // mov rax, 0x100
558     // mov rbx, qword ptr [rax+10h]
559     // mov rax, 0x200
560     fn test_mov_r64_imm64_and_r64_m64_two_insns() {
561         let target_rax: u64 = 0x1234567812345678;
562         let rax: u64 = 0x100;
563         let displacement: u64 = 0x10;
564         let ip: u64 = 0x1000;
565         let cpu_id = 0;
566         let memory: [u8; 8] = target_rax.to_le_bytes();
567         let insn = [
568             0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
569             0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
570             0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
571         ];
572 
573         let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
574         // Run the 2 first instructions.
575         assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok());
576 
577         assert_eq!(ip + 7 + 4, vmm.cpu_state(cpu_id).unwrap().ip());
578 
579         let rbx: u64 = vmm
580             .cpu_state(cpu_id)
581             .unwrap()
582             .read_reg(Register::RBX)
583             .unwrap();
584         assert_eq!(rbx, target_rax);
585 
586         // Check that rax is still at 0x100
587         let new_rax: u64 = vmm
588             .cpu_state(cpu_id)
589             .unwrap()
590             .read_reg(Register::RAX)
591             .unwrap();
592         assert_eq!(rax, new_rax);
593     }
594 
595     #[test]
596     // movzx eax, bl
597     fn test_movzx_r32_r8l() {
598         let bx: u16 = 0x8899;
599         let ip: u64 = 0x1000;
600         let cpu_id = 0;
601         let insn = [0x0f, 0xb6, 0xc3];
602         let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
603         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
604 
605         let eax: u64 = vmm
606             .cpu_state(cpu_id)
607             .unwrap()
608             .read_reg(Register::EAX)
609             .unwrap();
610         assert_eq!(eax, (bx & 0xff) as u64);
611     }
612 
613     #[test]
614     // movzx eax, bh
615     fn test_movzx_r32_r8h() {
616         let bx: u16 = 0x8899;
617         let ip: u64 = 0x1000;
618         let cpu_id = 0;
619         let insn = [0x0f, 0xb6, 0xc7];
620         let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
621         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
622 
623         let eax: u64 = vmm
624             .cpu_state(cpu_id)
625             .unwrap()
626             .read_reg(Register::EAX)
627             .unwrap();
628         assert_eq!(eax, (bx >> 8) as u64);
629     }
630 
631     #[test]
632     // movzx eax, byte ptr [rbx]
633     fn test_movzx_r32_m8() {
634         let rbx: u64 = 0x100;
635         let value: u8 = 0xaa;
636         let ip: u64 = 0x1000;
637         let cpu_id = 0;
638         let insn = [0x0f, 0xb7, 0x03];
639         let memory: [u8; 1] = value.to_le_bytes();
640         let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], Some((rbx, &memory)));
641         assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok());
642 
643         let eax: u64 = vmm
644             .cpu_state(cpu_id)
645             .unwrap()
646             .read_reg(Register::EAX)
647             .unwrap();
648         assert_eq!(eax, value as u64);
649     }
650 
651     #[test]
652     // movabs ax, ds:0x1337
653     // movabs eax, ds:0x1337
654     // movabs rax, ds:0x1337
655     fn test_mov_memoff_ax() {
656         let test_inputs: [(Register, &[u8]); 3] = [
657             (Register::AX, &[0x66, 0xa1]),
658             (Register::EAX, &[0xa1]),
659             (Register::RAX, &[0x48, 0xa1]),
660         ];
661 
662         // Constructs the instruction with the provided inputs and emulates it.
663         fn helper(register: Register, instruction_prefix: &[u8]) {
664             let mem_addr: u64 = 0x1337;
665             let mem_value: u64 = 0x13371337deadbeef;
666             let ip: u64 = 0x1000;
667             let cpu_id = 0;
668 
669             let mut instruction_bytes = Vec::new();
670             // instruction prefix with specified register
671             instruction_bytes.extend(instruction_prefix);
672             // 64-bit memory operand
673             instruction_bytes.extend([
674                 mem_addr.to_le_bytes()[0],
675                 mem_addr.to_le_bytes()[1],
676                 0,
677                 0,
678                 0,
679                 0,
680                 0,
681                 0,
682             ]);
683 
684             let memory: [u8; 8] = mem_value.to_le_bytes();
685             let mut vmm = MockVmm::new(ip, vec![], Some((mem_addr, &memory)));
686             assert!(vmm.emulate_first_insn(cpu_id, &instruction_bytes).is_ok());
687 
688             let ax: u64 = vmm.cpu_state(cpu_id).unwrap().read_reg(register).unwrap();
689 
690             match register {
691                 Register::AX => {
692                     assert_eq!(ax as u16, mem_value as u16);
693                 }
694                 Register::EAX => {
695                     assert_eq!(ax as u32, mem_value as u32);
696                 }
697                 Register::RAX => {
698                     assert_eq!(ax, mem_value);
699                 }
700                 _ => panic!(),
701             }
702         }
703 
704         for (register, instruction_prefix) in test_inputs {
705             helper(register, instruction_prefix)
706         }
707     }
708 
709     #[test]
710     // movabs ds:0x1337, ax
711     // movabs ds:0x1337, eax
712     // movabs ds:0x1337, rax
713     fn test_mov_ax_memoff() {
714         let test_inputs: [(Register, &[u8]); 3] = [
715             (Register::AX, &[0x66, 0xa3]),
716             (Register::EAX, &[0xa3]),
717             (Register::RAX, &[0x48, 0xa3]),
718         ];
719 
720         // Constructs the instruction with the provided inputs and emulates it.
721         fn helper(register: Register, instruction_prefix: &[u8]) {
722             let mem_addr: u64 = 0x1337;
723             let ax: u64 = 0x13371337deadbeef;
724             let ip: u64 = 0x1000;
725             let cpu_id = 0;
726 
727             let mut instruction_bytes = Vec::new();
728             // instruction prefix with specified register
729             instruction_bytes.extend(instruction_prefix);
730             // 64-bit memory operand
731             instruction_bytes.extend([
732                 mem_addr.to_le_bytes()[0],
733                 mem_addr.to_le_bytes()[1],
734                 0,
735                 0,
736                 0,
737                 0,
738                 0,
739                 0,
740             ]);
741 
742             let mut vmm = MockVmm::new(ip, vec![(Register::RAX, ax)], None);
743             assert!(vmm.emulate_first_insn(cpu_id, &instruction_bytes).is_ok());
744 
745             match register {
746                 Register::AX => {
747                     let mut memory: [u8; 2] = [0; 2];
748                     vmm.read_memory(mem_addr, &mut memory).unwrap();
749                     assert_eq!(u16::from_le_bytes(memory), ax as u16);
750                 }
751                 Register::EAX => {
752                     let mut memory: [u8; 4] = [0; 4];
753                     vmm.read_memory(mem_addr, &mut memory).unwrap();
754                     assert_eq!(u32::from_le_bytes(memory), ax as u32);
755                 }
756                 Register::RAX => {
757                     let mut memory: [u8; 8] = [0; 8];
758                     vmm.read_memory(mem_addr, &mut memory).unwrap();
759                     assert_eq!(u64::from_le_bytes(memory), ax);
760                 }
761                 _ => panic!(),
762             }
763         }
764 
765         for (register, instruction_prefix) in test_inputs {
766             helper(register, instruction_prefix)
767         }
768     }
769 }
770