head	1.1;
access;
symbols
	version39-41:1.1;
locks;
comment	@ *  @;


1.1
date	92.05.14.20.36.14;	author mwild;	state Exp;
branches;
next	;


desc
@trap handlers and glue code
@


1.1
log
@Initial revision
@
text
@/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  $Id$
 *
 *  $Log$
 */

	.globl	_trap_00
	.globl	_trap_20
	.globl	_restore_00
	.globl	_restore_20
	.globl	_supervisor
	.globl	_do_sigreturn
	.globl	_sup00_do_sigreturn
	.globl	_sup20_do_sigreturn
	.globl	_sup00_do_sigreturn_ssp
	.globl	_sup20_do_sigreturn_ssp
	.globl	_launch_glue
	.globl	_addupc


#undef DEBUG

	|
	| ATTENTION:
	| 
	| do_sigreturn() uses absolute offsets into struct user.
	| if you change struct user, be sure to check whether the current
	| offsets of p_sigmask and u_onstack are still valid !!!!
	|


	| This is the trap processing function for the mc68000.
	| Things are quite easy here, just save the general purpose registers
	| and the pc/sr combo, call trap(), then restore the previous
	| context and return

_trap_00:
	movel	a5,sp@@-		| need a scratch register
	movel	usp,a5		| get usp
	moveml	d0-d7/a0-a6,a5@@-| store registers on usp
	movel	sp@@+,a5@@(0x34)	| insert the saved a5 into the saveset
	movel	sp@@+,d2		| remember trapnumber
	movew	sp@@+,a5@@-	| copy SR
	movel	sp@@+,a0		| remember and
	movel	a0,a5@@-		| copy offending PC

	|
	| pass return address and ssp-value on userstack
	| This happens for the same reason as we have a glue_launch entry.
	| trap cleans up these 8 bytes on the user stack itself
	movel	sp,a5@@-
	movel	#_restore_00,a5@@-
	movel	a5,usp		| and remember current value of usp (a5)

	| fine, now process this trap. This might (doesn't have to) push
	| additional frames. If not, we just return where the exception 
	| took place (and probably will again...)

	movel	a0,sp@@-		| pass offending address (don't know more about it)
	addl	d2,d2		| convert the passed trap number into a fake
	addl	d2,d2		| 68020 frame format word
	movel	d2,sp@@-		| and pass it as argument
	jsr	_trap		| to the higher level C trap 
	lea	sp@@(8),sp	| processor

_restore_00:
	movel	usp,a5		| get usp
	lea	a5@@(0x42),a1	| skip over the now no longer needed frame
	movel	a1,usp		| -> 15 registers, one PC and one SR
	
	| set up the original supervisor stack frame
	movel	a5@@+,sp@@-	| PC
	movew	a5@@+,sp@@-	| SR
	moveml	a5@@,d0-d7/a0-a6	| and the other cpu registers
	rte



	| This is the trap processor for the mc68020 and above, paired with
	| an fpu (don't *need* an fpu though).
	| What is done: start is same as with 68000, but then the complete
	| additional exception frame is saved on the usp, together with the
	| fpu state. Then trap() is called, and then the previous context
	| is restored (involves copying back the frame from the usp over to the ssp)

_trap_20:
	movel	a5,sp@@		| nuke the trap number, we use the frame format word
	movel	usp,a5		| get usp
	moveml	d0-d7/a0-a6,a5@@-| store registers on usp
	movel	sp@@+,a5@@(0x34)	| insert the saved a5 into the saveset
	movew	sp@@+,a5@@-	| copy SR
	movel	sp@@+,d2		| remember and
	movel	d2,a5@@-		| copy (offending?) PC

	| find out more about the frame (according to the MC68030 user manual)
	clrl	d1
	movew	sp@@+,d1		| remember frame format word
	movew	d1,d0
	andw	#0xf000,d0
	beq	Lfmt_S0		| S0
	cmpw	#0x1000,d0
	beq	Lfmt_S1		| S1 this (interrupt) frame shouldn't be here...
	cmpw	#0x2000,d0
	beq	Lfmt_S2		| CHK{2},cpTRAPcc,TRAPV,Trace,Div0,MMUcfg,cp post instr
	cmpw	#0x9000,d0
	beq	Lfmt_S9		| cp mid instr,main det prot viol,int during cp instr
	cmpw	#0xa000,d0
	beq	Lfmt_SA_SB	| address or bus error, short and long frame
	cmpw	#0xb000,d0
	bne	Lfmt_S0		| ??? frame, this will probably not fully cleanup sp..

Lfmt_SA_SB:
	| this part (upto Lbe10) inspired by locore.s in sys/hp300/ of BSD4.3-reno
	movew	sp@@(2),d0	| grab SSW for fault processing
	btst	#12,d0		| RB set?
	beq	LbeX0		| no, test RC
	bset	#14,d0		| yes, must set FB
	movew	d0,sp@@(2)	| for hardware too
LbeX0:
	btst	#13,d0		| RC set?
	beq	LbeX1		| no, skip
	bset	#15,d0		| yes, must set FC
	movew	d0,sp@@(2)	| for hardware too
LbeX1:
	btst	#8,d0		| data fault?
	beq	Lbe0		| no, check for hard cases
	movel	sp@@(8),d2	| fault address is as given in frame
	bra	Lbe10		| thats it
Lbe0:
	btst	#12,d1		| long (type B) stack frame?
	bne	Lbe4		| yes, go handle
	btst	#14,d0		| no, can use saved PC. FB set?
	beq	Lbe3		| no, try FC
	addql	#4,d2		| yes, adjust address
	bra	Lbe10		| done
Lbe3:
	btst	#15,d0		| FC set?
	beq	Lbe10		| no, done
	addql	#2,d2		| yes, adjust address
	bra	Lbe10		| done
Lbe4:
	movel	sp@@(28),d2	| long format, use stage B address
	btst	#15,d0		| FC set?
	beq	Lbe10		| no, all done
	subql	#2,d2		| yes, adjust address
Lbe10:

	| now move the frame over to the usp (6/21 longwords remain)
	
	moveml	sp@@+,d3-d7/a0	| may trash as many registers as I like, I saved
	moveml	d3-d7/a0,a5@@-	| them already ;-) First copy 6 longs

	btst	#12,d1		| long (type B) stack frame?
	beq	Lfmt_S0		| nope, done

	moveml	sp@@+,d3-d7/a0-a2 | first copy 8 longs
	moveml	d3-d7/a0-a2,a5@@-
	moveml	sp@@+,d3-d7/a0-a1 | plus 7 gives 15, plus already stored 6 is 21
	moveml	d3-d7/a0-a1,a5@@-
	bra	Lfmt_S0		| finito

Lfmt_S9:
	movel	sp@@+,a5@@-	| S9 is an S2 plus 4 internal (word length) registers
	movel	sp@@+,a5@@-	| so store those registers, and fall into S2

Lfmt_S2:
	movel	sp@@+,d2		| S2 contains the offending instruction address
				| and the frame format word
	movel	d2,a5@@-		| we have the offending instruction address here

	| fall into

Lfmt_S0:
Lfmt_S1:
	movew	d1,a5@@-		| and as the last thing store the frame format word

	|
	| now lets look at the fpu, if there is an fpu in the system
	|
	
	btst	#4,(4)@@(0x129)	| is AFB_68881 set in SysBase->AttnFlags ??
	beq	Lno_fpu
	
	fsave	a5@@-		| dump the fpu state onto the usp
	moveb	a5@@,d0		| and get the fpu state identifier
	beq	Lno_fpu		| null frame?

	clrl	d0
	moveb	a5@@(1),d0	| load state frame size
	bset	#3,a5@@(d0)	| set bit 27 of BIU

	fmovemx	fp0-fp7,a5@@-		| push the fpu data registers and
	fmoveml	fpcr/fpsr/fpi,a5@@-	| fpu control registers

	movew	#-1,a5@@-	| mark that there is fpu stuff on the stack

Lno_fpu:

	|
	| pass return address and ssp-value on userstack
	| This happens for the same reason as we have a glue_launch entry.
	| trap cleans up these 8 bytes on the user stack itself
	movel	sp,a5@@-
	movel	#_restore_20,a5@@-

	movel	a5,usp		| set the new value of the usp

	| that's it, phew.. now process this frame, and perhaps throw some
	| frames on it as well to deal with the signal
	
	movel	d2,sp@@-		| pass offending PC
	movel	d1,sp@@-		| pass frame format word
	jsr	_trap		| do distribution in C ;-)
	lea	sp@@(8),sp

_restore_20:
	|
	| restore the saved stack frame from the usp, and copy the necessary
	| parts over to the ssp
	|

	movel	usp,a5
	
	| first deal with fpu stuff, if there's an fpu

	btst	#4,(4)@@(0x129)	| is AFB_68881 set in SysBase->AttnFlags ??
	beq	Lno_fpu2

	tstb	a5@@
	beq	Lrst_fpu_frame	| there's only the null frame, go and restore it

	lea	a5@@(2),a5	| skip fpu indicator
	fmoveml	a5@@+,fpcr/fpsr/fpi	| restore fpu control and
	fmovemx	a5@@+,fp0-fp7		| fpu data registers
	
Lrst_fpu_frame:
	frestore a5@@+		| and restore the internal fpu state

Lno_fpu2:
	movew	a5@@+,d1		| get frame format word
	movew	d1,d0
	andw	#0xf000,d0
	beq	Lrfmt_S0	| S0
	cmpw	#0x1000,d0
	beq	Lrfmt_S1	| S1
	cmpw	#0x2000,d0
	beq	Lrfmt_S2	| S2
	cmpw	#0x9000,d0
	beq	Lrfmt_S9	| S9
	cmpw	#0xa000,d0
	beq	Lrfmt_SA	| SA
	cmpw	#0xb000,d0
	bne	Lrfmt_S0	| ??? frame

Lrfmt_SB:
	moveml	a5@@+,d3-d7/a0-a2
	moveml	d3-d7/a0-a2,sp@@-
	moveml	a5@@+,d3-d7/a0-a1
	moveml	d3-d7/a0-a1,sp@@- | copy 15 longs

Lrfmt_SA:
	movel	a5@@+,sp@@-	| copy  3 longs
	movel	a5@@+,sp@@-
	movel	a5@@+,sp@@-
	
Lrfmt_S9:
	movel	a5@@+,sp@@-	| copy  2 longs
	movel	a5@@+,sp@@-

Lrfmt_S2:
	movel	a5@@+,sp@@-	| copy  1 long

Lrfmt_S1:
Lrfmt_S0:
	movew	d1,sp@@-		| insert frame format word
	movel	a5@@+,sp@@-	| copy PC
	movew	a5@@+,sp@@-	| and SR
	
	lea	a5@@(0x3c),a1	| skip the rest of the frame
	movel	a1,usp		| 0x3c -> 15 registers
	
	moveml	a5@@,d0-d7/a0-a6	| restore the cpu registers
	rte			| that's it (finally) .. 


	|
	| jump to the given argument in supervisor mode
	| does NOT return
	|

_supervisor:
	movel	sp@@(4),a5	| where to go in supervisor
	movel	sp@@(8),sp@@-	| with this usp
	movel	a6,sp@@-
	movel	4:w,a6
	jmp	a6@@(-0x1e)	| do it (Supervisor() system call)


	|
	| restore signal context
	| argument comes in usp
	|

	| mc68020 entry
_sup20_do_sigreturn_ssp:	| entry via jsr from supervisor mode
	movel	sp@@(4),sp	| set ssp
_sup20_do_sigreturn:
	lea	sp@@(-8),sp	| make room for an exception frame
	movew	#0x20,sp@@(6)	| fake format word
	bra	fromsup_sigreturn

	| mc68000 entry
_sup00_do_sigreturn_ssp:
	movel	sp@@(4),sp	| set ssp
_sup00_do_sigreturn:
	lea	sp@@(-6),sp	| make room for an exception frame

fromsup_sigreturn:
	moveml	d0/d1/a0/a1,sp@@-

	movel	usp,a0		| get signal context
	bra	resume_sigreturn

	| fall into _do_sigreturn


	| Supervisor() entry (already comes on exception frame)
_do_sigreturn:
	| make the sigreturn() function preserve all registers
	moveml	d0/d1/a0/a1,sp@@-

	movel	usp,a0		| get signal context
	movel	a0@@+,a6		| restore the trashed a6 register
	movel	a0@@,a0

resume_sigreturn:

#if 0
	movel	sp,a0@@-
	movel	a0,sp
	pea	dumpsc_msg
	jsr	_kprintf
	movel	sp@@(4),sp
	movel	usp,a0
#endif

	movel	4:w,a1		| SysBase
	movel	a1,d1		| remember for later setting of sc_ap
	movel	a1@@(0x114),a1	| ThisTask
	movel	a1@@(0x2e),a1	| TrapData -> (struct user *)
	
	| ok, now a0:sc, a1:u

	| get those offsets in struct user with `print_user.c' !
	movel	a0@@+,a1@@(0x11c)	| u.u_onstack = sc->sc_onstack
	movel	a0@@+,a1@@(0x134)	| u.p_sigmask = sc->sc_mask
	movel	a0@@+,a1		| usp = sc->sc_sp
	movel	a1,usp
	movel	a0@@+,a5		| fp  = sc->sc_fp


#if 0
	moveml	d1/a0/a1,sp@@-
	movel	a0@@,sp@@-
	pea	before_foo
	jsr	_kprintf
	lea	sp@@(8),sp
	moveml	sp@@+,d1/a0/a1
#endif

	movel	d1,a1		| get back SysBase
	movel	a1@@(0x114),a1	| ThisTask
	movel	a0@@,a1@@(0x0e)	| store Flags,State,IDNestCnt,TDNestCnt
	movel	d1,a1
	lea	a0@@(2),a0	| skip unused part of sc_ap
	movew	a0@@+,d1		| get IDNestCnt and TDNestCnt
	movew	d1,a1@@(0x126)	| store them in SysBase
	tstb	a1@@(0x126)
	bmi	Lenable
Ldisabled:
	movew	#0x4000,0xdff09a	| disable interrupts
	bra	Lint_twiddle
Lenable:
	movew	#0xc000,0xdff09a	| enable interrupts
Lint_twiddle:

#if 0
	moveml	d1/a0/a1,sp@@-
	pea	after_foo
	jsr	_kprintf
	lea	sp@@(4),sp
	moveml	sp@@+,d1/a0/a1
#endif

	| set pc and sr in current exception frame
	movel	a0@@+,sp@@(2+4*4)	| set PC
	movew	a0@@(2),sp@@(4*4)	| and SR

#ifdef DEBUG
	pea	sp@@
	pea	dumpframe_msg
	jsr	_kprintf
	lea	sp@@(8),sp
#endif

	moveml	sp@@+,d0/d1/a0/a1
	rte

#ifdef DEBUG
dumpsc_msg:
	.asciz	"ssp = $%lx, onstack = %ld, mask = $%lx, sp = $%lx\nfp = $%lx, ap = $%lx, pc = $%lx, ps = $%lx\n"
dumpframe_msg:
	.asciz	"restore: ssp = $%lx, sr = $%x, pc = $%lx, fmt = $%x.\n"
before_foo:
	.asciz	"before foo ($%lx), "
after_foo:
	.asciz	"after foo.\n"
	.even
#endif


	|
	| launch_glue is used to invoke the sig_launch handler. We have to care to
	| clean the supervisor stack, if we should call a signal handler from
	| the launch handler.
	| The bad thing is, that doing this right needs information on how 
	| the tc_Launch entry is called from the OS. Thus I pass two parameters
	| on the user stack, the value of the `virgin' ssp and the value of
	| a4, which happens to contain the address of the context restore
	| function.
	| It is assumed, that tc_Launch is called via 
	| ... jsr sub
	| ...
	| sub:jsr tc_Launch
	| Thus we have to backup the sp by two jsr's, which is 8.

_launch_glue:
	movel	4:w,a0
	movel	a0@@(0x114),a0	| tid = SysBase->ThisTask
	movel	a0@@(0x36),a0	| usp isn't setup correctly, do it now from tc_SPReg
	movel	sp,a0@@-
	addl	#8,a0@@
	movel	a4,a0@@-
	movel	a0,usp
	
	jsr	_sig_launch	| sig_launch `(void *pc_ret, *ssp_ret)' (`' on usp)

	| sig_launch() already corrects the usp to pop those two arguments
	rts



/*
 * update profiling information for the user
 * addupc(pc, &u.u_prof, ticks)
 */
_addupc:
	movl	a2,sp@@-			| scratch register
	movl	sp@@(12),a2		| get &u.u_prof
	movl	sp@@(8),d0		| get user pc
	subl	a2@@(8),d0		| pc -= pr->pr_off
	jlt	Lauexit			| less than 0, skip it
	movl	a2@@(12),d1		| get pr->pr_scale
	lsrl	#1,d0			| pc /= 2
	lsrl	#1,d1			| scale /= 2

	| mulul	d1,d0			| pc /= scale
	moveml	d0/d1,sp@@-
	jbsr	___mulsi3
	lea	sp@@(8),sp

	moveq	#14,d1
	lsrl	d1,d0			| pc >>= 14
	bclr	#0,d0			| pc &= ~1
	cmpl	a2@@(4),d0		| too big for buffer?
	jge	Lauexit			| yes, screw it
	addl	a2@@,d0			| no, add base

	| movl	d0,sp@@-			| push address
	| jbsr	_fusword		| grab old value
	| movl	sp@@+,a0			| grab address back
	| cmpl	#-1,d0			| access ok
	| jeq	Lauerror		| no, skip out

	movel	d0,a0
	movew	a0@@,d0

	addw	sp@@(18),d0		| add tick to current value

	movew	d0,a0@@

	| movl	d0,sp@@-			| push value
	| movl	a0,sp@@-			| push address
	| jbsr	_susword		| write back new value
	| addql	#8,sp			| pop params
	| tstl	d0			| fault?
	| jeq	Lauexit			| no, all done
Lauerror:
	| clrl	a2@@(12)			| clear scale (turn off prof)
Lauexit:
	movl	sp@@+,a2			| restore scratch reg
	rts
@
