blob: 9bbffebe3eb504833ed0937670bd4168751d61a4 [file] [log] [blame]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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