;***************************************************************************** ;* ;* Open Watcom Project ;* ;* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved. ;* ;* ======================================================================== ;* ;* This file contains Original Code and/or Modifications of Original ;* Code as defined in and that are subject to the Sybase Open Watcom ;* Public License version 1.0 (the 'License'). You may not use this file ;* except in compliance with the License. BY USING THIS FILE YOU AGREE TO ;* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is ;* provided with the Original Code and Modifications, and is also ;* available at www.sybase.com/developer/opensource. ;* ;* The Original Code and all software distributed under the License are ;* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER ;* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM ;* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF ;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR ;* NON-INFRINGEMENT. Please see the License for the specific language ;* governing rights and limitations under the License. ;* ;* ======================================================================== ;* ;* Description: 80x87 interrupt handler. ;* ;***************************************************************************** ;;; //e:\watcom\src\bld\watcom\h;E:\WATCOM\H;E:\WATCOM\H\NT .8087 .386p include struct.inc include mdef.inc include stword.inc include env387.inc include fstatus.inc xref __8087 ; indicate that NDP instructions are present modstart fpeinth datasegment extrn __FPE_exception_: proc extrn "C",_STACKLOW : dword TInf db 00h,00h,00h,00h,00h,00h,00h,80h,0ffh,7fh F8Inf db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0efh,7fh F4Inf db 0ffh,0ffh,7fh,7fh enddata ; User handler for 80x87 exceptions. xdefp __FPEHandler_ defp __FPEHandler_ public __FPE2Handler_ __FPE2Handler_ label byte push EAX ; save regs push EBX ; ... push ECX ; ... push EDX ; ... push ESI ; ... push EDI ; ... push EBP ; ... sub ESP,ENV_SIZE ; make room for environment information mov EBP,ESP ; point to buffer for 80x87 environment ; Now EXC_NUM is located in [EBP+ENV_SIZE+32] ; but it isn't necessary to testing EXC_NUM, ; because only #MF is unmasked now fnstenv [EBP] ; get 80x87 environment fwait ; wait for 80x87 mov EDX,ENV_CW[EBP] ; get control word not EDX ; flip the mask bits mov DH,0FFh ; turn on top byte and EDX,ENV_SW[EBP] ; get status word mov EDI,ENV_IP[EBP] ; ... opcode: mov BX,[EDI] ; get opcode inc EDI ; point to next opcode cmp BL,0d8h ; check if its the opcode jb opcode ; ... cmp BL,0dfh ; ... ja opcode ; ... mov EDI,ENV_OP[EBP] ; ... xchg BL,BH ; get opcode in right position mov CL,FPE_OK ; assume exception to be ignored _guess ; guess precision exception test DL,ST_EF_PR ; - check for precision exception _quif e ; - quit if not precision exception mov CL,FPE_INEXACT ; - indicate precision exception _admit ; guess stack under/overflow test DL,ST_EF_SF ; - check for stack under/overflow _quif e ; - quit if not stack under/overflow test DX,ST_C1 ; - check if underflow _if e ; - if underflow mov CL,FPE_STACKUNDERFLOW ; - - indicate stack underflow _else ; - else mov CL,FPE_STACKOVERFLOW ; - - indicate stack overflow _endif ; - endif _admit ; guess invalid operation test DL,ST_EF_IO ; - check for invalid operation _quif e ; - quit if not invalid operation call InvalidOp ; - process invalid operation _admit ; guess denormal operand test DL,ST_EF_DO ; - check for denormal operand _quif e ; - quit if not denormal operand mov CL,FPE_DENORMAL ; - indicate underflow _admit ; guess overflow test DL,ST_EF_OF ; - check for overflow _quif e ; - quit if not overflow call KOOverFlow ; - process overflow exception mov CL,FPE_OVERFLOW ; - set floating point error code _admit ; guess underflow test DL,ST_EF_UF ; - check for underflow _quif e ; - quit if not underflow mov CL,FPE_UNDERFLOW ; - indicate underflow _admit ; guess divide by 0 test DL,ST_EF_ZD ; - check for divide by zero _quif e ; - quit if not divide by zero call GetInf ; - process divide by zero mov CL,FPE_ZERODIVIDE ; - indicate divide by zero _endguess ; endguess ; More correctly to rise this mask bit - is on end of __FPE_exception_ ; but it may not returned at all... push ECX mov EAX, 68 mov EBX, 25 ; mov ECX, 16 ; #MF mov EDX, 1 ; rise activity int 40h ; change state of signal activity pop ECX _guess ; guess exception to be handled cmp CL,FPE_OK ; - check if exception allowed _quif e ; - quit if exception not allowed movzx EAX,CL ; - set floating point status call __FPE_exception_ ; - call user's handler _endguess ; endguess fclex ; clear exceptions that may have ; occurred as a result of handling the ; exception and word ptr ENV_CW[EBP],0FF72h fldcw word ptr ENV_CW[EBP] ; enable interrupts fwait ; ... add ESP,ENV_SIZE ; clean up stack pop EBP ; ... pop EDI ; ... pop ESI ; ... pop EDX ; ... pop ECX ; ... pop EBX ; ... pop EAX ; ... ret 4 ; return from interrupt handler ; with removing EXC_NUM endproc __FPEHandler_ ; Process invalid operation. InvalidOp proc near mov CL,FPE_INVALID ; assume invalid operation _guess ; guess it's square root cmp BX,0D9FAh ; - ... _quif ne ; - quit if it's not that instruction mov CL,FPE_SQRTNEG ; - indicate sqrt(negative number) ret ; - return _endguess ; endguess _guess ; guess it's square root cmp BX,0D9F1h ; - ... _quif ne ; - quit if it's not that instruction mov CL,FPE_LOGERR ; - indicate sqrt(negative number) ret ; - return _endguess ; endguess _guess ; guess: 'fprem' instruction cmp BX,0D9F8h ; - check for 'fprem' 10-may-90 _if ne ; - if not 'fprem' cmp BX,0D9F5h ; - - check for 'fprem1' _endif ; - endif _quif ne ; - quit if not 'fprem' or 'fprem1' mov CL,FPE_MODERR ; - indicate mod(negative number) _admit ; guess: integer overflow mov DX,BX ; - save op code and DX,0310h ; - check for fist/fistp instruction cmp DX,0310h ; - ... _quif ne ; - quit if its not that instruction mov CL,FPE_IOVERFLOW ; - indicate integer overflow _admit ; guess it's floating point underflow ;; mov DX,BX ; - save op code and DX,0110h ; - check if fst or fstp instruction cmp DX,0110h ; - ... _quif ne ; - quit if it's not that instruction ; Destination is short or long real and source register is an unnormal ; with exponent in range. fstp st(0) ; - pop old result fldz ; - load zero mov DL,BL ; - save op code and DL,0C0h ; - check the MOD bits of instruction cmp DL,0C0h ; - ... _if ne ; - if result to be placed in memory call Store ; - - store result in memory _endif ; - endif test BL,08h ; - check if result to be popped _if ne ; - if result to be popped fstp st(0) ; - - pop the result _endif ; - endif mov CL,FPE_UNDERFLOW ; - indicate underflow _admit ; guess it's divide mov DX,BX ; - save op code and DX,0130h ; - check for fdiv/fidiv instruction cmp DX,0030h ; - ... _quif ne ; - quit if it's not that instruction mov DX,ENV_TW[EBP] ; - get tag word mov CL,AH ; - get stack pointer and CL,38h ; - ... shr CL,2 ; - ... ror DX,CL ; - make stack top low order bits and DL,05h ; - check if top two elements are 0 cmp DL,05h ; - ... _quif ne ; - quif if they are not 0 mov CL,FPE_ZERODIVIDE ; - indicate divide by zero _endguess ; endguess ret endproc InvalidOp ; Process overflow exception (note that only floating point overflows ; are handled - integer overflows are invalid operations). KOOverFlow proc near _guess ; guess: fscale instruction 10-may-90 cmp BX,0D9FDh ; - quit if not 'fscale' instruction _quif ne ; - ... _admit ; guess: fst/fstp instruction mov DX,BX ; - save op code and DX,0110h ; - check if fst or fstp instruction cmp DX,0110h ; - ... _quif ne ; - quit if not an fst/fstp instr. call GetInf ; - load infinity mov DL,BL ; - save op code and DL,0C0h ; - check the MOD bits of instruction cmp DL,0C0h ; - ... _if ne ; - if result to be placed in memory call Store ; - - store infinity _endif ; - endif test BL,08h ; - check if result to be popped _if ne ; - if result to be popped fstp st(0) ; - - pop result _endif ; - endif _admit ; admit arithmetic operation mov DL,BL ; - save op code and DL,0C0h ; - check if both operands on stack cmp DL,0C0h ; - ... _quif ne ; - quif both operands not on stack ; ; This code handles overflow on the following intructions: ; fxxx ST,ST(i) ; fxxx ST(i),ST where xxx is one of mul,div,sub or add ; fxxxp ST(i),ST ; lea ESI,TInf ; - load internal infinity call Load ; - ... _admit ; admit ; ; This admit block is to handle overflow on the following intructions: ; fxxx short real ; fxxx long real where xxx is one of mul,div,sub or add ; call GetInf ; - load infinity _endguess ; endguess ret ; return endproc KOOverFlow ; Replace the top element of the stack with the appropriate signed ; infinity. GetInf proc near ftst ; get sign of result fstsw word ptr ENV_OP[EBP] fstp st(0) ; pop argument off stack (does fwait) test BH,04h ; check if single or double _if ne ; if double fld qword ptr F8Inf ; - load double precision infinity _else ; else fld dword ptr F4Inf ; - load single precision infinity _endif ; endif test word ptr ENV_OP[EBP],ST_C0 _if ne ; if argument is negative fchs ; - return negative infinity _endif ; endif ret ; return endproc GetInf ; Replace an element on the stack with internal zero or infinity. Load proc near test BH,04h ; check if result is top element _if e ; if result is not top element mov DL,0 ; - indicate we are at the top _else ; else mov DL,BL ; - get st(i) and DL,07h ; - . . . _endif ; endif push EDX ; save st(i) _loop ; loop dec DL ; - decrement counter _quif l ; - quit if we are at st(i) fincstp ; - increment stack pointer _endloop ; endloop fstp st(0) ; free the stack element fld tbyte ptr [ESI] ; load internal zero pop EDX ; get st(i) _loop ; loop dec DL ; - decrement counter _quif l ; - quit if we are at st(i) fdecstp ; - decrement stack pointer _endloop ; endloop ret ; return endproc Load ; Store the top element of the stack at ES:EDI. Store proc near test BH,04h _if ne ; if double fst qword ptr [EDI] ; - store as double precision result _else ; else fst dword ptr [EDI] ; - store as single precision result _endif ; endif ret ; return endproc Store endmod end