1 //: operating on memory at the address provided by some register
  2 
  3 :(scenario add_r32_to_mem_at_r32)
  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  18                                     # add EBX (reg 3) to *EAX (reg 0)
  9 +run: add reg 3 to effective address
 10 +run: effective address is mem at address 0x60 (reg 0)
 11 +run: storing 0x00000011
 12 
 13 :(before "End Mod Special-cases")
 14 case 0:
 15   // mod 0 is usually indirect addressing
 16   switch (rm) {
 17   default:
 18   ¦ trace(2, "run") << "effective address is mem at address 0x" << std::hex << Reg[rm].u << " (reg " << NUM(rm) << ")" << end();
 19   ¦ assert(Reg[rm].u + sizeof(int32_t) <= Mem.size());
 20   ¦ result = reinterpret_cast<int32_t*>(&Mem.at(Reg[rm].u));  // rely on the host itself being in little-endian order
 21   ¦ break;
 22   // End Mod 0 Special-cases
 23   }
 24   break;
 25 
 26 //:
 27 
 28 :(scenario add_mem_at_r32_to_r32)
 29 % Reg[0].i = 0x60;
 30 % Reg[3].i = 0x10;
 31 % SET_WORD_IN_MEM(0x60, 1);
 32 # op  ModR/M  SIB   displacement  immediate
 33   03  18                                      # add *EAX (reg 0) to EBX (reg 3)
 34 +run: add effective address to reg 3
 35 +run: effective address is mem at address 0x60 (reg 0)
 36 +run: storing 0x00000011
 37 
 38 :(before "End Single-Byte Opcodes")
 39 case 0x03: {  // add r/m32 to r32
 40   uint8_t modrm = next();
 41   uint8_t arg1 = (modrm>>3)&0x7;
 42   trace(2, "run") << "add effective address to reg " << NUM(arg1) << end();
 43   const int32_t* arg2 = effective_address(modrm);
 44   BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2);
 45   break;
 46 }
 47 
 48 //:: subtract
 49 
 50 :(scenario subtract_r32_from_mem_at_r32)
 51 % Reg[0].i = 0x60;
 52 % SET_WORD_IN_MEM(0x60, 10);
 53 % Reg[3].i = 1;
 54 # op  ModRM   SIB   displacement  immediate
 55   29  18                                      # subtract EBX (reg 3) from *EAX (reg 0)
 56 +run: subtract reg 3 from effective address
 57 +run: effective address is mem at address 0x60 (reg 0)
 58 +run: storing 0x00000009
 59 
 60 //:
 61 
 62 :(scenario subtract_mem_at_r32_from_r32)
 63 % Reg[0].i = 0x60;
 64 % SET_WORD_IN_MEM(0x60, 1);
 65 % Reg[3].i = 10;
 66 # op  ModRM   SIB   displacement  immediate
 67   2b  18                                      # subtract *EAX (reg 0) from EBX (reg 3)
 68 +run: subtract effective address from reg 3
 69 +run: effective address is mem at address 0x60 (reg 0)
 70 +run: storing 0x00000009
 71 
 72 :(before "End Single-Byte Opcodes")
 73 case 0x2b: {  // subtract r/m32 from r32
 74   uint8_t modrm = next();
 75   uint8_t arg1 = (modrm>>3)&0x7;
 76   trace(2, "run") << "subtract effective address from reg " << NUM(arg1) << end();
 77   const int32_t* arg2 = effective_address(modrm);
 78   BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2);
 79   break;
 80 }
 81 
 82 //:: and
 83 
 84 :(scenario and_r32_with_mem_at_r32)
 85 % Reg[0].i = 0x60;
 86 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
 87 % Reg[3].i = 0xff;
 88 # op  ModRM   SIB   displacement  immediate
 89   21  18                                      # and EBX (reg 3) with *EAX (reg 0)
 90 +run: and reg 3 with effective address
 91 +run: effective address is mem at address 0x60 (reg 0)
 92 +run: storing 0x0000000d
 93 
 94 //:
 95 
 96 :(scenario and_mem_at_r32_with_r32)
 97 % Reg[0].i = 0x60;
 98 % SET_WORD_IN_MEM(0x60, 0x000000ff);
 99 % Reg[3].i = 0x0a0b0c0d;
