; kcore/syscall.fn.x86.lin.64.s
; ~ lexi hale <lexi@hale.su>
;
; this function performs a syscall and stores its
; results in the variables provided. this makes it
; possible to bypass the hideous errno mechanism
; altogether and access the error value of a
; syscall directly. invoke as:
;
; 	void k_platform_syscall_raw(s64* result, u64* errno,
;		syscall, u8 valency, s64[] args)

bits 64
%include "../arch/x86.lin.64.s"
%include "../arch/x86.cdecl.64.s"
; vim: ft=nasm

%macro handle_arg 1
	%assign v %1+1
	mov sys.reg. %+ v, [ccall.reg.4 + 8 * %1]
	dec ccall.reg.3
	jz .perform_call
%endmacro

global k_platform_syscall_raw:function
k_platform_syscall_raw:
	; locals: rbx = s64* result
	;         r12 = u64* errno
	; arg 0 = s64* result
	; arg 1 = errno ptr
	; arg 2 = syscall num
	; arg 3 = valency
	; arg 4 = args ptr

	; store the locals in registers that
	; are guaranteed not to be clobbered,
	; saving us some cycles pushing to
	; and popping back from the stack
	mov rbx, ccall.reg.0
	mov r12, ccall.reg.1

	; this needs to go before the loop
	; or it'll get clobbered
	mov sys.reg.0, ccall.reg.2

	; automatically generate the code
	; needed to move the arguments into
	; their correct registers. see above
	%assign i 0
	%rep 6
		handle_arg i
		%assign i i+1
	%endrep

	; valency >= 7. this is not valid, so
	; we set our return value to -1 and the
	; error number to its maximum value in
	; order to indicate that the syscall
	; was invalid
	mov qword [rbx], -1
	mov qword [r12], -1
	ret

	; we have a valency match - perform the
	; requested syscall already store in rax
	.perform_call: sys.call

	; check for an error - on x86, error is
	; returned as a negative value in %rax
	test sys.reg.ret, sys.reg.ret
	js .error ; jump if sign flag set
	
	; move our return values into place and
	; return to the caller (which should
	; always be k_platform_syscall, btw)
	mov       [rbx], sys.reg.ret
	mov qword [r12], 0 ; no error
	ret

	; an error was returned - we need to set
	; the errno to its positive equivalent,
	; and store -1 in the return variable
	.error: neg sys.reg.ret
	        mov qword [rbx], -1
			mov       [r12], sys.reg.ret
			ret
