*       $NetBSD: gen_except.sa,v 1.4 2010/02/27 22:12:32 snj Exp $

*       MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP
*       M68000 Hi-Performance Microprocessor Division
*       M68040 Software Package
*
*       M68040 Software Package Copyright (c) 1993, 1994 Motorola Inc.
*       All rights reserved.
*
*       THE SOFTWARE is provided on an "AS IS" basis and without warranty.
*       To the maximum extent permitted by applicable law,
*       MOTOROLA DISCLAIMS ALL WARRANTIES WHETHER EXPRESS OR IMPLIED,
*       INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
*       PARTICULAR PURPOSE and any warranty against infringement with
*       regard to the SOFTWARE (INCLUDING ANY MODIFIED VERSIONS THEREOF)
*       and any accompanying written materials.
*
*       To the maximum extent permitted by applicable law,
*       IN NO EVENT SHALL MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER
*       (INCLUDING WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS
*       PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR
*       OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE
*       SOFTWARE.  Motorola assumes no responsibility for the maintenance
*       and support of the SOFTWARE.
*
*       You are hereby granted a copyright license to use, modify, and
*       distribute the SOFTWARE so long as this entire notice is retained
*       without alteration in any modified and/or redistributed versions,
*       and that such modified versions are clearly identified as such.
*       No licenses are granted by implication, estoppel or otherwise
*       under any patents or trademarks of Motorola, Inc.

*
*       gen_except.sa 3.7 1/16/92
*
*       gen_except --- FPSP routine to detect reportable exceptions
*
*       This routine compares the exception enable byte of the
*       user_fpcr on the stack with the exception status byte
*       of the user_fpsr.
*
*       Any routine which may report an exceptions must load
*       the stack frame in memory with the exceptional operand(s).
*
*       Priority for exceptions is:
*
*       Highest:        bsun
*                       snan
*                       operr
*                       ovfl
*                       unfl
*                       dz
*                       inex2
*       Lowest:         inex1
*
*       Note: The IEEE standard specifies that inex2 is to be
*       reported if ovfl occurs and the ovfl enable bit is not
*       set but the inex2 enable bit is.
*

GEN_EXCEPT    IDNT    2,1 Motorola 040 Floating Point Software Package

       section 8

       include fpsp.h

       xref    real_trace
       xref    fpsp_done
       xref    fpsp_fmt_error

exc_tbl:
       dc.l    bsun_exc
       dc.l    commonE1
       dc.l    commonE1
       dc.l    ovfl_unfl
       dc.l    ovfl_unfl
       dc.l    commonE1
       dc.l    commonE3
       dc.l    commonE3
       dc.l    no_match

       xdef    gen_except
gen_except:
       cmpi.b  #IDLE_SIZE-4,1(a7)      ;test for idle frame
       beq.w   do_check                ;go handle idle frame
       cmpi.b  #UNIMP_40_SIZE-4,1(a7)  ;test for orig unimp frame
       beq.b   unimp_x                 ;go handle unimp frame
       cmpi.b  #UNIMP_41_SIZE-4,1(a7)  ;test for rev unimp frame
       beq.b   unimp_x                 ;go handle unimp frame
       cmpi.b  #BUSY_SIZE-4,1(a7)      ;if size <> $60, fmt error
       bne.l   fpsp_fmt_error
       lea.l   BUSY_SIZE+LOCAL_SIZE(a7),a1 ;init a1 so fpsp.h
*                                       ;equates will work
* Fix up the new busy frame with entries from the unimp frame
*
       move.l  ETEMP_EX(a6),ETEMP_EX(a1) ;copy etemp from unimp
       move.l  ETEMP_HI(a6),ETEMP_HI(a1) ;frame to busy frame
       move.l  ETEMP_LO(a6),ETEMP_LO(a1)
       move.l  CMDREG1B(a6),CMDREG1B(a1) ;set inst in frame to unimp
       move.l  CMDREG1B(a6),d0         ;fix cmd1b to make it
       and.l   #$03c30000,d0           ;work for cmd3b
       bfextu  CMDREG1B(a6){13:1},d1   ;extract bit 2
       lsl.l   #5,d1
       swap    d1
       or.l    d1,d0                   ;put it in the right place
       bfextu  CMDREG1B(a6){10:3},d1   ;extract bit 3,4,5
       lsl.l   #2,d1
       swap    d1
       or.l    d1,d0                   ;put them in the right place
       move.l  d0,CMDREG3B(a1)         ;in the busy frame
*
* Or in the FPSR from the emulation with the USER_FPSR on the stack.
*
       fmove.l FPSR,d0
       or.l    d0,USER_FPSR(a6)
       move.l  USER_FPSR(a6),FPSR_SHADOW(a1) ;set exc bits
       or.l    #sx_mask,E_BYTE(a1)
       bra     do_clean

*
* Frame is an unimp frame possible resulting from an fmove <ea>,fp0
* that caused an exception
*
* a1 is modified to point into the new frame allowing fpsp equates
* to be valid.
*
unimp_x:
       cmpi.b  #UNIMP_40_SIZE-4,1(a7)  ;test for orig unimp frame
       bne.b   test_rev
       lea.l   UNIMP_40_SIZE+LOCAL_SIZE(a7),a1
       bra.b   unimp_con
test_rev:
       cmpi.b  #UNIMP_41_SIZE-4,1(a7)  ;test for rev unimp frame
       bne.l   fpsp_fmt_error          ;if not $28 or $30
       lea.l   UNIMP_41_SIZE+LOCAL_SIZE(a7),a1

unimp_con:
*
* Fix up the new unimp frame with entries from the old unimp frame
*
       move.l  CMDREG1B(a6),CMDREG1B(a1) ;set inst in frame to unimp
*
* Or in the FPSR from the emulation with the USER_FPSR on the stack.
*
       fmove.l FPSR,d0
       or.l    d0,USER_FPSR(a6)
       bra     do_clean

*
* Frame is idle, so check for exceptions reported through
* USER_FPSR and set the unimp frame accordingly.
* A7 must be incremented to the point before the
* idle fsave vector to the unimp vector.
*

do_check:
       add.l   #4,A7                   ;point A7 back to unimp frame
*
* Or in the FPSR from the emulation with the USER_FPSR on the stack.
*
       fmove.l FPSR,d0
       or.l    d0,USER_FPSR(a6)
*
* On a busy frame, we must clear the nmnexc bits.
*
       cmpi.b  #BUSY_SIZE-4,1(a7)      ;check frame type
       bne.b   check_fr                ;if busy, clr nmnexc
       clr.w   NMNEXC(a6)              ;clr nmnexc & nmcexc
       btst.b  #5,CMDREG1B(a6)         ;test for fmove out
       bne.b   frame_com
       move.l  USER_FPSR(a6),FPSR_SHADOW(a6) ;set exc bits
       or.l    #sx_mask,E_BYTE(a6)
       bra.b   frame_com
check_fr:
       cmp.b   #UNIMP_40_SIZE-4,1(a7)
       beq.b   frame_com
       clr.w   NMNEXC(a6)
frame_com:
       move.b  FPCR_ENABLE(a6),d0      ;get fpcr enable byte
       and.b   FPSR_EXCEPT(a6),d0      ;and in the fpsr exc byte
       bfffo   d0{24:8},d1             ;test for first set bit
       lea.l   exc_tbl,a0              ;load jmp table address
       subi.b  #24,d1                  ;normalize bit offset to 0-8
       move.l  (a0,d1.w*4),a0          ;load routine address based
*                                       ;based on first enabled exc
       jmp     (a0)                    ;jump to routine
*
* Bsun is not possible in unimp or unsupp
*
bsun_exc:
       bra     do_clean
*
* The typical work to be done to the unimp frame to report an
* exception is to set the E1/E3 byte and clr the U flag.
* commonE1 does this for E1 exceptions, which are snan,
* operr, and dz.  commonE3 does this for E3 exceptions, which
* are inex2 and inex1, and also clears the E1 exception bit
* left over from the unimp exception.
*
commonE1:
       bset.b  #E1,E_BYTE(a6)          ;set E1 flag
       bra.w   commonE                 ;go clean and exit

commonE3:
       tst.b   UFLG_TMP(a6)            ;test flag for unsup/unimp state
       bne.b   unsE3
uniE3:
       bset.b  #E3,E_BYTE(a6)          ;set E3 flag
       bclr.b  #E1,E_BYTE(a6)          ;clr E1 from unimp
       bra.w   commonE

unsE3:
       tst.b   RES_FLG(a6)
       bne.b   unsE3_0
unsE3_1:
       bset.b  #E3,E_BYTE(a6)          ;set E3 flag
unsE3_0:
       bclr.b  #E1,E_BYTE(a6)          ;clr E1 flag
       move.l  CMDREG1B(a6),d0
       and.l   #$03c30000,d0           ;work for cmd3b
       bfextu  CMDREG1B(a6){13:1},d1   ;extract bit 2
       lsl.l   #5,d1
       swap    d1
       or.l    d1,d0                   ;put it in the right place
       bfextu  CMDREG1B(a6){10:3},d1   ;extract bit 3,4,5
       lsl.l   #2,d1
       swap    d1
       or.l    d1,d0                   ;put them in the right place
       move.l  d0,CMDREG3B(a6)         ;in the busy frame

commonE:
       bclr.b  #UFLAG,T_BYTE(a6)       ;clr U flag from unimp
       bra.w   do_clean                ;go clean and exit
*
* No bits in the enable byte match existing exceptions.  Check for
* the case of the ovfl exc without the ovfl enabled, but with
* inex2 enabled.
*
no_match:
       btst.b  #inex2_bit,FPCR_ENABLE(a6) ;check for ovfl/inex2 case
       beq.b   no_exc                  ;if clear, exit
       btst.b  #ovfl_bit,FPSR_EXCEPT(a6) ;now check ovfl
       beq.b   no_exc                  ;if clear, exit
       bra.b   ovfl_unfl               ;go to unfl_ovfl to determine if
*                                       ;it is an unsupp or unimp exc

* No exceptions are to be reported.  If the instruction was
* unimplemented, no FPU restore is necessary.  If it was
* unsupported, we must perform the restore.
no_exc:
       tst.b   UFLG_TMP(a6)    ;test flag for unsupp/unimp state
       beq.b   uni_no_exc
uns_no_exc:
       tst.b   RES_FLG(a6)     ;check if frestore is needed
       bne.w   do_clean        ;if clear, no frestore needed
uni_no_exc:
       movem.l USER_DA(a6),d0-d1/a0-a1
       fmovem.x USER_FP0(a6),fp0-fp3
       fmovem.l USER_FPCR(a6),fpcr/fpsr/fpiar
       unlk    a6
       bra     finish_up
*
* Unsupported Data Type Handler:
* Ovfl:
*   An fmoveout that results in an overflow is reported this way.
* Unfl:
*   An fmoveout that results in an underflow is reported this way.
*
* Unimplemented Instruction Handler:
* Ovfl:
*   Only scosh, setox, ssinh, stwotox, and scale can set overflow in
*   this manner.
* Unfl:
*   Stwotox, setox, and scale can set underflow in this manner.
*   Any of the other Library Routines such that f(x)=x in which
*   x is an extended denorm can report an underflow exception.
*   It is the responsibility of the exception-causing exception
*   to make sure that WBTEMP is correct.
*
*   The exceptional operand is in FP_SCR1.
*
ovfl_unfl:
       tst.b   UFLG_TMP(a6)    ;test flag for unsupp/unimp state
       beq.b   ofuf_con
*
* The caller was from an unsupported data type trap.  Test if the
* caller set CU_ONLY.  If so, the exceptional operand is expected in
* FPTEMP, rather than WBTEMP.
*
       tst.b   CU_ONLY(a6)             ;test if inst is cu-only
       beq.w   unsE3
*       move.w  #$fe,CU_SAVEPC(a6)
       clr.b   CU_SAVEPC(a6)
       bset.b  #E1,E_BYTE(a6)          ;set E1 exception flag
       move.w  ETEMP_EX(a6),FPTEMP_EX(a6)
       move.l  ETEMP_HI(a6),FPTEMP_HI(a6)
       move.l  ETEMP_LO(a6),FPTEMP_LO(a6)
       bset.b  #fptemp15_bit,DTAG(a6)  ;set fpte15
       bclr.b  #UFLAG,T_BYTE(a6)       ;clr U flag from unimp
       bra.w   do_clean                ;go clean and exit

ofuf_con:
       move.b  (a7),VER_TMP(a6)        ;save version number
       cmpi.b  #BUSY_SIZE-4,1(a7)      ;check for busy frame
       beq.b   busy_fr                 ;if unimp, grow to busy
       cmpi.b  #VER_40,(a7)            ;test for orig unimp frame
       bne.b   try_41                  ;if not, test for rev frame
       moveq.l #13,d0                  ;need to zero 14 lwords
       bra.b   ofuf_fin
try_41:
       cmpi.b  #VER_41,(a7)            ;test for rev unimp frame
       bne.l   fpsp_fmt_error          ;if neither, exit with error
       moveq.l #11,d0                  ;need to zero 12 lwords

ofuf_fin:
       clr.l   (a7)
loop1:
       clr.l   -(a7)                   ;clear and dec a7
       dbra.w  d0,loop1
       move.b  VER_TMP(a6),(a7)
       move.b  #BUSY_SIZE-4,1(a7)              ;write busy fmt word.
busy_fr:
       move.l  FP_SCR1(a6),WBTEMP_EX(a6)       ;write
       move.l  FP_SCR1+4(a6),WBTEMP_HI(a6)     ;exceptional op to
       move.l  FP_SCR1+8(a6),WBTEMP_LO(a6)     ;wbtemp
       bset.b  #E3,E_BYTE(a6)                  ;set E3 flag
       bclr.b  #E1,E_BYTE(a6)                  ;make sure E1 is clear
       bclr.b  #UFLAG,T_BYTE(a6)               ;clr U flag
       move.l  USER_FPSR(a6),FPSR_SHADOW(a6)
       or.l    #sx_mask,E_BYTE(a6)
       move.l  CMDREG1B(a6),d0         ;fix cmd1b to make it
       and.l   #$03c30000,d0           ;work for cmd3b
       bfextu  CMDREG1B(a6){13:1},d1   ;extract bit 2
       lsl.l   #5,d1
       swap    d1
       or.l    d1,d0                   ;put it in the right place
       bfextu  CMDREG1B(a6){10:3},d1   ;extract bit 3,4,5
       lsl.l   #2,d1
       swap    d1
       or.l    d1,d0                   ;put them in the right place
       move.l  d0,CMDREG3B(a6)         ;in the busy frame

*
* Check if the frame to be restored is busy or unimp.
*** NOTE *** Bug fix for errata (0d43b #3)
* If the frame is unimp, we must create a busy frame to
* fix the bug with the nmnexc bits in cases in which they
* are set by a previous instruction and not cleared by
* the save. The frame will be unimp only if the final
* instruction in an emulation routine caused the exception
* by doing an fmove <ea>,fp0.  The exception operand, in
* internal format, is in fptemp.
*
do_clean:
       cmpi.b  #UNIMP_40_SIZE-4,1(a7)
       bne.b   do_con
       moveq.l #13,d0                  ;in orig, need to zero 14 lwords
       bra.b   do_build
do_con:
       cmpi.b  #UNIMP_41_SIZE-4,1(a7)
       bne.b   do_restore              ;frame must be busy
       moveq.l #11,d0                  ;in rev, need to zero 12 lwords

do_build:
       move.b  (a7),VER_TMP(a6)
       clr.l   (a7)
loop2:
       clr.l   -(a7)                   ;clear and dec a7
       dbra.w  d0,loop2
*
* Use a1 as pointer into new frame.  a6 is not correct if an unimp or
* busy frame was created as the result of an exception on the final
* instruction of an emulation routine.
*
* We need to set the nmcexc bits if the exception is E1. Otherwise,
* the exc taken will be inex2.
*
       lea.l   BUSY_SIZE+LOCAL_SIZE(a7),a1     ;init a1 for new frame
       move.b  VER_TMP(a6),(a7)        ;write busy fmt word
       move.b  #BUSY_SIZE-4,1(a7)
       move.l  FP_SCR1(a6),WBTEMP_EX(a1)       ;write
       move.l  FP_SCR1+4(a6),WBTEMP_HI(a1)     ;exceptional op to
       move.l  FP_SCR1+8(a6),WBTEMP_LO(a1)     ;wbtemp
*       btst.b  #E1,E_BYTE(a1)
*       beq.b   do_restore
       bfextu  USER_FPSR(a6){17:4},d0  ;get snan/operr/ovfl/unfl bits
       bfins   d0,NMCEXC(a1){4:4}      ;and insert them in nmcexc
       move.l  USER_FPSR(a6),FPSR_SHADOW(a1) ;set exc bits
       or.l    #sx_mask,E_BYTE(a1)

do_restore:
       movem.l USER_DA(a6),d0-d1/a0-a1
       fmovem.x USER_FP0(a6),fp0-fp3
       fmovem.l USER_FPCR(a6),fpcr/fpsr/fpiar
       frestore (a7)+
       tst.b   RES_FLG(a6)     ;RES_FLG indicates a "continuation" frame
       beq     cont
       bsr     bug1384
cont:
       unlk    a6
*
* If trace mode enabled, then go to trace handler.  This handler
* cannot have any fp instructions.  If there are fp inst's and an
* exception has been restored into the machine then the exception
* will occur upon execution of the fp inst.  This is not desirable
* in the kernel (supervisor mode).  See MC68040 manual Section 9.3.8.
*
finish_up:
       btst.b  #7,(a7)         ;test T1 in SR
       bne.b   g_trace
       btst.b  #6,(a7)         ;test T0 in SR
       bne.b   g_trace
       bra.l   fpsp_done
*
* Change integer stack to look like trace stack
* The address of the instruction that caused the
* exception is already in the integer stack (is
* the same as the saved friar)
*
* If the current frame is already a 6-word stack then all
* that needs to be done is to change the vector# to TRACE.
* If the frame is only a 4-word stack (meaning we got here
* on an Unsupported data type exception), then we need to grow
* the stack an extra 2 words and get the FPIAR from the FPU.
*
g_trace:
       bftst   EXC_VEC-4(sp){0:4}
       bne     g_easy

       subq.l  #4,sp           make room
       move.l  4(sp),(sp)
       move.l  8(sp),4(sp)
       sub.l   #BUSY_SIZE,sp
       fsave   (sp)
       fmove.l fpiar,BUSY_SIZE+EXC_EA-4(sp)
       frestore (sp)
       add.l   #BUSY_SIZE,sp

g_easy:
       move.w  #TRACE_VEC,EXC_VEC-4(a7)
       bra.l   real_trace
*
*  This is a work-around for hardware bug 1384.
*
bug1384:
       link    a5,#0
       fsave   -(sp)
       cmpi.b  #$41,(sp)       ; check for correct frame
       beq     frame_41
       bgt     nofix           ; if more advanced mask, do nada

frame_40:
       tst.b   1(sp)           ; check to see if idle
       bne     notidle
idle40:
       clr.l   (sp)            ; get rid of old fsave frame
       move.l  d1,USER_D1(a6)  ; save d1
       move.w  #8,d1           ; place unimp frame instead
loop40: clr.l   -(sp)
       dbra    d1,loop40
       move.l  USER_D1(a6),d1  ; restore d1
       move.l  #$40280000,-(sp)
       frestore (sp)+
       unlk    a5
       rts

frame_41:
       tst.b   1(sp)           ; check to see if idle
       bne     notidle
idle41:
       clr.l   (sp)            ; get rid of old fsave frame
       move.l  d1,USER_D1(a6)  ; save d1
       move.w  #10,d1          ; place unimp frame instead
loop41: clr.l   -(sp)
       dbra    d1,loop41
       move.l  USER_D1(a6),d1  ; restore d1
       move.l  #$41300000,-(sp)
       frestore (sp)+
       unlk    a5
       rts

notidle:
       bclr.b  #etemp15_bit,-40(a5)
       frestore (sp)+
       unlk    a5
       rts

nofix:
       frestore (sp)+
       unlk    a5
       rts

       end