/*
 *  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
 *
 */

#include "assyms.h"

/*
 * 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_exception':
 *	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) - stack adjustment
 *	46(sp) - sr
 *	48(sp) - pc
 *	4C(sp) - format & vector
 */

LOFF_D1		= 0x00
LOFF_D2		= 0x04
LOFF_D3		= 0x08
LOFF_D4		= 0x0C
LOFF_D5		= 0x10
LOFF_D6		= 0x14
LOFF_D7		= 0x18
LOFF_A0		= 0x1C
LOFF_A1		= 0x20
LOFF_A2		= 0x24
LOFF_A3		= 0x28
LOFF_A4		= 0x2C
LOFF_A5		= 0x30
LOFF_A6		= 0x34
LOFF_D0		= 0x38
LOFF_USP 	= 0x3C
LOFF_ORIG_D0	= 0x40
LOFF_STKADJ	= 0x44
LOFF_SR		= 0x46
LOFF_PC		= 0x48
LOFF_FORMAT	= 0x4C


LCF_MASK 	= 0x0001

LENOSYS = 38

.globl _system_call, _buserr, _trap
.globl _ret_from_exception, _inthandler, _hsync_handler

.text
.align 4
_buserr:
	clrw	sp@-			| stack adjustment
	subql	#8,sp			| make room for usp and orig_d0
	movel	d0,sp@-
	moveml	d1-a6,sp@-
	movec	usp,a0
	movel	a0,sp@(LOFF_USP)
	moveq	#-1,d0
	movel	d0,sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
					| signifies that the stack frame
					| is NOT for syscall

	movel	sp,sp@- 		| stack frame pointer argument
	bsr	_buserr_c
	addql	#4,sp
	bra	_ret_from_exception

.align 4
_trap:
	clrw	sp@-			| stack adjustment
	subql	#8,sp			| make room for usp and orig_d0
	movel	d0,sp@-
	moveml	d1-a6,sp@-
	movec	usp,a0
	movel	a0,sp@(LOFF_USP)
	moveq	#-1,d0
	movel	d0,sp@(LOFF_ORIG_D0)    | a -1 in the ORIG_D0 field
					| signifies that the stack frame
					| is NOT for syscall
	movel	sp,sp@- 		| stack frame pointer argument
	bsr	_trap_c
	addql	#4,sp
	bra	_ret_from_exception
.align 4
reschedule:
	movel	#_ret_from_exception,sp@-
	jmp	_schedule

