/*
 *	Pentium/386 and GCC specific assembler inserts
 *	Copyright (C) 1998 Jim Moores
 *	Modification copyright (C) 1999-2002 Fred Barnes  <frmb2@ukc.ac.uk>
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program 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 General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * This file attempts to isolate as many as possible of the platform 
 * specific features of this package as possible.  The macros have been
 * designed so that it should be easy to port this kernel to a more
 * register based machine fairly easily (eg Alpha, ARM etc).  Note that
 * there are numerous functions in this file labelled such that are no
 * longer used.
 */

#ifndef SCHED_ASM_INSERTS_H
#define SCHED_ASM_INSERTS_H

/* these for debugging mostly */
#define LOAD_ESP(X) \
	__asm__ __volatile__ ("\n\tmovl %%esp, %0\n\t" : "=r" (X))

#define STACK_CHK_ENTRY(callname) \
	LOAD_ESP (the_stackptr)
#define STACK_CHK_LEAVE(callname) \
	LOAD_ESP (the_stackptr2); \
	if (the_stackptr2 != the_stackptr) \
		fprintf (stderr, "CCSP: serious: stack deformation in %s: 0x%8.8x --> 0x%8.8x (%d words)\n", callname, the_stackptr, the_stackptr2, (int)(the_stackptr - the_stackptr2))

#if defined(OOS_BUILD)
#define OOS_CLI \
	__asm__ __volatile__ ("cli	\n");
#define OOS_STI \
	__asm__ __volatile__ ("sti	\n");
#endif	/* defined(OOS_BUILD) */

/* Load the Workspace pointer register into a variable.
 */
#define LOADWPTR \
  __asm__ __volatile__ ("movl   %%ebp, %0\n" \
			"movl	%%esi, %1\n" \
			"movl	%%edi, %2\n" \
                        : "=m" (Wptr), "=m" (Fptr), "=m" (Bptr) \
                       )
#define LOADQPTR \
	__asm__ __volatile__ ( \
		"	movl	%%esi, %0		\n" \
		"	movl	%%edi, %1		\n" \
		: "=m" (Fptr), "=m" (Bptr))

#define LOADWPTRRET \
	__asm__ __volatile__ ( \
		"	movl	%%ebp, %0\n" \
		"	movl	%%esi, %1\n" \
		"	movl	%%edi, %2\n" \
		"	popl	-4(%%ebp)\n" \
		: "=m" (Wptr), "=m" (Fptr), "=m" (Bptr))


#define SAVEWPTR \
  __asm__ __volatile__ ("movl	%0, %%ebp\n" \
  			: /* no outputs */ \
  			: "g" (Wptr))

/* Restore the workspace pointer and kernel entry table pointers to their
 * values before this function call.  This should be combined with a EJUMP
 * really, because it always happens.  Note that the jump table pointer is
 * not necessary in C.
 */
#define RESTORE \
	__asm__ __volatile__ ( \
		"	movl	%0, %%ebp		\n" \
		"	movl	%1, %%esi		\n" \
		"	movl	%2, %%edi		\n" \
		: /* no outputs */ \
		: "g" (Wptr), "g" (Fptr), "g" (Bptr) \
		: "memory", "cc")

#define RESTOREJRET \
	__asm__ __volatile__ ( \
		"	movl	%0, %%ebp	\n" \
		"	movl	%1, %%esi	\n" \
		"	movl	%2, %%edi	\n" \
		"	jmp	*-4(%%ebp)	\n" \
		: /* no outputs */ \
		: "g" (Wptr), "g" (Fptr), "g" (Bptr) \
		: "memory", "cc")

#define RESTOREIRET \
	__asm__ __volatile__ ( \
		"	movl	%0, %%ebp	\n" \
		"	movl	%1, %%esi	\n" \
		"	movl	%2, %%edi	\n" \
		"	jmp	*0(%%ebp)	\n" \
		: /* no outputs */ \
		: "g" (Wptr), "g" (Fptr), "g" (Bptr) \
		: "memory", "cc")

