Contributor: GERD KORTEMEYER          

{
GERD KORTEMEYER

here are two Units For trapping float-exceptions. In your Program you
will have to add

  Uses err387

and at the beginning of your main Program say For example

begin
   exception(overflow, masked);
   exception(underflow, dumpask);
   exception(invalid, dumpexit);
   autocorrect(zerodiv, 1.0);
   exception(precision, masked);

In this way you can choose For any kind of exception in which way it is
to be handeled. After the lines above the result of a division by zero
will be '1.0', in Case of an underflow there will be a dump of the copro
and the user will be asked For the result he wants the operation to have,
in Case of an overflow the largest available number will be chosen and
so on ...

Here are the Units

    err387 and dis387
}

{ ---------------------------------------------------------- }
{ Fehlerbehandlungsroutinen fuer den Intel 80387 bzw. 486 DX }
{ Geschrieben in Turbo Pascal 6.0                            }
{ von Gerd Kortemeyer, Hannover                              }
{ ---------------------------------------------------------- }

Unit err387;

Interface

Uses
  dis387, Dos, Crt;

Const
  invalid   = 1;
  denormal  = 2;
  zero_div  = 4;
  overflow  = 8;
  underflow = 16;
  precision = 32;
  stackfault= 64;
  con1      = 512;

  masked    = 0;
  runtime   = 1;
  dump      = 2;
  dumpexit  = 3;
  dumpask   = 4;
  autocorr  = 5;


Procedure exception(which, what : Word);
Procedure autocorrect(which : Word; by : Extended);

Procedure handle_off;
Procedure handle_on;

Procedure restore_masks;

Procedure clear_copro;
Function  status_Word : Word;

Var
  do_again : Word;

Implementation

Const
  valid = 0;
  zero  = 1;
  spec  = 2;
  empty = 3;

  topmask : Word = 14336;
  topdiv  = 2048;

  anyerrors : Word = 63;

  zweipot : Array [0..15] of Word =
    (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
     2048, 4096, 8192, 16384, 32768);

  ex_nam : Array[0..5] of String=
    ('Invalid   ',
     'Denormal  ',
     'Zero-Div  ',
     'Overflow  ',
     'Underflow ',
     'Precision ');

Var
  setmasks : Byte;
  normal   : Record
    Case Boolean OF
      True : (adr : Pointer);
      False: (pro : Procedure);
    end;

  Exit_on,
  dump_on,
  ask_on,
  auto_on,
  standard : Word;

  auto_val : Array [0..5] of Extended;

Procedure Mask(which : Word);
Var
  cw : Word;
begin
  Asm
    fstcw cw
  end;
  cw := cw or which;
  setmasks := Lo(cw);
  Asm
    fldcw cw
  end;
end;

Procedure Unmask(which : Word);
Var
  cw : Word;
begin
  Asm
    fclex
    fstcw cw
  end;
  cw := cw and not (which);
  setmasks := Lo(cw);
  Asm
    fldcw cw
  end;
end;

Procedure restore_masks;
Var
  setm : Word;
  i    :Integer;
begin
  setm:=setmasks;
  For i := 0 to 5 do
    if (setm and zweipot[i]) <> 0 then
      Mask  (zweipot[i])
    else
      Unmask(zweipot[i]);
end;

Procedure clear_copro;
Var
  cw : Word;
begin
  Asm
    fstcw cw
  end;
  setmasks := Lo(cw);
  Asm
    finit
  end;
end;

Function status_Word;
begin
  Asm
    fstsw @result
  end;
end;

