/*
 *	cifasm.S -- C interface for CCSP, asm bits
 *	Copyright (C) 2002-2005 Fred Barnes <frmb@kent.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.
 */

#ifdef HOSTOS_CYGWIN
#define CIFCCSP_WPTR		_cifccsp_wptr
#define CIFCCSP_SAVEVAR		_cifccsp_savevar
#define OCCSTUB_ENTRY_POINT	_occstub_entry_point
#define OCCSTUB_FORK_ENTRY	_occstub_fork_entry
#define OCCSTUB_EXTERNAL_CALL	_occstub_external_call
#define OCCSTUB_BLOCKING_CALL	_occstub_blocking_call
#define OCCSTUB_BLOCKING_KILLCALL	_occstub_blocking_killcall

#else	/*; !HOSTOS_CYGWIN */
#define CIFCCSP_WPTR		cifccsp_wptr
#define CIFCCSP_SAVEVAR		cifccsp_savevar
#define OCCSTUB_ENTRY_POINT	occstub_entry_point
#define OCCSTUB_FORK_ENTRY	occstub_fork_entry
#define OCCSTUB_EXTERNAL_CALL	occstub_external_call
#define OCCSTUB_BLOCKING_CALL	occstub_blocking_call
#define OCCSTUB_BLOCKING_KILLCALL	occstub_blocking_killcall

#endif	/*; !HOSTOS_CYGWIN */

#define PROCESS_OFFSET_ADJUST	8		/*; in words! */

/*{{{;  some macros (sort of)*/
#define CIF_SAVE_OCCAM_STATE \
	movl	%esp, 24(%ebp);	\
	movl	%esi, 32(%ebp);	\
	movl	%edi, 36(%ebp)
#define CIF_RESTORE_OCCAM_STATE \
	movl	24(%ebp), %esp; \
	movl	32(%ebp), %esi; \
	movl	36(%ebp), %edi
#define CIF_RESTORE_C_STATE \
	movl	16(%ebp), %esp; \
	movl	28(%ebp), %eax; \
	movl	%ebp, CIFCCSP_WPTR
#define CIF_SAVE_C_STATE \
	movl    CIFCCSP_WPTR, %ebp; \
	movl	$0, CIFCCSP_WPTR; \
	movl    %esp, 16(%ebp); \
	movl    $0f, 28(%ebp)

/*}}};*/

/*{{{;  occstub_entry_point */
	.align 4
	.globl OCCSTUB_ENTRY_POINT
OCCSTUB_ENTRY_POINT:

	/*; ebp has WS pointer */

	CIF_SAVE_OCCAM_STATE
	CIF_RESTORE_C_STATE

	movl	$0, %ebp
	call	*%eax			/*; jump into the C world */

	/*; we come out here when returning from the C function */
	movl	CIFCCSP_WPTR,%ebp

	CIF_RESTORE_OCCAM_STATE

	movl	$0, CIFCCSP_WPTR

	/*; oki, sanity -- if we were started with ProcPar(), this will be NULL */
	cmpl	$0,40(%ebp)
	je	0f
	pushl	40(%ebp)		/*; address of occam process which started us */
	movl	$0, 40(%ebp)		/*; clear it out */
	call	_X_runp			/*; resume suspend process */
	call	_X_stopp		/*; stop us inside the run-time kernel */
0:
	pushl	44(%ebp)		/*; address of parent process doing ProcPar */
	call	_X_endp

/*}}};*/
/*{{{;  occstub_fork_entry*/
	.align 4
	.globl OCCSTUB_FORK_ENTRY
OCCSTUB_FORK_ENTRY:

	/*; ebp has WS pointer */

	CIF_SAVE_OCCAM_STATE
	CIF_RESTORE_C_STATE

	movl	$0, %ebp
	call	*%eax			/*; jump into the C world */

	/*; we come out here when returning from the C function */
	movl	CIFCCSP_WPTR,%ebp

	CIF_RESTORE_OCCAM_STATE

	movl	$0, CIFCCSP_WPTR

	/*; when this returns, there is no parent process (and no fork barrier here)  */
	/*; oki, sanity -- if we were started with ProcPar(), this will be NULL */

	/*; we have to free the C stack if it was allocated initially */
	cmpl	$0,20(%ebp)
	je	0f
	pushl	20(%ebp)
	call	_X_mrelease
