Runtime Error 200 running a Pascal program on fast systems (PII 266+)
Contents of this document
General information
Programmers information
Programmers Option 1: Enhancing the Delay-routine
Programmers Option 2: Removing the Delay-routine
Optional replacement delayloop
Users
Patch-program
General information
The Runtime Error 200 (Division by zero) bug is not part of the Pentium
Errors®. It's a mistake Borland made. The initialization part of the
CRT unit has a calibration loop for the procedure DELAY. The resulting
value of a counter depends on the speed of the cpu. This counter has an
overflow on high speed cpu's, including Pentium II 266 Mhz and faster.
Actually it is the same bug that on earlier processors caused Delay to
be inaccurate, that now causes programs to fail with a runtime error
if they use the CRT unit! Some (earlier?) compiler versions mysteriously
seem to go free of this bug - Delphi doesn't have them, for instance,
and Turbo Pascal 6 seems to work too.
Please note: I take no responsibility for the potential damage people may
do to their RTL's, working programs or vital data while using the
instructions in this document. Keep backups of the appropriate files!
(and then some...)
Programmers
As a programmer you have several different options. First of all you can
disable the delay-routine completely. If you need a delay routine you can
use the one found in this document, you can create your own, or you can
find another one somewhere on the web.
Another solution is to change the Delay-routine so it will work on todays
fast systems. This solution found in this document should push the problem
about 10 years into the future if we assume that Moores Law is correct.
If you're having Borland Pascal, you also have the sources of the runtime
library. Just make the following changes and recompile the complete
runtime library. A MAKEFILE is included with the sources.
If you have Turbo Pascal only, it's a bit more complicated. You'll need
the sources of unit CRT. At least the files CRT.PAS, CRT.ASM and SE.ASM.
They are the same in 7.0 and 7.01. Unfortunately copyright laws prevent
me from letting you download the patched CRT.TPU file from this server,
so please change it yourself (it is pretty easy):
Implement the changes, and assemble (TASM 3.2 preferred):
TASM -w- -z -e -i. CRT,CRT.OBJ,CRT.LST
compile
TPC -$d-,l- CRT
and insert the result to TURBO.TPL
TPUMOVER TURBO.TPL -CRT
TPUMOVER TURBO.TPL +CRT
and now, it will even work on your neighbour's Pentium II.
Programmers Option 1: Enhancing the Delay-routine.
The changes are:
1. Replace the 16-bit delaycounter
DelayCnt DW ?
by
DelayCntL DW ?
DelayCntM DW ?
DelayCntH DW ?
2. Delete the old calibration in the initialisation part:
MOV ES,Seg0040
MOV DI,OFFSET Timer
MOV BL,ES:[DI]
@@2: CMP BL,ES:[DI]
JE @@2
MOV BL,ES:[DI]
MOV AX,-28
CWD
CALL DelayLoop
NOT AX
NOT DX
MOV CX,55
DIV CX
MOV DelayCnt,AX
and replace it by
MOV ES,Seg0040
MOV DI,OFFSET Timer
MOV SI,-1
MOV BL,0FFH
MOV BH,ES:[DI]
@@2: CMP BH,ES:[DI]
JE @@2
MOV BH,ES:[DI]
MOV AX,-28
CWD
CALL DelayLoop
NOT AX
NOT DX
NOT SI
NOT BL
XOR BH,BH
MOV CX,AX
OR CX,DX
OR CX,SI
OR CX,BX
JNZ @@3 ; overflow of calibration counter?
DEC BX ; then force runtime error 200
@@3: MOV CX,55
XCHG DX,BX
XCHG AX,SI
DIV CX ; RTE 200, if result exceeds 48 bits
MOV DelayCntH,AX
MOV AX,BX
DIV CX
MOV DelayCntM,AX
MOV AX,SI
DIV CX
MOV DelayCntL,AX
3. Replace the original delay procedure with
Delay:
MOV BX,SP
MOV CX,SS:[BX+4]
JCXZ @@2
MOV ES,Seg0040
XOR DI,DI
MOV BH,ES:[DI]
@@1: MOV AX,DelayCntL
MOV DX,DelayCntM
MOV SI,DelayCntH
XOR BL,BL
CALL DelayLoop
LOOP @@1
@@2: RETF 2
4. and change the delay loop to
DelayLoop:
@@1: SUB AX,1
SBB DX,0
SBB SI,0
SBB BL,0
JC @@2
CMP BH,ES:[DI]
JE @@1
@@2: RET
That's all. If you'll have a gigahertz-cpu in some years, you'll get
the runtime error again, so if you don't use the delay-routine at all,
you might want to remove it completely:
Programmers Option 2: Removing the Delay-routine.
In the CRT.ASM file:
1. Remove the Delay reference (including the comma) from line 65.
2. Remove the Delay initialization code (lines 94 to 107, both inclusive).
3. Remove the Delay related procedures themselves (lines 437 to 462, both
inclusive).
4. Now recompile the CRT.ASM file, using the command line:
TASM CRT.ASM /i..\inc /w0
The switch characters i and w must _not_ be capitals!
In the CRT.PAS file:
5. Remove the Delay procedure definitions, both in the interface and
implementation section (lines 91 and 125).
6. Now recompile the CRT.PAS file, and put it into the TPL files using
TPUMOVER:
TPUMOVER TURBO.TPL -CRT
TPUMOVER TURBO.TPL +CRT
Optional replacement delayloop.
I'm currently using the following procedures for generating a delay, the
delay procedure is an exact replacement of the faulty Borland delay procedure.
The ShortDelay procedure can do even shorter delays, though the procedure
calling overhead will probably ruin the accuracy.
procedure ShortDelay(Interval: Word); assembler;
{ Interval = number of ticks
Note: About 1193180 ticks/s }
asm
push ax
push bx
cmp Interval,0FFFFh { otherwise 0FFFFh will end in an infinite loop }
jne @start
dec Interval
@start:
in al,040h { save initial time in bx }
mov bl,al
in al,040h
mov bh,al
@delayloop:
in al,040h { get current time }
xchg al,ah
in al,040h
xchg al,ah
sub ax,bx { calculate the difference }
neg ax
cmp ax,Interval { are we done? }
jb @delayloop
pop bx
pop ax
end;
procedure delay(ms: Word);
{ identical to the faulty Borland delay procedure }
var
A : Word;
begin
for A := 1 to ms do
ShortDelay(1193); { pause for 1 ms }
end;
This delay-routine is machine-independent, that is the delay uses the
same time whether there're running on a slow 8086 or a fast giga-hertz
system, as long as the clock/timer-system is 100% IBM-compatible, and another
program haven't fooled around with the clock.
The combination of removing the old delay-routine and using this replacement
routine is what I do myself, if you include this routine in the crt unit,
all you need to do is recompile the programs and they'll work perfectly well
on all systems (regarding this particular problem).
Users
Before you do anything yourself, you should try to contact the programmer,
so he can fix the problem using the steps listed above. If this is
impossible patch the file yourself using the following steps (or use the
program found in the end of this document).
PLEASE NOTE: This is NOT a perfect solution. There WILL
be problems if the program is using the delay routine to time events.
Anyway, I've used this method on several programs, and it seems to work well.
You can patch compiled binaries by scanning for the
byte-sequence: F7 D0 F7 D2 B9 37 00 F7 F1
and replace with: .. .. .. .. B8 FF FF 90 90
original patched
not ax not ax
not dx not dx
mov cx,55 mov ax,FFFF
div cx <- causes DBZ nop; nop
because ax=0
The strange thing about this patch is, at it didn't even seem to break
the Delay() function as I thought it would. Or maybe it does and I
just didn't notice. Therefore: Use at your own risk.
Patch-program
I have created a program, which patches pascal executeables using the
method described above.
Download PascalPatcher. Size: 7 Kb
|