1 //: operating directly on a register
  2 
  3 :(before "End Initialize Op Names(name)")
  4 put(name, "01", "add r32 to rm32");
  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   uint8_t mod = (modrm>>6);
 33   // ignore middle 3 'reg opcode' bits
 34   uint8_t rm = modrm & 0x7;
 35   uint32_t addr = 0;
 36   switch (mod) {
 37   case 3:
 38     // mod 3 is just register direct addressing
 39     trace(90, "run") << "r/m32 is " << rname(rm) << end();
 40     return &Reg[rm].i;
 41   // End Mod Special-cases(addr)
 42   default:
 43     cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
 44     exit(1);
 45   }
 46   //: other mods are indirect, and they'll set addr appropriately
 47   return mem_addr_i32(addr);
 48 }
 49 
 50 string rname(uint8_t r) {
 51   switch (r) {
 52   case 0: return "EAX";
 53   case 1: return "ECX";
 54   case 2: return "EDX";
 55   case 3: return "EBX";
 56   case 4: return "ESP";
 57   case 5: return "EBP";
 58   case 6: return "ESI";
 59   case 7: return "EDI";
 60   default: raise << "invalid register " << r << '\n' << end();  return "";
 61   }
 62 }
 63 
 64 //:: subtract
 65 
 66 :(before "End Initialize Op Names(name)")
 67 put(name, "29", "subtract r32 from rm32");
 68 
 69 :(scenario subtract_r32_from_r32)
 70 % Reg[EAX].i = 10;
 71 % Reg[EBX].i = 1;
 72 == 0x1
 73 # op  ModR/M  SIB   displacement  immediate
 74   29  d8                                      # subtract EBX from EAX
 75 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
 76 +run: subtract EBX from r/m32
 77 +run: r/m32 is EAX
 78 +run: storing 0x00000009
 79 
 80 :(before "End Single-Byte Opcodes")
 81 case 0x29: {  // subtract r32 from r/m32
 82   uint8_t modrm = next();
 83   uint8_t arg2 = (modrm>>3)&0x7;
 84   trace(90, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
 85   int32_t* arg1 = effective_address(modrm);
 86   BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i);
 87   break;
 88 }
 89 
 90 //:: multiply
 91 
 92 :(before "End Initialize Op Names(name)")
 93 put(name, "f7", "test/negate/mul/div rm32 (with EAX if necessary) depending on subop");
 94 
 95 :(scenario multiply_eax_by_r32)
 96 % Reg[EAX].i = 4;
 97 % Reg[ECX].i = 3;
 98 == 0x1
 99 # op      ModR/M  SIB   displacement  immediate
