/* * This file is part of ixemul.library for the Amiga. * Copyright (C) 1991, 1992 Markus M. Wild * Portions Copyright (C) 1994 Rafael W. Luebbert * * 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: trap.s,v 1.1 1994/06/19 15:17:35 rluebbert Exp $ * * $Log: trap.s,v $ * Revision 1.1 1994/06/19 15:17:35 rluebbert * Initial revision * */ .globl _trap_00 .globl _restore_00 .globl _sup00_do_sigresume .globl _sup00_do_sigreturn .globl _sup00_do_sigreturn_ssp .globl _trap_20 .globl _restore_20 .globl _sup20_do_sigresume .globl _sup20_do_sigreturn .globl _sup20_do_sigreturn_ssp .globl _supervisor .globl _do_sigreturn .globl _launch_glue .globl _addupc .globl _resetfpu .globl _install_vector .globl _restore_vector .globl _vector_install_count .globl _vector_old_pc .globl _vector_nop | struct offsets are determined by create_defines #include "ix_internals_backup.h" | 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 movel a5,a5@(-10) | store usp lea a5@(-10),a5 | make room for sr, pc and usp. moveml d0-d7/a0-a6,a5@-| store registers on usp movel sp@+,a5@(0x34) | insert the saved a5 into the saveset movel sp@+,d1 | remember trapnumber movew sp@+,a5@(0x44) | copy SR movel sp@+,d2 | remember and movel d2,a5@(0x40) | copy (offending?) PC movel a5,a3 | save pointer to registers movel #0,a5@- | pass NULL pointer to stored fpu registers movel a3,a5@- | pass pointer to stored registers movel d2,a5@- | pass offending PC addl d1,d1 | convert the passed trap number into a fake addl d1,d1 | 68020 frame format word movel d1,a5@- | pass frame format word | | 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...) jsr _trap | to the higher level C trap processor _restore_00: movel usp,a5 | get usp lea a5@(16),a5 | skip trap argument movel a5@(0x3c),a1 | get stored usp movel a1,usp | set up the original supervisor stack frame movel a5@(0x40),sp@- | copy PC movew a5@(0x44),sp@- | and SR moveml a5@,d0-d7/a0-a6 | and the other 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 movel a5,a5@(-10) | store usp lea a5@(-10),a5 | make room for sr, pc and 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@(0x44) | copy SR movel sp@+,d2 | remember and movel d2,a5@(0x40) | copy (offending?) PC movel a5,a3 | save pointer to registers | 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 | movel #0,a4 | clear pointer to fpu registers tstl _has_fpu | do we have a fpu? 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? fmoveml fpcr/fpsr/fpi,a5@- | push the fpu control registers and fmovemx fp0-fp7,a5@- | the fpu data registers movel a5,a4 | store pointer to fpu 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 a4,a5@- | pass pointer to stored fpu registers movel a3,a5@- | pass pointer to stored registers movel d2,a5@- | pass offending PC movel d1,a5@- | pass frame format word 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 jsr _trap | do distribution in C ;-) _restore_20: | | restore the saved stack frame from the usp, and copy the necessary | parts over to the ssp | movel usp,a5 lea a5@(16),a5 | skip trap() arguments | first deal with fpu stuff, if there's an fpu tstl _has_fpu | do we have a fpu? 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 fmovemx a5@+,fp0-fp7 | restore fpu data registers and fmoveml a5@+,fpcr/fpsr/fpi | fpu control 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@(0x40),sp@- | copy PC movew a5@(0x44),sp@- | and SR movel a5@(0x3c),a1 | get the stored usp movel a1,usp 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),a0 | with this signal context movel a6,a1 | store a6 movel 4:w,a6 jmp a6@(-0x1e) | do it (Supervisor() system call) | | restore signal context | argument comes in usp | | mc68020 entry _sup20_do_sigresume: jsr _resume_signal_check bra _sup20_do_sigreturn _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_sigresume: jsr _resume_signal_check bra _sup00_do_sigreturn _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 | Supervisor() entry (already comes on exception frame) _do_sigreturn: | make the sigreturn() function preserve all registers moveml d0/d1/a0/a1,sp@- movel a1,a6 | restore a6, a0 already contains the signal context resume_sigreturn: movel a0,sp@- jsr _get_current_task | get current task movel sp@+,a0 movel d0,a1 movel a1@(USERPTR_OFFSET),a1 | (struct user *) | ok, now a0:sc, a1:u movel a0@+,a1@(U_ONSTACK_OFFSET) | u.u_onstack = sc->sc_onstack movel a0@+,a1@(P_SIGMASK_OFFSET) | u.p_sigmask = sc->sc_mask movel a0@+,a1 | usp = sc->sc_sp movel a1,usp movel a0@+,a5 | fp = sc->sc_fp movel d0,a1 | get back current task lea a0@(2),a0 | skip unused part of sc_ap movew a0@,a1@(IDNESTPTR_OFFSET) | store IDNestCnt,TDNestCnt movel 4:w,a1 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: | set pc and sr in current exception frame movel a0@+,sp@(2+4*4) | set PC movew a0@(2),sp@(4*4) | and SR moveml sp@+,d0/d1/a0/a1 rte | | 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: jsr _get_current_task movel d0,a0 movel 4:w,a4 movel a4@(560),a4 | ex_LaunchPoint movel a0@(SPREGPTR_OFFSET),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 movel d1,sp@- movel d0,sp@- jsr ___mulsi3 addqw #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 movel d0,a0 movew a0@,d0 addw sp@(18),d0 | add tick to current value movew d0,a0@ Lauexit: movl sp@+,a2 | restore scratch reg rts Lreset_fpu: clrl sp@- | prepare 060 fpu null state frame clrl sp@- | needs 2 additional longs on stack clrl sp@- frestore sp@+ | Note that after using frestore with a null state frame we have | to set up the control register to restore rounding to truncation | rather than round-to-nearest, as required by the ANSI C standard. moveq #72,d0 addl d0,d0 fmovel d0,fpcr rte _resetfpu: movel a6,sp@- movel a5,sp@- movel 4:w,a6 tstl _has_68060_or_up | do we have a 68060? lea Lreset_fpu,a5 jne L_skip addqw #4,a5 L_skip: jsr a6@(-30) | Supervisor() movel sp@+,a5 movel sp@+,a6 rts | See the Enforcer manual for the original assembler code of the | following code. | This is the new bus error handler for use with Enforcer & GDB. | There are two entry points, one for the 68020 and 68030, and one | for the 68040. _vector_nop: nop | make sure next data is on a 4 byte boundary! .align 2 OldVector: .long 0 | One long word _vector_install_count: .long 0 | use count _vector_old_pc: .long 0 | store old pc _new_vector_060: cmpl #4,sp@(8) | 68060 jeq TraceSkip | If AbsExecBase, OK jra TraceProcess _new_vector_040: cmpl #4,sp@(20) | 68040 jeq TraceSkip | If AbsExecBase, OK jra TraceProcess _new_vector: cmpl #4,sp@(16) jeq TraceSkip | If AbsExecBase, OK | Check if the current Task is running under ixemul control and | is being traced by ixemul. TraceProcess: moveml a0/a1/d0/d1,sp@- | Save this... jsr _get_current_task | get current task movel d0,a0 movel a0@(USERPTR_OFFSET:W),d0| UserData -> (struct user *) movel d0,a0 jeq NoTrace | Not ixemul movel a0@,d0 | get u_ixbase cmpl _ixemulbase,d0 | == ixemulbase? jne NoTrace | Nope, not an ixemul program btst #4,a0@(P_FLAG_OFFSET+3) | Test STRC flag beq NoTrace btst #7,sp@(16:W) | Are we already single stepping? jne NoTrace bset #7,sp@(16:W) | Set trace bit... lea _vector_old_pc,a0 tstl a0@ | is this variable already in use? jne NoTrace movel sp@(18:W),a0@ | store old pc lea _vector_nop,a0 movel a0,sp@(18:W) | store new pc NoTrace: moveml sp@+,a0/a1/d0/d1 | Restore... TraceSkip: movel OldVector,sp@- | Ready to return rts | Call this function in Supervisor mode _restore_vector: tstl _has_68040_or_up | do we have a 68040 or up? lea _new_vector,a0 jeq got_vector1 tstl _has_68060_or_up | do we have a 68060 or up? lea _new_vector_040,a0 jeq got_vector1 lea _new_vector_060,a0 got_vector1: lea _vector_install_count,a1 tstl a1@ beq restore_exit subql #1,a1@ | decrease use count bne restore_exit movec vbr,a1 | get Vector Base Register cmpl a1@(8:W),a0 | sanity check jne restore_exit movel OldVector,a1@(8:W) restore_exit: rte | Call this function in Supervisor mode _install_vector: lea _new_vector,a0 tstl _has_68040_or_up | do we have a 68040 or up? jeq got_vector2 lea _new_vector_040,a0 tstl _has_68060_or_up | do we have a 68060 or up? lea _new_vector_060,a0 jeq got_vector2 got_vector2: addql #1,_vector_install_count | increase use count movec vbr,a1 | get Vector Base Register cmpl a1@(8:W),a0 jeq install_done | sanity check movel a1@(8:W),OldVector movel a0,a1@(8:W) install_done: rte