/* Load the parameter value passed to the kernel into a variable.  
 */ 
#define PARAM(A) \
  __asm__ __volatile__ ("popl   %0\n" \
                        : "=g" (A) \
                       )
/* 
 * Load the two parameter values passed to the kernel into variables.  
 */ 
#define TWOPARAMS(A,B) \
  __asm__ __volatile__ ("popl   %0\n \
                         popl   %1\n" \
                        : "=g" (A), "=g" (B) \
                       )

/* Load the three parameter values passed to the kernel into variables.  
 */ 
#define THREEPARAMS(A,B,C) \
  __asm__ __volatile__ ("popl   %0\n \
                         popl   %1\n \
                         popl   %2\n" \
                        : "=g" (A), "=g" (B), "=g" (C) \
                       )

/* Load the four parameter values passed to the kernel into variables.  
 * this is in fact used to pass the time as a 64bit value, in two 32bit
 * chunks.
 */ 
 /* Mainly used for 64-bit quantities, but I use it for occam debugging. [fred] */
#define FOURPARAMS(A,B,C,D) \
  __asm__ __volatile__ ("popl   %0\n" \
                        "popl   %1\n" \
                        "popl   %2\n" \
                        "popl   %3\n" \
                        : "=g" (A), "=g" (B), "=g" (C), "=g" (D) \
                       )

/* didn't want to trample on a register for floating-point errors.. */
#define FIVEPARAMS(A,B,C,D,E) \
  __asm__ __volatile__ ("popl	%0\n" \
  			"popl	%1\n" \
  			"popl	%2\n" \
  			"popl	%3\n" \
  			"popl	%4\n" \
  			: "=g" (A), "=g" (B), "=g" (C), "=g" (D), "=g" (E) \
  			)

/* experimental kernel interface stuff
 */

#define K_SETGLABEL_THREE_IN(X,A,B,C) \
	__asm__ __volatile__ ("				\n" \
		".globl _"#X"				\n" \
		"	_"#X":				\n" \
		"	"#X":				\n" \
		"	movl	%%ebp, %0		\n" \
		"	movl	%%esi, %1		\n" \
		"	movl	%%edi, %2		\n" \
		: "=m" (Wptr), "=m" (Fptr), "=m" (Bptr), "=c" (C), "=b" (B), "=a" (A))
#define K_SETGLABEL_TWO_IN(X,A,B) \
	__asm__ __volatile__ ("				\n" \
		".globl _"#X"				\n" \
		"	_"#X":				\n" \
		"	"#X":				\n" \
		"	movl	%%ebp, %0		\n" \
		"	movl	%%esi, %1		\n" \
		"	movl	%%edi, %2		\n" \
		: "=m" (Wptr), "=m" (Fptr), "=m" (Bptr), "=b" (B), "=a" (A))
#define K_SETGLABEL_ONE_IN(X,A) \
	__asm__ __volatile__ ("				\n" \
		".globl _"#X"				\n" \
		"	_"#X":				\n" \
		"	"#X":				\n" \
		"	movl	%%ebp, %0		\n" \
		"	movl	%%esi, %1		\n" \
		"	movl	%%edi, %2		\n" \
		: "=m" (Wptr), "=m" (Fptr), "=m" (Bptr), "=a" (A))
#define K_SETGLABEL_ZERO_IN(X) \
	__asm__ __volatile__ ("				\n" \
		".globl _"#X"				\n" \
		"	_"#X":				\n" \
		"	"#X":				\n" \
		"	movl	%%ebp, %0		\n" \
		"	movl	%%esi, %1		\n" \
		"	movl	%%edi, %2		\n" \
		: "=m" (Wptr), "=m" (Fptr),"=m" (Bptr))

#define ONE_PARAM_IN(A) \
	__asm__ __volatile__ ( \
		"\n" \
		: "=a" (A))
#define TWO_PARAM_IN(A,B) \
	__asm__ __volatile__ ( \
		"\n" \
		: "=b" (A), "=a" (B))