.align 4
_system_call:
	clrw	sp@-			| stack adjustment
	subql	#8,sp			| make room for usp and orig_d0
	movel	#-LENOSYS,sp@-		| default return value in D0
	moveml	d1-a6,sp@-		| save registers
	movec	usp,a0
	movel	a0,sp@(LOFF_USP)        | save User Stack Pointer
	movel	d0,sp@(LOFF_ORIG_D0)    | save original D0 (syscall #)

# debug
	btst	#4,sp@(LOFF_SR)
	bne	1f
L1:	bra	L1
/*	pea	Lmsg
	jbsr	_panic
Lmsg:
	.asciz "coming from user mode, not on master stack\n"
	.even
*/
1:
# end debug

	cmpl	_NR_syscalls,d0
	bgt	_ret_from_exception
	movel	_current,a0
	andw	#~LCF_MASK,sp@(LOFF_SR) | assume syscall success
	clrl	a0@(LTS_ERRNO)
	btst	#5,a0@(LTS_FLAGS+3)     | PF_TRACESYS
	bnes	1f
	lea	_sys_call_table,a0
	movel	a0@(d0:l:4),a0
	jbsr	a0@
	movel	d0,sp@(LOFF_D0)         | save the return value
	movel	_current,a0

#
# This commented out section should be used when system calls
# set current->errno to indicate an error, rather than just
# returning a negative value.
#
#	movel	a0@(LTS_ERRNO),d1
#	movel	d0,d1
#	negl	d1
#	beq	2f
#	movel	d1,sp@(LOFF_D0)
#	oriw	#LCF_MASK,sp@(LOFF_SR)    | set carry to indicate error

#
# This section sets the carry bit if the result is negative.
# i.e. assume a negative return value is an error...
#
	tstl	d0
	bpl	2f
	negl	d0
	movel	d0,sp@(LOFF_D0)
	orw	#LCF_MASK,sp@(LOFF_SR)  | set carry to indicate error
	bra	_ret_from_exception
1:	jbsr	_syscall_trace
	movel	sp@(LOFF_ORIG_D0),d0
	lea	_sys_call_table,a0
	movel	a0@(d0:l:4),a0
	jbsr	a0@
	movel	d0,sp@(LOFF_D0)         | save the return value
	movel	_current,a0
#
# This commented out section should be used when system calls
# set current->errno to indicate an error, rather than just
# returning a negative value.
#
#	movel	a0@(LTS_ERRNO),d1
#	movel	d0,d1
#	negl	d1
#	beq	2f
#	movel	d1,sp@(LOFF_D0)
#	orw	#LCF_MASK,sp@(LOFF_SR)  | set carry to indicate error

#
# This section sets the carry bit if the result is negative.
# i.e. assume a negative return value is an error...
#
	tstl	d0
	bpl	2f
	negl	d0
	movel	d0,sp@(LOFF_D0)
	orw	#LCF_MASK,sp@(LOFF_SR)  | set carry to indicate error
2:	jbsr	 _syscall_trace

_ret_from_exception:
1:	tstl	_need_resched
	bne	reschedule
	movel	_current,a0
	cmpl	#_task,a0		| task[0] cannot have signals
	beq	2f
	moveq	#0,d0
	cmpl	a0@(LTS_STATE),d0       | state
	bne	reschedule
	cmpl	a0@(LTS_COUNTER),d0     | counter
	beq	reschedule
	movel	a0@(LTS_BLOCKED),d0
	movel	d0,d1			| save blocked in d1 for signal handling
	notl	d0
	andl	a0@(LTS_SIGNAL),d0
	bne	Lsignal_return
2:	movel	sp@(LOFF_USP),a0
	movec	a0,usp
	moveml	sp@+,d1-a6
	movel	sp@+,d0 	| restore d0
	addql	#8,sp		| skip the usp and orig_d0
	addw	sp@+,sp 	| add the stack adjustment
	rte
Lsignal_return:
	movel	sp,sp@-
	movel	d1,sp@-
	bsr	_do_signal
	addql	#8,sp
	movel	sp@(LOFF_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
	addw	sp@+,sp 	| add the stack adjustment
	rte


_hsync_handler: rte

/*
** This is the main interrupt handler, responsible for calling process_int()
*/
_inthandler: 
	moveml	a0-a1/d0-d1,sp@-	|  save registers

	addql	#1,_intr_count
	movew	sp@(22),d0              |  put exception # in d0
	andil	#0xfff,d0		|  mask out format nybble

	subil	#0x60,d0		|  convert from vector offset...
	lsrl	#2,d0			|  ...to IRQ number

	beq	Lspurious		|  if zero, spurious interrupt 
	pea	sp@			|  push interrupt stack frame address
	movel	d0,sp@- 		|  put IRQ # on stack
	jbsr	_process_int		|  process the IRQ 
	addql	#8,sp			|  pop parameters off stack

	subql	#1,_intr_count



	/* maybe process software interrupts */
	tstl	_intr_count
	bne	1f

	movel	_bh_mask,d0
	andl	d0,_bh_active
	beq	1f

	movew	sr,sp@-
	addql	#1,_intr_count
	andiw	#0xf8ff,sr		| allow interrupts
	jbsr	_do_bottom_half
	movew	sp@+,sr
	subql	#1,_intr_count
1:
	/*
	 * 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_exception
	 */
	moveb	sp@(22),d0                | get format word from int stack
	andb	#0xf0,d0		| mask off format
	cmpb	#0x10,d0		| check if throwaway frame
	beq	Lnotstacked		| if, not: interrupts stacked, rte
	moveml	sp@+,a0-a1/d0-d1	| restore registers
	rte
Lnotstacked:
	movec	msp,a0			| get the master stack pointer
	btst	#0x5,a0@		| supervisor bit set in saved SR?
	moveml	sp@+,a0-a1/d0-d1	| restore registers
	beq	Lnotkernel		| if in kernel, rte
	rte
Lnotkernel:
	addql	#8,sp			| throw away throwaway stack frame
	oriw	#0x1000,sr		| set master bit in SR (sp is now msp)
	andiw	#0xf8ff,sr		| allow interrupts
	clrw	sp@-			| stack adjustment
	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@(LOFF_ORIG_D0)    | indicate stack frame not for syscall
	movec	usp,a0
	movel	a0,sp@(LOFF_USP)        | save usp
	bra	_ret_from_exception	| deliver signals, reschedule etc..
Lspurious:
	addql	#1,_num_spurious	| increment the counter of spurious
					| interrupts.
	moveml	sp@+,a0-a1/d0-d1	| restore registers
	rte				| return from exception
