/*
 * $Id$
 *
 * This file has been modified as part of the FreeMiNT project. See
 * the file Changes.MH for details and dates.
 *
 *
 * Copyright 1992 Eric R. Smith
 * Copyright 1992,1993,1994 Atari Corporation
 * All rights reserved.
 *
 *
 * Interface for system calls
 *
 * The following entry points are defined:
 *
 * _mint_dos:	entry point for GEMDOS calls (trap #1)
 * _mint_bios:	entry point for BIOS calls (trap #13)
 * _mint_xbios: entry point for XBIOS calls (trap #14)
 * _lineA0:	calls the line A initialize routine
 * _call_aes:	calls the GEM AES
 * _do_usrcall: calls a user supplied function (e.g. for Supexec), with
 *              arguments supplied from global variables usrarg1, usrarg2, etc.
 * _callout:	calls an external function, after first saving all registers,
 *              and restores the registers afterward
 *
 * external variables referenced:
 *
 * _bios_tab, _bios_max:
 *    table of entry points for BIOS routines, max # of routines
 * _xbios_tab, _xbios_max:
 *    ditto for XBIOS
 * _dos_tab, _dos_max:
 *    ditto for GEMDOS
 * _curproc:
 *    pointer to current process table entry, and hence to save area for
 *    context (this is always the first entry in the PROC table).
 *
 * _bconbuf, _bconbsiz, _bconbdev:
 *    256 byte buffer for Bconout() output. If _bconbsiz is non-zero,
 *    there are that many bytes in _bconbuf waiting to be flushed. The
 *    output is for device _bconbdev.
 *
 * The function enter_gemdos() is called while entering
 * the kernel and leave_kernel() while leaving the kernel. These are
 * responsible for managing appropriate semaphores and pointers.
 *
 * we also call certain BIOS functions directly if these are known not to
 * require saving/restoring of context
 *
 * Best view in 800x600 ;-)
 *
 */

#include "magic/magic.i"

	.text

	.globl	_mint_bios,_mint_xbios
	.globl	_mint_dos
	.globl	_old_dos,_old_bios,_old_xbios
	.globl	_build_context
	.globl	_restore_context
	.globl	_proc_clock		// controls process' allocation of CPU time
	.globl	_enter_gemdos
	.globl	_leave_kernel
	.globl	_in_kernel
	.globl	_preempt
	.globl	_unwound_stack
	.globl	_check_sigs

	.globl	_old_trap2
	.globl	_mint_trap2
	.globl	_gdos_version,_vdi_handler,_aes_handler

	.globl	_curproc
	.globl	_bios_tab,_bios_max
	.globl	_xbios_tab,_xbios_max,_old_xbios
	.globl	_dos_tab,_dos_max

	.globl	_bconbuf,_bconbsiz,_bconbdev
	.globl	_bflush

	.globl	_sys_b_ubconstat,_do_bconin,_sys_b_ubcostat,_sys_b_kbshift

	.globl	_secure_mode,_falcontos
	
	.globl	_stop

// ***********************
// * GEMDOS trap handler *
// ***********************
//
// NOTICE: MiNT *no longer ever* traps to ROM GEMDOS, because even
//         the TOSFS *jumps* (not traps) there. So these two lines
//         below only waste space and CPU time. And btw I really hate
//         doing same changes twice! (draco)

	dc.l	0x58425241		// XBRA
	dc.l	0x4d694e54		// MiNT
_old_dos:
	dc.l	0
_mint_dos:

#ifdef SYSCALL_REENTRANT
//
// see at symbol reentrant_trap for a detailed explanation
//
	tst.w	_in_kernel
	beq.s	norm_gemdos

	movea.l	sp,a0
	movem.l	d2-d7/a2-a6,-(sp)
	lea	_dos_tab,a5
	move.w	_dos_max,d5
	bra	reentrant_trap

norm_gemdos:

#else

// Non-reentrant syscall handler
//
// if we are in kernel we simply jump to the old vector
// this allow direct trapping from kernel (not done from the
// kernel itself anymore)

	tst.w	_in_kernel
	bne.s	rom_gemdos

#endif

	clr.w	-(sp)			// no frame format needed
// NOTE: FOR NOW, WE PRESERVE A0 ACROSS GEMDOS CALLS. THIS WILL CHANGE
// SOMEDAY, DON'T RELY ON IT!!!
	//move.l	_curproc,d0		// note: preserve all regs but d0
	//addq.l	#4,d0			// for compatibility

	//move.l	d0,-(sp)		// push pointer to syscall context save
	move.l	_curproc,-(sp)
	addq.l	#4,(sp)
	
	jsr	_build_context
	lea	_dos_tab,a5		// set syscall_tab
	move.w	_dos_max,d5		// set syscall_max
//
// copy parameters onto process stack. a0 and a1 were set by _build_context
//
	move.l	_curproc,a0
	move.l	(a0),sp 		// this puts us in our private stack
#ifdef NEW_SYSCALL
	move.w (a1),d1
	cmp.w	_dos_max,d1
	bcc	gemdos
// offset 0 is func pointer, 4 is nr of args, 6 is their size
nospr:	move.w	6(a5,d1.w*8),d1		// fetch size in bytes
	lsr.w	#1,d1			// we need size in words
