Source to mint/syscall.spp


Enter a symbol's name here to quickly find it.

;
; 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.

%include "magic.i"
%include "locore.i"
;
; syscall: interface for system calls. The following entry points are
;    defined:
; _mint_bios:  entry point for the BIOS calls (trap #13)
; _mint_xbios: entry point for XBIOS calls (trap #14)
; _mint_dos:   entry point for GEMDOS calls (trap #1)
; _sig_return: user signal handlers return to this routine (see signal.c)
;              it is responsible for restoring the kernel's old context
;              via the Psigreturn() system call
; _lineA0:     calls the line A initialize routine
; _call_aes:   calls the GEM AES
; _call_dosound: calls the XBIOS Dosound() function
; _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 routine
; _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).
; _pc_valid_return:
;    used to indicate to the kernel that a valid return from user mode
;    is taking place
;
; _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 C function enter_kernel() is called on entry to the kernel, and the
; function leave_kernel() is called on exit. These functions are responsible
; for saving and restoring the various trap vectors, so that MiNT can trap
; out to TOS directly, but programs can only trap to MiNT.
;
; we also call certain BIOS functions directly if these are known not to
; require saving/restoring of context
;
	TEXT
	
	XDEF	_mint_bios,_mint_xbios
	XDEF	_mint_dos
	XREF	_build_context
	XREF	_restore_context
	XREF	_proc_clock		; controls process' allocation of CPU time
	XREF	_enter_kernel
	XREF	_leave_kernel
	XREF	_preempt
	XREF	_unwound_stack
	XREF	_check_sigs
	
	XREF	_curproc
	XREF	_bios_tab,_bios_max
	XREF	_xbios_tab,_xbios_max,_old_xbios
	XREF	_dos_tab,_dos_max

	XREF	_bconbuf,_bconbsiz,_bconbdev
	XREF	_bflush

	XREF	_ubconstat,_do_bconin,_ubcostat,_kbshift
	
_mint_dos:
	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
	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
	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)
	move.w	#1,-(sp)		; flag for in GEMDOS
	jsr	_enter_kernel		; set up vectors appropriately
	addq.w	#2,sp
	bra	_syscall

_mint_xbios:
;
; Kludge for Setscreen: under Falcon TOS, this
; calls a GEMDOS function (Srealloc) and so
; we must *not* change any of the vectors!!

	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	LC_FRAME		; 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
LX_check:
	cmp.w	#5,(a1)			; check for Setscreen command
	beq.s	LX_screen		; no -- fall through

	clr.w	-(sp)			; no frame format needed
	move.l	_curproc,a0
	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)
%ifdef ONLY030
	clr.w	-(sp)			; flag: NOT GEMDOS
%else
	move.w	#0,-(sp)		; flag: NOT GEMDOS
%endif
	jsr	_enter_kernel		; set up vectors appropriately
	addq.w	#2,sp
	bra	_syscall
;
; For setscreen, jump directly to the ROM vector --
; this avoids all hazards (like changing the vectors)

LX_screen:
	move.l	_old_xbios+8,a0
	jmp	(a0)


_mint_bios:
;
; 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
;
%ifdef ONLY030
	clr.w	-(sp)			; flag: NOT a GEMDOS call
%else
	move.w	#0,-(sp)		; flag: NOT a GEMDOS call
%endif
	jsr	_enter_kernel		; set up BIOS vectors
	addq.w	#2,sp
	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	_ubconstat
L_1out:
	addq.l	#2,sp
L_0out:
	move.l	d0,-(sp)		; save d0
	ori.w	#$0700,sr		; spl7()
	jsr	_leave_kernel
	move.l	(sp)+,d0		; retrieve value to be returned
	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	#$dead,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	_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	_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:
;
; 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:
;
; figure out which routine to call
;
	move.w	(sp),d0
	cmp.w	#-1,d0			; trapping with -1 means return
	bne.s	check_max		; the corresponding system table
	move.l	a5,d0
	bra.s	out
check_max:
	cmp.w	d5,d0
	bcc	error
%ifdef ONLY030
	move.l	0(a5,d0.w*4),d0		; d0 = syscall_tab[d0]
%else
	add.w	d0,d0
	add.w	d0,d0			; multiply by 4
	move.l	0(a5,d0.w),d0		; d0 = syscall_tab[d0]
%endif
	beq	error			; null entry means invalid call
	addq.w	#2,sp			; pop function number off stack
	move.l	d0,a0
	jsr	(a0)			; go do the call
out:
	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	#$c000,d1		; are either of the trace bits set
	sne	P_SYSCTXT+C_PTRACE(a0)	; mark as trace pending/not
notrace:
	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	LC_FLOPLCK		; 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	#$0700,sr		; spl7()
%ifdef ONLY030
	clr.l	_unwound_stack
%else
	moveq	#0,d0
	move.l	d0,_unwound_stack
%endif
	jsr	_leave_kernel		; restore vectors
	move.l	_curproc,a0
	pea	4(a0)
	jsr	_restore_context	; never returns

;
; 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	maybe_bios
	trap	#XBIOS
	bra	out
maybe_bios:
	cmp.l	#_dos_tab,a5
	beq.s	trap_1
	trap	#BIOS
	bra	out
trap_1:
	trap	#GEMDOS
	bra	out

;
; sig_return: user signal handlers return to us. At that point, the
; stack looks like this:
;  -4(sp)      (long) sfmt
;    (sp)      (long) signal number -- was a parameter for user routine
;
	XDEF	_sig_return
	XDEF	_pc_valid_return
_sig_return:
	addq.w	#8,sp			; pop signal number and sfmt
	move.w	#$11a,-(sp)		; Psigreturn() system call
	trap	#GEMDOS
_pc_valid_return:        ; tells kernel it's us
; we had better not come back; if we did, something terrible
; happened, and we might as well terminate
	move.w	#-998,-(sp)
	move.w	#$4c,-(sp)		; Pterm()
	trap	#GEMDOS
;
; 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	#$0700,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

;
; _lineA0: MiNT calls this to get the address of the line A variables
;
	XDEF	_lineA0
_lineA0:
	movem.l	d2/a2,-(sp)	; save scratch registers
	LNA_OP	LNA_VAL		; 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
;
	XDEF	_call_aes
_call_aes:
	move.l	4(sp),d1	; fetch pointer to parameter block
	move.w	#$c8,d0		; magic number for AES
	movem.l	d2/a2,-(sp)	; save scratch registers
	trap	#AES
	movem.l	(sp)+,d2/a2
	rts
;
; _call_dosound:
;
	XDEF	_call_dosound
_call_dosound:
	move.l	4(sp),-(sp)
	move.w	#32,-(sp)
	trap	#XBIOS
	addq.w	#6,sp
	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!
;
	XDEF	_callout
	XDEF	_callout1
	XDEF	_callout2
	XDEF	_callout6
	XDEF	_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	#$0700,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.

	XDEF	_do_usrcall
	XREF	_usrret
	XREF	_usrcall
	XREF	_usrarg1,_usrarg2,_usrarg3,_usrarg4,_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

	END