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