{ Bei welcher Exception soll was passieren? }
Procedure exception;
begin
  Case what OF

    masked  : Mask(which);

    runtime :
      begin
        Unmask(which);
        standard := standard or which;
      end;

    dump :
      begin
        Unmask(which);
        standard := standard and NOT(which);
        dump_on  := dump_on  or  which;
        Exit_on  := Exit_on  and NOT(which);
        ask_on   := ask_on   and NOT(which);
        auto_on  := auto_on  and NOT(which);
      end;

    dumpexit :
      begin
        Unmask(which);
        standard := standard and NOT(which);
        dump_on  := dump_on  or  which;
        Exit_on  := Exit_on  or  which;
        ask_on   := ask_on   and NOT(which);
        auto_on  := auto_on  and NOT(which);
      end;

    dumpask :
      begin
        Unmask(which);
        standard := standard and NOT(which);
        dump_on  := dump_on  or  which;
        Exit_on  := Exit_on  and NOT(which);
        ask_on   := ask_on   or  which;
        auto_on  := auto_on  and NOT(which);
      end;
   end;
end;

{ zum Setzen von Auto-Korrekt-Werten }

Procedure autocorrect;
Var
  i : Integer;
begin
   Unmask(which);
   standard := standard and NOT(which);
   dump_on  := dump_on  and NOT(which);
   Exit_on  := Exit_on  and NOT(which);
   ask_on   := ask_on   and NOT(which);
   auto_on  := auto_on  or  which;
   For i := 0 to 5 do
     if (which and zweipot[i]) <> 0 then
       auto_val[i] := by;
end;

{ ------------- Die Interrupt-Routine selbst ------------- }

