124. *****
Q: Curing Crt initialisation runtime error 200 on fast machines
A: I was not familiar with this problem myself since I have run
only sub-200MHz PCs, but I'll store here what I learned from the
excellent discussion on the subject in news:comp.lang.pascal.borland
by programmers much more knowledgeable than I. First of all the Crt
initialization crash only concerns TP and BP bersions 7.00 and 7.01.
Quoting Osmo Ronkanen: "The earlier versions had a bug that caused
only delays to be too slow from machines 386-33 or so on. This was
fixed in version 7.0 and the fix caused a new bug i.e. the division
by zero error on machines that were 55 times faster than the ones on
which the old bug appeared."
First some references. Please be aware that I can't guarantee
that the URL addresses below will stay current.
Prevent the "Divide by 0" error, Roger Donais. You'll need
ftp://users.southeast.net/private/rdonais/rdelay.zip
ftp://users.southeast.net/private/rdonais/util.zip
NewDelay, Fix for bug in Crt unit's Delay procedure. F.Heckenbach
http://www.mi.uni-erlangen.de/~heckenb/programs.htm#NewDelay
Problems with the Crt.Delay procedure, by Dr John Stockton
http://www.merlyn.demon.co.uk/pas-time.htm#Delay
Roger's solution is also available as (or whichever version numbers
are current when you read this):
4903 Jan 20 1997 ftp://garbo.uwasa.fi/pc/turbopas/rdelay10.zip
rdelay10.zip Prevent the divide-by-0 Crt error on fast machines, R.Donais
:
56849 Jun 21 1997 ftp://garbo.uwasa.fi/pc/turbopas/rutil10.zip
rutil10.zip Turbo Pascal utilities by R.Donais, (needed by rdelay)
Then there also is
ftp://garbo.uwasa.fi/pc/turbspec/bp7patch.zip
CRT Delay patch for TURBO.TPL 48,432 10-27-92 7:00a
and probably later versions in circulation.
The bug is in the Crt routine's initialization code. For example
Osmo Ronkanen writes "In the initialization code TP runs the delay
loop for one clock cycle. Then the result is divided by 55
(milliseconds in the cycle) to get loop size for one millisecond
delay. Now if that becomes greater than 65535 then the divide
overflow interrupt is called and that causes the runtime error to be
signaled." Dr John Stockton wrote "... initialize contains the
dreaded MOV 55 ; DIV CX"
A trivial solution to the problem is not to use the Crt unit at
all and to use the 'Wait' procedure from the item "If Delay
procedure does not work properly, how do I fix it?" instead of the
Crt.Delay.
Frank Heckenbach wrote about the 'Wait' procedure. "Besides the
fact that it doesn't prevent the runtime error 200 [if uses Crt is
included], as Osmo explained, this version's accuracy is only 1/18.2
seconds in contrast to 1 ms of the original Delay which should be
stuck to in replacements, IMHO."
Frank continued: "I'd also like to draw your attention to another
problem that the above version as well as the original Delay code
suffer from, namely busy waiting, this means executing some code
during the most time of the delay. Whereas this doesn't matter on a
single tasking DOS, it will significantly reduce the CPU time
available to other processes when run under multi tasking OS's. (And
such environments should really be taken into account these days.)"
Osmo Ronkanen posted the following solution for TP 7.0 which is
brief enough to be included in here. Osmo wrote about its previous
version: "[This runtime fix] does not disable delay. Delay works
just as it does before on machines that are slower than those that
cause problem. On faster machines it also works but as the counter
is set to 65535 instead of its true value of that would be something
higher the delays get slower and slower. So on machines that are
twice as fast as the ones that just cause the error the delays are
half as log as they should be." (Timo's addition. If the resolution
of 'Wait' procedure is sufficient for the programmer's purposes,
then it is better to use FDelay+Wait than FDelay+Delay.)
Unit Fdelay; { Place this before CRT. Real mode only }
interface
const dfix:word=1; { call delay() dfix times }
implementation
uses dos;
procedure oldints; assembler; { "variables" in the code segment }
asm dd 0,0 end;
Procedure error;
begin
runerror(200);
End;
Procedure Int0; assembler;
asm
cmp cx,55 { If CX<>55 we are at some other point }
je @ok
sti
call error
@ok:
shr dx,1 { divide dx:ax by 2 }
rcr ax,1
shl Dfix,1 { multiply Dfix by 2 }
iret { return to the DIV (286+) }
end;
{ Int21h handler removes the int0 handler (as well as itself) from
the memory when CtrlBreak vector is set by CRT right after
calculating the delay counter. Note DS does NOT point to the data
segment when this is called }
Procedure Int21h; assembler;
asm
cmp ax,$251B
jne @old { Not setint 1Bh? }
push es; push si; push di
mov si,offset oldints
xor di,di
mov es,di
cld
segcs; movsw
segcs; movsw { restore int 0 }
mov di,$21*4
segcs; movsw { restore int 21h }
segcs; movsw
pop di; pop si; pop es
@old: db $2e,$ff,$2e { jmp far indirect cs:[oldints+4] }
dw offset oldints+4
end;
type tr=record int0,int21:pointer; End;
pr=^tr;
begin
GetIntVec(0,pr(@oldints)^.int0);
GetIntVec($21,pr(@oldints)^.int21);
SetIntVec(0,@int0);
SetIntVec($21,@int21h);
end.
Roger Donais emailed me the following version based on Osmo's codes.
It is for TP 4.0-7.0. Just put
uses FDelay, Crt;
at the top of your program. Nothing else is needed. Not even a call
at the beginning of the main program. This is a nice end result of
the concerted efforts of the news:comp.lang.pascal.borland newsgroup.
Please note, however, that the code below does not have all the
safeguards that the code above does according to its author.
UNIT FDelay;
{ Purpose is to intercept 1st divide by zero error and if it appears
it could be from CRT to allow it to pass thereby allowing TP 7.0
(real mode) to use CRT (w/o delay) on systems with fast processors.
This solution will not work w/ multiple divide by 0 errors and may
leave the system unstable if an error occurs during TP exit process.
}
INTERFACE
USES DOS;
IMPLEMENTATION
VAR Old0 : Pointer;
TPExit: Pointer;
PROCEDURE Int0 (Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP
: Word); interrupt;
BEGIN
SetIntVec(0, Old0);
If CX = 55 Then Begin
AX := 65535;
DX := 54;
End; { Else error recurs w/ original int0 handler }
END;
PROCEDURE OnExit; FAR;
BEGIN
ExitProc := TPExit;
SetIntVec(0, Old0);
END;
BEGIN
GetIntVec(0, Old0);
SetIntVec(0, @Int0);
TPExit := ExitProc;
ExitProc := @OnExit;
END.
If you wish to solve the problem by not using the Crt unit at all,
below is a list of all the Crt unit procedures and functions and the
replacements that yours truly (Timo) has released or that can be
found in the FAQ.
Crt Replacement Where
--- ----------- -----
AssignCrt .. ..
ClrEol .. ..
ClrScr ClrScreen,CLS,CLS40 FAQ #117
Delay Wait FAQ #67
DelLine .. ..
GotoXY GOATXY TSUNTG in tspa*.zip
HighVideo .. ..
InsLine .. ..
KeyPressed KEYPREFN TSUNTM in tspa*.zip
LowVideo .. ..
NormVideo .. ..
NoSound .. ..
ReadKey READKEFN,RDENKEFN TSUNTM in tspa*.zip
Sound AUDIO TSUNTD in tspa*.zip
TextBackground ..
TextColor ..
TextMode ..
WhereX WHEREXFN TSUNTG in tspa*.zip
WhereY WHEREYFN TSUNTG in tspa*.zip
Window ..
--------------------------------------------------------------------
From ts@uwasa.fi Sun May 10 00:02:05 1998
Subject: Copying from TP help
               (
geocities.com/siliconvalley/2926)                   (
geocities.com/siliconvalley)