#define THREE_PARAM_IN(A,B,C) \
	__asm__ __volatile__ ( \
		"\n" \
		: "=c" (A), "=b" (B), "=a" (C));
#define FOUR_PARAM_IN(A,B,C,D) \
	__asm__ __volatile__ ( \
		"\n" \
		: "=d" (A), "=c" (B), "=b" (C), "=a" (D));
#define ONE_PARAM_OUT(A) \
	__asm__ __volatile__ ( \
		"\n" \
		: : "a" (A));
#define TWO_PARAM_OUT(A,B) \
	__asm__ __volatile__ ( \
		"\n" \
		: : "b" (A), "a" (B));
#define THREE_PARAM_OUT(A,B,C) \
	__asm__ __volatile__ ( \
		"\n" \
		: : "c" (A), "b" (B), "a" (C));


/* loads the return address on entry to the kernel function into a global 
 * variable called 'return_address'.  This could be paramterised, but we
 * never use any other variable, so this seemed pointless.
 */
#define LOADRETURN \
  __asm__ __volatile__ ("popl   %0\n" \
                        : "=g" (return_address) \
                       )

/* Return a value from the current kernel function.  In tranpc this is done
 * by pushing on the system stack, but the convension is to use EAX on the
 * 386.  We use tranpc's convention for compatablilty with occam.
 */
#define RETURNVAL(X) \
  __asm__ __volatile__ ("pushl  %0\n" \
                        : /* no outputs */ \
                        : "g" (X) \
                       )

/* NO LONGER USED - USED TO SET THE RETURN ADDRESS */
/* KERNEL FUNCTIONS NOW EXIT WITH EJUMP's */
#define SETRETURN(X) \
  __asm__ __volatile__ ("pushl  %0\n" \
                        : /* no outputs */ \
                        : "g" (X) \
                       )

/* NO LONGER USED */
#define JUMP(X) \
  __asm__ __volatile__ ("jmp    %0 \n" \
                        : /* no outputs */ \
                        : "r" (X) \
                        : "memory" \
                       )
/* NO LONGER USED - REPLACED WITH PARAMETER LOADING */
#define PUSH(X) \
  __asm__ __volatile__ ("pushl  %0\n" \
                        : /* no outputs */ \
                        : "g" (X) \
                       )

/*
 * scheduler label functions
 */
#define K_LOADLABADDR(X,V) \
	__asm__ __volatile__ ("\n" \
		"	leal "#X", %%eax\n" \
		"	movl %%eax, %0\n" \
		: "=g" (V) : : "eax" \
		)
#define K_JUMP(X) \
	__asm__ __volatile__ ("\n" \
		"	jmp "#X"\n" \
		)
#define K_SETLABEL(X) \
	__asm__ __volatile__ ("\n" \
		"	"#X":\n" \
		)
#define K_SETGLABEL(X) \
	__asm__ __volatile__ ("\n" \
		".globl _"#X"\n" \
		"	_"#X":\n" \
		"	"#X":\n" \
		)


/* an EJUMP is a jump out of the kernel into user code, usually the return
   address 
   Note that only GCC supports goto *(X), may need to be replaced with an
   asm jump if porting. */
#define EJUMP(X) \
	__asm__ __volatile__ ("\n" \
		"	jmp	*%0\n" \
		: /* no outputs */ : "g" (X))
/* #define EJUMP(X) goto *(X) */

/* Restore the stack pointer before returning to C */
#define RESTORESP(X) \
  __asm__ __volatile__ ("movl   %0, %%esp\n" \
                        : /* no outputs */ \
                        : "g" (X) \
                       ) 

/* Some macros for stacking/unstacking the static context so that kernel
 * calls can be called back from calls out to the communications subsystem
 * (is this 're-entrancy'?).  The current use of these assumes that they 
 * have LIFO stacking semantics.
 */
#define SAVESTATIC(X) \
    __asm__ __volatile__ ("pushl  %0\n" \
                          : /* no ouputs */ \
                          : "q" (X) \
                         );

#define RESTORESTATIC(X) \
    __asm__ __volatile__ ("popl  %0\n" \
                          : "=q" (X) \
                          : /* no inputs */ \
                         )
