; vim: ft=asm

%macro define_syscall 2
    global j6_%1
    j6_%1:
        push rbp
        mov rbp, rsp

        ; if the syscall has more than 6 arguments, the rest
        ; will be pushed on the stack. in that case, we'd need
        ; to pass this stack pointer to the kernel, so stash
        ; off rbx (callee-saved) and pass the pointer to the
        ; arguments there.
        push rbx
        mov rbx, rbp
        add rbx, 16 ; account for stack frame

        ; args should already be in rdi, etc, but rcx will
        ; get stomped, so stash it in r10, which isn't a
        ; callee-saved register, but also isn't used in the
        ; function call ABI.
        mov r10, rcx

        mov rax, %2
        syscall
        ; result is now already in rax, so just return

        pop rbx
        pop rbp
        ret
%endmacro

; [[[cog code generation
;   from definitions.context import Context
;
;   ctx = Context(definitions_path)
;   ctx.parse("syscalls.def")
;   syscalls = ctx.interfaces['syscalls']
;
;   if target != "kernel":
;       for id, scope, method in syscalls.methods:
;           if scope:
;               name = f"{scope.name}_{method.name}"
;           else:
;               name = method.name
;           cog.outl(f"define_syscall {name:20}, {id}")
; ]]]
; [[[end]]]
