# # $NetBSD: fpsp.s,v 1.7 2023/06/24 05:31:04 msaitoh Exp $ # #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # MOTOROLA MICROPROCESSOR & MEMORY TECHNOLOGY GROUP # M68000 Hi-Performance Microprocessor Division # M68060 Software Package Production Release # # M68060 Software Package Copyright (C) 1993, 1994, 1995, 1996 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. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # freal.s: # This file is appended to the top of the 060FPSP package # and contains the entry points into the package. The user, in # effect, branches to one of the branch table entries located # after _060FPSP_TABLE. # Also, subroutine stubs exist in this file (_fpsp_done for # example) that are referenced by the FPSP package itself in order # to call a given routine. The stub routine actually performs the # callout. The FPSP code does a "bsr" to the stub routine. This # extra layer of hierarchy adds a slight performance penalty but # it makes the FPSP code easier to read and more mainatinable. # set _off_bsun, 0x00 set _off_snan, 0x04 set _off_operr, 0x08 set _off_ovfl, 0x0c set _off_unfl, 0x10 set _off_dz, 0x14 set _off_inex, 0x18 set _off_fline, 0x1c set _off_fpu_dis, 0x20 set _off_trap, 0x24 set _off_trace, 0x28 set _off_access, 0x2c set _off_done, 0x30 set _off_imr, 0x40 set _off_dmr, 0x44 set _off_dmw, 0x48 set _off_irw, 0x4c set _off_irl, 0x50 set _off_drb, 0x54 set _off_drw, 0x58 set _off_drl, 0x5c set _off_dwb, 0x60 set _off_dww, 0x64 set _off_dwl, 0x68 _060FPSP_TABLE: ############################################################### # Here's the table of ENTRY POINTS for those linking the package. bra.l _fpsp_snan short 0x0000 bra.l _fpsp_operr short 0x0000 bra.l _fpsp_ovfl short 0x0000 bra.l _fpsp_unfl short 0x0000 bra.l _fpsp_dz short 0x0000 bra.l _fpsp_inex short 0x0000 bra.l _fpsp_fline short 0x0000 bra.l _fpsp_unsupp short 0x0000 bra.l _fpsp_effadd short 0x0000 space 56 ############################################################### global _fpsp_done _fpsp_done: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_done,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_ovfl _real_ovfl: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_ovfl,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_unfl _real_unfl: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_unfl,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_inex _real_inex: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_inex,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_bsun _real_bsun: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_bsun,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_operr _real_operr: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_operr,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_snan _real_snan: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_snan,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_dz _real_dz: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_dz,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_fline _real_fline: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_fline,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_fpu_disabled _real_fpu_disabled: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_fpu_dis,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_trap _real_trap: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_trap,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_trace _real_trace: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_trace,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _real_access _real_access: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_access,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 ####################################### global _imem_read _imem_read: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_imr,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_read _dmem_read: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_dmr,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_write _dmem_write: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_dmw,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _imem_read_word _imem_read_word: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_irw,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _imem_read_long _imem_read_long: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_irl,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_read_byte _dmem_read_byte: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_drb,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_read_word _dmem_read_word: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_drw,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_read_long _dmem_read_long: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_drl,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_write_byte _dmem_write_byte: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_dwb,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_write_word _dmem_write_word: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_dww,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 global _dmem_write_long _dmem_write_long: mov.l %d0,-(%sp) mov.l (_060FPSP_TABLE-0x80+_off_dwl,%pc),%d0 pea.l (_060FPSP_TABLE-0x80,%pc,%d0) mov.l 0x4(%sp),%d0 rtd &0x4 # # This file contains a set of define statements for constants # in order to promote readability within the corecode itself. # set LOCAL_SIZE, 192 # stack frame size(bytes) set LV, -LOCAL_SIZE # stack offset set EXC_SR, 0x4 # stack status register set EXC_PC, 0x6 # stack pc set EXC_VOFF, 0xa # stacked vector offset set EXC_EA, 0xc # stacked set EXC_FP, 0x0 # frame pointer set EXC_AREGS, -68 # offset of all address regs set EXC_DREGS, -100 # offset of all data regs set EXC_FPREGS, -36 # offset of all fp regs set EXC_A7, EXC_AREGS+(7*4) # offset of saved a7 set OLD_A7, EXC_AREGS+(6*4) # extra copy of saved a7 set EXC_A6, EXC_AREGS+(6*4) # offset of saved a6 set EXC_A5, EXC_AREGS+(5*4) set EXC_A4, EXC_AREGS+(4*4) set EXC_A3, EXC_AREGS+(3*4) set EXC_A2, EXC_AREGS+(2*4) set EXC_A1, EXC_AREGS+(1*4) set EXC_A0, EXC_AREGS+(0*4) set EXC_D7, EXC_DREGS+(7*4) set EXC_D6, EXC_DREGS+(6*4) set EXC_D5, EXC_DREGS+(5*4) set EXC_D4, EXC_DREGS+(4*4) set EXC_D3, EXC_DREGS+(3*4) set EXC_D2, EXC_DREGS+(2*4) set EXC_D1, EXC_DREGS+(1*4) set EXC_D0, EXC_DREGS+(0*4) set EXC_FP0, EXC_FPREGS+(0*12) # offset of saved fp0 set EXC_FP1, EXC_FPREGS+(1*12) # offset of saved fp1 set EXC_FP2, EXC_FPREGS+(2*12) # offset of saved fp2 (not used) set FP_SCR1, LV+80 # fp scratch 1 set FP_SCR1_EX, FP_SCR1+0 set FP_SCR1_SGN, FP_SCR1+2 set FP_SCR1_HI, FP_SCR1+4 set FP_SCR1_LO, FP_SCR1+8 set FP_SCR0, LV+68 # fp scratch 0 set FP_SCR0_EX, FP_SCR0+0 set FP_SCR0_SGN, FP_SCR0+2 set FP_SCR0_HI, FP_SCR0+4 set FP_SCR0_LO, FP_SCR0+8 set FP_DST, LV+56 # fp destination operand set FP_DST_EX, FP_DST+0 set FP_DST_SGN, FP_DST+2 set FP_DST_HI, FP_DST+4 set FP_DST_LO, FP_DST+8 set FP_SRC, LV+44 # fp source operand set FP_SRC_EX, FP_SRC+0 set FP_SRC_SGN, FP_SRC+2 set FP_SRC_HI, FP_SRC+4 set FP_SRC_LO, FP_SRC+8 set USER_FPIAR, LV+40 # FP instr address register set USER_FPSR, LV+36 # FP status register set FPSR_CC, USER_FPSR+0 # FPSR condition codes set FPSR_QBYTE, USER_FPSR+1 # FPSR qoutient byte set FPSR_EXCEPT, USER_FPSR+2 # FPSR exception status byte set FPSR_AEXCEPT, USER_FPSR+3 # FPSR accrued exception byte set USER_FPCR, LV+32 # FP control register set FPCR_ENABLE, USER_FPCR+2 # FPCR exception enable set FPCR_MODE, USER_FPCR+3 # FPCR rounding mode control set L_SCR3, LV+28 # integer scratch 3 set L_SCR2, LV+24 # integer scratch 2 set L_SCR1, LV+20 # integer scratch 1 set STORE_FLG, LV+19 # flag: operand store (ie. not fcmp/ftst) set EXC_TEMP2, LV+24 # temporary space set EXC_TEMP, LV+16 # temporary space set DTAG, LV+15 # destination operand type set STAG, LV+14 # source operand type set SPCOND_FLG, LV+10 # flag: special case (see below) set EXC_CC, LV+8 # saved condition codes set EXC_EXTWPTR, LV+4 # saved current PC (active) set EXC_EXTWORD, LV+2 # saved extension word set EXC_CMDREG, LV+2 # saved extension word set EXC_OPWORD, LV+0 # saved operation word ################################ # Helpful macros set FTEMP, 0 # offsets within an set FTEMP_EX, 0 # extended precision set FTEMP_SGN, 2 # value saved in memory. set FTEMP_HI, 4 set FTEMP_LO, 8 set FTEMP_GRS, 12 set LOCAL, 0 # offsets within an set LOCAL_EX, 0 # extended precision set LOCAL_SGN, 2 # value saved in memory. set LOCAL_HI, 4 set LOCAL_LO, 8 set LOCAL_GRS, 12 set DST, 0 # offsets within an set DST_EX, 0 # extended precision set DST_HI, 4 # value saved in memory. set DST_LO, 8 set SRC, 0 # offsets within an set SRC_EX, 0 # extended precision set SRC_HI, 4 # value saved in memory. set SRC_LO, 8 set SGL_LO, 0x3f81 # min sgl prec exponent set SGL_HI, 0x407e # max sgl prec exponent set DBL_LO, 0x3c01 # min dbl prec exponent set DBL_HI, 0x43fe # max dbl prec exponent set EXT_LO, 0x0 # min ext prec exponent set EXT_HI, 0x7ffe # max ext prec exponent set EXT_BIAS, 0x3fff # extended precision bias set SGL_BIAS, 0x007f # single precision bias set DBL_BIAS, 0x03ff # double precision bias set NORM, 0x00 # operand type for STAG/DTAG set ZERO, 0x01 # operand type for STAG/DTAG set INF, 0x02 # operand type for STAG/DTAG set QNAN, 0x03 # operand type for STAG/DTAG set DENORM, 0x04 # operand type for STAG/DTAG set SNAN, 0x05 # operand type for STAG/DTAG set UNNORM, 0x06 # operand type for STAG/DTAG ################## # FPSR/FPCR bits # ################## set neg_bit, 0x3 # negative result set z_bit, 0x2 # zero result set inf_bit, 0x1 # infinite result set nan_bit, 0x0 # NAN result set q_sn_bit, 0x7 # sign bit of quotient byte set bsun_bit, 7 # branch on unordered set snan_bit, 6 # signalling NAN set operr_bit, 5 # operand error set ovfl_bit, 4 # overflow set unfl_bit, 3 # underflow set dz_bit, 2 # divide by zero set inex2_bit, 1 # inexact result 2 set inex1_bit, 0 # inexact result 1 set aiop_bit, 7 # accrued inexact operation bit set aovfl_bit, 6 # accrued overflow bit set aunfl_bit, 5 # accrued underflow bit set adz_bit, 4 # accrued dz bit set ainex_bit, 3 # accrued inexact bit ############################# # FPSR individual bit masks # ############################# set neg_mask, 0x08000000 # negative bit mask (lw) set inf_mask, 0x02000000 # infinity bit mask (lw) set z_mask, 0x04000000 # zero bit mask (lw) set nan_mask, 0x01000000 # nan bit mask (lw) set neg_bmask, 0x08 # negative bit mask (byte) set inf_bmask, 0x02 # infinity bit mask (byte) set z_bmask, 0x04 # zero bit mask (byte) set nan_bmask, 0x01 # nan bit mask (byte) set bsun_mask, 0x00008000 # bsun exception mask set snan_mask, 0x00004000 # snan exception mask set operr_mask, 0x00002000 # operr exception mask set ovfl_mask, 0x00001000 # overflow exception mask set unfl_mask, 0x00000800 # underflow exception mask set dz_mask, 0x00000400 # dz exception mask set inex2_mask, 0x00000200 # inex2 exception mask set inex1_mask, 0x00000100 # inex1 exception mask set aiop_mask, 0x00000080 # accrued illegal operation set aovfl_mask, 0x00000040 # accrued overflow set aunfl_mask, 0x00000020 # accrued underflow set adz_mask, 0x00000010 # accrued divide by zero set ainex_mask, 0x00000008 # accrued inexact ###################################### # FPSR combinations used in the FPSP # ###################################### set dzinf_mask, inf_mask+dz_mask+adz_mask set opnan_mask, nan_mask+operr_mask+aiop_mask set nzi_mask, 0x01ffffff #clears N, Z, and I set unfinx_mask, unfl_mask+inex2_mask+aunfl_mask+ainex_mask set unf2inx_mask, unfl_mask+inex2_mask+ainex_mask set ovfinx_mask, ovfl_mask+inex2_mask+aovfl_mask+ainex_mask set inx1a_mask, inex1_mask+ainex_mask set inx2a_mask, inex2_mask+ainex_mask set snaniop_mask, nan_mask+snan_mask+aiop_mask set snaniop2_mask, snan_mask+aiop_mask set naniop_mask, nan_mask+aiop_mask set neginf_mask, neg_mask+inf_mask set infaiop_mask, inf_mask+aiop_mask set negz_mask, neg_mask+z_mask set opaop_mask, operr_mask+aiop_mask set unfl_inx_mask, unfl_mask+aunfl_mask+ainex_mask set ovfl_inx_mask, ovfl_mask+aovfl_mask+ainex_mask ######### # misc. # ######### set rnd_stky_bit, 29 # stky bit pos in longword set sign_bit, 0x7 # sign bit set signan_bit, 0x6 # signalling nan bit set sgl_thresh, 0x3f81 # minimum sgl exponent set dbl_thresh, 0x3c01 # minimum dbl exponent set x_mode, 0x0 # extended precision set s_mode, 0x4 # single precision set d_mode, 0x8 # double precision set rn_mode, 0x0 # round-to-nearest set rz_mode, 0x1 # round-to-zero set rm_mode, 0x2 # round-tp-minus-infinity set rp_mode, 0x3 # round-to-plus-infinity set mantissalen, 64 # length of mantissa in bits set BYTE, 1 # len(byte) == 1 byte set WORD, 2 # len(word) == 2 bytes set LONG, 4 # len(longword) == 2 bytes set BSUN_VEC, 0xc0 # bsun vector offset set INEX_VEC, 0xc4 # inexact vector offset set DZ_VEC, 0xc8 # dz vector offset set UNFL_VEC, 0xcc # unfl vector offset set OPERR_VEC, 0xd0 # operr vector offset set OVFL_VEC, 0xd4 # ovfl vector offset set SNAN_VEC, 0xd8 # snan vector offset ########################### # SPecial CONDition FLaGs # ########################### set ftrapcc_flg, 0x01 # flag bit: ftrapcc exception set fbsun_flg, 0x02 # flag bit: bsun exception set mia7_flg, 0x04 # flag bit: (a7)+ set mda7_flg, 0x08 # flag bit: -(a7) set fmovm_flg, 0x40 # flag bit: fmovm instruction set immed_flg, 0x80 # flag bit: & set ftrapcc_bit, 0x0 set fbsun_bit, 0x1 set mia7_bit, 0x2 set mda7_bit, 0x3 set immed_bit, 0x7 ################################## # TRANSCENDENTAL "LAST-OP" FLAGS # ################################## set FMUL_OP, 0x0 # fmul instr performed last set FDIV_OP, 0x1 # fdiv performed last set FADD_OP, 0x2 # fadd performed last set FMOV_OP, 0x3 # fmov performed last ############# # CONSTANTS # ############# T1: long 0x40C62D38,0xD3D64634 # 16381 LOG2 LEAD T2: long 0x3D6F90AE,0xB1E75CC7 # 16381 LOG2 TRAIL PI: long 0x40000000,0xC90FDAA2,0x2168C235,0x00000000 PIBY2: long 0x3FFF0000,0xC90FDAA2,0x2168C235,0x00000000 TWOBYPI: long 0x3FE45F30,0x6DC9C883 ######################################################################### # XDEF **************************************************************** # # _fpsp_ovfl(): 060FPSP entry point for FP Overflow exception. # # # # This handler should be the first code executed upon taking the # # FP Overflow exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword # # fix_skewed_ops() - adjust src operand in fsave frame # # set_tag_x() - determine optype of src/dst operands # # store_fpreg() - store opclass 0 or 2 result to FP regfile # # unnorm_fix() - change UNNORM operands to NORM or ZERO # # load_fpn2() - load dst operand from FP regfile # # fout() - emulate an opclass 3 instruction # # tbl_unsupp - add of table of emulation routines for opclass 0,2 # # _fpsp_done() - "callout" for 060FPSP exit (all work done!) # # _real_ovfl() - "callout" for Overflow exception enabled code # # _real_inex() - "callout" for Inexact exception enabled code # # _real_trace() - "callout" for Trace exception code # # # # INPUT *************************************************************** # # - The system stack contains the FP Ovfl exception stack frame # # - The fsave frame contains the source operand # # # # OUTPUT ************************************************************** # # Overflow Exception enabled: # # - The system stack is unchanged # # - The fsave frame contains the adjusted src op for opclass 0,2 # # Overflow Exception disabled: # # - The system stack is unchanged # # - The "exception present" flag in the fsave frame is cleared # # # # ALGORITHM *********************************************************** # # On the 060, if an FP overflow is present as the result of any # # instruction, the 060 will take an overflow exception whether the # # exception is enabled or disabled in the FPCR. For the disabled case, # # This handler emulates the instruction to determine what the correct # # default result should be for the operation. This default result is # # then stored in either the FP regfile, data regfile, or memory. # # Finally, the handler exits through the "callout" _fpsp_done() # # denoting that no exceptional conditions exist within the machine. # # If the exception is enabled, then this handler must create the # # exceptional operand and plave it in the fsave state frame, and store # # the default result (only if the instruction is opclass 3). For # # exceptions enabled, this handler must exit through the "callout" # # _real_ovfl() so that the operating system enabled overflow handler # # can handle this case. # # Two other conditions exist. First, if overflow was disabled # # but the inexact exception was enabled, this handler must exit # # through the "callout" _real_inex() regardless of whether the result # # was inexact. # # Also, in the case of an opclass three instruction where # # overflow was disabled and the trace exception was enabled, this # # handler must exit through the "callout" _real_trace(). # # # ######################################################################### global _fpsp_ovfl _fpsp_ovfl: #$# sub.l &24,%sp # make room for src/dst link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # grab the "busy" frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # the FPIAR holds the "current PC" of the faulting instruction mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################## btst &0x5,EXC_CMDREG(%a6) # is instr an fmove out? bne.w fovfl_out lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l fix_skewed_ops # fix src op # since, I believe, only NORMs and DENORMs can come through here, # maybe we can avoid the subroutine call. lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l set_tag_x # tag the operand type mov.b %d0,STAG(%a6) # maybe NORM,DENORM # bit five of the fp extension word separates the monadic and dyadic operations # that can pass through fpsp_ovfl(). remember that fcmp, ftst, and fsincos # will never take this exception. btst &0x5,1+EXC_CMDREG(%a6) # is operation monadic or dyadic? beq.b fovfl_extract # monadic bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg bsr.l load_fpn2 # load dst into FP_DST lea FP_DST(%a6),%a0 # pass: ptr to dst op bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b fovfl_op2_done # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO fovfl_op2_done: mov.b %d0,DTAG(%a6) # save dst optype tag fovfl_extract: #$# mov.l FP_SRC_EX(%a6),TRAP_SRCOP_EX(%a6) #$# mov.l FP_SRC_HI(%a6),TRAP_SRCOP_HI(%a6) #$# mov.l FP_SRC_LO(%a6),TRAP_SRCOP_LO(%a6) #$# mov.l FP_DST_EX(%a6),TRAP_DSTOP_EX(%a6) #$# mov.l FP_DST_HI(%a6),TRAP_DSTOP_HI(%a6) #$# mov.l FP_DST_LO(%a6),TRAP_DSTOP_LO(%a6) clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec/mode mov.b 1+EXC_CMDREG(%a6),%d1 andi.w &0x007f,%d1 # extract extension andi.l &0x00ff01ff,USER_FPSR(%a6) # zero all but accured field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr lea FP_SRC(%a6),%a0 lea FP_DST(%a6),%a1 # maybe we can make these entry points ONLY the OVFL entry points of each routine. mov.l (tbl_unsupp.l,%pc,%d1.w*4),%d1 # fetch routine addr jsr (tbl_unsupp.l,%pc,%d1.l*1) # the operation has been emulated. the result is in fp0. # the EXOP, if an exception occurred, is in fp1. # we must save the default result regardless of whether # traps are enabled or disabled. bfextu EXC_CMDREG(%a6){&6:&3},%d0 bsr.l store_fpreg # the exceptional possibilities we have left ourselves with are ONLY overflow # and inexact. and, the inexact is such that overflow occurred and was disabled # but inexact was enabled. btst &ovfl_bit,FPCR_ENABLE(%a6) bne.b fovfl_ovfl_on btst &inex2_bit,FPCR_ENABLE(%a6) bne.b fovfl_inex_on fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 #$# add.l &24,%sp bra.l _fpsp_done # overflow is enabled AND overflow, of course, occurred. so, we have the EXOP # in fp1. now, simply jump to _real_ovfl()! fovfl_ovfl_on: fmovm.x &0x40,FP_SRC(%a6) # save EXOP (fp1) to stack mov.w &0xe005,2+FP_SRC(%a6) # save exc status fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # do this after fmovm,other fs! unlk %a6 bra.l _real_ovfl # overflow occurred but is disabled. meanwhile, inexact is enabled. therefore, # we must jump to real_inex(). fovfl_inex_on: fmovm.x &0x40,FP_SRC(%a6) # save EXOP (fp1) to stack mov.b &0xc4,1+EXC_VOFF(%a6) # vector offset = 0xc4 mov.w &0xe001,2+FP_SRC(%a6) # save exc status fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # do this after fmovm,other fs! unlk %a6 bra.l _real_inex ######################################################################## fovfl_out: #$# mov.l FP_SRC_EX(%a6),TRAP_SRCOP_EX(%a6) #$# mov.l FP_SRC_HI(%a6),TRAP_SRCOP_HI(%a6) #$# mov.l FP_SRC_LO(%a6),TRAP_SRCOP_LO(%a6) # the src operand is definitely a NORM(!), so tag it as such mov.b &NORM,STAG(%a6) # set src optype tag clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec/mode and.l &0xffff00ff,USER_FPSR(%a6) # zero all but accured field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr lea FP_SRC(%a6),%a0 # pass ptr to src operand bsr.l fout btst &ovfl_bit,FPCR_ENABLE(%a6) bne.w fovfl_ovfl_on btst &inex2_bit,FPCR_ENABLE(%a6) bne.w fovfl_inex_on fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 #$# add.l &24,%sp btst &0x7,(%sp) # is trace on? beq.l _fpsp_done # no fmov.l %fpiar,0x8(%sp) # "Current PC" is in FPIAR mov.w &0x2024,0x6(%sp) # stk fmt = 0x2; voff = 0x024 bra.l _real_trace ######################################################################### # XDEF **************************************************************** # # _fpsp_unfl(): 060FPSP entry point for FP Underflow exception. # # # # This handler should be the first code executed upon taking the # # FP Underflow exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword # # fix_skewed_ops() - adjust src operand in fsave frame # # set_tag_x() - determine optype of src/dst operands # # store_fpreg() - store opclass 0 or 2 result to FP regfile # # unnorm_fix() - change UNNORM operands to NORM or ZERO # # load_fpn2() - load dst operand from FP regfile # # fout() - emulate an opclass 3 instruction # # tbl_unsupp - add of table of emulation routines for opclass 0,2 # # _fpsp_done() - "callout" for 060FPSP exit (all work done!) # # _real_ovfl() - "callout" for Overflow exception enabled code # # _real_inex() - "callout" for Inexact exception enabled code # # _real_trace() - "callout" for Trace exception code # # # # INPUT *************************************************************** # # - The system stack contains the FP Unfl exception stack frame # # - The fsave frame contains the source operand # # # # OUTPUT ************************************************************** # # Underflow Exception enabled: # # - The system stack is unchanged # # - The fsave frame contains the adjusted src op for opclass 0,2 # # Underflow Exception disabled: # # - The system stack is unchanged # # - The "exception present" flag in the fsave frame is cleared # # # # ALGORITHM *********************************************************** # # On the 060, if an FP underflow is present as the result of any # # instruction, the 060 will take an underflow exception whether the # # exception is enabled or disabled in the FPCR. For the disabled case, # # This handler emulates the instruction to determine what the correct # # default result should be for the operation. This default result is # # then stored in either the FP regfile, data regfile, or memory. # # Finally, the handler exits through the "callout" _fpsp_done() # # denoting that no exceptional conditions exist within the machine. # # If the exception is enabled, then this handler must create the # # exceptional operand and plave it in the fsave state frame, and store # # the default result (only if the instruction is opclass 3). For # # exceptions enabled, this handler must exit through the "callout" # # _real_unfl() so that the operating system enabled overflow handler # # can handle this case. # # Two other conditions exist. First, if underflow was disabled # # but the inexact exception was enabled and the result was inexact, # # this handler must exit through the "callout" _real_inex(). # # was inexact. # # Also, in the case of an opclass three instruction where # # underflow was disabled and the trace exception was enabled, this # # handler must exit through the "callout" _real_trace(). # # # ######################################################################### global _fpsp_unfl _fpsp_unfl: #$# sub.l &24,%sp # make room for src/dst link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # grab the "busy" frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # the FPIAR holds the "current PC" of the faulting instruction mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################## btst &0x5,EXC_CMDREG(%a6) # is instr an fmove out? bne.w funfl_out lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l fix_skewed_ops # fix src op lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l set_tag_x # tag the operand type mov.b %d0,STAG(%a6) # maybe NORM,DENORM # bit five of the fp ext word separates the monadic and dyadic operations # that can pass through fpsp_unfl(). remember that fcmp, and ftst # will never take this exception. btst &0x5,1+EXC_CMDREG(%a6) # is op monadic or dyadic? beq.b funfl_extract # monadic # now, what's left that's not dyadic is fsincos. we can distinguish it # from all dyadics by the '0110xxx pattern btst &0x4,1+EXC_CMDREG(%a6) # is op an fsincos? bne.b funfl_extract # yes bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg bsr.l load_fpn2 # load dst into FP_DST lea FP_DST(%a6),%a0 # pass: ptr to dst op bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b funfl_op2_done # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO funfl_op2_done: mov.b %d0,DTAG(%a6) # save dst optype tag funfl_extract: #$# mov.l FP_SRC_EX(%a6),TRAP_SRCOP_EX(%a6) #$# mov.l FP_SRC_HI(%a6),TRAP_SRCOP_HI(%a6) #$# mov.l FP_SRC_LO(%a6),TRAP_SRCOP_LO(%a6) #$# mov.l FP_DST_EX(%a6),TRAP_DSTOP_EX(%a6) #$# mov.l FP_DST_HI(%a6),TRAP_DSTOP_HI(%a6) #$# mov.l FP_DST_LO(%a6),TRAP_DSTOP_LO(%a6) clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec/mode mov.b 1+EXC_CMDREG(%a6),%d1 andi.w &0x007f,%d1 # extract extension andi.l &0x00ff01ff,USER_FPSR(%a6) fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr lea FP_SRC(%a6),%a0 lea FP_DST(%a6),%a1 # maybe we can make these entry points ONLY the OVFL entry points of each routine. mov.l (tbl_unsupp.l,%pc,%d1.w*4),%d1 # fetch routine addr jsr (tbl_unsupp.l,%pc,%d1.l*1) bfextu EXC_CMDREG(%a6){&6:&3},%d0 bsr.l store_fpreg # The `060 FPU multiplier hardware is such that if the result of a # multiply operation is the smallest possible normalized number # (0x00000000_80000000_00000000), then the machine will take an # underflow exception. Since this is incorrect, we need to check # if our emulation, after re-doing the operation, decided that # no underflow was called for. We do these checks only in # funfl_{unfl,inex}_on() because w/ both exceptions disabled, this # special case will simply exit gracefully with the correct result. # the exceptional possibilities we have left ourselves with are ONLY overflow # and inexact. and, the inexact is such that overflow occurred and was disabled # but inexact was enabled. btst &unfl_bit,FPCR_ENABLE(%a6) bne.b funfl_unfl_on funfl_chkinex: btst &inex2_bit,FPCR_ENABLE(%a6) bne.b funfl_inex_on funfl_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 #$# add.l &24,%sp bra.l _fpsp_done # overflow is enabled AND overflow, of course, occurred. so, we have the EXOP # in fp1 (don't forget to save fp0). what to do now? # well, we simply have to get to go to _real_unfl()! funfl_unfl_on: # The `060 FPU multiplier hardware is such that if the result of a # multiply operation is the smallest possible normalized number # (0x00000000_80000000_00000000), then the machine will take an # underflow exception. Since this is incorrect, we check here to see # if our emulation, after re-doing the operation, decided that # no underflow was called for. btst &unfl_bit,FPSR_EXCEPT(%a6) beq.w funfl_chkinex funfl_unfl_on2: fmovm.x &0x40,FP_SRC(%a6) # save EXOP (fp1) to stack mov.w &0xe003,2+FP_SRC(%a6) # save exc status fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # do this after fmovm,other fs! unlk %a6 bra.l _real_unfl # undeflow occurred but is disabled. meanwhile, inexact is enabled. therefore, # we must jump to real_inex(). funfl_inex_on: # The `060 FPU multiplier hardware is such that if the result of a # multiply operation is the smallest possible normalized number # (0x00000000_80000000_00000000), then the machine will take an # underflow exception. # But, whether bogus or not, if inexact is enabled AND it occurred, # then we have to branch to real_inex. btst &inex2_bit,FPSR_EXCEPT(%a6) beq.w funfl_exit funfl_inex_on2: fmovm.x &0x40,FP_SRC(%a6) # save EXOP to stack mov.b &0xc4,1+EXC_VOFF(%a6) # vector offset = 0xc4 mov.w &0xe001,2+FP_SRC(%a6) # save exc status fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # do this after fmovm,other fs! unlk %a6 bra.l _real_inex ####################################################################### funfl_out: #$# mov.l FP_SRC_EX(%a6),TRAP_SRCOP_EX(%a6) #$# mov.l FP_SRC_HI(%a6),TRAP_SRCOP_HI(%a6) #$# mov.l FP_SRC_LO(%a6),TRAP_SRCOP_LO(%a6) # the src operand is definitely a NORM(!), so tag it as such mov.b &NORM,STAG(%a6) # set src optype tag clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec/mode and.l &0xffff00ff,USER_FPSR(%a6) # zero all but accured field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr lea FP_SRC(%a6),%a0 # pass ptr to src operand bsr.l fout btst &unfl_bit,FPCR_ENABLE(%a6) bne.w funfl_unfl_on2 btst &inex2_bit,FPCR_ENABLE(%a6) bne.w funfl_inex_on2 fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 #$# add.l &24,%sp btst &0x7,(%sp) # is trace on? beq.l _fpsp_done # no fmov.l %fpiar,0x8(%sp) # "Current PC" is in FPIAR mov.w &0x2024,0x6(%sp) # stk fmt = 0x2; voff = 0x024 bra.l _real_trace ######################################################################### # XDEF **************************************************************** # # _fpsp_unsupp(): 060FPSP entry point for FP "Unimplemented # # Data Type" exception. # # # # This handler should be the first code executed upon taking the # # FP Unimplemented Data Type exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_{word,long}() - read instruction word/longword # # fix_skewed_ops() - adjust src operand in fsave frame # # set_tag_x() - determine optype of src/dst operands # # store_fpreg() - store opclass 0 or 2 result to FP regfile # # unnorm_fix() - change UNNORM operands to NORM or ZERO # # load_fpn2() - load dst operand from FP regfile # # load_fpn1() - load src operand from FP regfile # # fout() - emulate an opclass 3 instruction # # tbl_unsupp - add of table of emulation routines for opclass 0,2 # # _real_inex() - "callout" to operating system inexact handler # # _fpsp_done() - "callout" for exit; work all done # # _real_trace() - "callout" for Trace enabled exception # # funimp_skew() - adjust fsave src ops to "incorrect" value # # _real_snan() - "callout" for SNAN exception # # _real_operr() - "callout" for OPERR exception # # _real_ovfl() - "callout" for OVFL exception # # _real_unfl() - "callout" for UNFL exception # # get_packed() - fetch packed operand from memory # # # # INPUT *************************************************************** # # - The system stack contains the "Unimp Data Type" stk frame # # - The fsave frame contains the ssrc op (for UNNORM/DENORM) # # # # OUTPUT ************************************************************** # # If Inexact exception (opclass 3): # # - The system stack is changed to an Inexact exception stk frame # # If SNAN exception (opclass 3): # # - The system stack is changed to an SNAN exception stk frame # # If OPERR exception (opclass 3): # # - The system stack is changed to an OPERR exception stk frame # # If OVFL exception (opclass 3): # # - The system stack is changed to an OVFL exception stk frame # # If UNFL exception (opclass 3): # # - The system stack is changed to an UNFL exception stack frame # # If Trace exception enabled: # # - The system stack is changed to a Trace exception stack frame # # Else: (normal case) # # - Correct result has been stored as appropriate # # # # ALGORITHM *********************************************************** # # Two main instruction types can enter here: (1) DENORM or UNNORM # # unimplemented data types. These can be either opclass 0,2 or 3 # # instructions, and (2) PACKED unimplemented data format instructions # # also of opclasses 0,2, or 3. # # For UNNORM/DENORM opclass 0 and 2, the handler fetches the src # # operand from the fsave state frame and the dst operand (if dyadic) # # from the FP register file. The instruction is then emulated by # # choosing an emulation routine from a table of routines indexed by # # instruction type. Once the instruction has been emulated and result # # saved, then we check to see if any enabled exceptions resulted from # # instruction emulation. If none, then we exit through the "callout" # # _fpsp_done(). If there is an enabled FP exception, then we insert # # this exception into the FPU in the fsave state frame and then exit # # through _fpsp_done(). # # PACKED opclass 0 and 2 is similar in how the instruction is # # emulated and exceptions handled. The differences occur in how the # # handler loads the packed op (by calling get_packed() routine) and # # by the fact that a Trace exception could be pending for PACKED ops. # # If a Trace exception is pending, then the current exception stack # # frame is changed to a Trace exception stack frame and an exit is # # made through _real_trace(). # # For UNNORM/DENORM opclass 3, the actual move out to memory is # # performed by calling the routine fout(). If no exception should occur # # as the result of emulation, then an exit either occurs through # # _fpsp_done() or through _real_trace() if a Trace exception is pending # # (a Trace stack frame must be created here, too). If an FP exception # # should occur, then we must create an exception stack frame of that # # type and jump to either _real_snan(), _real_operr(), _real_inex(), # # _real_unfl(), or _real_ovfl() as appropriate. PACKED opclass 3 # # emulation is performed in a similar manner. # # # ######################################################################### # # (1) DENORM and UNNORM (unimplemented) data types: # # post-instruction # ***************** # * EA * # pre-instruction * * # ***************** ***************** # * 0x0 * 0x0dc * * 0x3 * 0x0dc * # ***************** ***************** # * Next * * Next * # * PC * * PC * # ***************** ***************** # * SR * * SR * # ***************** ***************** # # (2) PACKED format (unsupported) opclasses two and three: # ***************** # * EA * # * * # ***************** # * 0x2 * 0x0dc * # ***************** # * Next * # * PC * # ***************** # * SR * # ***************** # global _fpsp_unsupp _fpsp_unsupp: link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # save fp state movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack btst &0x5,EXC_SR(%a6) # user or supervisor mode? bne.b fu_s fu_u: mov.l %usp,%a0 # fetch user stack pointer mov.l %a0,EXC_A7(%a6) # save on stack bra.b fu_cont # if the exception is an opclass zero or two unimplemented data type # exception, then the a7' calculated here is wrong since it doesn't # stack an ea. however, we don't need an a7' for this case anyways. fu_s: lea 0x4+EXC_EA(%a6),%a0 # load old a7' mov.l %a0,EXC_A7(%a6) # save on stack fu_cont: # the FPIAR holds the "current PC" of the faulting instruction # the FPIAR should be set correctly for ALL exceptions passing through # this point. mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) # store OPWORD and EXTWORD ############################ clr.b SPCOND_FLG(%a6) # clear special condition flag # Separate opclass three (fpn-to-mem) ops since they have a different # stack frame and protocol. btst &0x5,EXC_CMDREG(%a6) # is it an fmove out? bne.w fu_out # yes # Separate packed opclass two instructions. bfextu EXC_CMDREG(%a6){&0:&6},%d0 cmpi.b %d0,&0x13 beq.w fu_in_pack # I'm not sure at this point what FPSR bits are valid for this instruction. # so, since the emulation routines re-create them anyways, zero exception field andi.l &0x00ff00ff,USER_FPSR(%a6) # zero exception field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr # Opclass two w/ memory-to-fpn operation will have an incorrect extended # precision format if the src format was single or double and the # source data type was an INF, NAN, DENORM, or UNNORM lea FP_SRC(%a6),%a0 # pass ptr to input bsr.l fix_skewed_ops # we don't know whether the src operand or the dst operand (or both) is the # UNNORM or DENORM. call the function that tags the operand type. if the # input is an UNNORM, then convert it to a NORM, DENORM, or ZERO. lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b fu_op2 # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO fu_op2: mov.b %d0,STAG(%a6) # save src optype tag bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg # bit five of the fp extension word separates the monadic and dyadic operations # at this point btst &0x5,1+EXC_CMDREG(%a6) # is operation monadic or dyadic? beq.b fu_extract # monadic cmpi.b 1+EXC_CMDREG(%a6),&0x3a # is operation an ftst? beq.b fu_extract # yes, so it's monadic, too bsr.l load_fpn2 # load dst into FP_DST lea FP_DST(%a6),%a0 # pass: ptr to dst op bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b fu_op2_done # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO fu_op2_done: mov.b %d0,DTAG(%a6) # save dst optype tag fu_extract: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # fetch rnd mode/prec bfextu 1+EXC_CMDREG(%a6){&1:&7},%d1 # extract extension lea FP_SRC(%a6),%a0 lea FP_DST(%a6),%a1 mov.l (tbl_unsupp.l,%pc,%d1.l*4),%d1 # fetch routine addr jsr (tbl_unsupp.l,%pc,%d1.l*1) # # Exceptions in order of precedence: # BSUN : none # SNAN : all dyadic ops # OPERR : fsqrt(-NORM) # OVFL : all except ftst,fcmp # UNFL : all except ftst,fcmp # DZ : fdiv # INEX2 : all except ftst,fcmp # INEX1 : none (packed doesn't go through here) # # we determine the highest priority exception(if any) set by the # emulation routine that has also been enabled by the user. mov.b FPCR_ENABLE(%a6),%d0 # fetch exceptions set bne.b fu_in_ena # some are enabled fu_in_cont: # fcmp and ftst do not store any result. mov.b 1+EXC_CMDREG(%a6),%d0 # fetch extension andi.b &0x38,%d0 # extract bits 3-5 cmpi.b %d0,&0x38 # is instr fcmp or ftst? beq.b fu_in_exit # yes bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg bsr.l store_fpreg # store the result fu_in_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 bra.l _fpsp_done fu_in_ena: and.b FPSR_EXCEPT(%a6),%d0 # keep only ones enabled bfffo %d0{&24:&8},%d0 # find highest priority exception bne.b fu_in_exc # there is at least one set # # No exceptions occurred that were also enabled. Now: # # if (OVFL && ovfl_disabled && inexact_enabled) { # branch to _real_inex() (even if the result was exact!); # } else { # save the result in the proper fp reg (unless the op is fcmp or ftst); # return; # } # btst &ovfl_bit,FPSR_EXCEPT(%a6) # was overflow set? beq.b fu_in_cont # no fu_in_ovflchk: btst &inex2_bit,FPCR_ENABLE(%a6) # was inexact enabled? beq.b fu_in_cont # no bra.w fu_in_exc_ovfl # go insert overflow frame # # An exception occurred and that exception was enabled: # # shift enabled exception field into lo byte of d0; # if (((INEX2 || INEX1) && inex_enabled && OVFL && ovfl_disabled) || # ((INEX2 || INEX1) && inex_enabled && UNFL && unfl_disabled)) { # /* # * this is the case where we must call _real_inex() now or else # * there will be no other way to pass it the exceptional operand # */ # call _real_inex(); # } else { # restore exc state (SNAN||OPERR||OVFL||UNFL||DZ||INEX) into the FPU; # } # fu_in_exc: subi.l &24,%d0 # fix offset to be 0-8 cmpi.b %d0,&0x6 # is exception INEX? (6) bne.b fu_in_exc_exit # no # the enabled exception was inexact btst &unfl_bit,FPSR_EXCEPT(%a6) # did disabled underflow occur? bne.w fu_in_exc_unfl # yes btst &ovfl_bit,FPSR_EXCEPT(%a6) # did disabled overflow occur? bne.w fu_in_exc_ovfl # yes # here, we insert the correct fsave status value into the fsave frame for the # corresponding exception. the operand in the fsave frame should be the original # src operand. fu_in_exc_exit: mov.l %d0,-(%sp) # save d0 bsr.l funimp_skew # skew sgl or dbl inputs mov.l (%sp)+,%d0 # restore d0 mov.w (tbl_except.b,%pc,%d0.w*2),2+FP_SRC(%a6) # create exc status fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # restore src op unlk %a6 bra.l _fpsp_done tbl_except: short 0xe000,0xe006,0xe004,0xe005 short 0xe003,0xe002,0xe001,0xe001 fu_in_exc_unfl: mov.w &0x4,%d0 bra.b fu_in_exc_exit fu_in_exc_ovfl: mov.w &0x03,%d0 bra.b fu_in_exc_exit # If the input operand to this operation was opclass two and a single # or double precision denorm, inf, or nan, the operand needs to be # "corrected" in order to have the proper equivalent extended precision # number. global fix_skewed_ops fix_skewed_ops: bfextu EXC_CMDREG(%a6){&0:&6},%d0 # extract opclass,src fmt cmpi.b %d0,&0x11 # is class = 2 & fmt = sgl? beq.b fso_sgl # yes cmpi.b %d0,&0x15 # is class = 2 & fmt = dbl? beq.b fso_dbl # yes rts # no fso_sgl: mov.w LOCAL_EX(%a0),%d0 # fetch src exponent andi.w &0x7fff,%d0 # strip sign cmpi.w %d0,&0x3f80 # is |exp| == $3f80? beq.b fso_sgl_dnrm_zero # yes cmpi.w %d0,&0x407f # no; is |exp| == $407f? beq.b fso_infnan # yes rts # no fso_sgl_dnrm_zero: andi.l &0x7fffffff,LOCAL_HI(%a0) # clear j-bit beq.b fso_zero # it's a skewed zero fso_sgl_dnrm: # here, we count on norm not to alter a0... bsr.l norm # normalize mantissa neg.w %d0 # -shft amt addi.w &0x3f81,%d0 # adjust new exponent andi.w &0x8000,LOCAL_EX(%a0) # clear old exponent or.w %d0,LOCAL_EX(%a0) # insert new exponent rts fso_zero: andi.w &0x8000,LOCAL_EX(%a0) # clear bogus exponent rts fso_infnan: andi.b &0x7f,LOCAL_HI(%a0) # clear j-bit ori.w &0x7fff,LOCAL_EX(%a0) # make exponent = $7fff rts fso_dbl: mov.w LOCAL_EX(%a0),%d0 # fetch src exponent andi.w &0x7fff,%d0 # strip sign cmpi.w %d0,&0x3c00 # is |exp| == $3c00? beq.b fso_dbl_dnrm_zero # yes cmpi.w %d0,&0x43ff # no; is |exp| == $43ff? beq.b fso_infnan # yes rts # no fso_dbl_dnrm_zero: andi.l &0x7fffffff,LOCAL_HI(%a0) # clear j-bit bne.b fso_dbl_dnrm # it's a skewed denorm tst.l LOCAL_LO(%a0) # is it a zero? beq.b fso_zero # yes fso_dbl_dnrm: # here, we count on norm not to alter a0... bsr.l norm # normalize mantissa neg.w %d0 # -shft amt addi.w &0x3c01,%d0 # adjust new exponent andi.w &0x8000,LOCAL_EX(%a0) # clear old exponent or.w %d0,LOCAL_EX(%a0) # insert new exponent rts ################################################################# # fmove out took an unimplemented data type exception. # the src operand is in FP_SRC. Call _fout() to write out the result and # to determine which exceptions, if any, to take. fu_out: # Separate packed move outs from the UNNORM and DENORM move outs. bfextu EXC_CMDREG(%a6){&3:&3},%d0 cmpi.b %d0,&0x3 beq.w fu_out_pack cmpi.b %d0,&0x7 beq.w fu_out_pack # I'm not sure at this point what FPSR bits are valid for this instruction. # so, since the emulation routines re-create them anyways, zero exception field. # fmove out doesn't affect ccodes. and.l &0xffff00ff,USER_FPSR(%a6) # zero exception field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr # the src can ONLY be a DENORM or an UNNORM! so, don't make any big subroutine # call here. just figure out what it is... mov.w FP_SRC_EX(%a6),%d0 # get exponent andi.w &0x7fff,%d0 # strip sign beq.b fu_out_denorm # it's a DENORM lea FP_SRC(%a6),%a0 bsr.l unnorm_fix # yes; fix it mov.b %d0,STAG(%a6) bra.b fu_out_cont fu_out_denorm: mov.b &DENORM,STAG(%a6) fu_out_cont: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # fetch rnd mode/prec lea FP_SRC(%a6),%a0 # pass ptr to src operand mov.l (%a6),EXC_A6(%a6) # in case a6 changes bsr.l fout # call fmove out routine # Exceptions in order of precedence: # BSUN : none # SNAN : none # OPERR : fmove.{b,w,l} out of large UNNORM # OVFL : fmove.{s,d} # UNFL : fmove.{s,d,x} # DZ : none # INEX2 : all # INEX1 : none (packed doesn't travel through here) # determine the highest priority exception(if any) set by the # emulation routine that has also been enabled by the user. mov.b FPCR_ENABLE(%a6),%d0 # fetch exceptions enabled bne.w fu_out_ena # some are enabled fu_out_done: mov.l EXC_A6(%a6),(%a6) # in case a6 changed # on extended precision opclass three instructions using pre-decrement or # post-increment addressing mode, the address register is not updated. is the # address register was the stack pointer used from user mode, then let's update # it here. if it was used from supervisor mode, then we have to handle this # as a special case. btst &0x5,EXC_SR(%a6) bne.b fu_out_done_s mov.l EXC_A7(%a6),%a0 # restore a7 mov.l %a0,%usp fu_out_done_cont: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 btst &0x7,(%sp) # is trace on? bne.b fu_out_trace # yes bra.l _fpsp_done # is the ea mode pre-decrement of the stack pointer from supervisor mode? # ("fmov.x fpm,-(a7)") if so, fu_out_done_s: cmpi.b SPCOND_FLG(%a6),&mda7_flg bne.b fu_out_done_cont # the extended precision result is still in fp0. but, we need to save it # somewhere on the stack until we can copy it to its final resting place. # here, we're counting on the top of the stack to be the old place-holders # for fp0/fp1 which have already been restored. that way, we can write # over those destinations with the shifted stack frame. fmovm.x &0x80,FP_SRC(%a6) # put answer on stack fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.l (%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+2+EXC_PC(%sp),LOCAL_SIZE+2+EXC_PC-0xc(%sp) # now, copy the result to the proper place on the stack mov.l LOCAL_SIZE+FP_SRC_EX(%sp),LOCAL_SIZE+EXC_SR+0x0(%sp) mov.l LOCAL_SIZE+FP_SRC_HI(%sp),LOCAL_SIZE+EXC_SR+0x4(%sp) mov.l LOCAL_SIZE+FP_SRC_LO(%sp),LOCAL_SIZE+EXC_SR+0x8(%sp) add.l &LOCAL_SIZE-0x8,%sp btst &0x7,(%sp) bne.b fu_out_trace bra.l _fpsp_done fu_out_ena: and.b FPSR_EXCEPT(%a6),%d0 # keep only ones enabled bfffo %d0{&24:&8},%d0 # find highest priority exception bne.b fu_out_exc # there is at least one set # no exceptions were set. # if a disabled overflow occurred and inexact was enabled but the result # was exact, then a branch to _real_inex() is made. btst &ovfl_bit,FPSR_EXCEPT(%a6) # was overflow set? beq.w fu_out_done # no fu_out_ovflchk: btst &inex2_bit,FPCR_ENABLE(%a6) # was inexact enabled? beq.w fu_out_done # no bra.w fu_inex # yes # # The fp move out that took the "Unimplemented Data Type" exception was # being traced. Since the stack frames are similar, get the "current" PC # from FPIAR and put it in the trace stack frame then jump to _real_trace(). # # UNSUPP FRAME TRACE FRAME # ***************** ***************** # * EA * * Current * # * * * PC * # ***************** ***************** # * 0x3 * 0x0dc * * 0x2 * 0x024 * # ***************** ***************** # * Next * * Next * # * PC * * PC * # ***************** ***************** # * SR * * SR * # ***************** ***************** # fu_out_trace: mov.w &0x2024,0x6(%sp) fmov.l %fpiar,0x8(%sp) bra.l _real_trace # an exception occurred and that exception was enabled. fu_out_exc: subi.l &24,%d0 # fix offset to be 0-8 # we don't mess with the existing fsave frame. just re-insert it and # jump to the "_real_{}()" handler... mov.w (tbl_fu_out.b,%pc,%d0.w*2),%d0 jmp (tbl_fu_out.b,%pc,%d0.w*1) swbeg &0x8 tbl_fu_out: short tbl_fu_out - tbl_fu_out # BSUN can't happen short tbl_fu_out - tbl_fu_out # SNAN can't happen short fu_operr - tbl_fu_out # OPERR short fu_ovfl - tbl_fu_out # OVFL short fu_unfl - tbl_fu_out # UNFL short tbl_fu_out - tbl_fu_out # DZ can't happen short fu_inex - tbl_fu_out # INEX2 short tbl_fu_out - tbl_fu_out # INEX1 won't make it here # for snan,operr,ovfl,unfl, src op is still in FP_SRC so just # frestore it. fu_snan: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30d8,EXC_VOFF(%a6) # vector offset = 0xd8 mov.w &0xe006,2+FP_SRC(%a6) frestore FP_SRC(%a6) unlk %a6 bra.l _real_snan fu_operr: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30d0,EXC_VOFF(%a6) # vector offset = 0xd0 mov.w &0xe004,2+FP_SRC(%a6) frestore FP_SRC(%a6) unlk %a6 bra.l _real_operr fu_ovfl: fmovm.x &0x40,FP_SRC(%a6) # save EXOP to the stack fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30d4,EXC_VOFF(%a6) # vector offset = 0xd4 mov.w &0xe005,2+FP_SRC(%a6) frestore FP_SRC(%a6) # restore EXOP unlk %a6 bra.l _real_ovfl # underflow can happen for extended precision. extended precision opclass # three instruction exceptions don't update the stack pointer. so, if the # exception occurred from user mode, then simply update a7 and exit normally. # if the exception occurred from supervisor mode, check if fu_unfl: mov.l EXC_A6(%a6),(%a6) # restore a6 btst &0x5,EXC_SR(%a6) bne.w fu_unfl_s mov.l EXC_A7(%a6),%a0 # restore a7 whether we need mov.l %a0,%usp # to or not... fu_unfl_cont: fmovm.x &0x40,FP_SRC(%a6) # save EXOP to the stack fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30cc,EXC_VOFF(%a6) # vector offset = 0xcc mov.w &0xe003,2+FP_SRC(%a6) frestore FP_SRC(%a6) # restore EXOP unlk %a6 bra.l _real_unfl fu_unfl_s: cmpi.b SPCOND_FLG(%a6),&mda7_flg # was the mode -(sp)? bne.b fu_unfl_cont # the extended precision result is still in fp0. but, we need to save it # somewhere on the stack until we can copy it to its final resting place # (where the exc frame is currently). make sure it's not at the top of the # frame or it will get overwritten when the exc stack frame is shifted "down". fmovm.x &0x80,FP_SRC(%a6) # put answer on stack fmovm.x &0x40,FP_DST(%a6) # put EXOP on stack fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30cc,EXC_VOFF(%a6) # vector offset = 0xcc mov.w &0xe003,2+FP_DST(%a6) frestore FP_DST(%a6) # restore EXOP mov.l (%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+2+EXC_PC(%sp),LOCAL_SIZE+2+EXC_PC-0xc(%sp) mov.l LOCAL_SIZE+EXC_EA(%sp),LOCAL_SIZE+EXC_EA-0xc(%sp) # now, copy the result to the proper place on the stack mov.l LOCAL_SIZE+FP_SRC_EX(%sp),LOCAL_SIZE+EXC_SR+0x0(%sp) mov.l LOCAL_SIZE+FP_SRC_HI(%sp),LOCAL_SIZE+EXC_SR+0x4(%sp) mov.l LOCAL_SIZE+FP_SRC_LO(%sp),LOCAL_SIZE+EXC_SR+0x8(%sp) add.l &LOCAL_SIZE-0x8,%sp bra.l _real_unfl # fmove in and out enter here. fu_inex: fmovm.x &0x40,FP_SRC(%a6) # save EXOP to the stack fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30c4,EXC_VOFF(%a6) # vector offset = 0xc4 mov.w &0xe001,2+FP_SRC(%a6) frestore FP_SRC(%a6) # restore EXOP unlk %a6 bra.l _real_inex ######################################################################### ######################################################################### fu_in_pack: # I'm not sure at this point what FPSR bits are valid for this instruction. # so, since the emulation routines re-create them anyways, zero exception field andi.l &0x0ff00ff,USER_FPSR(%a6) # zero exception field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr bsr.l get_packed # fetch packed src operand lea FP_SRC(%a6),%a0 # pass ptr to src bsr.l set_tag_x # set src optype tag mov.b %d0,STAG(%a6) # save src optype tag bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg # bit five of the fp extension word separates the monadic and dyadic operations # at this point btst &0x5,1+EXC_CMDREG(%a6) # is operation monadic or dyadic? beq.b fu_extract_p # monadic cmpi.b 1+EXC_CMDREG(%a6),&0x3a # is operation an ftst? beq.b fu_extract_p # yes, so it's monadic, too bsr.l load_fpn2 # load dst into FP_DST lea FP_DST(%a6),%a0 # pass: ptr to dst op bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b fu_op2_done_p # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO fu_op2_done_p: mov.b %d0,DTAG(%a6) # save dst optype tag fu_extract_p: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # fetch rnd mode/prec bfextu 1+EXC_CMDREG(%a6){&1:&7},%d1 # extract extension lea FP_SRC(%a6),%a0 lea FP_DST(%a6),%a1 mov.l (tbl_unsupp.l,%pc,%d1.l*4),%d1 # fetch routine addr jsr (tbl_unsupp.l,%pc,%d1.l*1) # # Exceptions in order of precedence: # BSUN : none # SNAN : all dyadic ops # OPERR : fsqrt(-NORM) # OVFL : all except ftst,fcmp # UNFL : all except ftst,fcmp # DZ : fdiv # INEX2 : all except ftst,fcmp # INEX1 : all # # we determine the highest priority exception(if any) set by the # emulation routine that has also been enabled by the user. mov.b FPCR_ENABLE(%a6),%d0 # fetch exceptions enabled bne.w fu_in_ena_p # some are enabled fu_in_cont_p: # fcmp and ftst do not store any result. mov.b 1+EXC_CMDREG(%a6),%d0 # fetch extension andi.b &0x38,%d0 # extract bits 3-5 cmpi.b %d0,&0x38 # is instr fcmp or ftst? beq.b fu_in_exit_p # yes bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg bsr.l store_fpreg # store the result fu_in_exit_p: btst &0x5,EXC_SR(%a6) # user or supervisor? bne.w fu_in_exit_s_p # supervisor mov.l EXC_A7(%a6),%a0 # update user a7 mov.l %a0,%usp fu_in_exit_cont_p: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 # unravel stack frame btst &0x7,(%sp) # is trace on? bne.w fu_trace_p # yes bra.l _fpsp_done # exit to os # the exception occurred in supervisor mode. check to see if the # addressing mode was (a7)+. if so, we'll need to shift the # stack frame "up". fu_in_exit_s_p: btst &mia7_bit,SPCOND_FLG(%a6) # was ea mode (a7)+ beq.b fu_in_exit_cont_p # no fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 # unravel stack frame # shift the stack frame "up". we don't really care about the field. mov.l 0x4(%sp),0x10(%sp) mov.l 0x0(%sp),0xc(%sp) add.l &0xc,%sp btst &0x7,(%sp) # is trace on? bne.w fu_trace_p # yes bra.l _fpsp_done # exit to os fu_in_ena_p: and.b FPSR_EXCEPT(%a6),%d0 # keep only ones enabled & set bfffo %d0{&24:&8},%d0 # find highest priority exception bne.b fu_in_exc_p # at least one was set # # No exceptions occurred that were also enabled. Now: # # if (OVFL && ovfl_disabled && inexact_enabled) { # branch to _real_inex() (even if the result was exact!); # } else { # save the result in the proper fp reg (unless the op is fcmp or ftst); # return; # } # btst &ovfl_bit,FPSR_EXCEPT(%a6) # was overflow set? beq.w fu_in_cont_p # no fu_in_ovflchk_p: btst &inex2_bit,FPCR_ENABLE(%a6) # was inexact enabled? beq.w fu_in_cont_p # no bra.w fu_in_exc_ovfl_p # do _real_inex() now # # An exception occurred and that exception was enabled: # # shift enabled exception field into lo byte of d0; # if (((INEX2 || INEX1) && inex_enabled && OVFL && ovfl_disabled) || # ((INEX2 || INEX1) && inex_enabled && UNFL && unfl_disabled)) { # /* # * this is the case where we must call _real_inex() now or else # * there will be no other way to pass it the exceptional operand # */ # call _real_inex(); # } else { # restore exc state (SNAN||OPERR||OVFL||UNFL||DZ||INEX) into the FPU; # } # fu_in_exc_p: subi.l &24,%d0 # fix offset to be 0-8 cmpi.b %d0,&0x6 # is exception INEX? (6 or 7) blt.b fu_in_exc_exit_p # no # the enabled exception was inexact btst &unfl_bit,FPSR_EXCEPT(%a6) # did disabled underflow occur? bne.w fu_in_exc_unfl_p # yes btst &ovfl_bit,FPSR_EXCEPT(%a6) # did disabled overflow occur? bne.w fu_in_exc_ovfl_p # yes # here, we insert the correct fsave status value into the fsave frame for the # corresponding exception. the operand in the fsave frame should be the original # src operand. # as a reminder for future predicted pain and agony, we are passing in fsave the # "non-skewed" operand for cases of sgl and dbl src INFs,NANs, and DENORMs. # this is INCORRECT for enabled SNAN which would give to the user the skewed SNAN!!! fu_in_exc_exit_p: btst &0x5,EXC_SR(%a6) # user or supervisor? bne.w fu_in_exc_exit_s_p # supervisor mov.l EXC_A7(%a6),%a0 # update user a7 mov.l %a0,%usp fu_in_exc_exit_cont_p: mov.w (tbl_except_p.b,%pc,%d0.w*2),2+FP_SRC(%a6) fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # restore src op unlk %a6 btst &0x7,(%sp) # is trace enabled? bne.w fu_trace_p # yes bra.l _fpsp_done tbl_except_p: short 0xe000,0xe006,0xe004,0xe005 short 0xe003,0xe002,0xe001,0xe001 fu_in_exc_ovfl_p: mov.w &0x3,%d0 bra.w fu_in_exc_exit_p fu_in_exc_unfl_p: mov.w &0x4,%d0 bra.w fu_in_exc_exit_p fu_in_exc_exit_s_p: btst &mia7_bit,SPCOND_FLG(%a6) beq.b fu_in_exc_exit_cont_p mov.w (tbl_except_p.b,%pc,%d0.w*2),2+FP_SRC(%a6) fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # restore src op unlk %a6 # unravel stack frame # shift stack frame "up". who cares about field. mov.l 0x4(%sp),0x10(%sp) mov.l 0x0(%sp),0xc(%sp) add.l &0xc,%sp btst &0x7,(%sp) # is trace on? bne.b fu_trace_p # yes bra.l _fpsp_done # exit to os # # The opclass two PACKED instruction that took an "Unimplemented Data Type" # exception was being traced. Make the "current" PC the FPIAR and put it in the # trace stack frame then jump to _real_trace(). # # UNSUPP FRAME TRACE FRAME # ***************** ***************** # * EA * * Current * # * * * PC * # ***************** ***************** # * 0x2 * 0x0dc * * 0x2 * 0x024 * # ***************** ***************** # * Next * * Next * # * PC * * PC * # ***************** ***************** # * SR * * SR * # ***************** ***************** fu_trace_p: mov.w &0x2024,0x6(%sp) fmov.l %fpiar,0x8(%sp) bra.l _real_trace ######################################################### ######################################################### fu_out_pack: # I'm not sure at this point what FPSR bits are valid for this instruction. # so, since the emulation routines re-create them anyways, zero exception field. # fmove out doesn't affect ccodes. and.l &0xffff00ff,USER_FPSR(%a6) # zero exception field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr bfextu EXC_CMDREG(%a6){&6:&3},%d0 bsr.l load_fpn1 # unlike other opclass 3, unimplemented data type exceptions, packed must be # able to detect all operand types. lea FP_SRC(%a6),%a0 bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b fu_op2_p # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO fu_op2_p: mov.b %d0,STAG(%a6) # save src optype tag clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # fetch rnd mode/prec lea FP_SRC(%a6),%a0 # pass ptr to src operand mov.l (%a6),EXC_A6(%a6) # in case a6 changes bsr.l fout # call fmove out routine # Exceptions in order of precedence: # BSUN : no # SNAN : yes # OPERR : if ((k_factor > +17) || (dec. exp exceeds 3 digits)) # OVFL : no # UNFL : no # DZ : no # INEX2 : yes # INEX1 : no # determine the highest priority exception(if any) set by the # emulation routine that has also been enabled by the user. mov.b FPCR_ENABLE(%a6),%d0 # fetch exceptions enabled bne.w fu_out_ena_p # some are enabled fu_out_exit_p: mov.l EXC_A6(%a6),(%a6) # restore a6 btst &0x5,EXC_SR(%a6) # user or supervisor? bne.b fu_out_exit_s_p # supervisor mov.l EXC_A7(%a6),%a0 # update user a7 mov.l %a0,%usp fu_out_exit_cont_p: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 # unravel stack frame btst &0x7,(%sp) # is trace on? bne.w fu_trace_p # yes bra.l _fpsp_done # exit to os # the exception occurred in supervisor mode. check to see if the # addressing mode was -(a7). if so, we'll need to shift the # stack frame "down". fu_out_exit_s_p: btst &mda7_bit,SPCOND_FLG(%a6) # was ea mode -(a7) beq.b fu_out_exit_cont_p # no fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.l (%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+2+EXC_PC(%sp),LOCAL_SIZE+2+EXC_PC-0xc(%sp) # now, copy the result to the proper place on the stack mov.l LOCAL_SIZE+FP_DST_EX(%sp),LOCAL_SIZE+EXC_SR+0x0(%sp) mov.l LOCAL_SIZE+FP_DST_HI(%sp),LOCAL_SIZE+EXC_SR+0x4(%sp) mov.l LOCAL_SIZE+FP_DST_LO(%sp),LOCAL_SIZE+EXC_SR+0x8(%sp) add.l &LOCAL_SIZE-0x8,%sp btst &0x7,(%sp) bne.w fu_trace_p bra.l _fpsp_done fu_out_ena_p: and.b FPSR_EXCEPT(%a6),%d0 # keep only ones enabled bfffo %d0{&24:&8},%d0 # find highest priority exception beq.w fu_out_exit_p mov.l EXC_A6(%a6),(%a6) # restore a6 # an exception occurred and that exception was enabled. # the only exception possible on packed move out are INEX, OPERR, and SNAN. fu_out_exc_p: cmpi.b %d0,&0x1a bgt.w fu_inex_p2 beq.w fu_operr_p fu_snan_p: btst &0x5,EXC_SR(%a6) bne.b fu_snan_s_p mov.l EXC_A7(%a6),%a0 mov.l %a0,%usp bra.w fu_snan fu_snan_s_p: cmpi.b SPCOND_FLG(%a6),&mda7_flg bne.w fu_snan # the instruction was "fmove.p fpn,-(a7)" from supervisor mode. # the strategy is to move the exception frame "down" 12 bytes. then, we # can store the default result where the exception frame was. fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30d8,EXC_VOFF(%a6) # vector offset = 0xd0 mov.w &0xe006,2+FP_SRC(%a6) # set fsave status frestore FP_SRC(%a6) # restore src operand mov.l (%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+2+EXC_PC(%sp),LOCAL_SIZE+2+EXC_PC-0xc(%sp) mov.l LOCAL_SIZE+EXC_EA(%sp),LOCAL_SIZE+EXC_EA-0xc(%sp) # now, we copy the default result to it's proper location mov.l LOCAL_SIZE+FP_DST_EX(%sp),LOCAL_SIZE+0x4(%sp) mov.l LOCAL_SIZE+FP_DST_HI(%sp),LOCAL_SIZE+0x8(%sp) mov.l LOCAL_SIZE+FP_DST_LO(%sp),LOCAL_SIZE+0xc(%sp) add.l &LOCAL_SIZE-0x8,%sp bra.l _real_snan fu_operr_p: btst &0x5,EXC_SR(%a6) bne.w fu_operr_p_s mov.l EXC_A7(%a6),%a0 mov.l %a0,%usp bra.w fu_operr fu_operr_p_s: cmpi.b SPCOND_FLG(%a6),&mda7_flg bne.w fu_operr # the instruction was "fmove.p fpn,-(a7)" from supervisor mode. # the strategy is to move the exception frame "down" 12 bytes. then, we # can store the default result where the exception frame was. fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30d0,EXC_VOFF(%a6) # vector offset = 0xd0 mov.w &0xe004,2+FP_SRC(%a6) # set fsave status frestore FP_SRC(%a6) # restore src operand mov.l (%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+2+EXC_PC(%sp),LOCAL_SIZE+2+EXC_PC-0xc(%sp) mov.l LOCAL_SIZE+EXC_EA(%sp),LOCAL_SIZE+EXC_EA-0xc(%sp) # now, we copy the default result to it's proper location mov.l LOCAL_SIZE+FP_DST_EX(%sp),LOCAL_SIZE+0x4(%sp) mov.l LOCAL_SIZE+FP_DST_HI(%sp),LOCAL_SIZE+0x8(%sp) mov.l LOCAL_SIZE+FP_DST_LO(%sp),LOCAL_SIZE+0xc(%sp) add.l &LOCAL_SIZE-0x8,%sp bra.l _real_operr fu_inex_p2: btst &0x5,EXC_SR(%a6) bne.w fu_inex_s_p2 mov.l EXC_A7(%a6),%a0 mov.l %a0,%usp bra.w fu_inex fu_inex_s_p2: cmpi.b SPCOND_FLG(%a6),&mda7_flg bne.w fu_inex # the instruction was "fmove.p fpn,-(a7)" from supervisor mode. # the strategy is to move the exception frame "down" 12 bytes. then, we # can store the default result where the exception frame was. fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0/fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.w &0x30c4,EXC_VOFF(%a6) # vector offset = 0xc4 mov.w &0xe001,2+FP_SRC(%a6) # set fsave status frestore FP_SRC(%a6) # restore src operand mov.l (%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+2+EXC_PC(%sp),LOCAL_SIZE+2+EXC_PC-0xc(%sp) mov.l LOCAL_SIZE+EXC_EA(%sp),LOCAL_SIZE+EXC_EA-0xc(%sp) # now, we copy the default result to it's proper location mov.l LOCAL_SIZE+FP_DST_EX(%sp),LOCAL_SIZE+0x4(%sp) mov.l LOCAL_SIZE+FP_DST_HI(%sp),LOCAL_SIZE+0x8(%sp) mov.l LOCAL_SIZE+FP_DST_LO(%sp),LOCAL_SIZE+0xc(%sp) add.l &LOCAL_SIZE-0x8,%sp bra.l _real_inex ######################################################################### # # if we're stuffing a source operand back into an fsave frame then we # have to make sure that for single or double source operands that the # format stuffed is as weird as the hardware usually makes it. # global funimp_skew funimp_skew: bfextu EXC_EXTWORD(%a6){&3:&3},%d0 # extract src specifier cmpi.b %d0,&0x1 # was src sgl? beq.b funimp_skew_sgl # yes cmpi.b %d0,&0x5 # was src dbl? beq.b funimp_skew_dbl # yes rts funimp_skew_sgl: mov.w FP_SRC_EX(%a6),%d0 # fetch DENORM exponent andi.w &0x7fff,%d0 # strip sign beq.b funimp_skew_sgl_not cmpi.w %d0,&0x3f80 bgt.b funimp_skew_sgl_not neg.w %d0 # make exponent negative addi.w &0x3f81,%d0 # find amt to shift mov.l FP_SRC_HI(%a6),%d1 # fetch DENORM hi(man) lsr.l %d0,%d1 # shift it bset &31,%d1 # set j-bit mov.l %d1,FP_SRC_HI(%a6) # insert new hi(man) andi.w &0x8000,FP_SRC_EX(%a6) # clear old exponent ori.w &0x3f80,FP_SRC_EX(%a6) # insert new "skewed" exponent funimp_skew_sgl_not: rts funimp_skew_dbl: mov.w FP_SRC_EX(%a6),%d0 # fetch DENORM exponent andi.w &0x7fff,%d0 # strip sign beq.b funimp_skew_dbl_not cmpi.w %d0,&0x3c00 bgt.b funimp_skew_dbl_not tst.b FP_SRC_EX(%a6) # make "internal format" smi.b 0x2+FP_SRC(%a6) mov.w %d0,FP_SRC_EX(%a6) # insert exponent with cleared sign clr.l %d0 # clear g,r,s lea FP_SRC(%a6),%a0 # pass ptr to src op mov.w &0x3c01,%d1 # pass denorm threshold bsr.l dnrm_lp # denorm it mov.w &0x3c00,%d0 # new exponent tst.b 0x2+FP_SRC(%a6) # is sign set? beq.b fss_dbl_denorm_done # no bset &15,%d0 # set sign fss_dbl_denorm_done: bset &0x7,FP_SRC_HI(%a6) # set j-bit mov.w %d0,FP_SRC_EX(%a6) # insert new exponent funimp_skew_dbl_not: rts ######################################################################### global _mem_write2 _mem_write2: btst &0x5,EXC_SR(%a6) beq.l _dmem_write mov.l 0x0(%a0),FP_DST_EX(%a6) mov.l 0x4(%a0),FP_DST_HI(%a6) mov.l 0x8(%a0),FP_DST_LO(%a6) clr.l %d1 rts ######################################################################### # XDEF **************************************************************** # # _fpsp_effadd(): 060FPSP entry point for FP "Unimplemented # # effective address" exception. # # # # This handler should be the first code executed upon taking the # # FP Unimplemented Effective Address exception in an operating # # system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword # # fix_skewed_ops() - adjust src operand in fsave frame # # set_tag_x() - determine optype of src/dst operands # # store_fpreg() - store opclass 0 or 2 result to FP regfile # # unnorm_fix() - change UNNORM operands to NORM or ZERO # # load_fpn2() - load dst operand from FP regfile # # tbl_unsupp - add of table of emulation routines for opclass 0,2 # # decbin() - convert packed data to FP binary data # # _real_fpu_disabled() - "callout" for "FPU disabled" exception # # _real_access() - "callout" for access error exception # # _mem_read() - read extended immediate operand from memory # # _fpsp_done() - "callout" for exit; work all done # # _real_trace() - "callout" for Trace enabled exception # # fmovm_dynamic() - emulate dynamic fmovm instruction # # fmovm_ctrl() - emulate fmovm control instruction # # # # INPUT *************************************************************** # # - The system stack contains the "Unimplemented " stk frame # # # # OUTPUT ************************************************************** # # If access error: # # - The system stack is changed to an access error stack frame # # If FPU disabled: # # - The system stack is changed to an FPU disabled stack frame # # If Trace exception enabled: # # - The system stack is changed to a Trace exception stack frame # # Else: (normal case) # # - None (correct result has been stored as appropriate) # # # # ALGORITHM *********************************************************** # # This exception handles 3 types of operations: # # (1) FP Instructions using extended precision or packed immediate # # addressing mode. # # (2) The "fmovm.x" instruction w/ dynamic register specification. # # (3) The "fmovm.l" instruction w/ 2 or 3 control registers. # # # # For immediate data operations, the data is read in w/ a # # _mem_read() "callout", converted to FP binary (if packed), and used # # as the source operand to the instruction specified by the instruction # # word. If no FP exception should be reported ads a result of the # # emulation, then the result is stored to the destination register and # # the handler exits through _fpsp_done(). If an enabled exc has been # # signalled as a result of emulation, then an fsave state frame # # corresponding to the FP exception type must be entered into the 060 # # FPU before exiting. In either the enabled or disabled cases, we # # must also check if a Trace exception is pending, in which case, we # # must create a Trace exception stack frame from the current exception # # stack frame. If no Trace is pending, we simply exit through # # _fpsp_done(). # # For "fmovm.x", call the routine fmovm_dynamic() which will # # decode and emulate the instruction. No FP exceptions can be pending # # as a result of this operation emulation. A Trace exception can be # # pending, though, which means the current stack frame must be changed # # to a Trace stack frame and an exit made through _real_trace(). # # For the case of "fmovm.x Dn,-(a7)", where the offending instruction # # was executed from supervisor mode, this handler must store the FP # # register file values to the system stack by itself since # # fmovm_dynamic() can't handle this. A normal exit is made through # # fpsp_done(). # # For "fmovm.l", fmovm_ctrl() is used to emulate the instruction. # # Again, a Trace exception may be pending and an exit made through # # _real_trace(). Else, a normal exit is made through _fpsp_done(). # # # # Before any of the above is attempted, it must be checked to # # see if the FPU is disabled. Since the "Unimp " exception is taken # # before the "FPU disabled" exception, but the "FPU disabled" exception # # has higher priority, we check the disabled bit in the PCR. If set, # # then we must create an 8 word "FPU disabled" exception stack frame # # from the current 4 word exception stack frame. This includes # # reproducing the effective address of the instruction to put on the # # new stack frame. # # # # In the process of all emulation work, if a _mem_read() # # "callout" returns a failing result indicating an access error, then # # we must create an access error stack frame from the current stack # # frame. This information includes a faulting address and a fault- # # status-longword. These are created within this handler. # # # ######################################################################### global _fpsp_effadd _fpsp_effadd: # This exception type takes priority over the "Line F Emulator" # exception. Therefore, the FPU could be disabled when entering here. # So, we must check to see if it's disabled and handle that case separately. mov.l %d0,-(%sp) # save d0 movc %pcr,%d0 # load proc cr btst &0x1,%d0 # is FPU disabled? bne.w iea_disabled # yes mov.l (%sp)+,%d0 # restore d0 link %a6,&-LOCAL_SIZE # init stack frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # PC of instruction that took the exception is the PC in the frame mov.l EXC_PC(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) # store OPWORD and EXTWORD ######################################################################### tst.w %d0 # is operation fmovem? bmi.w iea_fmovm # yes # # here, we will have: # fabs fdabs fsabs facos fmod # fadd fdadd fsadd fasin frem # fcmp fatan fscale # fdiv fddiv fsdiv fatanh fsin # fint fcos fsincos # fintrz fcosh fsinh # fmove fdmove fsmove fetox ftan # fmul fdmul fsmul fetoxm1 ftanh # fneg fdneg fsneg fgetexp ftentox # fsgldiv fgetman ftwotox # fsglmul flog10 # fsqrt flog2 # fsub fdsub fssub flogn # ftst flognp1 # which can all use f.{x,p} # so, now it's immediate data extended precision AND PACKED FORMAT! # iea_op: andi.l &0x00ff00ff,USER_FPSR(%a6) btst &0xa,%d0 # is src fmt x or p? bne.b iea_op_pack # packed mov.l EXC_EXTWPTR(%a6),%a0 # pass: ptr to # lea FP_SRC(%a6),%a1 # pass: ptr to super addr mov.l &0xc,%d0 # pass: 12 bytes bsr.l _imem_read # read extended immediate tst.l %d1 # did ifetch fail? bne.w iea_iacc # yes bra.b iea_op_setsrc iea_op_pack: mov.l EXC_EXTWPTR(%a6),%a0 # pass: ptr to # lea FP_SRC(%a6),%a1 # pass: ptr to super dst mov.l &0xc,%d0 # pass: 12 bytes bsr.l _imem_read # read packed operand tst.l %d1 # did ifetch fail? bne.w iea_iacc # yes # The packed operand is an INF or a NAN if the exponent field is all ones. bfextu FP_SRC(%a6){&1:&15},%d0 # get exp cmpi.w %d0,&0x7fff # INF or NAN? beq.b iea_op_setsrc # operand is an INF or NAN # The packed operand is a zero if the mantissa is all zero, else it's # a normal packed op. mov.b 3+FP_SRC(%a6),%d0 # get byte 4 andi.b &0x0f,%d0 # clear all but last nybble bne.b iea_op_gp_not_spec # not a zero tst.l FP_SRC_HI(%a6) # is lw 2 zero? bne.b iea_op_gp_not_spec # not a zero tst.l FP_SRC_LO(%a6) # is lw 3 zero? beq.b iea_op_setsrc # operand is a ZERO iea_op_gp_not_spec: lea FP_SRC(%a6),%a0 # pass: ptr to packed op bsr.l decbin # convert to extended fmovm.x &0x80,FP_SRC(%a6) # make this the srcop iea_op_setsrc: addi.l &0xc,EXC_EXTWPTR(%a6) # update extension word pointer # FP_SRC now holds the src operand. lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l set_tag_x # tag the operand type mov.b %d0,STAG(%a6) # could be ANYTHING!!! cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b iea_op_getdst # no bsr.l unnorm_fix # yes; convert to NORM/DENORM/ZERO mov.b %d0,STAG(%a6) # set new optype tag iea_op_getdst: clr.b STORE_FLG(%a6) # clear "store result" boolean btst &0x5,1+EXC_CMDREG(%a6) # is operation monadic or dyadic? beq.b iea_op_extract # monadic btst &0x4,1+EXC_CMDREG(%a6) # is operation fsincos,ftst,fcmp? bne.b iea_op_spec # yes iea_op_loaddst: bfextu EXC_CMDREG(%a6){&6:&3},%d0 # fetch dst regno bsr.l load_fpn2 # load dst operand lea FP_DST(%a6),%a0 # pass: ptr to dst op bsr.l set_tag_x # tag the operand type mov.b %d0,DTAG(%a6) # could be ANYTHING!!! cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b iea_op_extract # no bsr.l unnorm_fix # yes; convert to NORM/DENORM/ZERO mov.b %d0,DTAG(%a6) # set new optype tag bra.b iea_op_extract # the operation is fsincos, ftst, or fcmp. only fcmp is dyadic iea_op_spec: btst &0x3,1+EXC_CMDREG(%a6) # is operation fsincos? beq.b iea_op_extract # yes # now, we're left with ftst and fcmp. so, first let's tag them so that they don't # store a result. then, only fcmp will branch back and pick up a dst operand. st STORE_FLG(%a6) # don't store a final result btst &0x1,1+EXC_CMDREG(%a6) # is operation fcmp? beq.b iea_op_loaddst # yes iea_op_extract: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass: rnd mode,prec mov.b 1+EXC_CMDREG(%a6),%d1 andi.w &0x007f,%d1 # extract extension fmov.l &0x0,%fpcr fmov.l &0x0,%fpsr lea FP_SRC(%a6),%a0 lea FP_DST(%a6),%a1 mov.l (tbl_unsupp.l,%pc,%d1.w*4),%d1 # fetch routine addr jsr (tbl_unsupp.l,%pc,%d1.l*1) # # Exceptions in order of precedence: # BSUN : none # SNAN : all operations # OPERR : all reg-reg or mem-reg operations that can normally operr # OVFL : same as OPERR # UNFL : same as OPERR # DZ : same as OPERR # INEX2 : same as OPERR # INEX1 : all packed immediate operations # # we determine the highest priority exception(if any) set by the # emulation routine that has also been enabled by the user. mov.b FPCR_ENABLE(%a6),%d0 # fetch exceptions enabled bne.b iea_op_ena # some are enabled # now, we save the result, unless, of course, the operation was ftst or fcmp. # these don't save results. iea_op_save: tst.b STORE_FLG(%a6) # does this op store a result? bne.b iea_op_exit1 # exit with no frestore iea_op_store: bfextu EXC_CMDREG(%a6){&6:&3},%d0 # fetch dst regno bsr.l store_fpreg # store the result iea_op_exit1: mov.l EXC_PC(%a6),USER_FPIAR(%a6) # set FPIAR to "Current PC" mov.l EXC_EXTWPTR(%a6),EXC_PC(%a6) # set "Next PC" in exc frame fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 # unravel the frame btst &0x7,(%sp) # is trace on? bne.w iea_op_trace # yes bra.l _fpsp_done # exit to os iea_op_ena: and.b FPSR_EXCEPT(%a6),%d0 # keep only ones enable and set bfffo %d0{&24:&8},%d0 # find highest priority exception bne.b iea_op_exc # at least one was set # no exception occurred. now, did a disabled, exact overflow occur with inexact # enabled? if so, then we have to stuff an overflow frame into the FPU. btst &ovfl_bit,FPSR_EXCEPT(%a6) # did overflow occur? beq.b iea_op_save iea_op_ovfl: btst &inex2_bit,FPCR_ENABLE(%a6) # is inexact enabled? beq.b iea_op_store # no bra.b iea_op_exc_ovfl # yes # an enabled exception occurred. we have to insert the exception type back into # the machine. iea_op_exc: subi.l &24,%d0 # fix offset to be 0-8 cmpi.b %d0,&0x6 # is exception INEX? bne.b iea_op_exc_force # no # the enabled exception was inexact. so, if it occurs with an overflow # or underflow that was disabled, then we have to force an overflow or # underflow frame. btst &ovfl_bit,FPSR_EXCEPT(%a6) # did overflow occur? bne.b iea_op_exc_ovfl # yes btst &unfl_bit,FPSR_EXCEPT(%a6) # did underflow occur? bne.b iea_op_exc_unfl # yes iea_op_exc_force: mov.w (tbl_iea_except.b,%pc,%d0.w*2),2+FP_SRC(%a6) bra.b iea_op_exit2 # exit with frestore tbl_iea_except: short 0xe002, 0xe006, 0xe004, 0xe005 short 0xe003, 0xe002, 0xe001, 0xe001 iea_op_exc_ovfl: mov.w &0xe005,2+FP_SRC(%a6) bra.b iea_op_exit2 iea_op_exc_unfl: mov.w &0xe003,2+FP_SRC(%a6) iea_op_exit2: mov.l EXC_PC(%a6),USER_FPIAR(%a6) # set FPIAR to "Current PC" mov.l EXC_EXTWPTR(%a6),EXC_PC(%a6) # set "Next PC" in exc frame fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # restore exceptional state unlk %a6 # unravel the frame btst &0x7,(%sp) # is trace on? bne.b iea_op_trace # yes bra.l _fpsp_done # exit to os # # The opclass two instruction that took an "Unimplemented Effective Address" # exception was being traced. Make the "current" PC the FPIAR and put it in # the trace stack frame then jump to _real_trace(). # # UNIMP EA FRAME TRACE FRAME # ***************** ***************** # * 0x0 * 0x0f0 * * Current * # ***************** * PC * # * Current * ***************** # * PC * * 0x2 * 0x024 * # ***************** ***************** # * SR * * Next * # ***************** * PC * # ***************** # * SR * # ***************** iea_op_trace: mov.l (%sp),-(%sp) # shift stack frame "down" mov.w 0x8(%sp),0x4(%sp) mov.w &0x2024,0x6(%sp) # stk fmt = 0x2; voff = 0x024 fmov.l %fpiar,0x8(%sp) # "Current PC" is in FPIAR bra.l _real_trace ######################################################################### iea_fmovm: btst &14,%d0 # ctrl or data reg beq.w iea_fmovm_ctrl iea_fmovm_data: btst &0x5,EXC_SR(%a6) # user or supervisor mode bne.b iea_fmovm_data_s iea_fmovm_data_u: mov.l %usp,%a0 mov.l %a0,EXC_A7(%a6) # store current a7 bsr.l fmovm_dynamic # do dynamic fmovm mov.l EXC_A7(%a6),%a0 # load possibly new a7 mov.l %a0,%usp # update usp bra.w iea_fmovm_exit iea_fmovm_data_s: clr.b SPCOND_FLG(%a6) lea 0x2+EXC_VOFF(%a6),%a0 mov.l %a0,EXC_A7(%a6) bsr.l fmovm_dynamic # do dynamic fmovm cmpi.b SPCOND_FLG(%a6),&mda7_flg beq.w iea_fmovm_data_predec cmpi.b SPCOND_FLG(%a6),&mia7_flg bne.w iea_fmovm_exit # right now, d0 = the size. # the data has been fetched from the supervisor stack, but we have not # incremented the stack pointer by the appropriate number of bytes. # do it here. iea_fmovm_data_postinc: btst &0x7,EXC_SR(%a6) bne.b iea_fmovm_data_pi_trace mov.w EXC_SR(%a6),(EXC_SR,%a6,%d0) mov.l EXC_EXTWPTR(%a6),(EXC_PC,%a6,%d0) mov.w &0x00f0,(EXC_VOFF,%a6,%d0) lea (EXC_SR,%a6,%d0),%a0 mov.l %a0,EXC_SR(%a6) fmovm.x EXC_FP0(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 mov.l (%sp)+,%sp bra.l _fpsp_done iea_fmovm_data_pi_trace: mov.w EXC_SR(%a6),(EXC_SR-0x4,%a6,%d0) mov.l EXC_EXTWPTR(%a6),(EXC_PC-0x4,%a6,%d0) mov.w &0x2024,(EXC_VOFF-0x4,%a6,%d0) mov.l EXC_PC(%a6),(EXC_VOFF+0x2-0x4,%a6,%d0) lea (EXC_SR-0x4,%a6,%d0),%a0 mov.l %a0,EXC_SR(%a6) fmovm.x EXC_FP0(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 mov.l (%sp)+,%sp bra.l _real_trace # right now, d1 = size and d0 = the strg. iea_fmovm_data_predec: mov.b %d1,EXC_VOFF(%a6) # store strg mov.b %d0,0x1+EXC_VOFF(%a6) # store size fmovm.x EXC_FP0(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 mov.l (%a6),-(%sp) # make a copy of a6 mov.l %d0,-(%sp) # save d0 mov.l %d1,-(%sp) # save d1 mov.l EXC_EXTWPTR(%a6),-(%sp) # make a copy of Next PC clr.l %d0 mov.b 0x1+EXC_VOFF(%a6),%d0 # fetch size neg.l %d0 # get negative of size btst &0x7,EXC_SR(%a6) # is trace enabled? beq.b iea_fmovm_data_p2 mov.w EXC_SR(%a6),(EXC_SR-0x4,%a6,%d0) mov.l EXC_PC(%a6),(EXC_VOFF-0x2,%a6,%d0) mov.l (%sp)+,(EXC_PC-0x4,%a6,%d0) mov.w &0x2024,(EXC_VOFF-0x4,%a6,%d0) pea (%a6,%d0) # create final sp bra.b iea_fmovm_data_p3 iea_fmovm_data_p2: mov.w EXC_SR(%a6),(EXC_SR,%a6,%d0) mov.l (%sp)+,(EXC_PC,%a6,%d0) mov.w &0x00f0,(EXC_VOFF,%a6,%d0) pea (0x4,%a6,%d0) # create final sp iea_fmovm_data_p3: clr.l %d1 mov.b EXC_VOFF(%a6),%d1 # fetch strg tst.b %d1 bpl.b fm_1 fmovm.x &0x80,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_1: lsl.b &0x1,%d1 bpl.b fm_2 fmovm.x &0x40,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_2: lsl.b &0x1,%d1 bpl.b fm_3 fmovm.x &0x20,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_3: lsl.b &0x1,%d1 bpl.b fm_4 fmovm.x &0x10,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_4: lsl.b &0x1,%d1 bpl.b fm_5 fmovm.x &0x08,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_5: lsl.b &0x1,%d1 bpl.b fm_6 fmovm.x &0x04,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_6: lsl.b &0x1,%d1 bpl.b fm_7 fmovm.x &0x02,(0x4+0x8,%a6,%d0) addi.l &0xc,%d0 fm_7: lsl.b &0x1,%d1 bpl.b fm_end fmovm.x &0x01,(0x4+0x8,%a6,%d0) fm_end: mov.l 0x4(%sp),%d1 mov.l 0x8(%sp),%d0 mov.l 0xc(%sp),%a6 mov.l (%sp)+,%sp btst &0x7,(%sp) # is trace enabled? beq.l _fpsp_done bra.l _real_trace ######################################################################### iea_fmovm_ctrl: bsr.l fmovm_ctrl # load ctrl regs iea_fmovm_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 btst &0x7,EXC_SR(%a6) # is trace on? bne.b iea_fmovm_trace # yes mov.l EXC_EXTWPTR(%a6),EXC_PC(%a6) # set Next PC unlk %a6 # unravel the frame bra.l _fpsp_done # exit to os # # The control reg instruction that took an "Unimplemented Effective Address" # exception was being traced. The "Current PC" for the trace frame is the # PC stacked for Unimp EA. The "Next PC" is in EXC_EXTWPTR. # After fixing the stack frame, jump to _real_trace(). # # UNIMP EA FRAME TRACE FRAME # ***************** ***************** # * 0x0 * 0x0f0 * * Current * # ***************** * PC * # * Current * ***************** # * PC * * 0x2 * 0x024 * # ***************** ***************** # * SR * * Next * # ***************** * PC * # ***************** # * SR * # ***************** # this ain't a pretty solution, but it works: # -restore a6 (not with unlk) # -shift stack frame down over where old a6 used to be # -add LOCAL_SIZE to stack pointer iea_fmovm_trace: mov.l (%a6),%a6 # restore frame pointer mov.w EXC_SR+LOCAL_SIZE(%sp),0x0+LOCAL_SIZE(%sp) mov.l EXC_PC+LOCAL_SIZE(%sp),0x8+LOCAL_SIZE(%sp) mov.l EXC_EXTWPTR+LOCAL_SIZE(%sp),0x2+LOCAL_SIZE(%sp) mov.w &0x2024,0x6+LOCAL_SIZE(%sp) # stk fmt = 0x2; voff = 0x024 add.l &LOCAL_SIZE,%sp # clear stack frame bra.l _real_trace ######################################################################### # The FPU is disabled and so we should really have taken the "Line # F Emulator" exception. So, here we create an 8-word stack frame # from our 4-word stack frame. This means we must calculate the length # of the faulting instruction to get the "next PC". This is trivial for # immediate operands but requires some extra work for fmovm dynamic # which can use most addressing modes. iea_disabled: mov.l (%sp)+,%d0 # restore d0 link %a6,&-LOCAL_SIZE # init stack frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 # PC of instruction that took the exception is the PC in the frame mov.l EXC_PC(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) # store OPWORD and EXTWORD tst.w %d0 # is instr fmovm? bmi.b iea_dis_fmovm # yes # instruction is using an extended precision immediate operand. therefore, # the total instruction length is 16 bytes. iea_dis_immed: mov.l &0x10,%d0 # 16 bytes of instruction bra.b iea_dis_cont iea_dis_fmovm: btst &0xe,%d0 # is instr fmovm ctrl bne.b iea_dis_fmovm_data # no # the instruction is a fmovm.l with 2 or 3 registers. bfextu %d0{&19:&3},%d1 mov.l &0xc,%d0 cmpi.b %d1,&0x7 # move all regs? bne.b iea_dis_cont addq.l &0x4,%d0 bra.b iea_dis_cont # the instruction is an fmovm.x dynamic which can use many addressing # modes and thus can have several different total instruction lengths. # call fmovm_calc_ea which will go through the ea calc process and, # as a by-product, will tell us how long the instruction is. iea_dis_fmovm_data: clr.l %d0 bsr.l fmovm_calc_ea mov.l EXC_EXTWPTR(%a6),%d0 sub.l EXC_PC(%a6),%d0 iea_dis_cont: mov.w %d0,EXC_VOFF(%a6) # store stack shift value movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 # here, we actually create the 8-word frame from the 4-word frame, # with the "next PC" as additional info. # the field is let as undefined. subq.l &0x8,%sp # make room for new stack mov.l %d0,-(%sp) # save d0 mov.w 0xc(%sp),0x4(%sp) # move SR mov.l 0xe(%sp),0x6(%sp) # move Current PC clr.l %d0 mov.w 0x12(%sp),%d0 mov.l 0x6(%sp),0x10(%sp) # move Current PC add.l %d0,0x6(%sp) # make Next PC mov.w &0x402c,0xa(%sp) # insert offset,frame format mov.l (%sp)+,%d0 # restore d0 bra.l _real_fpu_disabled ########## iea_iacc: movc %pcr,%d0 btst &0x1,%d0 bne.b iea_iacc_cont fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 on stack iea_iacc_cont: movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 subq.w &0x8,%sp # make stack frame bigger mov.l 0x8(%sp),(%sp) # store SR,hi(PC) mov.w 0xc(%sp),0x4(%sp) # store lo(PC) mov.w &0x4008,0x6(%sp) # store voff mov.l 0x2(%sp),0x8(%sp) # store ea mov.l &0x09428001,0xc(%sp) # store fslw iea_acc_done: btst &0x5,(%sp) # user or supervisor mode? beq.b iea_acc_done2 # user bset &0x2,0xd(%sp) # set supervisor TM bit iea_acc_done2: bra.l _real_access iea_dacc: lea -LOCAL_SIZE(%a6),%sp movc %pcr,%d1 btst &0x1,%d1 bne.b iea_dacc_cont fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 on stack fmovm.l LOCAL_SIZE+USER_FPCR(%sp),%fpcr,%fpsr,%fpiar # restore ctrl regs iea_dacc_cont: mov.l (%a6),%a6 mov.l 0x4+LOCAL_SIZE(%sp),-0x8+0x4+LOCAL_SIZE(%sp) mov.w 0x8+LOCAL_SIZE(%sp),-0x8+0x8+LOCAL_SIZE(%sp) mov.w &0x4008,-0x8+0xa+LOCAL_SIZE(%sp) mov.l %a0,-0x8+0xc+LOCAL_SIZE(%sp) mov.w %d0,-0x8+0x10+LOCAL_SIZE(%sp) mov.w &0x0001,-0x8+0x12+LOCAL_SIZE(%sp) movm.l LOCAL_SIZE+EXC_DREGS(%sp),&0x0303 # restore d0-d1/a0-a1 add.w &LOCAL_SIZE-0x4,%sp bra.b iea_acc_done ######################################################################### # XDEF **************************************************************** # # _fpsp_operr(): 060FPSP entry point for FP Operr exception. # # # # This handler should be the first code executed upon taking the # # FP Operand Error exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword # # fix_skewed_ops() - adjust src operand in fsave frame # # _real_operr() - "callout" to operating system operr handler # # _dmem_write_{byte,word,long}() - store data to mem (opclass 3) # # store_dreg_{b,w,l}() - store data to data regfile (opclass 3) # # facc_out_{b,w,l}() - store to memory took access error (opcl 3) # # # # INPUT *************************************************************** # # - The system stack contains the FP Operr exception frame # # - The fsave frame contains the source operand # # # # OUTPUT ************************************************************** # # No access error: # # - The system stack is unchanged # # - The fsave frame contains the adjusted src op for opclass 0,2 # # # # ALGORITHM *********************************************************** # # In a system where the FP Operr exception is enabled, the goal # # is to get to the handler specified at _real_operr(). But, on the 060, # # for opclass zero and two instruction taking this exception, the # # input operand in the fsave frame may be incorrect for some cases # # and needs to be corrected. This handler calls fix_skewed_ops() to # # do just this and then exits through _real_operr(). # # For opclass 3 instructions, the 060 doesn't store the default # # operr result out to memory or data register file as it should. # # This code must emulate the move out before finally exiting through # # _real_inex(). The move out, if to memory, is performed using # # _mem_write() "callout" routines that may return a failing result. # # In this special case, the handler must exit through facc_out() # # which creates an access error stack frame from the current operr # # stack frame. # # # ######################################################################### global _fpsp_operr _fpsp_operr: link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # grab the "busy" frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # the FPIAR holds the "current PC" of the faulting instruction mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################## btst &13,%d0 # is instr an fmove out? bne.b foperr_out # fmove out # here, we simply see if the operand in the fsave frame needs to be "unskewed". # this would be the case for opclass two operations with a source infinity or # denorm operand in the sgl or dbl format. NANs also become skewed, but can't # cause an operr so we don't need to check for them here. lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l fix_skewed_ops # fix src op foperr_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) unlk %a6 bra.l _real_operr ######################################################################## # # the hardware does not save the default result to memory on enabled # operand error exceptions. we do this here before passing control to # the user operand error handler. # # byte, word, and long destination format operations can pass # through here. we simply need to test the sign of the src # operand and save the appropriate minimum or maximum integer value # to the effective address as pointed to by the stacked effective address. # # although packed opclass three operations can take operand error # exceptions, they won't pass through here since they are caught # first by the unsupported data format exception handler. that handler # sends them directly to _real_operr() if necessary. # foperr_out: mov.w FP_SRC_EX(%a6),%d1 # fetch exponent andi.w &0x7fff,%d1 cmpi.w %d1,&0x7fff bne.b foperr_out_not_qnan # the operand is either an infinity or a QNAN. tst.l FP_SRC_LO(%a6) bne.b foperr_out_qnan mov.l FP_SRC_HI(%a6),%d1 andi.l &0x7fffffff,%d1 beq.b foperr_out_not_qnan foperr_out_qnan: mov.l FP_SRC_HI(%a6),L_SCR1(%a6) bra.b foperr_out_jmp foperr_out_not_qnan: mov.l &0x7fffffff,%d1 tst.b FP_SRC_EX(%a6) bpl.b foperr_out_not_qnan2 addq.l &0x1,%d1 foperr_out_not_qnan2: mov.l %d1,L_SCR1(%a6) foperr_out_jmp: bfextu %d0{&19:&3},%d0 # extract dst format field mov.b 1+EXC_OPWORD(%a6),%d1 # extract mode,reg mov.w (tbl_operr.b,%pc,%d0.w*2),%a0 jmp (tbl_operr.b,%pc,%a0) tbl_operr: short foperr_out_l - tbl_operr # long word integer short tbl_operr - tbl_operr # sgl prec shouldn't happen short tbl_operr - tbl_operr # ext prec shouldn't happen short foperr_exit - tbl_operr # packed won't enter here short foperr_out_w - tbl_operr # word integer short tbl_operr - tbl_operr # dbl prec shouldn't happen short foperr_out_b - tbl_operr # byte integer short tbl_operr - tbl_operr # packed won't enter here foperr_out_b: mov.b L_SCR1(%a6),%d0 # load positive default result cmpi.b %d1,&0x7 # is mode a data reg? ble.b foperr_out_b_save_dn # yes mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_byte # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_b # yes bra.w foperr_exit foperr_out_b_save_dn: andi.w &0x0007,%d1 bsr.l store_dreg_b # store result to regfile bra.w foperr_exit foperr_out_w: mov.w L_SCR1(%a6),%d0 # load positive default result cmpi.b %d1,&0x7 # is mode a data reg? ble.b foperr_out_w_save_dn # yes mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_word # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_w # yes bra.w foperr_exit foperr_out_w_save_dn: andi.w &0x0007,%d1 bsr.l store_dreg_w # store result to regfile bra.w foperr_exit foperr_out_l: mov.l L_SCR1(%a6),%d0 # load positive default result cmpi.b %d1,&0x7 # is mode a data reg? ble.b foperr_out_l_save_dn # yes mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_long # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_l # yes bra.w foperr_exit foperr_out_l_save_dn: andi.w &0x0007,%d1 bsr.l store_dreg_l # store result to regfile bra.w foperr_exit ######################################################################### # XDEF **************************************************************** # # _fpsp_snan(): 060FPSP entry point for FP SNAN exception. # # # # This handler should be the first code executed upon taking the # # FP Signalling NAN exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword # # fix_skewed_ops() - adjust src operand in fsave frame # # _real_snan() - "callout" to operating system SNAN handler # # _dmem_write_{byte,word,long}() - store data to mem (opclass 3) # # store_dreg_{b,w,l}() - store data to data regfile (opclass 3) # # facc_out_{b,w,l,d,x}() - store to mem took acc error (opcl 3) # # _calc_ea_fout() - fix An if is -() or ()+; also get # # # # INPUT *************************************************************** # # - The system stack contains the FP SNAN exception frame # # - The fsave frame contains the source operand # # # # OUTPUT ************************************************************** # # No access error: # # - The system stack is unchanged # # - The fsave frame contains the adjusted src op for opclass 0,2 # # # # ALGORITHM *********************************************************** # # In a system where the FP SNAN exception is enabled, the goal # # is to get to the handler specified at _real_snan(). But, on the 060, # # for opclass zero and two instructions taking this exception, the # # input operand in the fsave frame may be incorrect for some cases # # and needs to be corrected. This handler calls fix_skewed_ops() to # # do just this and then exits through _real_snan(). # # For opclass 3 instructions, the 060 doesn't store the default # # SNAN result out to memory or data register file as it should. # # This code must emulate the move out before finally exiting through # # _real_snan(). The move out, if to memory, is performed using # # _mem_write() "callout" routines that may return a failing result. # # In this special case, the handler must exit through facc_out() # # which creates an access error stack frame from the current SNAN # # stack frame. # # For the case of an extended precision opclass 3 instruction, # # if the effective addressing mode was -() or ()+, then the address # # register must get updated by calling _calc_ea_fout(). If the # # was -(a7) from supervisor mode, then the exception frame currently # # on the system stack must be carefully moved "down" to make room # # for the operand being moved. # # # ######################################################################### global _fpsp_snan _fpsp_snan: link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # grab the "busy" frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # the FPIAR holds the "current PC" of the faulting instruction mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################## btst &13,%d0 # is instr an fmove out? bne.w fsnan_out # fmove out # here, we simply see if the operand in the fsave frame needs to be "unskewed". # this would be the case for opclass two operations with a source infinity or # denorm operand in the sgl or dbl format. NANs also become skewed and must be # fixed here. lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l fix_skewed_ops # fix src op fsnan_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) unlk %a6 bra.l _real_snan ######################################################################## # # the hardware does not save the default result to memory on enabled # snan exceptions. we do this here before passing control to # the user snan handler. # # byte, word, long, and packed destination format operations can pass # through here. since packed format operations already were handled by # fpsp_unsupp(), then we need to do nothing else for them here. # for byte, word, and long, we simply need to test the sign of the src # operand and save the appropriate minimum or maximum integer value # to the effective address as pointed to by the stacked effective address. # fsnan_out: bfextu %d0{&19:&3},%d0 # extract dst format field mov.b 1+EXC_OPWORD(%a6),%d1 # extract mode,reg mov.w (tbl_snan.b,%pc,%d0.w*2),%a0 jmp (tbl_snan.b,%pc,%a0) tbl_snan: short fsnan_out_l - tbl_snan # long word integer short fsnan_out_s - tbl_snan # sgl prec shouldn't happen short fsnan_out_x - tbl_snan # ext prec shouldn't happen short tbl_snan - tbl_snan # packed needs no help short fsnan_out_w - tbl_snan # word integer short fsnan_out_d - tbl_snan # dbl prec shouldn't happen short fsnan_out_b - tbl_snan # byte integer short tbl_snan - tbl_snan # packed needs no help fsnan_out_b: mov.b FP_SRC_HI(%a6),%d0 # load upper byte of SNAN bset &6,%d0 # set SNAN bit cmpi.b %d1,&0x7 # is mode a data reg? ble.b fsnan_out_b_dn # yes mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_byte # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_b # yes bra.w fsnan_exit fsnan_out_b_dn: andi.w &0x0007,%d1 bsr.l store_dreg_b # store result to regfile bra.w fsnan_exit fsnan_out_w: mov.w FP_SRC_HI(%a6),%d0 # load upper word of SNAN bset &14,%d0 # set SNAN bit cmpi.b %d1,&0x7 # is mode a data reg? ble.b fsnan_out_w_dn # yes mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_word # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_w # yes bra.w fsnan_exit fsnan_out_w_dn: andi.w &0x0007,%d1 bsr.l store_dreg_w # store result to regfile bra.w fsnan_exit fsnan_out_l: mov.l FP_SRC_HI(%a6),%d0 # load upper longword of SNAN bset &30,%d0 # set SNAN bit cmpi.b %d1,&0x7 # is mode a data reg? ble.b fsnan_out_l_dn # yes mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_long # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_l # yes bra.w fsnan_exit fsnan_out_l_dn: andi.w &0x0007,%d1 bsr.l store_dreg_l # store result to regfile bra.w fsnan_exit fsnan_out_s: cmpi.b %d1,&0x7 # is mode a data reg? ble.b fsnan_out_d_dn # yes mov.l FP_SRC_EX(%a6),%d0 # fetch SNAN sign andi.l &0x80000000,%d0 # keep sign ori.l &0x7fc00000,%d0 # insert new exponent,SNAN bit mov.l FP_SRC_HI(%a6),%d1 # load mantissa lsr.l &0x8,%d1 # shift mantissa for sgl or.l %d1,%d0 # create sgl SNAN mov.l EXC_EA(%a6),%a0 # pass: of default result bsr.l _dmem_write_long # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_l # yes bra.w fsnan_exit fsnan_out_d_dn: mov.l FP_SRC_EX(%a6),%d0 # fetch SNAN sign andi.l &0x80000000,%d0 # keep sign ori.l &0x7fc00000,%d0 # insert new exponent,SNAN bit mov.l %d1,-(%sp) mov.l FP_SRC_HI(%a6),%d1 # load mantissa lsr.l &0x8,%d1 # shift mantissa for sgl or.l %d1,%d0 # create sgl SNAN mov.l (%sp)+,%d1 andi.w &0x0007,%d1 bsr.l store_dreg_l # store result to regfile bra.w fsnan_exit fsnan_out_d: mov.l FP_SRC_EX(%a6),%d0 # fetch SNAN sign andi.l &0x80000000,%d0 # keep sign ori.l &0x7ff80000,%d0 # insert new exponent,SNAN bit mov.l FP_SRC_HI(%a6),%d1 # load hi mantissa mov.l %d0,FP_SCR0_EX(%a6) # store to temp space mov.l &11,%d0 # load shift amt lsr.l %d0,%d1 or.l %d1,FP_SCR0_EX(%a6) # create dbl hi mov.l FP_SRC_HI(%a6),%d1 # load hi mantissa andi.l &0x000007ff,%d1 ror.l %d0,%d1 mov.l %d1,FP_SCR0_HI(%a6) # store to temp space mov.l FP_SRC_LO(%a6),%d1 # load lo mantissa lsr.l %d0,%d1 or.l %d1,FP_SCR0_HI(%a6) # create dbl lo lea FP_SCR0(%a6),%a0 # pass: ptr to operand mov.l EXC_EA(%a6),%a1 # pass: dst addr movq.l &0x8,%d0 # pass: size of 8 bytes bsr.l _dmem_write # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_d # yes bra.w fsnan_exit # for extended precision, if the addressing mode is pre-decrement or # post-increment, then the address register did not get updated. # in addition, for pre-decrement, the stacked is incorrect. fsnan_out_x: clr.b SPCOND_FLG(%a6) # clear special case flag mov.w FP_SRC_EX(%a6),FP_SCR0_EX(%a6) clr.w 2+FP_SCR0(%a6) mov.l FP_SRC_HI(%a6),%d0 bset &30,%d0 mov.l %d0,FP_SCR0_HI(%a6) mov.l FP_SRC_LO(%a6),FP_SCR0_LO(%a6) btst &0x5,EXC_SR(%a6) # supervisor mode exception? bne.b fsnan_out_x_s # yes mov.l %usp,%a0 # fetch user stack pointer mov.l %a0,EXC_A7(%a6) # save on stack for calc_ea() mov.l (%a6),EXC_A6(%a6) bsr.l _calc_ea_fout # find the correct ea,update An mov.l %a0,%a1 mov.l %a0,EXC_EA(%a6) # stack correct mov.l EXC_A7(%a6),%a0 mov.l %a0,%usp # restore user stack pointer mov.l EXC_A6(%a6),(%a6) fsnan_out_x_save: lea FP_SCR0(%a6),%a0 # pass: ptr to operand movq.l &0xc,%d0 # pass: size of extended bsr.l _dmem_write # write the default result tst.l %d1 # did dstore fail? bne.l facc_out_x # yes bra.w fsnan_exit fsnan_out_x_s: mov.l (%a6),EXC_A6(%a6) bsr.l _calc_ea_fout # find the correct ea,update An mov.l %a0,%a1 mov.l %a0,EXC_EA(%a6) # stack correct mov.l EXC_A6(%a6),(%a6) cmpi.b SPCOND_FLG(%a6),&mda7_flg # is mode -(a7)? bne.b fsnan_out_x_save # no # the operation was "fmove.x SNAN,-(a7)" from supervisor mode. fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) mov.l EXC_A6(%a6),%a6 # restore frame pointer mov.l LOCAL_SIZE+EXC_SR(%sp),LOCAL_SIZE+EXC_SR-0xc(%sp) mov.l LOCAL_SIZE+EXC_PC+0x2(%sp),LOCAL_SIZE+EXC_PC+0x2-0xc(%sp) mov.l LOCAL_SIZE+EXC_EA(%sp),LOCAL_SIZE+EXC_EA-0xc(%sp) mov.l LOCAL_SIZE+FP_SCR0_EX(%sp),LOCAL_SIZE+EXC_SR(%sp) mov.l LOCAL_SIZE+FP_SCR0_HI(%sp),LOCAL_SIZE+EXC_PC+0x2(%sp) mov.l LOCAL_SIZE+FP_SCR0_LO(%sp),LOCAL_SIZE+EXC_EA(%sp) add.l &LOCAL_SIZE-0x8,%sp bra.l _real_snan ######################################################################### # XDEF **************************************************************** # # _fpsp_inex(): 060FPSP entry point for FP Inexact exception. # # # # This handler should be the first code executed upon taking the # # FP Inexact exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword # # fix_skewed_ops() - adjust src operand in fsave frame # # set_tag_x() - determine optype of src/dst operands # # store_fpreg() - store opclass 0 or 2 result to FP regfile # # unnorm_fix() - change UNNORM operands to NORM or ZERO # # load_fpn2() - load dst operand from FP regfile # # smovcr() - emulate an "fmovcr" instruction # # fout() - emulate an opclass 3 instruction # # tbl_unsupp - add of table of emulation routines for opclass 0,2 # # _real_inex() - "callout" to operating system inexact handler # # # # INPUT *************************************************************** # # - The system stack contains the FP Inexact exception frame # # - The fsave frame contains the source operand # # # # OUTPUT ************************************************************** # # - The system stack is unchanged # # - The fsave frame contains the adjusted src op for opclass 0,2 # # # # ALGORITHM *********************************************************** # # In a system where the FP Inexact exception is enabled, the goal # # is to get to the handler specified at _real_inex(). But, on the 060, # # for opclass zero and two instruction taking this exception, the # # hardware doesn't store the correct result to the destination FP # # register as did the '040 and '881/2. This handler must emulate the # # instruction in order to get this value and then store it to the # # correct register before calling _real_inex(). # # For opclass 3 instructions, the 060 doesn't store the default # # inexact result out to memory or data register file as it should. # # This code must emulate the move out by calling fout() before finally # # exiting through _real_inex(). # # # ######################################################################### global _fpsp_inex _fpsp_inex: link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # grab the "busy" frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # the FPIAR holds the "current PC" of the faulting instruction mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################## btst &13,%d0 # is instr an fmove out? bne.w finex_out # fmove out # the hardware, for "fabs" and "fneg" w/ a long source format, puts the # longword integer directly into the upper longword of the mantissa along # w/ an exponent value of 0x401e. we convert this to extended precision here. bfextu %d0{&19:&3},%d0 # fetch instr size bne.b finex_cont # instr size is not long cmpi.w FP_SRC_EX(%a6),&0x401e # is exponent 0x401e? bne.b finex_cont # no fmov.l &0x0,%fpcr fmov.l FP_SRC_HI(%a6),%fp0 # load integer src fmov.x %fp0,FP_SRC(%a6) # store integer as extended precision mov.w &0xe001,0x2+FP_SRC(%a6) finex_cont: lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l fix_skewed_ops # fix src op # Here, we zero the ccode and exception byte field since we're going to # emulate the whole instruction. Notice, though, that we don't kill the # INEX1 bit. This is because a packed op has long since been converted # to extended before arriving here. Therefore, we need to retain the # INEX1 bit from when the operand was first converted. andi.l &0x00ff01ff,USER_FPSR(%a6) # zero all but accured field fmov.l &0x0,%fpcr # zero current control regs fmov.l &0x0,%fpsr bfextu EXC_EXTWORD(%a6){&0:&6},%d1 # extract upper 6 of cmdreg cmpi.b %d1,&0x17 # is op an fmovecr? beq.w finex_fmovcr # yes lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l set_tag_x # tag the operand type mov.b %d0,STAG(%a6) # maybe NORM,DENORM # bits four and five of the fp extension word separate the monadic and dyadic # operations that can pass through fpsp_inex(). remember that fcmp and ftst # will never take this exception, but fsincos will. btst &0x5,1+EXC_CMDREG(%a6) # is operation monadic or dyadic? beq.b finex_extract # monadic btst &0x4,1+EXC_CMDREG(%a6) # is operation an fsincos? bne.b finex_extract # yes bfextu EXC_CMDREG(%a6){&6:&3},%d0 # dyadic; load dst reg bsr.l load_fpn2 # load dst into FP_DST lea FP_DST(%a6),%a0 # pass: ptr to dst op bsr.l set_tag_x # tag the operand type cmpi.b %d0,&UNNORM # is operand an UNNORM? bne.b finex_op2_done # no bsr.l unnorm_fix # yes; convert to NORM,DENORM,or ZERO finex_op2_done: mov.b %d0,DTAG(%a6) # save dst optype tag finex_extract: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec/mode mov.b 1+EXC_CMDREG(%a6),%d1 andi.w &0x007f,%d1 # extract extension lea FP_SRC(%a6),%a0 lea FP_DST(%a6),%a1 mov.l (tbl_unsupp.l,%pc,%d1.w*4),%d1 # fetch routine addr jsr (tbl_unsupp.l,%pc,%d1.l*1) # the operation has been emulated. the result is in fp0. finex_save: bfextu EXC_CMDREG(%a6){&6:&3},%d0 bsr.l store_fpreg finex_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) unlk %a6 bra.l _real_inex finex_fmovcr: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec,mode mov.b 1+EXC_CMDREG(%a6),%d1 andi.l &0x0000007f,%d1 # pass rom offset bsr.l smovcr bra.b finex_save ######################################################################## # # the hardware does not save the default result to memory on enabled # inexact exceptions. we do this here before passing control to # the user inexact handler. # # byte, word, and long destination format operations can pass # through here. so can double and single precision. # although packed opclass three operations can take inexact # exceptions, they won't pass through here since they are caught # first by the unsupported data format exception handler. that handler # sends them directly to _real_inex() if necessary. # finex_out: mov.b &NORM,STAG(%a6) # src is a NORM clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # pass rnd prec,mode andi.l &0xffff00ff,USER_FPSR(%a6) # zero exception field lea FP_SRC(%a6),%a0 # pass ptr to src operand bsr.l fout # store the default result bra.b finex_exit ######################################################################### # XDEF **************************************************************** # # _fpsp_dz(): 060FPSP entry point for FP DZ exception. # # # # This handler should be the first code executed upon taking # # the FP DZ exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_long() - read instruction longword from memory # # fix_skewed_ops() - adjust fsave operand # # _real_dz() - "callout" exit point from FP DZ handler # # # # INPUT *************************************************************** # # - The system stack contains the FP DZ exception stack. # # - The fsave frame contains the source operand. # # # # OUTPUT ************************************************************** # # - The system stack contains the FP DZ exception stack. # # - The fsave frame contains the adjusted source operand. # # # # ALGORITHM *********************************************************** # # In a system where the DZ exception is enabled, the goal is to # # get to the handler specified at _real_dz(). But, on the 060, when the # # exception is taken, the input operand in the fsave state frame may # # be incorrect for some cases and need to be adjusted. So, this package # # adjusts the operand using fix_skewed_ops() and then branches to # # _real_dz(). # # # ######################################################################### global _fpsp_dz _fpsp_dz: link.w %a6,&-LOCAL_SIZE # init stack frame fsave FP_SRC(%a6) # grab the "busy" frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 on stack # the FPIAR holds the "current PC" of the faulting instruction mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################## # here, we simply see if the operand in the fsave frame needs to be "unskewed". # this would be the case for opclass two operations with a source zero # in the sgl or dbl format. lea FP_SRC(%a6),%a0 # pass: ptr to src op bsr.l fix_skewed_ops # fix src op fdz_exit: fmovm.x EXC_FPREGS(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) unlk %a6 bra.l _real_dz ######################################################################### # XDEF **************************************************************** # # _fpsp_fline(): 060FPSP entry point for "Line F emulator" exc. # # # # This handler should be the first code executed upon taking the # # "Line F Emulator" exception in an operating system. # # # # XREF **************************************************************** # # _fpsp_unimp() - handle "FP Unimplemented" exceptions # # _real_fpu_disabled() - handle "FPU disabled" exceptions # # _real_fline() - handle "FLINE" exceptions # # _imem_read_long() - read instruction longword # # # # INPUT *************************************************************** # # - The system stack contains a "Line F Emulator" exception # # stack frame. # # # # OUTPUT ************************************************************** # # - The system stack is unchanged # # # # ALGORITHM *********************************************************** # # When a "Line F Emulator" exception occurs, there are 3 possible # # exception types, denoted by the exception stack frame format number: # # (1) FPU unimplemented instruction (6 word stack frame) # # (2) FPU disabled (8 word stack frame) # # (3) Line F (4 word stack frame) # # # # This module determines which and forks the flow off to the # # appropriate "callout" (for "disabled" and "Line F") or to the # # correct emulation code (for "FPU unimplemented"). # # This code also must check for "fmovecr" instructions w/ a # # non-zero field. These may get flagged as "Line F" but should # # really be flagged as "FPU Unimplemented". (This is a "feature" on # # the '060. # # # ######################################################################### global _fpsp_fline _fpsp_fline: # check to see if this exception is a "FP Unimplemented Instruction" # exception. if so, branch directly to that handler's entry point. cmpi.w 0x6(%sp),&0x202c beq.l _fpsp_unimp # check to see if the FPU is disabled. if so, jump to the OS entry # point for that condition. cmpi.w 0x6(%sp),&0x402c beq.l _real_fpu_disabled # the exception was an "F-Line Illegal" exception. we check to see # if the F-Line instruction is an "fmovecr" w/ a non-zero . if # so, convert the F-Line exception stack frame to an FP Unimplemented # Instruction exception stack frame else branch to the OS entry # point for the F-Line exception handler. link.w %a6,&-LOCAL_SIZE # init stack frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 mov.l EXC_PC(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch instruction words bfextu %d0{&0:&10},%d1 # is it an fmovecr? cmpi.w %d1,&0x03c8 bne.b fline_fline # no bfextu %d0{&16:&6},%d1 # is it an fmovecr? cmpi.b %d1,&0x17 bne.b fline_fline # no # it's an fmovecr w/ a non-zero that has entered through # the F-Line Illegal exception. # so, we need to convert the F-Line exception stack frame into an # FP Unimplemented Instruction stack frame and jump to that entry # point. # # but, if the FPU is disabled, then we need to jump to the FPU diabled # entry point. movc %pcr,%d0 btst &0x1,%d0 beq.b fline_fmovcr movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 sub.l &0x8,%sp # make room for "Next PC", mov.w 0x8(%sp),(%sp) mov.l 0xa(%sp),0x2(%sp) # move "Current PC" mov.w &0x402c,0x6(%sp) mov.l 0x2(%sp),0xc(%sp) addq.l &0x4,0x2(%sp) # set "Next PC" bra.l _real_fpu_disabled fline_fmovcr: movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 fmov.l 0x2(%sp),%fpiar # set current PC addq.l &0x4,0x2(%sp) # set Next PC mov.l (%sp),-(%sp) mov.l 0x8(%sp),0x4(%sp) mov.b &0x20,0x6(%sp) bra.l _fpsp_unimp fline_fline: movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 bra.l _real_fline ######################################################################### # XDEF **************************************************************** # # _fpsp_unimp(): 060FPSP entry point for FP "Unimplemented # # Instruction" exception. # # # # This handler should be the first code executed upon taking the # # FP Unimplemented Instruction exception in an operating system. # # # # XREF **************************************************************** # # _imem_read_{word,long}() - read instruction word/longword # # load_fop() - load src/dst ops from memory and/or FP regfile # # store_fpreg() - store opclass 0 or 2 result to FP regfile # # tbl_trans - addr of table of emulation routines for trnscndls # # _real_access() - "callout" for access error exception # # _fpsp_done() - "callout" for exit; work all done # # _real_trace() - "callout" for Trace enabled exception # # smovcr() - emulate "fmovecr" instruction # # funimp_skew() - adjust fsave src ops to "incorrect" value # # _ftrapcc() - emulate an "ftrapcc" instruction # # _fdbcc() - emulate an "fdbcc" instruction # # _fscc() - emulate an "fscc" instruction # # _real_trap() - "callout" for Trap exception # # _real_bsun() - "callout" for enabled Bsun exception # # # # INPUT *************************************************************** # # - The system stack contains the "Unimplemented Instr" stk frame # # # # OUTPUT ************************************************************** # # If access error: # # - The system stack is changed to an access error stack frame # # If Trace exception enabled: # # - The system stack is changed to a Trace exception stack frame # # Else: (normal case) # # - Correct result has been stored as appropriate # # # # ALGORITHM *********************************************************** # # There are two main cases of instructions that may enter here to # # be emulated: (1) the FPgen instructions, most of which were also # # unimplemented on the 040, and (2) "ftrapcc", "fscc", and "fdbcc". # # For the first set, this handler calls the routine load_fop() # # to load the source and destination (for dyadic) operands to be used # # for instruction emulation. The correct emulation routine is then # # chosen by decoding the instruction type and indexing into an # # emulation subroutine index table. After emulation returns, this # # handler checks to see if an exception should occur as a result of the # # FP instruction emulation. If so, then an FP exception of the correct # # type is inserted into the FPU state frame using the "frestore" # # instruction before exiting through _fpsp_done(). In either the # # exceptional or non-exceptional cases, we must check to see if the # # Trace exception is enabled. If so, then we must create a Trace # # exception frame from the current exception frame and exit through # # _real_trace(). # # For "fdbcc", "ftrapcc", and "fscc", the emulation subroutines # # _fdbcc(), _ftrapcc(), and _fscc() respectively are used. All three # # may flag that a BSUN exception should be taken. If so, then the # # current exception stack frame is converted into a BSUN exception # # stack frame and an exit is made through _real_bsun(). If the # # instruction was "ftrapcc" and a Trap exception should result, a Trap # # exception stack frame is created from the current frame and an exit # # is made through _real_trap(). If a Trace exception is pending, then # # a Trace exception frame is created from the current frame and a jump # # is made to _real_trace(). Finally, if none of these conditions exist, # # then the handler exits though the callout _fpsp_done(). # # # # In any of the above scenarios, if a _mem_read() or _mem_write() # # "callout" returns a failing value, then an access error stack frame # # is created from the current stack frame and an exit is made through # # _real_access(). # # # ######################################################################### # # FP UNIMPLEMENTED INSTRUCTION STACK FRAME: # # ***************** # * * => of fp unimp instr. # - EA - # * * # ***************** # * 0x2 * 0x02c * => frame format and vector offset(vector #11) # ***************** # * * # - Next PC - => PC of instr to execute after exc handling # * * # ***************** # * SR * => SR at the time the exception was taken # ***************** # # Note: the !NULL bit does not get set in the fsave frame when the # machine encounters an fp unimp exception. Therefore, it must be set # before leaving this handler. # global _fpsp_unimp _fpsp_unimp: link.w %a6,&-LOCAL_SIZE # init stack frame movm.l &0x0303,EXC_DREGS(%a6) # save d0-d1/a0-a1 fmovm.l %fpcr,%fpsr,%fpiar,USER_FPCR(%a6) # save ctrl regs fmovm.x &0xc0,EXC_FPREGS(%a6) # save fp0-fp1 btst &0x5,EXC_SR(%a6) # user mode exception? bne.b funimp_s # no; supervisor mode # save the value of the user stack pointer onto the stack frame funimp_u: mov.l %usp,%a0 # fetch user stack pointer mov.l %a0,EXC_A7(%a6) # store in stack frame bra.b funimp_cont # store the value of the supervisor stack pointer BEFORE the exc occurred. # old_sp is address just above stacked effective address. funimp_s: lea 4+EXC_EA(%a6),%a0 # load old a7' mov.l %a0,EXC_A7(%a6) # store a7' mov.l %a0,OLD_A7(%a6) # make a copy funimp_cont: # the FPIAR holds the "current PC" of the faulting instruction. mov.l USER_FPIAR(%a6),EXC_EXTWPTR(%a6) mov.l EXC_EXTWPTR(%a6),%a0 # fetch instruction addr addq.l &0x4,EXC_EXTWPTR(%a6) # incr instruction ptr bsr.l _imem_read_long # fetch the instruction words mov.l %d0,EXC_OPWORD(%a6) ############################################################################ fmov.l &0x0,%fpcr # clear FPCR fmov.l &0x0,%fpsr # clear FPSR clr.b SPCOND_FLG(%a6) # clear "special case" flag # Divide the fp instructions into 8 types based on the TYPE field in # bits 6-8 of the opword(classes 6,7 are undefined). # (for the '060, only two types can take this exception) # bftst %d0{&7:&3} # test TYPE btst &22,%d0 # type 0 or 1 ? bne.w funimp_misc # type 1 ######################################### # TYPE == 0: General instructions # ######################################### funimp_gen: clr.b STORE_FLG(%a6) # clear "store result" flag # clear the ccode byte and exception status byte andi.l &0x00ff00ff,USER_FPSR(%a6) bfextu %d0{&16:&6},%d1 # extract upper 6 of cmdreg cmpi.b %d1,&0x17 # is op an fmovecr? beq.w funimp_fmovcr # yes funimp_gen_op: bsr.l _load_fop # load clr.l %d0 mov.b FPCR_MODE(%a6),%d0 # fetch rnd mode mov.b 1+EXC_CMDREG(%a6),%d1 andi.w &0x003f,%d1 # extract extension bits lsl.w &0x3,%d1 # shift right 3 bits or.b STAG(%a6),%d1 # insert src optag bits lea FP_DST(%a6),%a1 # pass dst ptr in a1 lea FP_SRC(%a6),%a0 # pass src ptr in a0 mov.w (tbl_trans.w,%pc,%d1.w*2),%d1 jsr (tbl_trans.w,%pc,%d1.w*1) # emulate funimp_fsave: mov.b FPCR_ENABLE(%a6),%d0 # fetch exceptions enabled bne.w funimp_ena # some are enabled funimp_store: bfextu EXC_CMDREG(%a6){&6:&3},%d0 # fetch Dn bsr.l store_fpreg # store result to fp regfile funimp_gen_exit: fmovm.x EXC_FP0(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 funimp_gen_exit_cmp: cmpi.b SPCOND_FLG(%a6),&mia7_flg # was the ea mode (sp)+ ? beq.b funimp_gen_exit_a7 # yes cmpi.b SPCOND_FLG(%a6),&mda7_flg # was the ea mode -(sp) ? beq.b funimp_gen_exit_a7 # yes funimp_gen_exit_cont: unlk %a6 funimp_gen_exit_cont2: btst &0x7,(%sp) # is trace on? beq.l _fpsp_done # no # this catches a problem with the case where an exception will be re-inserted # into the machine. the frestore has already been executed...so, the fmov.l # alone of the control register would trigger an unwanted exception. # until I feel like fixing this, we'll sidestep the exception. fsave -(%sp) fmov.l %fpiar,0x14(%sp) # "Current PC" is in FPIAR frestore (%sp)+ mov.w &0x2024,0x6(%sp) # stk fmt = 0x2; voff = 0x24 bra.l _real_trace funimp_gen_exit_a7: btst &0x5,EXC_SR(%a6) # supervisor or user mode? bne.b funimp_gen_exit_a7_s # supervisor mov.l %a0,-(%sp) mov.l EXC_A7(%a6),%a0 mov.l %a0,%usp mov.l (%sp)+,%a0 bra.b funimp_gen_exit_cont # if the instruction was executed from supervisor mode and the addressing # mode was (a7)+, then the stack frame for the rte must be shifted "up" # "n" bytes where "n" is the size of the src operand type. # f.{b,w,l,s,d,x,p} funimp_gen_exit_a7_s: mov.l %d0,-(%sp) # save d0 mov.l EXC_A7(%a6),%d0 # load new a7' sub.l OLD_A7(%a6),%d0 # subtract old a7' mov.l 0x2+EXC_PC(%a6),(0x2+EXC_PC,%a6,%d0) # shift stack frame mov.l EXC_SR(%a6),(EXC_SR,%a6,%d0) # shift stack frame mov.w %d0,EXC_SR(%a6) # store incr number mov.l (%sp)+,%d0 # restore d0 unlk %a6 add.w (%sp),%sp # stack frame shifted bra.b funimp_gen_exit_cont2 ###################### # fmovecr.x #ccc,fpn # ###################### funimp_fmovcr: clr.l %d0 mov.b FPCR_MODE(%a6),%d0 mov.b 1+EXC_CMDREG(%a6),%d1 andi.l &0x0000007f,%d1 # pass rom offset in d1 bsr.l smovcr bra.w funimp_fsave ######################################################################### # # the user has enabled some exceptions. we figure not to see this too # often so that's why it gets lower priority. # funimp_ena: # was an exception set that was also enabled? and.b FPSR_EXCEPT(%a6),%d0 # keep only ones enabled and set bfffo %d0{&24:&8},%d0 # find highest priority exception bne.b funimp_exc # at least one was set # no exception that was enabled was set BUT if we got an exact overflow # and overflow wasn't enabled but inexact was (yech!) then this is # an inexact exception; otherwise, return to normal non-exception flow. btst &ovfl_bit,FPSR_EXCEPT(%a6) # did overflow occur? beq.w funimp_store # no; return to normal flow # the overflow w/ exact result happened but was inexact set in the FPCR? funimp_ovfl: btst &inex2_bit,FPCR_ENABLE(%a6) # is inexact enabled? beq.w funimp_store # no; return to normal flow bra.b funimp_exc_ovfl # yes # some exception happened that was actually enabled. # we'll insert this new exception into the FPU and then return. funimp_exc: subi.l &24,%d0 # fix offset to be 0-8 cmpi.b %d0,&0x6 # is exception INEX? bne.b funimp_exc_force # no # the enabled exception was inexact. so, if it occurs with an overflow # or underflow that was disabled, then we have to force an overflow or # underflow frame. the eventual overflow or underflow handler will see that # it's actually an inexact and act appropriately. this is the only easy # way to have the EXOP available for the enabled inexact handler when # a disabled overflow or underflow has also happened. btst &ovfl_bit,FPSR_EXCEPT(%a6) # did overflow occur? bne.b funimp_exc_ovfl # yes btst &unfl_bit,FPSR_EXCEPT(%a6) # did underflow occur? bne.b funimp_exc_unfl # yes # force the fsave exception status bits to signal an exception of the # appropriate type. don't forget to "skew" the source operand in case we # "unskewed" the one the hardware initially gave us. funimp_exc_force: mov.l %d0,-(%sp) # save d0 bsr.l funimp_skew # check for special case mov.l (%sp)+,%d0 # restore d0 mov.w (tbl_funimp_except.b,%pc,%d0.w*2),2+FP_SRC(%a6) bra.b funimp_gen_exit2 # exit with frestore tbl_funimp_except: short 0xe002, 0xe006, 0xe004, 0xe005 short 0xe003, 0xe002, 0xe001, 0xe001 # insert an overflow frame funimp_exc_ovfl: bsr.l funimp_skew # check for special case mov.w &0xe005,2+FP_SRC(%a6) bra.b funimp_gen_exit2 # insert an underflow frame funimp_exc_unfl: bsr.l funimp_skew # check for special case mov.w &0xe003,2+FP_SRC(%a6) # this is the general exit point for an enabled exception that will be # restored into the machine for the instruction just emulated. funimp_gen_exit2: fmovm.x EXC_FP0(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 frestore FP_SRC(%a6) # insert exceptional status bra.w funimp_gen_exit_cmp ############################################################################ # # TYPE == 1: FDB, FS, FTRAP # # These instructions were implemented on the '881/2 and '040 in hardware but # are emulated in software on the '060. # funimp_misc: bfextu %d0{&10:&3},%d1 # extract mode field cmpi.b %d1,&0x1 # is it an fdb? beq.w funimp_fdbcc # yes cmpi.b %d1,&0x7 # is it an fs? bne.w funimp_fscc # yes bfextu %d0{&13:&3},%d1 cmpi.b %d1,&0x2 # is it an fs? blt.w funimp_fscc # yes ######################### # ftrap # # ftrap.w # # # ftrap.l # # ######################### funimp_ftrapcc: bsr.l _ftrapcc # FTRAP() cmpi.b SPCOND_FLG(%a6),&fbsun_flg # is enabled bsun occurring? beq.w funimp_bsun # yes cmpi.b SPCOND_FLG(%a6),&ftrapcc_flg # should a trap occur? bne.w funimp_done # no # FP UNIMP FRAME TRAP FRAME # ***************** ***************** # ** ** ** Current PC ** # ***************** ***************** # * 0x2 * 0x02c * * 0x2 * 0x01c * # ***************** ***************** # ** Next PC ** ** Next PC ** # ***************** ***************** # * SR * * SR * # ***************** ***************** # (6 words) (6 words) # # the ftrapcc instruction should take a trap. so, here we must create a # trap stack frame from an unimplemented fp instruction stack frame and # jump to the user supplied entry point for the trap exception funimp_ftrapcc_tp: mov.l USER_FPIAR(%a6),EXC_EA(%a6) # Address = Current PC mov.w &0x201c,EXC_VOFF(%a6) # Vector Offset = 0x01c fmovm.x EXC_FP0(%a6),&0xc0 # restore fp0-fp1 fmovm.l USER_FPCR(%a6),%fpcr,%fpsr,%fpiar # restore ctrl regs movm.l EXC_DREGS(%a6),&0x0303 # restore d0-d1/a0-a1 unlk %a6 bra.l _real_trap ######################### # fdb Dn,