https://github.com/akkartik/mu/blob/master/subx/015immediate_addressing.cc
  1 //: instructions that (immediately) contain an argument to act with
  2 
  3 :(before "End Initialize Op Names")
  4 put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)");
  5 
  6 :(code)
  7 void test_add_imm32_to_r32() {
  8   Reg[EBX].i = 1;
  9   run(
 10       "== 0x1\n"  // code segment
 11       // op     ModR/M  SIB   displacement  immediate
 12       "  81     c3                          0a 0b 0c 0d\n"  // add 0x0d0c0b0a to EBX
 13       // ModR/M in binary: 11 (direct mode) 000 (add imm32) 011 (dest EBX)
 14   );
 15   CHECK_TRACE_CONTENTS(
 16       "run: combine imm32 with r/m32\n"
 17       "run: r/m32 is EBX\n"
 18       "run: imm32 is 0x0d0c0b0a\n"
 19       "run: subop add\n"
 20       "run: storing 0x0d0c0b0b\n"
 21   );
 22 }
 23 
 24 :(before "End Single-Byte Opcodes")
 25 case 0x81: {  // combine imm32 with r/m32
 26   trace(Callstack_depth+1, "run") << "combine imm32 with r/m32" << end();
 27   const uint8_t modrm = next();
 28   int32_t* arg1 = effective_address(modrm);
 29   const int32_t arg2 = next32();
 30   trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << arg2 << end();
 31   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
 32   switch (subop) {
 33   case 0:
 34     trace(Callstack_depth+1, "run") << "subop add" << end();
 35     BINARY_ARITHMETIC_OP(+, *arg1, arg2);
 36     break;
 37   // End Op 81 Subops
 38   default:
 39     cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n';
 40     exit(1);
 41   }
 42   break;
 43 }
 44 
 45 //:
 46 
 47 :(code)
 48 void test_add_imm32_to_mem_at_r32() {
 49   Reg[EBX].i = 0x2000;
 50   run(
 51       "== 0x1\n"  // code segment
 52       // op     ModR/M  SIB   displacement  immediate
 53       "  81     03                          0a 0b 0c 0d \n"  // add 0x0d0c0b0a to *EBX
 54       // ModR/M in binary: 00 (indirect mode) 000 (add imm32) 011 (dest EBX)
 55       "== 0x2000\n"  // data segment
 56       "01 00 00 00\n"  // 0x00000001
 57   );
 58   CHECK_TRACE_CONTENTS(
 59       "run: combine imm32 with r/m32\n"
 60       "run: effective address is 0x00002000 (EBX)\n"
 61       "run: imm32 is 0x0d0c0b0a\n"
 62       "run: subop add\n"
 63       "run: storing 0x0d0c0b0b\n"
 64   );
 65 }
 66 
 67 //:: subtract
 68 
 69 :(before "End Initialize Op Names")
 70 put_new(Name, "2d", "subtract imm32 from EAX (sub)");
 71 
 72 :(code)
 73 void test_subtract_imm32_from_eax() {
 74   Reg[EAX].i = 0x0d0c0baa;
 75   run(
 76       "== 0x1\n"  // code segment
 77       // op     ModR/M  SIB   displacement  immediate
 78       "  2d                                 0a 0b 0c 0d \n"  // subtract 0x0d0c0b0a from EAX
 79   );
 80   CHECK_TRACE_CONTENTS(
 81       "run: subtract imm32 0x0d0c0b0a from EAX\n"
 82       "run: storing 0x000000a0\n"
 83   );
 84 }
 85 
 86 :(before "End Single-Byte Opcodes")
 87 case 0x2d: {  // subtract imm32 from EAX
 88   const int32_t arg2 = next32();
 89   trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from EAX" << end();
 90   BINARY_ARITHMETIC_OP(-, Reg[EAX].i, arg2);
 91   break;
 92 }
 93 
 94 //:
 95 
 96 :(code)
 97 void test_subtract_imm32_from_mem_at_r32() {
 98   Reg[EBX].i = 0x2000;
 99   run(
100       "== 0x1\n"  // code segment
101       // op     ModR/M  SIB   displacement  immediate
102       "  81     2b                          01 00 00 00 \n"  // subtract 1 from *EBX
103       // ModR/M in binary: 00 (indirect mode) 101 (subtract imm32) 011 (dest EBX)
104       "== 0x2000\n"  // data segment
105       "0a 00 00 00\n"  // 0x0000000a
106   );
107   CHECK_TRACE_CONTENTS(
108       "run: combine imm32 with r/m32\n"
109       "run: effective address is 0x00002000 (EBX)\n"
110       "run: imm32 is 0x00000001\n"
111       "run: subop subtract\n"
112       "run: storing 0x00000009\n"
113   );
114 }
115 
116 :(before "End Op 81 Subops")
117 case 5: {
118   trace(Callstack_depth+1, "run") << "subop subtract" << end();
119   BINARY_ARITHMETIC_OP(-, *arg1, arg2);
120   break;
121 }
122 
123 //:
124 
125 :(code)
126 void test_subtract_imm32_from_r32() {
127   Reg[EBX].i = 10;
128   run(
129       "== 0x1\n"  // code segment
130       // op     ModR/M  SIB   displacement  immediate
131       "  81     eb                          01 00 00 00 \n"  // subtract 1 from EBX
132       // ModR/M in binary: 11 (direct mode) 101 (subtract imm32) 011 (dest EBX)
133   );
134   CHECK_TRACE_CONTENTS(
135       "run: combine imm32 with r/m32\n"
136       "run: r/m32 is EBX\n"
137       "run: imm32 is 0x00000001\n"
138       "run: subop subtract\n"
139       "run: storing 0x00000009\n"
140   );
141 }
142 
143 //:: shift left
144 
145 :(before "End Initialize Op Names")
146 put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)");
147 
148 :(code)
149 void test_shift_left_r32_with_imm8() {
150   Reg[EBX].i = 13;
151   run(
152       "== 0x1\n"  // code segment
153       // op     ModR/M  SIB   displacement  immediate
154       "  c1     e3                          01          \n"  // shift EBX left by 1 bit
155       // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
156   );
157   CHECK_TRACE_CONTENTS(
158       "run: operate on r/m32\n"
159       "run: r/m32 is EBX\n"
160       "run: subop: shift left by CL bits\n"
161       "run: storing 0x0000001a\n"
162   );
163 }
164 
165 :(before "End Single-Byte Opcodes")
166 case 0xc1: {
167   const uint8_t modrm = next();
168   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
169   int32_t* arg1 = effective_address(modrm);
170   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
171   switch (subop) {
172   case 4: {  // shift left r/m32 by CL
173     trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
174     uint8_t count = next() & 0x1f;
175     // OF is only defined if count is 1
176     if (count == 1) {
177       bool msb = (*arg1 & 0x80000000) >> 1;
178       bool pnsb = (*arg1 & 0x40000000);
179       OF = (msb != pnsb);
180     }
181     *arg1 = (*arg1 << count);
182     ZF = (*arg1 == 0);
183     SF = (*arg1 < 0);
184     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
185     break;
186   }
187   // End Op c1 Subops
188   default:
189     cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n';
190     exit(1);
191   }
192   break;
193 }
194 
195 //:: shift right arithmetic
196 
197 :(code)
198 void test_shift_right_arithmetic_r32_with_imm8() {
199   Reg[EBX].i = 26;
200   run(
201       "== 0x1\n"  // code segment
202       // op     ModR/M  SIB   displacement  immediate
203       "  c1     fb                          01          \n"  // shift EBX right by 1 bit
204       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
205   );
206   CHECK_TRACE_CONTENTS(
207       "run: operate on r/m32\n"
208       "run: r/m32 is EBX\n"
209       "run: subop: shift right by CL bits, while preserving sign\n"
210       "run: storing 0x0000000d\n"
211   );
212 }
213 
214 :(before "End Op c1 Subops")
215 case 7: {  // shift right r/m32 by CL, preserving sign
216   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
217   uint8_t count = next() & 0x1f;
218   *arg1 = (*arg1 >> count);
219   ZF = (*arg1 == 0);
220   SF = (*arg1 < 0);
221   // OF is only defined if count is 1
222   if (count == 1) OF = false;
223   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
224   break;
225 }
226 
227 :(code)
228 void test_shift_right_arithmetic_odd_r32_with_imm8() {
229   Reg[EBX].i = 27;
230   run(
231       "== 0x1\n"  // code segment
232       // op     ModR/M  SIB   displacement  immediate
233       "  c1     fb                          01          \n"  // shift EBX right by 1 bit
234       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
235   );
236   CHECK_TRACE_CONTENTS(
237       "run: operate on r/m32\n"
238       "run: r/m32 is EBX\n"
239       "run: subop: shift right by CL bits, while preserving sign\n"
240       // result: 13
241       "run: storing 0x0000000d\n"
242   );
243 }
244 
245 :(code)
246 void test_shift_right_arithmetic_negative_r32_with_imm8() {
247   Reg[EBX].i = 0xfffffffd;  // -3
248   run(
249       "== 0x1\n"  // code segment
250       // op     ModR/M  SIB   displacement  immediate
251       "  c1     fb                          01          \n"  // shift EBX right by 1 bit, while preserving sign
252       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
253   );
254   CHECK_TRACE_CONTENTS(
255       "run: operate on r/m32\n"
256       "run: r/m32 is EBX\n"
257       "run: subop: shift right by CL bits, while preserving sign\n"
258       // result: -2
259       "run: storing 0xfffffffe\n"
260   );
261 }
262 
263 //:: shift right logical
264 
265 :(code)
266 void test_shift_right_logical_r32_with_imm8() {
267   Reg[EBX].i = 26;
268   run(
269       "== 0x1\n"  // code segment
270       // op     ModR/M  SIB   displacement  immediate
271       "  c1     eb                          01          \n"  // shift EBX right by 1 bit, while padding zeroes
272       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
273   );
274   CHECK_TRACE_CONTENTS(
275       "run: operate on r/m32\n"
276       "run: r/m32 is EBX\n"
277       "run: subop: shift right by CL bits, while padding zeroes\n"
278       "run: storing 0x0000000d\n"
279   );
280 }
281 
282 :(before "End Op c1 Subops")
283 case 5: {  // shift right r/m32 by CL, preserving sign
284   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
285   uint8_t count = next() & 0x1f;
286   // OF is only defined if count is 1
287   if (count == 1) {
288     bool msb = (*arg1 & 0x80000000) >> 1;
289     bool pnsb = (*arg1 & 0x40000000);
290     OF = (msb != pnsb);
291   }
292   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
293   *uarg1 = (*uarg1 >> count);
294   ZF = (*uarg1 == 0);
295   // result is always positive by definition
296   SF = false;
297   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
298   break;
299 }
300 
301 :(code)
302 void test_shift_right_logical_odd_r32_with_imm8() {
303   Reg[EBX].i = 27;
304   run(
305       "== 0x1\n"  // code segment
306       // op     ModR/M  SIB   displacement  immediate
307       "  c1     eb                          01          \n"  // shift EBX right by 1 bit, while padding zeroes
308   );
309   CHECK_TRACE_CONTENTS(
310       "run: operate on r/m32\n"
311       "run: r/m32 is EBX\n"
312       "run: subop: shift right by CL bits, while padding zeroes\n"
313       // result: 13
314       "run: storing 0x0000000d\n"
315   );
316 }
317 
318 :(code)
319 void test_shift_right_logical_negative_r32_with_imm8() {
320   Reg[EBX].i = 0xfffffffd;
321   run(
322       "== 0x1\n"  // code segment
323       // op     ModR/M  SIB   displacement  immediate
324       "  c1     eb                          01          \n"  // shift EBX right by 1 bit, while padding zeroes
325       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
326   );
327   CHECK_TRACE_CONTENTS(
328       "run: operate on r/m32\n"
329       "run: r/m32 is EBX\n"
330       "run: subop: shift right by CL bits, while padding zeroes\n"
331       "run: storing 0x7ffffffe\n"
332   );
333 }
334 
335 //:: and
336 
337 :(before "End Initialize Op Names")
338 put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)");
339 
340 :(code)
341 void test_and_imm32_with_eax() {
342   Reg[EAX].i = 0xff;
343   run(
344       "== 0x1\n"  // code segment
345       // op     ModR/M  SIB   displacement  immediate
346       "  25                                 0a 0b 0c 0d \n"  // and 0x0d0c0b0a with EAX
347   );
348   CHECK_TRACE_CONTENTS(
349       "run: and imm32 0x0d0c0b0a with EAX\n"
350       "run: storing 0x0000000a\n"
351   );
352 }
353 
354 :(before "End Single-Byte Opcodes")
355 case 0x25: {  // and imm32 with EAX
356   const int32_t arg2 = next32();
357   trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
358   BINARY_BITWISE_OP(&, Reg[EAX].i, arg2);
359   break;
360 }
361 
362 //:
363 
364 :(code)
365 void test_and_imm32_with_mem_at_r32() {
366   Reg[EBX].i = 0x2000;
367   run(
368       "== 0x1\n"  // code segment
369       // op     ModR/M  SIB   displacement  immediate
370       "  81     23                          0a 0b 0c 0d \n"  // and 0x0d0c0b0a with *EBX
371       // ModR/M in binary: 00 (indirect mode) 100 (and imm32) 011 (dest EBX)
372       "== 0x2000\n"  // data segment
373       "ff 00 00 00\n"  // 0x000000ff
374   );
375   CHECK_TRACE_CONTENTS(
376       "run: combine imm32 with r/m32\n"
377       "run: effective address is 0x00002000 (EBX)\n"
378       "run: imm32 is 0x0d0c0b0a\n"
379       "run: subop and\n"
380       "run: storing 0x0000000a\n"
381   );
382 }
383 
384 :(before "End Op 81 Subops")
385 case 4: {
386   trace(Callstack_depth+1, "run") << "subop and" << end();
387   BINARY_BITWISE_OP(&, *arg1, arg2);
388   break;
389 }
390 
391 //:
392 
393 :(code)
394 void test_and_imm32_with_r32() {
395   Reg[EBX].i = 0xff;
396   run(
397       "== 0x1\n"  // code segment
398       // op     ModR/M  SIB   displacement  immediate
399       "  81     e3                          0a 0b 0c 0d \n"  // and 0x0d0c0b0a with EBX
400       // ModR/M in binary: 11 (direct mode) 100 (and imm32) 011 (dest EBX)
401   );
402   CHECK_TRACE_CONTENTS(
403       "run: combine imm32 with r/m32\n"
404       "run: r/m32 is EBX\n"
405       "run: imm32 is 0x0d0c0b0a\n"
406       "run: subop and\n"
407       "run: storing 0x0000000a\n"
408   );
409 }
410 
411 //:: or
412 
413 :(before "End Initialize Op Names")
414 put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)");
415 
416 :(code)
417 void test_or_imm32_with_eax() {
418   Reg[EAX].i = 0xd0c0b0a0;
419   run(
420       "== 0x1\n"  // code segment
421       // op     ModR/M  SIB   displacement  immediate
422       "  0d                                 0a 0b 0c 0d \n"  // or 0x0d0c0b0a with EAX
423   );
424   CHECK_TRACE_CONTENTS(
425       "run: or imm32 0x0d0c0b0a with EAX\n"
426       "run: storing 0xddccbbaa\n"
427   );
428 }
429 
430 :(before "End Single-Byte Opcodes")
431 case 0x0d: {  // or imm32 with EAX
432   const int32_t arg2 = next32();
433   trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
434   BINARY_BITWISE_OP(|, Reg[EAX].i, arg2);
435   break;
436 }
437 
438 //:
439 
440 :(code)
441 void test_or_imm32_with_mem_at_r32() {
442   Reg[EBX].i = 0x2000;
443   run(
444       "== 0x1\n"  // code segment
445       // op     ModR/M  SIB   displacement  immediate
446       "  81     0b                          0a 0b 0c 0d \n"  // or 0x0d0c0b0a with *EBX
447       // ModR/M in binary: 00 (indirect mode) 001 (or imm32) 011 (dest EBX)
448       "== 0x2000\n"  // data segment
449       "a0 b0 c0 d0\n"  // 0xd0c0b0a0
450   );
451   CHECK_TRACE_CONTENTS(
452       "run: combine imm32 with r/m32\n"
453       "run: effective address is 0x00002000 (EBX)\n"
454       "run: imm32 is 0x0d0c0b0a\n"
455       "run: subop or\n"
456       "run: storing 0xddccbbaa\n"
457   );
458 }
459 
460 :(before "End Op 81 Subops")
461 case 1: {
462   trace(Callstack_depth+1, "run") << "subop or" << end();
463   BINARY_BITWISE_OP(|, *arg1, arg2);
464   break;
465 }
466 
467 :(code)
468 void test_or_imm32_with_r32() {
469   Reg[EBX].i = 0xd0c0b0a0;
470   run(
471       "== 0x1\n"  // code segment
472       // op     ModR/M  SIB   displacement  immediate
473       "  81     cb                          0a 0b 0c 0d \n"  // or 0x0d0c0b0a with EBX
474       // ModR/M in binary: 11 (direct mode) 001 (or imm32) 011 (dest EBX)
475   );
476   CHECK_TRACE_CONTENTS(
477       "run: combine imm32 with r/m32\n"
478       "run: r/m32 is EBX\n"
479       "run: imm32 is 0x0d0c0b0a\n"
480       "run: subop or\n"
481       "run: storing 0xddccbbaa\n"
482   );
483 }
484 
485 //:: xor
486 
487 :(before "End Initialize Op Names")
488 put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)");
489 
490 :(code)
491 void test_xor_imm32_with_eax() {
492   Reg[EAX].i = 0xddccb0a0;
493   run(
494       "== 0x1\n"  // code segment
495       // op     ModR/M  SIB   displacement  immediate
496       "  35                                 0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with EAX
497   );
498   CHECK_TRACE_CONTENTS(
499       "run: xor imm32 0x0d0c0b0a with EAX\n"
500       "run: storing 0xd0c0bbaa\n"
501   );
502 }
503 
504 :(before "End Single-Byte Opcodes")
505 case 0x35: {  // xor imm32 with EAX
506   const int32_t arg2 = next32();
507   trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
508   BINARY_BITWISE_OP(^, Reg[EAX].i, arg2);
509   break;
510 }
511 
512 //:
513 
514 :(code)
515 void test_xor_imm32_with_mem_at_r32() {
516   Reg[EBX].i = 0x2000;
517   run(
518       "== 0x1\n"  // code segment
519       // op     ModR/M  SIB   displacement  immediate
520       "  81     33                          0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with *EBX
521       // ModR/M in binary: 00 (indirect mode) 110 (xor imm32) 011 (dest EBX)
522       "== 0x2000\n"  // data segment
523       "a0 b0 c0 d0\n"  // 0xd0c0b0a0
524   );
525   CHECK_TRACE_CONTENTS(
526       "run: combine imm32 with r/m32\n"
527       "run: effective address is 0x00002000 (EBX)\n"
528       "run: imm32 is 0x0d0c0b0a\n"
529       "run: subop xor\n"
530       "run: storing 0xddccbbaa\n"
531   );
532 }
533 
534 :(before "End Op 81 Subops")
535 case 6: {
536   trace(Callstack_depth+1, "run") << "subop xor" << end();
537   BINARY_BITWISE_OP(^, *arg1, arg2);
538   break;
539 }
540 
541 :(code)
542 void test_xor_imm32_with_r32() {
543   Reg[EBX].i = 0xd0c0b0a0;
544   run(
545       "== 0x1\n"  // code segment
546       // op     ModR/M  SIB   displacement  immediate
547       "  81     f3                          0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with EBX
548       // ModR/M in binary: 11 (direct mode) 110 (xor imm32) 011 (dest EBX)
549   );
550   CHECK_TRACE_CONTENTS(
551       "run: combine imm32 with r/m32\n"
552       "run: r/m32 is EBX\n"
553       "run: imm32 is 0x0d0c0b0a\n"
554       "run: subop xor\n"
555       "run: storing 0xddccbbaa\n"
556   );
557 }
558 
559 //:: compare (cmp)
560 
561 :(before "End Initialize Op Names")
562 put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)");
563 
564 :(code)
565 void test_compare_imm32_with_eax_greater() {
566   Reg[EAX].i = 0x0d0c0b0a;
567   run(
568       "== 0x1\n"  // code segment
569       // op     ModR/M  SIB   displacement  immediate
570       "  3d                                 07 0b 0c 0d \n"  // compare 0x0d0c0b07 with EAX
571   );
572   CHECK_TRACE_CONTENTS(
573       "run: compare EAX and imm32 0x0d0c0b07\n"
574       "run: SF=0; ZF=0; OF=0\n"
575   );
576 }
577 
578 :(before "End Single-Byte Opcodes")
579 case 0x3d: {  // compare EAX with imm32
580   const int32_t arg1 = Reg[EAX].i;
581   const int32_t arg2 = next32();
582   trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << arg2 << end();
583   const int32_t tmp1 = arg1 - arg2;
584   SF = (tmp1 < 0);
585   ZF = (tmp1 == 0);
586   const int64_t tmp2 = arg1 - arg2;
587   OF = (tmp1 != tmp2);
588   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
589   break;
590 }
591 
592 :(code)
593 void test_compare_imm32_with_eax_lesser() {
594   Reg[EAX].i = 0x0d0c0b07;
595   run(
596       "== 0x1\n"  // code segment
597       // op     ModR/M  SIB   displacement  immediate
598       "  3d                                 0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with EAX
599   );
600   CHECK_TRACE_CONTENTS(
601       "run: compare EAX and imm32 0x0d0c0b0a\n"
602       "run: SF=1; ZF=0; OF=0\n"
603   );
604 }
605 
606 :(code)
607 void test_compare_imm32_with_eax_equal() {
608   Reg[EAX].i = 0x0d0c0b0a;
609   run(
610       "== 0x1\n"  // code segment
611       // op     ModR/M  SIB   displacement  immediate
612       "  3d                                 0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with EAX
613   );
614   CHECK_TRACE_CONTENTS(
615       "run: compare EAX and imm32 0x0d0c0b0a\n"
616       "run: SF=0; ZF=1; OF=0\n"
617   );
618 }
619 
620 //:
621 
622 :(code)
623 void test_compare_imm32_with_r32_greater() {
624   Reg[EBX].i = 0x0d0c0b0a;
625   run(
626       "== 0x1\n"  // code segment
627       // op     ModR/M  SIB   displacement  immediate
628       "  81     fb                          07 0b 0c 0d \n"  // compare 0x0d0c0b07 with EBX
629       // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX)
630   );
631   CHECK_TRACE_CONTENTS(
632       "run: combine imm32 with r/m32\n"
633       "run: r/m32 is EBX\n"
634       "run: imm32 is 0x0d0c0b07\n"
635       "run: SF=0; ZF=0; OF=0\n"
636   );
637 }
638 
639 :(before "End Op 81 Subops")
640 case 7: {
641   trace(Callstack_depth+1, "run") << "subop compare" << end();
642   const int32_t tmp1 = *arg1 - arg2;
643   SF = (tmp1 < 0);
644   ZF = (tmp1 == 0);
645   const int64_t tmp2 = *arg1 - arg2;
646   OF = (tmp1 != tmp2);
647   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
648   break;
649 }
650 
651 :(code)
652 void test_compare_imm32_with_r32_lesser() {
653   Reg[EBX].i = 0x0d0c0b07;
654   run(
655       "== 0x1\n"  // code segment
656       // op     ModR/M  SIB   displacement  immediate
657       "  81     fb                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with EBX
658       // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX)
659   );
660   CHECK_TRACE_CONTENTS(
661       "run: combine imm32 with r/m32\n"
662       "run: r/m32 is EBX\n"
663       "run: imm32 is 0x0d0c0b0a\n"
664       "run: SF=1; ZF=0; OF=0\n"
665   );
666 }
667 
668 :(code)
669 void test_compare_imm32_with_r32_equal() {
670   Reg[EBX].i = 0x0d0c0b0a;
671   run(
672       "== 0x1\n"  // code segment
673       // op     ModR/M  SIB   displacement  immediate
674       "  81     fb                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with EBX
675       // ModR/M in binary: 11 (direct mode) 111 (compare imm32) 011 (dest EBX)
676   );
677   CHECK_TRACE_CONTENTS(
678       "run: combine imm32 with r/m32\n"
679       "run: r/m32 is EBX\n"
680       "run: imm32 is 0x0d0c0b0a\n"
681       "run: SF=0; ZF=1; OF=0\n"
682   );
683 }
684 
685 :(code)
686 void test_compare_imm32_with_mem_at_r32_greater() {
687   Reg[EBX].i = 0x2000;
688   run(
689       "== 0x1\n"  // code segment
690       // op     ModR/M  SIB   displacement  immediate
691       "  81     3b                          07 0b 0c 0d \n"  // compare 0x0d0c0b07 with *EBX
692       // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX)
693       "== 0x2000\n"  // data segment
694       "0a 0b 0c 0d\n"  // 0x0d0c0b0a
695   );
696   CHECK_TRACE_CONTENTS(
697       "run: combine imm32 with r/m32\n"
698       "run: effective address is 0x00002000 (EBX)\n"
699       "run: imm32 is 0x0d0c0b07\n"
700       "run: SF=0; ZF=0; OF=0\n"
701   );
702 }
703 
704 :(code)
705 void test_compare_imm32_with_mem_at_r32_lesser() {
706   Reg[EBX].i = 0x2000;
707   run(
708       "== 0x1\n"  // code segment
709       // op     ModR/M  SIB   displacement  immediate
710       "  81     3b                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with *EBX
711       // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX)
712       "== 0x2000\n"  // data segment
713       "07 0b 0c 0d\n"  // 0x0d0c0b07
714   );
715   CHECK_TRACE_CONTENTS(
716       "run: combine imm32 with r/m32\n"
717       "run: effective address is 0x00002000 (EBX)\n"
718       "run: imm32 is 0x0d0c0b0a\n"
719       "run: SF=1; ZF=0; OF=0\n"
720   );
721 }
722 
723 :(code)
724 void test_compare_imm32_with_mem_at_r32_equal() {
725   Reg[EBX].i = 0x0d0c0b0a;
726   Reg[EBX].i = 0x2000;
727   run(
728       "== 0x1\n"  // code segment
729       // op     ModR/M  SIB   displacement  immediate
730       "  81     3b                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with *EBX
731       // ModR/M in binary: 00 (indirect mode) 111 (compare imm32) 011 (dest EBX)
732       "== 0x2000\n"  // data segment
733       "0a 0b 0c 0d\n"  // 0x0d0c0b0a
734   );
735   CHECK_TRACE_CONTENTS(
736       "run: combine imm32 with r/m32\n"
737       "run: effective address is 0x00002000 (EBX)\n"
738       "run: imm32 is 0x0d0c0b0a\n"
739       "run: SF=0; ZF=1; OF=0\n"
740   );
741 }
742 
743 //:: copy (mov)
744 
745 :(before "End Initialize Op Names")
746 put_new(Name, "b8", "copy imm32 to EAX (mov)");
747 put_new(Name, "b9", "copy imm32 to ECX (mov)");
748 put_new(Name, "ba", "copy imm32 to EDX (mov)");
749 put_new(Name, "bb", "copy imm32 to EBX (mov)");
750 put_new(Name, "bc", "copy imm32 to ESP (mov)");
751 put_new(Name, "bd", "copy imm32 to EBP (mov)");
752 put_new(Name, "be", "copy imm32 to ESI (mov)");
753 put_new(Name, "bf", "copy imm32 to EDI (mov)");
754 
755 :(code)
756 void test_copy_imm32_to_r32() {
757   run(
758       "== 0x1\n"  // code segment
759       // op     ModR/M  SIB   displacement  immediate
760       "  bb                                 0a 0b 0c 0d \n"  // copy 0x0d0c0b0a to EBX
761   );
762   CHECK_TRACE_CONTENTS(
763       "run: copy imm32 0x0d0c0b0a to EBX\n"
764   );
765 }
766 
767 :(before "End Single-Byte Opcodes")
768 case 0xb8:
769 case 0xb9:
770 case 0xba:
771 case 0xbb:
772 case 0xbc:
773 case 0xbd:
774 case 0xbe:
775 case 0xbf: {  // copy imm32 to r32
776   const uint8_t rdest = op & 0x7;
777   const int32_t src = next32();
778   trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end();
779   Reg[rdest].i = src;
780   break;
781 }
782 
783 //:
784 
785 :(before "End Initialize Op Names")
786 put_new(Name, "c7", "copy imm32 to rm32 (mov)");
787 
788 :(code)
789 void test_copy_imm32_to_mem_at_r32() {
790   Reg[EBX].i = 0x60;
791   run(
792       "== 0x1\n"  // code segment
793       // op     ModR/M  SIB   displacement  immediate
794       "  c7     03                          0a 0b 0c 0d \n"  // copy 0x0d0c0b0a to *EBX
795       // ModR/M in binary: 00 (indirect mode) 000 (unused) 011 (dest EBX)
796   );
797   CHECK_TRACE_CONTENTS(
798       "run: copy imm32 to r/m32\n"
799       "run: effective address is 0x00000060 (EBX)\n"
800       "run: imm32 is 0x0d0c0b0a\n"
801   );
802 }
803 
804 :(before "End Single-Byte Opcodes")
805 case 0xc7: {  // copy imm32 to r32
806   const uint8_t modrm = next();
807   trace(Callstack_depth+1, "run") << "copy imm32 to r/m32" << end();
808   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
809   if (subop != 0) {
810     cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n";
811     exit(1);
812   }
813   int32_t* dest = effective_address(modrm);
814   const int32_t src = next32();
815   trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end();
816   *dest = src;
817   break;
818 }
819 
820 //:: push
821 
822 :(before "End Initialize Op Names")
823 put_new(Name, "68", "push imm32 to stack (push)");
824 
825 :(code)
826 void test_push_imm32() {
827   Reg[ESP].u = 0x14;
828   run(
829       "== 0x1\n"  // code segment
830       // op     ModR/M  SIB   displacement  immediate
831       "  68                                 af 00 00 00 \n"  // push *EAX to stack
832   );
833   CHECK_TRACE_CONTENTS(
834       "run: push imm32 0x000000af\n"
835       "run: ESP is now 0x00000010\n"
836       "run: contents at ESP: 0x000000af\n"
837   );
838 }
839 
840 :(before "End Single-Byte Opcodes")
841 case 0x68: {
842   const uint32_t val = static_cast<uint32_t>(next32());
843   trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end();
844 //?   cerr << "push: " << val << " => " << Reg[ESP].u << '\n';
845   push(val);
846   trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end();
847   trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end();
848   break;
849 }