1
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
11 01 d8
12
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: {
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
29
30
31 int32_t* effective_address(uint8_t modrm) {
32 const uint8_t mod = (modrm>>6);
33
34 const uint8_t rm = modrm & 0x7;
35 if (mod == 3) {
36
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
46 const uint8_t rm = modrm & 0x7;
47 uint32_t addr = 0;
48 switch (mod) {
49 case 3:
50
51 raise << "unexpected direct addressing mode\n" << end();
52 return 0;
53
54 default:
55 cerr << "unrecognized mod bits: " << NUM(mod) << '\n';
56 exit(1);
57 }
58
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
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
86 29 d8
87
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: {
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
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
112 f7 e1
113
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;
125 switch (subop) {
126 case 4: {
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
136 default:
137 cerr << "unrecognized sub-opcode after 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
153 0f af d8
154
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: {
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
170
171 :(before "End Initialize Op Names")
172 put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
173
174 :(scenario and_r32_with_r32)
175 % Reg[EAX].i = 0x0a0b0c0d;
176 % Reg[EBX].i = 0x000000ff;
177 == 0x1
178
179 21 d8
180
181 +run: and EBX with r/m32
182 +run: r/m32 is EAX
183 +run: storing 0x0000000d
184
185 :(before "End Single-Byte Opcodes")
186 case 0x21: {
187 const uint8_t modrm = next();
188 const uint8_t arg2 = (modrm>>3)&0x7;
189 trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end();
190 int32_t* arg1 = effective_address(modrm);
191 BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
192 break;
193 }
194
195
196
197 :(before "End Initialize Op Names")
198 put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
199
200 :(scenario or_r32_with_r32)
201 % Reg[EAX].i = 0x0a0b0c0d;
202 % Reg[EBX].i = 0xa0b0c0d0;
203 == 0x1
204
205 09 d8
206
207 +run: or EBX with r/m32
208 +run: r/m32 is EAX
209 +run: storing 0xaabbccdd
210
211 :(before "End Single-Byte Opcodes")
212 case 0x09: {
213 const uint8_t modrm = next();
214 const uint8_t arg2 = (modrm>>3)&0x7;
215 trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end();
216 int32_t* arg1 = effective_address(modrm);
217 BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
218 break;
219 }
220
221
222
223 :(before "End Initialize Op Names")
224 put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
225
226 :(scenario xor_r32_with_r32)
227 % Reg[EAX].i = 0x0a0b0c0d;
228 % Reg[EBX].i = 0xaabbc0d0;
229 == 0x1
230
231 31 d8
232
233 +run: xor EBX with r/m32
234 +run: r/m32 is EAX
235 +run: storing 0xa0b0ccdd
236
237 :(before "End Single-Byte Opcodes")
238 case 0x31: {
239 const uint8_t modrm = next();
240 const uint8_t arg2 = (modrm>>3)&0x7;
241 trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end();
242 int32_t* arg1 = effective_address(modrm);
243 BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
244 break;
245 }
246
247
248
249 :(scenario not_r32)
250 % Reg[EBX].i = 0x0f0f00ff;
251 == 0x1
252
253 f7 d3
254
255 +run: operate on r/m32
256 +run: r/m32 is EBX
257 +run: subop: not
258 +run: storing 0xf0f0ff00
259
260 :(before "End Op f7 Subops")
261 case 2: {
262 trace(90, "run") << "subop: not" << end();
263 *arg1 = ~(*arg1);
264 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
265 SF = (*arg1 >> 31);
266 ZF = (*arg1 == 0);
267 OF = false;
268 break;
269 }
270
271
272
273 :(before "End Initialize Op Names")
274 put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
275
276 :(scenario compare_r32_with_r32_greater)
277 % Reg[EAX].i = 0x0a0b0c0d;
278 % Reg[EBX].i = 0x0a0b0c07;
279 == 0x1
280
281 39 d8
282
283 +run: compare EBX with r/m32
284 +run: r/m32 is EAX
285 +run: SF=0; ZF=0; OF=0
286
287 :(before "End Single-Byte Opcodes")
288 case 0x39: {
289 const uint8_t modrm = next();
290 const uint8_t reg2 = (modrm>>3)&0x7;
291 trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
292 const int32_t* arg1 = effective_address(modrm);
293 const int32_t arg2 = Reg[reg2].i;
294 const int32_t tmp1 = *arg1 - arg2;
295 SF = (tmp1 < 0);
296 ZF = (tmp1 == 0);
297 const int64_t tmp2 = *arg1 - arg2;
298 OF = (tmp1 != tmp2);
299 trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
300 break;
301 }
302
303 :(scenario compare_r32_with_r32_lesser)
304 % Reg[EAX].i = 0x0a0b0c07;
305 % Reg[EBX].i = 0x0a0b0c0d;
306 == 0x1
307
308 39 d8
309
310 +run: compare EBX with r/m32
311 +run: r/m32 is EAX
312 +run: SF=1; ZF=0; OF=0
313
314 :(scenario compare_r32_with_r32_equal)
315 % Reg[EAX].i = 0x0a0b0c0d;
316 % Reg[EBX].i = 0x0a0b0c0d;
317 == 0x1
318
319 39 d8
320
321 +run: compare EBX with r/m32
322 +run: r/m32 is EAX
323 +run: SF=0; ZF=1; OF=0
324
325
326
327 :(before "End Initialize Op Names")
328 put_new(Name, "89", "copy r32 to rm32 (mov)");
329
330 :(scenario copy_r32_to_r32)
331 % Reg[EBX].i = 0xaf;
332 == 0x1
333
334 89 d8
335
336 +run: copy EBX to r/m32
337 +run: r/m32 is EAX
338 +run: storing 0x000000af
339
340 :(before "End Single-Byte Opcodes")
341 case 0x89: {
342 const uint8_t modrm = next();
343 const uint8_t rsrc = (modrm>>3)&0x7;
344 trace(90, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
345 int32_t* dest = effective_address(modrm);
346 *dest = Reg[rsrc].i;
347 trace(90, "run") << "storing 0x" << HEXWORD << *dest << end();
348 break;
349 }
350
351
352
353 :(before "End Initialize Op Names")
354 put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
355
356 :(scenario xchg_r32_with_r32)
357 % Reg[EBX].i = 0xaf;
358 % Reg[EAX].i = 0x2e;
359 == 0x1
360
361 87 d8
362
363 +run: exchange EBX with r/m32
364 +run: r/m32 is EAX
365 +run: storing 0x000000af in r/m32
366 +run: storing 0x0000002e in EBX
367
368 :(before "End Single-Byte Opcodes")
369 case 0x87: {
370 const uint8_t modrm = next();
371 const uint8_t reg2 = (modrm>>3)&0x7;
372 trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
373 int32_t* arg1 = effective_address(modrm);
374 const int32_t tmp = *arg1;
375 *arg1 = Reg[reg2].i;
376 Reg[reg2].i = tmp;
377 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
378 trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
379 break;
380 }
381
382
383
384 :(before "End Initialize Op Names")
385 put_new(Name, "40", "increment EAX (inc)");
386 put_new(Name, "41", "increment ECX (inc)");
387 put_new(Name, "42", "increment EDX (inc)");
388 put_new(Name, "43", "increment EBX (inc)");
389 put_new(Name, "44", "increment ESP (inc)");
390 put_new(Name, "45", "increment EBP (inc)");
391 put_new(Name, "46", "increment ESI (inc)");
392 put_new(Name, "47", "increment EDI (inc)");
393
394 :(scenario increment_r32)
395 % Reg[ECX].u = 0x1f;
396 == 0x1
397
398 41
399 +run: increment ECX
400 +run: storing value 0x00000020
401
402 :(before "End Single-Byte Opcodes")
403 case 0x40:
404 case 0x41:
405 case 0x42:
406 case 0x43:
407 case 0x44:
408 case 0x45:
409 case 0x46:
410 case 0x47: {
411 const uint8_t reg = op & 0x7;
412 trace(90, "run") << "increment " << rname(reg) << end();
413 ++Reg[reg].u;
414 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
415 break;
416 }
417
418 :(before "End Initialize Op Names")
419 put_new(Name, "ff", "increment/decrement/jump/push/call rm32 based on subop (inc/dec/jmp/push/call)");
420
421 :(scenario increment_rm32)
422 % Reg[EAX].u = 0x20;
423 == 0x1
424
425 ff c0
426
427 +run: increment r/m32
428 +run: r/m32 is EAX
429 +run: storing value 0x00000021
430
431 :(before "End Single-Byte Opcodes")
432 case 0xff: {
433 const uint8_t modrm = next();
434 const uint8_t subop = (modrm>>3)&0x7;
435 switch (subop) {
436 case 0: {
437 trace(90, "run") << "increment r/m32" << end();
438 int32_t* arg = effective_address(modrm);
439 ++*arg;
440 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
441 break;
442 }
443
444 }
445 break;
446 }
447
448
449
450 :(before "End Initialize Op Names")
451 put_new(Name, "48", "decrement EAX (dec)");
452 put_new(Name, "49", "decrement ECX (dec)");
453 put_new(Name, "4a", "decrement EDX (dec)");
454 put_new(Name, "4b", "decrement EBX (dec)");
455 put_new(Name, "4c", "decrement ESP (dec)");
456 put_new(Name, "4d", "decrement EBP (dec)");
457 put_new(Name, "4e", "decrement ESI (dec)");
458 put_new(Name, "4f", "decrement EDI (dec)");
459
460 :(scenario decrement_r32)
461 % Reg[ECX].u = 0x1f;
462 == 0x1
463
464 49
465 +run: decrement ECX
466 +run: storing value 0x0000001e
467
468 :(before "End Single-Byte Opcodes")
469 case 0x48:
470 case 0x49:
471 case 0x4a:
472 case 0x4b:
473 case 0x4c:
474 case 0x4d:
475 case 0x4e:
476 case 0x4f: {
477 const uint8_t reg = op & 0x7;
478 trace(90, "run") << "decrement " << rname(reg) << end();
479 --Reg[reg].u;
480 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
481 break;
482 }
483
484 :(scenario decrement_rm32)
485 % Reg[EAX].u = 0x20;
486 == 0x1
487
488 ff c8
489
490 +run: decrement r/m32
491 +run: r/m32 is EAX
492 +run: storing value 0x0000001f
493
494 :(before "End Op ff Subops")
495 case 1: {
496 trace(90, "run") << "decrement r/m32" << end();
497 int32_t* arg = effective_address(modrm);
498 --*arg;
499 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
500 break;
501 }
502
503
504
505 :(before "End Initialize Op Names")
506 put_new(Name, "50", "push EAX to stack (push)");
507 put_new(Name, "51", "push ECX to stack (push)");
508 put_new(Name, "52", "push EDX to stack (push)");
509 put_new(Name, "53", "push EBX to stack (push)");
510 put_new(Name, "54", "push ESP to stack (push)");
511 put_new(Name, "55", "push EBP to stack (push)");
512 put_new(Name, "56", "push ESI to stack (push)");
513 put_new(Name, "57", "push EDI to stack (push)");
514
515 :(scenario push_r32)
516 % Reg[ESP].u = 0x64;
517 % Reg[EBX].i = 0x0000000a;
518 == 0x1
519
520 53
521 +run: push EBX
522 +run: decrementing ESP to 0x00000060
523 +run: pushing value 0x0000000a
524
525 :(before "End Single-Byte Opcodes")
526 case 0x50:
527 case 0x51:
528 case 0x52:
529 case 0x53:
530 case 0x54:
531 case 0x55:
532 case 0x56:
533 case 0x57: {
534 uint8_t reg = op & 0x7;
535 trace(90, "run") << "push " << rname(reg) << end();
536
537 push(Reg[reg].u);
538 break;
539 }
540
541
542
543 :(before "End Initialize Op Names")
544 put_new(Name, "58", "pop top of stack to EAX (pop)");
545 put_new(Name, "59", "pop top of stack to ECX (pop)");
546 put_new(Name, "5a", "pop top of stack to EDX (pop)");
547 put_new(Name, "5b", "pop top of stack to EBX (pop)");
548 put_new(Name, "5c", "pop top of stack to ESP (pop)");
549 put_new(Name, "5d", "pop top of stack to EBP (pop)");
550 put_new(Name, "5e", "pop top of stack to ESI (pop)");
551 put_new(Name, "5f", "pop top of stack to EDI (pop)");
552
553 :(scenario pop_r32)
554 % Reg[ESP].u = 0x2000;
555 % Mem.push_back(vma(0x2000)); // manually allocate memory
556 % write_mem_i32(0x2000, 0x0000000a); // ..before this write
557 == 0x1
558
559 5b
560 == 0x2000
561 0a 00 00 00
562 +run: pop into EBX
563 +run: popping value 0x0000000a
564 +run: incrementing ESP to 0x00002004
565
566 :(before "End Single-Byte Opcodes")
567 case 0x58:
568 case 0x59:
569 case 0x5a:
570 case 0x5b:
571 case 0x5c:
572 case 0x5d:
573 case 0x5e:
574 case 0x5f: {
575 const uint8_t reg = op & 0x7;
576 trace(90, "run") << "pop into " << rname(reg) << end();
577
578 Reg[reg].u = pop();
579
580 break;
581 }
582 :(code)
583 uint32_t pop() {
584 const uint32_t result = read_mem_u32(Reg[ESP].u);
585 trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
586 Reg[ESP].u += 4;
587 trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
588 return result;
589 }