home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / PASCAL / ERR87_13 / ERROR87.PAS < prev    next >
Pascal/Delphi Source File  |  1993-01-24  |  12KB  |  406 lines

  1. {$n+,s-}
  2. unit error87;
  3.  
  4. interface
  5.  
  6. implementation
  7.  
  8. uses dos,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     : word;
  25.                case boolean of
  26.                false: (ip15_0,        { Real mode }
  27.                        ip_opcode,
  28.                        op15_0,
  29.                        op19_16 : word;
  30.                        stack    : array[0..7] of Extended);
  31.                true:  (ip,
  32.                        op : pointer);
  33.              end;
  34.  
  35.   function single_infinite(var s : Single) : Boolean;
  36.   begin
  37.     if (LongInt(s) and $7FFFFFFF) = $7F800000 then
  38.       single_infinite := True
  39.     else
  40.       single_infinite := False;
  41.   end;
  42.  
  43.   function single_nan(var s : Single) : Boolean;
  44.   var
  45.     words    : array[1..2] of Word absolute s;
  46.   begin
  47.     single_nan := False;
  48.     if ((words[2] and $7F80) = $7F80) and (not single_infinite(s)) then
  49.       single_nan := True;
  50.   end;
  51.  
  52.   function double_infinite(var d : Double) : Boolean;
  53.   var
  54.     longs    : array[1..2] of LongInt absolute d;
  55.   begin
  56.     if (longs[2] = $7FFFFFFF) and (longs[1] = 0) then
  57.       double_infinite := True
  58.     else
  59.       double_infinite := False;
  60.   end;
  61.  
  62.   function double_nan(var d : Double) : Boolean;
  63.   var
  64.     words    : array[1..4] of Word absolute d;
  65.   begin
  66.     double_nan := False;
  67.     if (words[4] and $7FF0) = $7FF0 then { not a number, but maybe INF }
  68.       if not double_infinite(d) then
  69.         double_nan := True;
  70.   end;
  71.  
  72.   function extended_infinite(var e : Extended) : Boolean;
  73.   var
  74.     words    : array[1..5] of Word absolute e;
  75.   begin
  76.     if ((words[5] and $7FFF) = $7FFF)
  77.     and (words[4] = $8000)
  78.     and (words[3] = 0)
  79.     and (words[2] = 0)
  80.     and (words[1] = 0) then
  81.       extended_infinite := True
  82.     else
  83.       extended_infinite := False;
  84.   end;
  85.  
  86.   function extended_nan(var e : Extended) : Boolean;
  87.   var
  88.     words    : array[1..5] of Word absolute e;
  89.   begin
  90.     extended_nan := False;
  91.     if ((words[5] and $7FFF) = $7FFF) and
  92.     ((words[4] and $8000) = $8000) then { not a number, but maybe INF }
  93.       if not extended_infinite(e) then
  94.         extended_nan := True;
  95.   end;
  96.  
  97.   function bcd_zero(var b)   : Boolean;
  98.   var
  99.     words    : array[1..5] of Word absolute b;
  100.   begin
  101.     bcd_zero := False;
  102.     if ((words[5] and $7FFF) = 0)
  103.     and (words[4] = 0)
  104.     and (words[3] = 0)
  105.     and (words[2] = 0)
  106.     and (words[1] = 0) then
  107.       bcd_zero := True;
  108.   end;
  109.  
  110. var
  111.   state    : state87;  { In data segment, in case there isn't much stack
  112.                          space }
  113. var
  114.   oldexitproc : Pointer;
  115. {$f+}
  116.   procedure my_exit_proc;
  117.   var
  118.     opcode   : Word;
  119.     last_inst : opcode_info;
  120.     ops_read : operand_set;
  121.     regs_read : operand_set;
  122.     op_address, ip_address : Pointer;
  123.     tos      : 0..7;
  124.     op       : operand_type;
  125.     danger   : Boolean;
  126.  
  127.     function physical(reg : operand_type) : Byte;
  128.       { Return the physical register number of a register }
  129.     begin
  130.       physical := (Ord(reg)+tos) mod 8;
  131.     end;
  132.  
  133.     function tag(reg : operand_type) : Byte;
  134.     begin
  135.       tag := (state.tags shr (2*physical(reg))) and 3;
  136.     end;
  137.  
  138.     function is_a_Nan(op : operand_type) : Boolean;
  139.     begin
  140.       is_a_Nan := False;
  141.       case op of
  142.         arReg0..arReg7 : begin
  143.                            if tag(op) <> 2 then
  144.                              Exit;
  145.                            is_a_Nan := extended_nan(state.stack[ord(op)]);
  146.                          end;
  147.         arSingle : is_a_Nan := single_nan(Single(op_address^));
  148.         arDouble : is_a_Nan := double_nan(Double(op_address^));
  149.         arExtended : is_a_Nan := extended_nan(Extended(op_address^));
  150.       end;
  151.       { others can't be NaNs }
  152.     end;
  153.  
  154.     function is_a_zero(op : operand_type) : Boolean;
  155.     begin
  156.       is_a_zero := False;
  157.       case op of
  158.         arReg0..arReg7 : begin
  159.                            if tag(op) = 1 then
  160.                              is_a_zero := True;
  161.                          end;
  162.         arSingle :
  163.           is_a_zero := (Single(op_address^) = 0.0);
  164.         arDouble :
  165.           is_a_zero := (Double(op_address^) = 0.0);
  166.         arExtended :
  167.           is_a_zero := (Extended(op_address^) = 0.0);
  168.         arWord :
  169.           is_a_zero := (Word(op_address^) = 0);
  170.         arLongint :
  171.           is_a_zero := (LongInt(op_address^) = 0);
  172.         arComp :
  173.           is_a_zero := (Comp(op_address^) = 0);
  174.         arBCD :
  175.           is_a_zero := bcd_zero(op_address^);
  176.       end;
  177.     end;
  178.  
  179.   function PtrToLong(p:pointer):longint;
  180.   begin
  181.     PtrToLong := longint(seg(p^)) shl 4 + ofs(p^);
  182.   end;
  183.  
  184.   function PtrDiff(p1,p2:pointer):longint;
  185.   begin
  186.     PtrDiff := abs(PtrToLong(p1)-PtrToLong(p2));
  187.   end;
  188.  
  189.   procedure adjust_for_prefix;
  190.   var
  191.     temp : longint;
  192.   begin
  193.     temp := PtrToLong(ip_address)-longint(prefixseg)*$10-$100;
  194.     { this is the linear address relative to the start of the program }
  195.     ip_address := ptr((temp and $FFFF0000) shl 12, temp and $FFFF);
  196.       { ip_address will have smallest possible segment number }
  197.       { User must manually work out true segment value }
  198.   end;
  199.  
  200.   procedure Find_ip;
  201.   var
  202.     i : integer;
  203.   begin
  204.     ip_address := Ptr(seg(ErrorAddr^)+PrefixSeg+$10,ofs(ErrorAddr^)-5);
  205.     { Start looking 5 bytes before ErrorAddr }
  206.     for i:=1 to 5 do
  207.     begin
  208.       if byte(ip_address^) = $CD then
  209.         exit;
  210.       ip_address := Ptr(seg(ip_address^),ofs(ip_address^)+1);
  211.     end;
  212.     ip_address := nil;
  213.   end;
  214.  
  215.   procedure rangecheck(lower,upper:extended);
  216.   var
  217.     reg : operand_type;
  218.   begin
  219.     if (last_inst.inst = iFISTP) and (tag(arReg0) = 3) then
  220.       reg := arReg7  { This doesn't really belong here, but
  221.                        a pop happens in trunc() because it temporarily
  222.                        masks exceptions. }
  223.     else
  224.       reg := arReg0;
  225.     danger :=   (state.stack[ord(reg)] < lower)
  226.              or (state.stack[ord(reg)] > upper);
  227.   end;
  228.  
  229.   begin                           {my_exit_proc}
  230.     ExitProc := oldexitproc;
  231.     if (ErrorAddr = nil) or (ExitCode <> 207) then
  232.       Exit;
  233.  
  234.     inline($cd/$39/$36/state/$9b);
  235.     if test8087 > 0 then          { Is this a real '87? }
  236.     begin
  237.       {$ifndef dpmi}
  238.       opcode := state.ip_opcode and $07FF+$d800;
  239.       op_address := Ptr(state.op19_16 and $F000, state.op15_0);
  240.       {$else}
  241.       opcode := swap(word(state.ip^));
  242.       op_address := state.op;
  243.       {$endif}
  244.  
  245.       {$ifdef ver70}
  246.       ip_address := ErrorAddr;
  247.       {$else}
  248.       ip_address := Ptr(state.ip_opcode and $F000, state.ip15_0);
  249.  
  250.       adjust_for_prefix;  { Make ip_address on same scale as ErrorAddr }
  251.  
  252.       if ptrdiff(ErrorAddr,ip_address) > 10 then
  253.         ErrorAddr := ip_address;
  254.       {$endif}
  255.     end
  256.     else
  257.     begin    { Handle the emulator }
  258.       find_ip;
  259.       if ip_address = nil then
  260.       begin
  261.         writeln('Error probably occurred in library routine.  Error87 can''t help.');
  262.         exit;
  263.       end;
  264.  
  265.       { Now ip_address points to $CD byte before instruction }
  266.       ip_address := Ptr(seg(ip_address^),ofs(ip_address^)+1);
  267.       opcode := swap(word(ip_address^)) + $a400;
  268.       op_address := Ptr(dseg, Memw[seg(ip_address^):ofs(ip_address^)+2]);
  269.                    { we don't know the segment, but we can guess }
  270.     end;
  271.  
  272.     decode_opcode(opcode, last_inst);
  273.     operands_read(last_inst, ops_read);
  274.     regs_read := ops_read*[arReg0..arReg7];
  275.  
  276.     tos := (Word(state.status) shr 11) and 7;
  277.  
  278.     { Look for bad square root }
  279.     if last_inst.inst = iFSQRT then
  280.       if state.stack[ord(arReg0)] < 0.0 then
  281.       begin
  282.         WriteLn('Taking the square root of a negative!');
  283.         Exit;
  284.       end;
  285.  
  286.     { Look for zero by zero divide }
  287.     if last_inst.inst in [iFDIV, iFDIVP, iFIDIV, iFDIVR, iFDIVRP, iFIDIVR] then
  288.     begin
  289.       danger := True;
  290.       for op := arReg0 to arExtended do
  291.         if op in ops_read then
  292.           if not is_a_zero(op) then
  293.             danger := False;
  294.       if danger then
  295.       begin
  296.         WriteLn('Zero divided by zero!');
  297.         Exit;
  298.       end;
  299.     end;
  300.  
  301.     { Look for stack overflow }
  302.  
  303.     for op := operand_type(8-num_pushes(last_inst)) to arReg7 do
  304.       if tag(op) <> 3 then
  305.       begin
  306.         WriteLn('Coprocessor stack overflow!');
  307.         Exit;
  308.       end;
  309.  
  310.     { Look for NANs }
  311.  
  312.     if ops_read <> [] then
  313.       for op := arReg0 to arExtended do
  314.         if op in ops_read then
  315.           if is_a_Nan(op) then
  316.           begin
  317.             WriteLn('Operand is not a number!');
  318.             Exit;
  319.           end;
  320.  
  321.     { Look for truncation errors.  Note that, contrary to the docs,
  322.       the stack may have been popped, so this has to come before the
  323.       underflow check }
  324.     if last_inst.inst in [iFIST,iFISTP] then
  325.     begin
  326.       { Should check rounding mode, but I'm too lazy! }
  327.       case last_inst.arg1 of
  328.       arWord:     rangecheck(-32768.5,32767.5);
  329.       arLongint:  rangecheck(-2147483648.5,2147483647.5);
  330.       arComp:     rangecheck(-9223372036854775808.5,
  331.                               9223372036854775807.5);
  332.       end;
  333.       if danger then
  334.       begin
  335.         WriteLn('Value too large to store in integer!');
  336.         Exit;
  337.       end;
  338.     end;
  339.  
  340.     { Look for stack underflow }
  341.  
  342.     if regs_read <> [] then
  343.       for op := arReg0 to arReg7 do { i is logical register number }
  344.         if op in regs_read then
  345.           if tag(op) = 3 then
  346.           begin
  347.             WriteLn('Coprocessor stack underflow!');
  348.             Exit;
  349.           end;
  350.  
  351.     WriteLn('Unrecognized floating point error!');
  352.  
  353.   end;
  354.  
  355.   function patch_system : Boolean;
  356.     { Patches system unit so that  8087 is not cleared on error }
  357.   type
  358.     one_instruction = array[1..3] of Byte;
  359.   const
  360.     before   : one_instruction = ($cd, $37, $e3); { FINIT }
  361.     after    : one_instruction = ($cd, $37, $e2); { FCLEX }
  362.     {$ifdef ver70}
  363.     patch_ofs = $31;
  364.     {$else}
  365.     patch_ofs = $32;
  366.     {$endif}
  367.  
  368.   var
  369.     int02_handler : Pointer;
  370.     int10_handler : Pointer;
  371.     patch_site : ^one_instruction;
  372.     b        : Byte;
  373.   begin
  374.     GetIntVec(2,int02_handler);
  375.     {$ifdef dpmi}
  376.     patch_site := Ptr(Seg(int02_handler^)+SelectorInc,
  377.                       Ofs(int02_handler^)+patch_ofs);
  378.     {$else}
  379.     patch_site := Ptr(Seg(int02_handler^), Ofs(int02_handler^)+patch_ofs);
  380.     {$endif}
  381.     for b := 1 to 3 do
  382.       if patch_site^[b] <> before[b] then
  383.       begin
  384.         patch_system := False;
  385.         Exit;
  386.       end;
  387.     patch_site^ := after;
  388.     patch_system := True;
  389.   end;
  390.  
  391. begin
  392.   if test8087 = 0 then
  393.   begin
  394.     writeln('Warning:  no coprocessor detected.  Error87 does not work well');
  395.     writeln('          with Borland''s emulator.');
  396.   end;
  397.   if patch_system then
  398.   begin
  399.     oldexitproc := ExitProc;
  400.     ExitProc := @my_exit_proc;
  401.   end
  402.   else
  403.     WriteLn(
  404.       'Error87 is unable to find the patch point., and is not installing itself');
  405. end.
  406.