# Stefan's Turbo Pascal Corner

Here, you find some unusual projects I did for Turbo Pascal. TP's best days are now over, as there are much better alternatives available. On the other hand, extending the old compiler's capabilities is a fun and challenging task. I still use and improve both programs. I'd be glad if you find a use for them, or maybe even improve them.

## tputuner

tputuner is a code optimizer for Turbo Pascal 6.0 and (NEW) 7.0. The Turbo Pascal compiler does not optimize much, all it does is a bit constant-folding and some dead-code elimination.

tputuner reads a compiled .tpu file (TPU9), disassembles it, optimizes it, and writes out the optimized version. tputuner features a rudimentary data flow analysis (mostly only reload optimisation), common subexpression elimination, some jump tweaking, peephole optimisations and basic register allocation. The main focus of tputuner is a size reduction, not necessarily speed increase, but usually, size reduction doesn't hurt speed. Reliable speed optimisations are not possible anyway with that little knowledge tputuner has.

I developed tputuner in C++ under Linux because that made it easier to handle large amounts of data. It also compiles under DOS with DJGPP.

TPUTUNER can be configured for Turbo 6 or Turbo 7 at compile time, the precompiled binary is for Turbo 7.

• Download tputuner: tputuner.zip (190 k), with source and documentation in German. The program itself is in English. The enclosed DOS .exe requires a 32-bit DPMI server such as cwsdpmi. Version March 2002.

## DPMI for Turbo Pascal

D4TP allows Turbo Pascal 6 / 7 users to write protected mode programs. This is a pure DPMI client, i.e., not a complete DOS extender. It loads your program into protected mode if possible (=if a resident DPMI server is found), otherwise it will continue to execute in real mode.

Since 27/Apr/2002, D4TP is able to load Borland's dpmi16bi.ovl DPMI server when needed.

The main advantage of D4TP is that it allows your program to access megabytes of memory (though it is only 16 bit, I've already seen a D4TP program allocate 20 meg). Also, it becomes harder to completely screw up the system with a dangling pointer.

You need to port a bit of your code. However, that's not as complicated as it sounds, I ported PCC, a 70000 lines project, in two or three days.

D4TP programs need a resident DPMI server. Unlike programs compiled with BP7, they do not automatically load one. This is only a problem for the few people who still run plain DOS (like me ;-). In DOS boxes, DPMI is usually available.

• Download DPMI for Turbo Pascal: d4tp.zip (29 k), with source, documentation, and some small demonstration programs. Version April 2002.

## Compiling big projects

Turbo Pascal was always classified as a "hobbyist" product. Still, people do big projects with it, me being one of them. What does "big" mean here? In the magnitude of 100 modules. When reaching this range, you'll experience more and more errors of the type "out of memory", "too many nested files", "too many symbols" -- not fun, really.

What is the source of these problems? Turbo Pascal insists on doing its own dependency analysis and recompilation management. There is no way of recompiling a module depending on something you modified without recompiling the modified module itself -- and this pulls in a myriad of other modules. This easily fills up Turbo's internal tables.

Of course, there's a way out. Everything Turbo Pascal really needs to recompile a module is the interface of the modules it used, not the implementation. You know C? You always include the header files, not the implementation files. So there must be a way to make Turbo Pascal compile only the interface of a module. And, there is one. Quite Hackish one, otherwise it would not be worth being mentioned here ;-)

• Let's assume your source code is in a directory src. Make a subdirectory src\int. For each module (.pas file), make a .pas file with the same name in the new subdirectory. This file should consist of the same interface part as the original module (identically, token-by-token), but an empty implementation part. For the module to be accepted, each procedure exported by the interface part must have an implementation -- an empty one suffices.
Of course, you do not have to do this work manually. At the end of this section, there is a perl script which does this for you.
• If you now want to recompile module mod, cd' to src\int and type
tpc /m ..\mod
What does this do? It reads the file src\mod.pas, and creates a unit src\mod.tpu. But instead of pulling in the large other units from src, it uses the short header-only units from src\int. Turbo Pascal looks first in the current directory when searching for units, so it will happily use the short ones.
• To recompile the main program, compile all units this way, and then issue a normal tpc main from the src directory.

This sounds complicated, but actually is not. You do not have to run through this whole one-two-three for each recompile. When you just changed part of an implementation of a function, a simple tpc /m main suffices. Only if you change critical items in interface parts, you need to do the whole thing. Even then, you do not have to regenerate all the interface parts in src\int, only those which actually changed.

• Step 1, building the interface parts: here's the promised Perl script (mkiface.pl) to make the interface parts. Invoke it as "perl mkiface unit.pas", it will read unit.pas, and write int\unit.pas. When you give it a program module instead of a unit, it will tell you and not do anything.
This is not a complete Pascal parser, if you have a very weird coding style, you may have to make it smarter. In particular, it is confused by comments within declarations.
• Step 2, automating making the interfaces. Regenerating files from changed source? Classic use-case for Make. I use the following piece of GNU make for this:
# This is expanded to all my source modules prefixed with int/'
iface=$(patsubst %, int/%,$(wildcard *.pas))

# Build goal: all interface units
int: $(iface) # This rule says how to make an interface unit from a complete one int/%.pas: %.pas perl mkiface.pl$*.pas

It's a bit harder to do that with Borland make; the version of Borland make shipped with Turbo 6 can't do it at all.
• Step 3, recompiling the modules when Turbo fails to do it. As I said, this is not needed quite often. Hence, recompiling everything is a viable alternative (usually you have to recompile 90% anyway when you do a critical change). Make sounds like a good candidate for this task, unfortunately my version of GNU make has problems calling the Turbo compiler (or, tpc has problems being called by GNU make), so I wrote a small pascal program (rebuild.pas) to do that. The actual compiler invocation must be customized in the source code.

Other tips to make programs compile better:

• Good structure. If the project is clearly structured, you can probably avoid all circular unit dependencies. Sure, when you discover your project is too big, it's usually too late. And my projects probably fail this requirement anyway ;-)
• Factor out stuff. Pulling in a whole 2000 line module just for a single type definition? Make that type definition a separate unit. Generally, it's usually handy anyways to have a unit full of types, a unit full of constants, etc. Unfortunately, you must implement an object type in the same module where you declare it.
• Drop a bit type safety. I occasionally declare functions as taking an untyped POINTER or an untyped VAR parameter, to not have to use the appropriate unit in the interface part.
• Use symbolic references. This is not always an option. I sometimes have function pointer tables, initialized at run-time with pointers to functions, and call the functions through the table. This gains a lot of additional flexibility sometimes. For example, the table indexes can be stored in files while pointers can not.

[ Main Page | All files ]

Stefan - Streu@gmx.de