Welcome to denMike's tiny page!
Home
Guestbook
Contact
 
Husky
 
Electronics
Fantasy
mtech.dk
Programming
  Resources in .exe Files

  Pascal Runtime Error 200

  The Pentium F0 bug

  Number Printing Routing

  Using the IOE Interrupt

 
Software
Space
Various Links
 
USA 2001
ISU MSS03
USA 2006
South Africa 2008


Last updated: July 18th, 1999
(Uploaded May 17th, 1998)
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