Why did MS-DOS applications built using Turbo Pascal fail to start with a division by zero error on faster systems?Zzpj Gуатъ2Кидиуз,нef хъндиx Y89Aа

14

On faster MS-DOS systems, it wasn't entirely uncommon for applications built using Borland's Turbo Pascal to fail to start, and (before exiting back to the command prompt) to report a division by zero error immediately upon launch.

As I recall, this behavior could be observed even with simple "Hello world" style programs.

A patch was available which fixed the issue both for freshly built executables as well as already-built ones, but my question here is: What actually caused the error in the first place?

A great answer will also discuss what the fix was, and how it fit into the executable code without requiring a recompilation.

share|improve this question
  • I found my sources for a TSR tool that automatically detects affected real-mode Turbo Pascal program on load and patches them accordingly (so no modification is of on-disk EXE files is needed). Alas, only in its most recent version that includes a failed experiment to add protected mode support, and completely in German. It completely replaces Delay by a new, multitasking-friendly (for long delays) implementation. Is RCSE interested in that tool? Should I go polish it up? – Michael Karcher 7 hours ago
  • @Michael there are a number of TSRs available, see here; if yours is different I guess it could be interesting! – Stephen Kitt 7 hours ago

1 Answer 1

active oldest votes
26

Turbo Pascal programs start by calibrating a delay loop (so that the Delay function knows how much to spin to achieve a certain delay). The calibration counts the number of times a certain loop is run for 55ms (as measurable using the PC’s timer interrupt with its default setting), then divides the number of loops by 55 so that Delay can then busy-wait in millisecond increments. On fast CPUs, 200MHz and up (on Intel CPUs), the loop runs too many times, and the division overflows. The CPU throws a “divide overflow” error, which the Pascal runtime reports as a division by zero error.

There are quite a few sites which explain this and provide patches; for example, J R Stockton’s page on the topic, which says

The Borland Crt unit is included in the TURBO.TPL & TPP.TPL libraries; its initialisation routine will be linked if Crt is cited in a uses clause. The problem lies in the initialisation of Crt.Delay, but will appear if the Crt unit is cited regardless of whether Delay or any other Crt routine is called.

During Crt unit initialisation, a loop executed for 55 ms increments a counter. Up to and in TP6, this was a 16-bit counter, and would happily overflow on a PC above about 20 MHz, leading to subsequent incorrect delays.

The counter in TP7 & BP7 is now 32-bit, and should not itself overflow until processor speeds reach the 100 GHz region. But the count is divided by 55, and if the result cannot fit into a 16-bit word, the CPU raises a "divide overflow" error. This is reported by Borland as a "divide by zero" error, Runtime Error 200, since the only way that a user's Pascal code can cause a divide overflow is by dividing by zero.

Arguably the best fix is to fix the Crt unit, and relink Pascal program. There are various approaches; for example, increasing the space allocated for the delay counter in CRT.ASM:

DelayCnt        DD      ?

(instead of DW), then altering the calibration routine to use both words instead of a single word.

Patching a fix into existing executables isn’t all that obvious since the fixed calibration routines take more space than the original, but that’s exactly what Andreas Bauer’s patch does: he shortened earlier initialisation code to make room for his fixed calibration routine, as detailed in the README file in his archive. Andreas’ patch doesn’t increase DelayCnt’s size, it only ensures that the calibration routine doesn’t overflow; as a result, on fast CPUs the Delay routine might not wait as long as intended.

Another approach is used by c’t’s patch: it relies on shortening another function in Crt (Break) to free up space for an improved version of Delay, and adjusting the divisor in the calibration routine so that the division no longer overflows. The calibration routine’s result isn’t used in this scenario.

There are also a number of TSRs which will handle the problem at runtime, without patching; one significant difference here is that most (if not all) such TSRs won’t work with protected-mode Turbo Pascal programs. Here also there are number of different approaches. PROT200 relies on handling the divide-by-zero error in the TSR instead of letting Borland’s code handle it. TP7P5FIX hooks the interrupt setup function in DOS and intercepts the initialisation code when it tries to setup its divide-by-zero handler, instead patching the initialisation code to return the highest possible value (0xFFFF). R200FIX patches dummy OUT instructions into the delay loop, which allows it to provide correct delays. (Thanks to Michael Karcher for the investigations.)

share|improve this answer
  • 2
    I've still got at least one customer using a patched TP application. They were going to switch to another vendor's Windows application years ago - but that turned out to be vaporware so they stuck with my DOS stuff and still use it to a limited extent today. – manassehkatz 6 hours ago
  • I still consider it an irony that run time error 200 was triggered by a CPU above 200 MHz. As if they wanted to tell the critical frequency right in the error message. :-) – celtschk 5 hours ago
  • The comment about the inner working of the TSRs is not accurate for all of them. I just checked TP7P5FIX and it instead patches the initialization code to replace the sequence "MOV CX, 55; DIV CX" by "MOV AX,FFFFh; NOP; NOP". I also checked PROT200, which indeed catches the divide-by-zero exception (and returns FFFF). And I checked R200FIX which patches an dummy OUT instruction into the timing loops. R200FIX is the only tool that keeps delays correct and contains the most elaborate (and memory-saving) TSR loading mechanism. None of these three tools can handle protected mode applications. – Michael Karcher 5 hours ago
  • Thanks @Michael, I’ve updated the comment in question (with some additional info on TP7P5FIX). – Stephen Kitt 4 hours ago
  • I still don't understand why they need to calculate the delay even though the user doesn't use anything related – phuclv 4 hours ago

Your Answer

Thanks for contributing an answer to Retrocomputing Stack Exchange!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged ibm-pc or ask your own question.

Popular posts from this blog

Coll del Lys (% Pa% 2deri lII

NG gemeente Greykerkda

पारमेनीडेस,hK9AaiUu,tgup 1 TreO Uu Ltomh ua12jnn