Turbo Pascal

In the 80s, Borland released the Turbo Pascal system to a market plagued by long edit-compile-link times. The Turbo Pascal compiler was itself written in Assembly and took advantage of Pascal's single-pass program structure to achieve fast compilation times. Turbo Pascal also included an integrated text editor which made the entire process as simple as a modern day IDE. Programs were compiled to COM executables, which generally made them faster than the interpreted Pascals of its peers. Overall, Turbo Pascal quickly became known and is still remembered for being a pleasant system.

By the 90s, Borland had discontinued Turbo Pascal and moved onto Borland Delphi, which is in many ways its successor. In 2008, the division of Borland that produced Turbo Pascal was sold to Embarcadero, which still maintains Delphi.

Table of contents
  1. Installation and usage
  2. Using the editor
  3. The language
    1. Units
    2. Object-oriented programming
  4. Some programs
    1. 10 PRINT
    2. Fibonacci
  5. Resources

Installation and usage

Bits and pieces of Turbo Pascal remain online. Although Embarcadero has various places on their site where the system is supposed to be preserved, most of these seem dead due to bitrot. In my experience, Turbo Pascal 5.5 seems to have been best preserved, and can be found here and there around the internet. The best place to get ahold of it is probably on archive.org. There you can find plenty of books and other material about Turbo Pascal too. The rest of this article describes Turbo Pascal 5.5.

The executables in these archives are generally not compatible with newer versions of Windows. DOSBox comes in handy here, and can run the Turbo Pascal system easily and without issue. To do this, download DOSBox, open it and mount the folder where Turbo Pascal is:

Z:\>rem mount <drive name> <path to the folder with TURBO.EXE>
Z:\>mount c c:\users\me\downloads\tp55\disk1
Drive C is mounted as local directory c:\users\me\downloads\tp55\disk1

Navigate to the drive

Z:\>c:

And run Turbo Pascal!

C:\>turbo

At this point, you should be greeted with the blue Turbo Pascal editor and its copyright notice. If not, type dir and make sure TURBO.EXE is in the directory listing.

The Turbo Pascal copyright notice visible on startup.

I'd recommend adding the folder where TURBO.EXE is stored to the PATH environment variable such that it can be run in any folder:

C:\>PATH=%PATH%;C:\

Note that the open source Free Pascal compiler also provides support for most Turbo Pascal code, which is an alternative if you wish to program with a slightly more modern setup. Free Pascal is just a compiler, however, and does not come with the integrated editor and debugger.

Using the editor

The editor is fairly conventional, with arrow keys to move around and tab to indent. The menus at the top can be opened with Alt + F for File, Alt + R for Run, and so on. Additionally, once a menu has been opened, the other menus can be navigated to with the arrow keys. The Edit menu just brings you back to the editor itself, and the Escape key has the same effect.

Once a program has been written, it can be run directly from the Run menu. There is a default shortcut Ctrl + F9 for that action, although DOSBox by default swallows it and closes the emulator. On my machine, DOSBox will by default execute at around 2000 cycles per second, which is way too fast to actually see any output, since Turbo Pascal switches to the terminal while the program is running and back to the editor once it is finished. This can be overcome either by slowing down DOSBox with Ctrl + F11 (Ctrl + F12 to speed it back up), although that will (naturally) make things annoyingly slow. Another option is to swap between the program output and the editor with Alt + F5. Finally, you can add a repeat until Keypressed at the end of your program to spin until a key is pressed like so:

program Wait;
  uses Crt; { Needed to use Keypressed }
begin
  Writeln('Hello, world!');
  repeat until Keypressed
end.

Error messages are definitely not what we might have become used too with languages like Rust and Elm, and generally the only information you'll get is a message like Unknown identifier, ";" expected or Type mismatch and your cursor will be placed at the start of the offending token. The language is (usually) simple enough though that this is (usually) enough to figure out the error.

The language

Turbo Pascal is (perhaps unsurprisingly) a dialect of Pascal, and Standard Pascal programs are generally compatible with Turbo Pascal. See for instance the Free Pascal wiki for a comprehensive tutorial and introduction to the language.

Units

Perhaps the most notable features of Turbo Pascal since 5.5 is the unit system and the object oriented features. The unit system allows for modular programming by separating the code into different units. A unit might look something like

unit greeter;
interface
  { uses and other declarations here }
  procedure Greet(msg : string};