cparg:	move.w	(a1,d1.w*2),-(sp)
	dbra.w	d1,cparg
#else
	move.l	32(a1),-(sp)		// a1 was set by build_context
	move.l	28(a1),-(sp)
	move.l	24(a1),-(sp)
	move.l	20(a1),-(sp)
	move.l	16(a1),-(sp)
	move.l	12(a1),-(sp)
	move.l	8(a1),-(sp)
	move.l	4(a1),-(sp)
	move.l	(a1),-(sp)
#endif
noargs:	jsr	_enter_gemdos		// set up vectors appropriately
#ifdef DEBUG_INFO
	cmpi.w	#4201,(sp)		// special case Sys_Printf
	bne.s	no_sys_printf
	clr.w	(sp)			// manipulate function number
	lea	_sys_mon_tab,a5		// tab
	move.w	#1,d5			// and max
no_sys_printf:
#endif
	bra	_syscall

#ifndef SYSCALL_REENTRANT
rom_gemdos:
	move.l	_old_dos.w(pc),-(sp)
	rts
#endif

// **********************
// * XBIOS trap handler *
// **********************
//
// Kludge for Setscreen: under Falcon TOS, this
// calls a GEMDOS function (Srealloc) and so
// we must *not* change any of the vectors!!
// Same kludge for Dsp_LoadProg() and Dsp_LodToBinary.
// They call GEMDOS *a lot* (Fgetdta, Fsetdta, Malloc, Mfree...)

	dc.l	0x58425241		// XBRA
	dc.l	0x4d694e54		// MiNT
_old_xbios:
	dc.l	0
_mint_xbios:

#ifdef SYSCALL_REENTRANT
//
// see at symbol reentrant_trap for a detailed explanation
//
	tst.w	_in_kernel
	beq	norm_xbios

	movea.l	sp,a0
	movem.l	d2-d7/a2-a6,-(sp)
	lea	_xbios_tab,a5
	move.w	_xbios_max,d5
	bra	reentrant_trap

norm_xbios:
#else
	tst.w	_in_kernel
	bne	LX_rom
#endif

	btst	#5,(sp) 		// test for user/super mode
	beq.s	LX_usr
#ifdef ONLY030
	lea	8(sp),a1
#else
	lea	6(sp),a1		// supervisor mode: args on stack
	tst.w	(0x59e).w		// test longframe
	beq.s	LX_check
	addq.w	#2,a1			// stack is a bit bigger
#endif
	bra.s	LX_check
LX_usr:
	move.l	usp,a1			// user mode: args on user stack

// XBIOS is for root, not users - start checking access privileges.

LX_check:
	move.l	_curproc,a0
	cmp.w	#17,(a1)		// no need to forbid Random()
	beq.s	pok
	cmp.w	#21,(a1)		// Cursconf() gets converted to ioctl()
	beq.s	pok
	cmp.w	#38,(a1)		// Supexec() has own protection code
	beq.s	pok
	tst.w	_secure_mode		// the rest is subject to check
	beq.s	pok
	tst.w	P_EUID(a0)		// curproc->euid, is this enough?
	bne.s	ret
pok:
	tst.w	_falcontos		// on TOS 4.0 - 4.04...
	beq.s	sk
	cmp.w	#5,(a1) 		// ...check for Setscreen command
	beq.s	LX_rom			// no -- fall through

// DSP XBIOS is Falcon030 only - am I right?

	cmp.w	#108,(a1)		// Dsp_LoadProg()
	beq.s	LX_rom
	cmp.w	#111,(a1)		// Dsp_LodToBinary()
	beq.s	LX_rom

// _curproc is already in a0 here

sk:	bset	#0x07,_in_kernel
	clr.w	-(sp)			// no frame format needed
	pea	4(a0)			// push pointer to syscall context save
	jsr	_build_context
	lea	_xbios_tab,a5		// set syscall_tab
	move.w	_xbios_max,d5		// set syscall_max
	
//
// copy parameters onto process stack. a0 and a1 were set by _build_context
//
	move.l	_curproc,a0
	move.l	(a0),sp 		// this puts us in our private stack
	move.l	24(a1),-(sp)		// a1 was set by build_context
	move.l	20(a1),-(sp)
	move.l	16(a1),-(sp)
	move.l	12(a1),-(sp)
	move.l	8(a1),-(sp)
	move.l	4(a1),-(sp)
	move.l	(a1),-(sp)
	//bset	#0x07,_in_kernel
	bra	_syscall

// For Dsp_LodToBinary(), Dsp_LoadProg()
// jump directly to the ROM vector -- this avoids all hazards.

LX_rom:	move.l	_old_xbios.w(pc),a0	// jump to ROM
	jmp	(a0)

ret:	sub.l	a0,a0			// curproc address is our sweet secret
	moveq	#-36,d0 		// return EACCDN
	rte

