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 }