After entering a function with 4 arguments: xxxx <- SP, FP ret_ip old_fp arg4 arg3 arg2 arg1 fsymref ... xxxx <- stack address 0 Register fp always points to the first address in the stack that the function code can freely use as eval stack. Rationale: fp is also checked for stack underruns in debug mode and those checks are faster this way Call: - compiled call code: - push function symbol reference fsymref - push arguments in order - (argc, the number of arguments at the caller is an instruction argument for CALL) - VM code for the CALL instruction: - consider argc, peek at fsymref for the function - if caller pushed less args than the function takes (argc < func.args), push NILs until there are enough arguments on the stack - push FP - push the next IP - set FP to the same value as SP - change IP to the function entry IP Ret: - in the RET instruction: - pop the result, should be the top of the stack ("void" function pushes FAWK_NIL) - pop the next IP - pop and restore FP - pop and cell_free() as many args as the func has (by declaration; parameter of ret) plus 1, for fsymref Stack before the call: xxxx <- SP, FP arg4 arg3 arg2 arg1 fsymref ... xxxx <- stack address 0