// *********************
// * BIOS trap handler *
// *********************
//
// Entering the kernel can be very expensive so, we take a short-cut
// if possible -- we try some BIOS functions out, and if they
// succeed without blocking then we're done otherwise, we go
// through the long procedure for entering the kernel
//
// These shortcuts aren't done when we're called in supervisor mode,
// because TOS uses very tiny stacks (smaller than we want) the
// shortcuts operate on the user-supplied ssp, whereas the "full"
// BIOS code works on our (private) system stack
//
// the shortcuts are also turned off if BIOSBUF=n

	dc.l	0x58425241		// XBRA
	dc.l	0x4d694e54		// MiNT
_old_bios:	dc.l	0
_mint_bios:

#ifdef	SYSCALL_REENTRANT
//
// see at symbol reentrant_trap for a detailed explanation
//

	tst.w	_in_kernel
	beq	norm_bios

	movea.l	sp,a0
	movem.l	d2-d7/a2-a6,-(sp)
	lea	_bios_tab,a5
	move.w	_bios_max,d5
	bra	reentrant_trap

norm_bios:

#else
	tst.w	_in_kernel
	bne	rom_bios
#endif

	bset	#0x07,_in_kernel
	tst.w	_bconbdev		// is BIOS buffering on?
	bmi	L_bios			// no, skip all this

	btst	#5,(sp) 		// test for user/super mode
	bne.s	L_bios			// if super, goto L_bios
	tst.w	_proc_clock		// are we about to be preempted?
	beq.s	L_bios

	move.l	usp,a1			// user mode: args on user stack
L_ubios:
	move.w	(a1),d0 		// get command
	cmp.w	#3,d0			// Bconout?
	beq	do_bconout		// yes -- go do it
//
// most of the remaining functions require BIOS vectors to be properly
// set up
	tst.w	_bconbsiz		// is BIOS output waiting?
	bne.s	L_bios			// yes -- do regular code

// test for various BIOS functions
	cmp.w	#1,d0			// Bconstat?
	bne.s	L_00
	move.w	2(a1),-(sp)		// push arg
	jsr	_sys_b_ubconstat
L_1out:
	addq.l	#2,sp
L_0out:
	ori.w	#0x0400,sr		// spl7()
	jsr	_leave_kernel		// does not change d0
	rte				// return to user
L_00:
	cmp.w	#2,d0			// Bconin?
	bne.s	L_01
	move.w	2(a1),-(sp)		// yes; push argument
	jsr	_do_bconin
	addq.w	#2,sp
	cmp.w	#0xdead,d0		// would Bconin block?
	bne.s	L_0out			// no -- we're done
	bra.s	L_bios			// yes -- do the long stuff
L_01:
	cmp.w	#8,d0			// Bcostat?
	bne.s	L_02
	move.w	2(a1),-(sp)		// push device
	jsr	_sys_b_ubcostat		// get status
	bra.s	L_1out
L_02:
	cmp.w	#11,d0			// Kbshift?
	bne.s	L_bios
	move.w	2(a1),-(sp)		// push arg
	jsr	_sys_b_kbshift
	bra.s	L_1out
L_bios:
	clr.w	-(sp)			// no frame format needed
	move.l	_curproc,a0
	pea	4(a0)			// push pointer to syscall context save
	jsr	_build_context
	lea	_bios_tab,a5		// set syscall_tab
	move.w	_bios_max,d5		// set syscall_max
//
// copy parameters onto process stack. a0 and a1 were set by _build_context
//
	move.l	_curproc,a0
	move.l	(a0),sp 		// this puts us in our private stack
	move.l	24(a1),-(sp)		// a1 was set by build_context
	move.l	20(a1),-(sp)
	move.l	16(a1),-(sp)
	move.l	12(a1),-(sp)
	move.l	8(a1),-(sp)
	move.l	4(a1),-(sp)
	move.l	(a1),-(sp)

_syscall:
// Ozk: From now on we preserve cpu register D0.. it seems to be important
//      for XBIOS, since some functions return shorts while other returns
//      longs. And apps apparently have ways of checking this...
//

//
// check here to see if we need to flush the Bconout() buffer
//
	tst.w	_bconbsiz		// characters in buffer?
	beq.s	L_noflush		// no: OK to proceed
//
// watch out, this could cause a context switch
//
	jsr	_bflush 		// flush the buffer

L_noflush:

// Code for ptrace mode PT_SYSCALL
	move.l	_curproc,a0
	tst.l	P_PTRACER(a0)		// is someone tracing us?
	beq.s	pt_ende
	tst.w	P_PTRACEFLAGS(a0)	// are system calls being traced?
	beq.s	pt_ende
	move.w	#5,-(SP)		// move.w #SIGTRAP,-(sp)
	jsr	_stop			// send SIGTRAP to the process
	addq.l	#2,sp
pt_ende:
// end of code for ptrace mode PT_SYSCALL


//
// figure out which routine to call
//
	move.w	(sp),d1
	cmp.w	d5,d1
	bcc	error
#ifdef ONLY030
#ifdef NEW_SYSCALL
	move.l	0(a5,d1.w*8),d1
#else
	move.l	0(a5,d1.w*4),d1 	// d0 = syscall_tab[d0]
#endif
#else
#ifdef NEW_SYSCALL
	add.w	d1,d1
