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