# Mu - 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 :(scenario add_r32_to_mem_at_r32)
      5 % Reg[EBX].i = 0x10;
      6 % Reg[EAX].i = 0x2000;
      7 == 0x1  # code segment
      8 # op  ModR/M  SIB   displacement  immediate
      9   01  18                                     # add EBX to *EAX
     10 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
     11 == 0x2000  # data segment
     12 01 00 00 00  # 1
     13 +run: add EBX to r/m32
     14 +run: effective address is 0x00002000 (EAX)
     15 +run: storing 0x00000011
     16
     17 :(before "End Mod Special-cases(addr)")
     18 case 0:  // indirect addressing
     19   switch (rm) {
     20   default:  // address in register
     21     trace(90, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
     22     addr = Reg[rm].u;
     23     break;
     24   // End Mod 0 Special-cases(addr)
     25   }
     26   break;
     27
     28 //:
     29
     30 :(before "End Initialize Op Names")
     31 put_new(Name, "03", "add rm32 to r32 (add)");
     32
     33 :(scenario add_mem_at_r32_to_r32)
     34 % Reg[EAX].i = 0x2000;
     35 % Reg[EBX].i = 0x10;
     36 == 0x1  # code segment
     37 # op  ModR/M  SIB   displacement  immediate
     38   03  18                                      # add *EAX to EBX
     39 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
     40 == 0x2000  # data segment
     41 01 00 00 00  # 1
     42 +run: add r/m32 to EBX
     43 +run: effective address is 0x00002000 (EAX)
     44 +run: storing 0x00000011
     45
     46 :(before "End Single-Byte Opcodes")
     47 case 0x03: {  // add r/m32 to r32
     48   const uint8_t modrm = next();
     49   const uint8_t arg1 = (modrm>>3)&0x7;
     50   trace(90, "run") << "add r/m32 to " << rname(arg1) << end();
     51   const int32_t* arg2 = effective_address(modrm);
     52   BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2);
     53   break;
     54 }
     55
     56 //:: subtract
     57
     58 :(scenario subtract_r32_from_mem_at_r32)
     59 % Reg[EAX].i = 0x2000;
     60 % Reg[EBX].i = 1;
     61 == 0x1  # code segment
     62 # op  ModR/M  SIB   displacement  immediate
     63   29  18                                      # subtract EBX from *EAX
     64 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
     65 == 0x2000  # data segment
     66 0a 00 00 00  # 10
     67 +run: subtract EBX from r/m32
     68 +run: effective address is 0x00002000 (EAX)
     69 +run: storing 0x00000009
     70
     71 //:
     72
     73 :(before "End Initialize Op Names")
     74 put_new(Name, "2b", "subtract rm32 from r32 (sub)");
     75
     76 :(scenario subtract_mem_at_r32_from_r32)
     77 % Reg[EAX].i = 0x2000;
     78 % Reg[EBX].i = 10;
     79 == 0x1  # code segment
     80 # op  ModR/M  SIB   displacement  immediate
     81   2b  18                                      # subtract *EAX from EBX
     82 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
     83 == 0x2000  # data segment
     84 01 00 00 00  # 1
     85 +run: subtract r/m32 from EBX
     86 +run: effective address is 0x00002000 (EAX)
     87 +run: storing 0x00000009
     88
     89 :(before "End Single-Byte Opcodes")
     90 case 0x2b: {  // subtract r/m32 from r32
     91   const uint8_t modrm = next();
     92   const uint8_t arg1 = (modrm>>3)&0x7;
     93   trace(90, "run") << "subtract r/m32 from " << rname(arg1) << end();
     94   const int32_t* arg2 = effective_address(modrm);
     95   BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2);
     96   break;
     97 }
     98
     99 //:: and
    100
    101 :(scenario and_r32_with_mem_at_r32)
    102 % Reg[EAX].i = 0x2000;
    103 % Reg[EBX].i = 0xff;
    104 == 0x1  # code segment
    105 # op  ModR/M  SIB   displacement  immediate
    106   21  18                                      # and EBX with *EAX
    107 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    108 == 0x2000  # data segment
    109 0d 0c 0b 0a  # 0x0a0b0c0d
    110 +run: and EBX with r/m32
    111 +run: effective address is 0x00002000 (EAX)
    112 +run: storing 0x0000000d
    113
    114 //:
    115
    116 :(before "End Initialize Op Names")
    117 put_new(Name, "23", "r32 = bitwise AND of r32 with rm32 (and)");
    118
    119 :(scenario and_mem_at_r32_with_r32)
    120 % Reg[EAX].i = 0x2000;
    121 % Reg[EBX].i = 0x0a0b0c0d;
    122 == 0x1  # code segment
    123 # op  ModR/M  SIB   displacement  immediate
    124   23  18                                      # and *EAX with EBX
    125 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    126 == 0x2000  # data segment
    127 ff 00 00 00  # 0xff
    128 +run: and r/m32 with EBX
    129 +run: effective address is 0x00002000 (EAX)
    130 +run: storing 0x0000000d
    131
    132 :(before "End Single-Byte Opcodes")
    133 case 0x23: {  // and r/m32 with r32
    134   const uint8_t modrm = next();
    135   const uint8_t arg1 = (modrm>>3)&0x7;
    136   trace(90, "run") << "and r/m32 with " << rname(arg1) << end();
    137   const int32_t* arg2 = effective_address(modrm);
    138   BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2);
    139   break;
    140 }
    141
    142 //:: or
    143
    144 :(scenario or_r32_with_mem_at_r32)
    145 % Reg[EAX].i = 0x2000;
    146 % Reg[EBX].i = 0xa0b0c0d0;
    147 == 0x1  # code segment
    148 # op  ModR/M  SIB   displacement  immediate
    149   09  18                                      # or EBX with *EAX
    150 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    151 == 0x2000  # data segment
    152 0d 0c 0b 0a  # 0x0a0b0c0d
    153 +run: or EBX with r/m32
    154 +run: effective address is 0x00002000 (EAX)
    155 +run: storing 0xaabbccdd
    156
    157 //:
    158
    159 :(before "End Initialize Op Names")
    160 put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
    161
    162 :(scenario or_mem_at_r32_with_r32)
    163 % Reg[EAX].i = 0x2000;
    164 % Reg[EBX].i = 0xa0b0c0d0;
    165 == 0x1  # code segment
    166 # op  ModR/M  SIB   displacement  immediate
    167   0b  18                                      # or *EAX with EBX
    168 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    169 == 0x2000  # data segment
    170 0d 0c 0b 0a  # 0x0a0b0c0d
    171 +run: or r/m32 with EBX
    172 +run: effective address is 0x00002000 (EAX)
    173 +run: storing 0xaabbccdd
    174
    175 :(before "End Single-Byte Opcodes")
    176 case 0x0b: {  // or r/m32 with r32
    177   const uint8_t modrm = next();
    178   const uint8_t arg1 = (modrm>>3)&0x7;
    179   trace(90, "run") << "or r/m32 with " << rname(arg1) << end();
    180   const int32_t* arg2 = effective_address(modrm);
    181   BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
    182   break;
    183 }
    184
    185 //:: xor
    186
    187 :(scenario xor_r32_with_mem_at_r32)
    188 % Reg[EAX].i = 0x2000;
    189 % Reg[EBX].i = 0xa0b0c0d0;
    190 == 0x1  # code segment
    191 # op  ModR/M  SIB   displacement  immediate
    192   31  18                                      # xor EBX with *EAX
    193 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    194 == 0x2000  # data segment
    195 0d 0c bb aa  # 0xaabb0c0d
    196 +run: xor EBX with r/m32
    197 +run: effective address is 0x00002000 (EAX)
    198 +run: storing 0x0a0bccdd
    199
    200 //:
    201
    202 :(before "End Initialize Op Names")
    203 put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
    204
    205 :(scenario xor_mem_at_r32_with_r32)
    206 % Reg[EAX].i = 0x2000;
    207 % Reg[EBX].i = 0xa0b0c0d0;
    208 == 0x1  # code segment
    209 # op  ModR/M  SIB   displacement  immediate
    210   33  18                                      # xor *EAX with EBX
    211 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    212 == 0x2000  # data segment
    213 0d 0c 0b 0a  # 0x0a0b0c0d
    214 +run: xor r/m32 with EBX
    215 +run: effective address is 0x00002000 (EAX)
    216 +run: storing 0xaabbccdd
    217
    218 :(before "End Single-Byte Opcodes")
    219 case 0x33: {  // xor r/m32 with r32
    220   const uint8_t modrm = next();
    221   const uint8_t arg1 = (modrm>>3)&0x7;
    222   trace(90, "run") << "xor r/m32 with " << rname(arg1) << end();
    223   const int32_t* arg2 = effective_address(modrm);
    224   BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
    225   break;
    226 }
    227
    228 //:: not
    229
    230 :(scenario not_of_mem_at_r32)
    231 % Reg[EBX].i = 0x2000;
    232 == 0x1  # code segment
    233 # op  ModR/M  SIB   displacement  immediate
    234   f7  13                                      # not *EBX
    235 # ModR/M in binary: 00 (indirect mode) 010 (subop not) 011 (dest EBX)
    236 == 0x2000  # data segment
    237 ff 00 0f 0f  # 0x0f0f00ff
    238 +run: operate on r/m32
    239 +run: effective address is 0x00002000 (EBX)
    240 +run: subop: not
    241 +run: storing 0xf0f0ff00
    242
    243 //:: compare (cmp)
    244
    245 :(scenario compare_mem_at_r32_with_r32_greater)
    246 % Reg[EAX].i = 0x2000;
    247 % Reg[EBX].i = 0x0a0b0c07;
    248 == 0x1  # code segment
    249 # op  ModR/M  SIB   displacement  immediate
    250   39  18                                      # compare EBX with *EAX
    251 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    252 == 0x2000  # data segment
    253 0d 0c 0b 0a  # 0x0a0b0c0d
    254 +run: compare EBX with r/m32
    255 +run: effective address is 0x00002000 (EAX)
    256 +run: SF=0; ZF=0; OF=0
    257
    258 :(scenario compare_mem_at_r32_with_r32_lesser)
    259 % Reg[EAX].i = 0x2000;
    260 % Reg[EBX].i = 0x0a0b0c0d;
    261 == 0x1  # code segment
    262 # op  ModR/M  SIB   displacement  immediate
    263   39  18                                      # compare EBX with *EAX
    264 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    265 == 0x2000  # data segment
    266 07 0c 0b 0a  # 0x0a0b0c0d
    267 +run: compare EBX with r/m32
    268 +run: effective address is 0x00002000 (EAX)
    269 +run: SF=1; ZF=0; OF=0
    270
    271 :(scenario compare_mem_at_r32_with_r32_equal)
    272 % Reg[EAX].i = 0x2000;
    273 % Reg[EBX].i = 0x0a0b0c0d;
    274 == 0x1  # code segment
    275 # op  ModR/M  SIB   displacement  immediate
    276   39  18                                      # compare EBX with *EAX
    277 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    278 == 0x2000  # data segment
    279 0d 0c 0b 0a  # 0x0a0b0c0d
    280 +run: compare EBX with r/m32
    281 +run: effective address is 0x00002000 (EAX)
    282 +run: SF=0; ZF=1; OF=0
    283
    284 //:
    285
    286 :(before "End Initialize Op Names")
    287 put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
    288
    289 :(scenario compare_r32_with_mem_at_r32_greater)
    290 % Reg[EAX].i = 0x2000;
    291 % Reg[EBX].i = 0x0a0b0c0d;
    292 == 0x1  # code segment
    293 # op  ModR/M  SIB   displacement  immediate
    294   3b  18                                      # compare *EAX with EBX
    295 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    296 == 0x2000  # data segment
    297 07 0c 0b 0a  # 0x0a0b0c0d
    298 +run: compare r/m32 with EBX
    299 +run: effective address is 0x00002000 (EAX)
    300 +run: SF=0; ZF=0; OF=0
    301
    302 :(before "End Single-Byte Opcodes")
    303 case 0x3b: {  // set SF if r32 < r/m32
    304   const uint8_t modrm = next();
    305   const uint8_t reg1 = (modrm>>3)&0x7;
    306   trace(90, "run") << "compare r/m32 with " << rname(reg1) << end();
    307   const int32_t arg1 = Reg[reg1].i;
    308   const int32_t* arg2 = effective_address(modrm);
    309   const int32_t tmp1 = arg1 - *arg2;
    310   SF = (tmp1 < 0);
    311   ZF = (tmp1 == 0);
    312   int64_t tmp2 = arg1 - *arg2;
    313   OF = (tmp1 != tmp2);
    314   trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
    315   break;
    316 }
    317
    318 :(scenario compare_r32_with_mem_at_r32_lesser)
    319 % Reg[EAX].i = 0x2000;
    320 % Reg[EBX].i = 0x0a0b0c07;
    321 == 0x1  # code segment
    322 # op  ModR/M  SIB   displacement  immediate
    323   3b  18                                      # compare *EAX with EBX
    324 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    325 == 0x2000  # data segment
    326 0d 0c 0b 0a  # 0x0a0b0c0d
    327 +run: compare r/m32 with EBX
    328 +run: effective address is 0x00002000 (EAX)
    329 +run: SF=1; ZF=0; OF=0
    330
    331 :(scenario compare_r32_with_mem_at_r32_equal)
    332 % Reg[EAX].i = 0x2000;
    333 % Reg[EBX].i = 0x0a0b0c0d;
    334 == 0x1  # code segment
    335 # op  ModR/M  SIB   displacement  immediate
    336   3b  18                                      # compare *EAX with EBX
    337 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    338 == 0x2000  # data segment
    339 0d 0c 0b 0a  # 0x0a0b0c0d
    340 +run: compare r/m32 with EBX
    341 +run: effective address is 0x00002000 (EAX)
    342 +run: SF=0; ZF=1; OF=0
    343
    344 //:: copy (mov)
    345
    346 :(scenario copy_r32_to_mem_at_r32)
    347 % Reg[EBX].i = 0xaf;
    348 % Reg[EAX].i = 0x60;
    349 == 0x1
    350 # op  ModR/M  SIB   displacement  immediate
    351   89  18                                      # copy EBX to *EAX
    352 # ModR/M in binary: 00 (indirect mode) 011 (src EAX) 000 (dest EAX)
    353 +run: copy EBX to r/m32
    354 +run: effective address is 0x00000060 (EAX)
    355 +run: storing 0x000000af
    356
    357 //:
    358
    359 :(before "End Initialize Op Names")
    360 put_new(Name, "8b", "copy rm32 to r32 (mov)");
    361
    362 :(scenario copy_mem_at_r32_to_r32)
    363 % Reg[EAX].i = 0x2000;
    364 == 0x1  # code segment
    365 # op  ModR/M  SIB   displacement  immediate
    366   8b  18                                      # copy *EAX to EBX
    367 # ModR/M in binary: 00 (indirect mode) 011 (src EBX) 000 (dest EAX)
    368 == 0x2000  # data segment
    369 af 00 00 00  # 0xaf
    370 +run: copy r/m32 to EBX
    371 +run: effective address is 0x00002000 (EAX)
    372 +run: storing 0x000000af
    373
    374 :(before "End Single-Byte Opcodes")
    375 case 0x8b: {  // copy r32 to r/m32
    376   const uint8_t modrm = next();
    377   const uint8_t rdest = (modrm>>3)&0x7;
    378   trace(90, "run") << "copy r/m32 to " << rname(rdest) << end();
    379   const int32_t* src = effective_address(modrm);
    380   Reg[rdest].i = *src;
    381   trace(90, "run") << "storing 0x" << HEXWORD << *src << end();
    382   break;
    383 }
    384
    385 //:: jump
    386
    387 :(scenario jump_mem_at_r32)
    388 % Reg[EAX].i = 0x2000;
    389 == 0x1  # code segment
    390 # op  ModR/M  SIB   displacement  immediate
    391   ff  20                                      # jump to *EAX
    392 # ModR/M in binary: 00 (indirect mode) 100 (jump to r/m32) 000 (src EAX)
    393   05                              00 00 00 01
    394   05                              00 00 00 02
    395 == 0x2000  # data segment
    396 08 00 00 00  # 8
    397 +run: inst: 0x00000001
    398 +run: jump to r/m32
    399 +run: effective address is 0x00002000 (EAX)
    400 +run: jumping to 0x00000008
    401 +run: inst: 0x00000008
    402 -run: inst: 0x00000003
    403
    404 :(before "End Op ff Subops")
    405 case 4: {  // jump to r/m32
    406   trace(90, "run") << "jump to r/m32" << end();
    407   const int32_t* arg2 = effective_address(modrm);
    408   EIP = *arg2;
    409   trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end();
    410   break;
    411 }
    412
    413 //:: push
    414
    415 :(scenario push_mem_at_r32)
    416 % Reg[EAX].i = 0x2000;
    417 % Reg[ESP].u = 0x14;
    418 == 0x1  # code segment
    419 # op  ModR/M  SIB   displacement  immediate
    420   ff  30                                      # push *EAX to stack
    421 # ModR/M in binary: 00 (indirect mode) 110 (push r/m32) 000 (src EAX)
    422 == 0x2000  # data segment
    423 af 00 00 00  # 0xaf
    424 +run: push r/m32
    425 +run: effective address is 0x00002000 (EAX)
    426 +run: decrementing ESP to 0x00000010
    427 +run: pushing value 0x000000af
    428
    429 :(before "End Op ff Subops")
    430 case 6: {  // push r/m32 to stack
    431   trace(90, "run") << "push r/m32" << end();
    432   const int32_t* val = effective_address(modrm);
    433   push(*val);
    434   break;
    435 }
    436
    437 //:: pop
    438
    439 :(before "End Initialize Op Names")
    440 put_new(Name, "8f", "pop top of stack to rm32 (pop)");
    441
    442 :(scenario pop_mem_at_r32)
    443 % Reg[EAX].i = 0x60;
    444 % Reg[ESP].u = 0x2000;
    445 == 0x1  # code segment
    446 # op  ModR/M  SIB   displacement  immediate
    447   8f  00                                      # pop stack into *EAX
    448 # ModR/M in binary: 00 (indirect mode) 000 (pop r/m32) 000 (dest EAX)
    449 == 0x2000  # data segment
    450 30 00 00 00  # 0x30
    451 +run: pop into r/m32
    452 +run: effective address is 0x00000060 (EAX)
    453 +run: popping value 0x00000030
    454 +run: incrementing ESP to 0x00002004
    455
    456 :(before "End Single-Byte Opcodes")
    457 case 0x8f: {  // pop stack into r/m32
    458   const uint8_t modrm = next();
    459   const uint8_t subop = (modrm>>3)&0x7;
    460   switch (subop) {
    461     case 0: {
    462       trace(90, "run") << "pop into r/m32" << end();
    463       int32_t* dest = effective_address(modrm);
    464       *dest = pop();
    465       break;
    466     }
    467   }
    468   break;
    469 }
    470
    471 //:: special-case for loading address from disp32 rather than register
    472
    473 :(scenario add_r32_to_mem_at_displacement)
    474 % Reg[EBX].i = 0x10;  // source
    475 == 0x1  # code segment
    476 # op  ModR/M  SIB   displacement  immediate
    477   01  1d            00 20 00 00              # add EBX to *0x2000
    478 # ModR/M in binary: 00 (indirect mode) 011 (src EBX) 101 (dest in disp32)
    479 == 0x2000  # data segment
    480 01 00 00 00  # 1
    481 +run: add EBX to r/m32
    482 +run: effective address is 0x00002000 (disp32)
    483 +run: storing 0x00000011
    484
    485 :(before "End Mod 0 Special-cases(addr)")
    486 case 5:  // exception: mod 0b00 rm 0b101 => incoming disp32
    487   addr = next32();
    488   trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
    489   break;
    490
    491 //:
    492
    493 :(scenario add_r32_to_mem_at_r32_plus_disp8)
    494 % Reg[EBX].i = 0x10;  // source
    495 % Reg[EAX].i = 0x1ffe;  // dest
    496 == 0x1  # code segment
    497 # op  ModR/M  SIB   displacement  immediate
    498   01  58            02                       # add EBX to *(EAX+2)
    499 # ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
    500 == 0x2000  # data segment
    501 01 00 00 00  # 1
    502 +run: add EBX to r/m32
    503 +run: effective address is initially 0x00001ffe (EAX)
    504 +run: effective address is 0x00002000 (after adding disp8)
    505 +run: storing 0x00000011
    506
    507 :(before "End Mod Special-cases(addr)")
    508 case 1:  // indirect + disp8 addressing
    509   switch (rm) {
    510   default:
    511     addr = Reg[rm].u;
    512     trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
    513     break;
    514   // End Mod 1 Special-cases(addr)
    515   }
    516   if (addr > 0) {
    517     addr += static_cast<int8_t>(next());
    518     trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
    519   }
    520   break;
    521
    522 :(scenario add_r32_to_mem_at_r32_plus_negative_disp8)
    523 % Reg[EBX].i = 0x10;  // source
    524 % Reg[EAX].i = 0x2001;  // dest
    525 == 0x1  # code segment
    526 # op  ModR/M  SIB   displacement  immediate
    527   01  58            ff                       # add EBX to *(EAX-1)
    528 # ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 000 (dest EAX)
    529 == 0x2000  # data segment
    530 01 00 00 00  # 1
    531 +run: add EBX to r/m32
    532 +run: effective address is initially 0x00002001 (EAX)
    533 +run: effective address is 0x00002000 (after adding disp8)
    534 +run: storing 0x00000011
    535
    536 //:
    537
    538 :(scenario add_r32_to_mem_at_r32_plus_disp32)
    539 % Reg[EBX].i = 0x10;  // source
    540 % Reg[EAX].i = 0x1ffe;  // dest
    541 == 0x1  # code segment
    542 # op  ModR/M  SIB   displacement  immediate
    543   01  98            02 00 00 00              # add EBX to *(EAX+2)
    544 # ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
    545 == 0x2000  # data segment
    546 01 00 00 00  # 1
    547 +run: add EBX to r/m32
    548 +run: effective address is initially 0x00001ffe (EAX)
    549 +run: effective address is 0x00002000 (after adding disp32)
    550 +run: storing 0x00000011
    551
    552 :(before "End Mod Special-cases(addr)")
    553 case 2:  // indirect + disp32 addressing
    554   switch (rm) {
    555   default:
    556     addr = Reg[rm].u;
    557     trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
    558     break;
    559   // End Mod 2 Special-cases(addr)
    560   }
    561   if (addr > 0) {
    562     addr += next32();
    563     trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
    564   }
    565   break;
    566
    567 :(scenario add_r32_to_mem_at_r32_plus_negative_disp32)
    568 % Reg[EBX].i = 0x10;  // source
    569 % Reg[EAX].i = 0x2001;  // dest
    570 == 0x1  # code segment
    571 # op  ModR/M  SIB   displacement  immediate
    572   01  98            ff ff ff ff              # add EBX to *(EAX-1)
    573 # ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 000 (dest EAX)
    574 == 0x2000  # data segment
    575 01 00 00 00  # 1
    576 +run: add EBX to r/m32
    577 +run: effective address is initially 0x00002001 (EAX)
    578 +run: effective address is 0x00002000 (after adding disp32)
    579 +run: storing 0x00000011
    580
    581 //:: copy address (lea)
    582
    583 :(before "End Initialize Op Names")
    584 put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
    585
    586 :(scenario copy_address)
    587 % Reg[EAX].u = 0x2000;
    588 == 0x1
    589 # op  ModR/M  SIB   displacement  immediate
    590   8d  18
    591 # ModR/M in binary: 00 (indirect mode) 011 (dest EBX) 000 (src EAX)
    592 +run: copy address into EBX
    593 +run: effective address is 0x00002000 (EAX)
    594
    595 :(before "End Single-Byte Opcodes")
    596 case 0x8d: {  // copy address of m32 to r32
    597   const uint8_t modrm = next();
    598   const uint8_t arg1 = (modrm>>3)&0x7;
    599   trace(90, "run") << "copy address into " << rname(arg1) << end();
    600   Reg[arg1].u = effective_address_number(modrm);
    601   break;
    602 }