/* Assembler macros for FR-V.
   Copyright (C) 1999, 2001, 2003, 2004 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <asm/unistd.h>
#include <sysdeps/frv/sysdep.h>
#include <sys/syscall.h>

#undef SYS_ify
#define SYS_ify(syscall_name)	(__NR_##syscall_name)


#ifdef __ASSEMBLER__

/* For Linux we can use the system call table in the header file
	/usr/include/asm/unistd.h
   of the kernel.  But these symbols do not follow the SYS_* syntax
   so we have to redefine the `SYS_ify' macro here.  */
#undef SYS_ify
#define SYS_ify(syscall_name)	__NR_##syscall_name

/* ELF-like local names start with `.L'.  */
#undef L
#define L(name)	.L##name

/* Linux uses a negative return value to indicate syscall errors,
   unlike most Unices, which use the condition codes' carry flag.

   Since version 2.1 the return value of a system call might be
   negative even if the call succeeded.  E.g., the `lseek' system call
   might return a large offset.  Therefore we must not anymore test
   for < 0, but test for a real error by making sure the value in %eax
   is a real error number.  Linus said he will make sure the no syscall
   returns a value in -1 .. -4095 as a valid result so we can safely
   test with -4095.  */

/* Syscall wrappers consist of
	#include <sysdep.h>
	PSEUDO (...)
	 ret
	PSEUDO_END (...)

   which expand to the following.  */

/* Linux takes system call arguments in registers:
	syscall number	GR7
	arg 1		GR8
	arg 2		GR9
	arg 3		GR10
	arg 4		GR11
	arg 5		GR12
	arg 6		GR13

   The compiler calls us by the C convention:
	syscall number	in the DO_CALL macro
	arg 1		GR8
	arg 2		GR9
	arg 3		GR10
	arg 4		GR11
	arg 5		GR12
	arg 6		GR13
   */

#define	PSEUDO(name, syscall_name, args) \
  ENTRY	(name)						! \
  setlos.p SYS_ify (syscall_name), gr7			! \
  setlos -4096, gr4					! \
  tra gr0, gr0						! \
  subcc gr8, gr4, gr0, icc0				! \
  blslr icc0,0x2					! \
  SYSCALL_ERROR_HANDLER

#if RTLD_PRIVATE_ERRNO
# define SYSCALL_ERROR_HANDLER \
  sub gr0, gr8, gr8					! \
  sethi.p #gotoffhi(errno), gr4				! \
  setlo #gotofflo(errno), gr4				! \
  st.p gr8, @(gr15, gr4)				! \
  setlos #-1, gr8

#elif defined _LIBC_REENTRANT
# ifndef NOT_IN_libc
#  define SYSCALL_ERROR_HANDLER \
	sub.p	gr0, gr8, gr4				! \
	sethi	#gotofffuncdeschi(__errno_location), gr14 ! \
	addi.p	sp, #-16, sp				! \
	setlo	#gotofffuncdesclo(__errno_location), gr14 ! \
	movsg	lr, gr5					! \
	sti.p	fp, @(sp, 8)				! \
	addi	sp, #8, fp				! \
	ldd	@(gr15, gr14), gr14			! \
	stdi	gr4, @(fp, -8)				! \
	calll	@(gr14, gr0)				! \
	lddi	@(fp, -8), gr4				! \
	ld.p	@(fp, gr0), fp				! \
	addi	sp, #16, sp				! \
	st	gr4, @(gr8, gr0)			! \
	jmpl.p	@(gr5, gr0)				! \
	setlos	#-1, gr8
# else
#  define SYSCALL_ERROR_HANDLER \
	sub.p	gr0, gr8, gr4				! \
	addi	sp, #-16, sp				! \
	movsg	lr, gr5					! \
	sti.p	fp, @(sp, 8)				! \
	addi	sp, #8, fp				! \
	stdi.p	gr4, @(fp, -8)				! \
	call	C_SYMBOL_NAME(__errno_location)		! \
	lddi	@(fp, -8), gr4				! \
	st.p	gr4, @(gr8, gr0)			! \
	setlos	#-1, gr8				! \
	ld	@(fp, gr0), fp				! \
	jmpl.p	@(gr5, gr0)				! \
	addi	sp, #16, sp
# endif
#else
# define SYSCALL_ERROR_HANDLER \
  sub gr0, gr8, gr8					! \
  sethi.p #gothi(errno), gr4				! \
  setlo #gotlo(errno), gr4				! \
  ld @(gr15, gr4), gr4					! \
  st.p gr8, @(gr4, gr0)					! \
  setlos #-1, gr8
#endif
  
#define	PSEUDO_END(name) \
  END (name)

#define	PSEUDO_NOERRNO(name, syscall_name, args) \
  ENTRY	(name)						! \
  setlos #SYS_ify (syscall_name), gr7			! \
  tra gr0, gr0

#define ret_NOERRNO ret

#define	PSEUDO_END_NOERRNO(name) \
  END (name)

#define	PSEUDO_ERRVAL(name, syscall_name, args) \
  ENTRY	(name)						! \
  setlos SYS_ify (syscall_name), gr7			! \
  tra gr0, gr0						! \
  sub gr0, gr8, gr8

#define ret_ERRVAL ret

#define	PSEUDO_END_ERRVAL(name) \
  END (name)

#else  /* not __ASSEMBLER__ */

