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