#endif
	add.w	d1,d1
	add.w	d1,d1			// multiply by 4
	move.l	0(a5,d1.w),d1		// d0 = syscall_tab[d0]
#endif
	beq	error			// null entry means invalid call
	addq.w	#2,sp			// pop function number off stack
	move.l	d1,a0
	jsr	(a0)			// go do the call

out:	moveq	#0,d2
out_select:

	move.l	_curproc,a0
	move.l	d0,P_SYSCTXT+C_D0(a0)	// set d0 in the saved context
	move.w	P_SYSCTXT+C_SR(a0),d0	// get saved status register

	tst.l	P_PTRACER(a0)		// check curproc->ptracer, if not set
	beq.s	notrace 		// then no pending trace this ensures
	move.w	d0,d1			// we work with non-MiNT debuggers
	and.w	#0xc000,d1		// are either of the trace bits set
	sne	P_SYSCTXT+C_PTRACE(a0)	// mark as trace pending/not
notrace:
	tst.w	d2			// If D2 set, dont preemtp
	bne.s	nosleep
	tst.w	_proc_clock		// has process exceeded time slice?
	bne.s	nosleep 		// no -- continue
	btst	#13,d0			// caller in supervisor mode?
	bne.s	nosleep 		// yes -- don't interrupt
	tst.w	(0x43e).w		// test floppy disk lock variable
	bne.s	nosleep 		// if locked, can't switch
sleep:
	tst.l	_unwound_stack		// did we unwind sysstack?
	beq.s	noreload1
	move.l	_curproc,a0		// then reload it before
	move.l	(a0),sp 		// doing anything further
noreload1:
	jsr	_preempt		// does a sleep(READY_Q)
	bra.s	nosig

nosleep:
	move.l	P_SIGMASK(a0),d0	// any unmasked signals left pendig?
	not.l	d0
	and.l	P_SIGPENDING(a0),d0
	beq.s	nosig
	tst.l	_unwound_stack		// did we unwind sysstack?
	beq.s	noreload2
	move.l	_curproc,a0		// then reload it before
	move.l	(a0),sp 		// doing anything further
noreload2:
	jsr	_check_sigs
nosig:
	ori.w	#0x0400,sr		// spl7()
	clr.l	_unwound_stack
	jsr	_leave_kernel		// restore vectors (loads a0)
	pea	4(a0)
	jsr	_restore_context	// never returns

rom_bios:
	move.l	_old_bios.w(pc),-(sp)
	rts

//
// bconout special code: on entry, a1 points to the stack the user
// was using. If possible, we just buffer the output until later.
//

do_bconout:
	tst.w	_bconbdev		// is BIOS buffering on?
	bmi	L_bios			// no buffering -- skip this code
	move.w	2(a1),d0		// what device is this for?
	beq	L_bios			// don't buffer the printer
	cmp.w	_bconbdev,d0		// same device as is buffered?
	bne.s	new_dev 		// no -- maybe we can't do this
put_buf:
	move.w	4(a1),d0		// get the character to output
	move.w	_bconbsiz,d1		// get index into buffer table
	cmp.w	#255,d1 		// buffer full?
	beq	L_bios			// yes -- flush it out
	lea	_bconbuf,a0
	add.w	d1,a0
	move.b	d0,(a0) 		// store the character
	addq.w	#1,d1
	move.w	d1,_bconbsiz
	ori.w	#0x0400,sr		// spl7()
	jsr	_leave_kernel		// restore vectors
	moveq.l	#-1,d0			// return character output OK
	rte

new_dev:
	tst.w	_bconbsiz		// characters already in buffer?
	bne	L_bios			// yes: we can't buffer this one
	move.w	d0,_bconbdev		// no: OK, we have a new device
	bra.s	put_buf

//
// we handle errors by calling through to GEMDOS or the BIOS/XBIOS,
// as appropriate, and letting them handle it -- that way, if the underlying
// system has functions we don't know about, they still work
// to figure out which trap we have to call, we use the system call
// table placed in a5 earlier

error:	cmp.l	#_xbios_tab,a5
	bne.s	enosys
	jsr	_trap_14_emu
	bra	out
enosys:	moveq	#-32,d0	// ENOSYS
	bra	out

_vdi_handler:
	dc.l	0
_aes_handler:
	dc.l	0
_gdos_version:
	dc.l	0

// ***********************
// * AES/VDI trap handler *
// ***********************
//
// The VDI intitialization needs to put the GDOS version at symbol
// gdos_version, because this trap handler returns what is at that
// symbol when a handler is installed in vdi_handler
//
	dc.l	0x58425241		// XBRA
	dc.l	0x4d694e54		// MiNT
_old_trap2:
	dc.l	0
_mint_trap2:

#ifdef SYSCALL_REENTRANT
//
// see at symbol reentrant_trap for a detailed explanation
//
	tst.w	_in_kernel
	beq.s	norm_trap2

	movem.l	d1-d7/a0-a6,-(sp)
	lea	((7+7)*4)(sp),a0
	bra	reentrant_trap2

norm_trap2:

#else

// Non-reentrant syscall handler
//
// if we are in kernel we simply jump to the old vector
// this allow direct trapping from kernel (not done from the
// kernel itself anymore)

	tst.w	_in_kernel
	bne	rom_trap2
