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;