#define SAVESTATICS \
/*  __asm__ __volatile__ ("pushl  %0\n" \
                        "pushl  %1\n" \
                        : \
                        : "q" (Wptr), "q" (return_address) \
                        : "eax", "ebx" \
                       )*/

#define RESTORESTATICS \
/*  __asm__ __volatile__ ("popl %0\n" \
                        "popl %1\n" \
                        : "=q" (return_address), "=q" (Wptr) \
                        : \
                        : "eax", "ebx" \
                       )*/

/* MACRO LBL(A) -   declared a kernel function's label on two levels: 
 *                  the C level, as in
 *                    label: 
 *                  and as a global asm label so that functions can be
 *                  called without going through the jump table.  
 *                    asm(".globl label\n label:");
 *
 * IMPLEMENTATION NOTE:
 * this is really wierd - we rely here on ANSI C string concatenation: 
 * "Jim" "John" "Jan" == "JimJohnJan" therefore if B is a string we get 
 * ".globl" "<label>" concating to ".globl <label>" followed by 
 * "<label>" ":" concatting to "<label>:".  Items that appear quoted
 * are actually outside the quotes, this is the confusing thing.
 * I also use 'stringification' as documented in the GNU cpp 
 * documentation.  If '#define A foo' then '#A' is '"foo"'.  This is
 * needed to insert the name into the asm block.  Before I new how to
 * do this, this macro took two paramters, one 'stringified' and one
 * not.  This is nicer. 
 */ 

#define MEMCPY(SRC,DEST,COUNT) \
  __asm__ __volatile__ ( \
    "cld\n" \
    "rep\n" \
    "movsb\n" \
    : /* no outputs */ \
    : "S" (SRC), "D" (DEST), "c" (COUNT) \
  )

#ifdef GSL_SMP
#define CLAIM(SEMA) \
  __asm__ __volatile__ ( \
    "   movl    $1, %%eax\n" \
    "1: xchg    %%eax, %0\n" \
    "   cmpl    $0, %%eax\n" \
    "   jne     1b\n" \
    : /* no outputs */ \
    : "m" (SEMA) \
    : "eax" \
  )

#define RELEASE(SEMA) \
  __asm__ __volatile__ ( \
    "   movl   $0, %0" \
    : /* no outputs */ \
    : "m" (SEMA) \
  )
#endif
#endif /* sched_asm_inserts.h */

#ifdef PROCESS_PRIORITY

#define ASM_BSF(V,R) \
	__asm__ __volatile__ ("         \n" \
	"       bsf     %1,%0   \n" \
	: "=r" (R) : "r" (V) : "cc")

#endif	/* PROCESS_PRIORITY */

#ifdef DYNAMIC_PROCS

#if defined(CAN_DO_SCHEDSTRUCT)
#define KERNEL_RUN_CODE(WP,EP) \
	__asm__ __volatile__ ("			\n" \
		"       movl    %0, %%ebp       \n" \
		"       movl    svars+4, %%esi     \n" \
		"       movl    svars+8, %%edi     \n" \
		"       movl    $0f, 0(%%ebp)   \n" \
		"       jmp     *%1             \n" \
		"0:     jmp     _X_dynproc_exit \n" \
		: /* no outputs */ : "g" (WP), "g" (EP) : "memory", "cc")
#else	/* !CAN_DO_SCHEDSTRUCT */
#define KERNEL_RUN_CODE(WP,EP) \
	__asm__ __volatile__ ("			\n" \
		"       movl    %0, %%ebp       \n" \
		"       movl    Fptr, %%esi     \n" \
		"       movl    Bptr, %%edi     \n" \
		"       movl    $0f, 0(%%ebp)   \n" \
		"       jmp     *%1             \n" \
		"0:     jmp     _X_dynproc_exit \n" \
		: /* no outputs */ : "g" (WP), "g" (EP) : "memory", "cc")
#endif	/* !CAN_DO_SCHEDSTRUCT */


#endif	/* DYNAMIC_PROCS */