Procedure errorcon; Interrupt;
Var
  copro : Record
    control_Word,
    status_Word,
    tag_Word, op,
    instruction_Pointer,
    ip, operand_Pointer, : Word;
    st                   : Array [0..7] of Extended;
  end;

  top : Integer; { welches Register ist Stacktop? }

  masked,            { welche Exceptions maskiert? }
  occured : Byte;    { welche Exceptions aufgetreten? }

  opcode  : Word;

  inst_seg,       { Instruction-Pointer, Segment }
  inst_off,       { "                  , Offset  }
  oper_seg,       { Operand-Pointer    , Segment }
  oper_off: Word; { "                  , Offset  }

  inst_point : ^Word;                 { zum Adressieren des Opcodes }

  oper_point : Record
    Case Integer of { zum Adressieren des Operanden }
      1 : (ex : ^Extended);
      2 : (db : ^Double);
      3 : (si : ^Single);
      4 : (co : ^Comp);
    end;

  marker: Array [0..7] of Word; { Register-Marker nach Tag-Word }

  opt_dump,               { soll ausgeben werden? }
  opt_exit,               { soll aufgehoert werden? }
  opt_ask,                { soll Ergebnis abgefragt werden? }
  opt_auto  : Boolean;    { soll Ergebnis automatisch korrigiert werden? }

  i         : Integer;

  mem_access: Boolean;    { gibt es Speicherzugriff? }

  op_name   : String;     { Mnemonik des Befehls }

{ Ersetze Stacktop durch abgefragten Wert }
Procedure ask_correct;
Var
  res  : Extended;
  ch   : Char;
  t    : String;
  code : Integer;
begin
   Asm
     fstp res
   end;
   WriteLN;
   Write('The result would be ', res, '. Change? (y/n) ' );
   Repeat
     Repeat Until KeyPressed;
     ch := ReadKey;;
   Until ch in ['Y','y','N','n'];
   Writeln;
   if ch in ['Y','y'] then
   Repeat
     Write('New value : ');
     READLN(t);
     VAL(t, res, code);
   Until code = 0;
   Asm
     fld res
   end;
end;

Function hex(w : Word) : String; { Ausgabe als HeX-Zahl }
Const
  zif : Array [0..15] of Char = ('0','1','2','3','4','5','6','7','8','9',
                                    'a','b','c','d','e','f');
begin
  hex := zif[w div zweipot[12]] +
         zif[(w MOD zweipot[12]) div zweipot[8]] +
         zif[(w MOD zweipot[8]) div zweipot[4]] +
         zif[w MOD zweipot[4]];
end;

Procedure choice;
Var
  ch : Char;
begin
  WriteLN;
  Write('C)ontinue, A)bort ');
  Repeat
    Repeat Until KeyPressed;
    ch:=ReadKey;;
    if ch in ['A','a'] then
      Halt(0);
  Until ch in ['C','c'];
  WriteLN;
end;

Procedure showcopro; { Ausgeben des FSAVE - Records }
Var
  i : Integer;
begin
  TextMode(LastMode);
  HighVideo;
  WriteLN('Floating point exception, last opcode: ',hex(opcode),
                                               ' (',op_name,')');
  NormVideo;
  WriteLN('Instruction Pointer : ',hex(inst_seg),':',hex(inst_off),
          ' (',hex(inst_point^),')');
  if mem_access then
  begin
    WriteLN('Operand Pointer     : ',hex(oper_seg),':',hex(oper_off));
    WriteLN('( Extended: ',oper_point.ex^,', Double: ',oper_point.db^);
    WriteLN('  Single  : ',oper_point.si^,', Comp  : ',oper_point.co^,' )');
  end
  else
  begin
    WriteLN;
    WriteLN ('No memory access');
    WriteLN;
  end;
  HighVideo;
  if (occured and stackfault) = 0 then
  begin
    WriteLN('Exception ','Masked':8,'Occured':8,'Should be masked':18);
    NormVideo;
    For i:=0 to 5 do
      WriteLN(ex_nam[i], (masked   and zweipot[i]) <> 0 : 8,
                         (occured  and zweipot[i]) <> 0 : 8,
                         (setmasks and zweipot[i]) <> 0 : 18);
    HighVideo;
  end
  else
  begin
    WriteLN('Invalid Operation:');
    if (copro.status_Word and con1) <> 0 then
      WriteLN('                       -- Stack Overflow --')
    else
      WriteLN('                       -- Stack Underflow --');
    WriteLN;
  end;

  WriteLN('Reg  ','Value':29,'Marked':10);
  Normvideo;
  For i := 0 to 7 do
  begin
    Write('st(',i,')', copro.st[i] : 29);
    Case marker[i] OF
       valid : WriteLN('Valid'   : 10);
       spec  : WriteLN('Special' : 10);
       empty : WriteLN('Empty'   : 10);
       zero  : WriteLN('Zero'    : 10);
    end;
  end;
end;

{ Ersetze Stacktop durch Auto-Korrekt-Wert }

Procedure auto_corr;
Var
  res : Extended;
  i   : Integer;
begin
  Asm
    fstp res
  end;
  For i := 0 to 5 do
    if ((occured and zweipot[i]) <> 0) and
       ((auto_on and zweipot[i]) <> 0) then
      res := auto_val[i];
  Asm
    fld res
  end;
end;


Procedure do_it_again;
Type
  codearr = Array[0..4] of Byte;
Var
  sam : Record
    Case Boolean OF
      True : (b: ^codearr );
      False: (p: Procedure);
    end;

  op_point : Pointer;
  x        : extended;
begin
  New(sam.b);
  sam.b^[0]:=Hi(opcode);
  sam.b^[1]:=Lo(opcode);
  if mem_access then
  begin
  { --- mod r/m auf ds:[di] stellen (00ttt101) --- }
    sam.b^[1] := sam.b^[1] and not (zweipot[7] + zweipot[6] + zweipot[1]);
    sam.b^[1] := sam.b^[1] or (zweipot[2] + zweipot[0]);
  end;
  sam.b^[2] := $ca; { retf 0000 }
  sam.b^[3] := $00;
  sam.b^[4] := $00;
  op_point  := oper_point.ex;
  Asm
    push ds
    lds di, op_point
  end;

  sam.p;

  Asm
    pop ds
  end;
  Dispose(sam.b);
end;

begin
  Asm
    push   ax
    xor    al,al
    out    0f0h,al
    mov    al,020h
    out    0a0h,al
    out    020h,al
    pop    ax
    fsave  copro
  end;

  { === Pruefen, ob Bearbeitung durch ERRORCON erwuenscht === }
  if (copro.status_Word and standard) <> 0 then
  begin
    Asm
      frstor copro
    end;
    normal.pro; { Bye, bye ... }
  end;
  { === Auswerten des FSAVE-Records ========================= }
  { --- Opcode wie im Copro gespeichert     --- }
  opcode := zweipot[15] + zweipot[14] + zweipot[12] + zweipot[11] +
            (copro.ip MOD zweipot[11]);
  op_name := dis(opcode);
  mem_access := op_name='...';
  { --- Was war maskiert, was ist passiert? --- }
  masked  := Lo(copro.control_Word);
  occured := Lo(copro.status_Word );
  { --- Der Instruction-Pointer             --- }
  inst_seg := copro.ip and (zweipot[15] + zweipot[14] + zweipot[13] +
                           zweipot[12]);
  inst_off := copro.instruction_Pointer;
  inst_point := Ptr(inst_seg,inst_off);
  { --- Der Operand-Pointer                 --- }
  oper_seg := copro.op and (zweipot[15] + zweipot[14] + zweipot[13] +
                            zweipot[12]);
  oper_off := copro.operand_Pointer;
  oper_point.ex := Ptr(oper_seg,oper_off);
  { --- Wer ist gerade Stacktop? --- }
  top := (copro.status_Word and topmask) div topdiv;
  { --- Einlesen der Marker aus Tag-Word --- }
  For i := 0 to 7 do
  begin
    marker[(8 + i - top) MOD 8] := (copro.tag_Word and (zweipot[i * 2] +
                                    zweipot[i * 2 + 1])) div zweipot[i * 2];
  end;

  { --- Welche Aktionen sollen ausgefuehrt werden? --- }
  opt_dump := (copro.status_Word and dump_on) <> 0;
  opt_exit := (copro.status_Word and Exit_on) <> 0;
  opt_ask  := (copro.status_Word and ask_on ) <> 0;
  opt_auto := (copro.status_Word and auto_on) <> 0;

  { === Aktionen ============================================ }
  if opt_dump then
    showcopro;
  if opt_exit then
  begin
    WriteLN;
    WriteLN('Exit Program due to Programmers request');
    HALT; { Bye, bye ... }
  end;
  if opt_dump and not (opt_ask) then
    choice;

  copro.control_Word := copro.control_Word or anyerrors;
  Asm
    frstor copro
    fclex
  end;
  { --- Befehl nochmals ausfuehren --- }
  if (occured and do_again) <> 0 then
    do_it_again;
  { --- Noch was? --- }
  if opt_auto then
    auto_corr;
  if opt_ask  then
    ask_correct;
  restore_masks;
end;

{ ------------- Ein- und Ausschalten ------------- }

Procedure handle_on;
begin
  Getintvec($75, normal.adr);
  Setintvec($75, @errorcon);
end;

Procedure handle_off;
begin
  Setintvec($75, normal.adr);
end;

begin
  handle_on;
  dump_on :=0;
  Exit_on :=0;
  ask_on  :=0;
  auto_on :=0;
  standard:=0;
  do_again:=invalid+zero_div+denormal;
  clear_copro;
end.






Unit dis387;

Interface

Function dis(opco : Word) : String;

Implementation

Function dis;
Var
  d, op : String;

  Procedure opcr(st : Word);
  Var
    t : String;
  begin
    str(st, t);
    op := ' st,st(' + t + ')';
  end;

  Procedure opc(st : Word);
  Var
    t : String;
  begin
    str(st, t);
    op := ' st(' + t + '),st';
  end;

  Procedure op1(st : Word);
  Var
    t : String;
  begin
    str(st, t);
    op := ' st(' + t + ')';
  end;

begin
  d  := '...';
  op := '';

  Case Hi(opco) OF
    $d8 :
      Case Lo(opco) div 16 OF
        $c :
          if opco MOD 16 >= 8 then
          begin
            d := 'fmul';
            opcr(opco MOD 16 - 8);
          end
          else
          begin
            d := 'fadd';
            opcr(opco MOD 16);
          end;

        $e :
          if opco MOD 16 >= 8 then
          begin
            d := 'fsubr';
            opcr(opco MOD 16 - 8);
          end
          else
          begin
            d := 'fsub';
            opcr(opco MOD 16);
          end;

        $f :
          if opco MOD 16 >= 8 then
          begin
            d := 'fdivr';
            opcr(opco MOD 16 - 8);
          end
          else
          begin
            d := 'fdiv';
            opcr(opco MOD 16);
          end;
      end;

   $d9 :
     Case Lo(opco) OF
       $d0 : d := 'fnop';
       $e0 : d := 'fchs';
       $e1 : d := 'fabs';
       $e4 : d := 'ftst';
       $e5 : d := 'fxam';
       $e8 : d := 'fld1';
       $e9 : d := 'fld2t';
       $ea : d := 'fld2e';
       $eb : d := 'fldpi';
       $ec : d := 'fldlg2';
       $ed : d := 'fldln2';
       $ee : d := 'fldz';
       $f0 : d := 'f2xm1';
       $f1 : d := 'fyl2x';
       $f2 : d := 'fptan';
       $f3 : d := 'fpatan';
       $f4 : d := 'fxtract';
       $f5 : d := 'fprem1';
       $f6 : d := 'fdecstp';
       $f7 : d := 'fincstp';
       $f8 : d := 'fprem';
       $f9 : d := 'fyl2xp1';
       $fa : d := 'fsqrt';
       $fb : d := 'fsincos';
       $fc : d := 'frndint';
       $fd : d := 'fscale';
       $fe : d := 'fsin';
       $ff : d := 'fcos';
     end;

   $db :
     Case Lo(opco) OF
       $e2 : d := 'fclex';
       $e3 : d := 'finit';
     end;
   $dc :
     Case Lo(opco) div 16 OF
       $c :
         if opco MOD 16 >= 8 then
         begin
           d := 'fmul';
           opc(opco MOD 16-8);
         end
         else
         begin
           d := 'fadd';
           opc(opco MOD 16);
         end;

       $e : if opco MOD 16 >= 8 then
         begin
           d := 'fsub';
           opc(opco MOD 16 - 8);
         end
         else
         begin
           d := 'fsubr';
           opc(opco MOD 16);
         end;

       $f :
         if opco MOD 16 >= 8 then
         begin
           d := 'fdiv';
           opc(opco MOD 16 - 8);
         end
         else
         begin
           d := 'fdivr';
           opc(opco MOD 16);
         end;
     end;

   $dd :
     Case Lo(opco) div 16 OF
       $c :
         begin
           d := 'ffree';
           op1(opco MOD 16);
         end;
       $d :
         if opco MOD 16 >= 8 then
         begin
           d := 'fstp';
           op1(opco MOD 16 - 8);
         end
         else
         begin
           d := 'fst';
           op1(opco MOD 16);
         end;
       $e :
         if opco MOD 16 >= 8 then
         begin
           d := 'fucomp';
           op1(opco MOD 16 - 8);
         end
         else
         begin
           d := 'fucom';
           op1(opco MOD 16);
         end;
     end;

   $de :
     Case Lo(opco) div 16 OF
       $c :
         if opco MOD 16 >= 8 then
         begin
           d := 'fmulp';
           opc(opco MOD 16 - 8);
         end
         else
         begin
           d := 'faddp';
           opc(opco MOD 16);
         end;

       $d : d := 'fcompp';

       $e :
         if opco MOD 16 >= 8 then
         begin
           d := 'fsubp';
           opc(opco MOD 16 - 8);
         end
         else
         begin
           d := 'fsubrp';
           opc(opco MOD 16);
         end;

       $f :
         if opco MOD 16 >= 8 then
         begin
           d := 'fdivp';
           opc(opco MOD 16 - 8);
         end
         else
         begin
           d := 'fdivrp';
           opc(opco MOD 16);
         end;
     end;
   end;

   dis := d + op;
end;

begin
end.