https://github.com/akkartik/mu/blob/main/linux/bootstrap/013direct_addressing.cc
1
2
3 :(before "End Initialize Op Names")
4 put_new(Name, "01", "add r32 to rm32 (add)");
5
6 :(code)
7 void test_add_r32_to_r32() {
8 Reg[EAX].i = 0x10;
9 Reg[EBX].i = 1;
10 run(
11 "== code 0x1\n"
12
13 " 01 d8 \n"
14
15 );
16 CHECK_TRACE_CONTENTS(
17 "run: add EBX to r/m32\n"
18 "run: r/m32 is EAX\n"
19 "run: storing 0x00000011\n"
20 );
21 }
22
23 :(before "End Single-Byte Opcodes")
24 case 0x01: {
25 uint8_t modrm = next();
26 uint8_t arg2 = (modrm>>3)&0x7;
27 trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end();
28 int32_t* signed_arg1 = effective_address(modrm);
29 int32_t signed_result = *signed_arg1 + Reg[arg2].i;
30 SF = (signed_result < 0);
31 ZF = (signed_result == 0);
32 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + Reg[arg2].i;
33 OF = (signed_result != signed_full_result);
34
35 uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
36 uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u;
37 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + Reg[arg2].u;
38 CF = (unsigned_result != unsigned_full_result);
39 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
40 *signed_arg1 = signed_result;
41 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
42 break;
43 }
44
45 :(code)
46 void test_add_r32_to_r32_signed_overflow() {
47 Reg[EAX].i = INT32_MAX;
48 Reg[EBX].i = 1;
49 run(
50 "== code 0x1\n"
51
52 " 01 d8 \n"
53
54 );
55 CHECK_TRACE_CONTENTS(
56 "run: add EBX to r/m32\n"
57 "run: r/m32 is EAX\n"
58 "run: SF=1; ZF=0; CF=0; OF=1\n"
59 "run: storing 0x80000000\n"
60 );
61 }
62
63 void test_add_r32_to_r32_unsigned_overflow() {
64 Reg[EAX].u = UINT32_MAX;
65 Reg[EBX].u = 1;
66 run(
67 "== code 0x1\n"
68
69 " 01 d8 \n"
70
71 );
72 CHECK_TRACE_CONTENTS(
73 "run: add EBX to r/m32\n"
74 "run: r/m32 is EAX\n"
75 "run: SF=0; ZF=1; CF=1; OF=0\n"
76 "run: storing 0x00000000\n"
77 );
78 }
79
80 void test_add_r32_to_r32_unsigned_and_signed_overflow() {
81 Reg[EAX].i = Reg[EBX].i = INT32_MIN;
82 run(
83 "== code 0x1\n"
84
85 " 01 d8 \n"
86
87 );
88 CHECK_TRACE_CONTENTS(
89 "run: add EBX to r/m32\n"
90 "run: r/m32 is EAX\n"
91 "run: SF=0; ZF=1; CF=1; OF=1\n"
92 "run: storing 0x00000000\n"
93 );
94 }
95
96 :(code)
97
98
99
100
101 int32_t* effective_address(uint8_t modrm) {
102 const uint8_t mod = (modrm>>6);
103
104 const uint8_t rm = modrm & 0x7;
105 if (mod == 3) {
106
107 trace(Callstack_depth+1, "run") << "r/m32 is " << rname(rm) << end();
108 return &Reg[rm].i;
109 }
110 uint32_t addr = effective_address_number(modrm);
111 trace(Callstack_depth+1, "run") << "effective address contains 0x" << HEXWORD << read_mem_i32(addr) << end();
112 return mem_addr_i32(addr);
113 }
114
115
116 uint32_t effective_address_number(uint8_t modrm) {
117 const uint8_t mod = (modrm>>6);
118
119 const uint8_t rm = modrm & 0x7;
120 uint32_t addr = 0;
121 switch (mod) {
122 case 3:
123
124 raise << "unexpected direct addressing mode\n" << end();
125 return 0;
126
127 default:
128 cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
129 exit(1);
130 }
131
132
133 return addr;
134 }
135
136 string rname(uint8_t r) {
137 switch (r) {
138 case 0: return "EAX";
139 case 1: return "ECX";
140 case 2: return "EDX";
141 case 3: return "EBX";
142 case 4: return "ESP";
143 case 5: return "EBP";
144 case 6: return "ESI";
145 case 7: return "EDI";
146 default: raise << "invalid register " << r << '\n' << end(); return "";
147 }
148 }
149
150
151
152 :(before "End Initialize Op Names")
153 put_new(Name, "29", "subtract r32 from rm32 (sub)");
154
155 :(code)
156 void test_subtract_r32_from_r32() {
157 Reg[EAX].i = 10;
158 Reg[EBX].i = 1;
159 run(
160 "== code 0x1\n"
161
162 " 29 d8 \n"
163
164 );
165 CHECK_TRACE_CONTENTS(
166 "run: subtract EBX from r/m32\n"
167 "run: r/m32 is EAX\n"
168 "run: storing 0x00000009\n"
169 );
170 }
171
172 :(before "End Single-Byte Opcodes")
173 case 0x29: {
174 const uint8_t modrm = next();
175 const uint8_t arg2 = (modrm>>3)&0x7;
176 trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
177 int32_t* signed_arg1 = effective_address(modrm);
178 int32_t signed_result = *signed_arg1 - Reg[arg2].i;
179 SF = (signed_result < 0);
180 ZF = (signed_result == 0);
181 int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - Reg[arg2].i;
182 OF = (signed_result != signed_full_result);
183
184 uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
185 uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u;
186 uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - Reg[arg2].u;
187 CF = (unsigned_result != unsigned_full_result);
188 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
189 *signed_arg1 = signed_result;
190 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
191 break;
192 }
193
194 :(code)
195 void test_subtract_r32_from_r32_signed_overflow() {
196 Reg[EAX].i = INT32_MIN;
197 Reg[EBX].i = INT32_MAX;
198 run(
199 "== code 0x1\n"
200
201 " 29 d8 \n"
202
203 );
204 CHECK_TRACE_CONTENTS(
205 "run: subtract EBX from r/m32\n"
206 "run: r/m32 is EAX\n"
207 "run: SF=0; ZF=0; CF=0; OF=1\n"
208 "run: storing 0x00000001\n"
209 );
210 }
211
212 void test_subtract_r32_from_r32_unsigned_overflow() {
213 Reg[EAX].i = 0;
214 Reg[EBX].i = 1;
215 run(
216 "== code 0x1\n"
217
218 " 29 d8 \n"
219
220 );
221 CHECK_TRACE_CONTENTS(
222 "run: subtract EBX from r/m32\n"
223 "run: r/m32 is EAX\n"
224 "run: SF=1; ZF=0; CF=1; OF=0\n"
225 "run: storing 0xffffffff\n"
226 );
227 }
228
229 void test_subtract_r32_from_r32_signed_and_unsigned_overflow() {
230 Reg[EAX].i = 0;
231 Reg[EBX].i = INT32_MIN;
232 run(
233 "== code 0x1\n"
234
235 " 29 d8 \n"
236
237 );
238 CHECK_TRACE_CONTENTS(
239 "run: subtract EBX from r/m32\n"
240 "run: r/m32 is EAX\n"
241 "run: SF=1; ZF=0; CF=1; OF=1\n"
242 "run: storing 0x80000000\n"
243 );
244 }
245
246
247
248 :(before "End Initialize Op Names")
249 put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)");
250
251 :(code)
252 void test_multiply_EAX_by_r32() {
253 Reg[EAX].i = 4;
254 Reg[ECX].i = 3;
255 run(
256 "== code 0x1\n"
257
258 " f7 e1 \n"
259
260 );
261 CHECK_TRACE_CONTENTS(
262 "run: operate on r/m32\n"
263 "run: r/m32 is ECX\n"
264 "run: subop: multiply EAX by r/m32\n"
265 "run: storing 0x0000000c\n"
266 );
267 }
268
269 :(before "End Single-Byte Opcodes")
270 case 0xf7: {
271 const uint8_t modrm = next();
272 trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
273 int32_t* arg1 = effective_address(modrm);
274 const uint8_t subop = (modrm>>3)&0x7;
275 switch (subop) {
276 case 4: {
277 trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end();
278 const uint64_t result = static_cast<uint64_t>(Reg[EAX].u) * static_cast<uint32_t>(*arg1);
279 Reg[EAX].u = result & 0xffffffff;
280 Reg[EDX].u = result >> 32;
281 OF = (Reg[EDX].u != 0);
282 CF = OF;
283 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
284 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
285 break;
286 }
287
288 default:
289 cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n';
290 exit(1);
291 }
292 break;
293 }
294
295
296
297 :(before "End Initialize Op Names")
298 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)");
299
300 :(code)
301 void test_multiply_r32_into_r32() {
302 Reg[EAX].i = 4;
303 Reg[EBX].i = 2;
304 run(
305 "== code 0x1\n"
306
307 " 0f af d8 \n"
308
309 );
310 CHECK_TRACE_CONTENTS(
311 "run: multiply EBX by r/m32\n"
312 "run: r/m32 is EAX\n"
313 "run: storing 0x00000008\n"
314 );
315 }
316
317 :(before "End Two-Byte Opcodes Starting With 0f")
318 case 0xaf: {
319 const uint8_t modrm = next();
320 const uint8_t arg1 = (modrm>>3)&0x7;
321 trace(Callstack_depth+1, "run") << "multiply " << rname(arg1) << " by r/m32" << end();
322 const int32_t* arg2 = effective_address(modrm);
323 int32_t result = Reg[arg1].i * (*arg2);
324 int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2);
325 OF = (result != full_result);
326 CF = OF;
327 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
328 Reg[arg1].i = result;
329 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
330 break;
331 }
332
333
334
335 :(code)
336 void test_negate_r32() {
337 Reg[EBX].i = 1;
338 run(
339 "== code 0x1\n"
340
341 " f7 db \n"
342
343 );
344 CHECK_TRACE_CONTENTS(
345 "run: operate on r/m32\n"
346 "run: r/m32 is EBX\n"
347 "run: subop: negate\n"
348 "run: storing 0xffffffff\n"
349 );
350 }
351
352 :(before "End Op f7 Subops")
353 case 3: {
354 trace(Callstack_depth+1, "run") << "subop: negate" << end();
355
356 if (static_cast<uint32_t>(*arg1) == 0x80000000) {
357 trace(Callstack_depth+1, "run") << "overflow" << end();
358 SF = true;
359 ZF = false;
360 OF = true;
361 break;
362 }
363 int32_t result = -(*arg1);
364 SF = (result >> 31);
365 ZF = (result == 0);
366 OF = false;
367 CF = (*arg1 != 0);
368 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
369 *arg1 = result;
370 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
371 break;
372 }
373
374 :(code)
375
376 void test_negate_can_overflow() {
377 Reg[EBX].i = INT32_MIN;
378 run(
379 "== code 0x1\n"
380
381 " f7 db \n"
382
383 );
384 CHECK_TRACE_CONTENTS(
385 "run: operate on r/m32\n"
386 "run: r/m32 is EBX\n"
387 "run: subop: negate\n"
388 "run: overflow\n"
389 );
390 }
391
392
393
394 void test_divide_EAX_by_rm32() {
395 Reg[EAX].u = 7;
396 Reg[EDX].u = 0;
397 Reg[ECX].i = 3;
398 run(
399 "== code 0x1\n"
400
401 " f7 f9 \n"
402
403 );
404 CHECK_TRACE_CONTENTS(
405 "run: operate on r/m32\n"
406 "run: r/m32 is ECX\n"
407 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
408 "run: quotient: 0x00000002\n"
409 "run: remainder: 0x00000001\n"
410 );
411 }
412
413 :(before "End Op f7 Subops")
414 case 7: {
415 trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end();
416 int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u);
417 int32_t divisor = *arg1;
418 assert(divisor != 0);
419 Reg[EAX].i = dividend/divisor;
420 Reg[EDX].i = dividend%divisor;
421
422 trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end();
423 trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end();
424 break;
425 }
426
427 :(code)
428 void test_divide_EAX_by_negative_rm32() {
429 Reg[EAX].u = 7;
430 Reg[EDX].u = 0;
431 Reg[ECX].i = -3;
432 run(
433 "== code 0x1\n"
434
435 " f7 f9 \n"
436
437 );
438 CHECK_TRACE_CONTENTS(
439 "run: operate on r/m32\n"
440 "run: r/m32 is ECX\n"
441 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
442 "run: quotient: 0xfffffffe\n"
443 "run: remainder: 0x00000001\n"
444 );
445 }
446
447 void test_divide_negative_EAX_by_rm32() {
448 Reg[EAX].i = -7;
449 Reg[EDX].i = -1;
450 Reg[ECX].i = 3;
451 run(
452 "== code 0x1\n"
453
454 " f7 f9 \n"
455
456 );
457 CHECK_TRACE_CONTENTS(
458 "run: operate on r/m32\n"
459 "run: r/m32 is ECX\n"
460 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
461 "run: quotient: 0xfffffffe\n"
462 "run: remainder: 0xffffffff\n"
463 );
464 }
465
466 void test_divide_negative_EDX_EAX_by_rm32() {
467 Reg[EAX].i = 0;
468 Reg[EDX].i = -7;
469 Reg[ECX].i = 0x40000000;
470 run(
471 "== code 0x1\n"
472
473 " f7 f9 \n"
474
475 );
476 CHECK_TRACE_CONTENTS(
477 "run: operate on r/m32\n"
478 "run: r/m32 is ECX\n"
479 "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
480 "run: quotient: 0xffffffe4\n"
481 "run: remainder: 0x00000000\n"
482 );
483 }
484
485
486
487 :(before "End Initialize Op Names")
488 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)");
489
490 :(code)
491 void test_shift_left_r32_with_cl() {
492 Reg[EBX].i = 13;
493 Reg[ECX].i = 1;
494 run(
495 "== code 0x1\n"
496
497 " d3 e3 \n"
498
499 );
500 CHECK_TRACE_CONTENTS(
501 "run: operate on r/m32\n"
502 "run: r/m32 is EBX\n"
503 "run: subop: shift left by CL bits\n"
504 "run: storing 0x0000001a\n"
505 );
506 }
507
508 :(before "End Single-Byte Opcodes")
509 case 0xd3: {
510 const uint8_t modrm = next();
511 trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
512 int32_t* arg1 = effective_address(modrm);
513 const uint8_t subop = (modrm>>3)&0x7;
514 switch (subop) {
515 case 4: {
516 trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
517 uint8_t count = Reg[ECX].u & 0x1f;
518
519 if (count == 1) {
520 bool msb = (*arg1 & 0x80000000) >> 1;
521 bool pnsb = (*arg1 & 0x40000000);
522 OF = (msb != pnsb);
523 }
524 int32_t result = (*arg1 << count);
525 ZF = (result == 0);
526 SF = (result < 0);
527 CF = (*arg1 << (count-1)) & 0x80000000;
528 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
529 *arg1 = result;
530 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
531 break;
532 }
533
534 default:
535 cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n';
536 exit(1);
537 }
538 break;
539 }
540
541
542
543 :(code)
544 void test_shift_right_arithmetic_r32_with_cl() {
545 Reg[EBX].i = 26;
546 Reg[ECX].i = 1;
547 run(
548 "== code 0x1\n"
549
550 " d3 fb \n"
551
552 );
553 CHECK_TRACE_CONTENTS(
554 "run: operate on r/m32\n"
555 "run: r/m32 is EBX\n"
556 "run: subop: shift right by CL bits, while preserving sign\n"
557 "run: storing 0x0000000d\n"
558 );
559 }
560
561 :(before "End Op d3 Subops")
562 case 7: {
563 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
564 uint8_t count = Reg[ECX].u & 0x1f;
565 *arg1 = (*arg1 >> count);
566 ZF = (*arg1 == 0);
567 SF = (*arg1 < 0);
568
569 if (count == 1) OF = false;
570
571 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
572 break;
573 }
574
575 :(code)
576 void test_shift_right_arithmetic_odd_r32_with_cl() {
577 Reg[EBX].i = 27;
578 Reg[ECX].i = 1;
579 run(
580 "== code 0x1\n"
581
582 " d3 fb \n"
583
584 );
585 CHECK_TRACE_CONTENTS(
586 "run: operate on r/m32\n"
587 "run: r/m32 is EBX\n"
588 "run: subop: shift right by CL bits, while preserving sign\n"
589
590 "run: storing 0x0000000d\n"
591 );
592 }
593
594 void test_shift_right_arithmetic_negative_r32_with_cl() {
595 Reg[EBX].i = 0xfffffffd;
596 Reg[ECX].i = 1;
597 run(
598 "== code 0x1\n"
599
600 " d3 fb \n"
601
602 );
603 CHECK_TRACE_CONTENTS(
604 "run: operate on r/m32\n"
605 "run: r/m32 is EBX\n"
606 "run: subop: shift right by CL bits, while preserving sign\n"
607
608 "run: storing 0xfffffffe\n"
609 );
610 }
611
612
613
614 :(code)
615 void test_shift_right_logical_r32_with_cl() {
616 Reg[EBX].i = 26;
617 Reg[ECX].i = 1;
618 run(
619 "== code 0x1\n"
620
621 " d3 eb \n"
622
623 );
624 CHECK_TRACE_CONTENTS(
625 "run: operate on r/m32\n"
626 "run: r/m32 is EBX\n"
627 "run: subop: shift right by CL bits, while padding zeroes\n"
628
629 "run: storing 0x0000000d\n"
630 );
631 }
632
633 :(before "End Op d3 Subops")
634 case 5: {
635 trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
636 uint8_t count = Reg[ECX].u & 0x1f;
637
638 if (count == 1) {
639 bool msb = (*arg1 & 0x80000000) >> 1;
640 bool pnsb = (*arg1 & 0x40000000);
641 OF = (msb != pnsb);
642 }
643 uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
644 *uarg1 = (*uarg1 >> count);
645 ZF = (*uarg1 == 0);
646
647 SF = false;
648
649 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
650 break;
651 }
652
653 :(code)
654 void test_shift_right_logical_odd_r32_with_cl() {
655 Reg[EBX].i = 27;
656 Reg[ECX].i = 1;
657 run(
658 "== code 0x1\n"
659
660 " d3 eb \n"
661
662 );
663 CHECK_TRACE_CONTENTS(
664 "run: operate on r/m32\n"
665 "run: r/m32 is EBX\n"
666 "run: subop: shift right by CL bits, while padding zeroes\n"
667
668 "run: storing 0x0000000d\n"
669 );
670 }
671
672 void test_shift_right_logical_negative_r32_with_cl() {
673 Reg[EBX].i = 0xfffffffd;
674 Reg[ECX].i = 1;
675 run(
676 "== code 0x1\n"
677
678 " d3 eb \n"
679
680 );
681 CHECK_TRACE_CONTENTS(
682 "run: operate on r/m32\n"
683 "run: r/m32 is EBX\n"
684 "run: subop: shift right by CL bits, while padding zeroes\n"
685 "run: storing 0x7ffffffe\n"
686 );
687 }
688
689
690
691 :(before "End Initialize Op Names")
692 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
693
694 :(code)
695 void test_and_r32_with_r32() {
696 Reg[EAX].i = 0x0a0b0c0d;
697 Reg[EBX].i = 0x000000ff;
698 run(
699 "== code 0x1\n"
700
701 " 21 d8 \n"
702
703 );
704 CHECK_TRACE_CONTENTS(
705 "run: and EBX with r/m32\n"
706 "run: r/m32 is EAX\n"
707 "run: storing 0x0000000d\n"
708 );
709 }
710
711 :(before "End Single-Byte Opcodes")
712 case 0x21: {
713 const uint8_t modrm = next();
714 const uint8_t arg2 = (modrm>>3)&0x7;
715 trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end();
716
717
718 int32_t* signed_arg1 = effective_address(modrm);
719 *signed_arg1 &= Reg[arg2].i;
720 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
721 SF = (*signed_arg1 >> 31);
722 ZF = (*signed_arg1 == 0);
723 CF = false;
724 OF = false;
725 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
726 break;
727 }
728
729
730
731 :(before "End Initialize Op Names")
732 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
733
734 :(code)
735 void test_or_r32_with_r32() {
736 Reg[EAX].i = 0x0a0b0c0d;
737 Reg[EBX].i = 0xa0b0c0d0;
738 run(
739 "== code 0x1\n"
740
741 " 09 d8 \n"
742
743 );
744 CHECK_TRACE_CONTENTS(
745 "run: or EBX with r/m32\n"
746 "run: r/m32 is EAX\n"
747 "run: storing 0xaabbccdd\n"
748 );
749 }
750
751 :(before "End Single-Byte Opcodes")
752 case 0x09: {
753 const uint8_t modrm = next();
754 const uint8_t arg2 = (modrm>>3)&0x7;
755 trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end();
756
757
758 int32_t* signed_arg1 = effective_address(modrm);
759 *signed_arg1 |= Reg[arg2].i;
760 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
761 SF = (*signed_arg1 >> 31);
762 ZF = (*signed_arg1 == 0);
763 CF = false;
764 OF = false;
765 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
766 break;
767 }
768
769
770
771 :(before "End Initialize Op Names")
772 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
773
774 :(code)
775 void test_xor_r32_with_r32() {
776 Reg[EAX].i = 0x0a0b0c0d;
777 Reg[EBX].i = 0xaabbc0d0;
778 run(
779 "== code 0x1\n"
780
781 " 31 d8 \n"
782
783 );
784 CHECK_TRACE_CONTENTS(
785 "run: xor EBX with r/m32\n"
786 "run: r/m32 is EAX\n"
787 "run: storing 0xa0b0ccdd\n"
788 );
789 }
790
791 :(before "End Single-Byte Opcodes")
792 case 0x31: {
793 const uint8_t modrm = next();
794 const uint8_t arg2 = (modrm>>3)&0x7;
795 trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end();
796
797
798 int32_t* signed_arg1 = effective_address(modrm);
799 *signed_arg1 ^= Reg[arg2].i;
800 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
801 SF = (*signed_arg1 >> 31);
802 ZF = (*signed_arg1 == 0);
803 CF = false;
804 OF = false;
805 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
806 break;
807 }
808
809
810
811 :(code)
812 void test_not_r32() {
813 Reg[EBX].i = 0x0f0f00ff;
814 run(
815 "== code 0x1\n"
816
817 " f7 d3 \n"
818
819 );
820 CHECK_TRACE_CONTENTS(
821 "run: operate on r/m32\n"
822 "run: r/m32 is EBX\n"
823 "run: subop: not\n"
824 "run: storing 0xf0f0ff00\n"
825 );
826 }
827
828 :(before "End Op f7 Subops")
829 case 2: {
830 trace(Callstack_depth+1, "run") << "subop: not" << end();
831 *arg1 = ~(*arg1);
832 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
833
834 break;
835 }
836
837
838
839 :(before "End Initialize Op Names")
840 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
841
842 :(code)
843 void test_compare_r32_with_r32_greater() {
844 Reg[EAX].i = 0x0a0b0c0d;
845 Reg[EBX].i = 0x0a0b0c07;
846 run(
847 "== code 0x1\n"
848
849 " 39 d8 \n"
850
851 );
852 CHECK_TRACE_CONTENTS(
853 "run: compare r/m32 with EBX\n"
854 "run: r/m32 is EAX\n"
855 "run: SF=0; ZF=0; CF=0; OF=0\n"
856 );
857 }
858
859 :(before "End Single-Byte Opcodes")
860 case 0x39: {
861 const uint8_t modrm = next();
862 const uint8_t reg2 = (modrm>>3)&0x7;
863 trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end();
864 const int32_t* signed_arg1 = effective_address(modrm);
865 const int32_t signed_difference = *signed_arg1 - Reg[reg2].i;
866 SF = (signed_difference < 0);
867 ZF = (signed_difference == 0);
868 const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i;
869 OF = (signed_difference != signed_full_difference);
870
871 const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
872 const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u;
873 const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u;
874 CF = (unsigned_difference != unsigned_full_difference);
875 trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
876 break;
877 }
878
879 :(code)
880 void test_compare_r32_with_r32_lesser_unsigned_and_signed() {
881 Reg[EAX].i = 0x0a0b0c07;
882 Reg[EBX].i = 0x0a0b0c0d;
883 run(
884 "== code 0x1\n"
885
886 " 39 d8 \n"
887
888 );
889 CHECK_TRACE_CONTENTS(
890 "run: compare r/m32 with EBX\n"
891 "run: r/m32 is EAX\n"
892 "run: SF=1; ZF=0; CF=1; OF=0\n"
893 );
894 }
895
896 void test_compare_r32_with_r32_lesser_unsigned_and_signed_due_to_overflow() {
897 Reg[EAX].i = INT32_MAX;
898 Reg[EBX].i = INT32_MIN;
899 run(
900 "== code 0x1\n"
901
902 " 39 d8 \n"
903
904 );
905 CHECK_TRACE_CONTENTS(
906 "run: compare r/m32 with EBX\n"
907 "run: r/m32 is EAX\n"
908 "run: SF=1; ZF=0; CF=1; OF=1\n"
909 );
910 }
911
912 void test_compare_r32_with_r32_lesser_signed() {
913 Reg[EAX].i = -1;
914 Reg[EBX].i = 1;
915 run(
916 "== code 0x1\n"
917
918 " 39 d8 \n"
919
920 );
921 CHECK_TRACE_CONTENTS(
922 "run: compare r/m32 with EBX\n"
923 "run: r/m32 is EAX\n"
924 "run: SF=1; ZF=0; CF=0; OF=0\n"
925 );
926 }
927
928 void test_compare_r32_with_r32_lesser_unsigned() {
929 Reg[EAX].i = 1;
930 Reg[EBX].i = -1;
931 run(
932 "== code 0x1\n"
933
934 " 39 d8 \n"
935
936 );
937 CHECK_TRACE_CONTENTS(
938 "run: compare r/m32 with EBX\n"
939 "run: r/m32 is EAX\n"
940 "run: SF=0; ZF=0; CF=1; OF=0\n"
941 );
942 }
943
944 void test_compare_r32_with_r32_equal() {
945 Reg[EAX].i = 0x0a0b0c0d;
946 Reg[EBX].i = 0x0a0b0c0d;
947 run(
948 "== code 0x1\n"
949
950 " 39 d8 \n"
951
952 );
953 CHECK_TRACE_CONTENTS(
954 "run: compare r/m32 with EBX\n"
955 "run: r/m32 is EAX\n"
956 "run: SF=0; ZF=1; CF=0; OF=0\n"
957 );
958 }
959
960
961
962 :(before "End Initialize Op Names")
963 put_new(Name, "89", "copy r32 to rm32 (mov)");
964
965 :(code)
966 void test_copy_r32_to_r32() {
967 Reg[EBX].i = 0xaf;
968 run(
969 "== code 0x1\n"
970
971 " 89 d8 \n"
972
973 );
974 CHECK_TRACE_CONTENTS(
975 "run: copy EBX to r/m32\n"
976 "run: r/m32 is EAX\n"
977 "run: storing 0x000000af\n"
978 );
979 }
980
981 :(before "End Single-Byte Opcodes")
982 case 0x89: {
983 const uint8_t modrm = next();
984 const uint8_t rsrc = (modrm>>3)&0x7;
985 trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
986 int32_t* dest = effective_address(modrm);
987 *dest = Reg[rsrc].i;
988 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end();
989 break;
990 }
991
992
993
994 :(before "End Initialize Op Names")
995 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
996
997 :(code)
998 void test_xchg_r32_with_r32() {
999 Reg[EBX].i = 0xaf;
1000 Reg[EAX].i = 0x2e;
1001 run(
1002 "== code 0x1\n"
1003
1004 " 87 d8 \n"
1005
1006 );
1007 CHECK_TRACE_CONTENTS(
1008 "run: exchange EBX with r/m32\n"
1009 "run: r/m32 is EAX\n"
1010 "run: storing 0x000000af in r/m32\n"
1011 "run: storing 0x0000002e in EBX\n"
1012 );
1013 }
1014
1015 :(before "End Single-Byte Opcodes")
1016 case 0x87: {
1017 const uint8_t modrm = next();
1018 const uint8_t reg2 = (modrm>>3)&0x7;
1019 trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
1020 int32_t* arg1 = effective_address(modrm);
1021 const int32_t tmp = *arg1;
1022 *arg1 = Reg[reg2].i;
1023 Reg[reg2].i = tmp;
1024 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
1025 trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
1026 break;
1027 }
1028
1029
1030
1031 :(before "End Initialize Op Names")
1032 put_new(Name, "40", "increment EAX (inc)");
1033 put_new(Name, "41", "increment ECX (inc)");
1034 put_new(Name, "42", "increment EDX (inc)");
1035 put_new(Name, "43", "increment EBX (inc)");
1036 put_new(Name, "44", "increment ESP (inc)");
1037 put_new(Name, "45", "increment EBP (inc)");
1038 put_new(Name, "46", "increment ESI (inc)");
1039 put_new(Name, "47", "increment EDI (inc)");
1040
1041 :(code)
1042 void test_increment_r32() {
1043 Reg[ECX].u = 0x1f;
1044 run(
1045 "== code 0x1\n"
1046
1047 " 41 \n"
1048 );
1049 CHECK_TRACE_CONTENTS(
1050 "run: increment ECX\n"
1051 "run: storing value 0x00000020\n"
1052 );
1053 }
1054
1055 :(before "End Single-Byte Opcodes")
1056 case 0x40:
1057 case 0x41:
1058 case 0x42:
1059 case 0x43:
1060 case 0x44:
1061 case 0x45:
1062 case 0x46:
1063 case 0x47: {
1064 const uint8_t reg = op & 0x7;
1065 trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end();
1066 ++Reg[reg].u;
1067 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
1068 break;
1069 }
1070
1071 :(before "End Initialize Op Names")
1072 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)");
1073
1074 :(code)
1075 void test_increment_rm32() {
1076 Reg[EAX].u = 0x20;
1077 run(
1078 "== code 0x1\n"
1079
1080 " ff c0 \n"
1081
1082 );
1083 CHECK_TRACE_CONTENTS(
1084 "run: increment r/m32\n"
1085 "run: r/m32 is EAX\n"
1086 "run: storing value 0x00000021\n"
1087 );
1088 }
1089
1090 :(before "End Single-Byte Opcodes")
1091 case 0xff: {
1092 const uint8_t modrm = next();
1093 const uint8_t subop = (modrm>>3)&0x7;
1094 switch (subop) {
1095 case 0: {
1096 trace(Callstack_depth+1, "run") << "increment r/m32" << end();
1097 int32_t* arg = effective_address(modrm);
1098 ++*arg;
1099 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
1100 break;
1101 }
1102 default:
1103 cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n';
1104 exit(1);
1105
1106 }
1107 break;
1108 }
1109
1110
1111
1112 :(before "End Initialize Op Names")
1113 put_new(Name, "48", "decrement EAX (dec)");
1114 put_new(Name, "49", "decrement ECX (dec)");
1115 put_new(Name, "4a", "decrement EDX (dec)");
1116 put_new(Name, "4b", "decrement EBX (dec)");
1117 put_new(Name, "4c", "decrement ESP (dec)");
1118 put_new(Name, "4d", "decrement EBP (dec)");
1119 put_new(Name, "4e", "decrement ESI (dec)");
1120 put_new(Name, "4f", "decrement EDI (dec)");
1121
1122 :(code)
1123 void test_decrement_r32() {
1124 Reg[ECX].u = 0x1f;
1125 run(
1126 "== code 0x1\n"
1127
1128 " 49 \n"
1129 );
1130 CHECK_TRACE_CONTENTS(
1131 "run: decrement ECX\n"
1132 "run: storing value 0x0000001e\n"
1133 );
1134 }
1135
1136 :(before "End Single-Byte Opcodes")
1137 case 0x48:
1138 case 0x49:
1139 case 0x4a:
1140 case 0x4b:
1141 case 0x4c:
1142 case 0x4d:
1143 case 0x4e:
1144 case 0x4f: {
1145 const uint8_t reg = op & 0x7;
1146 trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end();
1147 --Reg[reg].u;
1148 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
1149 break;
1150 }
1151
1152 :(code)
1153 void test_decrement_rm32() {
1154 Reg[EAX].u = 0x20;
1155 run(
1156 "== code 0x1\n"
1157
1158 " ff c8 \n"
1159
1160 );
1161 CHECK_TRACE_CONTENTS(
1162 "run: decrement r/m32\n"
1163 "run: r/m32 is EAX\n"
1164 "run: storing value 0x0000001f\n"
1165 );
1166 }
1167
1168 :(before "End Op ff Subops")
1169 case 1: {
1170 trace(Callstack_depth+1, "run") << "decrement r/m32" << end();
1171 int32_t* arg = effective_address(modrm);
1172 --*arg;
1173 trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
1174 break;
1175 }
1176
1177
1178
1179 :(before "End Initialize Op Names")
1180 put_new(Name, "50", "push EAX to stack (push)");
1181 put_new(Name, "51", "push ECX to stack (push)");
1182 put_new(Name, "52", "push EDX to stack (push)");
1183 put_new(Name, "53", "push EBX to stack (push)");
1184 put_new(Name, "54", "push ESP to stack (push)");
1185 put_new(Name, "55", "push EBP to stack (push)");
1186 put_new(Name, "56", "push ESI to stack (push)");
1187 put_new(Name, "57", "push EDI to stack (push)");
1188
1189 :(code)
1190 void test_push_r32() {
1191 Mem.push_back(vma(0xbd000000));
1192 Reg[ESP].u = 0xbd000008;
1193 Reg[EBX].i = 0x0000000a;
1194 run(
1195 "== code 0x1\n"
1196
1197 " 53 \n"
1198 );
1199 CHECK_TRACE_CONTENTS(
1200 "run: push EBX\n"
1201 "run: decrementing ESP to 0xbd000004\n"
1202 "run: pushing value 0x0000000a\n"
1203 );
1204 }
1205
1206 :(before "End Single-Byte Opcodes")
1207 case 0x50:
1208 case 0x51:
1209 case 0x52:
1210 case 0x53:
1211 case 0x54:
1212 case 0x55:
1213 case 0x56:
1214 case 0x57: {
1215 uint8_t reg = op & 0x7;
1216 trace(Callstack_depth+1, "run") << "push " << rname(reg) << end();
1217
1218 push(Reg[reg].u);
1219 break;
1220 }
1221
1222
1223
1224 :(before "End Initialize Op Names")
1225 put_new(Name, "58", "pop top of stack to EAX (pop)");
1226 put_new(Name, "59", "pop top of stack to ECX (pop)");
1227 put_new(Name, "5a", "pop top of stack to EDX (pop)");
1228 put_new(Name, "5b", "pop top of stack to EBX (pop)");
1229 put_new(Name, "5c", "pop top of stack to ESP (pop)");
1230 put_new(Name, "5d", "pop top of stack to EBP (pop)");
1231 put_new(Name, "5e", "pop top of stack to ESI (pop)");
1232 put_new(Name, "5f", "pop top of stack to EDI (pop)");
1233
1234 :(code)
1235 void test_pop_r32() {
1236 Mem.push_back(vma(0xbd000000));
1237 Reg[ESP].u = 0xbd000008;
1238 write_mem_i32(0xbd000008, 0x0000000a);
1239 run(
1240 "== code 0x1\n"
1241
1242 " 5b \n"
1243 "== data 0x2000\n"
1244 "0a 00 00 00\n"
1245 );
1246 CHECK_TRACE_CONTENTS(
1247 "run: pop into EBX\n"
1248 "run: popping value 0x0000000a\n"
1249 "run: incrementing ESP to 0xbd00000c\n"
1250 );
1251 }
1252
1253 :(before "End Single-Byte Opcodes")
1254 case 0x58:
1255 case 0x59:
1256 case 0x5a:
1257 case 0x5b:
1258 case 0x5c:
1259 case 0x5d:
1260 case 0x5e:
1261 case 0x5f: {
1262 const uint8_t reg = op & 0x7;
1263 trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end();
1264
1265 Reg[reg].u = pop();
1266
1267 break;
1268 }
1269 :(code)
1270 uint32_t pop() {
1271 const uint32_t result = read_mem_u32(Reg[ESP].u);
1272 trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end();
1273 Reg[ESP].u += 4;
1274 trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
1275 assert(Reg[ESP].u < AFTER_STACK);
1276 return result;
1277 }
1278
1279 :(before "End Includes")
1280 #include <climits>