A program is more than one function; today you let one function call another. The call instruction sets up a fresh frame for the callee, runs it, and brings its results back.
Execute call, passing arguments from the stack into a new frame and returning the callee's results.
So far a function has run in isolation. call (0x10) lets one function invoke another by index: it names a funcidx, pops that function’s parameters off the caller’s stack, builds a new frame for the callee with those arguments as its first locals, runs the callee’s body, and pushes whatever the callee returns back onto the caller’s stack. The argument order matches the earlier parameter rule - the values come off the stack so that the deepest becomes parameter 0 - so f pushing 2 then 3 and calling g(a, b) = a + b returns 5.
The important design point is that each call gets its own frame: its own locals, independent of the caller’s. Your Invoke from chapter three already ran one function on a fresh frame; call generalizes that to happen mid-execution, from inside another function’s body. This is the seed of the call stack - frames nested inside frames - which the next lesson leans on to make recursion work without a single new opcode. Keep the caller’s stack and the callee’s frame cleanly separate, and calls compose to any depth.
// call (0x10) reads a funcidx. Pop the callee's params off the caller's// stack (in order), build a frame, run the body, push the results back.case 0x10: // callidx := readVarU32(body, &pc)ft := m.Types[m.FuncType[idx]]args := stack.PopN(len(ft.Params)) // deepest -> param 0results := m.call(idx, args) // a new frame runs the calleestack.PushAll(results)