100   f7      e1                                      # multiply EAX by ECX
101 # ModR/M in binary: 11 (direct mode) 100 (subop mul) 001 (src ECX)
102 +run: operate on r/m32
103 +run: r/m32 is ECX
104 +run: subop: multiply EAX by r/m32
105 +run: storing 0x0000000c
106 
107 :(before "End Single-Byte Opcodes")
108 case 0xf7: {  // xor r32 with r/m32
109   uint8_t modrm = next();
110   trace(90, "run") << "operate on r/m32" << end();
111   int32_t* arg1 = effective_address(modrm);
112   uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
113   switch (subop) {
114   case 4: {  // mul unsigned EAX by r/m32
115     trace(90, "run") << "subop: multiply EAX by r/m32" << end();
116     uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1);
117     Reg[EAX].u = result & 0xffffffff;
118     Reg[EDX].u = result >> 32;
119     OF = (Reg[EDX].u != 0);
120     trace(90, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
121     break;
122   }
123   // End Op f7 Subops
124   default:
125     cerr << "unrecognized sub-opcode after f7: " << NUM(subop) << '\n';
126     exit(1);
127   }
128   break;
129 }
130 
131 //:
132 
133 :(before "End Initialize Op Names(name)")
134 put(name_0f, "af", "multiply rm32 into r32");
135 
136 :(scenario multiply_r32_into_r32)
137 % Reg[EAX].i = 4;
138 % Reg[EBX].i = 2;
139 == 0x1
140 # op      ModR/M  SIB   displacement  immediate
141   0f af   d8                                      # subtract EBX into EAX
142 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
143 +run: multiply r/m32 into EBX
144 +run: r/m32 is EAX
145 +run: storing 0x00000008
146 
147 :(before "End Two-Byte Opcodes Starting With 0f")
148 case 0xaf: {  // multiply r32 into r/m32
149   uint8_t modrm = next();
150   uint8_t arg2 = (modrm>>3)&0x7;
151   trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end();
152   int32_t* arg1 = effective_address(modrm);
153   BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1);
154   break;
155 }
156 
157 //:: and
158 
159 :(before "End Initialize Op Names(name)")
160 put(name, "21", "rm32 = bitwise AND of r32 with rm32");
161 
162 :(scenario and_r32_with_r32)
163 % Reg[EAX].i = 0x0a0b0c0d;
164 % Reg[EBX].i = 0x000000ff;
165 == 0x1
166 # op  ModR/M  SIB   displacement  immediate
167   21  d8                                      # and EBX with destination EAX
168 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
169 +run: and EBX with r/m32
170 +run: r/m32 is EAX
171 +run: storing 0x0000000d
172 
173 :(before "End Single-Byte Opcodes")
174 case 0x21: {  // and r32 with r/m32
175   uint8_t modrm = next();
176   uint8_t arg2 = (modrm>>3)&0x7;
177   trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end();
178   int32_t* arg1 = effective_address(modrm);
179   BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
180   break;
181 }
182 
183 //:: or
184 
185 :(before "End Initialize Op Names(name)")
186 put(name, "09", "rm32 = bitwise OR of r32 with rm32");
187 
188 :(scenario or_r32_with_r32)
189 % Reg[EAX].i = 0x0a0b0c0d;
190 % Reg[EBX].i = 0xa0b0c0d0;
191 == 0x1
192 # op  ModR/M  SIB   displacement  immediate
193   09  d8                                      # or EBX with destination EAX
194 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
195 +run: or EBX with r/m32
196 +run: r/m32 is EAX
197 +run: storing 0xaabbccdd
198 
199 :(before "End Single-Byte Opcodes")
200 case 0x09: {  // or r32 with r/m32
201   uint8_t modrm = next();
202   uint8_t arg2 = (modrm>>3)&0x7;
203   trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end();
204   int32_t* arg1 = effective_address(modrm);
205   BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
206   break;
207 }
208 
209 //:: xor
210 
211 :(before "End Initialize Op Names(name)")
212 put(name, "31", "rm32 = bitwise XOR of r32 with rm32");
213 
214 :(scenario xor_r32_with_r32)
215 % Reg[EAX].i = 0x0a0b0c0d;
216 % Reg[EBX].i = 0xaabbc0d0;
217 == 0x1
218 # op  ModR/M  SIB   displacement  immediate
219   31  d8                                      # xor EBX with destination EAX
220 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
221 +run: xor EBX with r/m32
222 +run: r/m32 is EAX
223 +run: storing 0xa0b0ccdd
224 
225 :(before "End Single-Byte Opcodes")
226 case 0x31: {  // xor r32 with r/m32
227   uint8_t modrm = next();
228   uint8_t arg2 = (modrm>>3)&0x7;
229   trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end();
230   int32_t* arg1 = effective_address(modrm);
231   BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
232   break;
233 }
234 
235 //:: not
236 
237 :(before "End Initialize Op Names(name)")
238 put(name, "f7", "bitwise complement of rm32");
239 
240 :(scenario not_r32)
241 % Reg[EBX].i = 0x0f0f00ff;
242 == 0x1
243 # op  ModR/M  SIB   displacement  immediate
244   f7  d3                                      # not EBX
245 # ModR/M in binary: 11 (direct mode) 010 (subop not) 011 (dest EBX)
246 +run: operate on r/m32
247 +run: r/m32 is EBX
248 +run: subop: not
249 +run: storing 0xf0f0ff00
250 
251 :(before "End Op f7 Subops")
252 case 2: {  // not r/m32
253   trace(90, "run") << "subop: not" << end();
254   *arg1 = ~(*arg1);
255   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
256   SF = (*arg1 >> 31);
257   ZF = (*arg1 == 0);
258   OF = false;
259   break;
260 }
261 
262 //:: compare (cmp)
263 
264 :(before "End Initialize Op Names(name)")
265 put(name, "39", "compare: set SF if rm32 < r32");
266 
267 :(scenario compare_r32_with_r32_greater)
268 % Reg[EAX].i = 0x0a0b0c0d;
269 % Reg[EBX].i = 0x0a0b0c07;
270 == 0x1
271 # op  ModR/M  SIB   displacement  immediate
272   39  d8                                      # compare EBX with EAX
273 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
274 +run: compare EBX with r/m32
275 +run: r/m32 is EAX
276 +run: SF=0; ZF=0; OF=0
277 
278 :(before "End Single-Byte Opcodes")
279 case 0x39: {  // set SF if r/m32 < r32
280   uint8_t modrm = next();
281   uint8_t reg2 = (modrm>>3)&0x7;
282   trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
283   int32_t* arg1 = effective_address(modrm);
284   int32_t arg2 = Reg[reg2].i;
285   int32_t tmp1 = *arg1 - arg2;
286   SF = (tmp1 < 0);
287   ZF = (tmp1 == 0);
288   int64_t tmp2 = *arg1 - arg2;
289   OF = (tmp1 != tmp2);
290   trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
291   break;
292 }
293 
294 :(scenario compare_r32_with_r32_lesser)
295 % Reg[EAX].i = 0x0a0b0c07;
296 % Reg[EBX].i = 0x0a0b0c0d;
297 == 0x1
298 # op  ModR/M  SIB   displacement  immediate
299   39  d8                                      # compare EBX with EAX
300 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
301 +run: compare EBX with r/m32
302 +run: r/m32 is EAX
303 +run: SF=1; ZF=0; OF=0
304 
305 :(scenario compare_r32_with_r32_equal)
306 % Reg[EAX].i = 0x0a0b0c0d;
307 % Reg[EBX].i = 0x0a0b0c0d;
308 == 0x1
309 # op  ModR/M  SIB   displacement  immediate
310   39  d8                                      # compare EBX with EAX
311 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
312 +run: compare EBX with r/m32
313 +run: r/m32 is EAX
314 +run: SF=0; ZF=1; OF=0
315 
316 //:: copy (mov)
317 
318 :(before "End Initialize Op Names(name)")
319 put(name, "89", "copy r32 to rm32");
320 
321 :(scenario copy_r32_to_r32)
322 % Reg[EBX].i = 0xaf;
323 == 0x1
324 # op  ModR/M  SIB   displacement  immediate
325   89  d8                                      # copy EBX to EAX
326 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
327 +run: copy EBX to r/m32
328 +run: r/m32 is EAX
329 +run: storing 0x000000af
330 
331 :(before "End Single-Byte Opcodes")
332 case 0x89: {  // copy r32 to r/m32
333   uint8_t modrm = next();
334   uint8_t reg2 = (modrm>>3)&0x7;
335   trace(90, "run") << "copy " << rname(reg2) << " to r/m32" << end();
336   int32_t* arg1 = effective_address(modrm);
337   *arg1 = Reg[reg2].i;
338   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
339   break;
340 }
341 
342 //:: xchg
343 
344 :(before "End Initialize Op Names(name)")
345 put(name, "87", "swap the contents of r32 and rm32");
346 
347 :(scenario xchg_r32_with_r32)
348 % Reg[EBX].i = 0xaf;
349 % Reg[EAX].i = 0x2e;
350 == 0x1
351 # op  ModR/M  SIB   displacement  immediate
352   87  d8                                      # exchange EBX with EAX
353 # ModR/M in binary: 11 (direct mode) 011 (src EBX) 000 (dest EAX)
354 +run: exchange EBX with r/m32
355 +run: r/m32 is EAX
356 +run: storing 0x000000af in r/m32
357 +run: storing 0x0000002e in EBX
358 
359 :(before "End Single-Byte Opcodes")
360 case 0x87: {  // exchange r32 with r/m32
361   uint8_t modrm = next();
362   uint8_t reg2 = (modrm>>3)&0x7;
363   trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
364   int32_t* arg1 = effective_address(modrm);
365   int32_t tmp = *arg1;
366   *arg1 = Reg[reg2].i;
367   Reg[reg2].i = tmp;
368   trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
369   trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
370   break;
371 }
372 
373 //:: increment
374 
375 :(before "End Initialize Op Names(name)")
376 put(name, "40", "increment R0 (EAX)");
377 put(name, "41", "increment R1 (ECX)");
378 put(name, "42", "increment R2 (EDX)");
379 put(name, "43", "increment R3 (EBX)");
380 put(name, "44", "increment R4 (ESP)");
381 put(name, "45", "increment R5 (EBP)");
382 put(name, "46", "increment R6 (ESI)");
383 put(name, "47", "increment R7 (EDI)");
384 
385 :(scenario increment_r32)
386 % Reg[ECX].u = 0x1f;
387 == 0x1  # code segment
388 # op  ModR/M  SIB   displacement  immediate
389   41                                          # increment ECX
390 +run: increment ECX
391 +run: storing value 0x00000020
392 
393 :(before "End Single-Byte Opcodes")
394 case 0x40:
395 case 0x41:
396 case 0x42:
397 case 0x43:
398 case 0x44:
399 case 0x45:
400 case 0x46:
401 case 0x47: {  // increment r32
402   uint8_t reg = op & 0x7;
403   trace(90, "run") << "increment " << rname(reg) << end();
404   ++Reg[reg].u;
405   trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
406   break;
407 }
408 
409 :(before "End Initialize Op Names(name)")
410 put(name, "ff", "inc/dec/jump/push/call rm32 based on subop");
411 
412 :(scenario increment_rm32)
413 % Reg[EAX].u = 0x20;
414 == 0x1  # code segment
415 # op  ModR/M  SIB   displacement  immediate
416   ff  c0                                      # increment EAX
417 # ModR/M in binary: 11 (direct mode) 000 (subop inc) 000 (EAX)
418 +run: increment r/m32
419 +run: r/m32 is EAX
420 +run: storing value 0x00000021
421 
422 :(before "End Single-Byte Opcodes")
423 case 0xff: {
424   uint8_t modrm = next();
425   uint8_t subop = (modrm>>3)&0x7;  // middle 3 'reg opcode' bits
426   switch (subop) {
427     case 0: {  // increment r/m32
428       trace(90, "run") << "increment r/m32" << end();
429       int32_t* arg = effective_address(modrm);
430       ++*arg;
431       trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
432       break;
433     }
434     // End Op ff Subops
435   }
436   break;
437 }
438 
439 //:: decrement
440 
441 :(before "End Initialize Op Names(name)")
442 put(name, "48", "decrement R0 (EAX)");
443 put(name, "49", "decrement R1 (ECX)");
444 put(name, "4a", "decrement R2 (EDX)");
445 put(name, "4b", "decrement R3 (EBX)");
446 put(name, "4c", "decrement R4 (ESP)");
447 put(name, "4d", "decrement R5 (EBP)");
448 put(name, "4e", "decrement R6 (ESI)");
449 put(name, "4f", "decrement R7 (EDI)");
450 
451 :(scenario decrement_r32)
452 % Reg[ECX].u = 0x1f;
453 == 0x1  # code segment
454 # op  ModR/M  SIB   displacement  immediate
455   49                                          # decrement ECX
456 +run: decrement ECX
457 +run: storing value 0x0000001e
458 
459 :(before "End Single-Byte Opcodes")
460 case 0x48:
461 case 0x49:
462 case 0x4a:
463 case 0x4b:
464 case 0x4c:
465 case 0x4d:
466 case 0x4e:
467 case 0x4f: {  // decrement r32
468   uint8_t reg = op & 0x7;
469   trace(90, "run") << "decrement " << rname(reg) << end();
470   --Reg[reg].u;
471   trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
472   break;
473 }
474 
475 :(scenario decrement_rm32)
476 % Reg[EAX].u = 0x20;
477 == 0x1  # code segment
478 # op  ModR/M  SIB   displacement  immediate
479   ff  c8                                      # decrement EAX
480 # ModR/M in binary: 11 (direct mode) 001 (subop inc) 000 (EAX)
481 +run: decrement r/m32
482 +run: r/m32 is EAX
483 +run: storing value 0x0000001f
484 
485 :(before "End Op ff Subops")
486 case 1: {  // decrement r/m32
487   trace(90, "run") << "decrement r/m32" << end();
488   int32_t* arg = effective_address(modrm);
489   --*arg;
490   trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
491   break;
492 }
493 
494 //:: push
495 
496 :(before "End Initialize Op Names(name)")
497 put(name, "50", "push R0 (EAX) to stack");
498 put(name, "51", "push R1 (ECX) to stack");
499 put(name, "52", "push R2 (EDX) to stack");
500 put(name, "53", "push R3 (EBX) to stack");
501 put(name, "54", "push R4 (ESP) to stack");
502 put(name, "55", "push R5 (EBP) to stack");
503 put(name, "56", "push R6 (ESI) to stack");
504 put(name, "57", "push R7 (EDI) to stack");
505 
506 :(scenario push_r32)
507 % Reg[ESP].u = 0x64;
508 % Reg[EBX].i = 0x0000000a;
509 == 0x1
510 # op  ModR/M  SIB   displacement  immediate
511   53                                          # push EBX to stack
512 +run: push EBX
513 +run: decrementing ESP to 0x00000060
514 +run: pushing value 0x0000000a
515 
516 :(before "End Single-Byte Opcodes")
517 case 0x50:
518 case 0x51:
519 case 0x52:
520 case 0x53:
521 case 0x54:
522 case 0x55:
523 case 0x56:
524 case 0x57: {  // push r32 to stack
525   uint8_t reg = op & 0x7;
526   trace(90, "run") << "push " << rname(reg) << end();
527 //?   cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
528   push(Reg[reg].u);
529   break;
530 }
531 
532 //:: pop
533 
534 :(before "End Initialize Op Names(name)")
535 put(name, "58", "pop top of stack to R0 (EAX)");
536 put(name, "59", "pop top of stack to R1 (ECX)");
537 put(name, "5a", "pop top of stack to R2 (EDX)");
538 put(name, "5b", "pop top of stack to R3 (EBX)");
539 put(name, "5c", "pop top of stack to R4 (ESP)");
540 put(name, "5d", "pop top of stack to R5 (EBP)");
541 put(name, "5e", "pop top of stack to R6 (ESI)");
542 put(name, "5f", "pop top of stack to R7 (EDI)");
543 
544 :(scenario pop_r32)
545 % Reg[ESP].u = 0x60;
546 % write_mem_i32(0x60, 0x0000000a);
547 == 0x1  # code segment
548 # op  ModR/M  SIB   displacement  immediate
549   5b                                          # pop stack to EBX
550 == 0x60  # data segment
551 0a 00 00 00  # 0x0a
552 +run: pop into EBX
553 +run: popping value 0x0000000a
554 +run: incrementing ESP to 0x00000064
555 
556 :(before "End Single-Byte Opcodes")
557 case 0x58:
558 case 0x59:
559 case 0x5a:
560 case 0x5b:
561 case 0x5c:
562 case 0x5d:
563 case 0x5e:
564 case 0x5f: {  // pop stack into r32
565   uint8_t reg = op & 0x7;
566   trace(90, "run") << "pop into " << rname(reg) << end();
567 //?   cerr << "pop from " << Reg[ESP].u << '\n';
568   Reg[reg].u = pop();
569 //?   cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
570   break;
571 }
572 :(code)
573 uint32_t pop() {
574   uint32_t result = read_mem_u32(Reg[ESP].u);
575   trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
576   Reg[ESP].u += 4;
577   trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
578   return result;
579 }