/* Modify saved context.
   Copyright (C) 2009 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Maciej W. Rozycki <macro@codesourcery.com>.

   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, see
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>
#include <sys/asm.h>
#include <sys/fpregdef.h>
#include <sys/regdef.h>

#include "ucontext_i.h"

/* int makecontext (ucontext_t *ucp, (void *func) (), int argc, ...) */

	.text
LOCALSZ = 0
ARGSZ = 0
MASK = 0x00000000
#ifdef __PIC__
LOCALSZ = 1						/* save gp */
#endif
#if _MIPS_SIM != _ABIO32
ARGSZ = 5						/* save a3-a7 */
# ifdef __PIC__
MASK = 0x10000000
# endif
#endif
FRAMESZ = (((ARGSZ + LOCALSZ) * SZREG) + ALSZ) & ALMASK
GPOFF = FRAMESZ - ((ARGSZ + 1) * SZREG)
#if _MIPS_SIM != _ABIO32
A3OFF = FRAMESZ - (5 * SZREG)				/* callee-allocated */
A4OFF = FRAMESZ - (4 * SZREG)
A5OFF = FRAMESZ - (3 * SZREG)
A6OFF = FRAMESZ - (2 * SZREG)
A7OFF = FRAMESZ - (1 * SZREG)
NARGREGS = 8
#else
A3OFF = FRAMESZ + (3 * SZREG)				/* caller-allocated */
NARGREGS = 4
#endif

NESTED (__makecontext, FRAMESZ, ra)
	.mask	MASK, -(ARGSZ * SZREG)
	.fmask	0x00000000, 0

98:
#ifdef __PIC__
	SETUP_GP
#endif

	PTR_ADDIU sp, -FRAMESZ

#ifdef __PIC__
	SETUP_GP64 (GPOFF, __makecontext)
	SAVE_GP (GPOFF)
#endif

#ifdef PROF
	.set	noat
	move	AT, ra
	jal	_mcount
	.set	at
#endif

	/* Store args to be passed.  */
	REG_S	a3, A3OFF(sp)
#if _MIPS_SIM != _ABIO32
	REG_S	a4, A4OFF(sp)
	REG_S	a5, A5OFF(sp)
	REG_S	a6, A6OFF(sp)
	REG_S	a7, A7OFF(sp)
#endif

	/* Store a magic flag.  */
	li	v1, 1
	REG_S	v1, (0 * SZREG + MCONTEXT_GREGS)(a0)	/* zero */

	/* Set up the stack.  */
	PTR_L	t0, STACK_SP(a0)
	PTR_L	t2, STACK_SIZE(a0)
	PTR_ADDIU t1, sp, A3OFF
	PTR_ADDU t0, t2
	and	t0, ALMASK
	blez	a2, 2f					/* no arguments */

	/* Store register arguments.  */
	PTR_ADDIU t2, a0, MCONTEXT_GREGS + 4 * SZREG
	move	t3, zero
0:
	addiu	t3, 1
	REG_L	v1, (t1)
	PTR_ADDIU t1, SZREG
	REG_S	v1, (t2)
	PTR_ADDIU t2, SZREG
	bgeu	t3, a2, 2f				/* all done */
	bltu	t3, NARGREGS, 0b			/* next */

	/* Make room for stack arguments.  */
	PTR_SUBU t2, a2, t3
	PTR_SLL	t2, 3
	PTR_SUBU t0, t2
	and	t0, ALMASK

	/* Store stack arguments.  */
	move	t2, t0
1:
	addiu	t3, 1
	REG_L	v1, (t1)
	PTR_ADDIU t1, SZREG
	REG_S	v1, (t2)
	PTR_ADDIU t2, SZREG
	bltu	t3, a2, 1b				/* next */

2:
#if _MIPS_SIM == _ABIO32
	/* Make room for a0-a3 storage.  */
	PTR_ADDIU t0, -(NARGSAVE * SZREG)
#endif
	PTR_L	v1, UCONTEXT_LINK(a0)
#ifdef __PIC__
	PTR_ADDIU t9, 99f - 98b
#else
	PTR_LA	t9, 99f
#endif
	REG_S	t0, (29 * SZREG + MCONTEXT_GREGS)(a0)	/* sp */
	REG_S	v1, (16 * SZREG + MCONTEXT_GREGS)(a0)	/* s0 */
#ifdef __PIC__
	REG_S	gp, (17 * SZREG + MCONTEXT_GREGS)(a0)	/* s1 */
#endif
	REG_S	t9, (31 * SZREG + MCONTEXT_GREGS)(a0)	/* ra */
	REG_S	a1, MCONTEXT_PC(a0)

#ifdef __PIC__
	RESTORE_GP64
	PTR_ADDIU sp, FRAMESZ
#endif
	jr	ra

99:
#ifdef __PIC__
	move	gp, s1
#endif
	move	a0, zero
	beqz	s0, 0f

	/* setcontext (ucp) */
	move	a0, s0
#ifdef __PIC__
	PTR_LA	t9, JUMPTARGET (__setcontext)
	jalr	t9
# if _MIPS_SIM == _ABIO32
	move	gp, s1
# endif
#else
	jal	JUMPTARGET (__setcontext)
#endif
	move	a0, v0

0:
	/* exit (a0) */
#ifdef __PIC__
	PTR_LA	t9, HIDDEN_JUMPTARGET (exit)
	jalr	t9
#else
	jal	HIDDEN_JUMPTARGET (exit)
#endif

	/* You don't exist, you won't feel anything.  */
1:
	lb	zero, (zero)
	b	1b
PSEUDO_END (__makecontext)

weak_alias (__makecontext, makecontext)