#endif
	bset	#0x07,_in_kernel
	// The AES/VDI passes parameters in D0/D1, so we do not touch
	// them registers..
	clr.w	-(sp)
	move.l	_curproc,-(sp)
	addq.l	#4,(sp)
	jsr	_build_context

	// A1 set to callers stack by build_context
	move.l	_curproc,a0
	move.l	(a0),sp			// fetch our systemstack
	move.w	6(a0),d0		// D0.w saved by build_context, function code (AES or VDI)
	move.l	8(a0),d1		// AES/VDI pb
	cmp.w	#115,d0			// VDI?
	beq	enter_vdi
	cmp.w	#200,d0			// AES?
	beq	enter_aes
	cmp.w	#201,d0			// appl_yield()?
	beq	enter_aes		// or should this be its own function ?

	// some VDI special cases, must be prepared by any VDI module ..
	cmp.w	#-2,d0			// Query GDOS version
	beq	trap2_gdosvers
	cmp.w	#-1,d0			// This returns the address of the VDI handler
	beq	trap2_vdihandler
	
	// What to do with trap 2 calls we dont know?
	// for now just noting..
trap2_out:
	//moveq	#1,d2
	bra	out_select

	// Call the VDI
	// long VDI_dispach( VDIpb *pb );
enter_vdi:
	move.l	_vdi_handler,d2
	beq.s	old_trap2
	move.l	d2,a0
	move.l	d1,-(sp)		// VDI parameter block address
	jsr	(a0)
	addq.w	#4,sp
	moveq	#1,d2
	bra.s	trap2_out

	// Call the AES
	// long AES_dispatch ( AESpb *pb );
enter_aes:
	move.l	_aes_handler,d2
	beq.s	old_trap2
	move.l	d2,a0
	move.l	d1,-(sp)
	jsr	(a0)
	addq.w	#4,sp
	moveq	#0,d2
	bra.s	trap2_out

	// Return the GDOS version if VDI is installed
	// as a kernel module. 
trap2_gdosvers:
	tst.l	_vdi_handler
	beq.s	old_trap2
	move.l	_gdos_version,d0
	moveq	#1,d2
	bra.s	trap2_out

	// Deliver the address of the VDI function dispatcher
	// if we have it.
trap2_vdihandler:
	move.l	_vdi_handler,d0
	moveq	#1,d2
	bne.s	trap2_out

	// Call old trap 2, when no function registered
old_trap2:
	bsr	_trap_2_emu
	moveq	#1,d2
	bra.s	trap2_out

rom_trap2:
	move.l	_old_trap2,-(sp)
	rts

// *********************************
// * ROM GEMDOS/BIOS/XBIOS callout *
// *********************************
//
// Trap wrapper for calling ROM from the inside of the kernel.
// Usage: just replace trap #1 with a jsr _trap_1_emu etc.

	.globl	_trap_1_emu
	.globl	_trap_2_emu
	.globl	_trap_13_emu
	.globl	_trap_14_emu

	.text

_trap_1_emu:
#ifndef ONLY030
	tst.w	(0x059e).w		// test longframe
	beq.s	TE_D
#endif
	move.w	(sp),-(sp)
	move.w	4(sp),2(sp)
	clr.w	4(sp)
//	move.l	(sp)+,d0
//	clr.w	-(sp)			// frame format word
//	move.l	d0,-(sp)		// return address
TE_D:	move.w	sr,-(sp)
#ifdef	ONLY030
	jmp	([_old_dos])
#else
	move.l	_old_dos.w(pc),-(sp)
	rts
#endif

	// Ozk: we cannot use D0/D1 becaues the ugly VDI and AES
	// passes paremters in these registers.
_trap_2_emu:
#ifndef	ONLY030
	tst.w	(0x059e).w
	beq.s	TE_AV
#endif
	move.w	(sp),-(sp)
	move.w	4(sp),2(sp)
	clr.w	4(sp)
TE_AV:	move.w	sr,-(sp)
#ifdef	ONLY030
	jmp	([_old_trap2])
#else
	move.l	_old_trap2.w(pc),-(sp)
	rts
#endif
	
_trap_13_emu:
#ifndef ONLY030
	tst.w	(0x059e).w		// test longframe
	beq.s	TE_B
#endif
	move.w	(sp),-(sp)
	move.w	4(sp),2(sp)
	clr.w	4(sp)
//	move.l	(sp)+,d0
//	clr.w	-(sp)			// frame format word
//	move.l	d0,-(sp)		// return address
TE_B:	move.w	sr,-(sp)
#ifdef	ONLY030
	jmp	([_old_bios])
#else
	move.l	_old_bios.w(pc),-(sp)
	rts
#endif

_trap_14_emu:
#ifndef ONLY030
	tst.w	(0x059e).w		// test longframe
	beq.s	TE_X
#endif
	move.w	(sp),-(sp)
	move.w	4(sp),2(sp)
	clr.w	4(sp)
//	move.l	(sp)+,d0
//	clr.w	-(sp)			// frame format word
//	move.l	d0,-(sp)		// return address
TE_X:	move.w	sr,-(sp)
#ifdef	ONLY030
	jmp	([_old_xbios])
