home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / PROG / PASCAL / ERROR87.ZIP / ERROR87.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-08-15  |  9.6 KB  |  343 lines

  1. {$n+}
  2. unit error87;
  3.  
  4. interface
  5.  
  6. implementation
  7.  
  8. uses decode87;
  9.  
  10. type
  11.   controlword = set of (Invalidmask, Denormmask, Zerodivmask, Overflowmask,
  12.                         Underflowmask, Precisionmask,
  13.                         CReserved6, IntEnable, Precision0, Precision1, Round0,
  14.                         Round1, Infinity, CReserved13, CReserved14,
  15.                         CReserved15);
  16.  
  17.   statusword = set of (Invalid, Denorm, Zerodiv, Overflow, Underflow, Precision,
  18.                        SReserved6, IntRequest, C0, C1, C2, ST0, ST1, ST2, C3,
  19.                        Busy);
  20.   bitnumbers = 0..15;
  21.   state87  = record
  22.                control  : controlword;
  23.                status   : statusword;
  24.                tags,
  25.                ip15_0,
  26.                ip_opcode,
  27.                op15_0,
  28.                op19_16  : Word;
  29.                stack    : array[0..7] of Extended;
  30.              end;
  31.  
  32.   function single_infinite(var s : Single) : Boolean;
  33.   begin
  34.     if (LongInt(s) and $7FFFFFFF) = $7F800000 then
  35.       single_infinite := True
  36.     else
  37.       single_infinite := False;
  38.   end;
  39.  
  40.   function single_nan(var s : Single) : Boolean;
  41.   var
  42.     words    : array[1..2] of Word absolute s;
  43.   begin
  44.     single_nan := False;
  45.     if ((words[2] and $7F80) = $7F80) and (not single_infinite(s)) then
  46.       single_nan := True;
  47.   end;
  48.  
  49.   function double_infinite(var d : Double) : Boolean;
  50.   var
  51.     longs    : array[1..2] of LongInt absolute d;
  52.   begin
  53.     if (longs[2] = $7FFFFFFF) and (longs[1] = 0) then
  54.       double_infinite := True
  55.     else
  56.       double_infinite := False;
  57.   end;
  58.  
  59.   function double_nan(var d : Double) : Boolean;
  60.   var
  61.     words    : array[1..4] of Word absolute d;
  62.   begin
  63.     double_nan := False;
  64.     if (words[4] and $7FF0) = $7FF0 then { not a number, but maybe INF }
  65.       if not double_infinite(d) then
  66.         double_nan := True;
  67.   end;
  68.  
  69.   function extended_infinite(var e : Extended) : Boolean;
  70.   var
  71.     words    : array[1..5] of Word absolute e;
  72.   begin
  73.     if ((words[5] and $7FFF) = $7FFF)
  74.     and (words[4] = $8000)
  75.     and (words[3] = 0)
  76.     and (words[2] = 0)
  77.     and (words[1] = 0) then
  78.       extended_infinite := True
  79.     else
  80.       extended_infinite := False;
  81.   end;
  82.  
  83.   function extended_nan(var e : Extended) : Boolean;
  84.   var
  85.     words    : array[1..5] of Word absolute e;
  86.   begin
  87.     extended_nan := False;
  88.     if ((words[5] and $7FFF) = $7FFF) and
  89.     ((words[4] and $8000) = $8000) then { not a number, but maybe INF }
  90.       if not extended_infinite(e) then
  91.         extended_nan := True;
  92.   end;
  93.  
  94.   function bcd_zero(var b)   : Boolean;
  95.   var
  96.     words    : array[1..5] of Word absolute b;
  97.   begin
  98.     bcd_zero := False;
  99.     if ((words[5] and $7FFF) = 0)
  100.     and (words[4] = 0)
  101.     and (words[3] = 0)
  102.     and (words[2] = 0)
  103.     and (words[1] = 0) then
  104.       bcd_zero := True;
  105.   end;
  106.  
  107. var
  108.   state    : state87;  { In data segment, in case there isn't much stack
  109.                          space }
  110. var
  111.   oldexitproc : Pointer;
  112. {$f+}
  113.   procedure my_exit_proc;
  114.   var
  115.     opcode   : Word;
  116.     last_inst : opcode_info;
  117.     ops_read : operand_set;
  118.     regs_read : operand_set;
  119.     op_address, ip_address : Pointer;
  120.     tos      : 0..7;
  121.     op       : operand_type;
  122.     danger   : Boolean;
  123.  
  124.     function physical(reg : operand_type) : Byte;
  125.       { Return the physical register number of a register }
  126.     begin
  127.       physical := (Ord(reg)+tos) mod 8;
  128.     end;
  129.  
  130.     function tag(reg : operand_type) : Byte;
  131.     begin
  132.       tag := (state.tags shr (2*physical(reg))) and 3;
  133.     end;
  134.  
  135.     function is_a_Nan(op : operand_type) : Boolean;
  136.     begin
  137.       is_a_Nan := False;
  138.       case op of
  139.         arReg0..arReg7 : begin
  140.                            if tag(op) <> 2 then
  141.                              Exit;
  142.                            is_a_Nan := extended_nan(state.stack[ord(op)]);
  143.                          end;
  144.         arSingle : is_a_Nan := single_nan(Single(op_address^));
  145.         arDouble : is_a_Nan := double_nan(Double(op_address^));
  146.         arExtended : is_a_Nan := extended_nan(Extended(op_address^));
  147.       end;
  148.       { others can't be NaNs }
  149.     end;
  150.  
  151.     function is_a_zero(op : operand_type) : Boolean;
  152.     begin
  153.       is_a_zero := False;
  154.       case op of
  155.         arReg0..arReg7 : begin
  156.                            if tag(op) = 1 then
  157.                              is_a_zero := True;
  158.                          end;
  159.         arSingle :
  160.           is_a_zero := (Single(op_address^) = 0.0);
  161.         arDouble :
  162.           is_a_zero := (Double(op_address^) = 0.0);
  163.         arExtended :
  164.           is_a_zero := (Extended(op_address^) = 0.0);
  165.         arWord :
  166.           is_a_zero := (Word(op_address^) = 0);
  167.         arLongint :
  168.           is_a_zero := (LongInt(op_address^) = 0);
  169.         arComp :
  170.           is_a_zero := (Comp(op_address^) = 0);
  171.         arBCD :
  172.           is_a_zero := bcd_zero(op_address^);
  173.       end;
  174.     end;
  175.  
  176.   function PtrToLong(p:pointer):longint;
  177.   begin
  178.     PtrToLong := longint(seg(p^)) shl 4 + ofs(p^);
  179.   end;
  180.  
  181.   function PtrDiff(p1,p2:pointer):longint;
  182.   begin
  183.     PtrDiff := abs(PtrToLong(p1)-PtrToLong(p2));
  184.   end;
  185.  
  186.   procedure adjust_for_prefix;
  187.   var
  188.     temp : longint;
  189.   begin
  190.     temp := PtrToLong(ip_address)-longint(prefixseg)*$10-$100;
  191.     { this is the linear address relative to the start of the program }
  192.     ip_address := ptr((temp and $FFFF0000) shl 12, temp and $FFFF);
  193.       { ip_address will have smallest possible segment number }
  194.       { User must manually work out true segment value }
  195.   end;
  196.  
  197.   procedure rangecheck(lower,upper:extended);
  198.   var
  199.     reg : operand_type;
  200.   begin
  201.     if (last_inst.inst = iFISTP) and (tag(arReg0) = 3) then
  202.       reg := arReg7  { This doesn't really belong here, but
  203.                        a pop happens in trunc() because it temporarily
  204.                        masks exceptions. }
  205.     else
  206.       reg := arReg0;
  207.     danger :=   (state.stack[ord(reg)] < lower)
  208.              or (state.stack[ord(reg)] > upper);
  209.   end;
  210.  
  211.   begin                           {my_exit_proc}
  212.     ExitProc := oldexitproc;
  213.     if (ErrorAddr = nil) or (ExitCode <> 207) then
  214.       Exit;
  215.  
  216.     inline($cd/$39/$36/state/$9b);
  217.     opcode := state.ip_opcode and $07FF+$d800;
  218.     decode_opcode(opcode, last_inst);
  219.     operands_read(last_inst, ops_read);
  220.     regs_read := ops_read*[arReg0..arReg7];
  221.  
  222.     op_address := Ptr(state.op19_16 and $F000, state.op15_0);
  223.     ip_address := Ptr(state.ip_opcode and $F000, state.ip15_0);
  224.  
  225.     adjust_for_prefix;  { Make ip_address on same scale as ErrorAddr }
  226.  
  227.     if ptrdiff(ErrorAddr,ip_address) > 10 then
  228.       ErrorAddr := ip_address;
  229.  
  230.     tos := (Word(state.status) shr 11) and 7;
  231.  
  232.     { Look for bad square root }
  233.     if last_inst.inst = iFSQRT then
  234.       if state.stack[ord(arReg0)] < 0.0 then
  235.       begin
  236.         WriteLn('Taking the square root of a negative!');
  237.         Exit;
  238.       end;
  239.  
  240.     { Look for zero by zero divide }
  241.     if last_inst.inst in [iFDIV, iFDIVP, iFIDIV, iFDIVR, iFDIVRP, iFIDIVR] then
  242.     begin
  243.       danger := True;
  244.       for op := arReg0 to arExtended do
  245.         if op in ops_read then
  246.           if not is_a_zero(op) then
  247.             danger := False;
  248.       if danger then
  249.       begin
  250.         WriteLn('Zero divided by zero!');
  251.         Exit;
  252.       end;
  253.     end;
  254.  
  255.     { Look for stack overflow }
  256.  
  257.     for op := operand_type(8-num_pushes(last_inst)) to arReg7 do
  258.       if tag(op) <> 3 then
  259.       begin
  260.         WriteLn('Coprocessor stack overflow!');
  261.         Exit;
  262.       end;
  263.  
  264.     { Look for NANs }
  265.  
  266.     if ops_read <> [] then
  267.       for op := arReg0 to arExtended do
  268.         if op in ops_read then
  269.           if is_a_Nan(op) then
  270.           begin
  271.             WriteLn('Operand is not a number!');
  272.             Exit;
  273.           end;
  274.  
  275.     { Look for truncation errors.  Note that, contrary to the docs,
  276.       the stack may have been popped, so this has to come before the
  277.       underflow check }
  278.     if last_inst.inst in [iFIST,iFISTP] then
  279.     begin
  280.       { Should check rounding mode, but I'm too lazy! }
  281.       case last_inst.arg1 of
  282.       arWord:     rangecheck(-32768.5,32767.5);
  283.       arLongint:  rangecheck(-2147483648.5,2147483647.5);
  284.       arComp:     rangecheck(-9223372036854775808.5,
  285.                               9223372036854775807.5);
  286.       end;
  287.       if danger then
  288.       begin
  289.         WriteLn('Value too large to store in integer!');
  290.         Exit;
  291.       end;
  292.     end;
  293.  
  294.     { Look for stack underflow }
  295.  
  296.     if regs_read <> [] then
  297.       for op := arReg0 to arReg7 do { i is logical register number }
  298.         if op in regs_read then
  299.           if tag(op) = 3 then
  300.           begin
  301.             WriteLn('Coprocessor stack underflow!');
  302.             Exit;
  303.           end;
  304.  
  305.     WriteLn('Unrecognized floating point error!');
  306.  
  307.   end;
  308.  
  309.   function patch_system : Boolean;
  310.     { Patches system unit so that  8087 is not cleared on error }
  311.   type
  312.     one_instruction = array[1..3] of Byte;
  313.   const
  314.     before   : one_instruction = ($cd, $37, $e3); { FINIT }
  315.     after    : one_instruction = ($cd, $37, $e2); { FCLEX }
  316.  
  317.   var
  318.     int02_handler : Pointer absolute 0 : 8;
  319.     patch_site : ^one_instruction;
  320.     b        : Byte;
  321.   begin
  322.     patch_site := Ptr(Seg(int02_handler^), Ofs(int02_handler^)+$32);
  323.     for b := 1 to 3 do
  324.       if patch_site^[b] <> before[b] then
  325.       begin
  326.         patch_system := False;
  327.         Exit;
  328.       end;
  329.     patch_site^ := after;
  330.     patch_system := True;
  331.   end;
  332.  
  333. begin
  334.   if patch_system then
  335.   begin
  336.     oldexitproc := ExitProc;
  337.     ExitProc := @my_exit_proc;
  338.   end
  339.   else
  340.     WriteLn(
  341.       'Error87 is unable to find the patch point., and is not installing itself');
  342. end.
  343.