1
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
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 uint8_t mod = (modrm>>6);
33
34 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 uint8_t mod = (modrm>>6);
45
46 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(name)")
79 put(name, "29", "subtract r32 from rm32");
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 uint8_t modrm = next();
95 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(name)")
105 put(name, "f7", "test/negate/mul/div rm32 (with EAX if necessary) depending on subop");
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 uint8_t modrm = next();
122 trace(90, "run") << "operate on r/m32" << end();
123 int32_t* arg1 = effective_address(modrm);
124 uint8_t subop = (modrm>>3)&0x7;
125 switch (subop) {
126 case 4: {
127 trace(90, "run") << "subop: multiply EAX by r/m32" << end();
128 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(name)")
146 put(name_0f, "af", "multiply rm32 into r32");
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 uint8_t modrm = next();
162 uint8_t arg2 = (modrm>>3)&0x7;
163 trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end();
164 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(name)")
172 put(name, "21", "rm32 = bitwise AND of r32 with rm32");
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 uint8_t modrm = next();
188 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(name)")
198 put(name, "09", "rm32 = bitwise OR of r32 with rm32");
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 uint8_t modrm = next();
214 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(name)")
224 put(name, "31", "rm32 = bitwise XOR of r32 with rm32");
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 uint8_t modrm = next();
240 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 :(before "End Initialize Op Names(name)")
250 put(name, "f7", "bitwise complement of rm32");
251
252 :(scenario not_r32)
253 % Reg[EBX].i = 0x0f0f00ff;
254 == 0x1
255
256 f7 d3
257
258 +run: operate on r/m32
259 +run: r/m32 is EBX
260 +run: subop: not
261 +run: storing 0xf0f0ff00
262
263 :(before "End Op f7 Subops")
264 case 2: {
265 trace(90, "run") << "subop: not" << end();
266 *arg1 = ~(*arg1);
267 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
268 SF = (*arg1 >> 31);
269 ZF = (*arg1 == 0);
270 OF = false;
271 break;
272 }
273
274
275
276 :(before "End Initialize Op Names(name)")
277 put(name, "39", "compare: set SF if rm32 < r32");
278
279 :(scenario compare_r32_with_r32_greater)
280 % Reg[EAX].i = 0x0a0b0c0d;
281 % Reg[EBX].i = 0x0a0b0c07;
282 == 0x1
283
284 39 d8
285
286 +run: compare EBX with r/m32
287 +run: r/m32 is EAX
288 +run: SF=0; ZF=0; OF=0
289
290 :(before "End Single-Byte Opcodes")
291 case 0x39: {
292 uint8_t modrm = next();
293 uint8_t reg2 = (modrm>>3)&0x7;
294 trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
295 int32_t* arg1 = effective_address(modrm);
296 int32_t arg2 = Reg[reg2].i;
297 int32_t tmp1 = *arg1 - arg2;
298 SF = (tmp1 < 0);
299 ZF = (tmp1 == 0);
300 int64_t tmp2 = *arg1 - arg2;
301 OF = (tmp1 != tmp2);
302 trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
303 break;
304 }
305
306 :(scenario compare_r32_with_r32_lesser)
307 % Reg[EAX].i = 0x0a0b0c07;
308 % Reg[EBX].i = 0x0a0b0c0d;
309 == 0x1
310
311 39 d8
312
313 +run: compare EBX with r/m32
314 +run: r/m32 is EAX
315 +run: SF=1; ZF=0; OF=0
316
317 :(scenario compare_r32_with_r32_equal)
318 % Reg[EAX].i = 0x0a0b0c0d;
319 % Reg[EBX].i = 0x0a0b0c0d;
320 == 0x1
321
322 39 d8
323
324 +run: compare EBX with r/m32
325 +run: r/m32 is EAX
326 +run: SF=0; ZF=1; OF=0
327
328
329
330 :(before "End Initialize Op Names(name)")
331 put(name, "89", "copy r32 to rm32");
332
333 :(scenario copy_r32_to_r32)
334 % Reg[EBX].i = 0xaf;
335 == 0x1
336
337 89 d8
338
339 +run: copy EBX to r/m32
340 +run: r/m32 is EAX
341 +run: storing 0x000000af
342
343 :(before "End Single-Byte Opcodes")
344 case 0x89: {
345 uint8_t modrm = next();
346 uint8_t reg2 = (modrm>>3)&0x7;
347 trace(90, "run") << "copy " << rname(reg2) << " to r/m32" << end();
348 int32_t* arg1 = effective_address(modrm);
349 *arg1 = Reg[reg2].i;
350 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
351 break;
352 }
353
354
355
356 :(before "End Initialize Op Names(name)")
357 put(name, "87", "swap the contents of r32 and rm32");
358
359 :(scenario xchg_r32_with_r32)
360 % Reg[EBX].i = 0xaf;
361 % Reg[EAX].i = 0x2e;
362 == 0x1
363
364 87 d8
365
366 +run: exchange EBX with r/m32
367 +run: r/m32 is EAX
368 +run: storing 0x000000af in r/m32
369 +run: storing 0x0000002e in EBX
370
371 :(before "End Single-Byte Opcodes")
372 case 0x87: {
373 uint8_t modrm = next();
374 uint8_t reg2 = (modrm>>3)&0x7;
375 trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
376 int32_t* arg1 = effective_address(modrm);
377 int32_t tmp = *arg1;
378 *arg1 = Reg[reg2].i;
379 Reg[reg2].i = tmp;
380 trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
381 trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
382 break;
383 }
384
385
386
387 :(before "End Initialize Op Names(name)")
388 put(name, "40", "increment R0 (EAX)");
389 put(name, "41", "increment R1 (ECX)");
390 put(name, "42", "increment R2 (EDX)");
391 put(name, "43", "increment R3 (EBX)");
392 put(name, "44", "increment R4 (ESP)");
393 put(name, "45", "increment R5 (EBP)");
394 put(name, "46", "increment R6 (ESI)");
395 put(name, "47", "increment R7 (EDI)");
396
397 :(scenario increment_r32)
398 % Reg[ECX].u = 0x1f;
399 == 0x1
400
401 41
402 +run: increment ECX
403 +run: storing value 0x00000020
404
405 :(before "End Single-Byte Opcodes")
406 case 0x40:
407 case 0x41:
408 case 0x42:
409 case 0x43:
410 case 0x44:
411 case 0x45:
412 case 0x46:
413 case 0x47: {
414 uint8_t reg = op & 0x7;
415 trace(90, "run") << "increment " << rname(reg) << end();
416 ++Reg[reg].u;
417 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
418 break;
419 }
420
421 :(before "End Initialize Op Names(name)")
422 put(name, "ff", "inc/dec/jump/push/call rm32 based on subop");
423
424 :(scenario increment_rm32)
425 % Reg[EAX].u = 0x20;
426 == 0x1
427
428 ff c0
429
430 +run: increment r/m32
431 +run: r/m32 is EAX
432 +run: storing value 0x00000021
433
434 :(before "End Single-Byte Opcodes")
435 case 0xff: {
436 uint8_t modrm = next();
437 uint8_t subop = (modrm>>3)&0x7;
438 switch (subop) {
439 case 0: {
440 trace(90, "run") << "increment r/m32" << end();
441 int32_t* arg = effective_address(modrm);
442 ++*arg;
443 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
444 break;
445 }
446
447 }
448 break;
449 }
450
451
452
453 :(before "End Initialize Op Names(name)")
454 put(name, "48", "decrement R0 (EAX)");
455 put(name, "49", "decrement R1 (ECX)");
456 put(name, "4a", "decrement R2 (EDX)");
457 put(name, "4b", "decrement R3 (EBX)");
458 put(name, "4c", "decrement R4 (ESP)");
459 put(name, "4d", "decrement R5 (EBP)");
460 put(name, "4e", "decrement R6 (ESI)");
461 put(name, "4f", "decrement R7 (EDI)");
462
463 :(scenario decrement_r32)
464 % Reg[ECX].u = 0x1f;
465 == 0x1
466
467 49
468 +run: decrement ECX
469 +run: storing value 0x0000001e
470
471 :(before "End Single-Byte Opcodes")
472 case 0x48:
473 case 0x49:
474 case 0x4a:
475 case 0x4b:
476 case 0x4c:
477 case 0x4d:
478 case 0x4e:
479 case 0x4f: {
480 uint8_t reg = op & 0x7;
481 trace(90, "run") << "decrement " << rname(reg) << end();
482 --Reg[reg].u;
483 trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
484 break;
485 }
486
487 :(scenario decrement_rm32)
488 % Reg[EAX].u = 0x20;
489 == 0x1
490
491 ff c8
492
493 +run: decrement r/m32
494 +run: r/m32 is EAX
495 +run: storing value 0x0000001f
496
497 :(before "End Op ff Subops")
498 case 1: {
499 trace(90, "run") << "decrement r/m32" << end();
500 int32_t* arg = effective_address(modrm);
501 --*arg;
502 trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
503 break;
504 }
505
506
507
508 :(before "End Initialize Op Names(name)")
509 put(name, "50", "push R0 (EAX) to stack");
510 put(name, "51", "push R1 (ECX) to stack");
511 put(name, "52", "push R2 (EDX) to stack");
512 put(name, "53", "push R3 (EBX) to stack");
513 put(name, "54", "push R4 (ESP) to stack");
514 put(name, "55", "push R5 (EBP) to stack");
515 put(name, "56", "push R6 (ESI) to stack");
516 put(name, "57", "push R7 (EDI) to stack");
517
518 :(scenario push_r32)
519 % Reg[ESP].u = 0x64;
520 % Reg[EBX].i = 0x0000000a;
521 == 0x1
522
523 53
524 +run: push EBX
525 +run: decrementing ESP to 0x00000060
526 +run: pushing value 0x0000000a
527
528 :(before "End Single-Byte Opcodes")
529 case 0x50:
530 case 0x51:
531 case 0x52:
532 case 0x53:
533 case 0x54:
534 case 0x55:
535 case 0x56:
536 case 0x57: {
537 uint8_t reg = op & 0x7;
538 trace(90, "run") << "push " << rname(reg) << end();
539
540 push(Reg[reg].u);
541 break;
542 }
543
544
545
546 :(before "End Initialize Op Names(name)")
547 put(name, "58", "pop top of stack to R0 (EAX)");
548 put(name, "59", "pop top of stack to R1 (ECX)");
549 put(name, "5a", "pop top of stack to R2 (EDX)");
550 put(name, "5b", "pop top of stack to R3 (EBX)");
551 put(name, "5c", "pop top of stack to R4 (ESP)");
552 put(name, "5d", "pop top of stack to R5 (EBP)");
553 put(name, "5e", "pop top of stack to R6 (ESI)");
554 put(name, "5f", "pop top of stack to R7 (EDI)");
555
556 :(scenario pop_r32)
557 % Reg[ESP].u = 0x2000;
558 % Mem.push_back(vma(0x2000)); // manually allocate memory
559 % write_mem_i32(0x2000, 0x0000000a); // ..before this write
560 == 0x1
561
562 5b
563 == 0x2000
564 0a 00 00 00
565 +run: pop into EBX
566 +run: popping value 0x0000000a
567 +run: incrementing ESP to 0x00002004
568
569 :(before "End Single-Byte Opcodes")
570 case 0x58:
571 case 0x59:
572 case 0x5a:
573 case 0x5b:
574 case 0x5c:
575 case 0x5d:
576 case 0x5e:
577 case 0x5f: {
578 uint8_t reg = op & 0x7;
579 trace(90, "run") << "pop into " << rname(reg) << end();
580
581 Reg[reg].u = pop();
582
583 break;
584 }
585 :(code)
586 uint32_t pop() {
587 uint32_t result = read_mem_u32(Reg[ESP].u);
588 trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
589 Reg[ESP].u += 4;
590 trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
591 return result;
592 }