100 # op  ModRM   SIB   displacement  immediate
101   23  18                                      # and *EAX (reg 0) with EBX (reg 3)
102 +run: and effective address with reg 3
103 +run: effective address is mem at address 0x60 (reg 0)
104 +run: storing 0x0000000d
105 
106 :(before "End Single-Byte Opcodes")
107 case 0x23: {  // and r/m32 with r32
108   uint8_t modrm = next();
109   uint8_t arg1 = (modrm>>3)&0x7;
110   trace(2, "run") << "and effective address with reg " << NUM(arg1) << end();
111   const int32_t* arg2 = effective_address(modrm);
112   BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2);
113   break;
114 }
115 
116 //:: or
117 
118 :(scenario or_r32_with_mem_at_r32)
119 % Reg[0].i = 0x60;
120 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
121 % Reg[3].i = 0xa0b0c0d0;
122 # op  ModRM   SIB   displacement  immediate
123   09  18                                      # or EBX (reg 3) with *EAX (reg 0)
124 +run: or reg 3 with effective address
125 +run: effective address is mem at address 0x60 (reg 0)
126 +run: storing 0xaabbccdd
127 
128 //:
129 
130 :(scenario or_mem_at_r32_with_r32)
131 % Reg[0].i = 0x60;
132 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
133 % Reg[3].i = 0xa0b0c0d0;
134 # op  ModRM   SIB   displacement  immediate
135   0b  18                                      # or *EAX (reg 0) with EBX (reg 3)
136 +run: or effective address with reg 3
137 +run: effective address is mem at address 0x60 (reg 0)
138 +run: storing 0xaabbccdd
139 
140 :(before "End Single-Byte Opcodes")
141 case 0x0b: {  // or r/m32 with r32
142   uint8_t modrm = next();
143   uint8_t arg1 = (modrm>>3)&0x7;
144   trace(2, "run") << "or effective address with reg " << NUM(arg1) << end();
145   const int32_t* arg2 = effective_address(modrm);
146   BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
147   break;
148 }
149 
150 //:: xor
151 
152 :(scenario xor_r32_with_mem_at_r32)
153 % Reg[0].i = 0x60;
154 % SET_WORD_IN_MEM(0x60, 0xaabb0c0d);
155 % Reg[3].i = 0xa0b0c0d0;
156 # op  ModRM   SIB   displacement  immediate
157   31  18                                      # xor EBX (reg 3) with *EAX (reg 0)
158 +run: xor reg 3 with effective address
159 +run: effective address is mem at address 0x60 (reg 0)
160 +run: storing 0x0a0bccdd
161 
162 //:
163 
164 :(scenario xor_mem_at_r32_with_r32)
165 % Reg[0].i = 0x60;
166 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
167 % Reg[3].i = 0xa0b0c0d0;
168 # op  ModRM   SIB   displacement  immediate
169   33  18                                      # xor *EAX (reg 0) with EBX (reg 3)
170 +run: xor effective address with reg 3
171 +run: effective address is mem at address 0x60 (reg 0)
172 +run: storing 0xaabbccdd
173 
174 :(before "End Single-Byte Opcodes")
175 case 0x33: {  // xor r/m32 with r32
176   uint8_t modrm = next();
177   uint8_t arg1 = (modrm>>3)&0x7;
178   trace(2, "run") << "xor effective address with reg " << NUM(arg1) << end();
179   const int32_t* arg2 = effective_address(modrm);
180   BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
181   break;
182 }
183 
184 //:: not
185 
186 :(scenario not_r32_with_mem_at_r32)
187 % Reg[3].i = 0x60;
188 # word at 0x60 is 0x0f0f00ff
189 % SET_WORD_IN_MEM(0x60, 0x0f0f00ff);
190 # op  ModRM   SIB   displacement  immediate
191   f7  03                                      # negate *EBX (reg 3)
192 +run: 'not' of effective address
193 +run: effective address is mem at address 0x60 (reg 3)
194 +run: storing 0xf0f0ff00
195 
196 //:: compare (cmp)
197 
198 :(scenario compare_mem_at_r32_with_r32_greater)
199 % Reg[0].i = 0x60;
200 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
201 % Reg[3].i = 0x0a0b0c07;
202 # op  ModRM   SIB   displacement  immediate
203   39  18                                      # compare EBX (reg 3) with *EAX (reg 0)
204 +run: compare reg 3 with effective address
205 +run: effective address is mem at address 0x60 (reg 0)
206 +run: SF=0; ZF=0; OF=0
207 
208 :(scenario compare_mem_at_r32_with_r32_lesser)
209 % Reg[0].i = 0x60;
210 % SET_WORD_IN_MEM(0x60, 0x0a0b0c07);
211 % Reg[3].i = 0x0a0b0c0d;
212 # op  ModRM   SIB   displacement  immediate
213   39  18                                      # compare EBX (reg 3) with *EAX (reg 0)
214 +run: compare reg 3 with effective address
215 +run: effective address is mem at address 0x60 (reg 0)
216 +run: SF=1; ZF=0; OF=0
217 
218 :(scenario compare_mem_at_r32_with_r32_equal)
219 % Reg[0].i = 0x60;
220 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
221 % Reg[3].i = 0x0a0b0c0d;
222 # op  ModRM   SIB   displacement  immediate
223   39  18                                      # compare EBX (reg 3) with *EAX (reg 0)
224 +run: compare reg 3 with effective address
225 +run: effective address is mem at address 0x60 (reg 0)
226 +run: SF=0; ZF=1; OF=0
227 
228 //:
229 
230 :(scenario compare_r32_with_mem_at_r32_greater)
231 % Reg[0].i = 0x60;
232 % SET_WORD_IN_MEM(0x60, 0x0a0b0c07);
233 % Reg[3].i = 0x0a0b0c0d;
234 # op  ModRM   SIB   displacement  immediate
235   3b  18                                      # compare *EAX (reg 0) with EBX (reg 3)
236 +run: compare effective address with reg 3
237 +run: effective address is mem at address 0x60 (reg 0)
238 +run: SF=0; ZF=0; OF=0
239 
240 :(before "End Single-Byte Opcodes")
241 case 0x3b: {  // set SF if r32 < r/m32
242   uint8_t modrm = next();
243   uint8_t reg1 = (modrm>>3)&0x7;
244   trace(2, "run") << "compare effective address with reg " << NUM(reg1) << end();
245   int32_t arg1 = Reg[reg1].i;
246   int32_t* arg2 = effective_address(modrm);
247   int32_t tmp1 = arg1 - *arg2;
248   SF = (tmp1 < 0);
249   ZF = (tmp1 == 0);
250   int64_t tmp2 = arg1 - *arg2;
251   OF = (tmp1 != tmp2);
252   trace(2, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
253   break;
254 }
255 
256 :(scenario compare_r32_with_mem_at_r32_lesser)
257 % Reg[0].i = 0x60;
258 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
259 % Reg[3].i = 0x0a0b0c07;
260 # op  ModRM   SIB   displacement  immediate
261   3b  18                                      # compare *EAX (reg 0) with EBX (reg 3)
262 +run: compare effective address with reg 3
263 +run: effective address is mem at address 0x60 (reg 0)
264 +run: SF=1; ZF=0; OF=0
265 
266 :(scenario compare_r32_with_mem_at_r32_equal)
267 % Reg[0].i = 0x60;
268 % SET_WORD_IN_MEM(0x60, 0x0a0b0c0d);
269 % Reg[3].i = 0x0a0b0c0d;
270 # op  ModRM   SIB   displacement  immediate
271   3b  18                                      # compare *EAX (reg 0) with EBX (reg 3)
272 +run: compare effective address with reg 3
273 +run: effective address is mem at address 0x60 (reg 0)
274 +run: SF=0; ZF=1; OF=0
275 
276 //:: copy (mov)
277 
278 :(scenario copy_r32_to_mem_at_r32)
279 % Reg[3].i = 0xaf;
280 % Reg[0].i = 0x60;
281 # op  ModRM   SIB   displacement  immediate
282   89  18                                      # copy EBX (reg 3) to *EAX (reg 0)
283 +run: copy reg 3 to effective address
284 +run: effective address is mem at address 0x60 (reg 0)
285 +run: storing 0x000000af
286 
287 //:
288 
289 :(scenario copy_mem_at_r32_to_r32)
290 % Reg[0].i = 0x60;
291 % SET_WORD_IN_MEM(0x60, 0x000000af);
292 # op  ModRM   SIB   displacement  immediate
293   8b  18                                      # copy *EAX (reg 0) to EBX (reg 3)
294 +run: copy effective address to reg 3
295 +run: effective address is mem at address 0x60 (reg 0)
296 +run: storing 0x000000af
297 
298 :(before "End Single-Byte Opcodes")
299 case 0x8b: {  // copy r32 to r/m32
300   uint8_t modrm = next();
301   uint8_t reg1 = (modrm>>3)&0x7;
302   trace(2, "run") << "copy effective address to reg " << NUM(reg1) << end();
303   int32_t* arg2 = effective_address(modrm);
304   Reg[reg1].i = *arg2;
305   trace(2, "run") << "storing 0x" << HEXWORD << *arg2 << end();
306   break;
307 }
308 
309 //:: jump
310 
311 :(scenario jump_mem_at_r32)
312 % Reg[0].i = 0x60;
313 % SET_WORD_IN_MEM(0x60, 8);
314 # op  ModRM   SIB   displacement  immediate
315   ff  20                                      # jump to *EAX (reg 0)
316   05                              00 00 00 01
317   05                              00 00 00 02
318 +run: inst: 0x00000001
319 +run: jump to effective address
320 +run: effective address is mem at address 0x60 (reg 0)
321 +run: jumping to 0x00000008
322 +run: inst: 0x00000008
323 -run: inst: 0x00000003
324 
325 :(before "End Single-Byte Opcodes")
326 case 0xff: {
327   uint8_t modrm = next();
328   uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
329   switch (subop) {
330   ¦ case 4: {  // jump to r/m32
331   ¦ ¦ trace(2, "run") << "jump to effective address" << end();
332   ¦ ¦ int32_t* arg2 = effective_address(modrm);
333   ¦ ¦ EIP = *arg2;
334   ¦ ¦ trace(2, "run") << "jumping to 0x" << HEXWORD << EIP << end();
335   ¦ ¦ break;
336   ¦ }
337   ¦ // End Op ff Subops
338   }
339   break;
340 }
341 
342 //:: push
343 
344 :(scenario push_mem_at_r32)
345 % Reg[0].i = 0x60;
346 % SET_WORD_IN_MEM(0x60, 0x000000af);
347 % Reg[ESP].u = 0x14;
348 # op  ModRM   SIB   displacement  immediate
349   ff  30                                      # push *EAX (reg 0) to stack
350 +run: push effective address
351 +run: effective address is mem at address 0x60 (reg 0)
352 +run: decrementing ESP to 0x00000010
353 +run: pushing value 0x000000af
354 
355 :(before "End Op ff Subops")
356 case 6: {  // push r/m32 to stack
357   trace(2, "run") << "push effective address" << end();
358   const int32_t* val = effective_address(modrm);
359   push(*val);
360   break;
361 }
362 
363 //:: pop
364 
365 :(scenario pop_mem_at_r32)
366 % Reg[0].i = 0x60;
367 % Reg[ESP].u = 0x10;
368 % SET_WORD_IN_MEM(0x10, 0x00000030);
369 # op  ModRM   SIB   displacement  immediate
370   8f  00                                      # pop stack into *EAX (reg 0)
371 +run: pop into effective address
372 +run: effective address is mem at address 0x60 (reg 0)
373 +run: popping value 0x00000030
374 +run: incrementing ESP to 0x00000014
375 
376 :(before "End Single-Byte Opcodes")
377 case 0x8f: {  // pop stack into r/m32
378   uint8_t modrm = next();
379   uint8_t subop = (modrm>>3)&0x7;
380   switch (subop) {
381   ¦ case 0: {
382   ¦ ¦ trace(2, "run") << "pop into effective address" << end();
383   ¦ ¦ int32_t* dest = effective_address(modrm);
384   ¦ ¦ *dest = pop();
385   ¦ ¦ break;
386   ¦ }
387   }
388   break;
389 }