0:

	pushl	$-PROCESS_OFFSET_ADJUST
	call	_X_mreleasep
	
/*}}};*/
/*{{{;  occstub_resume_point */
	.align 4
	.globl occstub_resume_point
occstub_resume_point:

	/*; ebp has WS pointer */

	CIF_SAVE_OCCAM_STATE
	CIF_RESTORE_C_STATE
	jmp	*%eax			/*; go there directly */
/*}}}*/
/*{{{;  occstub_resume_oneval */
	.align 4
	.globl occstub_resume_oneval
occstub_resume_oneval:

	/*; ebp has WS pointer */
	popl	%ebx			/*; grab returned value from C stack */

	CIF_SAVE_OCCAM_STATE
	CIF_RESTORE_C_STATE
	jmp	*%eax			/*; go directly */

/*}}};*/
#ifdef KROC_USES_PTHREADS
/*{{{;  occstub_external_call*/
/*;
 *;	this is called in a CIF local stack, that looks something like:
 *;	    | program context |
 *;         | args            |
 *;	    | local-address   |
 *;	    | return-address  |  <-- esp
 *;	   [| local-storage   |]             (for EBP,EAX..EDX,ESI,EDI,-) -- 7 words
 *;
 *;	the contents between "&args" and "local-address" are copied onto the
 *;	main KRoC stack, before calling the external function.  The function
 *;	to call is always the first argument, so we place esp just above it
 *;	before calling.
 *;
 *;	note: this is currently only used when posix threads support is enabled
 */
	.align 4
	.globl OCCSTUB_EXTERNAL_CALL
OCCSTUB_EXTERNAL_CALL:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%eax
	pushl	%ebx
	pushl	%ecx
	pushl	%edx
	pushl	%esi
	pushl	%edi
	pushl	$0			/*; round up to 8 words (32,0x20 bytes) */

	movl	24(%esp), %ecx		/*; load high stack address */
	subl	%esp, %ecx		/*; get required size in ecx */
	addl	$0x20, %ecx		/*; adjust for local stack usage */

	cmp	$64, %ecx
	jb	0f
	movl	$64, %ecx
0:

	movl	CIFCCSP_WPTR, %eax
	movl	24(%eax), %eax		/*; get occam-kernel stack ptr */
	movl	%esp, %esi
	addl	$0x20, %esi		/*; adjust for local stack usage */

	movl	CIFCCSP_WPTR, %ebx
	movl	%ebx, CIFCCSP_SAVEVAR	/*; save it */
	movl	%esp, 16(%ebx)		/*; save our stack-pointer in the cifccsp_wptr struct */
	movl	$0, CIFCCSP_WPTR

	xchg	%eax, %esp		/*; switch stacks */

	subl	%ecx, %esp		/*; adjust for copy */
	subl	$32, %esp		/*; plus some slack */
	movl	%esp, %edi		/*; where the argumements are copied to */

	rep movsb			/*; copy */

	movl	8(%esp),%ebx		/*; load address of function */
	addl	$12,%esp		/*; move stack-pointer up to the first user-arg */
	call	*%ebx			/*; call function */

	movl	CIFCCSP_SAVEVAR, %ebx	/*; recover saved CIFCCSP_WPTR */
	movl	%ebx, CIFCCSP_WPTR	/*; put back */
	movl	16(%ebx), %eax		/*; recover saved stack-pointer */

	xchg	%eax, %esp		/*; switch stacks */

	addl	$4, %esp
	popl	%edi
	popl	%esi
	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%eax

	popl	%ebp

	ret

/*}}};*/
#endif
/*{{{;  occstub_blocking_entry*/
/*;
 *;	this is called in a blocking syscall thread's stack, that looks like:
 *;	    | bsyscall context |
 *;	    | cstack-addr      |
 *;	    | return-address   |  <-- esp
 *;        [| saved regs       |]              (EBP,EAX..EDX,ESI,EDI)
 *;
 *;	where "cstack-addr" points at a stack-structure of:
 *;         | args            |
 *;	    | func            |
 *;	    | local-address   |   <--  cstack-addr
 *;
 *;	this adjusts the local stack-pointer, copies the arguments then calls
 *;	the user-function, returning in the normal way.
 */
	.align 4
	.globl occstub_blocking_entry
