https://github.com/akkartik/mu/blob/master/subx/014indirect_addressing.cc
1
2
3
4 void test_add_r32_to_mem_at_r32() {
5 Reg[EBX].i = 0x10;
6 Reg[EAX].i = 0x2000;
7 run(
8 "== 0x1\n"
9
10 " 01 18 \n"
11
12 "== 0x2000\n"
13 "01 00 00 00\n"
14 );
15 CHECK_TRACE_CONTENTS(
16 "run: add EBX to r/m32\n"
17 "run: effective address is 0x00002000 (EAX)\n"
18 "run: storing 0x00000011\n"
19 );
20 }
21
22 :(before "End Mod Special-cases(addr)")
23 case 0:
24 switch (rm) {
25 default:
26 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
27 addr = Reg[rm].u;
28 break;
29
30 }
31 break;
32
33
34
35 :(before "End Initialize Op Names")
36 put_new(Name, "03", "add rm32 to r32 (add)");
37
38 :(code)
39 void test_add_mem_at_r32_to_r32() {
40 Reg[EAX].i = 0x2000;
41 Reg[EBX].i = 0x10;
42 run(
43 "== 0x1\n"
44
45 " 03 18 \n"
46
47 "== 0x2000\n"
48 "01 00 00 00\n"
49 );
50 CHECK_TRACE_CONTENTS(
51 "run: add r/m32 to EBX\n"
52 "run: effective address is 0x00002000 (EAX)\n"
53 "run: storing 0x00000011\n"
54 );
55 }
56
57 :(before "End Single-Byte Opcodes")
58 case 0x03: {
59 const uint8_t modrm = next();
60 const uint8_t arg1 = (modrm>>3)&0x7;
61 trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end();
62 const int32_t* arg2 = effective_address(modrm);
63 BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2);
64 break;
65 }
66
67
68
69 :(code)
70 void test_subtract_r32_from_mem_at_r32() {
71 Reg[EAX].i = 0x2000;
72 Reg[EBX].i = 1;
73 run(
74 "== 0x1\n"
75
76 " 29 18 \n"
77
78 "== 0x2000\n"
79 "0a 00 00 00\n"
80 );
81 CHECK_TRACE_CONTENTS(
82 "run: subtract EBX from r/m32\n"
83 "run: effective address is 0x00002000 (EAX)\n"
84 "run: storing 0x00000009\n"
85 );
86 }
87
88
89
90 :(before "End Initialize Op Names")
91 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
92
93 :(code)
94 void test_subtract_mem_at_r32_from_r32() {
95 Reg[EAX].i = 0x2000;
96 Reg[EBX].i = 10;
97 run(
98 "== 0x1\n"
99
100 " 2b 18 \n"
101
102 "== 0x2000\n"
103 "01 00 00 00\n"
104 );
105 CHECK_TRACE_CONTENTS(
106 "run: subtract r/m32 from EBX\n"
107 "run: effective address is 0x00002000 (EAX)\n"
108 "run: storing 0x00000009\n"
109 );
110 }
111
112 :(before "End Single-Byte Opcodes")
113 case 0x2b: {
114 const uint8_t modrm = next();
115 const uint8_t arg1 = (modrm>>3)&0x7;
116 trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end();
117 const int32_t* arg2 = effective_address(modrm);
118 BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2);
119 break;
120 }
121
122
123 :(code)
124 void test_and_r32_with_mem_at_r32() {
125 Reg[EAX].i = 0x2000;
126 Reg[EBX].i = 0xff;
127 run(
128 "== 0x1\n"
129
130 " 21 18 \n"
131
132 "== 0x2000\n"
133 "0d 0c 0b 0a\n"
134 );
135 CHECK_TRACE_CONTENTS(
136 "run: and EBX with r/m32\n"
137 "run: effective address is 0x00002000 (EAX)\n"
138 "run: storing 0x0000000d\n"
139 );
140 }
141
142
143
144 :(before "End Initialize Op Names")
145 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
146
147 :(code)
148 void test_and_mem_at_r32_with_r32() {
149 Reg[EAX].i = 0x2000;
150 Reg[EBX].i = 0x0a0b0c0d;
151 run(
152 "== 0x1\n"
153
154 " 23 18 \n"
155
156 "== 0x2000\n"
157 "ff 00 00 00\n"
158 );
159 CHECK_TRACE_CONTENTS(
160 "run: and r/m32 with EBX\n"
161 "run: effective address is 0x00002000 (EAX)\n"
162 "run: storing 0x0000000d\n"
163 );
164 }
165
166 :(before "End Single-Byte Opcodes")
167 case 0x23: {
168 const uint8_t modrm = next();
169 const uint8_t arg1 = (modrm>>3)&0x7;
170 trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end();
171 const int32_t* arg2 = effective_address(modrm);
172 BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2);
173 break;
174 }
175
176
177
178 :(code)
179 void test_or_r32_with_mem_at_r32() {
180 Reg[EAX].i = 0x2000;
181 Reg[EBX].i = 0xa0b0c0d0;
182 run(
183 "== 0x1\n"
184
185 " 09 18 #\n"
186
187 "== 0x2000\n"
188 "0d 0c 0b 0a\n"
189 );
190 CHECK_TRACE_CONTENTS(
191 "run: or EBX with r/m32\n"
192 "run: effective address is 0x00002000 (EAX)\n"
193 "run: storing 0xaabbccdd\n"
194 );
195 }
196
197
198
199 :(before "End Initialize Op Names")
200 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
201
202 :(code)
203 void test_or_mem_at_r32_with_r32() {
204 Reg[EAX].i = 0x2000;
205 Reg[EBX].i = 0xa0b0c0d0;
206 run(
207 "== 0x1\n"
208
209 " 0b 18 \n"
210
211 "== 0x2000\n"
212 "0d 0c 0b 0a\n"
213 );
214 CHECK_TRACE_CONTENTS(
215 "run: or r/m32 with EBX\n"
216 "run: effective address is 0x00002000 (EAX)\n"
217 "run: storing 0xaabbccdd\n"
218 );
219 }
220
221 :(before "End Single-Byte Opcodes")
222 case 0x0b: {
223 const uint8_t modrm = next();
224 const uint8_t arg1 = (modrm>>3)&0x7;
225 trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end();
226 const int32_t* arg2 = effective_address(modrm);
227 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
228 break;
229 }
230
231
232
233 :(code)
234 void test_xor_r32_with_mem_at_r32() {
235 Reg[EAX].i = 0x2000;
236 Reg[EBX].i = 0xa0b0c0d0;
237 run(
238 "== 0x1\n"
239
240 " 31 18 \n"
241 "== 0x2000\n"
242 "0d 0c bb aa\n"
243 );
244 CHECK_TRACE_CONTENTS(
245 "run: xor EBX with r/m32\n"
246 "run: effective address is 0x00002000 (EAX)\n"
247 "run: storing 0x0a0bccdd\n"
248 );
249 }
250
251
252
253 :(before "End Initialize Op Names")
254 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
255
256 :(code)
257 void test_xor_mem_at_r32_with_r32() {
258 Reg[EAX].i = 0x2000;
259 Reg[EBX].i = 0xa0b0c0d0;
260 run(
261 "== 0x1\n"
262
263 " 33 18 \n"
264
265 "== 0x2000\n"
266 "0d 0c 0b 0a\n"
267 );
268 CHECK_TRACE_CONTENTS(
269 "run: xor r/m32 with EBX\n"
270 "run: effective address is 0x00002000 (EAX)\n"
271 "run: storing 0xaabbccdd\n"
272 );
273 }
274
275 :(before "End Single-Byte Opcodes")
276 case 0x33: {
277 const uint8_t modrm = next();
278 const uint8_t arg1 = (modrm>>3)&0x7;
279 trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end();
280 const int32_t* arg2 = effective_address(modrm);
281 BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
282 break;
283 }
284
285
286
287 :(code)
288 void test_not_of_mem_at_r32() {
289 Reg[EBX].i = 0x2000;
290 run(
291 "== 0x1\n"
292
293 " f7 13 \n"
294
295 "== 0x2000\n"
296 "ff 00 0f 0f\n"
297 );
298 CHECK_TRACE_CONTENTS(
299 "run: operate on r/m32\n"
300 "run: effective address is 0x00002000 (EBX)\n"
301 "run: subop: not\n"
302 "run: storing 0xf0f0ff00\n"
303 );
304 }
305
306
307
308 :(code)
309 void test_compare_mem_at_r32_with_r32_greater() {
310 Reg[EAX].i = 0x2000;
311 Reg[EBX].i = 0x0a0b0c07;
312 run(
313 "== 0x1\n"
314
315 " 39 18 \n"
316
317 "== 0x2000\n"
318 "0d 0c 0b 0a\n"
319 );
320 CHECK_TRACE_CONTENTS(
321 "run: compare EBX with r/m32\n"
322 "run: effective address is 0x00002000 (EAX)\n"
323 "run: SF=0; ZF=0; OF=0\n"
324 );
325 }
326
327 :(code)
328 void test_compare_mem_at_r32_with_r32_lesser() {
329 Reg[EAX].i = 0x2000;
330 Reg[EBX].i = 0x0a0b0c0d;
331 run(
332 "== 0x1\n"
333
334 " 39 18 \n"
335
336 "== 0x2000\n"
337 "07 0c 0b 0a\n"
338 );
339 CHECK_TRACE_CONTENTS(
340 "run: compare EBX with r/m32\n"
341 "run: effective address is 0x00002000 (EAX)\n"
342 "run: SF=1; ZF=0; OF=0\n"
343 );
344 }
345
346 :(code)
347 void test_compare_mem_at_r32_with_r32_equal() {
348 Reg[EAX].i = 0x2000;
349 Reg[EBX].i = 0x0a0b0c0d;
350 run(
351 "== 0x1\n"
352
353 " 39 18 \n"
354
355 "== 0x2000\n"
356 "0d 0c 0b 0a\n"
357 );
358 CHECK_TRACE_CONTENTS(
359 "run: compare EBX with r/m32\n"
360 "run: effective address is 0x00002000 (EAX)\n"
361 "run: SF=0; ZF=1; OF=0\n"
362 );
363 }
364
365
366
367 :(before "End Initialize Op Names")
368 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
369
370 :(code)
371 void test_compare_r32_with_mem_at_r32_greater() {
372 Reg[EAX].i = 0x2000;
373 Reg[EBX].i = 0x0a0b0c0d;
374 run(
375 "== 0x1\n"
376
377 " 3b 18 \n"
378
379 "== 0x2000\n"
380 "07 0c 0b 0a\n"
381 );
382 CHECK_TRACE_CONTENTS(
383 "run: compare r/m32 with EBX\n"
384 "run: effective address is 0x00002000 (EAX)\n"
385 "run: SF=0; ZF=0; OF=0\n"
386 );
387 }
388
389 :(before "End Single-Byte Opcodes")
390 case 0x3b: {
391 const uint8_t modrm = next();
392 const uint8_t reg1 = (modrm>>3)&0x7;
393 trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg1) << end();
394 const int32_t arg1 = Reg[reg1].i;
395 const int32_t* arg2 = effective_address(modrm);
396 const int32_t tmp1 = arg1 - *arg2;
397 SF = (tmp1 < 0);
398 ZF = (tmp1 == 0);
399 int64_t tmp2 = arg1 - *arg2;
400 OF = (tmp1 != tmp2);
401 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
402 break;
403 }
404
405 :(code)
406 void test_compare_r32_with_mem_at_r32_lesser() {
407 Reg[EAX].i = 0x2000;
408 Reg[EBX].i = 0x0a0b0c07;
409 run(
410 "== 0x1\n"
411
412 " 3b 18 \n"
413
414 "== 0x2000\n"
415 "0d 0c 0b 0a\n"
416 );
417 CHECK_TRACE_CONTENTS(
418 "run: compare r/m32 with EBX\n"
419 "run: effective address is 0x00002000 (EAX)\n"
420 "run: SF=1; ZF=0; OF=0\n"
421 );
422 }
423
424 :(code)
425 void test_compare_r32_with_mem_at_r32_equal() {
426 Reg[EAX].i = 0x2000;
427 Reg[EBX].i = 0x0a0b0c0d;
428 run(
429 "== 0x1\n"
430
431 " 3b 18 \n"
432
433 "== 0x2000\n"
434 "0d 0c 0b 0a\n"
435 );
436 CHECK_TRACE_CONTENTS(
437 "run: compare r/m32 with EBX\n"
438 "run: effective address is 0x00002000 (EAX)\n"
439 "run: SF=0; ZF=1; OF=0\n"
440 );
441 }
442
443
444
445 :(code)
446 void test_copy_r32_to_mem_at_r32() {
447 Reg[EBX].i = 0xaf;
448 Reg[EAX].i = 0x60;
449 run(
450 "== 0x1\n"
451
452 " 89 18 \n"
453
454 );
455 CHECK_TRACE_CONTENTS(
456 "run: copy EBX to r/m32\n"
457 "run: effective address is 0x00000060 (EAX)\n"
458 "run: storing 0x000000af\n"
459 );
460 }
461
462
463
464 :(before "End Initialize Op Names")
465 put_new(Name, "8b", "copy rm32 to r32 (mov)");
466
467 :(code)
468 void test_copy_mem_at_r32_to_r32() {
469 Reg[EAX].i = 0x2000;
470 run(
471 "== 0x1\n"
472
473 " 8b 18 \n"
474 "== 0x2000\n"
475 "af 00 00 00\n"
476 );
477 CHECK_TRACE_CONTENTS(
478 "run: copy r/m32 to EBX\n"
479 "run: effective address is 0x00002000 (EAX)\n"
480 "run: storing 0x000000af\n"
481 );
482 }
483
484 :(before "End Single-Byte Opcodes")
485 case 0x8b: {
486 const uint8_t modrm = next();
487 const uint8_t rdest = (modrm>>3)&0x7;
488 trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end();
489 const int32_t* src = effective_address(modrm);
490 Reg[rdest].i = *src;
491 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end();
492 break;
493 }
494
495
496
497 :(code)
498 void test_jump_mem_at_r32() {
499 Reg[EAX].i = 0x2000;
500 run(
501 "== 0x1\n"
502
503 " ff 20 \n"
504
505 " 05 00 00 00 01\n"
506 " 05 00 00 00 02\n"
507 "== 0x2000\n"
508 "08 00 00 00\n"
509 );
510 CHECK_TRACE_CONTENTS(
511 "run: 0x00000001 opcode: ff\n"
512 "run: jump to r/m32\n"
513 "run: effective address is 0x00002000 (EAX)\n"
514 "run: jumping to 0x00000008\n"
515 "run: 0x00000008 opcode: 05\n"
516 );
517 CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
518 }
519
520 :(before "End Op ff Subops")
521 case 4: {
522 trace(Callstack_depth+1, "run") << "jump to r/m32" << end();
523 const int32_t* arg2 = effective_address(modrm);
524 EIP = *arg2;
525 trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
526 break;
527 }
528
529
530
531 :(code)
532 void test_push_mem_at_r32() {
533 Reg[EAX].i = 0x2000;
534 Reg[ESP].u = 0x14;
535 run(
536 "== 0x1\n"
537
538 " ff 30 \n"
539 "== 0x2000\n"
540 "af 00 00 00\n"
541 );
542 CHECK_TRACE_CONTENTS(
543 "run: push r/m32\n"
544 "run: effective address is 0x00002000 (EAX)\n"
545 "run: decrementing ESP to 0x00000010\n"
546 "run: pushing value 0x000000af\n"
547 );
548 }
549
550 :(before "End Op ff Subops")
551 case 6: {
552 trace(Callstack_depth+1, "run") << "push r/m32" << end();
553 const int32_t* val = effective_address(modrm);
554 push(*val);
555 break;
556 }
557
558
559
560 :(before "End Initialize Op Names")
561 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
562
563 :(code)
564 void test_pop_mem_at_r32() {
565 Reg[EAX].i = 0x60;
566 Reg[ESP].u = 0x2000;
567 run(
568 "== 0x1\n"
569
570 " 8f 00 \n"
571
572 "== 0x2000\n"
573 "30 00 00 00\n"
574 );
575 CHECK_TRACE_CONTENTS(
576 "run: pop into r/m32\n"
577 "run: effective address is 0x00000060 (EAX)\n"
578 "run: popping value 0x00000030\n"
579 "run: incrementing ESP to 0x00002004\n"
580 );
581 }
582
583 :(before "End Single-Byte Opcodes")
584 case 0x8f: {
585 const uint8_t modrm = next();
586 const uint8_t subop = (modrm>>3)&0x7;
587 switch (subop) {
588 case 0: {
589 trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
590 int32_t* dest = effective_address(modrm);
591 *dest = pop();
592 break;
593 }
594 }
595 break;
596 }
597
598
599
600 :(code)
601 void test_add_r32_to_mem_at_displacement() {
602 Reg[EBX].i = 0x10;
603 run(
604 "== 0x1\n"
605
606 " 01 1d 00 20 00 00 \n"
607
608 "== 0x2000\n"
609 "01 00 00 00\n"
610 );
611 CHECK_TRACE_CONTENTS(
612 "run: add EBX to r/m32\n"
613 "run: effective address is 0x00002000 (disp32)\n"
614 "run: storing 0x00000011\n"
615 );
616 }
617
618 :(before "End Mod 0 Special-cases(addr)")
619 case 5:
620 addr = next32();
621 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
622 break;
623
624
625
626 :(code)
627 void test_add_r32_to_mem_at_r32_plus_disp8() {
628 Reg[EBX].i = 0x10;
629 Reg[EAX].i = 0x1ffe;
630 run(
631 "== 0x1\n"
632
633 " 01 58 02 \n"
634
635 "== 0x2000\n"
636 "01 00 00 00\n"
637 );
638 CHECK_TRACE_CONTENTS(
639 "run: add EBX to r/m32\n"
640 "run: effective address is initially 0x00001ffe (EAX)\n"
641 "run: effective address is 0x00002000 (after adding disp8)\n"
642 "run: storing 0x00000011\n"
643 );
644 }
645
646 :(before "End Mod Special-cases(addr)")
647 case 1:
648 switch (rm) {
649 default:
650 addr = Reg[rm].u;
651 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
652 break;
653
654 }
655 if (addr > 0) {
656 addr += static_cast<int8_t>(next());
657 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
658 }
659 break;
660
661 :(code)
662 void test_add_r32_to_mem_at_r32_plus_negative_disp8() {
663 Reg[EBX].i = 0x10;
664 Reg[EAX].i = 0x2001;
665 run(
666 "== 0x1\n"
667
668 " 01 58 ff \n"
669
670 "== 0x2000\n"
671 "01 00 00 00\n"
672 );
673 CHECK_TRACE_CONTENTS(
674 "run: add EBX to r/m32\n"
675 "run: effective address is initially 0x00002001 (EAX)\n"
676 "run: effective address is 0x00002000 (after adding disp8)\n"
677 "run: storing 0x00000011\n"
678 );
679 }
680
681
682
683 :(code)
684 void test_add_r32_to_mem_at_r32_plus_disp32() {
685 Reg[EBX].i = 0x10;
686 Reg[EAX].i = 0x1ffe;
687 run(
688 "== 0x1\n"
689
690 " 01 98 02 00 00 00 \n"
691
692 "== 0x2000\n"
693 "01 00 00 00\n"
694 );
695 CHECK_TRACE_CONTENTS(
696 "run: add EBX to r/m32\n"
697 "run: effective address is initially 0x00001ffe (EAX)\n"
698 "run: effective address is 0x00002000 (after adding disp32)\n"
699 "run: storing 0x00000011\n"
700 );
701 }
702
703 :(before "End Mod Special-cases(addr)")
704 case 2:
705 switch (rm) {
706 default:
707 addr = Reg[rm].u;
708 trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
709 break;
710
711 }
712 if (addr > 0) {
713 addr += next32();
714 trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
715 }
716 break;
717
718 :(code)
719 void test_add_r32_to_mem_at_r32_plus_negative_disp32() {
720 Reg[EBX].i = 0x10;
721 Reg[EAX].i = 0x2001;
722 run(
723 "== 0x1\n"
724
725 " 01 98 ff ff ff ff \n"
726
727 "== 0x2000\n"
728 "01 00 00 00\n"
729 );
730 CHECK_TRACE_CONTENTS(
731 "run: add EBX to r/m32\n"
732 "run: effective address is initially 0x00002001 (EAX)\n"
733 "run: effective address is 0x00002000 (after adding disp32)\n"
734 "run: storing 0x00000011\n"
735 );
736 }
737
738
739
740 :(before "End Initialize Op Names")
741 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
742
743 :(code)
744 void test_copy_address() {
745 Reg[EAX].u = 0x2000;
746 run(
747 "== 0x1\n"
748
749 " 8d 18 \n"
750
751 );
752 CHECK_TRACE_CONTENTS(
753 "run: copy address into EBX\n"
754 "run: effective address is 0x00002000 (EAX)\n"
755 );
756 }
757
758 :(before "End Single-Byte Opcodes")
759 case 0x8d: {
760 const uint8_t modrm = next();
761 const uint8_t arg1 = (modrm>>3)&0x7;
762 trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
763 Reg[arg1].u = effective_address_number(modrm);
764 break;
765 }