#else
	move.l	_old_xbios.w(pc),-(sp)
	rts
#endif

// ******************************
// * unused trap's trap handler *
// ******************************
//
// Default routine for "unused" traps. Without this, any user (even
// on remote) can draw bombs on the console freely. So this is sorta
// security patch.
// Notice any programs those take these traps should now be loaded
// *after* MiNT. init_intr() decides which traps to take, see there.

	.globl	_unused_trap

	dc.l	0x58425241		// XBRA
	dc.l	0x4d694e54		// MiNT
	dc.l	v_rte

_unused_trap:
	move.w	#0x010b,-(sp)		// getpid
	trap	#1
	move.w	#0x000c,-(sp)		// SIGSYS
	move.w	d0,-(sp)		// himself
	move.w	#0x0111,-(sp)		// kill
	trap	#1
	addq.l	#0x08,sp
v_rte:	rte				// and return :-)

// ************************
// * miscellaneous things *
// ************************

//
// _lineA0: MiNT calls this to get the address of the line A variables
//
	.globl	_lineA0

_lineA0:
	movem.l	d2/a2,-(sp)		// save scratch registers
	dc.w	0xa000			// call the line A initialization routine
	movem.l	(sp)+,d2/a2
	rts

//
// _call_aes: calls the GEM AES, using the control block passed as
// a parameter. Used only for doing appl_init(), to see
// if the AES is active yet
//
	.globl	_call_aes

_call_aes:
	move.l	4(sp),d1		// fetch pointer to parameter block
	move.l	#200,d0 		// magic number for AES
	movem.l	d2/a2,-(sp)		// save scratch registers
	trap	#2
	movem.l	(sp)+,d2/a2
	rts

//
// _callout: Call an external function, passing <32 bytes of arguments,
// and return the value from the function. NOTE: we must be careful
// to save all registers here!
//
	.globl	_callout
	.globl	_callout1
	.globl	_callout2
	.globl	_callout6
	.globl	_callout6spl7
//
// _callout is the general purpose one
//
_callout:
	lea	8(sp),a0		// pointer to args
	move.l	4(sp),a1		// pointer to pointer to function
	movem.l	d2-d7/a2-a6,-(sp)	// save registers
	movem.l	(a0),d0-d7		// copy parameters
	movem.l	d0-d7,-(sp)
	suba.l	a5,a5			// the BIOS expects 0 in a5
	jsr	(a1)			// go do it
	lea	32(sp),sp
	movem.l	(sp)+,d2-d7/a2-a6	// restore reggies
	rts
//
// _callout2 and _callout1 are for functions with just 1 or
// 2 16 bit parameters. We cheat and use the same code for
// both, since passing 32 bits isn't much more expensive than
// passing 16 bits (and since the called function will just
// ignore any extra arg)
//

_callout1:
_callout2:
	movem.l	4(sp),a0/a1		// get function ptr & args
	movem.l	d2-d7/a2-a6,-(sp)	// save reggies
	move.l	a1,-(sp)		// push args
	suba.l	a5,a5			// the BIOS expects 0 in a5
	jsr	(a0)			// do function
	addq.w	#4,sp
	movem.l	(sp)+,d2-d7/a2-a6	// restore reggies
	rts
//
// _callout6 passes 6 words, saving sr
//
_callout6:
	movem.l	d2-d7/a2-a6,-(sp)	// save registers
	movem.l	4+44(sp),a0-a3		// get function ptr & args
	move.w	sr,-(sp)
	movem.l	a1-a3,-(sp)		// copy args
	suba.l	a5,a5			// the BIOS expects 0 in a5
	jsr	(a0)			// go do it
	lea	12(sp),sp
	move.w	(sp)+,sr
	movem.l	(sp)+,d2-d7/a2-a6	// restore reggies
	rts
//
// _callout6spl7 passes 6 words at ipl7
//
_callout6spl7:
	movem.l	d2-d7/a2-a6,-(sp)	// save registers
	movem.l	4+44(sp),a0-a3		// get function ptr & args
	move.w	sr,-(sp)
	movem.l	a1-a3,-(sp)		// copy args
	suba.l	a5,a5			// the BIOS expects 0 in a5
	ori.w	#0x0700,sr		// spl7()
	jsr	(a0)			// go do it
	lea	12(sp),sp
	move.w	(sp)+,sr
	movem.l	(sp)+,d2-d7/a2-a6	// restore reggies
	rts

//
// do_usrcall: call the user supplied function (*usrcall)(), with
// arguments given in the longwords usrarg1..usrarg5. Return value
// is placed in usrret. This function is used by the Supexec code
// in xbios.c.

	.globl	_do_usrcall
	.globl	_usrret
	.globl	_usrcall
	.globl	_usrarg1
	.globl	_usrarg2
	.globl	_usrarg3
	.globl	_usrarg4
	.globl	_usrarg5

