https://github.com/akkartik/mu/blob/master/subx/013direct_addressing.cc
  1 //: operating directly on a register
  2 
  3 :(before "End Initialize Op Names")
  4 put_new(Name, "01", "add r32 to rm32 (add)");
  5 
  6 :(scenario add_r32_to_r32)
  7 % Reg[EAX].i = 0x10;
  8 % Reg[EBX].i = 1;
  9 == 0x1
 10 # op  ModR/M  SIB   displacement  immediate
 11   01  d8                                      # add EBX to EAX
 12 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
 13 +run: add EBX to r/m32
 14 +run: r/m32 is EAX
 15 +run: storing 0x00000011
 16 
 17 :(before "End Single-Byte Opcodes")
 18 case 0x01: {  // add r32 to r/m32
 19   uint8_t modrm = next();
 20   uint8_t arg2 = (modrm>>3)&0x7;
 21   trace(90, "run") << "add " << rname(arg2) << " to r/m32" << end();
 22   int32_t* arg1 = effective_address(modrm);
 23   BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i);
 24   break;
 25 }
 26 
 27 :(code)
 28 // Implement tables 2-2 and 2-3 in the Intel manual, Volume 2.
 29 // We return a pointer so that instructions can write to multiple bytes in
 30 // 'Mem' at once.
 31 int32_t* effective_address(uint8_t modrm) {
 32   const uint8_t mod = (modrm>>6);
 33   // ignore middle 3 'reg opcode' bits
 34   const uint8_t rm = modrm & 0x7;
 35   if (mod == 3) {
 36     // mod 3 is just register direct addressing
 37     trace(90, "run") << "r/m32 is " << rname(rm) << end();
 38     return &Reg[rm].i;
 39   }
 40   return mem_addr_i32(effective_address_number(modrm));
 41 }
 42 
 43 uint32_t effective_address_number(uint8_t modrm) {
 44   const uint8_t mod = (modrm>>6);
 45   // ignore middle 3 'reg opcode' bits
 46   const uint8_t rm = modrm & 0x7;
 47   uint32_t addr = 0;
 48   switch (mod) {
 49   case 3:
 50     // mod 3 is just register direct addressing
 51     raise << "unexpected direct addressing mode\n" << end();
 52     return 0;
 53   // End Mod Special-cases(addr)
 54   default:
 55     cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
 56     exit(1);
 57   }
 58   //: other mods are indirect, and they'll set addr appropriately
 59   return addr;
 60 }
 61 
 62 string rname(uint8_t r) {
 63   switch (r) {
 64   case 0: return "EAX";
 65   case 1: return "ECX";
 66   case 2: return "EDX";
 67   case 3: return "EBX";
 68   case 4: return "ESP";
 69   case 5: return "EBP";
 70   case 6: return "ESI";
 71   case 7: return "EDI";
 72   default: raise << "invalid register " << r << '\n' << end();  return "";
 73   }
 74 }
 75 
 76 //:: subtract
 77 
 78 :(before "End Initialize Op Names")
 79 put_new(Name, "29", "subtract r32 from rm32 (sub)");
 80 
 81 :(scenario subtract_r32_from_r32)
 82 % Reg[EAX].i = 10;
 83 % Reg[EBX].i = 1;
 84 == 0x1
 85 # op  ModR/M  SIB   displacement  immediate
 86   29  d8                                      # subtract EBX from EAX
 87 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
 88 +run: subtract EBX from r/m32
 89 +run: r/m32 is EAX
 90 +run: storing 0x00000009
 91 
 92 :(before "End Single-Byte Opcodes")
 93 case 0x29: {  // subtract r32 from r/m32
 94   const uint8_t modrm = next();
 95   const uint8_t arg2 = (modrm>>3)&0x7;
 96   trace(90, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
 97   int32_t* arg1 = effective_address(modrm);
 98   BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i);
 99   break;
100 }
101 
102 //:: multiply
103 
104 :(before "End Initialize Op Names")
105 put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on subop (neg/mul)");
106 
107 :(scenario multiply_eax_by_r32)
108 % Reg[EAX].i = 4;
109 % Reg[ECX].i = 3;
110 == 0x1
111 # op      ModR/M  SIB   displacement  immediate
112   f7      e1                                      # multiply EAX by ECX
113 # ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX)
114 +run: operate on r/m32
115 +run: r/m32 is ECX
116 +run: subop: multiply EAX by r/m32
117 +run: storing 0x0000000c
118 
119 :(before "End Single-Byte Opcodes")
120 case 0xf7: {
121   const uint8_t modrm = next();
122   trace(90, "run") << "operate on r/m32" << end();
123   int32_t* arg1 = effective_address(modrm);
124   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
125   switch (subop) {
126   case 4: {  // mul unsigned EAX by r/m32
127     trace(90, "run") << "subop: multiply EAX by r/m32" << end();
128     const uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1);
129     Reg[EAX].u = result & 0xffffffff;
130     Reg[EDX].u = result >> 32;
131     OF = (Reg[EDX].u != 0);
132     trace(90, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
133     break;
134   }
135   // End Op f7 Subops
136   default:
137     cerr << "unrecognized subop for opcode f7: " << NUM(subop) << '\n';
138     exit(1);
139   }
140   break;
141 }
142 
143 //:
144 
145 :(before "End Initialize Op Names")
146 put_new(Name_0f, "af", "multiply rm32 into r32 (imul)");
147 
148 :(scenario multiply_r32_into_r32)
149 % Reg[EAX].i = 4;
150 % Reg[EBX].i = 2;
151 == 0x1
152 # op      ModR/M  SIB   displacement  immediate
153   0f af   d8                                      # subtract EBX into EAX
154 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
155 +run: multiply r/m32 into EBX
156 +run: r/m32 is EAX
157 +run: storing 0x00000008
158 
159 :(before "End Two-Byte Opcodes Starting With 0f")
160 case 0xaf: {  // multiply r32 into r/m32
161   const uint8_t modrm = next();
162   const uint8_t arg2 = (modrm>>3)&0x7;
163   trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end();
164   const int32_t* arg1 = effective_address(modrm);
165   BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1);
166   break;
167 }
168 
169 //:: negate
170 
171 :(scenario negate_r32)
172 % Reg[EBX].i = 1;
173 == 0x1
174 # op  ModR/M  SIB   displacement  immediate
175   f7  db                                      # negate EBX
176 # ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
177 +run: operate on r/m32
178 +run: r/m32 is EBX
179 +run: subop: negate
180 +run: storing 0xffffffff
181 
182 :(before "End Op f7 Subops")
183 case 3: {  // negate r/m32
184   trace(90, "run") << "subop: negate" << end();
185   // one case that can overflow
186   if (static_cast<uint32_t>(*arg1) == 0x80000000) {
187     trace(90, "run") << "overflow" << end();
188     SF = true;
189     ZF = false;
190     OF = true;
191     break;
192   }
193   *arg1 = -(*arg1);
194   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
195   SF = (*arg1 >> 31);
196   ZF = (*arg1 == 0);
197   OF = false;
198   break;
199 }
200 
201 :(scenario negate_can_overflow)  // in exactly one situation
202 % Reg[EBX].i = 0x80000000;  // INT_MIN
203 == 0x1
204 # op  ModR/M  SIB   displacement  immediate
205   f7  db                                      # negate EBX
206 # ModR/M in binary: 11 (direct mode) 011 (subop negate) 011 (dest EBX)
207 +run: operate on r/m32
208 +run: r/m32 is EBX
209 +run: subop: negate
210 +run: overflow
211 
212 //:: shift left
213 
214 :(before "End Initialize Op Names")
215 put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)");
216 
217 :(scenario shift_left_r32_with_cl)
218 % Reg[EBX].i = 13;
219 % Reg[ECX].i = 1;
220 == 0x1
221 # op  ModR/M  SIB   displacement  immediate
222   d3  e3                                      # negate EBX
223 # ModR/M in binary: 11 (direct mode) 100 (subop shift left) 011 (dest EBX)
224 +run: operate on r/m32
225 +run: r/m32 is EBX
226 +run: subop: shift left by CL bits
227 +run: storing 0x0000001a
228 
229 :(before "End Single-Byte Opcodes")
230 case 0xd3: {
231   const uint8_t modrm = next();
232   trace(90, "run") << "operate on r/m32" << end();
233   int32_t* arg1 = effective_address(modrm);
234   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
235   switch (subop) {
236   case 4: {  // shift left r/m32 by CL
237     trace(90, "run") << "subop: shift left by CL bits" << end();
238     uint8_t count = Reg[ECX].u & 0x1f;
239     // OF is only defined if count is 1
240     if (count == 1) {
241       bool msb = (*arg1 & 0x80000000) >> 1;
242       bool pnsb = (*arg1 & 0x40000000);
243       OF = (msb != pnsb);
244     }
245     *arg1 = (*arg1 << count);
246     ZF = (*arg1 == 0);
247     SF = (*arg1 < 0);
248     trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
249     break;
250   }
251   // End Op d3 Subops
252   default:
253     cerr << "unrecognized subop for opcode d3: " << NUM(subop) << '\n';
254     exit(1);
255   }
256   break;
257 }
258 
259 //:: shift right arithmetic
260 
261 :(scenario shift_right_arithmetic_r32_with_cl)
262 % Reg[EBX].i = 26;
263 % Reg[ECX].i = 1;
264 == 0x1
265 # op  ModR/M  SIB   displacement  immediate
266   d3  fb                                      # negate EBX
267 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
268 +run: operate on r/m32
269 +run: r/m32 is EBX
270 +run: subop: shift right by CL bits, while preserving sign
271 +run: storing 0x0000000d
272 
273 :(before "End Op d3 Subops")
274 case 7: {  // shift right r/m32 by CL, preserving sign
275   trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end();
276   uint8_t count = Reg[ECX].u & 0x1f;
277   *arg1 = (*arg1 >> count);
278   ZF = (*arg1 == 0);
279   SF = (*arg1 < 0);
280   // OF is only defined if count is 1
281   if (count == 1) OF = false;
282   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
283   break;
284 }
285 
286 :(scenario shift_right_arithmetic_odd_r32_with_cl)
287 % Reg[EBX].i = 27;
288 % Reg[ECX].i = 1;
289 == 0x1
290 # op  ModR/M  SIB   displacement  immediate
291   d3  fb                                      # negate EBX
292 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
293 +run: operate on r/m32
294 +run: r/m32 is EBX
295 +run: subop: shift right by CL bits, while preserving sign
296 # result: 13
297 +run: storing 0x0000000d
298 
299 :(scenario shift_right_arithmetic_negative_r32_with_cl)
300 % Reg[EBX].i = 0xfffffffd;  // -3
301 % Reg[ECX].i = 1;
302 == 0x1
303 # op  ModR/M  SIB   displacement  immediate
304   d3  fb                                      # negate EBX
305 # ModR/M in binary: 11 (direct mode) 111 (subop shift right arithmetic) 011 (dest EBX)
306 +run: operate on r/m32
307 +run: r/m32 is EBX
308 +run: subop: shift right by CL bits, while preserving sign
309 # result: -2
310 +run: storing 0xfffffffe
311 
312 //:: shift right logical
313 
314 :(scenario shift_right_logical_r32_with_cl)
315 % Reg[EBX].i = 26;
316 % Reg[ECX].i = 1;
317 == 0x1
318 # op  ModR/M  SIB   displacement  immediate
319   d3  eb                                      # negate EBX
320 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
321 +run: operate on r/m32
322 +run: r/m32 is EBX
323 +run: subop: shift right by CL bits, while padding zeroes
324 +run: storing 0x0000000d
325 
326 :(before "End Op d3 Subops")
327 case 5: {  // shift right r/m32 by CL, preserving sign
328   trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
329   uint8_t count = Reg[ECX].u & 0x1f;
330   // OF is only defined if count is 1
331   if (count == 1) {
332     bool msb = (*arg1 & 0x80000000) >> 1;
333     bool pnsb = (*arg1 & 0x40000000);
334     OF = (msb != pnsb);
335   }
336   uint32_t* uarg1 = reinterpret_cast<uint32_t*>(arg1);
337   *uarg1 = (*uarg1 >> count);
338   ZF = (*uarg1 == 0);
339   // result is always positive by definition
340   SF = false;
341   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
342   break;
343 }
344 
345 :(scenario shift_right_logical_odd_r32_with_cl)
346 % Reg[EBX].i = 27;
347 % Reg[ECX].i = 1;
348 == 0x1
349 # op  ModR/M  SIB   displacement  immediate
350   d3  eb                                      # negate EBX
351 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
352 +run: operate on r/m32
353 +run: r/m32 is EBX
354 +run: subop: shift right by CL bits, while padding zeroes
355 # result: 13
356 +run: storing 0x0000000d
357 
358 :(scenario shift_right_logical_negative_r32_with_cl)
359 % Reg[EBX].i = 0xfffffffd;
360 % Reg[ECX].i = 1;
361 == 0x1
362 # op  ModR/M  SIB   displacement  immediate
363   d3  eb                                      # negate EBX
364 # ModR/M in binary: 11 (direct mode) 101 (subop shift right logical) 011 (dest EBX)
365 +run: operate on r/m32
366 +run: r/m32 is EBX
367 +run: subop: shift right by CL bits, while padding zeroes
368 +run: storing 0x7ffffffe
369 
370 //:: and
371 
372 :(before "End Initialize Op Names")
373 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
374 
375 :(scenario and_r32_with_r32)
376 % Reg[EAX].i = 0x0a0b0c0d;
377 % Reg[EBX].i = 0x000000ff;
378 == 0x1
379 # op  ModR/M  SIB   displacement  immediate
380   21  d8                                      # and EBX with destination EAX
381 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
382 +run: and EBX with r/m32
383 +run: r/m32 is EAX
384 +run: storing 0x0000000d
385 
386 :(before "End Single-Byte Opcodes")
387 case 0x21: {  // and r32 with r/m32
388   const uint8_t modrm = next();
389   const uint8_t arg2 = (modrm>>3)&0x7;
390   trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end();
391   int32_t* arg1 = effective_address(modrm);
392   BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
393   break;
394 }
395 
396 //:: or
397 
398 :(before "End Initialize Op Names")
399 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
400 
401 :(scenario or_r32_with_r32)
402 % Reg[EAX].i = 0x0a0b0c0d;
403 % Reg[EBX].i = 0xa0b0c0d0;
404 == 0x1
405 # op  ModR/M  SIB   displacement  immediate
406   09  d8                                      # or EBX with destination EAX
407 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
408 +run: or EBX with r/m32
409 +run: r/m32 is EAX
410 +run: storing 0xaabbccdd
411 
412 :(before "End Single-Byte Opcodes")
413 case 0x09: {  // or r32 with r/m32
414   const uint8_t modrm = next();
415   const uint8_t arg2 = (modrm>>3)&0x7;
416   trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end();
417   int32_t* arg1 = effective_address(modrm);
418   BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
419   break;
420 }
421 
422 //:: xor
423 
424 :(before "End Initialize Op Names")
425 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
426 
427 :(scenario xor_r32_with_r32)
428 % Reg[EAX].i = 0x0a0b0c0d;
429 % Reg[EBX].i = 0xaabbc0d0;
430 == 0x1
431 # op  ModR/M  SIB   displacement  immediate
432   31  d8                                      # xor EBX with destination EAX
433 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
434 +run: xor EBX with r/m32
435 +run: r/m32 is EAX
436 +run: storing 0xa0b0ccdd
437 
438 :(before "End Single-Byte Opcodes")
439 case 0x31: {  // xor r32 with r/m32
440   const uint8_t modrm = next();
441   const uint8_t arg2 = (modrm>>3)&0x7;
442   trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end();
443   int32_t* arg1 = effective_address(modrm);
444   BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
445   break;
446 }
447 
448 //:: not
449 
450 :(scenario not_r32)
451 % Reg[EBX].i = 0x0f0f00ff;
452 == 0x1
453 # op  ModR/M  SIB   displacement  immediate
454   f7  d3                                      # not EBX
455 # ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX)
456 +run: operate on r/m32
457 +run: r/m32 is EBX
458 +run: subop: not
459 +run: storing 0xf0f0ff00
460 
461 :(before "End Op f7 Subops")
462 case 2: {  // not r/m32
463   trace(90, "run") << "subop: not" << end();
464   *arg1 = ~(*arg1);
465   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
466   SF = (*arg1 >> 31);
467   ZF = (*arg1 == 0);
468   OF = false;
469   break;
470 }
471 
472 //:: compare (cmp)
473 
474 :(before "End Initialize Op Names")
475 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
476 
477 :(scenario compare_r32_with_r32_greater)
478 % Reg[EAX].i = 0x0a0b0c0d;
479 % Reg[EBX].i = 0x0a0b0c07;
480 == 0x1
481 # op  ModR/M  SIB   displacement  immediate
482   39  d8                                      # compare EBX with EAX
483 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
484 +run: compare EBX with r/m32
485 +run: r/m32 is EAX
486 +run: SF=0; ZF=0; OF=0
487 
488 :(before "End Single-Byte Opcodes")
489 case 0x39: {  // set SF if r/m32 < r32
490   const uint8_t modrm = next();
491   const uint8_t reg2 = (modrm>>3)&0x7;
492   trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
493   const int32_t* arg1 = effective_address(modrm);
494   const int32_t arg2 = Reg[reg2].i;
495   const int32_t tmp1 = *arg1 - arg2;
496   SF = (tmp1 < 0);
497   ZF = (tmp1 == 0);
498   const int64_t tmp2 = *arg1 - arg2;
499   OF = (tmp1 != tmp2);
500   trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
501   break;
502 }
503 
504 :(scenario compare_r32_with_r32_lesser)
505 % Reg[EAX].i = 0x0a0b0c07;
506 % Reg[EBX].i = 0x0a0b0c0d;
507 == 0x1
508 # op  ModR/M  SIB   displacement  immediate
509   39  d8                                      # compare EBX with EAX
510 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
511 +run: compare EBX with r/m32
512 +run: r/m32 is EAX
513 +run: SF=1; ZF=0; OF=0
514 
515 :(scenario compare_r32_with_r32_equal)
516 % Reg[EAX].i = 0x0a0b0c0d;
517 % Reg[EBX].i = 0x0a0b0c0d;
518 == 0x1
519 # op  ModR/M  SIB   displacement  immediate
520   39  d8                                      # compare EBX with EAX
521 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
522 +run: compare EBX with r/m32
523 +run: r/m32 is EAX
524 +run: SF=0; ZF=1; OF=0
525 
526 //:: copy (mov)
527 
528 :(before "End Initialize Op Names")
529 put_new(Name, "89", "copy r32 to rm32 (mov)");
530 
531 :(scenario copy_r32_to_r32)
532 % Reg[EBX].i = 0xaf;
533 == 0x1
534 # op  ModR/M  SIB   displacement  immediate
535   89  d8                                      # copy EBX to EAX
536 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
537 +run: copy EBX to r/m32
538 +run: r/m32 is EAX
539 +run: storing 0x000000af
540 
541 :(before "End Single-Byte Opcodes")
542 case 0x89: {  // copy r32 to r/m32
543   const uint8_t modrm = next();
544   const uint8_t rsrc = (modrm>>3)&0x7;
545   trace(90, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
546   int32_t* dest = effective_address(modrm);
547   *dest = Reg[rsrc].i;
548   trace(90, "run") << "storing 0x" << HEXWORD << *dest << end();
549   break;
550 }
551 
552 //:: xchg
553 
554 :(before "End Initialize Op Names")
555 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
556 
557 :(scenario xchg_r32_with_r32)
558 % Reg[EBX].i = 0xaf;
559 % Reg[EAX].i = 0x2e;
560 == 0x1
561 # op  ModR/M  SIB   displacement  immediate
562   87  d8                                      # exchange EBX with EAX
563 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
564 +run: exchange EBX with r/m32
565 +run: r/m32 is EAX
566 +run: storing 0x000000af in r/m32
567 +run: storing 0x0000002e in EBX
568 
569 :(before "End Single-Byte Opcodes")
570 case 0x87: {  // exchange r32 with r/m32
571   const uint8_t modrm = next();
572   const uint8_t reg2 = (modrm>>3)&0x7;
573   trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
574   int32_t* arg1 = effective_address(modrm);
575   const int32_t tmp = *arg1;
576   *arg1 = Reg[reg2].i;
577   Reg[reg2].i = tmp;
578   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
579   trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
580   break;
581 }
582 
583 //:: increment
584 
585 :(before "End Initialize Op Names")
586 put_new(Name, "40", "increment EAX (inc)");
587 put_new(Name, "41", "increment ECX (inc)");
588 put_new(Name, "42", "increment EDX (inc)");
589 put_new(Name, "43", "increment EBX (inc)");
590 put_new(Name, "44", "increment ESP (inc)");
591 put_new(Name, "45", "increment EBP (inc)");
592 put_new(Name, "46", "increment ESI (inc)");
593 put_new(Name, "47", "increment EDI (inc)");
594 
595 :(scenario increment_r32)
596 % Reg[ECX].u = 0x1f;
597 == 0x1  # code segment
598 # op  ModR/M  SIB   displacement  immediate
599   41                                          # increment ECX
600 +run: increment ECX
601 +run: storing value 0x00000020
602 
603 :(before "End Single-Byte Opcodes")
604 case 0x40:
605 case 0x41:
606 case 0x42:
607 case 0x43:
608 case 0x44:
609 case 0x45:
610 case 0x46:
611 case 0x47: {  // increment r32
612   const uint8_t reg = op & 0x7;
613   trace(90, "run") << "increment " << rname(reg) << end();
614   ++Reg[reg].u;
615   trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
616   break;
617 }
618 
619 :(before "End Initialize Op Names")
620 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)");
621 
622 :(scenario increment_rm32)
623 % Reg[EAX].u = 0x20;
624 == 0x1  # code segment
625 # op  ModR/M  SIB   displacement  immediate
626   ff  c0                                      # increment EAX
627 # ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX)
628 +run: increment r/m32
629 +run: r/m32 is EAX
630 +run: storing value 0x00000021
631 
632 :(before "End Single-Byte Opcodes")
633 case 0xff: {
634   const uint8_t modrm = next();
635   const uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
636   switch (subop) {
637     case 0: {  // increment r/m32
638       trace(90, "run") << "increment r/m32" << end();
639       int32_t* arg = effective_address(modrm);
640       ++*arg;
641       trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
642       break;
643     }
644     default:
645       cerr << "unrecognized subop for ff: " << HEXBYTE << NUM(subop) << '\n';
646       DUMP("");
647       exit(1);
648     // End Op ff Subops
649   }
650   break;
651 }
652 
653 //:: decrement
654 
655 :(before "End Initialize Op Names")
656 put_new(Name, "48", "decrement EAX (dec)");
657 put_new(Name, "49", "decrement ECX (dec)");
658 put_new(Name, "4a", "decrement EDX (dec)");
659 put_new(Name, "4b", "decrement EBX (dec)");
660 put_new(Name, "4c", "decrement ESP (dec)");
661 put_new(Name, "4d", "decrement EBP (dec)");
662 put_new(Name, "4e", "decrement ESI (dec)");
663 put_new(Name, "4f", "decrement EDI (dec)");
664 
665 :(scenario decrement_r32)
666 % Reg[ECX].u = 0x1f;
667 == 0x1  # code segment
668 # op  ModR/M  SIB   displacement  immediate
669   49                                          # decrement ECX
670 +run: decrement ECX
671 +run: storing value 0x0000001e
672 
673 :(before "End Single-Byte Opcodes")
674 case 0x48:
675 case 0x49:
676 case 0x4a:
677 case 0x4b:
678 case 0x4c:
679 case 0x4d:
680 case 0x4e:
681 case 0x4f: {  // decrement r32
682   const uint8_t reg = op & 0x7;
683   trace(90, "run") << "decrement " << rname(reg) << end();
684   --Reg[reg].u;
685   trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
686   break;
687 }
688 
689 :(scenario decrement_rm32)
690 % Reg[EAX].u = 0x20;
691 == 0x1  # code segment
692 # op  ModR/M  SIB   displacement  immediate
693   ff  c8                                      # decrement EAX
694 # ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX)
695 +run: decrement r/m32
696 +run: r/m32 is EAX
697 +run: storing value 0x0000001f
698 
699 :(before "End Op ff Subops")
700 case 1: {  // decrement r/m32
701   trace(90, "run") << "decrement r/m32" << end();
702   int32_t* arg = effective_address(modrm);
703   --*arg;
704   trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
705   break;
706 }
707 
708 //:: push
709 
710 :(before "End Initialize Op Names")
711 put_new(Name, "50", "push EAX to stack (push)");
712 put_new(Name, "51", "push ECX to stack (push)");
713 put_new(Name, "52", "push EDX to stack (push)");
714 put_new(Name, "53", "push EBX to stack (push)");
715 put_new(Name, "54", "push ESP to stack (push)");
716 put_new(Name, "55", "push EBP to stack (push)");
717 put_new(Name, "56", "push ESI to stack (push)");
718 put_new(Name, "57", "push EDI to stack (push)");
719 
720 :(scenario push_r32)
721 % Reg[ESP].u = 0x64;
722 % Reg[EBX].i = 0x0000000a;
723 == 0x1
724 # op  ModR/M  SIB   displacement  immediate
725   53                                          # push EBX to stack
726 +run: push EBX
727 +run: decrementing ESP to 0x00000060
728 +run: pushing value 0x0000000a
729 
730 :(before "End Single-Byte Opcodes")
731 case 0x50:
732 case 0x51:
733 case 0x52:
734 case 0x53:
735 case 0x54:
736 case 0x55:
737 case 0x56:
738 case 0x57: {  // push r32 to stack
739   uint8_t reg = op & 0x7;
740   trace(90, "run") << "push " << rname(reg) << end();
741 //?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
742   push(Reg[reg].u);
743   break;
744 }
745 
746 //:: pop
747 
748 :(before "End Initialize Op Names")
749 put_new(Name, "58", "pop top of stack to EAX (pop)");
750 put_new(Name, "59", "pop top of stack to ECX (pop)");
751 put_new(Name, "5a", "pop top of stack to EDX (pop)");
752 put_new(Name, "5b", "pop top of stack to EBX (pop)");
753 put_new(Name, "5c", "pop top of stack to ESP (pop)");
754 put_new(Name, "5d", "pop top of stack to EBP (pop)");
755 put_new(Name, "5e", "pop top of stack to ESI (pop)");
756 put_new(Name, "5f", "pop top of stack to EDI (pop)");
757 
758 :(scenario pop_r32)
759 % Reg[ESP].u = 0x02000000;
760 % Mem.push_back(vma(0x02000000));  // manually allocate memory
761 % write_mem_i32(0x02000000, 0x0000000a);  // ..before this write
762 == 0x1  # code segment
763 # op  ModR/M  SIB   displacement  immediate
764   5b                                          # pop stack to EBX
765 == 0x2000  # data segment
766 0a 00 00 00  # 0x0a
767 +run: pop into EBX
768 +run: popping value 0x0000000a
769 +run: incrementing ESP to 0x02000004
770 
771 :(before "End Single-Byte Opcodes")
772 case 0x58:
773 case 0x59:
774 case 0x5a:
775 case 0x5b:
776 case 0x5c:
777 case 0x5d:
778 case 0x5e:
779 case 0x5f: {  // pop stack into r32
780   const uint8_t reg = op & 0x7;
781   trace(90, "run") << "pop into " << rname(reg) << end();
782 //?   cerr << "pop from " << Reg[ESP].u << '\n';
783   Reg[reg].u = pop();
784 //?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
785   break;
786 }
787 :(code)
788 uint32_t pop() {
789   const uint32_t result = read_mem_u32(Reg[ESP].u);
790   trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
791   Reg[ESP].u += 4;
792   trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
793   return result;
794 }