https://github.com/akkartik/mu/blob/main/linux/bootstrap/014indirect_addressing.cc
   1 //: operating on memory at the address provided by some register
   2 //: we'll now start providing data in a separate segment
   3 
   4 void test_add_r32_to_mem_at_rm32() {
   5   Reg[EBX].i = 0x10;
   6   Reg[EAX].i = 0x2000;
   7   run(
   8       "== code 0x1\n"
   9       // op     ModR/M  SIB   displacement  immediate
  10       "  01     18                                    \n"  // add EBX to *EAX
  11       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
  12       "== data 0x2000\n"
  13       "01 00 00 00\n"  // 1
  14   );
  15   CHECK_TRACE_CONTENTS(
  16       "run: add EBX to r/m32\n"
  17       "run: effective address is 0x00002000 (EAX)\n"
  18       "run: storing 0x00000011\n"
  19   );
  20 }
  21 
  22 :(before "End Mod Special-cases(addr)")
  23 case 0:  // indirect addressing
  24   switch (rm) {
  25   default:  // address in register
  26     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
  27     addr = Reg[rm].u;
  28     break;
  29   // End Mod 0 Special-cases(addr)
  30   }
  31   break;
  32 
  33 //:
  34 
  35 :(before "End Initialize Op Names")
  36 put_new(Name, "03", "add rm32 to r32 (add)");
  37 
  38 :(code)
  39 void test_add_mem_at_rm32_to_r32() {
  40   Reg[EAX].i = 0x2000;
  41   Reg[EBX].i = 0x10;
  42   run(
  43       "== code 0x1\n"
  44       // op     ModR/M  SIB   displacement  immediate
  45       "  03     18                                    \n"  // add *EAX to EBX
  46       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
  47       "== data 0x2000\n"
  48       "01 00 00 00\n"  // 1
  49   );
  50   CHECK_TRACE_CONTENTS(
  51       "run: add r/m32 to EBX\n"
  52       "run: effective address is 0x00002000 (EAX)\n"
  53       "run: storing 0x00000011\n"
  54   );
  55 }
  56 
  57 :(before "End Single-Byte Opcodes")
  58 case 0x03: {  // add r/m32 to r32
  59   const uint8_t modrm = next();
  60   const uint8_t arg1 = (modrm>>3)&0x7;
  61   trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end();
  62   const int32_t* signed_arg2 = effective_address(modrm);
  63   int32_t signed_result = Reg[arg1].i + *signed_arg2;
  64   SF = (signed_result < 0);
  65   ZF = (signed_result == 0);
  66   int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) + *signed_arg2;
  67   OF = (signed_result != signed_full_result);
  68   // set CF
  69   uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
  70   uint32_t unsigned_result = Reg[arg1].u + unsigned_arg2;
  71   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) + unsigned_arg2;
  72   CF = (unsigned_result != unsigned_full_result);
  73   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
  74   Reg[arg1].i = signed_result;
  75   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
  76   break;
  77 }
  78 
  79 :(code)
  80 void test_add_mem_at_rm32_to_r32_signed_overflow() {
  81   Reg[EAX].i = 0x2000;
  82   Reg[EBX].i = INT32_MAX;
  83   run(
  84       "== code 0x1\n"
  85       // op     ModR/M  SIB   displacement  immediate
  86       "  03     18                                    \n" // add *EAX to EBX
  87       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
  88       "== data 0x2000\n"
  89       "01 00 00 00\n"  // 1
  90   );
  91   CHECK_TRACE_CONTENTS(
  92       "run: add r/m32 to EBX\n"
  93       "run: effective address is 0x00002000 (EAX)\n"
  94       "run: effective address contains 0x00000001\n"
  95       "run: SF=1; ZF=0; CF=0; OF=1\n"
  96       "run: storing 0x80000000\n"
  97   );
  98 }
  99 
 100 void test_add_mem_at_rm32_to_r32_unsigned_overflow() {
 101   Reg[EAX].u = 0x2000;
 102   Reg[EBX].u = UINT32_MAX;
 103   run(
 104       "== code 0x1\n"
 105       // op     ModR/M  SIB   displacement  immediate
 106       "  03     18                                    \n" // add *EAX to EBX
 107       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 108       "== data 0x2000\n"
 109       "01 00 00 00\n"
 110   );
 111   CHECK_TRACE_CONTENTS(
 112       "run: add r/m32 to EBX\n"
 113       "run: effective address is 0x00002000 (EAX)\n"
 114       "run: effective address contains 0x00000001\n"
 115       "run: SF=0; ZF=1; CF=1; OF=0\n"
 116       "run: storing 0x00000000\n"
 117   );
 118 }
 119 
 120 void test_add_mem_at_rm32_to_r32_unsigned_and_signed_overflow() {
 121   Reg[EAX].u = 0x2000;
 122   Reg[EBX].i = INT32_MIN;
 123   run(
 124       "== code 0x1\n"
 125       // op     ModR/M  SIB   displacement  immediate
 126       "  03     18                                    \n" // add *EAX to EBX
 127       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 128       "== data 0x2000\n"
 129       "00 00 00 80\n"  // INT32_MIN
 130   );
 131   CHECK_TRACE_CONTENTS(
 132       "run: add r/m32 to EBX\n"
 133       "run: effective address is 0x00002000 (EAX)\n"
 134       "run: effective address contains 0x80000000\n"
 135       "run: SF=0; ZF=1; CF=1; OF=1\n"
 136       "run: storing 0x00000000\n"
 137   );
 138 }
 139 
 140 //:: subtract
 141 
 142 :(code)
 143 void test_subtract_r32_from_mem_at_rm32() {
 144   Reg[EAX].i = 0x2000;
 145   Reg[EBX].i = 1;
 146   run(
 147       "== code 0x1\n"
 148       // op     ModR/M  SIB   displacement  immediate
 149       "  29     18                                    \n"  // subtract EBX from *EAX
 150       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 151       "== data 0x2000\n"
 152       "0a 00 00 00\n"  // 0xa
 153   );
 154   CHECK_TRACE_CONTENTS(
 155       "run: subtract EBX from r/m32\n"
 156       "run: effective address is 0x00002000 (EAX)\n"
 157       "run: storing 0x00000009\n"
 158   );
 159 }
 160 
 161 //:
 162 
 163 :(before "End Initialize Op Names")
 164 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
 165 
 166 :(code)
 167 void test_subtract_mem_at_rm32_from_r32() {
 168   Reg[EAX].i = 0x2000;
 169   Reg[EBX].i = 10;
 170   run(
 171       "== code 0x1\n"
 172       // op     ModR/M  SIB   displacement  immediate
 173       "  2b     18                                    \n"  // subtract *EAX from EBX
 174       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 175       "== data 0x2000\n"
 176       "01 00 00 00\n"  // 1
 177   );
 178   CHECK_TRACE_CONTENTS(
 179       "run: subtract r/m32 from EBX\n"
 180       "run: effective address is 0x00002000 (EAX)\n"
 181       "run: storing 0x00000009\n"
 182   );
 183 }
 184 
 185 :(before "End Single-Byte Opcodes")
 186 case 0x2b: {  // subtract r/m32 from r32
 187   const uint8_t modrm = next();
 188   const uint8_t arg1 = (modrm>>3)&0x7;
 189   trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end();
 190   const int32_t* signed_arg2 = effective_address(modrm);
 191   const int32_t signed_result = Reg[arg1].i - *signed_arg2;
 192   SF = (signed_result < 0);
 193   ZF = (signed_result == 0);
 194   int64_t signed_full_result = static_cast<int64_t>(Reg[arg1].i) - *signed_arg2;
 195   OF = (signed_result != signed_full_result);
 196   // set CF
 197   uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
 198   uint32_t unsigned_result = Reg[arg1].u - unsigned_arg2;
 199   uint64_t unsigned_full_result = static_cast<uint64_t>(Reg[arg1].u) - unsigned_arg2;
 200   CF = (unsigned_result != unsigned_full_result);
 201   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
 202   Reg[arg1].i = signed_result;
 203   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
 204   break;
 205 }
 206 
 207 :(code)
 208 void test_subtract_mem_at_rm32_from_r32_signed_overflow() {
 209   Reg[EAX].i = 0x2000;
 210   Reg[EBX].i = INT32_MIN;
 211   run(
 212       "== code 0x1\n"
 213       // op     ModR/M  SIB   displacement  immediate
 214       "  2b     18                                    \n"  // subtract *EAX from EBX
 215       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 216       "== data 0x2000\n"
 217       "ff ff ff 7f\n"  // INT32_MAX
 218   );
 219   CHECK_TRACE_CONTENTS(
 220       "run: subtract r/m32 from EBX\n"
 221       "run: effective address is 0x00002000 (EAX)\n"
 222       "run: effective address contains 0x7fffffff\n"
 223       "run: SF=0; ZF=0; CF=0; OF=1\n"
 224       "run: storing 0x00000001\n"
 225   );
 226 }
 227 
 228 void test_subtract_mem_at_rm32_from_r32_unsigned_overflow() {
 229   Reg[EAX].i = 0x2000;
 230   Reg[EBX].i = 0;
 231   run(
 232       "== code 0x1\n"
 233       // op     ModR/M  SIB   displacement  immediate
 234       "  2b     18                                    \n"  // subtract *EAX from EBX
 235       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 236       "== data 0x2000\n"
 237       "01 00 00 00\n"  // 1
 238   );
 239   CHECK_TRACE_CONTENTS(
 240       "run: subtract r/m32 from EBX\n"
 241       "run: effective address is 0x00002000 (EAX)\n"
 242       "run: effective address contains 0x00000001\n"
 243       "run: SF=1; ZF=0; CF=1; OF=0\n"
 244       "run: storing 0xffffffff\n"
 245   );
 246 }
 247 
 248 void test_subtract_mem_at_rm32_from_r32_signed_and_unsigned_overflow() {
 249   Reg[EAX].i = 0x2000;
 250   Reg[EBX].i = 0;
 251   run(
 252       "== code 0x1\n"
 253       // op     ModR/M  SIB   displacement  immediate
 254       "  2b     18                                    \n"  // subtract *EAX from EBX
 255       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 256       "== data 0x2000\n"
 257       "00 00 00 80\n"  // INT32_MIN
 258   );
 259   CHECK_TRACE_CONTENTS(
 260       "run: subtract r/m32 from EBX\n"
 261       "run: effective address is 0x00002000 (EAX)\n"
 262       "run: effective address contains 0x80000000\n"
 263       "run: SF=1; ZF=0; CF=1; OF=1\n"
 264       "run: storing 0x80000000\n"
 265   );
 266 }
 267 
 268 //:: and
 269 :(code)
 270 void test_and_r32_with_mem_at_rm32() {
 271   Reg[EAX].i = 0x2000;
 272   Reg[EBX].i = 0xff;
 273   run(
 274       "== code 0x1\n"
 275       // op     ModR/M  SIB   displacement  immediate
 276       "  21     18                                    \n"  // and EBX with *EAX
 277       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 278       "== data 0x2000\n"
 279       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 280   );
 281   CHECK_TRACE_CONTENTS(
 282       "run: and EBX with r/m32\n"
 283       "run: effective address is 0x00002000 (EAX)\n"
 284       "run: storing 0x0000000d\n"
 285   );
 286 }
 287 
 288 //:
 289 
 290 :(before "End Initialize Op Names")
 291 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
 292 
 293 :(code)
 294 void test_and_mem_at_rm32_with_r32() {
 295   Reg[EAX].i = 0x2000;
 296   Reg[EBX].i = 0x0a0b0c0d;
 297   run(
 298       "== code 0x1\n"
 299       // op     ModR/M  SIB   displacement  immediate
 300       "  23     18                                    \n"  // and *EAX with EBX
 301       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 302       "== data 0x2000\n"
 303       "ff 00 00 00\n"  // 0xff
 304   );
 305   CHECK_TRACE_CONTENTS(
 306       "run: and r/m32 with EBX\n"
 307       "run: effective address is 0x00002000 (EAX)\n"
 308       "run: storing 0x0000000d\n"
 309   );
 310 }
 311 
 312 :(before "End Single-Byte Opcodes")
 313 case 0x23: {  // and r/m32 with r32
 314   const uint8_t modrm = next();
 315   const uint8_t arg1 = (modrm>>3)&0x7;
 316   trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end();
 317   // bitwise ops technically operate on unsigned numbers, but it makes no
 318   // difference
 319   const int32_t* signed_arg2 = effective_address(modrm);
 320   Reg[arg1].i &= *signed_arg2;
 321   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
 322   SF = (Reg[arg1].i >> 31);
 323   ZF = (Reg[arg1].i == 0);
 324   CF = false;
 325   OF = false;
 326   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
 327   break;
 328 }
 329 
 330 //:: or
 331 
 332 :(code)
 333 void test_or_r32_with_mem_at_rm32() {
 334   Reg[EAX].i = 0x2000;
 335   Reg[EBX].i = 0xa0b0c0d0;
 336   run(
 337       "== code 0x1\n"
 338       // op     ModR/M  SIB   displacement  immediate
 339       "  09     18                                   #\n"  // EBX with *EAX
 340       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 341       "== data 0x2000\n"
 342       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 343   );
 344   CHECK_TRACE_CONTENTS(
 345       "run: or EBX with r/m32\n"
 346       "run: effective address is 0x00002000 (EAX)\n"
 347       "run: storing 0xaabbccdd\n"
 348   );
 349 }
 350 
 351 //:
 352 
 353 :(before "End Initialize Op Names")
 354 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
 355 
 356 :(code)
 357 void test_or_mem_at_rm32_with_r32() {
 358   Reg[EAX].i = 0x2000;
 359   Reg[EBX].i = 0xa0b0c0d0;
 360   run(
 361       "== code 0x1\n"
 362       // op     ModR/M  SIB   displacement  immediate
 363       "  0b     18                                    \n"  // or *EAX with EBX
 364       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 365       "== data 0x2000\n"
 366       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 367   );
 368   CHECK_TRACE_CONTENTS(
 369       "run: or r/m32 with EBX\n"
 370       "run: effective address is 0x00002000 (EAX)\n"
 371       "run: storing 0xaabbccdd\n"
 372   );
 373 }
 374 
 375 :(before "End Single-Byte Opcodes")
 376 case 0x0b: {  // or r/m32 with r32
 377   const uint8_t modrm = next();
 378   const uint8_t arg1 = (modrm>>3)&0x7;
 379   trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end();
 380   // bitwise ops technically operate on unsigned numbers, but it makes no
 381   // difference
 382   const int32_t* signed_arg2 = effective_address(modrm);
 383   Reg[arg1].i |= *signed_arg2;
 384   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
 385   SF = (Reg[arg1].i >> 31);
 386   ZF = (Reg[arg1].i == 0);
 387   CF = false;
 388   OF = false;
 389   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
 390   break;
 391 }
 392 
 393 //:: xor
 394 
 395 :(code)
 396 void test_xor_r32_with_mem_at_rm32() {
 397   Reg[EAX].i = 0x2000;
 398   Reg[EBX].i = 0xa0b0c0d0;
 399   run(
 400       "== code 0x1\n"
 401       // op     ModR/M  SIB   displacement  immediate
 402       "  31     18                                    \n"  // xor EBX with *EAX
 403       "== data 0x2000\n"
 404       "0d 0c bb aa\n"  // 0xaabb0c0d
 405   );
 406   CHECK_TRACE_CONTENTS(
 407       "run: xor EBX with r/m32\n"
 408       "run: effective address is 0x00002000 (EAX)\n"
 409       "run: storing 0x0a0bccdd\n"
 410   );
 411 }
 412 
 413 //:
 414 
 415 :(before "End Initialize Op Names")
 416 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
 417 
 418 :(code)
 419 void test_xor_mem_at_rm32_with_r32() {
 420   Reg[EAX].i = 0x2000;
 421   Reg[EBX].i = 0xa0b0c0d0;
 422   run(
 423       "== code 0x1\n"
 424       // op     ModR/M  SIB   displacement  immediate
 425       "  33     18                                    \n"  // xor *EAX with EBX
 426       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 427       "== data 0x2000\n"
 428       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 429   );
 430   CHECK_TRACE_CONTENTS(
 431       "run: xor r/m32 with EBX\n"
 432       "run: effective address is 0x00002000 (EAX)\n"
 433       "run: storing 0xaabbccdd\n"
 434   );
 435 }
 436 
 437 :(before "End Single-Byte Opcodes")
 438 case 0x33: {  // xor r/m32 with r32
 439   const uint8_t modrm = next();
 440   const uint8_t arg1 = (modrm>>3)&0x7;
 441   trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end();
 442   // bitwise ops technically operate on unsigned numbers, but it makes no
 443   // difference
 444   const int32_t* signed_arg2 = effective_address(modrm);
 445   Reg[arg1].i |= *signed_arg2;
 446   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
 447   SF = (Reg[arg1].i >> 31);
 448   ZF = (Reg[arg1].i == 0);
 449   CF = false;
 450   OF = false;
 451   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
 452   break;
 453 }
 454 
 455 //:: not
 456 
 457 :(code)
 458 void test_not_of_mem_at_rm32() {
 459   Reg[EBX].i = 0x2000;
 460   run(
 461       "== code 0x1\n"
 462       // op     ModR/M  SIB   displacement  immediate
 463       "  f7     13                                    \n"  // not *EBX
 464       // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX)
 465       "== data 0x2000\n"
 466       "ff 00 0f 0f\n"  // 0x0f0f00ff
 467   );
 468   CHECK_TRACE_CONTENTS(
 469       "run: operate on r/m32\n"
 470       "run: effective address is 0x00002000 (EBX)\n"
 471       "run: subop: not\n"
 472       "run: storing 0xf0f0ff00\n"
 473   );
 474 }
 475 
 476 //:: compare (cmp)
 477 
 478 :(code)
 479 void test_compare_mem_at_rm32_with_r32_greater() {
 480   Reg[EAX].i = 0x2000;
 481   Reg[EBX].i = 0x0a0b0c07;
 482   run(
 483       "== code 0x1\n"
 484       // op     ModR/M  SIB   displacement  immediate
 485       "  39     18                                    \n"  // compare *EAX with EBX
 486       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 487       "== data 0x2000\n"
 488       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 489   );
 490   CHECK_TRACE_CONTENTS(
 491       "run: compare r/m32 with EBX\n"
 492       "run: effective address is 0x00002000 (EAX)\n"
 493       "run: SF=0; ZF=0; CF=0; OF=0\n"
 494   );
 495 }
 496 
 497 :(code)
 498 void test_compare_mem_at_rm32_with_r32_lesser() {
 499   Reg[EAX].i = 0x2000;
 500   Reg[EBX].i = 0x0a0b0c0d;
 501   run(
 502       "== code 0x1\n"
 503       // op     ModR/M  SIB   displacement  immediate
 504       "  39     18                                    \n"  // compare *EAX with EBX
 505       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 506       "== data 0x2000\n"
 507       "07 0c 0b 0a\n"  // 0x0a0b0c0d
 508   );
 509   CHECK_TRACE_CONTENTS(
 510       "run: compare r/m32 with EBX\n"
 511       "run: effective address is 0x00002000 (EAX)\n"
 512       "run: SF=1; ZF=0; CF=1; OF=0\n"
 513   );
 514 }
 515 
 516 :(code)
 517 void test_compare_mem_at_rm32_with_r32_equal() {
 518   Reg[EAX].i = 0x2000;
 519   Reg[EBX].i = 0x0a0b0c0d;
 520   run(
 521       "== code 0x1\n"
 522       // op     ModR/M  SIB   displacement  immediate
 523       "  39     18                                    \n"  // compare *EAX and EBX
 524       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 525       "== data 0x2000\n"
 526       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 527   );
 528   CHECK_TRACE_CONTENTS(
 529       "run: compare r/m32 with EBX\n"
 530       "run: effective address is 0x00002000 (EAX)\n"
 531       "run: SF=0; ZF=1; CF=0; OF=0\n"
 532   );
 533 }
 534 
 535 //:
 536 
 537 :(before "End Initialize Op Names")
 538 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
 539 
 540 :(code)
 541 void test_compare_r32_with_mem_at_rm32_greater() {
 542   Reg[EAX].i = 0x2000;
 543   Reg[EBX].i = 0x0a0b0c0d;
 544   run(
 545       "== code 0x1\n"
 546       // op     ModR/M  SIB   displacement  immediate
 547       "  3b     18                                    \n"  // compare EBX with *EAX
 548       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
 549       "== data 0x2000\n"
 550       "07 0c 0b 0a\n"  // 0x0a0b0c07
 551   );
 552   CHECK_TRACE_CONTENTS(
 553       "run: compare EBX with r/m32\n"
 554       "run: effective address is 0x00002000 (EAX)\n"
 555       "run: SF=0; ZF=0; CF=0; OF=0\n"
 556   );
 557 }
 558 
 559 :(before "End Single-Byte Opcodes")
 560 case 0x3b: {  // set SF if r32 < r/m32
 561   const uint8_t modrm = next();
 562   const uint8_t reg1 = (modrm>>3)&0x7;
 563   trace(Callstack_depth+1, "run") << "compare " << rname(reg1) << " with r/m32" << end();
 564   const int32_t* signed_arg2 = effective_address(modrm);
 565   const int32_t signed_difference = Reg[reg1].i - *signed_arg2;
 566   SF = (signed_difference < 0);
 567   ZF = (signed_difference == 0);
 568   int64_t full_signed_difference = static_cast<int64_t>(Reg[reg1].i) - *signed_arg2;
 569   OF = (signed_difference != full_signed_difference);
 570   const uint32_t unsigned_arg2 = static_cast<uint32_t>(*signed_arg2);
 571   const uint32_t unsigned_difference = Reg[reg1].u - unsigned_arg2;
 572   const uint64_t full_unsigned_difference = static_cast<uint64_t>(Reg[reg1].u) - unsigned_arg2;
 573   CF = (unsigned_difference != full_unsigned_difference);
 574   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
 575   break;
 576 }
 577 
 578 :(code)
 579 void test_compare_r32_with_mem_at_rm32_lesser_unsigned_and_signed() {
 580   Reg[EAX].i = 0x2000;
 581   Reg[EBX].i = 0x0a0b0c07;
 582   run(
 583       "== code 0x1\n"
 584       // op     ModR/M  SIB   displacement  immediate
 585       "  3b     18                                    \n"  // compare EBX with *EAX
 586       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
 587       "== data 0x2000\n"
 588       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 589   );
 590   CHECK_TRACE_CONTENTS(
 591       "run: compare EBX with r/m32\n"
 592       "run: effective address is 0x00002000 (EAX)\n"
 593       "run: effective address contains 0x0a0b0c0d\n"
 594       "run: SF=1; ZF=0; CF=1; OF=0\n"
 595   );
 596 }
 597 
 598 void test_compare_r32_with_mem_at_rm32_lesser_unsigned_and_signed_due_to_overflow() {
 599   Reg[EAX].i = 0x2000;
 600   Reg[EBX].i = INT32_MAX;
 601   run(
 602       "== code 0x1\n"
 603       // op     ModR/M  SIB   displacement  immediate
 604       "  3b     18                                    \n"  // compare EBX with *EAX
 605       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
 606       "== data 0x2000\n"
 607       "00 00 00 80\n"  // INT32_MIN
 608   );
 609   CHECK_TRACE_CONTENTS(
 610       "run: compare EBX with r/m32\n"
 611       "run: effective address is 0x00002000 (EAX)\n"
 612       "run: effective address contains 0x80000000\n"
 613       "run: SF=1; ZF=0; CF=1; OF=1\n"
 614   );
 615 }
 616 
 617 void test_compare_r32_with_mem_at_rm32_lesser_signed() {
 618   Reg[EAX].i = 0x2000;
 619   Reg[EBX].i = -1;
 620   run(
 621       "== code 0x1\n"
 622       // op     ModR/M  SIB   displacement  immediate
 623       "  3b     18                                    \n"  // compare EBX with *EAX
 624       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
 625       "== data 0x2000\n"
 626       "01 00 00 00\n"  // 1
 627   );
 628   CHECK_TRACE_CONTENTS(
 629       "run: compare EBX with r/m32\n"
 630       "run: effective address is 0x00002000 (EAX)\n"
 631       "run: effective address contains 0x00000001\n"
 632       "run: SF=1; ZF=0; CF=0; OF=0\n"
 633   );
 634 }
 635 
 636 void test_compare_r32_with_mem_at_rm32_lesser_unsigned() {
 637   Reg[EAX].i = 0x2000;
 638   Reg[EBX].i = 1;
 639   run(
 640       "== code 0x1\n"
 641       // op     ModR/M  SIB   displacement  immediate
 642       "  3b     18                                    \n"  // compare EBX with *EAX
 643       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
 644       "== data 0x2000\n"
 645       "ff ff ff ff\n"  // -1
 646   );
 647   CHECK_TRACE_CONTENTS(
 648       "run: compare EBX with r/m32\n"
 649       "run: effective address is 0x00002000 (EAX)\n"
 650       "run: effective address contains 0xffffffff\n"
 651       "run: SF=0; ZF=0; CF=1; OF=0\n"
 652   );
 653 }
 654 
 655 void test_compare_r32_with_mem_at_rm32_equal() {
 656   Reg[EAX].i = 0x2000;
 657   Reg[EBX].i = 0x0a0b0c0d;
 658   run(
 659       "== code 0x1\n"
 660       // op     ModR/M  SIB   displacement  immediate
 661       "  3b     18                                    \n"  // compare EBX with *EAX
 662       // ModR/M in binary: 00 (indirect mode) 011 (lhs EBX) 000 (rhs EAX)
 663       "== data 0x2000\n"
 664       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
 665   );
 666   CHECK_TRACE_CONTENTS(
 667       "run: compare EBX with r/m32\n"
 668       "run: effective address is 0x00002000 (EAX)\n"
 669       "run: SF=0; ZF=1; CF=0; OF=0\n"
 670   );
 671 }
 672 
 673 //:: copy (mov)
 674 
 675 void test_copy_r32_to_mem_at_rm32() {
 676   Reg[EBX].i = 0xaf;
 677   Reg[EAX].i = 0x60;
 678   run(
 679       "== code 0x1\n"
 680       // op     ModR/M  SIB   displacement  immediate
 681       "  89     18                                    \n"  // copy EBX to *EAX
 682       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
 683   );
 684   CHECK_TRACE_CONTENTS(
 685       "run: copy EBX to r/m32\n"
 686       "run: effective address is 0x00000060 (EAX)\n"
 687       "run: storing 0x000000af\n"
 688   );
 689 }
 690 
 691 //:
 692 
 693 :(before "End Initialize Op Names")
 694 put_new(Name, "8b", "copy rm32 to r32 (mov)");
 695 
 696 :(code)
 697 void test_copy_mem_at_rm32_to_r32() {
 698   Reg[EAX].i = 0x2000;
 699   run(
 700       "== code 0x1\n"
 701       // op     ModR/M  SIB   displacement  immediate
 702       "  8b     18                                    \n"  // copy *EAX to EBX
 703       "== data 0x2000\n"
 704       "af 00 00 00\n"  // 0xaf
 705   );
 706   CHECK_TRACE_CONTENTS(
 707       "run: copy r/m32 to EBX\n"
 708       "run: effective address is 0x00002000 (EAX)\n"
 709       "run: storing 0x000000af\n"
 710   );
 711 }
 712 
 713 :(before "End Single-Byte Opcodes")
 714 case 0x8b: {  // copy r32 to r/m32
 715   const uint8_t modrm = next();
 716   const uint8_t rdest = (modrm>>3)&0x7;
 717   trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end();
 718   const int32_t* src = effective_address(modrm);
 719   Reg[rdest].i = *src;
 720   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end();
 721   break;
 722 }
 723 
 724 //:: jump
 725 
 726 :(code)
 727 void test_jump_mem_at_rm32() {
 728   Reg[EAX].i = 0x2000;
 729   run(
 730       "== code 0x1\n"
 731       // op     ModR/M  SIB   displacement  immediate
 732       "  ff     20                                    \n"  // jump to *EAX
 733       // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX)
 734       "  b8                                 00 00 00 01\n"
 735       "  b8                                 00 00 00 02\n"
 736       "== data 0x2000\n"
 737       "08 00 00 00\n"  // 0x8
 738   );
 739   CHECK_TRACE_CONTENTS(
 740       "run: 0x00000001 opcode: ff\n"
 741       "run: jump to r/m32\n"
 742       "run: effective address is 0x00002000 (EAX)\n"
 743       "run: jumping to 0x00000008\n"
 744       "run: 0x00000008 opcode: b8\n"
 745   );
 746   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: b8");
 747 }
 748 
 749 :(before "End Op ff Subops")
 750 case 4: {  // jump to r/m32
 751   trace(Callstack_depth+1, "run") << "jump to r/m32" << end();
 752   const int32_t* arg2 = effective_address(modrm);
 753   EIP = *arg2;
 754   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
 755   break;
 756 }
 757 
 758 //:: push
 759 
 760 :(code)
 761 void test_push_mem_at_rm32() {
 762   Reg[EAX].i = 0x2000;
 763   Mem.push_back(vma(0xbd000000));  // manually allocate memory
 764   Reg[ESP].u = 0xbd000014;
 765   run(
 766       "== code 0x1\n"
 767       // op     ModR/M  SIB   displacement  immediate
 768       "  ff     30                                    \n"  // push *EAX to stack
 769       "== data 0x2000\n"
 770       "af 00 00 00\n"  // 0xaf
 771   );
 772   CHECK_TRACE_CONTENTS(
 773       "run: push r/m32\n"
 774       "run: effective address is 0x00002000 (EAX)\n"
 775       "run: decrementing ESP to 0xbd000010\n"
 776       "run: pushing value 0x000000af\n"
 777   );
 778 }
 779 
 780 :(before "End Op ff Subops")
 781 case 6: {  // push r/m32 to stack
 782   trace(Callstack_depth+1, "run") << "push r/m32" << end();
 783   const int32_t* val = effective_address(modrm);
 784   push(*val);
 785   break;
 786 }
 787 
 788 //:: pop
 789 
 790 :(before "End Initialize Op Names")
 791 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
 792 
 793 :(code)
 794 void test_pop_mem_at_rm32() {
 795   Reg[EAX].i = 0x60;
 796   Mem.push_back(vma(0xbd000000));  // manually allocate memory
 797   Reg[ESP].u = 0xbd000000;
 798   write_mem_i32(0xbd000000, 0x00000030);
 799   run(
 800       "== code 0x1\n"
 801       // op     ModR/M  SIB   displacement  immediate
 802       "  8f     00                                    \n"  // pop stack into *EAX
 803       // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX)
 804   );
 805   CHECK_TRACE_CONTENTS(
 806       "run: pop into r/m32\n"
 807       "run: effective address is 0x00000060 (EAX)\n"
 808       "run: popping value 0x00000030\n"
 809       "run: incrementing ESP to 0xbd000004\n"
 810   );
 811 }
 812 
 813 :(before "End Single-Byte Opcodes")
 814 case 0x8f: {  // pop stack into r/m32
 815   const uint8_t modrm = next();
 816   const uint8_t subop = (modrm>>3)&0x7;
 817   switch (subop) {
 818     case 0: {
 819       trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
 820       int32_t* dest = effective_address(modrm);
 821       *dest = pop();  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
 822       break;
 823     }
 824   }
 825   break;
 826 }
 827 
 828 //:: special-case for loading address from disp32 rather than register
 829 
 830 :(code)
 831 void test_add_r32_to_mem_at_displacement() {
 832   Reg[EBX].i = 0x10;  // source
 833   run(
 834       "== code 0x1\n"
 835       // op     ModR/M  SIB   displacement  immediate
 836       "  01     1d            00 20 00 00             \n"  // add EBX to *0x2000
 837       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32)
 838       "== data 0x2000\n"
 839       "01 00 00 00\n"  // 1
 840   );
 841   CHECK_TRACE_CONTENTS(
 842       "run: add EBX to r/m32\n"
 843       "run: effective address is 0x00002000 (disp32)\n"
 844       "run: storing 0x00000011\n"
 845   );
 846 }
 847 
 848 :(before "End Mod 0 Special-cases(addr)")
 849 case 5:  // exception: mod 0b00 rm 0b101 => incoming disp32
 850   addr = next32();
 851   trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
 852   break;
 853 
 854 //:
 855 
 856 :(code)
 857 void test_add_r32_to_mem_at_rm32_plus_disp8() {
 858   Reg[EBX].i = 0x10;  // source
 859   Reg[EAX].i = 0x1ffe;  // dest
 860   run(
 861       "== code 0x1\n"
 862       // op     ModR/M  SIB   displacement  immediate
 863       "  01     58            02                      \n"  // add EBX to *(EAX+2)
 864       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
 865       "== data 0x2000\n"
 866       "01 00 00 00\n"  // 1
 867   );
 868   CHECK_TRACE_CONTENTS(
 869       "run: add EBX to r/m32\n"
 870       "run: effective address is initially 0x00001ffe (EAX)\n"
 871       "run: effective address is 0x00002000 (after adding disp8)\n"
 872       "run: storing 0x00000011\n"
 873   );
 874 }
 875 
 876 :(before "End Mod Special-cases(addr)")
 877 case 1: {  // indirect + disp8 addressing
 878   switch (rm) {
 879   default:
 880     addr = Reg[rm].u;
 881     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
 882     break;
 883   // End Mod 1 Special-cases(addr)
 884   }
 885   int8_t displacement = static_cast<int8_t>(next());
 886   if (addr > 0) {
 887     addr += displacement;
 888     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
 889   }
 890   else {
 891     trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end();
 892   }
 893   break;
 894 }
 895 
 896 :(code)
 897 void test_add_r32_to_mem_at_rm32_plus_negative_disp8() {
 898   Reg[EBX].i = 0x10;  // source
 899   Reg[EAX].i = 0x2001;  // dest
 900   run(
 901       "== code 0x1\n"
 902       // op     ModR/M  SIB   displacement  immediate
 903       "  01     58            ff                      \n"  // add EBX to *(EAX-1)
 904       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
 905       "== data 0x2000\n"
 906       "01 00 00 00\n"  // 1
 907   );
 908   CHECK_TRACE_CONTENTS(
 909       "run: add EBX to r/m32\n"
 910       "run: effective address is initially 0x00002001 (EAX)\n"
 911       "run: effective address is 0x00002000 (after adding disp8)\n"
 912       "run: storing 0x00000011\n"
 913   );
 914 }
 915 
 916 //:
 917 
 918 :(code)
 919 void test_add_r32_to_mem_at_rm32_plus_disp32() {
 920   Reg[EBX].i = 0x10;  // source
 921   Reg[EAX].i = 0x1ffe;  // dest
 922   run(
 923       "== code 0x1\n"
 924       // op     ModR/M  SIB   displacement  immediate
 925       "  01     98            02 00 00 00             \n"  // add EBX to *(EAX+2)
 926       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
 927       "== data 0x2000\n"
 928       "01 00 00 00\n"  // 1
 929   );
 930   CHECK_TRACE_CONTENTS(
 931       "run: add EBX to r/m32\n"
 932       "run: effective address is initially 0x00001ffe (EAX)\n"
 933       "run: effective address is 0x00002000 (after adding disp32)\n"
 934       "run: storing 0x00000011\n"
 935   );
 936 }
 937 
 938 :(before "End Mod Special-cases(addr)")
 939 case 2: {  // indirect + disp32 addressing
 940   switch (rm) {
 941   default:
 942     addr = Reg[rm].u;
 943     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
 944     break;
 945   // End Mod 2 Special-cases(addr)
 946   }
 947   int32_t displacement = static_cast<int32_t>(next32());
 948   if (addr > 0) {
 949     addr += displacement;
 950     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
 951   }
 952   else {
 953     trace(Callstack_depth+1, "run") << "null address; skipping displacement" << end();
 954   }
 955   break;
 956 }
 957 
 958 :(code)
 959 void test_add_r32_to_mem_at_rm32_plus_negative_disp32() {
 960   Reg[EBX].i = 0x10;  // source
 961   Reg[EAX].i = 0x2001;  // dest
 962   run(
 963       "== code 0x1\n"
 964       // op     ModR/M  SIB   displacement  immediate
 965       "  01     98            ff ff ff ff             \n"  // add EBX to *(EAX-1)
 966       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
 967       "== data 0x2000\n"
 968       "01 00 00 00\n"  // 1
 969   );
 970   CHECK_TRACE_CONTENTS(
 971       "run: add EBX to r/m32\n"
 972       "run: effective address is initially 0x00002001 (EAX)\n"
 973       "run: effective address is 0x00002000 (after adding disp32)\n"
 974       "run: storing 0x00000011\n"
 975   );
 976 }
 977 
 978 //:: copy address (lea)
 979 
 980 :(before "End Initialize Op Names")
 981 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
 982 
 983 :(code)
 984 void test_copy_address() {
 985   Reg[EAX].u = 0x2000;
 986   run(
 987       "== code 0x1\n"
 988       // op     ModR/M  SIB   displacement  immediate
 989       "  8d     18                                    \n"  // copy address in EAX into EBX
 990       // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
 991   );
 992   CHECK_TRACE_CONTENTS(
 993       "run: copy address into EBX\n"
 994       "run: effective address is 0x00002000 (EAX)\n"
 995   );
 996 }
 997 
 998 :(before "End Single-Byte Opcodes")
 999 case 0x8d: {  // copy address of m32 to r32
1000   const uint8_t modrm = next();
1001   const uint8_t arg1 = (modrm>>3)&0x7;
1002   trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
1003   Reg[arg1].u = effective_address_number(modrm);
1004   break;
1005 }