https://github.com/akkartik/mu/blob/master/subx/019functions.cc
 1 //:: call
 2 
 3 :(before "End Initialize Op Names")
 4 put_new(Name, "e8", "call disp32 (call)");
 5 
 6 :(scenario call_disp32)
 7 % Reg[ESP].u = 0x64;
 8 == 0x1
 9 # op  ModR/M  SIB   displacement  immediate
10   e8                              a0 00 00 00  # call function offset at 0x000000a0
11   # next EIP is 6
12 +run: call imm32 0x000000a0
13 +run: decrementing ESP to 0x00000060
14 +run: pushing value 0x00000006
15 +run: jumping to 0x000000a6
16 
17 :(before "End Single-Byte Opcodes")
18 case 0xe8: {  // call disp32 relative to next EIP
19   const int32_t offset = next32();
20   ++Callstack_depth;
21   trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end();
22 //?   cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n';
23   push(EIP);
24   EIP += offset;
25   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
26   break;
27 }
28 
29 //:
30 
31 :(scenario call_r32)
32 % Reg[ESP].u = 0x64;
33 % Reg[EBX].u = 0x000000a0;
34 == 0x1
35 # op  ModR/M  SIB   displacement  immediate
36   ff  d3                                       # call function offset at EBX
37   # next EIP is 3
38 +run: call to r/m32
39 +run: r/m32 is EBX
40 +run: decrementing ESP to 0x00000060
41 +run: pushing value 0x00000003
42 +run: jumping to 0x000000a3
43 
44 :(before "End Op ff Subops")
45 case 2: {  // call function pointer at r/m32
46   trace(Callstack_depth+1, "run") << "call to r/m32" << end();
47   const int32_t* offset = effective_address(modrm);
48   push(EIP);
49   EIP += *offset;
50   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
51   ++Callstack_depth;
52   break;
53 }
54 
55 :(scenario call_mem_at_r32)
56 % Reg[ESP].u = 0x64;
57 % Reg[EBX].u = 0x2000;
58 == 0x1  # code segment
59 # op  ModR/M  SIB   displacement  immediate
60   ff  13                                       # call function offset at *EBX
61   # next EIP is 3
62 == 0x2000  # data segment
63 a0 00 00 00  # 0xa0
64 +run: call to r/m32
65 +run: effective address is 0x00002000 (EBX)
66 +run: decrementing ESP to 0x00000060
67 +run: pushing value 0x00000003
68 +run: jumping to 0x000000a3
69 
70 //:: ret
71 
72 :(before "End Initialize Op Names")
73 put_new(Name, "c3", "return from most recent unfinished call (ret)");
74 
75 :(scenario ret)
76 % Reg[ESP].u = 0x2000;
77 == 0x1  # code segment
78 # op  ModR/M  SIB   displacement  immediate
79   c3
80 == 0x2000  # data segment
81 10 00 00 00  # 0x10
82 +run: return
83 +run: popping value 0x00000010
84 +run: jumping to 0x00000010
85 
86 :(before "End Single-Byte Opcodes")
87 case 0xc3: {  // return from a call
88   trace(Callstack_depth+1, "run") << "return" << end();
89   --Callstack_depth;
90   EIP = pop();
91   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
92   break;
93 }