https://github.com/akkartik/mu/blob/main/linux/bootstrap/015immediate_addressing.cc

       1 //: instructions that (immediately) contain an argument to act with
       2
       3 :(before "End Initialize Op Names")
       4 put_new(Name, "05", "add imm32 to EAX (add)");
       5
       6 :(before "End Single-Byte Opcodes")
       7 case 0x05: {  // add imm32 to EAX
       8   int32_t signed_arg2 = next32();
       9   trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << signed_arg2 << " to EAX" << end();
      10   int32_t signed_result = Reg[EAX].i + signed_arg2;
      11   SF = (signed_result < 0);
      12   ZF = (signed_result == 0);
      13   int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) + signed_arg2;
      14   OF = (signed_result != signed_full_result);
      15   // set CF
      16   uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
      17   uint32_t unsigned_result = Reg[EAX].u + unsigned_arg2;
      18   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) + unsigned_arg2;
      19   CF = (unsigned_result != unsigned_full_result);
      20   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
      21   Reg[EAX].i = signed_result;
      22   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end();
      23   break;
      24 }
      25
      26 :(code)
      27 void test_add_imm32_to_EAX_signed_overflow() {
      28   Reg[EAX].i = INT32_MAX;
      29   run(
      30       "== code 0x1\n"
      31       // op     ModR/M  SIB   displacement  immediate
      32       "  05                                 01 00 00 00 \n" // add 1 to EAX
      33   );
      34   CHECK_TRACE_CONTENTS(
      35       "run: add imm32 0x00000001 to EAX\n"
      36       "run: SF=1; ZF=0; CF=0; OF=1\n"
      37       "run: storing 0x80000000\n"
      38   );
      39 }
      40
      41 void test_add_imm32_to_EAX_unsigned_overflow() {
      42   Reg[EAX].u = UINT32_MAX;
      43   Reg[EBX].u = 1;
      44   run(
      45       "== code 0x1\n"
      46       // op     ModR/M  SIB   displacement  immediate
      47       "  05                                 01 00 00 00 \n" // add 1 to EAX
      48   );
      49   CHECK_TRACE_CONTENTS(
      50       "run: add imm32 0x00000001 to EAX\n"
      51       "run: SF=0; ZF=1; CF=1; OF=0\n"
      52       "run: storing 0x00000000\n"
      53   );
      54 }
      55
      56 void test_add_imm32_to_EAX_unsigned_and_signed_overflow() {
      57   Reg[EAX].i = INT32_MIN;
      58   run(
      59       "== code 0x1\n"
      60       // op     ModR/M  SIB   displacement  immediate
      61       "  05                                 00 00 00 80 \n" // add 0x80000000 to EAX
      62   );
      63   CHECK_TRACE_CONTENTS(
      64       "run: add imm32 0x80000000 to EAX\n"
      65       "run: SF=0; ZF=1; CF=1; OF=1\n"
      66       "run: storing 0x00000000\n"
      67   );
      68 }
      69
      70 //:
      71
      72 :(before "End Initialize Op Names")
      73 put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/cmp)");
      74
      75 :(code)
      76 void test_add_imm32_to_r32() {
      77   Reg[EBX].i = 1;
      78   run(
      79       "== code 0x1\n"
      80       // op     ModR/M  SIB   displacement  immediate
      81       "  81     c3                          0a 0b 0c 0d\n"  // add 0x0d0c0b0a to EBX
      82       // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX)
      83   );
      84   CHECK_TRACE_CONTENTS(
      85       "run: combine r/m32 with imm32\n"
      86       "run: r/m32 is EBX\n"
      87       "run: imm32 is 0x0d0c0b0a\n"
      88       "run: subop add\n"
      89       "run: storing 0x0d0c0b0b\n"
      90   );
      91 }
      92
      93 :(before "End Single-Byte Opcodes")
      94 case 0x81: {  // combine r/m32 with imm32
      95   trace(Callstack_depth+1, "run") << "combine r/m32 with imm32" << end();
      96   const uint8_t modrm = next();
      97   int32_t* signed_arg1 = effective_address(modrm);
      98   const int32_t signed_arg2 = next32();
      99   trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << signed_arg2 << end();
     100   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
     101   switch (subop) {
     102   case 0: {
     103     trace(Callstack_depth+1, "run") << "subop add" << end();
     104     int32_t signed_result = *signed_arg1 + signed_arg2;
     105     SF = (signed_result < 0);
     106     ZF = (signed_result == 0);
     107     int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + signed_arg2;
     108     OF = (signed_result != signed_full_result);
     109     // set CF
     110     uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
     111     uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
     112     uint32_t unsigned_result = unsigned_arg1 + unsigned_arg2;
     113     uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + unsigned_arg2;
     114     CF = (unsigned_result != unsigned_full_result);
     115     trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     116     *signed_arg1 = signed_result;
     117     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     118     break;
     119   }
     120   // End Op 81 Subops
     121   default:
     122     cerr << "unrecognized subop for opcode 81: " << NUM(subop) << '\n';
     123     exit(1);
     124   }
     125   break;
     126 }
     127
     128 :(code)
     129 void test_add_imm32_to_r32_signed_overflow() {
     130   Reg[EBX].i = INT32_MAX;
     131   run(
     132       "== code 0x1\n"
     133       // op     ModR/M  SIB   displacement  immediate
     134       "  81     c3                          01 00 00 00\n"  // add 1 to EBX
     135       // ModR/M in binary: 11 (direct mode) 000 (subop add) 011 (dest EBX)
     136   );
     137   CHECK_TRACE_CONTENTS(
     138       "run: combine r/m32 with imm32\n"
     139       "run: r/m32 is EBX\n"
     140       "run: imm32 is 0x00000001\n"
     141       "run: subop add\n"
     142       "run: SF=1; ZF=0; CF=0; OF=1\n"
     143       "run: storing 0x80000000\n"
     144   );
     145 }
     146
     147 void test_add_imm32_to_r32_unsigned_overflow() {
     148   Reg[EBX].u = UINT32_MAX;
     149   run(
     150       "== code 0x1\n"
     151       // op     ModR/M  SIB   displacement  immediate
     152       "  81     c3                          01 00 00 00\n"  // add 1 to EBX
     153       // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX)
     154   );
     155   CHECK_TRACE_CONTENTS(
     156       "run: combine r/m32 with imm32\n"
     157       "run: r/m32 is EBX\n"
     158       "run: imm32 is 0x00000001\n"
     159       "run: subop add\n"
     160       "run: SF=0; ZF=1; CF=1; OF=0\n"
     161       "run: storing 0x00000000\n"
     162   );
     163 }
     164
     165 void test_add_imm32_to_r32_unsigned_and_signed_overflow() {
     166   Reg[EBX].i = INT32_MIN;
     167   run(
     168       "== code 0x1\n"
     169       // op     ModR/M  SIB   displacement  immediate
     170       "  81     c3                          00 00 00 80\n"  // add 0x80000000 to EBX
     171       // ModR/M in binary: 11 (direct mode) 011 (subop add) 011 (dest EBX)
     172   );
     173   CHECK_TRACE_CONTENTS(
     174       "run: combine r/m32 with imm32\n"
     175       "run: r/m32 is EBX\n"
     176       "run: imm32 is 0x80000000\n"
     177       "run: subop add\n"
     178       "run: SF=0; ZF=1; CF=1; OF=1\n"
     179       "run: storing 0x00000000\n"
     180   );
     181 }
     182
     183 //:
     184
     185 :(code)
     186 void test_add_imm32_to_mem_at_rm32() {
     187   Reg[EBX].i = 0x2000;
     188   run(
     189       "== code 0x1\n"
     190       // op     ModR/M  SIB   displacement  immediate
     191       "  81     03                          0a 0b 0c 0d \n"  // add 0x0d0c0b0a to *EBX
     192       // ModR/M in binary: 00 (indirect mode) 000 (subop add) 011 (dest EBX)
     193       "== data 0x2000\n"
     194       "01 00 00 00\n"  // 1
     195   );
     196   CHECK_TRACE_CONTENTS(
     197       "run: combine r/m32 with imm32\n"
     198       "run: effective address is 0x00002000 (EBX)\n"
     199       "run: imm32 is 0x0d0c0b0a\n"
     200       "run: subop add\n"
     201       "run: storing 0x0d0c0b0b\n"
     202   );
     203 }
     204
     205 //:: subtract
     206
     207 :(before "End Initialize Op Names")
     208 put_new(Name, "2d", "subtract imm32 from EAX (sub)");
     209
     210 :(code)
     211 void test_subtract_imm32_from_EAX() {
     212   Reg[EAX].i = 0x0d0c0baa;
     213   run(
     214       "== code 0x1\n"
     215       // op     ModR/M  SIB   displacement  immediate
     216       "  2d                                 0a 0b 0c 0d \n"  // subtract 0x0d0c0b0a from EAX
     217   );
     218   CHECK_TRACE_CONTENTS(
     219       "run: subtract imm32 0x0d0c0b0a from EAX\n"
     220       "run: storing 0x000000a0\n"
     221   );
     222 }
     223
     224 :(before "End Single-Byte Opcodes")
     225 case 0x2d: {  // subtract imm32 from EAX
     226   const int32_t signed_arg2 = next32();
     227   trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << signed_arg2 << " from EAX" << end();
     228   int32_t signed_result = Reg[EAX].i - signed_arg2;
     229   SF = (signed_result < 0);
     230   ZF = (signed_result == 0);
     231   int64_t signed_full_result = static_cast<int64_t>(Reg[EAX].i) - signed_arg2;
     232   OF = (signed_result != signed_full_result);
     233   // set CF
     234   uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
     235   uint32_t unsigned_result = Reg[EAX].u - unsigned_arg2;
     236   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[EAX].u) - unsigned_arg2;
     237   CF = (unsigned_result != unsigned_full_result);
     238   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     239   Reg[EAX].i = signed_result;
     240   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end();
     241   break;
     242 }
     243
     244 :(code)
     245 void test_subtract_imm32_from_EAX_signed_overflow() {
     246   Reg[EAX].i = INT32_MIN;
     247   run(
     248       "== code 0x1\n"
     249       // op     ModR/M  SIB   displacement  immediate
     250       "  2d                                 01 00 00 00 \n"  // subtract 1 from EAX
     251   );
     252   CHECK_TRACE_CONTENTS(
     253       "run: subtract imm32 0x00000001 from EAX\n"
     254       "run: SF=0; ZF=0; CF=0; OF=1\n"
     255       "run: storing 0x7fffffff\n"  // INT32_MAX
     256   );
     257 }
     258
     259 void test_subtract_imm32_from_EAX_unsigned_overflow() {
     260   Reg[EAX].i = 0;
     261   run(
     262       "== code 0x1\n"
     263       // op     ModR/M  SIB   displacement  immediate
     264       "  2d                                 01 00 00 00 \n"  // subtract 1 from EAX
     265   );
     266   CHECK_TRACE_CONTENTS(
     267       "run: subtract imm32 0x00000001 from EAX\n"
     268       "run: SF=1; ZF=0; CF=1; OF=0\n"
     269       "run: storing 0xffffffff\n"
     270   );
     271 }
     272
     273 void test_subtract_imm32_from_EAX_signed_and_unsigned_overflow() {
     274   Reg[EAX].i = 0;
     275   run(
     276       "== code 0x1\n"
     277       // op     ModR/M  SIB   displacement  immediate
     278       "  2d                                 00 00 00 80 \n"  // subtract INT32_MIN from EAX
     279   );
     280   CHECK_TRACE_CONTENTS(
     281       "run: subtract imm32 0x80000000 from EAX\n"
     282       "run: SF=1; ZF=0; CF=1; OF=1\n"
     283       "run: storing 0x80000000\n"
     284   );
     285 }
     286
     287 //:
     288
     289 void test_subtract_imm32_from_mem_at_rm32() {
     290   Reg[EBX].i = 0x2000;
     291   run(
     292       "== code 0x1\n"
     293       // op     ModR/M  SIB   displacement  immediate
     294       "  81     2b                          01 00 00 00 \n"  // subtract 1 from *EBX
     295       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
     296       "== data 0x2000\n"
     297       "0a 00 00 00\n"  // 0xa
     298   );
     299   CHECK_TRACE_CONTENTS(
     300       "run: combine r/m32 with imm32\n"
     301       "run: effective address is 0x00002000 (EBX)\n"
     302       "run: imm32 is 0x00000001\n"
     303       "run: subop subtract\n"
     304       "run: storing 0x00000009\n"
     305   );
     306 }
     307
     308 :(before "End Op 81 Subops")
     309 case 5: {
     310   trace(Callstack_depth+1, "run") << "subop subtract" << end();
     311   int32_t signed_result = *signed_arg1 - signed_arg2;
     312   SF = (signed_result < 0);
     313   ZF = (signed_result == 0);
     314   int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - signed_arg2;
     315   OF = (signed_result != signed_full_result);
     316   // set CF
     317   uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
     318   uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
     319   uint32_t unsigned_result = unsigned_arg1 - unsigned_arg2;
     320   uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2;
     321   CF = (unsigned_result != unsigned_full_result);
     322   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     323   *signed_arg1 = signed_result;
     324   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     325   break;
     326 }
     327
     328 :(code)
     329 void test_subtract_imm32_from_mem_at_rm32_signed_overflow() {
     330   Reg[EBX].i = 0x2000;
     331   run(
     332       "== code 0x1\n"
     333       // op     ModR/M  SIB   displacement  immediate
     334       "  81     2b                          ff ff ff 7f \n"  // subtract INT32_MAX from *EBX
     335       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
     336       "== data 0x2000\n"
     337       "00 00 00 80\n"  // INT32_MIN
     338   );
     339   CHECK_TRACE_CONTENTS(
     340       "run: combine r/m32 with imm32\n"
     341       "run: effective address is 0x00002000 (EBX)\n"
     342       "run: effective address contains 0x80000000\n"
     343       "run: imm32 is 0x7fffffff\n"
     344       "run: subop subtract\n"
     345       "run: SF=0; ZF=0; CF=0; OF=1\n"
     346       "run: storing 0x00000001\n"
     347   );
     348 }
     349
     350 void test_subtract_imm32_from_mem_at_rm32_unsigned_overflow() {
     351   Reg[EBX].i = 0x2000;
     352   run(
     353       "== code 0x1\n"
     354       // op     ModR/M  SIB   displacement  immediate
     355       "  81     2b                          01 00 00 00 \n"  // subtract 1 from *EBX
     356       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
     357       "== data 0x2000\n"
     358       "00 00 00 00\n"  // 0
     359   );
     360   CHECK_TRACE_CONTENTS(
     361       "run: combine r/m32 with imm32\n"
     362       "run: effective address is 0x00002000 (EBX)\n"
     363       "run: effective address contains 0x00000000\n"
     364       "run: imm32 is 0x00000001\n"
     365       "run: subop subtract\n"
     366       "run: SF=1; ZF=0; CF=1; OF=0\n"
     367       "run: storing 0xffffffff\n"
     368   );
     369 }
     370
     371 void test_subtract_imm32_from_mem_at_rm32_signed_and_unsigned_overflow() {
     372   Reg[EBX].i = 0x2000;
     373   run(
     374       "== code 0x1\n"
     375       // op     ModR/M  SIB   displacement  immediate
     376       "  81     2b                          00 00 00 80 \n"  // subtract INT32_MIN from *EBX
     377       // ModR/M in binary: 00 (indirect mode) 101 (subop subtract) 011 (dest EBX)
     378       "== data 0x2000\n"
     379       "00 00 00 00\n"  // 0
     380   );
     381   CHECK_TRACE_CONTENTS(
     382       "run: combine r/m32 with imm32\n"
     383       "run: effective address is 0x00002000 (EBX)\n"
     384       "run: effective address contains 0x00000000\n"
     385       "run: imm32 is 0x80000000\n"
     386       "run: subop subtract\n"
     387       "run: SF=1; ZF=0; CF=1; OF=1\n"
     388       "run: storing 0x80000000\n"
     389   );
     390 }
     391
     392 //:
     393
     394 void test_subtract_imm32_from_r32() {
     395   Reg[EBX].i = 10;
     396   run(
     397       "== code 0x1\n"
     398       // op     ModR/M  SIB   displacement  immediate
     399       "  81     eb                          01 00 00 00 \n"  // subtract 1 from EBX
     400       // ModR/M in binary: 11 (direct mode) 101 (subop subtract) 011 (dest EBX)
     401   );
     402   CHECK_TRACE_CONTENTS(
     403       "run: combine r/m32 with imm32\n"
     404       "run: r/m32 is EBX\n"
     405       "run: imm32 is 0x00000001\n"
     406       "run: subop subtract\n"
     407       "run: storing 0x00000009\n"
     408   );
     409 }
     410
     411 //:: shift left
     412
     413 :(before "End Initialize Op Names")
     414 put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr)");
     415
     416 :(code)
     417 void test_shift_left_r32_with_imm8() {
     418   Reg[EBX].i = 13;
     419   run(
     420       "== code 0x1\n"
     421       // op     ModR/M  SIB   displacement  immediate
     422       "  c1     e3                          01          \n"  // shift EBX left by 1 bit
     423       // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
     424   );
     425   CHECK_TRACE_CONTENTS(
     426       "run: operate on r/m32\n"
     427       "run: r/m32 is EBX\n"
     428       "run: subop: shift left by CL bits\n"
     429       "run: storing 0x0000001a\n"
     430   );
     431 }
     432
     433 :(before "End Single-Byte Opcodes")
     434 case 0xc1: {
     435   const uint8_t modrm = next();
     436   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
     437   int32_t* arg1 = effective_address(modrm);
     438   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
     439   switch (subop) {
     440   case 4: {  // shift left r/m32 by CL
     441     trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
     442     uint8_t count = next() & 0x1f;
     443     // OF is only defined if count is 1
     444     if (count == 1) {
     445       bool msb = (*arg1 & 0x80000000) >> 1;
     446       bool pnsb = (*arg1 & 0x40000000);
     447       OF = (msb != pnsb);
     448     }
     449     *arg1 = (*arg1 << count);
     450     ZF = (*arg1 == 0);
     451     SF = (*arg1 < 0);
     452     // CF undefined
     453     trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     454     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     455     break;
     456   }
     457   // End Op c1 Subops
     458   default:
     459     cerr << "unrecognized subop for opcode c1: " << NUM(subop) << '\n';
     460     exit(1);
     461   }
     462   break;
     463 }
     464
     465 //:: shift right arithmetic
     466
     467 :(code)
     468 void test_shift_right_arithmetic_r32_with_imm8() {
     469   Reg[EBX].i = 26;
     470   run(
     471       "== code 0x1\n"
     472       // op     ModR/M  SIB   displacement  immediate
     473       "  c1     fb                          01          \n"  // shift EBX right by 1 bit
     474       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
     475   );
     476   CHECK_TRACE_CONTENTS(
     477       "run: operate on r/m32\n"
     478       "run: r/m32 is EBX\n"
     479       "run: subop: shift right by CL bits, while preserving sign\n"
     480       "run: storing 0x0000000d\n"
     481   );
     482 }
     483
     484 :(before "End Op c1 Subops")
     485 case 7: {  // shift right r/m32 by CL, preserving sign
     486   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
     487   uint8_t count = next() & 0x1f;
     488   int32_t result = (*arg1 >> count);
     489   ZF = (*arg1 == 0);
     490   SF = (*arg1 < 0);
     491   // OF is only defined if count is 1
     492   if (count == 1) OF = false;
     493   // CF
     494   CF = ((*arg1 >> (count-1)) & 0x1);
     495   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     496   *arg1 = result;
     497   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     498   break;
     499 }
     500
     501 :(code)
     502 void test_shift_right_arithmetic_odd_r32_with_imm8() {
     503   Reg[EBX].i = 27;
     504   run(
     505       "== code 0x1\n"
     506       // op     ModR/M  SIB   displacement  immediate
     507       "  c1     fb                          01          \n"  // shift EBX right by 1 bit
     508       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
     509   );
     510   CHECK_TRACE_CONTENTS(
     511       "run: operate on r/m32\n"
     512       "run: r/m32 is EBX\n"
     513       "run: subop: shift right by CL bits, while preserving sign\n"
     514       // result: 13
     515       "run: storing 0x0000000d\n"
     516   );
     517 }
     518
     519 :(code)
     520 void test_shift_right_arithmetic_negative_r32_with_imm8() {
     521   Reg[EBX].i = 0xfffffffd;  // -3
     522   run(
     523       "== code 0x1\n"
     524       // op     ModR/M  SIB   displacement  immediate
     525       "  c1     fb                          01          \n"  // shift EBX right by 1 bit, while preserving sign
     526       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
     527   );
     528   CHECK_TRACE_CONTENTS(
     529       "run: operate on r/m32\n"
     530       "run: r/m32 is EBX\n"
     531       "run: subop: shift right by CL bits, while preserving sign\n"
     532       // result: -2
     533       "run: storing 0xfffffffe\n"
     534   );
     535 }
     536
     537 //:: shift right logical
     538
     539 :(code)
     540 void test_shift_right_logical_r32_with_imm8() {
     541   Reg[EBX].i = 26;
     542   run(
     543       "== code 0x1\n"
     544       // op     ModR/M  SIB   displacement  immediate
     545       "  c1     eb                          01          \n"  // shift EBX right by 1 bit, while padding zeroes
     546       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
     547   );
     548   CHECK_TRACE_CONTENTS(
     549       "run: operate on r/m32\n"
     550       "run: r/m32 is EBX\n"
     551       "run: subop: shift right by CL bits, while padding zeroes\n"
     552       "run: storing 0x0000000d\n"
     553   );
     554 }
     555
     556 :(before "End Op c1 Subops")
     557 case 5: {  // shift right r/m32 by CL, preserving sign
     558   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
     559   uint8_t count = next() & 0x1f;
     560   // OF is only defined if count is 1
     561   if (count == 1) {
     562     bool msb = (*arg1 & 0x80000000) >> 1;
     563     bool pnsb = (*arg1 & 0x40000000);
     564     OF = (msb != pnsb);
     565   }
     566   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
     567   *uarg1 = (*uarg1 >> count);
     568   ZF = (*uarg1 == 0);
     569   // result is always positive by definition
     570   SF = false;
     571   // CF undefined
     572   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     573   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     574   break;
     575 }
     576
     577 :(code)
     578 void test_shift_right_logical_odd_r32_with_imm8() {
     579   Reg[EBX].i = 27;
     580   run(
     581       "== code 0x1\n"
     582       // op     ModR/M  SIB   displacement  immediate
     583       "  c1     eb                          01          \n"  // shift EBX right by 1 bit, while padding zeroes
     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 padding zeroes\n"
     589       // result: 13
     590       "run: storing 0x0000000d\n"
     591   );
     592 }
     593
     594 :(code)
     595 void test_shift_right_logical_negative_r32_with_imm8() {
     596   Reg[EBX].i = 0xfffffffd;
     597   run(
     598       "== code 0x1\n"
     599       // op     ModR/M  SIB   displacement  immediate
     600       "  c1     eb                          01          \n"  // shift EBX right by 1 bit, while padding zeroes
     601       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
     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 padding zeroes\n"
     607       "run: storing 0x7ffffffe\n"
     608   );
     609 }
     610
     611 //:: and
     612
     613 :(before "End Initialize Op Names")
     614 put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)");
     615
     616 :(code)
     617 void test_and_EAX_with_imm32() {
     618   Reg[EAX].i = 0xff;
     619   run(
     620       "== code 0x1\n"
     621       // op     ModR/M  SIB   displacement  immediate
     622       "  25                                 0a 0b 0c 0d \n"  // and 0x0d0c0b0a with EAX
     623   );
     624   CHECK_TRACE_CONTENTS(
     625       "run: and imm32 0x0d0c0b0a with EAX\n"
     626       "run: storing 0x0000000a\n"
     627   );
     628 }
     629
     630 :(before "End Single-Byte Opcodes")
     631 case 0x25: {  // and imm32 with EAX
     632   // bitwise ops technically operate on unsigned numbers, but it makes no
     633   // difference
     634   const int32_t signed_arg2 = next32();
     635   trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end();
     636   Reg[EAX].i &= signed_arg2;
     637   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end();
     638   SF = (Reg[EAX].i >> 31);
     639   ZF = (Reg[EAX].i == 0);
     640   CF = false;
     641   OF = false;
     642   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     643   break;
     644 }
     645
     646 //:
     647
     648 :(code)
     649 void test_and_imm32_with_mem_at_rm32() {
     650   Reg[EBX].i = 0x2000;
     651   run(
     652       "== code 0x1\n"
     653       // op     ModR/M  SIB   displacement  immediate
     654       "  81     23                          0a 0b 0c 0d \n"  // and 0x0d0c0b0a with *EBX
     655       // ModR/M in binary: 00 (indirect mode) 100 (subop and) 011 (dest EBX)
     656       "== data 0x2000\n"
     657       "ff 00 00 00\n"  // 0xff
     658   );
     659   CHECK_TRACE_CONTENTS(
     660       "run: combine r/m32 with imm32\n"
     661       "run: effective address is 0x00002000 (EBX)\n"
     662       "run: imm32 is 0x0d0c0b0a\n"
     663       "run: subop and\n"
     664       "run: storing 0x0000000a\n"
     665   );
     666 }
     667
     668 :(before "End Op 81 Subops")
     669 case 4: {
     670   trace(Callstack_depth+1, "run") << "subop and" << end();
     671   // bitwise ops technically operate on unsigned numbers, but it makes no
     672   // difference
     673   *signed_arg1 &= signed_arg2;
     674   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     675   SF = (*signed_arg1 >> 31);
     676   ZF = (*signed_arg1 == 0);
     677   CF = false;
     678   OF = false;
     679   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     680   break;
     681 }
     682
     683 //:
     684
     685 :(code)
     686 void test_and_imm32_with_r32() {
     687   Reg[EBX].i = 0xff;
     688   run(
     689       "== code 0x1\n"
     690       // op     ModR/M  SIB   displacement  immediate
     691       "  81     e3                          0a 0b 0c 0d \n"  // and 0x0d0c0b0a with EBX
     692       // ModR/M in binary: 11 (direct mode) 100 (subop and) 011 (dest EBX)
     693   );
     694   CHECK_TRACE_CONTENTS(
     695       "run: combine r/m32 with imm32\n"
     696       "run: r/m32 is EBX\n"
     697       "run: imm32 is 0x0d0c0b0a\n"
     698       "run: subop and\n"
     699       "run: storing 0x0000000a\n"
     700   );
     701 }
     702
     703 //:: or
     704
     705 :(before "End Initialize Op Names")
     706 put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)");
     707
     708 :(code)
     709 void test_or_EAX_with_imm32() {
     710   Reg[EAX].i = 0xd0c0b0a0;
     711   run(
     712       "== code 0x1\n"
     713       // op     ModR/M  SIB   displacement  immediate
     714       "  0d                                 0a 0b 0c 0d \n"  // or 0x0d0c0b0a with EAX
     715   );
     716   CHECK_TRACE_CONTENTS(
     717       "run: or imm32 0x0d0c0b0a with EAX\n"
     718       "run: storing 0xddccbbaa\n"
     719   );
     720 }
     721
     722 :(before "End Single-Byte Opcodes")
     723 case 0x0d: {  // or imm32 with EAX
     724   // bitwise ops technically operate on unsigned numbers, but it makes no
     725   // difference
     726   const int32_t signed_arg2 = next32();
     727   trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end();
     728   Reg[EAX].i |= signed_arg2;
     729   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end();
     730   SF = (Reg[EAX].i >> 31);
     731   ZF = (Reg[EAX].i == 0);
     732   CF = false;
     733   OF = false;
     734   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     735   break;
     736 }
     737
     738 //:
     739
     740 :(code)
     741 void test_or_imm32_with_mem_at_rm32() {
     742   Reg[EBX].i = 0x2000;
     743   run(
     744       "== code 0x1\n"
     745       // op     ModR/M  SIB   displacement  immediate
     746       "  81     0b                          0a 0b 0c 0d \n"  // or 0x0d0c0b0a with *EBX
     747       // ModR/M in binary: 00 (indirect mode) 001 (subop or) 011 (dest EBX)
     748       "== data 0x2000\n"
     749       "a0 b0 c0 d0\n"  // 0xd0c0b0a0
     750   );
     751   CHECK_TRACE_CONTENTS(
     752       "run: combine r/m32 with imm32\n"
     753       "run: effective address is 0x00002000 (EBX)\n"
     754       "run: imm32 is 0x0d0c0b0a\n"
     755       "run: subop or\n"
     756       "run: storing 0xddccbbaa\n"
     757   );
     758 }
     759
     760 :(before "End Op 81 Subops")
     761 case 1: {
     762   trace(Callstack_depth+1, "run") << "subop or" << end();
     763   // bitwise ops technically operate on unsigned numbers, but it makes no
     764   // difference
     765   *signed_arg1 |= signed_arg2;
     766   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     767   SF = (*signed_arg1 >> 31);
     768   ZF = (*signed_arg1 == 0);
     769   CF = false;
     770   OF = false;
     771   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     772   break;
     773 }
     774
     775 :(code)
     776 void test_or_imm32_with_r32() {
     777   Reg[EBX].i = 0xd0c0b0a0;
     778   run(
     779       "== code 0x1\n"
     780       // op     ModR/M  SIB   displacement  immediate
     781       "  81     cb                          0a 0b 0c 0d \n"  // or 0x0d0c0b0a with EBX
     782       // ModR/M in binary: 11 (direct mode) 001 (subop or) 011 (dest EBX)
     783   );
     784   CHECK_TRACE_CONTENTS(
     785       "run: combine r/m32 with imm32\n"
     786       "run: r/m32 is EBX\n"
     787       "run: imm32 is 0x0d0c0b0a\n"
     788       "run: subop or\n"
     789       "run: storing 0xddccbbaa\n"
     790   );
     791 }
     792
     793 //:: xor
     794
     795 :(before "End Initialize Op Names")
     796 put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)");
     797
     798 :(code)
     799 void test_xor_EAX_with_imm32() {
     800   Reg[EAX].i = 0xddccb0a0;
     801   run(
     802       "== code 0x1\n"
     803       // op     ModR/M  SIB   displacement  immediate
     804       "  35                                 0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with EAX
     805   );
     806   CHECK_TRACE_CONTENTS(
     807       "run: xor imm32 0x0d0c0b0a with EAX\n"
     808       "run: storing 0xd0c0bbaa\n"
     809   );
     810 }
     811
     812 :(before "End Single-Byte Opcodes")
     813 case 0x35: {  // xor imm32 with EAX
     814   // bitwise ops technically operate on unsigned numbers, but it makes no
     815   // difference
     816   const int32_t signed_arg2 = next32();
     817   trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << signed_arg2 << " with EAX" << end();
     818   Reg[EAX].i ^= signed_arg2;
     819   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].i << end();
     820   SF = (Reg[EAX].i >> 31);
     821   ZF = (Reg[EAX].i == 0);
     822   CF = false;
     823   OF = false;
     824   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     825   break;
     826 }
     827
     828 //:
     829
     830 :(code)
     831 void test_xor_imm32_with_mem_at_rm32() {
     832   Reg[EBX].i = 0x2000;
     833   run(
     834       "== code 0x1\n"
     835       // op     ModR/M  SIB   displacement  immediate
     836       "  81     33                          0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with *EBX
     837       // ModR/M in binary: 00 (indirect mode) 110 (subop xor) 011 (dest EBX)
     838       "== data 0x2000\n"
     839       "a0 b0 c0 d0\n"  // 0xd0c0b0a0
     840   );
     841   CHECK_TRACE_CONTENTS(
     842       "run: combine r/m32 with imm32\n"
     843       "run: effective address is 0x00002000 (EBX)\n"
     844       "run: imm32 is 0x0d0c0b0a\n"
     845       "run: subop xor\n"
     846       "run: storing 0xddccbbaa\n"
     847   );
     848 }
     849
     850 :(before "End Op 81 Subops")
     851 case 6: {
     852   trace(Callstack_depth+1, "run") << "subop xor" << end();
     853   // bitwise ops technically operate on unsigned numbers, but it makes no
     854   // difference
     855   *signed_arg1 ^= signed_arg2;
     856   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     857   SF = (*signed_arg1 >> 31);
     858   ZF = (*signed_arg1 == 0);
     859   CF = false;
     860   OF = false;
     861   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     862   break;
     863 }
     864
     865 :(code)
     866 void test_xor_imm32_with_r32() {
     867   Reg[EBX].i = 0xd0c0b0a0;
     868   run(
     869       "== code 0x1\n"
     870       // op     ModR/M  SIB   displacement  immediate
     871       "  81     f3                          0a 0b 0c 0d \n"  // xor 0x0d0c0b0a with EBX
     872       // ModR/M in binary: 11 (direct mode) 110 (subop xor) 011 (dest EBX)
     873   );
     874   CHECK_TRACE_CONTENTS(
     875       "run: combine r/m32 with imm32\n"
     876       "run: r/m32 is EBX\n"
     877       "run: imm32 is 0x0d0c0b0a\n"
     878       "run: subop xor\n"
     879       "run: storing 0xddccbbaa\n"
     880   );
     881 }
     882
     883 //:: compare (cmp)
     884
     885 :(before "End Initialize Op Names")
     886 put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)");
     887
     888 :(code)
     889 void test_compare_EAX_with_imm32_greater() {
     890   Reg[EAX].i = 0x0d0c0b0a;
     891   run(
     892       "== code 0x1\n"
     893       // op     ModR/M  SIB   displacement  immediate
     894       "  3d                                 07 0b 0c 0d \n"  // compare EAX with 0x0d0c0b07
     895   );
     896   CHECK_TRACE_CONTENTS(
     897       "run: compare EAX with imm32 0x0d0c0b07\n"
     898       "run: SF=0; ZF=0; CF=0; OF=0\n"
     899   );
     900 }
     901
     902 :(before "End Single-Byte Opcodes")
     903 case 0x3d: {  // compare EAX with imm32
     904   const int32_t signed_arg1 = Reg[EAX].i;
     905   const int32_t signed_arg2 = next32();
     906   trace(Callstack_depth+1, "run") << "compare EAX with imm32 0x" << HEXWORD << signed_arg2 << end();
     907   const int32_t signed_difference = signed_arg1 - signed_arg2;
     908   SF = (signed_difference < 0);
     909   ZF = (signed_difference == 0);
     910   const int64_t full_signed_difference = static_cast<int64_t>(signed_arg1) - signed_arg2;
     911   OF = (signed_difference != full_signed_difference);
     912   const uint32_t unsigned_arg1 = static_cast<uint32_t>(signed_arg1);
     913   const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
     914   const uint32_t unsigned_difference = unsigned_arg1 - unsigned_arg2;
     915   const uint64_t full_unsigned_difference = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2;
     916   CF = (unsigned_difference != full_unsigned_difference);
     917   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     918   break;
     919 }
     920
     921 :(code)
     922 void test_compare_EAX_with_imm32_lesser_unsigned_and_signed() {
     923   Reg[EAX].i = 0x0a0b0c07;
     924   run(
     925       "== code 0x1\n"
     926       // op     ModR/M  SIB   displacement  immediate
     927       "  3d                                 0d 0c 0b 0a \n"  // compare EAX with imm32
     928   );
     929   CHECK_TRACE_CONTENTS(
     930       "run: compare EAX with imm32 0x0a0b0c0d\n"
     931       "run: SF=1; ZF=0; CF=1; OF=0\n"
     932   );
     933 }
     934
     935 void test_compare_EAX_with_imm32_lesser_unsigned_and_signed_due_to_overflow() {
     936   Reg[EAX].i = INT32_MAX;
     937   run(
     938       "== code 0x1\n"
     939       // op     ModR/M  SIB   displacement  immediate
     940       "  3d                                 00 00 00 80\n"  // compare EAX with INT32_MIN
     941   );
     942   CHECK_TRACE_CONTENTS(
     943       "run: compare EAX with imm32 0x80000000\n"
     944       "run: SF=1; ZF=0; CF=1; OF=1\n"
     945   );
     946 }
     947
     948 void test_compare_EAX_with_imm32_lesser_signed() {
     949   Reg[EAX].i = -1;
     950   run(
     951       "== code 0x1\n"
     952       // op     ModR/M  SIB   displacement  immediate
     953       "  3d                                 01 00 00 00\n"  // compare EAX with 1
     954   );
     955   CHECK_TRACE_CONTENTS(
     956       "run: compare EAX with imm32 0x00000001\n"
     957       "run: SF=1; ZF=0; CF=0; OF=0\n"
     958   );
     959 }
     960
     961 void test_compare_EAX_with_imm32_lesser_unsigned() {
     962   Reg[EAX].i = 1;
     963   run(
     964       "== code 0x1\n"
     965       // op     ModR/M  SIB   displacement  immediate
     966       "  3d                                 ff ff ff ff\n"  // compare EAX with -1
     967   );
     968   CHECK_TRACE_CONTENTS(
     969       "run: compare EAX with imm32 0xffffffff\n"
     970       "run: SF=0; ZF=0; CF=1; OF=0\n"
     971   );
     972 }
     973
     974 void test_compare_EAX_with_imm32_equal() {
     975   Reg[EAX].i = 0x0d0c0b0a;
     976   run(
     977       "== code 0x1\n"
     978       // op     ModR/M  SIB   displacement  immediate
     979       "  3d                                 0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with EAX
     980   );
     981   CHECK_TRACE_CONTENTS(
     982       "run: compare EAX with imm32 0x0d0c0b0a\n"
     983       "run: SF=0; ZF=1; CF=0; OF=0\n"
     984   );
     985 }
     986
     987 //:
     988
     989 void test_compare_imm32_with_r32_greater() {
     990   Reg[EBX].i = 0x0d0c0b0a;
     991   run(
     992       "== code 0x1\n"
     993       // op     ModR/M  SIB   displacement  immediate
     994       "  81     fb                          07 0b 0c 0d \n"  // compare 0x0d0c0b07 with EBX
     995       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX)
     996   );
     997   CHECK_TRACE_CONTENTS(
     998       "run: combine r/m32 with imm32\n"
     999       "run: r/m32 is EBX\n"
    1000       "run: imm32 is 0x0d0c0b07\n"
    1001       "run: SF=0; ZF=0; CF=0; OF=0\n"
    1002   );
    1003 }
    1004
    1005 :(before "End Op 81 Subops")
    1006 case 7: {
    1007   trace(Callstack_depth+1, "run") << "subop compare" << end();
    1008   const int32_t tmp1 = *signed_arg1 - signed_arg2;
    1009   SF = (tmp1 < 0);
    1010   ZF = (tmp1 == 0);
    1011   const int64_t tmp2 = static_cast<int64_t>(*signed_arg1) - signed_arg2;
    1012   OF = (tmp1 != tmp2);
    1013   const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
    1014   const uint32_t unsigned_arg2 = static_cast<uint32_t>(signed_arg2);
    1015   const uint32_t tmp3 = unsigned_arg1 - unsigned_arg2;
    1016   const uint64_t tmp4 = static_cast<uint64_t>(unsigned_arg1) - unsigned_arg2;
    1017   CF = (tmp3 != tmp4);
    1018   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
    1019   break;
    1020 }
    1021
    1022 :(code)
    1023 void test_compare_rm32_with_imm32_lesser_unsigned_and_signed() {
    1024   Reg[EAX].i = 0x0a0b0c07;
    1025   run(
    1026       "== code 0x1\n"
    1027       // op     ModR/M  SIB   displacement  immediate
    1028       "  81     f8                          0d 0c 0b 0a \n"  // compare EAX with imm32
    1029       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
    1030   );
    1031   CHECK_TRACE_CONTENTS(
    1032       "run: combine r/m32 with imm32\n"
    1033       "run: r/m32 is EAX\n"
    1034       "run: imm32 is 0x0a0b0c0d\n"
    1035       "run: subop compare\n"
    1036       "run: SF=1; ZF=0; CF=1; OF=0\n"
    1037   );
    1038 }
    1039
    1040 void test_compare_rm32_with_imm32_lesser_unsigned_and_signed_due_to_overflow() {
    1041   Reg[EAX].i = INT32_MAX;
    1042   run(
    1043       "== code 0x1\n"
    1044       // op     ModR/M  SIB   displacement  immediate
    1045       "  81     f8                          00 00 00 80\n"  // compare EAX with INT32_MIN
    1046       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
    1047   );
    1048   CHECK_TRACE_CONTENTS(
    1049       "run: combine r/m32 with imm32\n"
    1050       "run: r/m32 is EAX\n"
    1051       "run: imm32 is 0x80000000\n"
    1052       "run: subop compare\n"
    1053       "run: SF=1; ZF=0; CF=1; OF=1\n"
    1054   );
    1055 }
    1056
    1057 void test_compare_rm32_with_imm32_lesser_signed() {
    1058   Reg[EAX].i = -1;
    1059   run(
    1060       "== code 0x1\n"
    1061       // op     ModR/M  SIB   displacement  immediate
    1062       "  81     f8                          01 00 00 00\n"  // compare EAX with 1
    1063       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
    1064   );
    1065   CHECK_TRACE_CONTENTS(
    1066       "run: combine r/m32 with imm32\n"
    1067       "run: r/m32 is EAX\n"
    1068       "run: imm32 is 0x00000001\n"
    1069       "run: subop compare\n"
    1070       "run: SF=1; ZF=0; CF=0; OF=0\n"
    1071   );
    1072 }
    1073
    1074 void test_compare_rm32_with_imm32_lesser_unsigned() {
    1075   Reg[EAX].i = 1;
    1076   run(
    1077       "== code 0x1\n"
    1078       // op     ModR/M  SIB   displacement  immediate
    1079       "  81     f8                          ff ff ff ff\n"  // compare EAX with -1
    1080       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 000 (dest EAX)
    1081   );
    1082   CHECK_TRACE_CONTENTS(
    1083       "run: combine r/m32 with imm32\n"
    1084       "run: r/m32 is EAX\n"
    1085       "run: imm32 is 0xffffffff\n"
    1086       "run: subop compare\n"
    1087       "run: SF=0; ZF=0; CF=1; OF=0\n"
    1088   );
    1089 }
    1090
    1091 :(code)
    1092 void test_compare_imm32_with_r32_equal() {
    1093   Reg[EBX].i = 0x0d0c0b0a;
    1094   run(
    1095       "== code 0x1\n"
    1096       // op     ModR/M  SIB   displacement  immediate
    1097       "  81     fb                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with EBX
    1098       // ModR/M in binary: 11 (direct mode) 111 (subop compare) 011 (dest EBX)
    1099   );
    1100   CHECK_TRACE_CONTENTS(
    1101       "run: combine r/m32 with imm32\n"
    1102       "run: r/m32 is EBX\n"
    1103       "run: imm32 is 0x0d0c0b0a\n"
    1104       "run: SF=0; ZF=1; CF=0; OF=0\n"
    1105   );
    1106 }
    1107
    1108 :(code)
    1109 void test_compare_imm32_with_mem_at_rm32_greater() {
    1110   Reg[EBX].i = 0x2000;
    1111   run(
    1112       "== code 0x1\n"
    1113       // op     ModR/M  SIB   displacement  immediate
    1114       "  81     3b                          07 0b 0c 0d \n"  // compare 0x0d0c0b07 with *EBX
    1115       // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX)
    1116       "== data 0x2000\n"
    1117       "0a 0b 0c 0d\n"  // 0x0d0c0b0a
    1118   );
    1119   CHECK_TRACE_CONTENTS(
    1120       "run: combine r/m32 with imm32\n"
    1121       "run: effective address is 0x00002000 (EBX)\n"
    1122       "run: imm32 is 0x0d0c0b07\n"
    1123       "run: SF=0; ZF=0; CF=0; OF=0\n"
    1124   );
    1125 }
    1126
    1127 :(code)
    1128 void test_compare_imm32_with_mem_at_rm32_lesser() {
    1129   Reg[EAX].i = 0x2000;
    1130   run(
    1131       "== code 0x1\n"
    1132       // op     ModR/M  SIB   displacement  immediate
    1133       "  81     38                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with *EAX
    1134       // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 000 (dest EAX)
    1135       "== data 0x2000\n"
    1136       "07 0b 0c 0d\n"  // 0x0d0c0b07
    1137   );
    1138   CHECK_TRACE_CONTENTS(
    1139       "run: combine r/m32 with imm32\n"
    1140       "run: effective address is 0x00002000 (EAX)\n"
    1141       "run: imm32 is 0x0d0c0b0a\n"
    1142       "run: SF=1; ZF=0; CF=1; OF=0\n"
    1143   );
    1144 }
    1145
    1146 :(code)
    1147 void test_compare_imm32_with_mem_at_rm32_equal() {
    1148   Reg[EBX].i = 0x0d0c0b0a;
    1149   Reg[EBX].i = 0x2000;
    1150   run(
    1151       "== code 0x1\n"
    1152       // op     ModR/M  SIB   displacement  immediate
    1153       "  81     3b                          0a 0b 0c 0d \n"  // compare 0x0d0c0b0a with *EBX
    1154       // ModR/M in binary: 00 (indirect mode) 111 (subop compare) 011 (dest EBX)
    1155       "== data 0x2000\n"
    1156       "0a 0b 0c 0d\n"  // 0x0d0c0b0a
    1157   );
    1158   CHECK_TRACE_CONTENTS(
    1159       "run: combine r/m32 with imm32\n"
    1160       "run: effective address is 0x00002000 (EBX)\n"
    1161       "run: imm32 is 0x0d0c0b0a\n"
    1162       "run: SF=0; ZF=1; CF=0; OF=0\n"
    1163   );
    1164 }
    1165
    1166 //:: copy (mov)
    1167
    1168 :(before "End Initialize Op Names")
    1169 // b8 defined earlier to copy imm32 to EAX
    1170 put_new(Name, "b9", "copy imm32 to ECX (mov)");
    1171 put_new(Name, "ba", "copy imm32 to EDX (mov)");
    1172 put_new(Name, "bb", "copy imm32 to EBX (mov)");
    1173 put_new(Name, "bc", "copy imm32 to ESP (mov)");
    1174 put_new(Name, "bd", "copy imm32 to EBP (mov)");
    1175 put_new(Name, "be", "copy imm32 to ESI (mov)");
    1176 put_new(Name, "bf", "copy imm32 to EDI (mov)");
    1177
    1178 :(code)
    1179 void test_copy_imm32_to_r32() {
    1180   run(
    1181       "== code 0x1\n"
    1182       // op     ModR/M  SIB   displacement  immediate
    1183       "  bb                                 0a 0b 0c 0d \n"  // copy 0x0d0c0b0a to EBX
    1184   );
    1185   CHECK_TRACE_CONTENTS(
    1186       "run: copy imm32 0x0d0c0b0a to EBX\n"
    1187   );
    1188 }
    1189
    1190 :(before "End Single-Byte Opcodes")
    1191 case 0xb9:
    1192 case 0xba:
    1193 case 0xbb:
    1194 case 0xbc:
    1195 case 0xbd:
    1196 case 0xbe:
    1197 case 0xbf: {  // copy imm32 to r32
    1198   const uint8_t rdest = op & 0x7;
    1199   const int32_t src = next32();
    1200   trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end();
    1201   Reg[rdest].i = src;
    1202   break;
    1203 }
    1204
    1205 //:
    1206
    1207 :(before "End Initialize Op Names")
    1208 put_new(Name, "c7", "copy imm32 to rm32 with subop 0 (mov)");
    1209
    1210 :(code)
    1211 void test_copy_imm32_to_mem_at_rm32() {
    1212   Reg[EBX].i = 0x60;
    1213   run(
    1214       "== code 0x1\n"
    1215       // op     ModR/M  SIB   displacement  immediate
    1216       "  c7     03                          0a 0b 0c 0d \n"  // copy 0x0d0c0b0a to *EBX
    1217       // ModR/M in binary: 00 (indirect mode) 000 (subop) 011 (dest EBX)
    1218   );
    1219   CHECK_TRACE_CONTENTS(
    1220       "run: copy imm32 to r/m32\n"
    1221       "run: effective address is 0x00000060 (EBX)\n"
    1222       "run: imm32 is 0x0d0c0b0a\n"
    1223   );
    1224 }
    1225
    1226 :(before "End Single-Byte Opcodes")
    1227 case 0xc7: {  // copy imm32 to r32
    1228   const uint8_t modrm = next();
    1229   trace(Callstack_depth+1, "run") << "copy imm32 to r/m32" << end();
    1230   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
    1231   if (subop != 0) {
    1232     cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n";
    1233     exit(1);
    1234   }
    1235   int32_t* dest = effective_address(modrm);
    1236   const int32_t src = next32();
    1237   trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end();
    1238   *dest = src;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
    1239   break;
    1240 }
    1241
    1242 //:: push
    1243
    1244 :(before "End Initialize Op Names")
    1245 put_new(Name, "68", "push imm32 to stack (push)");
    1246
    1247 :(code)
    1248 void test_push_imm32() {
    1249   Mem.push_back(vma(0xbd000000));  // manually allocate memory
    1250   Reg[ESP].u = 0xbd000014;
    1251   run(
    1252       "== code 0x1\n"
    1253       // op     ModR/M  SIB   displacement  immediate
    1254       "  68                                 af 00 00 00 \n"  // push *EAX to stack
    1255   );
    1256   CHECK_TRACE_CONTENTS(
    1257       "run: push imm32 0x000000af\n"
    1258       "run: ESP is now 0xbd000010\n"
    1259       "run: contents at ESP: 0x000000af\n"
    1260   );
    1261 }
    1262
    1263 :(before "End Single-Byte Opcodes")
    1264 case 0x68: {
    1265   const uint32_t val = static_cast<uint32_t>(next32());
    1266   trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end();
    1267 //?   cerr << "push: " << val << " => " << Reg[ESP].u << '\n';
    1268   push(val);
    1269   trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end();
    1270   trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end();
    1271   break;
    1272 }
    1273
    1274 //:: multiply
    1275
    1276 :(before "End Initialize Op Names")
    1277 put_new(Name, "69", "multiply rm32 by imm32 and store result in r32 (imul)");
    1278
    1279 :(code)
    1280 void test_multiply_imm32() {
    1281   Reg[EAX].i = 2;
    1282   Reg[EBX].i = 3;
    1283   run(
    1284       "== code 0x1\n"
    1285       // op     ModR/M  SIB   displacement  immediate
    1286       "  69     c3                          04 00 00 00 \n"  // EAX = EBX * 4
    1287       // ModR/M in binary: 11 (direct) 000 (dest EAX) 011 (src EBX)
    1288   );
    1289   CHECK_TRACE_CONTENTS(
    1290       "run: multiply r/m32 by 0x00000004 and store result in EAX\n"
    1291       "run: r/m32 is EBX\n"
    1292       "run: storing 0x0000000c\n"
    1293   );
    1294 }
    1295
    1296 :(before "End Single-Byte Opcodes")
    1297 case 0x69: {
    1298   const uint8_t modrm = next();
    1299   const uint8_t rdest = (modrm>>3)&0x7;
    1300   const int32_t val = next32();
    1301   trace(Callstack_depth+1, "run") << "multiply r/m32 by 0x" << HEXWORD << val << " and store result in " << rname(rdest) << end();
    1302   const int32_t* signed_arg1 = effective_address(modrm);
    1303   int32_t result = *signed_arg1 * val;
    1304   int64_t full_result = static_cast<int64_t>(*signed_arg1) * val;
    1305   OF = (result != full_result);
    1306   CF = OF;
    1307   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
    1308   Reg[rdest].i = result;
    1309   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[rdest].i << end();
    1310   break;
    1311 }