occstub_blocking_entry:

	pushl	%ebp
	movl	%esp, %ebp

	pushl	%eax
	pushl	%ebx
	pushl	%ecx
	pushl	%edx
	pushl	%esi
	pushl	%edi

	movl	32(%esp), %eax		/*; load address of CIF stack vars */
	movl	0(%eax), %ecx		/*; load high stack address */
	subl	%eax, %ecx		/*; get required size in ecx */

	cmp	$64, %ecx
	jb	0f
	movl	$64, %ecx
0:

	subl	$64, %esp		/*; advance stack-pointer 16 words */

	movl	%esp, %edi
	movl	%eax, %esi
	rep movsb			/*; copy args from "local-address" */

	/*; 0(%esp) now contains "local-address" argument */
	movl	4(%esp), %ebx
	addl	$8, %esp
	call	*%ebx			/*; do it! */

	addl	$56,%esp

	popl	%edi
	popl	%esi
	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%eax

	popl	%ebp

	ret
/*}}}*/
/*{{{;  occstub_blocking_call*/
/*;
 *;	this is called in a CIF local stack, that looks something like:
 *;	    | program context |
 *;         | args            |
 *;	    | local-address   |
 *;	    | return-address  |  <-- esp
 *;	   [| local-storage   |]             (for EBP,EAX..EDX,ESI,EDI,-) -- 7 words
 *;
 *;	the X_b_dispatch entry-point is called as normal, passing the address
 *;	of where local-address lives.  "occstub_blocking_entry" is used to
 *;	copy params and make the actual call.  We have to do stuff separately
 *;	here to handle the var-args business correctly.
 *;
 */
 	.align 4
	.globl OCCSTUB_BLOCKING_CALL
OCCSTUB_BLOCKING_CALL:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%eax
	pushl	%ebx
	pushl	%ecx
	pushl	%edx
	pushl	%esi
	pushl	%edi
	pushl	$0			/*; round up to 8 words (32,0x20 bytes) */

	leal	36(%esp), %eax
	CIF_SAVE_C_STATE
	CIF_RESTORE_OCCAM_STATE
	pushl	%eax			/*; stack-address of "local-address" */
	pushl	$occstub_blocking_entry
	pushl	$occstub_resume_point
	jmp	_X_b_dispatch

0:
	addl	$4, %esp
	popl	%edi
	popl	%esi
	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%eax

	popl	%ebp

	ret
/*}}}*/
/*{{{;  occstub_blocking_killcall*/
/*;
 *;	this is called in a CIF local stack, that looks something like:
 *;	    | program context |
 *;         | args            |
 *;	    | local-address   |
 *;	    | return-address  |  <-- esp
 *;
 *;	the b_x_dispatch entry-point is called as normal, passing the address
 *;	of where local-address lives.  "occstub_blocking_entry" is used to
 *;	copy params and make the actual call.  We have to do stuff separately
 *;	here to handle the var-args business correctly.
 *;
 */
	.align 4
	.globl OCCSTUB_BLOCKING_KILLCALL
OCCSTUB_BLOCKING_KILLCALL:
	pushl	%ebp
	movl	%esp, %ebp

	pushl	%eax
	pushl	%ebx
	pushl	%ecx
	pushl	%edx
	pushl	%esi
	pushl	%edi
	pushl	$0			/*; round up to 8 words (32,0x20 bytes) */

	leal	36(%esp), %eax
	CIF_SAVE_C_STATE
	CIF_RESTORE_OCCAM_STATE
	pushl	%eax			/*; stack-address of "kill-channel-pointer" */
	pushl	$occstub_blocking_entry
	pushl	$occstub_resume_point
	jmp	_X_bx_dispatch
0:
	addl	$4, %esp
	popl	%edi
	popl	%esi
	popl	%edx
	popl	%ecx
	popl	%ebx
	popl	%eax

	popl	%ebp
	ret
/*}}}*/

