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 :(code)
  7 void test_call_disp32() {
  8   Reg[ESP].u = 0x64;
  9   run(
 10       "== 0x1\n"  // code segment
 11       // op     ModR/M  SIB   displacement  immediate
 12       "  e8                                 a0 00 00 00 \n"  // call function offset at 0x000000a0
 13       // next EIP is 6
 14   );
 15   CHECK_TRACE_CONTENTS(
 16       "run: call imm32 0x000000a0\n"
 17       "run: decrementing ESP to 0x00000060\n"
 18       "run: pushing value 0x00000006\n"
 19       "run: jumping to 0x000000a6\n"
 20   );
 21 }
 22 
 23 :(before "End Single-Byte Opcodes")
 24 case 0xe8: {  // call disp32 relative to next EIP
 25   const int32_t offset = next32();
 26   ++Callstack_depth;
 27   trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end();
 28 //?   cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n';
 29   push(EIP);
 30   EIP += offset;
 31   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
 32   break;
 33 }
 34 
 35 //:
 36 
 37 :(code)
 38 void test_call_r32() {
 39   Reg[ESP].u = 0x64;
 40   Reg[EBX].u = 0x000000a0;
 41   run(
 42       "== 0x1\n"  // code segment
 43       // op     ModR/M  SIB   displacement  immediate
 44       "  ff     d3                                      \n"  // call function offset at EBX
 45       // next EIP is 3
 46   );
 47   CHECK_TRACE_CONTENTS(
 48       "run: call to r/m32\n"
 49       "run: r/m32 is EBX\n"
 50       "run: decrementing ESP to 0x00000060\n"
 51       "run: pushing value 0x00000003\n"
 52       "run: jumping to 0x000000a3\n"
 53   );
 54 }
 55 
 56 :(before "End Op ff Subops")
 57 case 2: {  // call function pointer at r/m32
 58   trace(Callstack_depth+1, "run") << "call to r/m32" << end();
 59   const int32_t* offset = effective_address(modrm);
 60   push(EIP);
 61   EIP += *offset;
 62   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
 63   ++Callstack_depth;
 64   break;
 65 }
 66 
 67 :(code)
 68 void test_call_mem_at_r32() {
 69   Reg[ESP].u = 0x64;
 70   Reg[EBX].u = 0x2000;
 71   run(
 72       "== 0x1\n"  // code segment
 73       // op     ModR/M  SIB   displacement  immediate
 74       "  ff     13                                      \n"  // call function offset at *EBX
 75       // next EIP is 3
 76       "== 0x2000\n"  // data segment
 77       "a0 00 00 00\n"  // 0x000000a0
 78   );
 79   CHECK_TRACE_CONTENTS(
 80       "run: call to r/m32\n"
 81       "run: effective address is 0x00002000 (EBX)\n"
 82       "run: decrementing ESP to 0x00000060\n"
 83       "run: pushing value 0x00000003\n"
 84       "run: jumping to 0x000000a3\n"
 85   );
 86 }
 87 
 88 //:: ret
 89 
 90 :(before "End Initialize Op Names")
 91 put_new(Name, "c3", "return from most recent unfinished call (ret)");
 92 
 93 :(code)
 94 void test_ret() {
 95   Reg[ESP].u = 0x2000;
 96   run(
 97       "== 0x1\n"  // code segment
 98       // op     ModR/M  SIB   displacement  immediate
 99       "  c3                                           \n"  // return
100       "== 0x2000\n"  // data segment
101       "10 00 00 00\n"  // 0x00000010
102   );
103   CHECK_TRACE_CONTENTS(
104       "run: return\n"
105       "run: popping value 0x00000010\n"
106       "run: jumping to 0x00000010\n"
107   );
108 }
109 
110 :(before "End Single-Byte Opcodes")
111 case 0xc3: {  // return from a call
112   trace(Callstack_depth+1, "run") << "return" << end();
113   --Callstack_depth;
114   EIP = pop();
115   trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
116   break;
117 }