; kcore/syscall.fn.x86.lin.32.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(s32* result, u32* errno,
;		syscall, u8 valency, s32[] args)

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


%define result_ptr edi
%define errno_ptr ebx
%define valency ecx
%define args esi
%define opcode eax

%macro handle_arg 1
	push dword [args]
	sub args, 4
	dec valency 
	jz .perform_call
%endmacro

global k_platform_syscall_raw:function
k_platform_syscall_raw:
	; locals: rbx = s32* result
	;         r12 = u32* errno
	; arg 0 = s32* 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
	pop result_ptr 
	pop errno_ptr
	pop edx ; opcode

	; set up registers for the loop
	pop valency
	pop args ; ptr to args array
	mov eax, 4
	mul valency
	add args, eax ; adjust args pointer to point
	              ; at end of array, so it can be
				  ; trivially pushed in backwards

	; this needs to go before the loop
	; or it'll get clobbered
	mov opcode, edx ; syscall number

	; 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 0 and the
	; error number to its maximum value in
	; order to indicate that the syscall
	; was invalid
	mov dword [result_ptr], -1
	mov dword [errno_ptr], -1
	ret

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

	; check if an error has occurred, and
	; jump to the error handling routine
	; if necessary
	test sys.reg.ret, sys.reg.ret
	js .error
	
	; move our return values into place and
	; return to the caller (which should
	; always be k_platform_syscall, btw)
	mov       [result_ptr], sys.reg.ret
	mov dword [errno_ptr], 0
	ret

	.error: neg sys.reg.ret
	        mov dword [result_ptr], -1
			mov       [errno_ptr], sys.reg.ret
			ret
