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