# The OS/K Team licenses this file to you under the MIT license. # See the LICENSE file in the project root for more information. #------------------------------------------------------------------------------# 1. STACK Stack grows downward. 'ebp' and 'esp' are both used. The lowest stack frame is marked by having 'ebp' = 0. There is a 128-bytes red zone below 'esp'. A function's assembly code looks like this: label: enter N*8 ... ... ... leave ret 'N' is the number of local variables used by the function. 'N' may be omitted if it is zero. The above code is equivalent to (and can be substitued by) the following: label: push ebp mov ebp, esp sub esp, N*8 ... ... ... mov esp, ebp pop ebp ret Between the 'enter' and the 'leave', the stack looks like this: . . . . . (caller's) . |---------------| | saved eip | ebp+8 esp+(N*8)+16 | saved ebp | ebp esp+(N*8)+8 | . | ebp-8 esp+(N*8) \ | . | ebp-16 esp+(N-1)*8 | | . | ... | <- local | . | ebp-(N-1)*8 esp+8 | variables | . | ebp-(N*8) esp | |---------------| / . (red zone) . . . . . 'enter' and 'leave' can be omitted if ALL the following hold: the function - uses no local variables (on the stack) - never uses any function that changes 'ebp' nor 'esp', aside from 'call' and 'ret' You can never omit 'enter' without omitting 'leave', and vice-versa. #------------------------------------------------------------------------------# 2. CALLING CONVENTION No matter whether 'enter' and 'leave' were ommited or not, the following must hold true whenever a function is entered or exited from: - 'ebp' must be 8-bytes aligned - 'esp' must be 8-bytes aligned - the DF flag must be cleared Aside from the DF flag, a function cannot assume anything about the state of the flags in the FLG register. Passing parameters is done using the following registers, in that order: ax0, ax1, ax2, ax3, ax4, ax5 Further parameters are passed on the stack in reverse order. The caller then cleans the stack. Return values are passed in 'rax'. If the return value does not fit and require more registers, use the following registers, in that order: eax, edx The following registers are volatile; the caller cannot assume that they will be left unmodified by the callee: eax, ebx, ecx, edx, esi, edi, ax0, ax1, ax2, ax3, ax4, ax5 The following registers are nonvolatile; the callee must preserve them: nx0, nx1, nx2, nx3, nx4, nx5, nx6, nx7, nx8 #------------------------------------------------------------------------------# 3. VARIADIC FUNCTIONS To call a variadic function, do this: sub esp, nargs * 8 mov [esp], arg0 ... ... ... mov [esp+(N*8)], argN call variadic_func add esp, nargs * 8 To the variadic function, argN can be accessed the following way: mov reg, [ebp+N*8+16] For instance: mov eax, [ebp+16] ; arg0 mov edx, [ebp+24] ; arg1 It is recommended to use the reg+reg*imm16+imm16 memory format: mov eax, [ebp+ecx*8+16] ; accesses arg#ecx The 'va_list' type can be regarded as a pointer to the variadic function's ebp+16 #------------------------------------------------------------------------------# 4. SPECIAL REGISTERS (TO BE COMPLETED) #------------------------------------------------------------------------------#