https://github.com/akkartik/mu/blob/main/linux/bootstrap/016index_addressing.cc

      1 //: operating on memory at the address provided by some register plus optional scale and offset
      2
      3 :(code)
      4 void test_add_r32_to_mem_at_rm32_with_sib() {
      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     1c      20                              \n"  // add EBX to *EAX
     11       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
     12       // SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX)
     13       "== data 0x2000\n"
     14       "01 00 00 00\n"  // 1
     15   );
     16   CHECK_TRACE_CONTENTS(
     17       "run: add EBX to r/m32\n"
     18       "run: effective address is initially 0x00002000 (EAX)\n"
     19       "run: effective address is 0x00002000\n"
     20       "run: storing 0x00000011\n"
     21   );
     22 }
     23
     24 :(before "End Mod 0 Special-cases(addr)")
     25 case 4:  // exception: mod 0b00 rm 0b100 => incoming SIB (scale-index-base) byte
     26   addr = effective_address_from_sib(mod);
     27   break;
     28 :(code)
     29 uint32_t effective_address_from_sib(uint8_t mod) {
     30   const uint8_t sib = next();
     31   const uint8_t base = sib&0x7;
     32   uint32_t addr = 0;
     33   if (base != EBP || mod != 0) {
     34     addr = Reg[base].u;
     35     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end();
     36   }
     37   else {
     38     // base == EBP && mod == 0
     39     addr = next32();  // ignore base
     40     trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end();
     41   }
     42   const uint8_t index = (sib>>3)&0x7;
     43   if (index == ESP) {
     44     // ignore index and scale
     45     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end();
     46   }
     47   else {
     48     const uint8_t scale = (1 << (sib>>6));
     49     addr += Reg[index].i*scale;  // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative.
     50     trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end();
     51   }
     52   return addr;
     53 }
     54
     55 :(code)
     56 void test_add_r32_to_mem_at_base_r32_index_r32() {
     57   Reg[EBX].i = 0x10;  // source
     58   Reg[EAX].i = 0x1ffe;  // dest base
     59   Reg[ECX].i = 0x2;  // dest index
     60   run(
     61       "== code 0x1\n"
     62       // op     ModR/M  SIB   displacement  immediate
     63       "  01     1c      08                              \n"  // add EBX to *(EAX+ECX)
     64       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
     65       // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
     66       "== data 0x2000\n"
     67       "01 00 00 00\n"  // 1
     68   );
     69   CHECK_TRACE_CONTENTS(
     70       "run: add EBX to r/m32\n"
     71       "run: effective address is initially 0x00001ffe (EAX)\n"
     72       "run: effective address is 0x00002000 (after adding ECX*1)\n"
     73       "run: storing 0x00000011\n"
     74   );
     75 }
     76
     77 :(code)
     78 void test_add_r32_to_mem_at_displacement_using_sib() {
     79   Reg[EBX].i = 0x10;  // source
     80   run(
     81       "== code 0x1\n"
     82       // op     ModR/M  SIB   displacement  immediate
     83       "  01     1c      25    00 20 00 00               \n"  // add EBX to *0x2000
     84       // ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
     85       // SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32)
     86       "== data 0x2000\n"
     87       "01 00 00 00\n"  // 1
     88   );
     89   CHECK_TRACE_CONTENTS(
     90       "run: add EBX to r/m32\n"
     91       "run: effective address is initially 0x00002000 (disp32)\n"
     92       "run: effective address is 0x00002000\n"
     93       "run: storing 0x00000011\n"
     94   );
     95 }
     96
     97 //:
     98
     99 :(code)
    100 void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp8() {
    101   Reg[EBX].i = 0x10;  // source
    102   Reg[EAX].i = 0x1ff9;  // dest base
    103   Reg[ECX].i = 0x5;  // dest index
    104   run(
    105       "== code 0x1\n"
    106       // op     ModR/M  SIB   displacement  immediate
    107       "  01     5c      08    02                        \n"  // add EBX to *(EAX+ECX+2)
    108       // ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB)
    109       // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
    110       "== data 0x2000\n"
    111       "01 00 00 00\n"  // 1
    112   );
    113   CHECK_TRACE_CONTENTS(
    114       "run: add EBX to r/m32\n"
    115       "run: effective address is initially 0x00001ff9 (EAX)\n"
    116       "run: effective address is 0x00001ffe (after adding ECX*1)\n"
    117       "run: effective address is 0x00002000 (after adding disp8)\n"
    118       "run: storing 0x00000011\n"
    119   );
    120 }
    121
    122 :(before "End Mod 1 Special-cases(addr)")
    123 case 4:  // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte
    124   addr = effective_address_from_sib(mod);
    125   break;
    126
    127 //:
    128
    129 :(code)
    130 void test_add_r32_to_mem_at_base_r32_index_r32_plus_disp32() {
    131   Reg[EBX].i = 0x10;  // source
    132   Reg[EAX].i = 0x1ff9;  // dest base
    133   Reg[ECX].i = 0x5;  // dest index
    134   run(
    135       "== code 0x1\n"
    136       // op     ModR/M  SIB   displacement  immediate
    137       "  01     9c      08    02 00 00 00               \n"  // add EBX to *(EAX+ECX+2)
    138       // ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB)
    139       // SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
    140       "== data 0x2000\n"
    141       "01 00 00 00\n"  // 1
    142   );
    143   CHECK_TRACE_CONTENTS(
    144       "run: add EBX to r/m32\n"
    145       "run: effective address is initially 0x00001ff9 (EAX)\n"
    146       "run: effective address is 0x00001ffe (after adding ECX*1)\n"
    147       "run: effective address is 0x00002000 (after adding disp32)\n"
    148       "run: storing 0x00000011\n"
    149   );
    150 }
    151
    152 :(before "End Mod 2 Special-cases(addr)")
    153 case 4:  // exception: mod 0b10 rm 0b100 => incoming SIB (scale-index-base) byte
    154   addr = effective_address_from_sib(mod);
    155   break;