// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// System calls and other sys.stuff for 386, Darwin
// System calls are implemented in libSystem, this file contains
// trampolines that convert from Go to C calling convention.

#include "go_asm.h"
#include "go_tls.h"
#include "textflag.h"

// Exit the entire program (like C exit)
TEXT runtime·exit_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP   	// allocate space for callee args (must be 8 mod 16)
	MOVL	16(SP), CX	// arg ptr
	MOVL	0(CX), AX	// arg 1 exit status
	MOVL	AX, 0(SP)
	CALL	libc_exit(SB)
	MOVL	$0xf1, 0xf1  // crash
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·open_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 name
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 mode
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 perm
	MOVL	AX, 8(SP)
	CALL	libc_open(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·close_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX		// arg 1 fd
	MOVL	AX, 0(SP)
	CALL	libc_close(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·read_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 fd
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 buf
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 count
	MOVL	AX, 8(SP)
	CALL	libc_read(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·write_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 fd
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 buf
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 count
	MOVL	AX, 8(SP)
	CALL	libc_write(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 addr
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 len
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 prot
	MOVL	AX, 8(SP)
	MOVL	12(CX), AX		// arg 4 flags
	MOVL	AX, 12(SP)
	MOVL	16(CX), AX		// arg 5 fid
	MOVL	AX, 16(SP)
	MOVL	20(CX), AX		// arg 6 offset
	MOVL	AX, 20(SP)
	CALL	libc_mmap(SB)
	XORL	DX, DX
	CMPL	AX, $-1
	JNE	ok
	CALL	libc_error(SB)
	MOVL	(AX), DX		// errno
	XORL	AX, AX
ok:
	MOVL	32(SP), CX
	MOVL	AX, 24(CX)		// result pointer
	MOVL	DX, 28(CX)		// errno
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·madvise_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 addr
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 len
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 advice
	MOVL	AX, 8(SP)
	CALL	libc_madvise(SB)
	// ignore failure - maybe pages are locked
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·munmap_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX		// arg 1 addr
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 len
	MOVL	AX, 4(SP)
	CALL	libc_munmap(SB)
	TESTL	AX, AX
	JEQ	2(PC)
	MOVL	$0xf1, 0xf1  // crash
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 mode
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 new
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 old
	MOVL	AX, 8(SP)
	CALL	libc_setitimer(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·walltime_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), AX
	MOVL	AX, 0(SP)	// *timeval
	MOVL	$0, 4(SP)	// no timezone needed
	CALL	libc_gettimeofday(SB)
	MOVL	BP, SP
	POPL	BP
	RET

GLOBL timebase<>(SB),NOPTR,$(machTimebaseInfo__size)

TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8+(machTimebaseInfo__size+15)/16*16, SP
	CALL	libc_mach_absolute_time(SB)
	MOVL	16+(machTimebaseInfo__size+15)/16*16(SP), CX
	MOVL	AX, 0(CX)
	MOVL	DX, 4(CX)
	MOVL	timebase<>+machTimebaseInfo_denom(SB), DI // atomic read
	MOVL	timebase<>+machTimebaseInfo_numer(SB), SI
	TESTL	DI, DI
	JNE	initialized

	LEAL	4(SP), AX
	MOVL	AX, 0(SP)
	CALL	libc_mach_timebase_info(SB)
	MOVL	4+machTimebaseInfo_numer(SP), SI
	MOVL	4+machTimebaseInfo_denom(SP), DI

	MOVL	SI, timebase<>+machTimebaseInfo_numer(SB)
	MOVL	DI, AX
	XCHGL	AX, timebase<>+machTimebaseInfo_denom(SB) // atomic write
	MOVL	16+(machTimebaseInfo__size+15)/16*16(SP), CX

initialized:
	MOVL	SI, 8(CX)
	MOVL	DI, 12(CX)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·sigaction_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 sig
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 new
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 old
	MOVL	AX, 8(SP)
	CALL	libc_sigaction(SB)
	TESTL	AX, AX
	JEQ	2(PC)
	MOVL	$0xf1, 0xf1  // crash
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 how
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 new
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 old
	MOVL	AX, 8(SP)
	CALL	libc_pthread_sigmask(SB)
	TESTL	AX, AX
	JEQ	2(PC)
	MOVL	$0xf1, 0xf1  // crash
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX		// arg 1 new
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 old
	MOVL	AX, 4(SP)
	CALL	libc_sigaltstack(SB)
	TESTL	AX, AX
	JEQ	2(PC)
	MOVL	$0xf1, 0xf1  // crash
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·raiseproc_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	CALL	libc_getpid(SB)
	MOVL	AX, 0(SP)	// arg 1 pid
	MOVL	16(SP), CX
	MOVL	0(CX), AX
	MOVL	AX, 4(SP)	// arg 2 signal
	CALL	libc_kill(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·sigfwd(SB),NOSPLIT,$0-16
	MOVL	fn+0(FP), AX
	MOVL	sig+4(FP), BX
	MOVL	info+8(FP), CX
	MOVL	ctx+12(FP), DX
	MOVL	SP, SI
	SUBL	$32, SP
	ANDL	$~15, SP	// align stack: handler might be a C function
	MOVL	BX, 0(SP)
	MOVL	CX, 4(SP)
	MOVL	DX, 8(SP)
	MOVL	SI, 12(SP)	// save SI: handler might be a Go function
	CALL	AX
	MOVL	12(SP), AX
	MOVL	AX, SP
	RET

// Sigtramp's job is to call the actual signal handler.
// It is called with the C calling convention, and calls out
// to sigtrampgo with the Go calling convention.
TEXT runtime·sigtramp(SB),NOSPLIT,$0
	SUBL	$28, SP

	// Save callee-save registers.
	MOVL	BP, 12(SP)
	MOVL	BX, 16(SP)
	MOVL	SI, 20(SP)
	MOVL	DI, 24(SP)

	MOVL	32(SP), AX
	MOVL	AX, 0(SP)	// arg 1 signal number
	MOVL	36(SP), AX
	MOVL	AX, 4(SP)	// arg 2 siginfo
	MOVL	40(SP), AX
	MOVL	AX, 8(SP)	// arg 3 ctxt
	CALL	runtime·sigtrampgo(SB)

	// Restore callee-save registers.
	MOVL	12(SP), BP
	MOVL	16(SP), BX
	MOVL	20(SP), SI
	MOVL	24(SP), DI

	ADDL	$28, SP
	RET

TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
	JMP	runtime·sigtramp(SB)

TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 usec
	MOVL	AX, 0(SP)
	CALL	libc_usleep(SB)
	MOVL	BP, SP
	POPL	BP
	RET

// func setldt(entry int, address int, limit int)
TEXT runtime·setldt(SB),NOSPLIT,$32
	// Nothing to do on Darwin, pthread already set thread-local storage up.
	RET

TEXT runtime·sysctl_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 mib
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 miblen
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 out
	MOVL	AX, 8(SP)
	MOVL	12(CX), AX		// arg 4 size
	MOVL	AX, 12(SP)
	MOVL	16(CX), AX		// arg 5 dst
	MOVL	AX, 16(SP)
	MOVL	20(CX), AX		// arg 6 ndst
	MOVL	AX, 20(SP)
	CALL	libc_sysctl(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·kqueue_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	CALL	libc_kqueue(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 kq
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 ch
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 nch
	MOVL	AX, 8(SP)
	MOVL	12(CX), AX		// arg 4 ev
	MOVL	AX, 12(SP)
	MOVL	16(CX), AX		// arg 5 nev
	MOVL	AX, 16(SP)
	MOVL	20(CX), AX		// arg 6 ts
	MOVL	AX, 20(SP)
	CALL	libc_kevent(SB)
	CMPL	AX, $-1
	JNE	ok
	CALL	libc_error(SB)
	MOVL	(AX), AX		// errno
	NEGL	AX			// caller wants it as a negative error code
ok:
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX		// arg 1 fd
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX		// arg 2 cmd
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX		// arg 3 arg
	MOVL	AX, 8(SP)
	CALL	libc_fcntl(SB)
	MOVL	BP, SP
	POPL	BP
	RET

// mstart_stub is the first function executed on a new thread started by pthread_create.
// It just does some low-level setup and then calls mstart.
// Note: called with the C calling convention.
TEXT runtime·mstart_stub(SB),NOSPLIT,$0
	// The value at SP+4 points to the m.
	// We are already on m's g0 stack.

	// Save callee-save registers.
	SUBL	$16, SP
	MOVL	BP, 0(SP)
	MOVL	BX, 4(SP)
	MOVL	SI, 8(SP)
	MOVL	DI, 12(SP)

	MOVL	SP, AX       // hide argument read from vet (vet thinks this function is using the Go calling convention)
	MOVL	20(AX), DI   // m
	MOVL	m_g0(DI), DX // g

	// Initialize TLS entry.
	// See cmd/link/internal/ld/sym.go:computeTLSOffset.
	MOVL	DX, 0x18(GS)

	// Someday the convention will be D is always cleared.
	CLD

	CALL	runtime·mstart(SB)

	// Restore callee-save registers.
	MOVL	0(SP), BP
	MOVL	4(SP), BX
	MOVL	8(SP), SI
	MOVL	12(SP), DI

	// Go is all done with this OS thread.
	// Tell pthread everything is ok (we never join with this thread, so
	// the value here doesn't really matter).
	XORL	AX, AX

	ADDL	$16, SP
	RET

TEXT runtime·pthread_attr_init_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 attr
	MOVL	AX, 0(SP)
	CALL	libc_pthread_attr_init(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_attr_setstacksize_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 attr
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX	// arg 2 size
	MOVL	AX, 4(SP)
	CALL	libc_pthread_attr_setstacksize(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_attr_setdetachstate_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 attr
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX	// arg 2 state
	MOVL	AX, 4(SP)
	CALL	libc_pthread_attr_setdetachstate(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_create_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	LEAL	16(SP), AX	// arg "0" &threadid (which we throw away)
	MOVL	AX, 0(SP)
	MOVL	0(CX), AX	// arg 1 attr
	MOVL	AX, 4(SP)
	MOVL	4(CX), AX	// arg 2 start
	MOVL	AX, 8(SP)
	MOVL	8(CX), AX	// arg 3 arg
	MOVL	AX, 12(SP)
	CALL	libc_pthread_create(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·raise_trampoline(SB),NOSPLIT,$0
	PUSHL   BP
	MOVL    SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL    0(CX), AX	// arg 1 sig
	MOVL	AX, 0(SP)
	CALL    libc_raise(SB)
	MOVL    BP, SP
	POPL    BP
	RET

TEXT runtime·pthread_mutex_init_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 mutex
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX	// arg 2 attr
	MOVL	AX, 4(SP)
	CALL	libc_pthread_mutex_init(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_mutex_lock_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 mutex
	MOVL	AX, 0(SP)
	CALL	libc_pthread_mutex_lock(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_mutex_unlock_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 mutex
	MOVL	AX, 0(SP)
	CALL	libc_pthread_mutex_unlock(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_cond_init_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 cond
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX	// arg 2 attr
	MOVL	AX, 4(SP)
	CALL	libc_pthread_cond_init(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_cond_wait_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 cond
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX	// arg 2 mutex
	MOVL	AX, 4(SP)
	CALL	libc_pthread_cond_wait(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_cond_timedwait_relative_np_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	0(CX), AX	// arg 1 cond
	MOVL	AX, 0(SP)
	MOVL	4(CX), AX	// arg 2 mutex
	MOVL	AX, 4(SP)
	MOVL	8(CX), AX	// arg 3 timeout
	MOVL	AX, 8(SP)
	CALL	libc_pthread_cond_timedwait_relative_np(SB)
	MOVL	BP, SP
	POPL	BP
	RET

TEXT runtime·pthread_cond_signal_trampoline(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$8, SP
	MOVL	16(SP), CX
	MOVL	0(CX), AX	// arg 1 cond
	MOVL	AX, 0(SP)
	CALL	libc_pthread_cond_signal(SB)
	MOVL	BP, SP
	POPL	BP
	RET

// syscall calls a function in libc on behalf of the syscall package.
// syscall takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	(0*4)(CX), AX // fn
	MOVL	(1*4)(CX), DX // a1
	MOVL	DX, 0(SP)
	MOVL	(2*4)(CX), DX // a2
	MOVL	DX, 4(SP)
	MOVL	(3*4)(CX), DX // a3
	MOVL	DX, 8(SP)

	CALL	AX

	MOVL	32(SP), CX
	MOVL	AX, (4*4)(CX) // r1
	MOVL	DX, (5*4)(CX) // r2

	// Standard libc functions return -1 on error
	// and set errno.
	CMPL	AX, $-1
	JNE	ok

	// Get error code from libc.
	CALL	libc_error(SB)
	MOVL	(AX), AX
	MOVL	32(SP), CX
	MOVL	AX, (6*4)(CX) // err

ok:
	XORL	AX, AX        // no error (it's ignored anyway)
	MOVL	BP, SP
	POPL	BP
	RET

// Not used on 386.
TEXT runtime·syscallPtr(SB),NOSPLIT,$0
	MOVL	$0xf1, 0xf1  // crash
	RET

// syscall6 calls a function in libc on behalf of the syscall package.
// syscall6 takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	a4    uintptr
//	a5    uintptr
//	a6    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall6 must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall6(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	(0*4)(CX), AX // fn
	MOVL	(1*4)(CX), DX // a1
	MOVL	DX, 0(SP)
	MOVL	(2*4)(CX), DX // a2
	MOVL	DX, 4(SP)
	MOVL	(3*4)(CX), DX // a3
	MOVL	DX, 8(SP)
	MOVL	(4*4)(CX), DX // a4
	MOVL	DX, 12(SP)
	MOVL	(5*4)(CX), DX // a5
	MOVL	DX, 16(SP)
	MOVL	(6*4)(CX), DX // a6
	MOVL	DX, 20(SP)

	CALL	AX

	MOVL	32(SP), CX
	MOVL	AX, (7*4)(CX) // r1
	MOVL	DX, (8*4)(CX) // r2

	// Standard libc functions return -1 on error
	// and set errno.
	CMPL	AX, $-1
	JNE	ok

	// Get error code from libc.
	CALL	libc_error(SB)
	MOVL	(AX), AX
	MOVL	32(SP), CX
	MOVL	AX, (9*4)(CX) // err

ok:
	XORL	AX, AX        // no error (it's ignored anyway)
	MOVL	BP, SP
	POPL	BP
	RET

// syscall6X calls a function in libc on behalf of the syscall package.
// syscall6X takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	a4    uintptr
//	a5    uintptr
//	a6    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall6X must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall6X(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$24, SP
	MOVL	32(SP), CX
	MOVL	(0*4)(CX), AX // fn
	MOVL	(1*4)(CX), DX // a1
	MOVL	DX, 0(SP)
	MOVL	(2*4)(CX), DX // a2
	MOVL	DX, 4(SP)
	MOVL	(3*4)(CX), DX // a3
	MOVL	DX, 8(SP)
	MOVL	(4*4)(CX), DX // a4
	MOVL	DX, 12(SP)
	MOVL	(5*4)(CX), DX // a5
	MOVL	DX, 16(SP)
	MOVL	(6*4)(CX), DX // a6
	MOVL	DX, 20(SP)

	CALL	AX

	MOVL	32(SP), CX
	MOVL	AX, (7*4)(CX) // r1
	MOVL	DX, (8*4)(CX) // r2

	// Standard libc functions return -1 on error
	// and set errno.
	CMPL	AX, $-1
	JNE	ok
	CMPL	DX, $-1
	JNE	ok

	// Get error code from libc.
	CALL	libc_error(SB)
	MOVL	(AX), AX
	MOVL	32(SP), CX
	MOVL	AX, (9*4)(CX) // err

ok:
	XORL	AX, AX        // no error (it's ignored anyway)
	MOVL	BP, SP
	POPL	BP
	RET

// syscall9 calls a function in libc on behalf of the syscall package.
// syscall9 takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	a4    uintptr
//	a5    uintptr
//	a6    uintptr
//	a7    uintptr
//	a8    uintptr
//	a9    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall9 must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall9(SB),NOSPLIT,$0
	PUSHL	BP
	MOVL	SP, BP
	SUBL	$40, SP
	MOVL	48(SP), CX
	MOVL	(0*4)(CX), AX // fn
	MOVL	(1*4)(CX), DX // a1
	MOVL	DX, 0(SP)
	MOVL	(2*4)(CX), DX // a2
	MOVL	DX, 4(SP)
	MOVL	(3*4)(CX), DX // a3
	MOVL	DX, 8(SP)
	MOVL	(4*4)(CX), DX // a4
	MOVL	DX, 12(SP)
	MOVL	(5*4)(CX), DX // a5
	MOVL	DX, 16(SP)
	MOVL	(6*4)(CX), DX // a6
	MOVL	DX, 20(SP)
	MOVL	(7*4)(CX), DX // a7
	MOVL	DX, 24(SP)
	MOVL	(8*4)(CX), DX // a8
	MOVL	DX, 28(SP)
	MOVL	(9*4)(CX), DX // a9
	MOVL	DX, 32(SP)

	CALL	AX

	MOVL	48(SP), CX
	MOVL	AX, (10*4)(CX) // r1
	MOVL	DX, (11*4)(CX) // r2

	// Standard libc functions return -1 on error
	// and set errno.
	CMPL	AX, $-1
	JNE	ok

	// Get error code from libc.
	CALL	libc_error(SB)
	MOVL	(AX), AX
	MOVL	48(SP), CX
	MOVL	AX, (12*4)(CX) // err

ok:
	XORL	AX, AX        // no error (it's ignored anyway)
	MOVL	BP, SP
	POPL	BP
	RET
