/*
 *  linux/kernel/sys_call.S
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 *
 * 680x0 support by Hamish Macdonald
 *
 */

/*
 * sys_call.S  contains the system-call and fault low-level handling routines.
 * This also contains the timer-interrupt handler, as well as all interrupts
 * and faults that can result in a task-switch.
 *
 * NOTE: This code handles signal-recognition, which happens every time
 * after a timer-interrupt and after each system call.
 *
 * Stack layout in '_ret_from_sys_call':
 *	ptrace needs to have all regs on the stack.
 *	if the order here is changed, it needs to be
 *	updated in fork.c:sys_fork, signal.c:do_signal,
 *	ptrace.c and ptrace.h
 *
 *	This allows access to the syscall arguments in registers d1-d5
 *
 *	 0(sp) - d1
 *	 4(sp) - d2
 *	 8(sp) - d3
 *	 C(sp) - d4
 *	10(sp) - d5
 *	14(sp) - d6
 *	18(sp) - d7
 *	1C(sp) - a0
 *	20(sp) - a1
 *	24(sp) - a2
 *	28(sp) - a3
 *	2C(sp) - a4
 *	30(sp) - a5
 *	34(sp) - a6
 *	38(sp) - d0
 *	3C(sp) - usp
 *	40(sp) - orig_d0
 *	44(sp) - sr
 *	46(sp) - pc
 *	4A(sp) - format & vector
 */

OFF_D1		= 0x00
OFF_D2		= 0x04
OFF_D3		= 0x08
OFF_D4		= 0x0C
OFF_D5		= 0x10
OFF_D6		= 0x14
OFF_D7		= 0x18
OFF_A0		= 0x1C
OFF_A1		= 0x20
OFF_A2		= 0x24
OFF_A3		= 0x28
OFF_A4		= 0x2C
OFF_A5		= 0x30
OFF_A6		= 0x34
OFF_D0		= 0x38
OFF_USP 	= 0x3C
OFF_ORIG_D0	= 0x40
OFF_SR		= 0x44
OFF_PC		= 0x46
OFF_FORMAT	= 0x4A

/*
 * these are offsets into the task-struct.
 */
state		= 0
counter 	= 4
priority	= 8
signal		= 12
blocked		= 16
flags		= 20

ENOSYS = 38

.globl _system_call, _buserr, _trap, _inthandler, _sys_execve
.globl _ret_from_sys_call

.text
.align 4
_buserr:
	subql	#8,sp		| make room for usp and orig_d0
	movel	d0,sp@-
	moveml	d1-a6,sp@-
	movec	usp,a0
	movel	a0,sp@(OFF_USP)
	moveq	#-1,d0
	movel	d0,sp@(OFF_ORIG_D0)     | stack frame is NOT for syscall
	movel	sp,sp@- 	| stack frame pointer argument
	bsr	_buserr_c
	addql	#4,sp
	bra	_ret_from_sys_call

.align 4
_trap:
	subql	#8,sp		| make room for usp and orig_d0
	movel	d0,sp@-
	moveml	d1-a6,sp@-
	movec	usp,a0
	movel	a0,sp@(OFF_USP)
	moveq	#-1,d0
	movel	d0,sp@(OFF_ORIG_D0)     | stack frame is NOT for syscall
	movel	sp,sp@- 	| stack frame pointer argument
	bsr	_trap_c
	addql	#4,sp
	bra	_ret_from_sys_call

.align 4
reschedule:
	movel	#_ret_from_sys_call,sp@-
	jmp	_schedule

.align 4
_system_call:
	subql	#8,sp			| make room for usp, orig_d0 and d0
	subql	#4,sp
	moveml	d1-a6,sp@-		| save registers
	movec	usp,a0
	movel	a0,sp@(OFF_USP)         | save User Stack Pointer
	movel	d0,sp@(OFF_ORIG_D0)     | save original D0

	movel	#-ENOSYS,sp@(OFF_D0)    | default return value
	cmpl	_NR_syscalls,d0
	bgt	_ret_from_sys_call
	movel	_current,a0
	andil	#0x20,a0@(flags)        | PF_TRACESYS
	beq	1f
	pea	0
	movel	a0,sp@-
	pea	5			| SIGTRAP
	bsr	_send_sig
	addql	#8,sp
	addql	#4,sp
	bsr	_schedule
	movel	sp@(OFF_ORIG_D0),d0
