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 >
Wrap
Pascal/Delphi Source File
|
1993-01-24
|
12KB
|
406 lines
{$n+,s-}
unit error87;
interface
implementation
uses dos,decode87;
type
controlword = set of (Invalidmask, Denormmask, Zerodivmask, Overflowmask,
Underflowmask, Precisionmask,
CReserved6, IntEnable, Precision0, Precision1, Round0,
Round1, Infinity, CReserved13, CReserved14,
CReserved15);
statusword = set of (Invalid, Denorm, Zerodiv, Overflow, Underflow, Precision,
SReserved6, IntRequest, C0, C1, C2, ST0, ST1, ST2, C3,
Busy);
bitnumbers = 0..15;
state87 = record
control : controlword;
status : statusword;
tags : word;
case boolean of
false: (ip15_0, { Real mode }
ip_opcode,
op15_0,
op19_16 : word;
stack : array[0..7] of Extended);
true: (ip,
op : pointer);
end;
function single_infinite(var s : Single) : Boolean;
begin
if (LongInt(s) and $7FFFFFFF) = $7F800000 then
single_infinite := True
else
single_infinite := False;
end;
function single_nan(var s : Single) : Boolean;
var
words : array[1..2] of Word absolute s;
begin
single_nan := False;
if ((words[2] and $7F80) = $7F80) and (not single_infinite(s)) then
single_nan := True;
end;
function double_infinite(var d : Double) : Boolean;
var
longs : array[1..2] of LongInt absolute d;
begin
if (longs[2] = $7FFFFFFF) and (longs[1] = 0) then
double_infinite := True
else
double_infinite := False;
end;
function double_nan(var d : Double) : Boolean;
var
words : array[1..4] of Word absolute d;
begin
double_nan := False;
if (words[4] and $7FF0) = $7FF0 then { not a number, but maybe INF }
if not double_infinite(d) then
double_nan := True;
end;
function extended_infinite(var e : Extended) : Boolean;
var
words : array[1..5] of Word absolute e;
begin
if ((words[5] and $7FFF) = $7FFF)
and (words[4] = $8000)
and (words[3] = 0)
and (words[2] = 0)
and (words[1] = 0) then
extended_infinite := True
else
extended_infinite := False;
end;
function extended_nan(var e : Extended) : Boolean;
var
words : array[1..5] of Word absolute e;
begin
extended_nan := False;
if ((words[5] and $7FFF) = $7FFF) and
((words[4] and $8000) = $8000) then { not a number, but maybe INF }
if not extended_infinite(e) then
extended_nan := True;
end;
function bcd_zero(var b) : Boolean;
var
words : array[1..5] of Word absolute b;
begin
bcd_zero := False;
if ((words[5] and $7FFF) = 0)
and (words[4] = 0)
and (words[3] = 0)
and (words[2] = 0)
and (words[1] = 0) then
bcd_zero := True;
end;
var
state : state87; { In data segment, in case there isn't much stack
space }
var
oldexitproc : Pointer;
{$f+}
procedure my_exit_proc;
var
opcode : Word;
last_inst : opcode_info;
ops_read : operand_set;
regs_read : operand_set;
op_address, ip_address : Pointer;
tos : 0..7;
op : operand_type;
danger : Boolean;
function physical(reg : operand_type) : Byte;
{ Return the physical register number of a register }
begin
physical := (Ord(reg)+tos) mod 8;
end;
function tag(reg : operand_type) : Byte;
begin
tag := (state.tags shr (2*physical(reg))) and 3;
end;
function is_a_Nan(op : operand_type) : Boolean;
begin
is_a_Nan := False;
case op of
arReg0..arReg7 : begin
if tag(op) <> 2 then
Exit;
is_a_Nan := extended_nan(state.stack[ord(op)]);
end;
arSingle : is_a_Nan := single_nan(Single(op_address^));
arDouble : is_a_Nan := double_nan(Double(op_address^));
arExtended : is_a_Nan := extended_nan(Extended(op_address^));
end;
{ others can't be NaNs }
end;
function is_a_zero(op : operand_type) : Boolean;
begin
is_a_zero := False;
case op of
arReg0..arReg7 : begin
if tag(op) = 1 then
is_a_zero := True;
end;
arSingle :
is_a_zero := (Single(op_address^) = 0.0);
arDouble :
is_a_zero := (Double(op_address^) = 0.0);
arExtended :
is_a_zero := (Extended(op_address^) = 0.0);
arWord :
is_a_zero := (Word(op_address^) = 0);
arLongint :
is_a_zero := (LongInt(op_address^) = 0);
arComp :
is_a_zero := (Comp(op_address^) = 0);
arBCD :
is_a_zero := bcd_zero(op_address^);
end;
end;
function PtrToLong(p:pointer):longint;
begin
PtrToLong := longint(seg(p^)) shl 4 + ofs(p^);
end;
function PtrDiff(p1,p2:pointer):longint;
begin
PtrDiff := abs(PtrToLong(p1)-PtrToLong(p2));
end;
procedure adjust_for_prefix;
var
temp : longint;
begin
temp := PtrToLong(ip_address)-longint(prefixseg)*$10-$100;
{ this is the linear address relative to the start of the program }
ip_address := ptr((temp and $FFFF0000) shl 12, temp and $FFFF);
{ ip_address will have smallest possible segment number }
{ User must manually work out true segment value }
end;
procedure Find_ip;
var
i : integer;
begin
ip_address := Ptr(seg(ErrorAddr^)+PrefixSeg+$10,ofs(ErrorAddr^)-5);
{ Start looking 5 bytes before ErrorAddr }
for i:=1 to 5 do
begin
if byte(ip_address^) = $CD then
exit;
ip_address := Ptr(seg(ip_address^),ofs(ip_address^)+1);
end;
ip_address := nil;
end;
procedure rangecheck(lower,upper:extended);
var
reg : operand_type;
begin
if (last_inst.inst = iFISTP) and (tag(arReg0) = 3) then
reg := arReg7 { This doesn't really belong here, but
a pop happens in trunc() because it temporarily
masks exceptions. }
else
reg := arReg0;
danger := (state.stack[ord(reg)] < lower)
or (state.stack[ord(reg)] > upper);
end;
begin {my_exit_proc}
ExitProc := oldexitproc;
if (ErrorAddr = nil) or (ExitCode <> 207) then
Exit;
inline($cd/$39/$36/state/$9b);
if test8087 > 0 then { Is this a real '87? }
begin
{$ifndef dpmi}
opcode := state.ip_opcode and $07FF+$d800;
op_address := Ptr(state.op19_16 and $F000, state.op15_0);
{$else}
opcode := swap(word(state.ip^));
op_address := state.op;
{$endif}
{$ifdef ver70}
ip_address := ErrorAddr;
{$else}
ip_address := Ptr(state.ip_opcode and $F000, state.ip15_0);
adjust_for_prefix; { Make ip_address on same scale as ErrorAddr }
if ptrdiff(ErrorAddr,ip_address) > 10 then
ErrorAddr := ip_address;
{$endif}
end
else
begin { Handle the emulator }
find_ip;
if ip_address = nil then
begin
writeln('Error probably occurred in library routine. Error87 can''t help.');
exit;
end;
{ Now ip_address points to $CD byte before instruction }
ip_address := Ptr(seg(ip_address^),ofs(ip_address^)+1);
opcode := swap(word(ip_address^)) + $a400;
op_address := Ptr(dseg, Memw[seg(ip_address^):ofs(ip_address^)+2]);
{ we don't know the segment, but we can guess }
end;
decode_opcode(opcode, last_inst);
operands_read(last_inst, ops_read);
regs_read := ops_read*[arReg0..arReg7];
tos := (Word(state.status) shr 11) and 7;
{ Look for bad square root }
if last_inst.inst = iFSQRT then
if state.stack[ord(arReg0)] < 0.0 then
begin
WriteLn('Taking the square root of a negative!');
Exit;
end;
{ Look for zero by zero divide }
if last_inst.inst in [iFDIV, iFDIVP, iFIDIV, iFDIVR, iFDIVRP, iFIDIVR] then
begin
danger := True;
for op := arReg0 to arExtended do
if op in ops_read then
if not is_a_zero(op) then
danger := False;
if danger then
begin
WriteLn('Zero divided by zero!');
Exit;
end;
end;
{ Look for stack overflow }
for op := operand_type(8-num_pushes(last_inst)) to arReg7 do
if tag(op) <> 3 then
begin
WriteLn('Coprocessor stack overflow!');
Exit;
end;
{ Look for NANs }
if ops_read <> [] then
for op := arReg0 to arExtended do
if op in ops_read then
if is_a_Nan(op) then
begin
WriteLn('Operand is not a number!');
Exit;
end;
{ Look for truncation errors. Note that, contrary to the docs,
the stack may have been popped, so this has to come before the
underflow check }
if last_inst.inst in [iFIST,iFISTP] then
begin
{ Should check rounding mode, but I'm too lazy! }
case last_inst.arg1 of
arWord: rangecheck(-32768.5,32767.5);
arLongint: rangecheck(-2147483648.5,2147483647.5);
arComp: rangecheck(-9223372036854775808.5,
9223372036854775807.5);
end;
if danger then
begin
WriteLn('Value too large to store in integer!');
Exit;
end;
end;
{ Look for stack underflow }
if regs_read <> [] then
for op := arReg0 to arReg7 do { i is logical register number }
if op in regs_read then
if tag(op) = 3 then
begin
WriteLn('Coprocessor stack underflow!');
Exit;
end;
WriteLn('Unrecognized floating point error!');
end;
function patch_system : Boolean;
{ Patches system unit so that 8087 is not cleared on error }
type
one_instruction = array[1..3] of Byte;
const
before : one_instruction = ($cd, $37, $e3); { FINIT }
after : one_instruction = ($cd, $37, $e2); { FCLEX }
{$ifdef ver70}
patch_ofs = $31;
{$else}
patch_ofs = $32;
{$endif}
var
int02_handler : Pointer;
int10_handler : Pointer;
patch_site : ^one_instruction;
b : Byte;
begin
GetIntVec(2,int02_handler);
{$ifdef dpmi}
patch_site := Ptr(Seg(int02_handler^)+SelectorInc,
Ofs(int02_handler^)+patch_ofs);
{$else}
patch_site := Ptr(Seg(int02_handler^), Ofs(int02_handler^)+patch_ofs);
{$endif}
for b := 1 to 3 do
if patch_site^[b] <> before[b] then
begin
patch_system := False;
Exit;
end;
patch_site^ := after;
patch_system := True;
end;
begin
if test8087 = 0 then
begin
writeln('Warning: no coprocessor detected. Error87 does not work well');
writeln(' with Borland''s emulator.');
end;
if patch_system then
begin
oldexitproc := ExitProc;
ExitProc := @my_exit_proc;
end
else
WriteLn(
'Error87 is unable to find the patch point., and is not installing itself');
end.