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 }