1:	lea	_sys_call_table,a0
	movel	a0@(d0:l:4),a0
	jsr	a0@
	movel	d0,sp@(OFF_D0)          | save the return value
	movel	_current,a0
	andil	#0x20,a0@(flags)        | PF_TRACESYS
	beq	_ret_from_sys_call
	tstl	a0@(signal)
	bne	_ret_from_sys_call	| ptrace would clear signal
	pea	0
	movel	a0,sp@-
	pea	5			| SIGTRAP
	bsr	_send_sig
	addql	#8,sp
	addql	#4,sp
	bsr	_schedule

_ret_from_sys_call:
1:	tstl	_need_resched
	bne	reschedule
	movel	_current,a0
	cmpl	#_task,a0			| task[0] cannot have signals
	beq	2f
	moveq	#0,d0
	cmpl	a0@(state),d0              | state
	bne	reschedule
	cmpl	a0@(counter),d0            | counter
	beq	reschedule
	movel	a0@(blocked),d0
	notl	d0
	andl	a0@(signal),d0
	bne	signal_return
2:	movel	sp@(OFF_USP),a0
	movec	a0,usp
	moveml	sp@+,d1-a6
	movel	sp@+,d0 	| restore d0
	addql	#8,sp		| skip the usp and orig_d0
	rte
.align 4
signal_return:
	movel	sp,sp@-
	movel	a0@(blocked),sp@-
	bsr	_do_signal
	addql	#8,sp
	movel	sp@(OFF_USP),a0 | return to user mode
	movec	a0,usp
	moveml	sp@+,d1-a6
	movel	sp@+,d0 	| restore d0
	addql	#8,sp		| skip the usp and orig_d0
	rte


/*
** This is the main interrupt handler, responsible for calling process_int()
*/

_inthandler:
	moveml	a0-a7/d0-d7,sp@-	|  save registers

	movew	sp@(70),d0              |  put exception # in d0
	andil	#0xfff,d0		|  mask out format nybble

	subil	#0x60,d0		|  convert from vector offset...
	divul	#4,d0			|  ...to IRQ number

	clrl	d1
	movew	sp@(64),d1              |  put old status register in d1

	movel	d1,sp@- 		|  put old status register on stack
	movel	d0,sp@- 		|  put IRQ # on stack
	jbsr	_process_int		|  process the IRQ
	addql	#8,sp			|  pop parameters off stack

	/*
	 * check if returning to user mode.
	 * if so, pop stuff off interrupt stack, switch
	 * from interrupt mode to master mode, push
	 * stack frame on kernel stack and jump to ret_from_sys_call
	 */

	moveb	sp@(70),d0              | get format word from int stack
	andb	#0xf0,d0		| mask off format
	cmpb	#0x10,d0		| check if throwaway frame
	bne	1f			| nope, interrupts stacked, rte
	movec	msp,a0			| get the master stack pointer
	btst	#0x5,a0@		| supervisor bit set in saved SR?
	bne	1f			| yup, was in kernel, rte

	moveml	sp@+,a0-a7/d0-d7	| restore registers
	addql	#8,sp			| throw away throwaway stack frame
	oriw	#0x1000,sr		| set master bit in SR
	andiw	#0xf7ff,sr		| allow interrupts
	subql	#8,sp			| make room for usp and orig_d0
	moveml	d0,sp@- 		| save registers as for syscall
	moveml	d1-a6,sp@-
	moveq	#-1,d0
	movel	d0,sp@(OFF_ORIG_D0)     | indicate stack frame not for syscall
	movec	usp,a0
	movel	a0,sp@(OFF_USP)         | save usp
	bra	_ret_from_sys_call	| deliver signals, reschedule etc..
1:
	moveml	sp@+,a0-a7/d0-d7	|  restore registers
	rte				|  return from exception


.align 4
_sys_execve:
	movel sp,d0			| save stack frame address
	addql #4,d0			|  make allowance for return address
	movel d0,sp@-
	bsr   _do_execve
	addql #4,sp
	rts
