1 //: operating on memory at the address provided by some register plus optional scale and offset
  2 
  3 :(scenario add_r32_to_mem_at_r32_with_sib)
  4 % Reg[3].i = 0x10;
  5 % Reg[0].i = 0x60;
  6 % SET_WORD_IN_MEM(0x60, 1);
  7 # op  ModR/M  SIB   displacement  immediate
  8   01  1c      20                             # add EBX to *EAX
  9 # ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
 10 # SIB in binary: 00 (scale 1) 100 (no index) 000 (base EAX)
 11 +run: add EBX to r/m32
 12 +run: effective address is initially 0x60 (EAX)
 13 +run: effective address is 0x60
 14 +run: storing 0x00000011
 15 
 16 :(before "End Mod 0 Special-cases(addr)")
 17 case 4:  // exception: mod 0b00 rm 0b100 => incoming SIB (scale-index-base) byte
 18   addr = effective_address_from_sib(mod);
 19   break;
 20 :(code)
 21 uint32_t effective_address_from_sib(uint8_t mod) {
 22   uint8_t sib = next();
 23   uint8_t base = sib&0x7;
 24   uint32_t addr = 0;
 25   if (base != EBP || mod != 0) {
 26     addr = Reg[base].u;
 27     trace(2, "run") << "effective address is initially 0x" << std::hex << addr << " (" << rname(base) << ")" << end();
 28   }
 29   else {
 30     // base == EBP && mod == 0
 31     addr = imm32();  // ignore base
 32     trace(2, "run") << "effective address is initially 0x" << std::hex << addr << " (disp32)" << end();
 33   }
 34   uint8_t index = (sib>>3)&0x7;
 35   if (index == ESP) {
 36     // ignore index and scale
 37     trace(2, "run") << "effective address is 0x" << std::hex << addr << end();
 38   }
 39   else {
 40     uint8_t scale = (1 << (sib>>6));
 41     addr += Reg[index].i*scale;  // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative.
 42     trace(2, "run") << "effective address is 0x" << std::hex << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end();
 43   }
 44   return addr;
 45 }
 46 
 47 :(scenario add_r32_to_mem_at_base_r32_index_r32)
 48 % Reg[3].i = 0x10;  // source
 49 % Reg[0].i = 0x5e;  // dest base
 50 % Reg[1].i = 0x2;  // dest index
 51 % SET_WORD_IN_MEM(0x60, 1);
 52 # op  ModR/M  SIB   displacement  immediate
 53   01  1c      08                             # add EBX to *(EAX+ECX)
 54 # ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
 55 # SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
 56 +run: add EBX to r/m32
 57 +run: effective address is initially 0x5e (EAX)
 58 +run: effective address is 0x60 (after adding ECX*1)
 59 +run: storing 0x00000011
 60 
 61 :(scenario add_r32_to_mem_at_displacement_using_sib)
 62 % Reg[3].i = 0x10;  // source
 63 % SET_WORD_IN_MEM(0x60, 1);
 64 # op  ModR/M  SIB   displacement  immediate
 65   01  1c      25    60 00 00 00              # add EBX to *0x60
 66 # ModR/M in binary: 00 (indirect mode) 011 (src EBX) 100 (dest in SIB)
 67 # SIB in binary: 00 (scale 1) 100 (no index) 101 (not EBP but disp32)
 68 +run: add EBX to r/m32
 69 +run: effective address is initially 0x60 (disp32)
 70 +run: effective address is 0x60
 71 +run: storing 0x00000011
 72 
 73 //:
 74 
 75 :(scenario add_r32_to_mem_at_base_r32_index_r32_plus_disp8)
 76 % Reg[3].i = 0x10;  // source
 77 % Reg[0].i = 0x59;  // dest base
 78 % Reg[1].i = 0x5;  // dest index
 79 % SET_WORD_IN_MEM(0x60, 1);
 80 # op  ModR/M  SIB   displacement  immediate
 81   01  5c      08    02                       # add EBX to *(EAX+ECX+2)
 82 # ModR/M in binary: 01 (indirect+disp8 mode) 011 (src EBX) 100 (dest in SIB)
 83 # SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
 84 +run: add EBX to r/m32
 85 +run: effective address is initially 0x59 (EAX)
 86 +run: effective address is 0x5e (after adding ECX*1)
 87 +run: effective address is 0x60 (after adding disp8)
 88 +run: storing 0x00000011
 89 
 90 :(before "End Mod 1 Special-cases(addr)")
 91 case 4:  // exception: mod 0b01 rm 0b100 => incoming SIB (scale-index-base) byte
 92   addr = effective_address_from_sib(mod);
 93   break;
 94 
 95 //:
 96 
 97 :(scenario add_r32_to_mem_at_base_r32_index_r32_plus_disp32)
 98 % Reg[3].i = 0x10;  // source
 99 % Reg[0].i = 0x59;  // dest base
100 % Reg[1].i = 0x5;  // dest index
101 % SET_WORD_IN_MEM(0x60, 1);
102 # op  ModR/M  SIB   displacement  immediate
103   01  9c      08    02 00 00 00              # add EBX to *(EAX+ECX+2)
104 # ModR/M in binary: 10 (indirect+disp32 mode) 011 (src EBX) 100 (dest in SIB)
105 # SIB in binary: 00 (scale 1) 001 (index ECX) 000 (base EAX)
106 +run: add EBX to r/m32
107 +run: effective address is initially 0x59 (EAX)
108 +run: effective address is 0x5e (after adding ECX*1)
109 +run: effective address is 0x60 (after adding disp32)
110 +run: storing 0x00000011
111 
112 :(before "End Mod 2 Special-cases(addr)")
113 case 4:  // exception: mod 0b10 rm 0b100 => incoming SIB (scale-index-base) byte
114   addr = effective_address_from_sib(mod);
115   break;