https://github.com/akkartik/mu/blob/main/linux/bootstrap/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       "== code 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* signed_arg1 = effective_address(modrm);
      29   int32_t signed_result = *signed_arg1 + Reg[arg2].i;
      30   SF = (signed_result < 0);
      31   ZF = (signed_result == 0);
      32   int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) + Reg[arg2].i;
      33   OF = (signed_result != signed_full_result);
      34   // set CF
      35   uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
      36   uint32_t unsigned_result = unsigned_arg1 + Reg[arg2].u;
      37   uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) + Reg[arg2].u;
      38   CF = (unsigned_result != unsigned_full_result);
      39   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
      40   *signed_arg1 = signed_result;
      41   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
      42   break;
      43 }
      44
      45 :(code)
      46 void test_add_r32_to_r32_signed_overflow() {
      47   Reg[EAX].i = INT32_MAX;
      48   Reg[EBX].i = 1;
      49   run(
      50       "== code 0x1\n"  // code segment
      51       // op     ModR/M  SIB   displacement  immediate
      52       "  01     d8                                    \n" // add EBX to EAX
      53       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
      54   );
      55   CHECK_TRACE_CONTENTS(
      56       "run: add EBX to r/m32\n"
      57       "run: r/m32 is EAX\n"
      58       "run: SF=1; ZF=0; CF=0; OF=1\n"
      59       "run: storing 0x80000000\n"
      60   );
      61 }
      62
      63 void test_add_r32_to_r32_unsigned_overflow() {
      64   Reg[EAX].u = UINT32_MAX;
      65   Reg[EBX].u = 1;
      66   run(
      67       "== code 0x1\n"  // code segment
      68       // op     ModR/M  SIB   displacement  immediate
      69       "  01     d8                                    \n" // add EBX to EAX
      70       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
      71   );
      72   CHECK_TRACE_CONTENTS(
      73       "run: add EBX to r/m32\n"
      74       "run: r/m32 is EAX\n"
      75       "run: SF=0; ZF=1; CF=1; OF=0\n"
      76       "run: storing 0x00000000\n"
      77   );
      78 }
      79
      80 void test_add_r32_to_r32_unsigned_and_signed_overflow() {
      81   Reg[EAX].i = Reg[EBX].i = INT32_MIN;
      82   run(
      83       "== code 0x1\n"  // code segment
      84       // op     ModR/M  SIB   displacement  immediate
      85       "  01     d8                                    \n" // add EBX to EAX
      86       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
      87   );
      88   CHECK_TRACE_CONTENTS(
      89       "run: add EBX to r/m32\n"
      90       "run: r/m32 is EAX\n"
      91       "run: SF=0; ZF=1; CF=1; OF=1\n"
      92       "run: storing 0x00000000\n"
      93   );
      94 }
      95
      96 :(code)
      97 // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
      98 // We return a pointer so that instructions can write to multiple bytes in
      99 // 'Mem' at once.
     100 // beware: will eventually have side-effects
     101 int32_t* effective_address(uint8_t modrm) {
     102   const uint8_t mod = (modrm>>6);
     103   // ignore middle 3 'reg opcode' bits
     104   const uint8_t rm = modrm & 0x7;
     105   if (mod == 3) {
     106     // mod 3 is just register direct addressing
     107     trace(Callstack_depth+1, "run") << "r/m32 is " << rname(rm) << end();
     108     return &Reg[rm].i;
     109   }
     110   uint32_t addr = effective_address_number(modrm);
     111   trace(Callstack_depth+1, "run") << "effective address contains 0x" << HEXWORD << read_mem_i32(addr) << end();
     112   return mem_addr_i32(addr);
     113 }
     114
     115 // beware: will eventually have side-effects
     116 uint32_t effective_address_number(uint8_t modrm) {
     117   const uint8_t mod = (modrm>>6);
     118   // ignore middle 3 'reg opcode' bits
     119   const uint8_t rm = modrm & 0x7;
     120   uint32_t addr = 0;
     121   switch (mod) {
     122   case 3:
     123     // mod 3 is just register direct addressing
     124     raise << "unexpected direct addressing mode\n" << end();
     125     return 0;
     126   // End Mod Special-cases(addr)
     127   default:
     128     cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
     129     exit(1);
     130   }
     131   //: other mods are indirect, and they'll set addr appropriately
     132   // Found effective_address(addr)
     133   return addr;
     134 }
     135
     136 string rname(uint8_t r) {
     137   switch (r) {
     138   case 0: return "EAX";
     139   case 1: return "ECX";
     140   case 2: return "EDX";
     141   case 3: return "EBX";
     142   case 4: return "ESP";
     143   case 5: return "EBP";
     144   case 6: return "ESI";
     145   case 7: return "EDI";
     146   default: raise << "invalid register " << r << '\n' << end();  return "";
     147   }
     148 }
     149
     150 //:: subtract
     151
     152 :(before "End Initialize Op Names")
     153 put_new(Name, "29", "subtract r32 from rm32 (sub)");
     154
     155 :(code)
     156 void test_subtract_r32_from_r32() {
     157   Reg[EAX].i = 10;
     158   Reg[EBX].i = 1;
     159   run(
     160       "== code 0x1\n"  // code segment
     161       // op     ModR/M  SIB   displacement  immediate
     162       "  29     d8                                    \n"  // subtract EBX from EAX
     163       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     164   );
     165   CHECK_TRACE_CONTENTS(
     166       "run: subtract EBX from r/m32\n"
     167       "run: r/m32 is EAX\n"
     168       "run: storing 0x00000009\n"
     169   );
     170 }
     171
     172 :(before "End Single-Byte Opcodes")
     173 case 0x29: {  // subtract r32 from r/m32
     174   const uint8_t modrm = next();
     175   const uint8_t arg2 = (modrm>>3)&0x7;
     176   trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
     177   int32_t* signed_arg1 = effective_address(modrm);
     178   int32_t signed_result = *signed_arg1 - Reg[arg2].i;
     179   SF = (signed_result < 0);
     180   ZF = (signed_result == 0);
     181   int64_t signed_full_result = static_cast<int64_t>(*signed_arg1) - Reg[arg2].i;
     182   OF = (signed_result != signed_full_result);
     183   // set CF
     184   uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
     185   uint32_t unsigned_result = unsigned_arg1 - Reg[arg2].u;
     186   uint64_t unsigned_full_result = static_cast<uint64_t>(unsigned_arg1) - Reg[arg2].u;
     187   CF = (unsigned_result != unsigned_full_result);
     188   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     189   *signed_arg1 = signed_result;
     190   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     191   break;
     192 }
     193
     194 :(code)
     195 void test_subtract_r32_from_r32_signed_overflow() {
     196   Reg[EAX].i = INT32_MIN;
     197   Reg[EBX].i = INT32_MAX;
     198   run(
     199       "== code 0x1\n"  // code segment
     200       // op     ModR/M  SIB   displacement  immediate
     201       "  29     d8                                    \n"  // subtract EBX from EAX
     202       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     203   );
     204   CHECK_TRACE_CONTENTS(
     205       "run: subtract EBX from r/m32\n"
     206       "run: r/m32 is EAX\n"
     207       "run: SF=0; ZF=0; CF=0; OF=1\n"
     208       "run: storing 0x00000001\n"
     209   );
     210 }
     211
     212 void test_subtract_r32_from_r32_unsigned_overflow() {
     213   Reg[EAX].i = 0;
     214   Reg[EBX].i = 1;
     215   run(
     216       "== code 0x1\n"  // code segment
     217       // op     ModR/M  SIB   displacement  immediate
     218       "  29     d8                                    \n"  // subtract EBX from EAX
     219       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     220   );
     221   CHECK_TRACE_CONTENTS(
     222       "run: subtract EBX from r/m32\n"
     223       "run: r/m32 is EAX\n"
     224       "run: SF=1; ZF=0; CF=1; OF=0\n"
     225       "run: storing 0xffffffff\n"
     226   );
     227 }
     228
     229 void test_subtract_r32_from_r32_signed_and_unsigned_overflow() {
     230   Reg[EAX].i = 0;
     231   Reg[EBX].i = INT32_MIN;
     232   run(
     233       "== code 0x1\n"  // code segment
     234       // op     ModR/M  SIB   displacement  immediate
     235       "  29     d8                                    \n"  // subtract EBX from EAX
     236       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     237   );
     238   CHECK_TRACE_CONTENTS(
     239       "run: subtract EBX from r/m32\n"
     240       "run: r/m32 is EAX\n"
     241       "run: SF=1; ZF=0; CF=1; OF=1\n"
     242       "run: storing 0x80000000\n"
     243   );
     244 }
     245
     246 //:: multiply
     247
     248 :(before "End Initialize Op Names")
     249 put_new(Name, "f7", "negate/multiply/divide rm32 (with EAX and EDX if necessary) depending on subop (neg/mul/idiv)");
     250
     251 :(code)
     252 void test_multiply_EAX_by_r32() {
     253   Reg[EAX].i = 4;
     254   Reg[ECX].i = 3;
     255   run(
     256       "== code 0x1\n"  // code segment
     257       // op     ModR/M  SIB   displacement  immediate
     258       "  f7     e1                                    \n"  // multiply EAX by ECX
     259       // ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX)
     260   );
     261   CHECK_TRACE_CONTENTS(
     262       "run: operate on r/m32\n"
     263       "run: r/m32 is ECX\n"
     264       "run: subop: multiply EAX by r/m32\n"
     265       "run: storing 0x0000000c\n"
     266   );
     267 }
     268
     269 :(before "End Single-Byte Opcodes")
     270 case 0xf7: {
     271   const uint8_t modrm = next();
     272   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
     273   int32_t* arg1 = effective_address(modrm);
     274   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
     275   switch (subop) {
     276   case 4: {  // mul unsigned EAX by r/m32
     277     trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end();
     278     const uint64_t result = static_cast<uint64_t>(Reg[EAX].u) * static_cast<uint32_t>(*arg1);
     279     Reg[EAX].u = result & 0xffffffff;
     280     Reg[EDX].u = result >> 32;
     281     OF = (Reg[EDX].u != 0);
     282     CF = OF;
     283     trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     284     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
     285     break;
     286   }
     287   // End Op f7 Subops
     288   default:
     289     cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n';
     290     exit(1);
     291   }
     292   break;
     293 }
     294
     295 //:
     296
     297 :(before "End Initialize Op Names")
     298 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)");
     299
     300 :(code)
     301 void test_multiply_r32_into_r32() {
     302   Reg[EAX].i = 4;
     303   Reg[EBX].i = 2;
     304   run(
     305       "== code 0x1\n"  // code segment
     306       // op     ModR/M  SIB   displacement  immediate
     307       "  0f af  d8                                    \n"  // subtract EBX into EAX
     308       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     309   );
     310   CHECK_TRACE_CONTENTS(
     311       "run: multiply EBX by r/m32\n"
     312       "run: r/m32 is EAX\n"
     313       "run: storing 0x00000008\n"
     314   );
     315 }
     316
     317 :(before "End Two-Byte Opcodes Starting With 0f")
     318 case 0xaf: {  // multiply r32 by r/m32
     319   const uint8_t modrm = next();
     320   const uint8_t arg1 = (modrm>>3)&0x7;
     321   trace(Callstack_depth+1, "run") << "multiply " << rname(arg1) << " by r/m32" << end();
     322   const int32_t* arg2 = effective_address(modrm);
     323   int32_t result = Reg[arg1].i * (*arg2);
     324   int64_t full_result = static_cast<int64_t>(Reg[arg1].i) * (*arg2);
     325   OF = (result != full_result);
     326   CF = OF;
     327   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     328   Reg[arg1].i = result;
     329   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[arg1].i << end();
     330   break;
     331 }
     332
     333 //:: negate
     334
     335 :(code)
     336 void test_negate_r32() {
     337   Reg[EBX].i = 1;
     338   run(
     339       "== code 0x1\n"  // code segment
     340       // op     ModR/M  SIB   displacement  immediate
     341       "  f7     db                                    \n"  // negate EBX
     342       // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
     343   );
     344   CHECK_TRACE_CONTENTS(
     345       "run: operate on r/m32\n"
     346       "run: r/m32 is EBX\n"
     347       "run: subop: negate\n"
     348       "run: storing 0xffffffff\n"
     349   );
     350 }
     351
     352 :(before "End Op f7 Subops")
     353 case 3: {  // negate r/m32
     354   trace(Callstack_depth+1, "run") << "subop: negate" << end();
     355   // one case that can overflow
     356   if (static_cast<uint32_t>(*arg1) == 0x80000000) {
     357     trace(Callstack_depth+1, "run") << "overflow" << end();
     358     SF = true;
     359     ZF = false;
     360     OF = true;
     361     break;
     362   }
     363   int32_t result = -(*arg1);
     364   SF = (result >> 31);
     365   ZF = (result == 0);
     366   OF = false;
     367   CF = (*arg1 != 0);
     368   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     369   *arg1 = result;
     370   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     371   break;
     372 }
     373
     374 :(code)
     375 // negate can overflow in exactly one situation
     376 void test_negate_can_overflow() {
     377   Reg[EBX].i = INT32_MIN;
     378   run(
     379       "== code 0x1\n"  // code segment
     380       // op     ModR/M  SIB   displacement  immediate
     381       "  f7     db                                    \n"  // negate EBX
     382       // ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
     383   );
     384   CHECK_TRACE_CONTENTS(
     385       "run: operate on r/m32\n"
     386       "run: r/m32 is EBX\n"
     387       "run: subop: negate\n"
     388       "run: overflow\n"
     389   );
     390 }
     391
     392 //:: divide with remainder
     393
     394 void test_divide_EAX_by_rm32() {
     395   Reg[EAX].u = 7;
     396   Reg[EDX].u = 0;
     397   Reg[ECX].i = 3;
     398   run(
     399       "== code 0x1\n"  // code segment
     400       // op     ModR/M  SIB   displacement  immediate
     401       "  f7     f9                                    \n"  // multiply EAX by ECX
     402       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
     403   );
     404   CHECK_TRACE_CONTENTS(
     405       "run: operate on r/m32\n"
     406       "run: r/m32 is ECX\n"
     407       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
     408       "run: quotient: 0x00000002\n"
     409       "run: remainder: 0x00000001\n"
     410   );
     411 }
     412
     413 :(before "End Op f7 Subops")
     414 case 7: {  // divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX
     415   trace(Callstack_depth+1, "run") << "subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX" << end();
     416   int64_t dividend = static_cast<int64_t>((static_cast<uint64_t>(Reg[EDX].u) << 32) | Reg[EAX].u);
     417   int32_t divisor = *arg1;
     418   assert(divisor != 0);
     419   Reg[EAX].i = dividend/divisor;  // quotient
     420   Reg[EDX].i = dividend%divisor;  // remainder
     421   // flag state undefined
     422   trace(Callstack_depth+1, "run") << "quotient: 0x" << HEXWORD << Reg[EAX].i << end();
     423   trace(Callstack_depth+1, "run") << "remainder: 0x" << HEXWORD << Reg[EDX].i << end();
     424   break;
     425 }
     426
     427 :(code)
     428 void test_divide_EAX_by_negative_rm32() {
     429   Reg[EAX].u = 7;
     430   Reg[EDX].u = 0;
     431   Reg[ECX].i = -3;
     432   run(
     433       "== code 0x1\n"  // code segment
     434       // op     ModR/M  SIB   displacement  immediate
     435       "  f7     f9                                    \n"  // multiply EAX by ECX
     436       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
     437   );
     438   CHECK_TRACE_CONTENTS(
     439       "run: operate on r/m32\n"
     440       "run: r/m32 is ECX\n"
     441       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
     442       "run: quotient: 0xfffffffe\n"  // -2
     443       "run: remainder: 0x00000001\n"
     444   );
     445 }
     446
     447 void test_divide_negative_EAX_by_rm32() {
     448   Reg[EAX].i = -7;
     449   Reg[EDX].i = -1;  // sign extend
     450   Reg[ECX].i = 3;
     451   run(
     452       "== code 0x1\n"  // code segment
     453       // op     ModR/M  SIB   displacement  immediate
     454       "  f7     f9                                    \n"  // multiply EAX by ECX
     455       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
     456   );
     457   CHECK_TRACE_CONTENTS(
     458       "run: operate on r/m32\n"
     459       "run: r/m32 is ECX\n"
     460       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
     461       "run: quotient: 0xfffffffe\n"  // -2
     462       "run: remainder: 0xffffffff\n"  // -1, same sign as divident (EDX:EAX)
     463   );
     464 }
     465
     466 void test_divide_negative_EDX_EAX_by_rm32() {
     467   Reg[EAX].i = 0;  // lower 32 bits are clear
     468   Reg[EDX].i = -7;
     469   Reg[ECX].i = 0x40000000;  // 2^30 (largest positive power of 2)
     470   run(
     471       "== code 0x1\n"  // code segment
     472       // op     ModR/M  SIB   displacement  immediate
     473       "  f7     f9                                    \n"  // multiply EAX by ECX
     474       // ModR/M in binary: 11 (direct mode) 111 (subop idiv) 001 (divisor ECX)
     475   );
     476   CHECK_TRACE_CONTENTS(
     477       "run: operate on r/m32\n"
     478       "run: r/m32 is ECX\n"
     479       "run: subop: divide EDX:EAX by r/m32, storing quotient in EAX and remainder in EDX\n"
     480       "run: quotient: 0xffffffe4\n"  // (-7 << 32) / (1 << 30) = -7 << 2 = -28
     481       "run: remainder: 0x00000000\n"
     482   );
     483 }
     484
     485 //:: shift left
     486
     487 :(before "End Initialize Op Names")
     488 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)");
     489
     490 :(code)
     491 void test_shift_left_r32_with_cl() {
     492   Reg[EBX].i = 13;
     493   Reg[ECX].i = 1;
     494   run(
     495       "== code 0x1\n"  // code segment
     496       // op     ModR/M  SIB   displacement  immediate
     497       "  d3     e3                                    \n"  // shift EBX left by CL bits
     498       // ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
     499   );
     500   CHECK_TRACE_CONTENTS(
     501       "run: operate on r/m32\n"
     502       "run: r/m32 is EBX\n"
     503       "run: subop: shift left by CL bits\n"
     504       "run: storing 0x0000001a\n"
     505   );
     506 }
     507
     508 :(before "End Single-Byte Opcodes")
     509 case 0xd3: {
     510   const uint8_t modrm = next();
     511   trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
     512   int32_t* arg1 = effective_address(modrm);
     513   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
     514   switch (subop) {
     515   case 4: {  // shift left r/m32 by CL
     516     trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
     517     uint8_t count = Reg[ECX].u & 0x1f;
     518     // OF is only defined if count is 1
     519     if (count == 1) {
     520       bool msb = (*arg1 & 0x80000000) >> 1;
     521       bool pnsb = (*arg1 & 0x40000000);
     522       OF = (msb != pnsb);
     523     }
     524     int32_t result = (*arg1 << count);
     525     ZF = (result == 0);
     526     SF = (result < 0);
     527     CF = (*arg1 << (count-1)) & 0x80000000;
     528     trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     529     *arg1 = result;
     530     trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     531     break;
     532   }
     533   // End Op d3 Subops
     534   default:
     535     cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n';
     536     exit(1);
     537   }
     538   break;
     539 }
     540
     541 //:: shift right arithmetic
     542
     543 :(code)
     544 void test_shift_right_arithmetic_r32_with_cl() {
     545   Reg[EBX].i = 26;
     546   Reg[ECX].i = 1;
     547   run(
     548       "== code 0x1\n"  // code segment
     549       // op     ModR/M  SIB   displacement  immediate
     550       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
     551       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
     552   );
     553   CHECK_TRACE_CONTENTS(
     554       "run: operate on r/m32\n"
     555       "run: r/m32 is EBX\n"
     556       "run: subop: shift right by CL bits, while preserving sign\n"
     557       "run: storing 0x0000000d\n"
     558   );
     559 }
     560
     561 :(before "End Op d3 Subops")
     562 case 7: {  // shift right r/m32 by CL, preserving sign
     563   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
     564   uint8_t count = Reg[ECX].u & 0x1f;
     565   *arg1 = (*arg1 >> count);
     566   ZF = (*arg1 == 0);
     567   SF = (*arg1 < 0);
     568   // OF is only defined if count is 1
     569   if (count == 1) OF = false;
     570   // CF undefined
     571   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     572   break;
     573 }
     574
     575 :(code)
     576 void test_shift_right_arithmetic_odd_r32_with_cl() {
     577   Reg[EBX].i = 27;
     578   Reg[ECX].i = 1;
     579   run(
     580       "== code 0x1\n"  // code segment
     581       // op     ModR/M  SIB   displacement  immediate
     582       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
     583       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
     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 preserving sign\n"
     589       // result: 13
     590       "run: storing 0x0000000d\n"
     591   );
     592 }
     593
     594 void test_shift_right_arithmetic_negative_r32_with_cl() {
     595   Reg[EBX].i = 0xfffffffd;  // -3
     596   Reg[ECX].i = 1;
     597   run(
     598       "== code 0x1\n"  // code segment
     599       // op     ModR/M  SIB   displacement  immediate
     600       "  d3     fb                                    \n"  // shift EBX right by CL bits, while preserving sign
     601       // ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 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 preserving sign\n"
     607       // result: -2
     608       "run: storing 0xfffffffe\n"
     609   );
     610 }
     611
     612 //:: shift right logical
     613
     614 :(code)
     615 void test_shift_right_logical_r32_with_cl() {
     616   Reg[EBX].i = 26;
     617   Reg[ECX].i = 1;
     618   run(
     619       "== code 0x1\n"  // code segment
     620       // op     ModR/M  SIB   displacement  immediate
     621       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
     622       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
     623   );
     624   CHECK_TRACE_CONTENTS(
     625       "run: operate on r/m32\n"
     626       "run: r/m32 is EBX\n"
     627       "run: subop: shift right by CL bits, while padding zeroes\n"
     628       // result: 13
     629       "run: storing 0x0000000d\n"
     630   );
     631 }
     632
     633 :(before "End Op d3 Subops")
     634 case 5: {  // shift right r/m32 by CL, padding zeroes
     635   trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
     636   uint8_t count = Reg[ECX].u & 0x1f;
     637   // OF is only defined if count is 1
     638   if (count == 1) {
     639     bool msb = (*arg1 & 0x80000000) >> 1;
     640     bool pnsb = (*arg1 & 0x40000000);
     641     OF = (msb != pnsb);
     642   }
     643   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
     644   *uarg1 = (*uarg1 >> count);
     645   ZF = (*uarg1 == 0);
     646   // result is always positive by definition
     647   SF = false;
     648   // CF undefined
     649   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     650   break;
     651 }
     652
     653 :(code)
     654 void test_shift_right_logical_odd_r32_with_cl() {
     655   Reg[EBX].i = 27;
     656   Reg[ECX].i = 1;
     657   run(
     658       "== code 0x1\n"  // code segment
     659       // op     ModR/M  SIB   displacement  immediate
     660       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
     661       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
     662   );
     663   CHECK_TRACE_CONTENTS(
     664       "run: operate on r/m32\n"
     665       "run: r/m32 is EBX\n"
     666       "run: subop: shift right by CL bits, while padding zeroes\n"
     667       // result: 13
     668       "run: storing 0x0000000d\n"
     669   );
     670 }
     671
     672 void test_shift_right_logical_negative_r32_with_cl() {
     673   Reg[EBX].i = 0xfffffffd;
     674   Reg[ECX].i = 1;
     675   run(
     676       "== code 0x1\n"  // code segment
     677       // op     ModR/M  SIB   displacement  immediate
     678       "  d3     eb                                    \n"  // shift EBX right by CL bits, while padding zeroes
     679       // ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
     680   );
     681   CHECK_TRACE_CONTENTS(
     682       "run: operate on r/m32\n"
     683       "run: r/m32 is EBX\n"
     684       "run: subop: shift right by CL bits, while padding zeroes\n"
     685       "run: storing 0x7ffffffe\n"
     686   );
     687 }
     688
     689 //:: and
     690
     691 :(before "End Initialize Op Names")
     692 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
     693
     694 :(code)
     695 void test_and_r32_with_r32() {
     696   Reg[EAX].i = 0x0a0b0c0d;
     697   Reg[EBX].i = 0x000000ff;
     698   run(
     699       "== code 0x1\n"  // code segment
     700       // op     ModR/M  SIB   displacement  immediate
     701       "  21     d8                                    \n"  // and EBX with destination EAX
     702       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     703   );
     704   CHECK_TRACE_CONTENTS(
     705       "run: and EBX with r/m32\n"
     706       "run: r/m32 is EAX\n"
     707       "run: storing 0x0000000d\n"
     708   );
     709 }
     710
     711 :(before "End Single-Byte Opcodes")
     712 case 0x21: {  // and r32 with r/m32
     713   const uint8_t modrm = next();
     714   const uint8_t arg2 = (modrm>>3)&0x7;
     715   trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end();
     716   // bitwise ops technically operate on unsigned numbers, but it makes no
     717   // difference
     718   int32_t* signed_arg1 = effective_address(modrm);
     719   *signed_arg1 &= Reg[arg2].i;
     720   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     721   SF = (*signed_arg1 >> 31);
     722   ZF = (*signed_arg1 == 0);
     723   CF = false;
     724   OF = false;
     725   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     726   break;
     727 }
     728
     729 //:: or
     730
     731 :(before "End Initialize Op Names")
     732 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
     733
     734 :(code)
     735 void test_or_r32_with_r32() {
     736   Reg[EAX].i = 0x0a0b0c0d;
     737   Reg[EBX].i = 0xa0b0c0d0;
     738   run(
     739       "== code 0x1\n"  // code segment
     740       // op     ModR/M  SIB   displacement  immediate
     741       "  09     d8                                    \n"  // or EBX with destination EAX
     742       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     743   );
     744   CHECK_TRACE_CONTENTS(
     745       "run: or EBX with r/m32\n"
     746       "run: r/m32 is EAX\n"
     747       "run: storing 0xaabbccdd\n"
     748   );
     749 }
     750
     751 :(before "End Single-Byte Opcodes")
     752 case 0x09: {  // or r32 with r/m32
     753   const uint8_t modrm = next();
     754   const uint8_t arg2 = (modrm>>3)&0x7;
     755   trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end();
     756   // bitwise ops technically operate on unsigned numbers, but it makes no
     757   // difference
     758   int32_t* signed_arg1 = effective_address(modrm);
     759   *signed_arg1 |= Reg[arg2].i;
     760   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     761   SF = (*signed_arg1 >> 31);
     762   ZF = (*signed_arg1 == 0);
     763   CF = false;
     764   OF = false;
     765   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     766   break;
     767 }
     768
     769 //:: xor
     770
     771 :(before "End Initialize Op Names")
     772 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
     773
     774 :(code)
     775 void test_xor_r32_with_r32() {
     776   Reg[EAX].i = 0x0a0b0c0d;
     777   Reg[EBX].i = 0xaabbc0d0;
     778   run(
     779       "== code 0x1\n"  // code segment
     780       // op     ModR/M  SIB   displacement  immediate
     781       "  31     d8                                    \n"  // xor EBX with destination EAX
     782       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     783   );
     784   CHECK_TRACE_CONTENTS(
     785       "run: xor EBX with r/m32\n"
     786       "run: r/m32 is EAX\n"
     787       "run: storing 0xa0b0ccdd\n"
     788   );
     789 }
     790
     791 :(before "End Single-Byte Opcodes")
     792 case 0x31: {  // xor r32 with r/m32
     793   const uint8_t modrm = next();
     794   const uint8_t arg2 = (modrm>>3)&0x7;
     795   trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end();
     796   // bitwise ops technically operate on unsigned numbers, but it makes no
     797   // difference
     798   int32_t* signed_arg1 = effective_address(modrm);
     799   *signed_arg1 ^= Reg[arg2].i;
     800   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *signed_arg1 << end();
     801   SF = (*signed_arg1 >> 31);
     802   ZF = (*signed_arg1 == 0);
     803   CF = false;
     804   OF = false;
     805   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     806   break;
     807 }
     808
     809 //:: not
     810
     811 :(code)
     812 void test_not_r32() {
     813   Reg[EBX].i = 0x0f0f00ff;
     814   run(
     815       "== code 0x1\n"  // code segment
     816       // op     ModR/M  SIB   displacement  immediate
     817       "  f7     d3                                    \n"  // not EBX
     818       // ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX)
     819   );
     820   CHECK_TRACE_CONTENTS(
     821       "run: operate on r/m32\n"
     822       "run: r/m32 is EBX\n"
     823       "run: subop: not\n"
     824       "run: storing 0xf0f0ff00\n"
     825   );
     826 }
     827
     828 :(before "End Op f7 Subops")
     829 case 2: {  // not r/m32
     830   trace(Callstack_depth+1, "run") << "subop: not" << end();
     831   *arg1 = ~(*arg1);
     832   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
     833   // no flags affected
     834   break;
     835 }
     836
     837 //:: compare (cmp)
     838
     839 :(before "End Initialize Op Names")
     840 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
     841
     842 :(code)
     843 void test_compare_r32_with_r32_greater() {
     844   Reg[EAX].i = 0x0a0b0c0d;
     845   Reg[EBX].i = 0x0a0b0c07;
     846   run(
     847       "== code 0x1\n"  // code segment
     848       // op     ModR/M  SIB   displacement  immediate
     849       "  39     d8                                    \n"  // compare EAX with EBX
     850       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
     851   );
     852   CHECK_TRACE_CONTENTS(
     853       "run: compare r/m32 with EBX\n"
     854       "run: r/m32 is EAX\n"
     855       "run: SF=0; ZF=0; CF=0; OF=0\n"
     856   );
     857 }
     858
     859 :(before "End Single-Byte Opcodes")
     860 case 0x39: {  // set SF if r/m32 < r32
     861   const uint8_t modrm = next();
     862   const uint8_t reg2 = (modrm>>3)&0x7;
     863   trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg2) << end();
     864   const int32_t* signed_arg1 = effective_address(modrm);
     865   const int32_t signed_difference = *signed_arg1 - Reg[reg2].i;
     866   SF = (signed_difference < 0);
     867   ZF = (signed_difference == 0);
     868   const int64_t signed_full_difference = static_cast<int64_t>(*signed_arg1) - Reg[reg2].i;
     869   OF = (signed_difference != signed_full_difference);
     870   // set CF
     871   const uint32_t unsigned_arg1 = static_cast<uint32_t>(*signed_arg1);
     872   const uint32_t unsigned_difference = unsigned_arg1 - Reg[reg2].u;
     873   const uint64_t unsigned_full_difference = static_cast<uint64_t>(unsigned_arg1) - Reg[reg2].u;
     874   CF = (unsigned_difference != unsigned_full_difference);
     875   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; CF=" << CF << "; OF=" << OF << end();
     876   break;
     877 }
     878
     879 :(code)
     880 void test_compare_r32_with_r32_lesser_unsigned_and_signed() {
     881   Reg[EAX].i = 0x0a0b0c07;
     882   Reg[EBX].i = 0x0a0b0c0d;
     883   run(
     884       "== code 0x1\n"  // code segment
     885       // op     ModR/M  SIB   displacement  immediate
     886       "  39     d8                                    \n"  // compare EAX with EBX
     887       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
     888   );
     889   CHECK_TRACE_CONTENTS(
     890       "run: compare r/m32 with EBX\n"
     891       "run: r/m32 is EAX\n"
     892       "run: SF=1; ZF=0; CF=1; OF=0\n"
     893   );
     894 }
     895
     896 void test_compare_r32_with_r32_lesser_unsigned_and_signed_due_to_overflow() {
     897   Reg[EAX].i = INT32_MAX;
     898   Reg[EBX].i = INT32_MIN;
     899   run(
     900       "== code 0x1\n"  // code segment
     901       // op     ModR/M  SIB   displacement  immediate
     902       "  39     d8                                    \n"  // compare EAX with EBX
     903       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
     904   );
     905   CHECK_TRACE_CONTENTS(
     906       "run: compare r/m32 with EBX\n"
     907       "run: r/m32 is EAX\n"
     908       "run: SF=1; ZF=0; CF=1; OF=1\n"
     909   );
     910 }
     911
     912 void test_compare_r32_with_r32_lesser_signed() {
     913   Reg[EAX].i = -1;
     914   Reg[EBX].i = 1;
     915   run(
     916       "== code 0x1\n"  // code segment
     917       // op     ModR/M  SIB   displacement  immediate
     918       "  39     d8                                    \n"  // compare EAX with EBX
     919       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
     920   );
     921   CHECK_TRACE_CONTENTS(
     922       "run: compare r/m32 with EBX\n"
     923       "run: r/m32 is EAX\n"
     924       "run: SF=1; ZF=0; CF=0; OF=0\n"
     925   );
     926 }
     927
     928 void test_compare_r32_with_r32_lesser_unsigned() {
     929   Reg[EAX].i = 1;
     930   Reg[EBX].i = -1;
     931   run(
     932       "== code 0x1\n"  // code segment
     933       // op     ModR/M  SIB   displacement  immediate
     934       "  39     d8                                    \n"  // compare EAX with EBX
     935       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
     936   );
     937   CHECK_TRACE_CONTENTS(
     938       "run: compare r/m32 with EBX\n"
     939       "run: r/m32 is EAX\n"
     940       "run: SF=0; ZF=0; CF=1; OF=0\n"
     941   );
     942 }
     943
     944 void test_compare_r32_with_r32_equal() {
     945   Reg[EAX].i = 0x0a0b0c0d;
     946   Reg[EBX].i = 0x0a0b0c0d;
     947   run(
     948       "== code 0x1\n"  // code segment
     949       // op     ModR/M  SIB   displacement  immediate
     950       "  39     d8                                    \n"  // compare EAX and EBX
     951       // ModR/M in binary: 11 (direct mode) 011 (rhs EBX) 000 (lhs EAX)
     952   );
     953   CHECK_TRACE_CONTENTS(
     954       "run: compare r/m32 with EBX\n"
     955       "run: r/m32 is EAX\n"
     956       "run: SF=0; ZF=1; CF=0; OF=0\n"
     957   );
     958 }
     959
     960 //:: copy (mov)
     961
     962 :(before "End Initialize Op Names")
     963 put_new(Name, "89", "copy r32 to rm32 (mov)");
     964
     965 :(code)
     966 void test_copy_r32_to_r32() {
     967   Reg[EBX].i = 0xaf;
     968   run(
     969       "== code 0x1\n"  // code segment
     970       // op     ModR/M  SIB   displacement  immediate
     971       "  89     d8                                    \n"  // copy EBX to EAX
     972       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
     973   );
     974   CHECK_TRACE_CONTENTS(
     975       "run: copy EBX to r/m32\n"
     976       "run: r/m32 is EAX\n"
     977       "run: storing 0x000000af\n"
     978   );
     979 }
     980
     981 :(before "End Single-Byte Opcodes")
     982 case 0x89: {  // copy r32 to r/m32
     983   const uint8_t modrm = next();
     984   const uint8_t rsrc = (modrm>>3)&0x7;
     985   trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
     986   int32_t* dest = effective_address(modrm);
     987   *dest = Reg[rsrc].i;  // Write multiple elements of vector<uint8_t> at once. Assumes sizeof(int) == 4 on the host as well.
     988   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end();
     989   break;
     990 }
     991
     992 //:: xchg
     993
     994 :(before "End Initialize Op Names")
     995 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
     996
     997 :(code)
     998 void test_xchg_r32_with_r32() {
     999   Reg[EBX].i = 0xaf;
    1000   Reg[EAX].i = 0x2e;
    1001   run(
    1002       "== code 0x1\n"  // code segment
    1003       // op     ModR/M  SIB   displacement  immediate
    1004       "  87     d8                                    \n"  // exchange EBX with EAX
    1005       // ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
    1006   );
    1007   CHECK_TRACE_CONTENTS(
    1008       "run: exchange EBX with r/m32\n"
    1009       "run: r/m32 is EAX\n"
    1010       "run: storing 0x000000af in r/m32\n"
    1011       "run: storing 0x0000002e in EBX\n"
    1012   );
    1013 }
    1014
    1015 :(before "End Single-Byte Opcodes")
    1016 case 0x87: {  // exchange r32 with r/m32
    1017   const uint8_t modrm = next();
    1018   const uint8_t reg2 = (modrm>>3)&0x7;
    1019   trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
    1020   int32_t* arg1 = effective_address(modrm);
    1021   const int32_t tmp = *arg1;
    1022   *arg1 = Reg[reg2].i;
    1023   Reg[reg2].i = tmp;
    1024   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
    1025   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
    1026   break;
    1027 }
    1028
    1029 //:: increment
    1030
    1031 :(before "End Initialize Op Names")
    1032 put_new(Name, "40", "increment EAX (inc)");
    1033 put_new(Name, "41", "increment ECX (inc)");
    1034 put_new(Name, "42", "increment EDX (inc)");
    1035 put_new(Name, "43", "increment EBX (inc)");
    1036 put_new(Name, "44", "increment ESP (inc)");
    1037 put_new(Name, "45", "increment EBP (inc)");
    1038 put_new(Name, "46", "increment ESI (inc)");
    1039 put_new(Name, "47", "increment EDI (inc)");
    1040
    1041 :(code)
    1042 void test_increment_r32() {
    1043   Reg[ECX].u = 0x1f;
    1044   run(
    1045       "== code 0x1\n"  // code segment
    1046       // op     ModR/M  SIB   displacement  immediate
    1047       "  41                                           \n"  // increment ECX
    1048   );
    1049   CHECK_TRACE_CONTENTS(
    1050       "run: increment ECX\n"
    1051       "run: storing value 0x00000020\n"
    1052   );
    1053 }
    1054
    1055 :(before "End Single-Byte Opcodes")
    1056 case 0x40:
    1057 case 0x41:
    1058 case 0x42:
    1059 case 0x43:
    1060 case 0x44:
    1061 case 0x45:
    1062 case 0x46:
    1063 case 0x47: {  // increment r32
    1064   const uint8_t reg = op & 0x7;
    1065   trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end();
    1066   ++Reg[reg].u;
    1067   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
    1068   break;
    1069 }
    1070
    1071 :(before "End Initialize Op Names")
    1072 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)");
    1073
    1074 :(code)
    1075 void test_increment_rm32() {
    1076   Reg[EAX].u = 0x20;
    1077   run(
    1078       "== code 0x1\n"  // code segment
    1079       // op     ModR/M  SIB   displacement  immediate
    1080       "  ff     c0                                    \n"  // increment EAX
    1081       // ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX)
    1082   );
    1083   CHECK_TRACE_CONTENTS(
    1084       "run: increment r/m32\n"
    1085       "run: r/m32 is EAX\n"
    1086       "run: storing value 0x00000021\n"
    1087   );
    1088 }
    1089
    1090 :(before "End Single-Byte Opcodes")
    1091 case 0xff: {
    1092   const uint8_t modrm = next();
    1093   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
    1094   switch (subop) {
    1095     case 0: {  // increment r/m32
    1096       trace(Callstack_depth+1, "run") << "increment r/m32" << end();
    1097       int32_t* arg = effective_address(modrm);
    1098       ++*arg;
    1099       trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
    1100       break;
    1101     }
    1102     default:
    1103       cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n';
    1104       exit(1);
    1105     // End Op ff Subops
    1106   }
    1107   break;
    1108 }
    1109
    1110 //:: decrement
    1111
    1112 :(before "End Initialize Op Names")
    1113 put_new(Name, "48", "decrement EAX (dec)");
    1114 put_new(Name, "49", "decrement ECX (dec)");
    1115 put_new(Name, "4a", "decrement EDX (dec)");
    1116 put_new(Name, "4b", "decrement EBX (dec)");
    1117 put_new(Name, "4c", "decrement ESP (dec)");
    1118 put_new(Name, "4d", "decrement EBP (dec)");
    1119 put_new(Name, "4e", "decrement ESI (dec)");
    1120 put_new(Name, "4f", "decrement EDI (dec)");
    1121
    1122 :(code)
    1123 void test_decrement_r32() {
    1124   Reg[ECX].u = 0x1f;
    1125   run(
    1126       "== code 0x1\n"  // code segment
    1127       // op     ModR/M  SIB   displacement  immediate
    1128       "  49                                           \n"  // decrement ECX
    1129   );
    1130   CHECK_TRACE_CONTENTS(
    1131       "run: decrement ECX\n"
    1132       "run: storing value 0x0000001e\n"
    1133   );
    1134 }
    1135
    1136 :(before "End Single-Byte Opcodes")
    1137 case 0x48:
    1138 case 0x49:
    1139 case 0x4a:
    1140 case 0x4b:
    1141 case 0x4c:
    1142 case 0x4d:
    1143 case 0x4e:
    1144 case 0x4f: {  // decrement r32
    1145   const uint8_t reg = op & 0x7;
    1146   trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end();
    1147   --Reg[reg].u;
    1148   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
    1149   break;
    1150 }
    1151
    1152 :(code)
    1153 void test_decrement_rm32() {
    1154   Reg[EAX].u = 0x20;
    1155   run(
    1156       "== code 0x1\n"  // code segment
    1157       // op     ModR/M  SIB   displacement  immediate
    1158       "  ff     c8                                    \n"  // decrement EAX
    1159       // ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX)
    1160   );
    1161   CHECK_TRACE_CONTENTS(
    1162       "run: decrement r/m32\n"
    1163       "run: r/m32 is EAX\n"
    1164       "run: storing value 0x0000001f\n"
    1165   );
    1166 }
    1167
    1168 :(before "End Op ff Subops")
    1169 case 1: {  // decrement r/m32
    1170   trace(Callstack_depth+1, "run") << "decrement r/m32" << end();
    1171   int32_t* arg = effective_address(modrm);
    1172   --*arg;
    1173   trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
    1174   break;
    1175 }
    1176
    1177 //:: push
    1178
    1179 :(before "End Initialize Op Names")
    1180 put_new(Name, "50", "push EAX to stack (push)");
    1181 put_new(Name, "51", "push ECX to stack (push)");
    1182 put_new(Name, "52", "push EDX to stack (push)");
    1183 put_new(Name, "53", "push EBX to stack (push)");
    1184 put_new(Name, "54", "push ESP to stack (push)");
    1185 put_new(Name, "55", "push EBP to stack (push)");
    1186 put_new(Name, "56", "push ESI to stack (push)");
    1187 put_new(Name, "57", "push EDI to stack (push)");
    1188
    1189 :(code)
    1190 void test_push_r32() {
    1191   Mem.push_back(vma(0xbd000000));  // manually allocate memory
    1192   Reg[ESP].u = 0xbd000008;
    1193   Reg[EBX].i = 0x0000000a;
    1194   run(
    1195       "== code 0x1\n"  // code segment
    1196       // op     ModR/M  SIB   displacement  immediate
    1197       "  53                                           \n"  // push EBX to stack
    1198   );
    1199   CHECK_TRACE_CONTENTS(
    1200       "run: push EBX\n"
    1201       "run: decrementing ESP to 0xbd000004\n"
    1202       "run: pushing value 0x0000000a\n"
    1203   );
    1204 }
    1205
    1206 :(before "End Single-Byte Opcodes")
    1207 case 0x50:
    1208 case 0x51:
    1209 case 0x52:
    1210 case 0x53:
    1211 case 0x54:
    1212 case 0x55:
    1213 case 0x56:
    1214 case 0x57: {  // push r32 to stack
    1215   uint8_t reg = op & 0x7;
    1216   trace(Callstack_depth+1, "run") << "push " << rname(reg) << end();
    1217 //?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
    1218   push(Reg[reg].u);
    1219   break;
    1220 }
    1221
    1222 //:: pop
    1223
    1224 :(before "End Initialize Op Names")
    1225 put_new(Name, "58", "pop top of stack to EAX (pop)");
    1226 put_new(Name, "59", "pop top of stack to ECX (pop)");
    1227 put_new(Name, "5a", "pop top of stack to EDX (pop)");
    1228 put_new(Name, "5b", "pop top of stack to EBX (pop)");
    1229 put_new(Name, "5c", "pop top of stack to ESP (pop)");
    1230 put_new(Name, "5d", "pop top of stack to EBP (pop)");
    1231 put_new(Name, "5e", "pop top of stack to ESI (pop)");
    1232 put_new(Name, "5f", "pop top of stack to EDI (pop)");
    1233
    1234 :(code)
    1235 void test_pop_r32() {
    1236   Mem.push_back(vma(0xbd000000));  // manually allocate memory
    1237   Reg[ESP].u = 0xbd000008;
    1238   write_mem_i32(0xbd000008, 0x0000000a);  // ..before this write
    1239   run(
    1240       "== code 0x1\n"  // code segment
    1241       // op     ModR/M  SIB   displacement  immediate
    1242       "  5b                                           \n"  // pop stack to EBX
    1243       "== data 0x2000\n"  // data segment
    1244       "0a 00 00 00\n"  // 0xa
    1245   );
    1246   CHECK_TRACE_CONTENTS(
    1247       "run: pop into EBX\n"
    1248       "run: popping value 0x0000000a\n"
    1249       "run: incrementing ESP to 0xbd00000c\n"
    1250   );
    1251 }
    1252
    1253 :(before "End Single-Byte Opcodes")
    1254 case 0x58:
    1255 case 0x59:
    1256 case 0x5a:
    1257 case 0x5b:
    1258 case 0x5c:
    1259 case 0x5d:
    1260 case 0x5e:
    1261 case 0x5f: {  // pop stack into r32
    1262   const uint8_t reg = op & 0x7;
    1263   trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end();
    1264 //?   cerr << "pop from " << Reg[ESP].u << '\n';
    1265   Reg[reg].u = pop();
    1266 //?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
    1267   break;
    1268 }
    1269 :(code)
    1270 uint32_t pop() {
    1271   const uint32_t result = read_mem_u32(Reg[ESP].u);
    1272   trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end();
    1273   Reg[ESP].u += 4;
    1274   trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
    1275   assert(Reg[ESP].u < AFTER_STACK);
    1276   return result;
    1277 }
    1278
    1279 :(before "End Includes")
    1280 #include <climits>