https://github.com/akkartik/mu/blob/master/subx/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_r32() {
      5   Reg[EBX].i = 0x10;
      6   Reg[EAX].i = 0x2000;
      7   run(
      8       "== 0x1\n"  // code segment
      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 EAX) 000 (dest EAX)
     12       "== 0x2000\n"  // data segment
     13       "01 00 00 00\n"  // 0x00000001
     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_r32_to_r32() {
     40   Reg[EAX].i = 0x2000;
     41   Reg[EBX].i = 0x10;
     42   run(
     43       "== 0x1\n"  // code segment
     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 EAX) 000 (dest EAX)
     47       "== 0x2000\n"  // data segment
     48       "01 00 00 00\n"  // 0x00000001
     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* arg2 = effective_address(modrm);
     63   BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2);
     64   break;
     65 }
     66
     67 //:: subtract
     68
     69 :(code)
     70 void test_subtract_r32_from_mem_at_r32() {
     71   Reg[EAX].i = 0x2000;
     72   Reg[EBX].i = 1;
     73   run(
     74       "== 0x1\n"  // code segment
     75       // op     ModR/M  SIB   displacement  immediate
     76       "  29  18                                       \n"  // subtract EBX from *EAX
     77       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
     78       "== 0x2000\n"  // data segment
     79       "0a 00 00 00\n"  // 0x0000000a
     80   );
     81   CHECK_TRACE_CONTENTS(
     82       "run: subtract EBX from r/m32\n"
     83       "run: effective address is 0x00002000 (EAX)\n"
     84       "run: storing 0x00000009\n"
     85   );
     86 }
     87
     88 //:
     89
     90 :(before "End Initialize Op Names")
     91 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
     92
     93 :(code)
     94 void test_subtract_mem_at_r32_from_r32() {
     95   Reg[EAX].i = 0x2000;
     96   Reg[EBX].i = 10;
     97   run(
     98       "== 0x1\n"  // code segment
     99       // op     ModR/M  SIB   displacement  immediate
    100       "  2b     18                                    \n"  // subtract *EAX from EBX
    101       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    102       "== 0x2000\n"  // data segment
    103       "01 00 00 00\n"  // 0x00000001
    104   );
    105   CHECK_TRACE_CONTENTS(
    106       "run: subtract r/m32 from EBX\n"
    107       "run: effective address is 0x00002000 (EAX)\n"
    108       "run: storing 0x00000009\n"
    109   );
    110 }
    111
    112 :(before "End Single-Byte Opcodes")
    113 case 0x2b: {  // subtract r/m32 from r32
    114   const uint8_t modrm = next();
    115   const uint8_t arg1 = (modrm>>3)&0x7;
    116   trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end();
    117   const int32_t* arg2 = effective_address(modrm);
    118   BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2);
    119   break;
    120 }
    121
    122 //:: and
    123 :(code)
    124 void test_and_r32_with_mem_at_r32() {
    125   Reg[EAX].i = 0x2000;
    126   Reg[EBX].i = 0xff;
    127   run(
    128       "== 0x1\n"  // code segment
    129       // op     ModR/M  SIB   displacement  immediate
    130       "  21     18                                    \n"  // and EBX with *EAX
    131       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    132       "== 0x2000\n"  // data segment
    133       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    134   );
    135   CHECK_TRACE_CONTENTS(
    136       "run: and EBX with r/m32\n"
    137       "run: effective address is 0x00002000 (EAX)\n"
    138       "run: storing 0x0000000d\n"
    139   );
    140 }
    141
    142 //:
    143
    144 :(before "End Initialize Op Names")
    145 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
    146
    147 :(code)
    148 void test_and_mem_at_r32_with_r32() {
    149   Reg[EAX].i = 0x2000;
    150   Reg[EBX].i = 0x0a0b0c0d;
    151   run(
    152       "== 0x1\n"  // code segment
    153       // op     ModR/M  SIB   displacement  immediate
    154       "  23     18                                    \n"  // and *EAX with EBX
    155       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    156       "== 0x2000\n"  // data segment
    157       "ff 00 00 00\n"  // 0x000000ff
    158   );
    159   CHECK_TRACE_CONTENTS(
    160       "run: and r/m32 with EBX\n"
    161       "run: effective address is 0x00002000 (EAX)\n"
    162       "run: storing 0x0000000d\n"
    163   );
    164 }
    165
    166 :(before "End Single-Byte Opcodes")
    167 case 0x23: {  // and r/m32 with r32
    168   const uint8_t modrm = next();
    169   const uint8_t arg1 = (modrm>>3)&0x7;
    170   trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end();
    171   const int32_t* arg2 = effective_address(modrm);
    172   BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2);
    173   break;
    174 }
    175
    176 //:: or
    177
    178 :(code)
    179 void test_or_r32_with_mem_at_r32() {
    180   Reg[EAX].i = 0x2000;
    181   Reg[EBX].i = 0xa0b0c0d0;
    182   run(
    183       "== 0x1\n"  // code segment
    184       // op     ModR/M  SIB   displacement  immediate
    185       "  09  18                                      #\n"  // EBX with *EAX
    186       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    187       "== 0x2000\n"  // data segment
    188       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    189   );
    190   CHECK_TRACE_CONTENTS(
    191       "run: or EBX with r/m32\n"
    192       "run: effective address is 0x00002000 (EAX)\n"
    193       "run: storing 0xaabbccdd\n"
    194   );
    195 }
    196
    197 //:
    198
    199 :(before "End Initialize Op Names")
    200 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
    201
    202 :(code)
    203 void test_or_mem_at_r32_with_r32() {
    204   Reg[EAX].i = 0x2000;
    205   Reg[EBX].i = 0xa0b0c0d0;
    206   run(
    207       "== 0x1\n"  // code segment
    208       // op     ModR/M  SIB   displacement  immediate
    209       "  0b     18                                    \n"  // or *EAX with EBX
    210       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    211       "== 0x2000\n"  // data segment
    212       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    213   );
    214   CHECK_TRACE_CONTENTS(
    215       "run: or r/m32 with EBX\n"
    216       "run: effective address is 0x00002000 (EAX)\n"
    217       "run: storing 0xaabbccdd\n"
    218   );
    219 }
    220
    221 :(before "End Single-Byte Opcodes")
    222 case 0x0b: {  // or r/m32 with r32
    223   const uint8_t modrm = next();
    224   const uint8_t arg1 = (modrm>>3)&0x7;
    225   trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end();
    226   const int32_t* arg2 = effective_address(modrm);
    227   BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
    228   break;
    229 }
    230
    231 //:: xor
    232
    233 :(code)
    234 void test_xor_r32_with_mem_at_r32() {
    235   Reg[EAX].i = 0x2000;
    236   Reg[EBX].i = 0xa0b0c0d0;
    237   run(
    238       "== 0x1\n"  // code segment
    239       // op     ModR/M  SIB   displacement  immediate
    240       "  31     18                                    \n"  // xor EBX with *EAX
    241       "== 0x2000\n"  // data segment
    242       "0d 0c bb aa\n"  // 0xaabb0c0d
    243   );
    244   CHECK_TRACE_CONTENTS(
    245       "run: xor EBX with r/m32\n"
    246       "run: effective address is 0x00002000 (EAX)\n"
    247       "run: storing 0x0a0bccdd\n"
    248   );
    249 }
    250
    251 //:
    252
    253 :(before "End Initialize Op Names")
    254 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
    255
    256 :(code)
    257 void test_xor_mem_at_r32_with_r32() {
    258   Reg[EAX].i = 0x2000;
    259   Reg[EBX].i = 0xa0b0c0d0;
    260   run(
    261       "== 0x1\n"  // code segment
    262       // op     ModR/M  SIB   displacement  immediate
    263       "  33     18                                    \n"  // xor *EAX with EBX
    264       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    265       "== 0x2000\n"  // data segment
    266       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    267   );
    268   CHECK_TRACE_CONTENTS(
    269       "run: xor r/m32 with EBX\n"
    270       "run: effective address is 0x00002000 (EAX)\n"
    271       "run: storing 0xaabbccdd\n"
    272   );
    273 }
    274
    275 :(before "End Single-Byte Opcodes")
    276 case 0x33: {  // xor r/m32 with r32
    277   const uint8_t modrm = next();
    278   const uint8_t arg1 = (modrm>>3)&0x7;
    279   trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end();
    280   const int32_t* arg2 = effective_address(modrm);
    281   BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
    282   break;
    283 }
    284
    285 //:: not
    286
    287 :(code)
    288 void test_not_of_mem_at_r32() {
    289   Reg[EBX].i = 0x2000;
    290   run(
    291       "== 0x1\n"  // code segment
    292       // op     ModR/M  SIB   displacement  immediate
    293       "  f7     13                                    \n"  // not *EBX
    294       // ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX)
    295       "== 0x2000\n"  // data segment
    296       "ff 00 0f 0f\n"  // 0x0f0f00ff
    297   );
    298   CHECK_TRACE_CONTENTS(
    299       "run: operate on r/m32\n"
    300       "run: effective address is 0x00002000 (EBX)\n"
    301       "run: subop: not\n"
    302       "run: storing 0xf0f0ff00\n"
    303   );
    304 }
    305
    306 //:: compare (cmp)
    307
    308 :(code)
    309 void test_compare_mem_at_r32_with_r32_greater() {
    310   Reg[EAX].i = 0x2000;
    311   Reg[EBX].i = 0x0a0b0c07;
    312   run(
    313       "== 0x1\n"  // code segment
    314       // op     ModR/M  SIB   displacement  immediate
    315       "  39     18                                    \n"  // compare EBX with *EAX
    316       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    317       "== 0x2000\n"  // data segment
    318       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    319   );
    320   CHECK_TRACE_CONTENTS(
    321       "run: compare EBX with r/m32\n"
    322       "run: effective address is 0x00002000 (EAX)\n"
    323       "run: SF=0; ZF=0; OF=0\n"
    324   );
    325 }
    326
    327 :(code)
    328 void test_compare_mem_at_r32_with_r32_lesser() {
    329   Reg[EAX].i = 0x2000;
    330   Reg[EBX].i = 0x0a0b0c0d;
    331   run(
    332       "== 0x1\n"  // code segment
    333       // op     ModR/M  SIB   displacement  immediate
    334       "  39     18                                    \n"  // compare EBX with *EAX
    335       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    336       "== 0x2000\n"  // data segment
    337       "07 0c 0b 0a\n"  // 0x0a0b0c0d
    338   );
    339   CHECK_TRACE_CONTENTS(
    340       "run: compare EBX with r/m32\n"
    341       "run: effective address is 0x00002000 (EAX)\n"
    342       "run: SF=1; ZF=0; OF=0\n"
    343   );
    344 }
    345
    346 :(code)
    347 void test_compare_mem_at_r32_with_r32_equal() {
    348   Reg[EAX].i = 0x2000;
    349   Reg[EBX].i = 0x0a0b0c0d;
    350   run(
    351       "== 0x1\n"  // code segment
    352       // op     ModR/M  SIB   displacement  immediate
    353       "  39     18                                    \n"  // compare EBX with *EAX
    354       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    355       "== 0x2000\n"  // data segment
    356       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    357   );
    358   CHECK_TRACE_CONTENTS(
    359       "run: compare EBX with r/m32\n"
    360       "run: effective address is 0x00002000 (EAX)\n"
    361       "run: SF=0; ZF=1; OF=0\n"
    362   );
    363 }
    364
    365 //:
    366
    367 :(before "End Initialize Op Names")
    368 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
    369
    370 :(code)
    371 void test_compare_r32_with_mem_at_r32_greater() {
    372   Reg[EAX].i = 0x2000;
    373   Reg[EBX].i = 0x0a0b0c0d;
    374   run(
    375       "== 0x1\n"  // code segment
    376       // op     ModR/M  SIB   displacement  immediate
    377       "  3b     18                                    \n"  // compare *EAX with EBX
    378       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    379       "== 0x2000\n"  // data segment
    380       "07 0c 0b 0a\n"  // 0x0a0b0c0d
    381   );
    382   CHECK_TRACE_CONTENTS(
    383       "run: compare r/m32 with EBX\n"
    384       "run: effective address is 0x00002000 (EAX)\n"
    385       "run: SF=0; ZF=0; OF=0\n"
    386   );
    387 }
    388
    389 :(before "End Single-Byte Opcodes")
    390 case 0x3b: {  // set SF if r32 < r/m32
    391   const uint8_t modrm = next();
    392   const uint8_t reg1 = (modrm>>3)&0x7;
    393   trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg1) << end();
    394   const int32_t arg1 = Reg[reg1].i;
    395   const int32_t* arg2 = effective_address(modrm);
    396   const int32_t tmp1 = arg1 - *arg2;
    397   SF = (tmp1 < 0);
    398   ZF = (tmp1 == 0);
    399   int64_t tmp2 = arg1 - *arg2;
    400   OF = (tmp1 != tmp2);
    401   trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
    402   break;
    403 }
    404
    405 :(code)
    406 void test_compare_r32_with_mem_at_r32_lesser() {
    407   Reg[EAX].i = 0x2000;
    408   Reg[EBX].i = 0x0a0b0c07;
    409   run(
    410       "== 0x1\n"  // code segment
    411       // op     ModR/M  SIB   displacement  immediate
    412       "  3b     18                                    \n"  // compare *EAX with EBX
    413       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    414       "== 0x2000\n"  // data segment
    415       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    416   );
    417   CHECK_TRACE_CONTENTS(
    418       "run: compare r/m32 with EBX\n"
    419       "run: effective address is 0x00002000 (EAX)\n"
    420       "run: SF=1; ZF=0; OF=0\n"
    421   );
    422 }
    423
    424 :(code)
    425 void test_compare_r32_with_mem_at_r32_equal() {
    426   Reg[EAX].i = 0x2000;
    427   Reg[EBX].i = 0x0a0b0c0d;
    428   run(
    429       "== 0x1\n"  // code segment
    430       // op     ModR/M  SIB   displacement  immediate
    431       "  3b     18                                    \n"  // compare *EAX with EBX
    432       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    433       "== 0x2000\n"  // data segment
    434       "0d 0c 0b 0a\n"  // 0x0a0b0c0d
    435   );
    436   CHECK_TRACE_CONTENTS(
    437       "run: compare r/m32 with EBX\n"
    438       "run: effective address is 0x00002000 (EAX)\n"
    439       "run: SF=0; ZF=1; OF=0\n"
    440   );
    441 }
    442
    443 //:: copy (mov)
    444
    445 :(code)
    446 void test_copy_r32_to_mem_at_r32() {
    447   Reg[EBX].i = 0xaf;
    448   Reg[EAX].i = 0x60;
    449   run(
    450       "== 0x1\n"  // code segment
    451       // op     ModR/M  SIB   displacement  immediate
    452       "  89     18                                    \n"  // copy EBX to *EAX
    453       // ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    454   );
    455   CHECK_TRACE_CONTENTS(
    456       "run: copy EBX to r/m32\n"
    457       "run: effective address is 0x00000060 (EAX)\n"
    458       "run: storing 0x000000af\n"
    459   );
    460 }
    461
    462 //:
    463
    464 :(before "End Initialize Op Names")
    465 put_new(Name, "8b", "copy rm32 to r32 (mov)");
    466
    467 :(code)
    468 void test_copy_mem_at_r32_to_r32() {
    469   Reg[EAX].i = 0x2000;
    470   run(
    471       "== 0x1\n"  // code segment
    472       // op     ModR/M  SIB   displacement  immediate
    473       "  8b     18                                    \n"  // copy *EAX to EBX
    474       "== 0x2000\n"  // data segment
    475       "af 00 00 00\n"  // 0x000000af
    476   );
    477   CHECK_TRACE_CONTENTS(
    478       "run: copy r/m32 to EBX\n"
    479       "run: effective address is 0x00002000 (EAX)\n"
    480       "run: storing 0x000000af\n"
    481   );
    482 }
    483
    484 :(before "End Single-Byte Opcodes")
    485 case 0x8b: {  // copy r32 to r/m32
    486   const uint8_t modrm = next();
    487   const uint8_t rdest = (modrm>>3)&0x7;
    488   trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end();
    489   const int32_t* src = effective_address(modrm);
    490   Reg[rdest].i = *src;
    491   trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end();
    492   break;
    493 }
    494
    495 //:: jump
    496
    497 :(code)
    498 void test_jump_mem_at_r32() {
    499   Reg[EAX].i = 0x2000;
    500   run(
    501       "== 0x1\n"  // code segment
    502       // op     ModR/M  SIB   displacement  immediate
    503       "  ff     20                                    \n"  // jump to *EAX
    504       // ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX)
    505       "  05                                 00 00 00 01\n"
    506       "  05                                 00 00 00 02\n"
    507       "== 0x2000\n"  // data segment
    508       "08 00 00 00\n"  // 0x00000008
    509   );
    510   CHECK_TRACE_CONTENTS(
    511       "run: 0x00000001 opcode: ff\n"
    512       "run: jump to r/m32\n"
    513       "run: effective address is 0x00002000 (EAX)\n"
    514       "run: jumping to 0x00000008\n"
    515       "run: 0x00000008 opcode: 05\n"
    516   );
    517   CHECK_TRACE_DOESNT_CONTAIN("run: 0x00000003 opcode: 05");
    518 }
    519
    520 :(before "End Op ff Subops")
    521 case 4: {  // jump to r/m32
    522   trace(Callstack_depth+1, "run") << "jump to r/m32" << end();
    523   const int32_t* arg2 = effective_address(modrm);
    524   EIP = *arg2;
    525   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
    526   break;
    527 }
    528
    529 //:: push
    530
    531 :(code)
    532 void test_push_mem_at_r32() {
    533   Reg[EAX].i = 0x2000;
    534   Reg[ESP].u = 0x14;
    535   run(
    536       "== 0x1\n"  // code segment
    537       // op     ModR/M  SIB   displacement  immediate
    538       "  ff     30                                    \n"  // push *EAX to stack
    539       "== 0x2000\n"  // data segment
    540       "af 00 00 00\n"  // 0x000000af
    541   );
    542   CHECK_TRACE_CONTENTS(
    543       "run: push r/m32\n"
    544       "run: effective address is 0x00002000 (EAX)\n"
    545       "run: decrementing ESP to 0x00000010\n"
    546       "run: pushing value 0x000000af\n"
    547   );
    548 }
    549
    550 :(before "End Op ff Subops")
    551 case 6: {  // push r/m32 to stack
    552   trace(Callstack_depth+1, "run") << "push r/m32" << end();
    553   const int32_t* val = effective_address(modrm);
    554   push(*val);
    555   break;
    556 }
    557
    558 //:: pop
    559
    560 :(before "End Initialize Op Names")
    561 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
    562
    563 :(code)
    564 void test_pop_mem_at_r32() {
    565   Reg[EAX].i = 0x60;
    566   Reg[ESP].u = 0x2000;
    567   run(
    568       "== 0x1\n"  // code segment
    569       // op     ModR/M  SIB   displacement  immediate
    570       "  8f     00                                    \n"  // pop stack into *EAX
    571       // ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX)
    572       "== 0x2000\n"  // data segment
    573       "30 00 00 00\n"  // 0x00000030
    574   );
    575   CHECK_TRACE_CONTENTS(
    576       "run: pop into r/m32\n"
    577       "run: effective address is 0x00000060 (EAX)\n"
    578       "run: popping value 0x00000030\n"
    579       "run: incrementing ESP to 0x00002004\n"
    580   );
    581 }
    582
    583 :(before "End Single-Byte Opcodes")
    584 case 0x8f: {  // pop stack into r/m32
    585   const uint8_t modrm = next();
    586   const uint8_t subop = (modrm>>3)&0x7;
    587   switch (subop) {
    588     case 0: {
    589       trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
    590       int32_t* dest = effective_address(modrm);
    591       *dest = pop();
    592       break;
    593     }
    594   }
    595   break;
    596 }
    597
    598 //:: special-case for loading address from disp32 rather than register
    599
    600 :(code)
    601 void test_add_r32_to_mem_at_displacement() {
    602   Reg[EBX].i = 0x10;  // source
    603   run(
    604       "== 0x1\n"  // code segment
    605       // op     ModR/M  SIB   displacement  immediate
    606       "  01     1d            00 20 00 00             \n"  // add EBX to *0x2000
    607       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32)
    608       "== 0x2000\n"  // data segment
    609       "01 00 00 00\n"  // 0x00000001
    610   );
    611   CHECK_TRACE_CONTENTS(
    612       "run: add EBX to r/m32\n"
    613       "run: effective address is 0x00002000 (disp32)\n"
    614       "run: storing 0x00000011\n"
    615   );
    616 }
    617
    618 :(before "End Mod 0 Special-cases(addr)")
    619 case 5:  // exception: mod 0b00 rm 0b101 => incoming disp32
    620   addr = next32();
    621   trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
    622   break;
    623
    624 //:
    625
    626 :(code)
    627 void test_add_r32_to_mem_at_r32_plus_disp8() {
    628   Reg[EBX].i = 0x10;  // source
    629   Reg[EAX].i = 0x1ffe;  // dest
    630   run(
    631       "== 0x1\n"  // code segment
    632       // op     ModR/M  SIB   displacement  immediate
    633       "  01     58            02                      \n"  // add EBX to *(EAX+2)
    634       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
    635       "== 0x2000\n"  // data segment
    636       "01 00 00 00\n"  // 0x00000001
    637   );
    638   CHECK_TRACE_CONTENTS(
    639       "run: add EBX to r/m32\n"
    640       "run: effective address is initially 0x00001ffe (EAX)\n"
    641       "run: effective address is 0x00002000 (after adding disp8)\n"
    642       "run: storing 0x00000011\n"
    643   );
    644 }
    645
    646 :(before "End Mod Special-cases(addr)")
    647 case 1:  // indirect + disp8 addressing
    648   switch (rm) {
    649   default:
    650     addr = Reg[rm].u;
    651     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
    652     break;
    653   // End Mod 1 Special-cases(addr)
    654   }
    655   if (addr > 0) {
    656     addr += static_cast<int8_t>(next());
    657     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
    658   }
    659   break;
    660
    661 :(code)
    662 void test_add_r32_to_mem_at_r32_plus_negative_disp8() {
    663   Reg[EBX].i = 0x10;  // source
    664   Reg[EAX].i = 0x2001;  // dest
    665   run(
    666       "== 0x1\n"  // code segment
    667       // op     ModR/M  SIB   displacement  immediate
    668       "  01     58            ff                      \n"  // add EBX to *(EAX-1)
    669       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
    670       "== 0x2000\n"  // data segment
    671       "01 00 00 00\n"  // 0x00000001
    672   );
    673   CHECK_TRACE_CONTENTS(
    674       "run: add EBX to r/m32\n"
    675       "run: effective address is initially 0x00002001 (EAX)\n"
    676       "run: effective address is 0x00002000 (after adding disp8)\n"
    677       "run: storing 0x00000011\n"
    678   );
    679 }
    680
    681 //:
    682
    683 :(code)
    684 void test_add_r32_to_mem_at_r32_plus_disp32() {
    685   Reg[EBX].i = 0x10;  // source
    686   Reg[EAX].i = 0x1ffe;  // dest
    687   run(
    688       "== 0x1\n"  // code segment
    689       // op     ModR/M  SIB   displacement  immediate
    690       "  01     98            02 00 00 00             \n"  // add EBX to *(EAX+2)
    691       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
    692       "== 0x2000\n"  // data segment
    693       "01 00 00 00\n"  // 0x00000001
    694   );
    695   CHECK_TRACE_CONTENTS(
    696       "run: add EBX to r/m32\n"
    697       "run: effective address is initially 0x00001ffe (EAX)\n"
    698       "run: effective address is 0x00002000 (after adding disp32)\n"
    699       "run: storing 0x00000011\n"
    700   );
    701 }
    702
    703 :(before "End Mod Special-cases(addr)")
    704 case 2:  // indirect + disp32 addressing
    705   switch (rm) {
    706   default:
    707     addr = Reg[rm].u;
    708     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
    709     break;
    710   // End Mod 2 Special-cases(addr)
    711   }
    712   if (addr > 0) {
    713     addr += next32();
    714     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
    715   }
    716   break;
    717
    718 :(code)
    719 void test_add_r32_to_mem_at_r32_plus_negative_disp32() {
    720   Reg[EBX].i = 0x10;  // source
    721   Reg[EAX].i = 0x2001;  // dest
    722   run(
    723       "== 0x1\n"  // code segment
    724       // op     ModR/M  SIB   displacement  immediate
    725       "  01     98            ff ff ff ff             \n"  // add EBX to *(EAX-1)
    726       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
    727       "== 0x2000\n"  // data segment
    728       "01 00 00 00\n"  // 0x00000001
    729   );
    730   CHECK_TRACE_CONTENTS(
    731       "run: add EBX to r/m32\n"
    732       "run: effective address is initially 0x00002001 (EAX)\n"
    733       "run: effective address is 0x00002000 (after adding disp32)\n"
    734       "run: storing 0x00000011\n"
    735   );
    736 }
    737
    738 //:: copy address (lea)
    739
    740 :(before "End Initialize Op Names")
    741 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
    742
    743 :(code)
    744 void test_copy_address() {
    745   Reg[EAX].u = 0x2000;
    746   run(
    747       "== 0x1\n"  // code segment
    748       // op     ModR/M  SIB   displacement  immediate
    749       "  8d     18                                    \n"  // copy address in EAX into EBX
    750       // ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
    751   );
    752   CHECK_TRACE_CONTENTS(
    753       "run: copy address into EBX\n"
    754       "run: effective address is 0x00002000 (EAX)\n"
    755   );
    756 }
    757
    758 :(before "End Single-Byte Opcodes")
    759 case 0x8d: {  // copy address of m32 to r32
    760   const uint8_t modrm = next();
    761   const uint8_t arg1 = (modrm>>3)&0x7;
    762   trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
    763   Reg[arg1].u = effective_address_number(modrm);
    764   break;
    765 }