_do_usrcall:
	move.l	_usrarg5,-(sp)
	move.l	_usrarg4,-(sp)
	move.l	_usrarg3,-(sp)
	move.l	_usrarg2,-(sp)
	move.l	_usrarg1,-(sp)
	move.l	_usrcall,-(sp)		// the user expects to see this on the stack
	pea	ucret(pc)		// so rts puts us back here
	move.l	_usrcall,-(sp)		// this copy is for us to use
	rts				// to jump to (we don't want to use registers)
ucret:	lea	24(sp),sp		// fix up stack
	move.l	d0,_usrret		// save returned value
	rts				// back to caller

#ifdef SYSCALL_REENTRANT

// *****************************
// * Reentrant syscall handler *
// *****************************
//
// Implemented by Odd Skancke.
//
// Reentrant means here that we can enter the kernel again through
// the trap handler from inside kernel space.
// This is not neccessary for the kernel itself but rather if you want
// to use other subsystems which use the trap#1 to enter the kernel again.
// You can, for example,use the TOS-VDI subsystem safely and the VDI can
// reenter the kernel through the trap handler.
//
// This piece of code needs more testing, NEW_SYSCALL not tested,
// 68000 not tested (without ONLY030)
//
reentrant_trap:
#ifndef ONLY030
	addq.w	#2+4,a0
	tst.w	(0x59e).w
	beq.s	reent_shortframe
	addq.w	#2,a0
reent_shortframe:
#else
	addq.w	#2+4+2,a0
#endif
	// Save hardware registers (CPU/FPU)
	tst.w	_fpu
	beq.s	reent_fpu_saved
	fsave	-(sp)
	cmp.w	#60,_mcpu+2
	bne.s	reent_no_060
	tst.b	2(sp)
	beq.s	reent_fpu_notused
	bra.s	reent_save_fpu
reent_no_060:
	tst.b	(sp)
	beq.s	reent_fpu_notused
reent_save_fpu:
	fmovem.x fp0-fp7,-(sp)
	fmovem.l fpcr/fpsr/fpiar,-(sp)
	move.w	#1,-(sp)
	bra.s	reent_fpu_saved
reent_fpu_notused:
	clr.w	-(sp)
reent_fpu_saved:

	moveq	#0,d1
	move.w	(a0),d1			// Function number
	cmp.w	d5,d1			// check if within bounds
	bcc.s	reent_erange		// Out of range

	// D0 will hold the address of function to call
	// D1 function number when staring here, indexing function tab
	// D5 will hold byte size of parameters
	// A0 will be placed at param_start + D5 (param_size)
	// A5 contains address of function tab

// ---------- BEGIN without NEW_SYSCALL ---------------
#ifndef NEW_SYSCALL
	// Fetch address of function, and copy 24 bytes of the parameters
	// (or 32 bytes if function table == gemdos) from the original place.
 #ifdef ONLY030
	move.l	(a5,d1.l*4),d2
 #else
	add.w	d1,d1
	add.w	d1,d1
	move.l	(a5,d1.l),d2
 #endif
	beq.s	reent_nofunc
	moveq	#24,d5
	cmpa.l	#_dos_tab,a5
	bne.s	reent_24
	addq.w	#8,d5
reent_24:

// ---------- BEGIN without NEW_SYSCALL ---------------
#else
// ---------- BEGIN with NEW_SYSCALL ------------------
// Ozk:	When writing this routine, I didnt know if the
//	size of params in the function table includes
//	the function number. If not, this code needs
//	to be modified to reflect that, it now thinks
//	param size includes function number!
 #ifdef ONLY030
	move.l	(a5,d1.l*8),d2
 #else
	add.w	d1,d1
	add.w	d1,d1
	add.w	d1,d1
	move.l	(a5,d1.l),d2
 #endif
	beq.s	reent_nofunc
 #ifdef	ONLY030
	move.w	6(a5,d1.l*8),d5
 #else
 	move.w	6(a5,d1.l),d5
 #endif
#endif	//NEW_SYSCALL
// ------------ END with NEW_SYSCALL ------------------
	add.w	d5,a0		// A0 now points to the last parameter (first one pushed)
	subq.w	#2,d5		// Skip the Function number
	move.w	d5,d1
	beq.s	reent_noargs	// No args (only function number)
	lsr.w	#1,d1		// Turn bytes into words
	subq.w	#1,d1
reent_cpyargs:
	move.w	-(a0),-(sp)
	dbra	d1,reent_cpyargs
reent_noargs:

	move.l	d2,a0		// Function address
	jsr	(a0)
	moveq	#0,d1		// Clear D1 -- we're not gonna call anything else
reent_out:
	add.w	d5,sp		// Correct stack
	// Unstack all cpu/fpu registers...
	tst.w	_fpu
	beq.s	reent_out_no_fpu
	tst.w	(sp)+
	beq.s	reent_no_fpu_saved
	fmovem.l (sp)+,fpcr/fpsr/fpiar
	fmovem.x (sp)+,fp0-fp7
reent_no_fpu_saved:
	frestore (sp)+
reent_out_no_fpu:
	movem.l	(sp)+,d2-d7/a2-a6
	tst.l	d1		// If D1 is not NULL, it is the address of some old trap
	bne.s	reent_call_rom
	rte
reent_call_rom:
	move.l	d1,a0
	jmp	(a0)

