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 }