/* Define a macro which expands inline into the wrapper code for a system
   call.  */
#undef INLINE_SYSCALL
#define INLINE_SYSCALL(name, nr, args...) \
  ({									      \
    unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args);	      \
    if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (resultvar, ), 0))	      \
      {									      \
	__set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, ));		      \
	resultvar = 0xffffffff;						      \
      }									      \
    (int) resultvar; })

#undef INTERNAL_SYSCALL
#define INTERNAL_SYSCALL(name, err, nr, args...)	\
  ({						\
     register unsigned long __res asm ("gr8");	\
     LOAD_ARGS_c_##nr (args)			\
     register unsigned long __callno asm ("gr7")\
       = SYS_ify (name);			\
     asm volatile (LOAD_ARGS_asm_##nr (args)	\
		   "tra gr0, gr0"		\
		   : "=r" (__res)		\
		   : ASM_ARGS_##nr (args)	\
		   : ASM_CLOBBER_##nr);		\
     (int) __res;				\
   })

#undef INTERNAL_SYSCALL_DECL
#define INTERNAL_SYSCALL_DECL(err) do { } while (0)

#undef INTERNAL_SYSCALL_ERROR_P
#define INTERNAL_SYSCALL_ERROR_P(val, err) \
  ((unsigned int) (val) > 0xfffff000u)

#undef INTERNAL_SYSCALL_ERRNO
#define INTERNAL_SYSCALL_ERRNO(val, err)	(-(val))

#define LOAD_ARGS_c_0()
#define LOAD_ARGS_asm_0()
#define ASM_CLOBBER_0 "memory"
#define ASM_ARGS_0() "r" (__callno)

#define LOAD_ARGS_c_1(gr8) \
	LOAD_ARGS_c_0()						\
	register unsigned long __gr8 __asm__ ("gr8") = (unsigned long) (gr8);
#define LOAD_ARGS_asm_1(gr8) LOAD_ARGS_asm_0 ()
#define ASM_CLOBBER_1 ASM_CLOBBER_0
#define ASM_ARGS_1(gr8) ASM_ARGS_0 (), "0" (__gr8)

#define LOAD_ARGS_c_2(gr8, gr9) \
	LOAD_ARGS_c_1(gr8)					\
	register unsigned long __gr9 __asm__ ("gr9") = (unsigned long) (gr9);
#define LOAD_ARGS_asm_2(gr8, gr9) LOAD_ARGS_asm_1 (gr8)
#define ASM_CLOBBER_2 ASM_CLOBBER_1
#define ASM_ARGS_2(gr8, gr9) ASM_ARGS_1 (gr8), "r" (__gr9)

#define LOAD_ARGS_c_3(gr8, gr9, gr10) \
	LOAD_ARGS_c_2(gr8, gr9)					\
	register unsigned long __gr10 __asm__ ("gr10") = (unsigned long) (gr10);
#define LOAD_ARGS_asm_3(gr8, gr9, gr10) LOAD_ARGS_asm_2 (gr8, gr9)
#define ASM_CLOBBER_3 ASM_CLOBBER_2
#define ASM_ARGS_3(gr8, gr9, gr10) ASM_ARGS_2 (gr8, gr9), "r" (__gr10)

#define LOAD_ARGS_c_4(gr8, gr9, gr10, gr11) \
	LOAD_ARGS_c_3(gr8, gr9, gr10)				\
	register unsigned long __gr11 __asm__ ("gr11") = (unsigned long) (gr11);
#define LOAD_ARGS_asm_4(gr8, gr9, gr10, gr11) LOAD_ARGS_asm_3 (gr8, gr9, gr10)
#define ASM_CLOBBER_4 ASM_CLOBBER_3
#define ASM_ARGS_4(gr8, gr9, gr10, gr11)			\
 	ASM_ARGS_3 (gr8, gr9, gr10), "r" (__gr11)

#define LOAD_ARGS_c_5(gr8, gr9, gr10, gr11, gr12) \
	LOAD_ARGS_c_4(gr8, gr9, gr10, gr11)			\
	register unsigned long __gr12 __asm__ ("gr12") = (unsigned long) (gr12);
#define LOAD_ARGS_asm_5(gr8, gr9, gr10, gr11, gr12) \
	LOAD_ARGS_asm_4 (gr8, gr9, gr10, gr11)
#define ASM_CLOBBER_5 ASM_CLOBBER_4
#define ASM_ARGS_5(gr8, gr9, gr10, gr11, gr12) \
	ASM_ARGS_4 (gr8, gr9, gr10, gr11), "r" (__gr12)

#define LOAD_ARGS_c_6(gr8, gr9, gr10, gr11, gr12, gr13)		\
	LOAD_ARGS_c_5(gr8, gr9, gr10, gr11, gr12)		\
	register unsigned long __gr13 __asm__ ("gr13") = (unsigned long) (gr13);
#define LOAD_ARGS_asm_6(gr8, gr9, gr10, gr11, gr12, gr13)	\
	LOAD_ARGS_asm_5(gr8, gr9, gr10, gr11, gr12)
#define ASM_CLOBBER_6 ASM_CLOBBER_5
#define ASM_ARGS_6(gr8, gr9, gr10, gr11, gr12, gr13) \
	ASM_ARGS_5 (gr8, gr9, gr10, gr11, gr12), "r" (__gr13)

#endif	/* not __ASSEMBLER__ */