// ---- What to do when function number is outside function range
reent_erange:
	moveq	#0,d5
	cmpa.l	#_xbios_tab,a5
	beq.s	reent_rom_xbios
	bra.s	reent_enosys

// ---- What to do when there is no funcion installed
// ---- in the function table
reent_nofunc:
	moveq	#0,d5
	cmpa.l	#_bios_tab,a5
	beq.s	reent_rom_bios
	cmpa.l	#_xbios_tab,a5
	beq.s	reent_rom_xbios
reent_enosys:
	moveq	#-32,d0
	moveq	#0,d1
	bra.s	reent_out

// ---- set D1 to point to the old trap entry
reent_rom_bios:
	move.l	_old_bios,d1
	bra.s	reent_out
reent_rom_xbios:
	move.l	_old_xbios,d1
	bra.s	reent_out

|---------------------------------------------------------------------------------

// Because of different parameter passing, we need a whole different
// scheme with AES/VDI traps... ARGH!! :-)
reentrant_trap2:
	// Since we're not interested in the stack now,
	// we wont bother with it. VDI/AES passes args in D0/D1.
#ifdef	TRAP2_STACK_PARAMS
#ifndef ONLY030
	addq.w	#2+4,a0
	tst.w	(0x59e).w
	beq.s	reent2_shortframe
	addq.w	#2,a0
reent2_shortframe:
#else
	addq.w	#2+4+2,a0
#endif
#endif
	// Save hardware registers (CPU/FPU)
	tst.w	_fpu
	beq.s	reent2_fpu_saved
	fsave	-(sp)
	cmp.w	#60,_mcpu+2
	bne.s	reent2_no_060
	tst.b	2(sp)
	beq.s	reent2_fpu_notused
	bra.s	reent2_save_fpu
reent2_no_060:
	tst.b	(sp)
	beq.s	reent2_fpu_notused
reent2_save_fpu:
	fmovem.x fp0-fp7,-(sp)
	fmovem.l fpcr/fpsr/fpiar,-(sp)
	move.w	#1,-(sp)
	bra.s	reent2_fpu_saved
reent2_fpu_notused:
	clr.w	-(sp)
reent2_fpu_saved:

|-----------------------
/// FUNCTION CALLING HERE
	cmp.w	#115,d0			// VDI?
	beq	reent2_enter_vdi
	cmp.w	#200,d0			// AES?
	beq	reent2_enter_aes
	cmp.w	#201,d0			// appl_yield()?
	beq	reent2_enter_aes	// or should this be its own function ?

	// some VDI special cases, must be prepared by any VDI module ..
	cmp.w	#-2,d0			// Query GDOS version
	beq	reent2_trap2_gdosvers
	cmp.w	#-1,d0			// This returns the address of the VDI handler
	beq	reent2_trap2_vdihandler
	
	// What to do with trap 2 calls we dont know?
	// for now just noting..
	moveq	#0,d2

reent2_trap2_out:
	bra.s	reent2_out

	// Call the VDI
	// long VDI_dispach( VDIpb *pb );
reent2_enter_vdi:
	move.l	_vdi_handler,d2
	beq.s	reent2_old_trap2
	move.l	d2,a0
	move.l	d1,-(sp)
	jsr	(a0)
	addq.w	#4,sp
	moveq	#0,d2
	bra.s	reent2_trap2_out

	// Call the AES
	// long AES_dispatch ( AESpb *pb );
reent2_enter_aes:
	move.l	_aes_handler,d2
	beq.s	reent2_old_trap2
	move.l	d2,a0
	move.l	d1,-(sp)
	jsr	(a0)
	addq.w	#4,sp
	moveq	#0,d2
	bra.s	reent2_trap2_out

	// REturn the GDOS version if VDI is installed
	// as a kernel module. 
reent2_trap2_gdosvers:
	tst.l	_vdi_handler
	beq.s	reent2_old_trap2
	move.l	_gdos_version,d0
	moveq	#0,d2
	bra.s	reent2_trap2_out

	// Deliver the address of the VDI function dispatcher
	// if we have it.
reent2_trap2_vdihandler:
	moveq	#0,d2
	move.l	_vdi_handler,d0
	bne.s	reent2_trap2_out

reent2_rom_trap2:
reent2_old_trap2:
	moveq	#1,d2
	bra.s	reent2_trap2_out

/// OUT begins here
|-----------------------
	// If D2 is NULL, we exit normally, because we did something
	// If D2 is not NULL, we call the old trap2
reent2_out:
	// Unstack all cpu/fpu registers...
	tst.w	_fpu
	beq.s	reent2_out_no_fpu
	tst.w	(sp)+
	beq.s	reent2_no_fpu_saved
	fmovem.l (sp)+,fpcr/fpsr/fpiar
	fmovem.x (sp)+,fp0-fp7
reent2_no_fpu_saved:
	frestore (sp)+
reent2_out_no_fpu:
	tst.l	d2
	bne.s	reent2_call_old
	movem.l	(sp)+,d1-d7/a0-a6
	rte
reent2_call_old:
	movem.l	(sp)+,d1-d7/a0-a6
	move.l	_old_trap2,-(sp)
	rts
	
#endif /* SYSCALL_REENTRANT */
