| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 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 <ea> |
| |
| 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)+ <ea> |
| set mda7_flg, 0x08 # flag bit: -(a7) <ea> |
| set fmovm_flg, 0x40 # flag bit: fmovm instruction |
| set immed_flg, 0x80 # flag bit: &<data> <ea> |
| |
| 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 f<op>s! |
| |
| 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 f<op>s! |
| |
| 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 f<op>s! |
| |
| unlk %a6 |
| |
| bra.l _real_unfl |
| |
| # underflow 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 f<op>s! |
| |
| 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 <ea> 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 <ea> 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 <ea> 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 <ea>" 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 <ea>" 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<op>.{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 #<data> |
| 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 #<data> |
| 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 <ea> 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 <ea> 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 <ea> mode a data reg? |
| ble.b foperr_out_b_save_dn # yes |
| mov.l EXC_EA(%a6),%a0 # pass: <ea> 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 <ea> mode a data reg? |
| ble.b foperr_out_w_save_dn # yes |
| mov.l EXC_EA(%a6),%a0 # pass: <ea> 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 <ea> mode a data reg? |
| ble.b foperr_out_l_save_dn # yes |
| mov.l EXC_EA(%a6),%a0 # pass: <ea> 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 <ea> is -() or ()+; also get <ea> # |
| # # |
| # 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 <ea> # |
| # 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 <ea> 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 <ea> mode a data reg? |
| ble.b fsnan_out_b_dn # yes |
| mov.l EXC_EA(%a6),%a0 # pass: <ea> 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 <ea> mode a data reg? |
| ble.b fsnan_out_w_dn # yes |
| mov.l EXC_EA(%a6),%a0 # pass: <ea> 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 <ea> mode a data reg? |
| ble.b fsnan_out_l_dn # yes |
| mov.l EXC_EA(%a6),%a0 # pass: <ea> 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 <ea> 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: <ea> 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 <ea> 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 <ea> |
| |
| 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 <ea> |
| |
| mov.l EXC_A6(%a6),(%a6) |
| |
| cmpi.b SPCOND_FLG(%a6),&mda7_flg # is <ea> 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( |