implementation
  { Definitions here }
  procedure Greet(msg : string);
  begin
    Writeln(msg);
  end;

{ Unit initialization can be done with a begin block here. }

end.

Note that in the implementation part above, the parameter part of the procedure declaration can be omitted (since it is already specified in the interface part). This is also true for functions.

A unit like greeter should be stored in a file with the unit name and the .PAS extension (so GREETER.PAS) for the above unit. Otherwise, Turbo Pascal won't automatically find and compile the unit for you.

To use a unit in some other unit or program, we can use the uses declaration. There can only be a single uses per unit or program, though multiple units can be used in a single such declaration by separating them with a comma. Finally, uses declarations must come first in the program definitions and the unit interface.

program main;
  uses greeter;
begin
  Greet('Hello, world!')
end.

Object-oriented programming

Version 5.5 introduced object oriented programming, and features most things one might expect from such a language. Objects are defined by creating object types, which may specify some fields and some methods.

program Main;
  type
    Animal = object
      constructor Init;
      procedure Greet;
    end;

  constructor Animal.Init;
  begin end;

  procedure Animal.Greet;
  begin
    Writeln('[Generic animal noise]')
  end;

  var
    A : Animal;
begin
  A.Init;
  A.Greet;
end.

Note that all objects must have an explicitly defined constructor. On my setup, Turbo Pascal crashes if it attempts to run a method on an object which hasn't been constructed (although no warning is emitted).

Objects may inherit from other objects. This ”brings along“ methods and fields from the parent object, which may be overriden by redeclaring and redefining them.

    Cat = object(Animal) { Cat inherits from Animal }
      color : string;
      constructor Init(fur_color : string);
    end;

    { ... }

    constructor Cat.Init(fur_color : string);
    begin
      color := fur_color;
    end;

    { ... }

    var
      A : Animal;
      B : Cat;

begin
  A.Init;
  B.Init('black');
  A.Greet;
  B.Greet; { Calls Animal.Greet }
end;

Methods (except constructors) may be virtual, which means the method call will be resolved at run-time through a vtable. This will only be done for references (aka pointer types in Turbo Pascal).

To demonstrate this, let's first change the Greet procedure to be virtual and override it for the Cat

  type
    Animal = object
      { ... }
      procedure Greet; virtual; { note the semicolon }
    end;

    Cat = object(Animal)
      { ... }
      procedure Greet; virtual;
    end;

  { ... }

  procedure Cat.Greet;
  begin
    Write('"Meow," the ');
    Write(color);
    Writeln(' cat says.')
  end;

Next we'll create a pointer type to Animal and a procedure that uses it.

    AnimalPtr = ^Animal;

  { ... }

  procedure SayHi(it : AnimalPtr);
  begin
    it^.Greet
  end;

At this point, we can give SayHi pointers to any objects which inherits from Animal, and it will select the method at runtime.

  var
    A : Animal;
    B : Cat;
begin
  A.Init;
  B.Init('black');
  SayHi(@A); { => [Generic animal noise] }
  SayHi(@B)  { => "Meow," says the black cat. }
end.

Some programs

10 PRINT

Uses Random from System for random number generation and Delay from Crt for timing. Note that Random(x) returns a number in the range 1 .. x (inclusive in both ends).

program main;
  uses Crt; { Delay, Keypressed }
begin
  Randomize;
  repeat
    if Random(2) = 1
    then Write('/')
    else Write('\');
    Delay(25) { milliseconds }
  until Keypressed
end.

Fibonacci

Uses longint instead of integer because the latter is only 16 bits while the former is 32 bits. Written to see how slow things get. Note that because DOS is without multitasking, the only way to stop the programs once things get slow is to press a key (setting Keypressed to true in the process) and wait for the current fibonacci computation to complete. If things get unbearably slow, DOSBox can be sped up to about 15 000 cycles per second without any configuration (and even more with).

One fun thing to try is rewriting this program to use the imperative version of the fibonacci function instead and see the difference in speed. That version will overflow before the recursive version gets even a few numbers done.

program main;
  uses Crt;

  var i : longint;

  function Fibonacci(n : longint) : longint;
  begin
    if n = 0 then Fibonacci := 0
    else if n = 1 then Fibonacci := 1
    else Fibonacci := Fibonacci(n - 1) + Fibonacci(n - 2)
  end;
begin
  i := 0;
  repeat
    Writeln(Fibonacci(i));
    i := i + 1;
  until Keypressed
end.

Resources