https://github.com/akkartik/mu/blob/master/subx/013direct_addressing.cc

      1 //: operating directly on a register
      2
      3 :(before "End Initialize Op Names")
      4 put_new(Name, "01", "add r32 to rm32 (add)");
      5
      6 :(code)
      7 void test_add_r32_to_r32() {
      8   Reg[EAX].i = 0x10;
      9   Reg[EBX].i = 1;
     10   run(
     11       "== 0x1\n"  // code segment
     12       // op     ModR/M  SIB   displacement  immediate
     13       "  01     d8                                    \n" // add EBX to EAX
     14       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     15   );
     16   CHECK_TRACE_CONTENTS(
     17       "run: add EBX to r/m32\n"
     18       "run: r/m32 is EAX\n"
     19       "run: storing 0x00000011\n"
     20   );
     21 }
     22
     23 :(before "End Single-Byte Opcodes")
     24 case 0x01: {  // add r32 to r/m32
     25   uint8_t modrm = next();
     26   uint8_t arg2 = (modrm>>3)&0x7;
     27   trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end();
     28   int32_t* arg1 = effective_address(modrm);
     29   BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i);
     30   break;
     31 }
     32
     33 :(code)
     34 // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
     35 // We return a pointer so that instructions can write to multiple bytes in
     36 // 'Mem' at once.
     37 // beware: will eventually have side-effects
     38 int32_t* effective_address(uint8_t modrm) {
     39   const uint8_t mod = (modrm>>6);
     40   // ignore middle 3 'reg opcode' bits
     41   const uint8_t rm = modrm & 0x7;
     42   if (mod == 3) {
     43     // mod 3 is just register direct addressing
     44     trace(Callstack_depth+1, "run") << "r/m32 is " << rname(rm) << end();
     45     return &Reg[rm].i;
     46   }
     47   uint32_t addr = effective_address_number(modrm);
     48   trace(Callstack_depth+1, "run") << "effective address contains " << read_mem_i32(addr) << end();
     49   return mem_addr_i32(addr);
     50 }
     51
     52 // beware: will eventually have side-effects
     53 uint32_t effective_address_number(uint8_t modrm) {
     54   const uint8_t mod = (modrm>>6);
     55   // ignore middle 3 'reg opcode' bits
     56   const uint8_t rm = modrm & 0x7;
     57   uint32_t addr = 0;
     58   switch (mod) {
     59   case 3:
     60     // mod 3 is just register direct addressing
     61     raise << "unexpected direct addressing mode\n" << end();
     62     return 0;
     63   // End Mod Special-cases(addr)
     64   default:
     65     cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
     66     exit(1);
     67   }
     68   //: other mods are indirect, and they'll set addr appropriately
     69   // Found effective_address(addr)
     70   return addr;
     71 }
     72
     73 string rname(uint8_t r) {
     74   switch (r) {
     75   case 0: return "EAX";
     76   case 1: return "ECX";
     77   case 2: return "EDX";
     78   case 3: return "EBX";
     79   case 4: return "ESP";
     80   case 5: return "EBP";
     81   case 6: return "ESI";
     82   case 7: return "EDI";
     83   default: raise << "invalid register " << r << '\n' << end();  return "";
     84   }
     85 }
     86
     87 //:: subtract
     88
     89 :(before "End Initialize Op Names")
     90 put_new(Name, "29", "subtract r32 from rm32 (sub)");
     91
     92 :(code)
     93 void test_subtract_r32_from_r32() {
     94   Reg[EAX].i = 10;
     95   Reg[EBX].i = 1;
     96   run(
     97       "== 0x1\n"  // code segment
     98       // op     ModR/M  SIB   displacement  immediate
     99       "  29     d8                                    \n"  // subtract EBX from EAX
    100       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    101   );
    102   CHECK_TRACE_CONTENTS(
    103       "run: subtract EBX from r/m32\n"
    104       "run: r/m32 is EAX\n"
    105       "run: storing 0x00000009\n"
    106   );
    107 }
    108
    109 :(before "End Single-Byte Opcodes")
    110 case 0x29: {  // subtract r32 from r/m32
    111   const uint8_t modrm = next();
    112   const uint8_t arg2 = (modrm>>3)&0x7;
    113   trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
    114   int32_t* arg1 = effective_address(modrm);
    115   BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i);
    116   break;
    117 }
    118
    119 //:: multiply
    120
    121 :(before "End Initialize Op Names")
    122 put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)");
    123
    124 :(code)
    125 void test_multiply_eax_by_r32() {
    126   Reg[EAX].i = 4;
    127   Reg[ECX].i = 3;
    128   run(
    129       "== 0x1\n"  // code segment
    130       // op     ModR/M  SIB   displacement  immediate
    131       "  f7     e1                                    \n"  // multiply EAX by ECX
    132       // ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX)
    133   );
    134   CHECK_TRACE_CONTENTS(
    135       "run: operate on r/m32\n"
    136       "run: r/m32 is ECX\n"
    137       "run: subop: multiply EAX by r/m32\n"
    138       "run: storing 0x0000000c\n"
    139   );
    140 }
    141
    142 :(before "End Single-Byte Opcodes")
    143 case 0xf7: {
    144   const uint8_t modrm = next();
    145   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
    146   int32_t* arg1 = effective_address(modrm);
    147   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
    148   switch (subop) {
    149   case 4: {  // mul unsigned EAX by r/m32
    150     trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end();
    151     const uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1);
    152     Reg[EAX].u = result & 0xffffffff;
    153     Reg[EDX].u = result >> 32;
    154     OF = (Reg[EDX].u != 0);
    155     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
    156     break;
    157   }
    158   // End Op f7 Subops
    159   default:
    160     cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n';
    161     exit(1);
    162   }
    163   break;
    164 }
    165
    166 //:
    167
    168 :(before "End Initialize Op Names")
    169 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)");
    170
    171 :(code)
    172 void test_multiply_r32_into_r32() {
    173   Reg[EAX].i = 4;
    174   Reg[EBX].i = 2;
    175   run(
    176       "== 0x1\n"  // code segment
    177       // op     ModR/M  SIB   displacement  immediate
    178       "  0f af  d8                                    \n"  // subtract EBX into EAX
    179       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    180   );
    181   CHECK_TRACE_CONTENTS(
    182       "run: multiply r/m32 into EBX\n"
    183       "run: r/m32 is EAX\n"
    184       "run: storing 0x00000008\n"
    185   );
    186 }
    187
    188 :(before "End Two-Byte Opcodes Starting With 0f")
    189 case 0xaf: {  // multiply r32 into r/m32
    190   const uint8_t modrm = next();
    191   const uint8_t arg2 = (modrm>>3)&0x7;
    192   trace(Callstack_depth+1, "run") << "multiply r/m32 into " << rname(arg2) << end();
    193   const int32_t* arg1 = effective_address(modrm);
    194   BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1);
    195   break;
    196 }
    197
    198 //:: negate
    199
    200 :(code)
    201 void test_negate_r32() {
    202   Reg[EBX].i = 1;
    203   run(
    204       "== 0x1\n"  // code segment
    205       // op     ModR/M  SIB   displacement  immediate
    206       "  f7 db                                        \n"  // negate EBX
    207       // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
    208   );
    209   CHECK_TRACE_CONTENTS(
    210       "run: operate on r/m32\n"
    211       "run: r/m32 is EBX\n"
    212       "run: subop: negate\n"
    213       "run: storing 0xffffffff\n"
    214   );
    215 }
    216
    217 :(before "End Op f7 Subops")
    218 case 3: {  // negate r/m32
    219   trace(Callstack_depth+1, "run") << "subop: negate" << end();
    220   // one case that can overflow
    221   if (static_cast<uint32_t>(*arg1) == 0x80000000) {
    222     trace(Callstack_depth+1, "run") << "overflow" << end();
    223     SF = true;
    224     ZF = false;
    225     OF = true;
    226     break;
    227   }
    228   *arg1 = -(*arg1);
    229   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
    230   SF = (*arg1 >> 31);
    231   ZF = (*arg1 == 0);
    232   OF = false;
    233   break;
    234 }
    235
    236 :(code)
    237 // negate can overflow in exactly one situation
    238 void test_negate_can_overflow() {
    239   Reg[EBX].i = 0x80000000;  // INT_MIN
    240   run(
    241       "== 0x1\n"  // code segment
    242       // op     ModR/M  SIB   displacement  immediate
    243       "  f7 db                                        \n"  // negate EBX
    244       // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
    245   );
    246   CHECK_TRACE_CONTENTS(
    247       "run: operate on r/m32\n"
    248       "run: r/m32 is EBX\n"
    249       "run: subop: negate\n"
    250       "run: overflow\n"
    251   );
    252 }
    253
    254 //:: shift left
    255
    256 :(before "End Initialize Op Names")
    257 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)");
    258
    259 :(code)
    260 void test_shift_left_r32_with_cl() {
    261   Reg[EBX].i = 13;
    262   Reg[ECX].i = 1;
    263   run(
    264       "== 0x1\n"  // code segment
    265       // op     ModR/M  SIB   displacement  immediate
    266       "  d3     e3                                    \n"  // shift EBX left by CL bits
    267       // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
    268   );
    269   CHECK_TRACE_CONTENTS(
    270       "run: operate on r/m32\n"
    271       "run: r/m32 is EBX\n"
    272       "run: subop: shift left by CL bits\n"
    273       "run: storing 0x0000001a\n"
    274   );
    275 }
    276
    277 :(before "End Single-Byte Opcodes")
    278 case 0xd3: {
    279   const uint8_t modrm = next();
    280   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
    281   int32_t* arg1 = effective_address(modrm);
    282   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
    283   switch (subop) {
    284   case 4: {  // shift left r/m32 by CL
    285     trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
    286     uint8_t count = Reg[ECX].u & 0x1f;
    287     // OF is only defined if count is 1
    288     if (count == 1) {
    289       bool msb = (*arg1 & 0x80000000) >> 1;
    290       bool pnsb = (*arg1 & 0x40000000);
    291       OF = (msb != pnsb);
    292     }
    293     *arg1 = (*arg1 << count);
    294     ZF = (*arg1 == 0);
    295     SF = (*arg1 < 0);
    296     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
    297     break;
    298   }
    299   // End Op d3 Subops
    300   default:
    301     cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n';
    302     exit(1);
    303   }
    304   break;
    305 }
    306
    307 //:: shift right arithmetic
    308
    309 :(code)
    310 void test_shift_right_arithmetic_r32_with_cl() {
    311   Reg[EBX].i = 26;
    312   Reg[ECX].i = 1;
    313   run(
    314       "== 0x1\n"  // code segment
    315       // op     ModR/M  SIB   displacement  immediate
    316       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
    317       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
    318   );
    319   CHECK_TRACE_CONTENTS(
    320       "run: operate on r/m32\n"
    321       "run: r/m32 is EBX\n"
    322       "run: subop: shift right by CL bits, while preserving sign\n"
    323       "run: storing 0x0000000d\n"
    324   );
    325 }
    326
    327 :(before "End Op d3 Subops")
    328 case 7: {  // shift right r/m32 by CL, preserving sign
    329   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
    330   uint8_t count = Reg[ECX].u & 0x1f;
    331   *arg1 = (*arg1 >> count);
    332   ZF = (*arg1 == 0);
    333   SF = (*arg1 < 0);
    334   // OF is only defined if count is 1
    335   if (count == 1) OF = false;
    336   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
    337   break;
    338 }
    339
    340 :(code)
    341 void test_shift_right_arithmetic_odd_r32_with_cl() {
    342   Reg[EBX].i = 27;
    343   Reg[ECX].i = 1;
    344   run(
    345       "== 0x1\n"  // code segment
    346       // op     ModR/M  SIB   displacement  immediate
    347       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
    348       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
    349   );
    350   CHECK_TRACE_CONTENTS(
    351       "run: operate on r/m32\n"
    352       "run: r/m32 is EBX\n"
    353       "run: subop: shift right by CL bits, while preserving sign\n"
    354       // result: 13
    355       "run: storing 0x0000000d\n"
    356   );
    357 }
    358
    359 void test_shift_right_arithmetic_negative_r32_with_cl() {
    360   Reg[EBX].i = 0xfffffffd;  // -3
    361   Reg[ECX].i = 1;
    362   run(
    363       "== 0x1\n"  // code segment
    364       // op     ModR/M  SIB   displacement  immediate
    365       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
    366       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
    367   );
    368   CHECK_TRACE_CONTENTS(
    369       "run: operate on r/m32\n"
    370       "run: r/m32 is EBX\n"
    371       "run: subop: shift right by CL bits, while preserving sign\n"
    372       // result: -2
    373       "run: storing 0xfffffffe\n"
    374   );
    375 }
    376
    377 //:: shift right logical
    378
    379 :(code)
    380 void test_shift_right_logical_r32_with_cl() {
    381   Reg[EBX].i = 26;
    382   Reg[ECX].i = 1;
    383   run(
    384       "== 0x1\n"  // code segment
    385       // op     ModR/M  SIB   displacement  immediate
    386       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
    387       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
    388   );
    389   CHECK_TRACE_CONTENTS(
    390       "run: operate on r/m32\n"
    391       "run: r/m32 is EBX\n"
    392       "run: subop: shift right by CL bits, while padding zeroes\n"
    393       // result: 13
    394       "run: storing 0x0000000d\n"
    395   );
    396 }
    397
    398 :(before "End Op d3 Subops")
    399 case 5: {  // shift right r/m32 by CL, preserving sign
    400   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
    401   uint8_t count = Reg[ECX].u & 0x1f;
    402   // OF is only defined if count is 1
    403   if (count == 1) {
    404     bool msb = (*arg1 & 0x80000000) >> 1;
    405     bool pnsb = (*arg1 & 0x40000000);
    406     OF = (msb != pnsb);
    407   }
    408   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
    409   *uarg1 = (*uarg1 >> count);
    410   ZF = (*uarg1 == 0);
    411   // result is always positive by definition
    412   SF = false;
    413   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
    414   break;
    415 }
    416
    417 :(code)
    418 void test_shift_right_logical_odd_r32_with_cl() {
    419   Reg[EBX].i = 27;
    420   Reg[ECX].i = 1;
    421   run(
    422       "== 0x1\n"  // code segment
    423       // op     ModR/M  SIB   displacement  immediate
    424       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
    425       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
    426   );
    427   CHECK_TRACE_CONTENTS(
    428       "run: operate on r/m32\n"
    429       "run: r/m32 is EBX\n"
    430       "run: subop: shift right by CL bits, while padding zeroes\n"
    431       // result: 13
    432       "run: storing 0x0000000d\n"
    433   );
    434 }
    435
    436 void test_shift_right_logical_negative_r32_with_cl() {
    437   Reg[EBX].i = 0xfffffffd;
    438   Reg[ECX].i = 1;
    439   run(
    440       "== 0x1\n"  // code segment
    441       // op     ModR/M  SIB   displacement  immediate
    442       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
    443       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
    444   );
    445   CHECK_TRACE_CONTENTS(
    446       "run: operate on r/m32\n"
    447       "run: r/m32 is EBX\n"
    448       "run: subop: shift right by CL bits, while padding zeroes\n"
    449       "run: storing 0x7ffffffe\n"
    450   );
    451 }
    452
    453 //:: and
    454
    455 :(before "End Initialize Op Names")
    456 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
    457
    458 :(code)
    459 void test_and_r32_with_r32() {
    460   Reg[EAX].i = 0x0a0b0c0d;
    461   Reg[EBX].i = 0x000000ff;
    462   run(
    463       "== 0x1\n"  // code segment
    464       // op     ModR/M  SIB   displacement  immediate
    465       "  21     d8                                    \n"  // and EBX with destination EAX
    466       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    467   );
    468   CHECK_TRACE_CONTENTS(
    469       "run: and EBX with r/m32\n"
    470       "run: r/m32 is EAX\n"
    471       "run: storing 0x0000000d\n"
    472   );
    473 }
    474
    475 :(before "End Single-Byte Opcodes")
    476 case 0x21: {  // and r32 with r/m32
    477   const uint8_t modrm = next();
    478   const uint8_t arg2 = (modrm>>3)&0x7;
    479   trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end();
    480   int32_t* arg1 = effective_address(modrm);
    481   BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
    482   break;
    483 }
    484
    485 //:: or
    486
    487 :(before "End Initialize Op Names")
    488 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
    489
    490 :(code)
    491 void test_or_r32_with_r32() {
    492   Reg[EAX].i = 0x0a0b0c0d;
    493   Reg[EBX].i = 0xa0b0c0d0;
    494   run(
    495       "== 0x1\n"  // code segment
    496       // op     ModR/M  SIB   displacement  immediate
    497       "  09     d8                                    \n"  // or EBX with destination EAX
    498       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    499   );
    500   CHECK_TRACE_CONTENTS(
    501       "run: or EBX with r/m32\n"
    502       "run: r/m32 is EAX\n"
    503       "run: storing 0xaabbccdd\n"
    504   );
    505 }
    506
    507 :(before "End Single-Byte Opcodes")
    508 case 0x09: {  // or r32 with r/m32
    509   const uint8_t modrm = next();
    510   const uint8_t arg2 = (modrm>>3)&0x7;
    511   trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end();
    512   int32_t* arg1 = effective_address(modrm);
    513   BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
    514   break;
    515 }
    516
    517 //:: xor
    518
    519 :(before "End Initialize Op Names")
    520 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
    521
    522 :(code)
    523 void test_xor_r32_with_r32() {
    524   Reg[EAX].i = 0x0a0b0c0d;
    525   Reg[EBX].i = 0xaabbc0d0;
    526   run(
    527       "== 0x1\n"  // code segment
    528       // op     ModR/M  SIB   displacement  immediate
    529       "  31     d8                                    \n"  // xor EBX with destination EAX
    530       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    531   );
    532   CHECK_TRACE_CONTENTS(
    533       "run: xor EBX with r/m32\n"
    534       "run: r/m32 is EAX\n"
    535       "run: storing 0xa0b0ccdd\n"
    536   );
    537 }
    538
    539 :(before "End Single-Byte Opcodes")
    540 case 0x31: {  // xor r32 with r/m32
    541   const uint8_t modrm = next();
    542   const uint8_t arg2 = (modrm>>3)&0x7;
    543   trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end();
    544   int32_t* arg1 = effective_address(modrm);
    545   BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
    546   break;
    547 }
    548
    549 //:: not
    550
    551 :(code)
    552 void test_not_r32() {
    553   Reg[EBX].i = 0x0f0f00ff;
    554   run(
    555       "== 0x1\n"  // code segment
    556       // op     ModR/M  SIB   displacement  immediate
    557       "  f7     d3                                    \n"  // not EBX
    558       // ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX)
    559   );
    560   CHECK_TRACE_CONTENTS(
    561       "run: operate on r/m32\n"
    562       "run: r/m32 is EBX\n"
    563       "run: subop: not\n"
    564       "run: storing 0xf0f0ff00\n"
    565   );
    566 }
    567
    568 :(before "End Op f7 Subops")
    569 case 2: {  // not r/m32
    570   trace(Callstack_depth+1, "run") << "subop: not" << end();
    571   *arg1 = ~(*arg1);
    572   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
    573   SF = (*arg1 >> 31);
    574   ZF = (*arg1 == 0);
    575   OF = false;
    576   break;
    577 }
    578
    579 //:: compare (cmp)
    580
    581 :(before "End Initialize Op Names")
    582 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
    583
    584 :(code)
    585 void test_compare_r32_with_r32_greater() {
    586   Reg[EAX].i = 0x0a0b0c0d;
    587   Reg[EBX].i = 0x0a0b0c07;
    588   run(
    589       "== 0x1\n"  // code segment
    590       // op     ModR/M  SIB   displacement  immediate
    591       "  39     d8                                    \n"  // compare EBX with EAX
    592       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    593   );
    594   CHECK_TRACE_CONTENTS(
    595       "run: compare EBX with r/m32\n"
    596       "run: r/m32 is EAX\n"
    597       "run: SF=0; ZF=0; OF=0\n"
    598   );
    599 }
    600
    601 :(before "End Single-Byte Opcodes")
    602 case 0x39: {  // set SF if r/m32 < r32
    603   const uint8_t modrm = next();
    604   const uint8_t reg2 = (modrm>>3)&0x7;
    605   trace(Callstack_depth+1, "run") << "compare " << rname(reg2) << " with r/m32" << end();
    606   const int32_t* arg1 = effective_address(modrm);
    607   const int32_t arg2 = Reg[reg2].i;
    608   const int32_t tmp1 = *arg1 - arg2;
    609   SF = (tmp1 < 0);
    610   ZF = (tmp1 == 0);
    611   const int64_t tmp2 = *arg1 - arg2;
    612   OF = (tmp1 != tmp2);
    613   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
    614   break;
    615 }
    616
    617 :(code)
    618 void test_compare_r32_with_r32_lesser() {
    619   Reg[EAX].i = 0x0a0b0c07;
    620   Reg[EBX].i = 0x0a0b0c0d;
    621   run(
    622       "== 0x1\n"  // code segment
    623       // op     ModR/M  SIB   displacement  immediate
    624       "  39     d8                                    \n"  // compare EBX with EAX
    625       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    626   );
    627   CHECK_TRACE_CONTENTS(
    628       "run: compare EBX with r/m32\n"
    629       "run: r/m32 is EAX\n"
    630       "run: SF=1; ZF=0; OF=0\n"
    631   );
    632 }
    633
    634 void test_compare_r32_with_r32_equal() {
    635   Reg[EAX].i = 0x0a0b0c0d;
    636   Reg[EBX].i = 0x0a0b0c0d;
    637   run(
    638       "== 0x1\n"  // code segment
    639       // op     ModR/M  SIB   displacement  immediate
    640       "  39     d8                                    \n"  // compare EBX with EAX
    641       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    642   );
    643   CHECK_TRACE_CONTENTS(
    644       "run: compare EBX with r/m32\n"
    645       "run: r/m32 is EAX\n"
    646       "run: SF=0; ZF=1; OF=0\n"
    647   );
    648 }
    649
    650 //:: copy (mov)
    651
    652 :(before "End Initialize Op Names")
    653 put_new(Name, "89", "copy r32 to rm32 (mov)");
    654
    655 :(code)
    656 void test_copy_r32_to_r32() {
    657   Reg[EBX].i = 0xaf;
    658   run(
    659       "== 0x1\n"  // code segment
    660       // op     ModR/M  SIB   displacement  immediate
    661       "  89     d8                                    \n"  // copy EBX to EAX
    662       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    663   );
    664   CHECK_TRACE_CONTENTS(
    665       "run: copy EBX to r/m32\n"
    666       "run: r/m32 is EAX\n"
    667       "run: storing 0x000000af\n"
    668   );
    669 }
    670
    671 :(before "End Single-Byte Opcodes")
    672 case 0x89: {  // copy r32 to r/m32
    673   const uint8_t modrm = next();
    674   const uint8_t rsrc = (modrm>>3)&0x7;
    675   trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
    676   int32_t* dest = effective_address(modrm);
    677   *dest = Reg[rsrc].i;
    678   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end();
    679   break;
    680 }
    681
    682 //:: xchg
    683
    684 :(before "End Initialize Op Names")
    685 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
    686
    687 :(code)
    688 void test_xchg_r32_with_r32() {
    689   Reg[EBX].i = 0xaf;
    690   Reg[EAX].i = 0x2e;
    691   run(
    692       "== 0x1\n"  // code segment
    693       // op     ModR/M  SIB   displacement  immediate
    694       "  87     d8                                    \n"  // exchange EBX with EAX
    695       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    696   );
    697   CHECK_TRACE_CONTENTS(
    698       "run: exchange EBX with r/m32\n"
    699       "run: r/m32 is EAX\n"
    700       "run: storing 0x000000af in r/m32\n"
    701       "run: storing 0x0000002e in EBX\n"
    702   );
    703 }
    704
    705 :(before "End Single-Byte Opcodes")
    706 case 0x87: {  // exchange r32 with r/m32
    707   const uint8_t modrm = next();
    708   const uint8_t reg2 = (modrm>>3)&0x7;
    709   trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
    710   int32_t* arg1 = effective_address(modrm);
    711   const int32_t tmp = *arg1;
    712   *arg1 = Reg[reg2].i;
    713   Reg[reg2].i = tmp;
    714   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
    715   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
    716   break;
    717 }
    718
    719 //:: increment
    720
    721 :(before "End Initialize Op Names")
    722 put_new(Name, "40", "increment EAX (inc)");
    723 put_new(Name, "41", "increment ECX (inc)");
    724 put_new(Name, "42", "increment EDX (inc)");
    725 put_new(Name, "43", "increment EBX (inc)");
    726 put_new(Name, "44", "increment ESP (inc)");
    727 put_new(Name, "45", "increment EBP (inc)");
    728 put_new(Name, "46", "increment ESI (inc)");
    729 put_new(Name, "47", "increment EDI (inc)");
    730
    731 :(code)
    732 void test_increment_r32() {
    733   Reg[ECX].u = 0x1f;
    734   run(
    735       "== 0x1\n"  // code segment
    736       // op     ModR/M  SIB   displacement  immediate
    737       "  41                                           \n"  // increment ECX
    738   );
    739   CHECK_TRACE_CONTENTS(
    740       "run: increment ECX\n"
    741       "run: storing value 0x00000020\n"
    742   );
    743 }
    744
    745 :(before "End Single-Byte Opcodes")
    746 case 0x40:
    747 case 0x41:
    748 case 0x42:
    749 case 0x43:
    750 case 0x44:
    751 case 0x45:
    752 case 0x46:
    753 case 0x47: {  // increment r32
    754   const uint8_t reg = op & 0x7;
    755   trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end();
    756   ++Reg[reg].u;
    757   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
    758   break;
    759 }
    760
    761 :(before "End Initialize Op Names")
    762 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)");
    763
    764 :(code)
    765 void test_increment_rm32() {
    766   Reg[EAX].u = 0x20;
    767   run(
    768       "== 0x1\n"  // code segment
    769       // op     ModR/M  SIB   displacement  immediate
    770       "  ff     c0                                    \n"  // increment EAX
    771       // ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX)
    772   );
    773   CHECK_TRACE_CONTENTS(
    774       "run: increment r/m32\n"
    775       "run: r/m32 is EAX\n"
    776       "run: storing value 0x00000021\n"
    777   );
    778 }
    779
    780 :(before "End Single-Byte Opcodes")
    781 case 0xff: {
    782   const uint8_t modrm = next();
    783   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
    784   switch (subop) {
    785     case 0: {  // increment r/m32
    786       trace(Callstack_depth+1, "run") << "increment r/m32" << end();
    787       int32_t* arg = effective_address(modrm);
    788       ++*arg;
    789       trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
    790       break;
    791     }
    792     default:
    793       cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n';
    794       DUMP("");
    795       exit(1);
    796     // End Op ff Subops
    797   }
    798   break;
    799 }
    800
    801 //:: decrement
    802
    803 :(before "End Initialize Op Names")
    804 put_new(Name, "48", "decrement EAX (dec)");
    805 put_new(Name, "49", "decrement ECX (dec)");
    806 put_new(Name, "4a", "decrement EDX (dec)");
    807 put_new(Name, "4b", "decrement EBX (dec)");
    808 put_new(Name, "4c", "decrement ESP (dec)");
    809 put_new(Name, "4d", "decrement EBP (dec)");
    810 put_new(Name, "4e", "decrement ESI (dec)");
    811 put_new(Name, "4f", "decrement EDI (dec)");
    812
    813 :(code)
    814 void test_decrement_r32() {
    815   Reg[ECX].u = 0x1f;
    816   run(
    817       "== 0x1\n"  // code segment
    818       // op     ModR/M  SIB   displacement  immediate
    819       "  49                                           \n"  // decrement ECX
    820   );
    821   CHECK_TRACE_CONTENTS(
    822       "run: decrement ECX\n"
    823       "run: storing value 0x0000001e\n"
    824   );
    825 }
    826
    827 :(before "End Single-Byte Opcodes")
    828 case 0x48:
    829 case 0x49:
    830 case 0x4a:
    831 case 0x4b:
    832 case 0x4c:
    833 case 0x4d:
    834 case 0x4e:
    835 case 0x4f: {  // decrement r32
    836   const uint8_t reg = op & 0x7;
    837   trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end();
    838   --Reg[reg].u;
    839   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
    840   break;
    841 }
    842
    843 :(code)
    844 void test_decrement_rm32() {
    845   Reg[EAX].u = 0x20;
    846   run(
    847       "== 0x1\n"  // code segment
    848       // op     ModR/M  SIB   displacement  immediate
    849       "  ff     c8                                    \n"  // decrement EAX
    850       // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX)
    851   );
    852   CHECK_TRACE_CONTENTS(
    853       "run: decrement r/m32\n"
    854       "run: r/m32 is EAX\n"
    855       "run: storing value 0x0000001f\n"
    856   );
    857 }
    858
    859 :(before "End Op ff Subops")
    860 case 1: {  // decrement r/m32
    861   trace(Callstack_depth+1, "run") << "decrement r/m32" << end();
    862   int32_t* arg = effective_address(modrm);
    863   --*arg;
    864   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
    865   break;
    866 }
    867
    868 //:: push
    869
    870 :(before "End Initialize Op Names")
    871 put_new(Name, "50", "push EAX to stack (push)");
    872 put_new(Name, "51", "push ECX to stack (push)");
    873 put_new(Name, "52", "push EDX to stack (push)");
    874 put_new(Name, "53", "push EBX to stack (push)");
    875 put_new(Name, "54", "push ESP to stack (push)");
    876 put_new(Name, "55", "push EBP to stack (push)");
    877 put_new(Name, "56", "push ESI to stack (push)");
    878 put_new(Name, "57", "push EDI to stack (push)");
    879
    880 :(code)
    881 void test_push_r32() {
    882   Reg[ESP].u = 0x64;
    883   Reg[EBX].i = 0x0000000a;
    884   run(
    885       "== 0x1\n"  // code segment
    886       // op     ModR/M  SIB   displacement  immediate
    887       "  53                                           \n"  // push EBX to stack
    888   );
    889   CHECK_TRACE_CONTENTS(
    890       "run: push EBX\n"
    891       "run: decrementing ESP to 0x00000060\n"
    892       "run: pushing value 0x0000000a\n"
    893   );
    894 }
    895
    896 :(before "End Single-Byte Opcodes")
    897 case 0x50:
    898 case 0x51:
    899 case 0x52:
    900 case 0x53:
    901 case 0x54:
    902 case 0x55:
    903 case 0x56:
    904 case 0x57: {  // push r32 to stack
    905   uint8_t reg = op & 0x7;
    906   trace(Callstack_depth+1, "run") << "push " << rname(reg) << end();
    907 //?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
    908   push(Reg[reg].u);
    909   break;
    910 }
    911
    912 //:: pop
    913
    914 :(before "End Initialize Op Names")
    915 put_new(Name, "58", "pop top of stack to EAX (pop)");
    916 put_new(Name, "59", "pop top of stack to ECX (pop)");
    917 put_new(Name, "5a", "pop top of stack to EDX (pop)");
    918 put_new(Name, "5b", "pop top of stack to EBX (pop)");
    919 put_new(Name, "5c", "pop top of stack to ESP (pop)");
    920 put_new(Name, "5d", "pop top of stack to EBP (pop)");
    921 put_new(Name, "5e", "pop top of stack to ESI (pop)");
    922 put_new(Name, "5f", "pop top of stack to EDI (pop)");
    923
    924 :(code)
    925 void test_pop_r32() {
    926   Reg[ESP].u = 0x02000000;
    927   Mem.push_back(vma(0x02000000));  // manually allocate memory
    928   write_mem_i32(0x02000000, 0x0000000a);  // ..before this write
    929   run(
    930       "== 0x1\n"  // code segment
    931       // op     ModR/M  SIB   displacement  immediate
    932       "  5b                                           \n"  // pop stack to EBX
    933       "== 0x2000\n"  // data segment
    934       "0a 00 00 00\n"  // 0x0000000a
    935   );
    936   CHECK_TRACE_CONTENTS(
    937       "run: pop into EBX\n"
    938       "run: popping value 0x0000000a\n"
    939       "run: incrementing ESP to 0x02000004\n"
    940   );
    941 }
    942
    943 :(before "End Single-Byte Opcodes")
    944 case 0x58:
    945 case 0x59:
    946 case 0x5a:
    947 case 0x5b:
    948 case 0x5c:
    949 case 0x5d:
    950 case 0x5e:
    951 case 0x5f: {  // pop stack into r32
    952   const uint8_t reg = op & 0x7;
    953   trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end();
    954 //?   cerr << "pop from " << Reg[ESP].u << '\n';
    955   Reg[reg].u = pop();
    956 //?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
    957   break;
    958 }
    959 :(code)
    960 uint32_t pop() {
    961   const uint32_t result = read_mem_u32(Reg[ESP].u);
    962   trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end();
    963   Reg[ESP].u += 4;
    964   trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
    965   return result;
    966 }