STRUCTURED PROGRAMMING

The Day the Earth Wouldn't Sit Still

Jeff Duntemann

First, you freeze. The little jitters happen out here now and then. They only last a second, and most of us just stop where we are and wait. At 5:04 I was outside on the new redwood front steps with a tube of Plastic Wood in my hands, filling in a syntax error I had made with the router.

But this jitter didn't pass. Out of the earth came a noise unlike anything in creation, a thing so deep and so pervasive that it bypassed the ears and went straight to the guts. What was a jitter became a violent, unending spasm, the earth boiling amidst the sound of two continents dragged one over the other. I dropped the plastic wood and held onto the stairs. From inside the house, I heard things begin to break: First glass, then the crockery of our water dispenser, then something large and heavy collapsing in the garage. Finally, with the horror in full sway, the agony of my house joined the agony of the earth, and it went on, and on, and on . . .

Twenty seconds passed, and the world grew quiet again.

The Machine Stops

Very quiet. Scotts Valley was dead. The silence was absolute, made sharper by the contrast with the all-consuming cacophony that had come before it. No birds, no cars on Highway 17, no tumbling burr from the sawmill, no susurrus from Seagate's many nearby buildings.

My three dogs stood like statues on the asphalt; I dove for them just as panic broke in them, and caught Mr. Byte and Chewy in time to throw them into the back of the Magic Van. Max, on the other hand, was already far down the driveway at full speed and was gone for many hours.

My water heater had broken its pipes, and the laundry room was filling with water.

In the east, a plume of black smoke was roiling into the sky. Somewhere there were sirens. I heard a woman yell in the distance. Another answered her. By 5:06 the silence had ended, and the long night had begun.

Being four miles from the epicenter of a Richter 7.1 temblor can teach you a few things. Technology is fragile. In an instant the power lines and phone lines were swept away. The town with more computers than people (and more disk drives than computers) simply stopped. No TV, no cable, no satellite downlinks. Ever the prepared radio amateur, I hooked a gel cell to an FM radio and tuned the dials. The local stations were not there. What did I think they ran on, wood stoves? A distant station crackled to me that unconfirmed reports of a violent earthquake in San Francisco were being investigated.

Right.

Over the days we learned the extent of the damage: Borland International's main building was deemed too dangerous to occupy. Much of Seagate's precision equipment was rendered useless. Hundreds of houses were utterly destroyed, including the home of Jack Davis, CEO of Metagraphics, maker of the Meta Window graphics library. Thousands more were severely damaged. Downtown Santa Cruz was nearly destroyed.

A New Paradigm

Over the week we spent cleaning up, I did a lot of thinking. Our urban paradigm is very much one of the mainframe and the terminal. We are terminals connected to the power company mainframe, the telephone company mainframe, the natural gas line mainframe. It doesn't take much to make the mainframe go down, and when it goes down, the terminals go dead.

We need to stop being terminals and start being PCs. We need to plate every roof with amorphous silicon photovoltaic panels, and perfectly sealed battery technology to the point where a water heater-sized unit can operate the household for several days on a full charge and not cost as much as the house to buy. Homes should be power exporters during the day, charging their own cells to capacity and then feeding surplus energy onto a peer-to-peer power grid to run smaller installations without their own cells. If the grid goes down, the homes themselves remain livable refuges.

Similarly, every home should have a 12-inch precision parabolic dish antenna on the roof, aimed at a geosynchronous satellite with the bandwidth to handle an entire state at one gulp. Communication with the outside world should not hang from a fragile copper wire.

Home heating is a problem. Perhaps the answer is to break down water into hydrogen with solar current during the day and then burn the hydrogen for heat at night. We need to set the methane and propane and yes, my fellow Sixties radicals, even the "clean" wood stoves aside. Burning carbon -- any carbon -- feeds the greenhouse.

It makes you grin with the kind of grin that hurts: For the last hundred years we've been using technology to tie ourselves to abject dependence upon one another; now it's pretty plain that we'd better use that same technology to help each household stand alone. For the Earth he done spoke, as he does now and then. Even if it takes another hundred years, we need to change our paradigm to take the Earth into account.

Let's consider this one -- the Pretty Big One -- a warning.

Why Is There Modula?

It took the Mighty U.S. Post Office three days to resume the mails, but Federal Express was making deliveries the next day. We were still living out of the Magic Van when the FedX lady roared up the drive and dropped a box in my hands. In the box was a truly remarkable thing: Stony Brook Modula-2.

Now, at last count I had nine DOS-based Modula-2 compilers on my shelves here. All work; most work well; some work beautifully, and one -- TopSpeed Modula-2 -- works spectacularly. But perhaps latecomers have the edge, because there is a certain crackle of excellence that sets Stony Brook apart, even from the formidable TopSpeed. Stony Brook is worth a closer look, in the interest of understanding the Vice President of structured languages: Modula-2.

I've given Modula short shrift in this column, because President Pascal keeps hogging the spotlight. Modula-2 freaks keep waiting for the Prez to kick off and let their man ascend to the top spot, but the Prez lives on. (Lordy, now there are even two of them.)

It won't happen. Turbo Pascal brought Pascal to the same sort of critical mass that Cobol and OS/360 achieved many years ago. It will literally never go away because there is so much of it out there. Fortunately, Pascal continues to evolve, and today it serves needs unimagined 18 years ago at its birth. Pascal may in fact absorb Modula-2 over time by stealing its best features one by one. The last chapter has definitely not been written.

(And Pascal's not the only one. I heard not long ago that a committee has convened to add object-oriented extensions to -- hold your breath -- Cobol. I can see it now: "File: Go read yourself. Record: Go write yourself." It is to boggle the mind.)

So why is there Modula? What good is it? And why hasn't it done better?

Good questions. Let's talk.

No Ambiguities

If I were to choose the #1 imperative of Modula-2, it would be this: Let there be no ambiguities. This imperative works in the small and in the large.

For example, in Pascal it's sometimes impossible to tell whether an element in an expression is a variable or a function. Like this: Is MaskedMarauder a variable or a function?

Zorro := MaskedMarauder; 
  { Pascal code }

The answer, of course, is that you can't tell. You have to go look further up the source code to see.

In Modula-2, a function identifier is always followed by a pair of parentheses, even when it takes no parameters. Like so:

  
Zorro := MaskedMarauder( );
  (* Modula-2 code *)

Now the marauder loses his mask a little. C does this as well, which is one of C's few advantages over Pascal. (And a small one it is, too; rather like a diamond ring dropped into a fifty-year-old outhouse. You dig for it . . .)

In the larger view, Modula-2 always lets you know where every program element is defined. Apart from a small suite of standard functions and control structures, everything used by a Modula-2 program must be listed at the top of each module, in statements that describe where each element comes from:

  
FROM SuperHeroes IMPORT MaskedMarauder;
FROM Japan IMPORT Rice, Cars, VCRs;
FROM LawSchool IMPORT SmallMinds;

Sometimes, in a largish module of a largish program, there may be dozens of such statements.

This is one very full step past the modularity mechanisms of Turbo/QuickPascal, which tells you which modules (in Pascal, units) are referenced but not what was taken from them:

  
PROGRAM UnholyMess;
USES SuperHeroes, Japan, LawSchool;

This doesn't tell me where the identifier SmallMinds comes from, though depending on your politics you might intuit either Japan or LawSchool. (I didn't use unit Congress here or it'd be a no-brainer.) In Modula you don't have to intuit, or look it up in the interface section of every module you use. The importer tells all. And in a complex program this can save enormous amounts of source code scanning!

Sometimes this quest for unambiguity makes things a little bit inconvenient. In Modula-2, the type and number of parameters in a procedure can never change. Out goes Pascal's Read and Write, which can take any number of parameters in numerous modes. Instead, you have a separate output and input procedure for each data type, such as WriteString, ReadInt, ReadCard (for the CARDINAL unsigned integer type), and so on. What might be done in a single Writeln statement in Pascal becomes a conga line of WriteThis, WriteThat, and WriteTOther, one for each different type you need to output. Whether this is a bug or a feature depends on whether you write applications or compilers; Pascal's open-ended I/O routines make matters lots trickier for compiler architects.

So Modula-2 eliminates a great many of Pascal's ambiguities, but, oddly, it doesn't go nearly as far in one respect as it might. Numerous control structures end in the identical word END; what Wirth should have used is a unique terminator for each structure, such as ENDFOR, ENDCASE, ENDWHILE, and so on. Wirth wisely added an END word to the end of every IF .. THEN statement:

  
IF Richter < 8.0 THEN
		GrabSomething;
		HangOn;
   	ELSE
		GrabSomething;
		SayPrayers
END;

(Note the welcome absence of redundant BEGINwords.) Still, what he should have added was a unique terminator word such as ENDIF. This would have added tremendously to the readability of heavily nested control structures. Ahh~ well. Maybe Modula-3.

Flexibility Without Ambiguity

One of the critics' numerous gripes against Pascal is its rigid type checking If you want to pass an array to a procedure, the formal parameter must be identical in type to the actual parameter. In other words, if you want to create a general-purpose routine for sorting arrays of records, in Pascal you must declare the formal parameter as having specific bounds. When called, the array passed to the sort procedure as its actual parameter must not only have elements of the same type as the formal parameter but identical bounds as well. A variable defined as

  ARRAY[0 .. 1023] OF MyRecord

cannot be passed as an actual parameter to a sort procedure defined as

  PROCEDURE SortEm(VAR Target: RecArray);

where RecArray has this definition:

  ARRAY[0..255] OF MyRecord

Rigid indeed. And not entirely true, even of Pascal. Standard Pascal contains a feature that is implemented so rarely (and not in either Turbo or Quick-Pascal, though present in MS Pascal as its "super array") that most people have just plumb forgot about it: The conformant array.

Modula-2 supports conformant arrays, though it uses the far more descriptive term "open array." A formal array parameter of a procedure is an open array when it is defined without bounds:

  PROCEDURE SortEm(VAR Target: ARRAY OF MyRecord);

Only one-dimensional arrays may be open arrays. Within the procedure, the high bound may be returned by calling the standard procedure HIGH. The low bound is always assumed to be 0. This way, you can pass an array of MyRecord of any size to SortEm, and simply ask the HIGH procedure for the index of the last element. Open arrays are an excellent example of how Modula-2 can simultaneously be more flexible than Pascal while remaining less ambiguous.

Modula-2 also provides standard port for coroutines, which try as I might defy description in anything smaller than a column unto themselves. We'll take them up again in the future. For now, suffice it to say that coroutines amount to poor-man's multitasking, and are of minimal usefulness -- but are a giant step in the right direction.

And Some Boo-boos

The news on Modula-2 is almost entirely good. Just to make sure we were all awake, however, old Nick Wirth threw in some zingers. Worst of these is the rather ridiculous restriction on the number of elements in a set. A standard Modula-2 set may have only 16 elements. Wirth says he did this to make compiler implementation easier, which is an explanation but not an excuse. Somebody has to use these damned things, Dear Doctor! Probably 90 percent of all sets used in Pascal are sets of Char, in which there must be 255 slots. Out the window. (In standard Modula-2, at least. More later...) My hunch is that this restriction has sunk more conversions to Modula-2 than all its other peccadillos put together.

Compared to that, other irritations are minor. Modula-2 is case sensitive. I don't like it in C, and I don't like it in Modula ... but programmers can and will live with it. Comments must be bracketed with the (* *) comment delimiters rather than the simpler curly brackets used almost universally in Pascal. Open arrays are limited to one dimension. There is no double-precision real number type, like Double or Extended in Turbo Pascal. In general, with regard to numeric calculations, Modula-2 is type-poor compared to Turbo Pascal.

But by and large, Modula-2 is a considerable improvement over Pascal. This is especially true when large projects must be broken up into modules. Modules, after all, were what drove the design of Modula and gave it its name. So why hasn't Modula done better than it has? There are two fundamental reasons:

Overdue at the Standard Library

The first of these problems was simple fate, and a reflection of the power of momentum and source code critical mass. Had Philippe Kahn brought a super fast Modula-2 to America in his gym bag in 1982, Pascal would probably be about as popular today as JOVIAL or SNOBOL. Turbo Pascal is now Standard Pascal, regardless of how much those ISO people grind their teeth. When Microsoft anointed the Turbo Pascal Standard with QuickPascal last May, the game was over in the Pascal standards business.

The second problem was totally avoidable. When Wirth released the Modula spec back in 1980, he deliberately made it lean and mean -- about as lean as a language could be without lapsing into anorexia. Pure Modula-2 is all bones and no flesh. Wirth defined the standard data types, modularity mechanisms, control structures, a handful of standard procedures and functions ... and stopped.

Why? Portability, of all things, which in today's world of philosophically incompatible operating platforms isn't even as valuable as your average city planner and drops to the usefulness of a tobacco lobbyist. It's silly to argue with portability freaks and I won't try. But my position is plain: Drop-in portability is impossible. Least-common-denominator portability costs more than it is worth. The smart thing to do is choose a platform and make the most of it. When you must jump platforms, expect to rebuild everything you own from scratch, or you're strapping on a wooden leg and tying one hand behind your back.

One reason that Turbo Pascal caught on so quickly is that it made the most of its operating environment, DOS. It had built-in support for parsing and returning DOS command-line parameters, built-in dynamic string support, built-in 8086 memory addressing and I/O support, and plenty more. Version 4.0 added versatile (if not lightning-quick) graphics and the fabulous DOS unit. The value in such libraries is almost immeasurable: How many of you could duplicate the BGI on your own? 90 percent of you with your hands up, lower your eyes and go to confession.

Programmer man-hours cost more every year. Libraries save man-hours. Lots of them. Once he was satisfied with the basic definition of Modula-2, Wirth should have spent six months or so thinking about the sorts of utility modules that programmers spend the most time building, and come up with specs for a suite of libraries to serve those needs. With a little thought, most utility libraries can be divorced almost completely from the details of their operating platforms. A world-coordinate graphics system, for example, can be made to operate on any raster-based graphics system. Any file system needs library routines to scan for ambiguous file names, and most basic I/O operations can be specified without bowing to the details of a particular platform. Obviously, the details of implementation are closely tied to the platform beneath them, but the spec can with cleverness be made almost entirely platform-independent.

What bothers me most, I think, is that Wirth didn't even try.

So what we've seen is each Modula-2 compiler vendor implementing libraries to meet the needs of DOS programmers, and every one does things just a little bit differently, both in terms of how things are done, and also in terms of what is done and what is left out. The kernel of Modula-2 is so compact and simple that the most visible portions of any given implementation are its libraries, and these are so different from vendor to vendor that each implementation begins to look like a separate language. Modula-2 has become a Tower of Babel, and, sadly, (with a little foresight) Niklaus Wirth could have prevented most of that.

Stony Brook Specifics

Maybe this glum view will scare you away from Modula-2. I hope it won't. If you choose a solid implementation and stick with it, for your purposes the Babel Factor ceases to be a problem. There are two extremely good implementations for DOS these days, and either will serve you well. I've discussed TopSpeed Modula-2 in earlier columns, and I'd now like to focus on Stony Brook.

Editors and writers tend to notice documentation. Mark me well: Stony Brook has by far the finest documentation of any Modula-2 product that has ever crossed my desk. It is well written and well organized, and while I've found plenty of small quibbles (mostly in explanations that do not say nearly enough) the total effect is wonderful.

Stony Brook goes its own way in terms of a programming environment. Instead of centering on a single text file as the focus of a project, Stony Brook's environment centers on a subdirectory that is devoted to a single project consisting of several or many files. "Home base" in the environment is a screen listing of all component files belonging to that project. Modula-2 tends to produce a lot of files, because each module has a separate implementation and definition file. Stony Brook's environment lines them all up vertically, with a highlight bar selecting one as the current focus. By pressing enter, the highlighted file may be edited. By pressing other command keys, additional environment tools may be brought to bear on the highlighted file.

For example, it's often useful to know what modules are used by a given module, and also what modules use that module. Two keystrokes will display any given module's import list (what it uses) or client list (who uses it.) When I had to divide my attention among a great many separate but related module files, I found that this system works quickly and extremely intuitively.

Within the Professional package, there are two complete compilers: One designed for fast compilation, and the other designed to squeeze the last bit of performance out of the code. The global optimizations are impressive, both in terms of .EXE size and speed. An example: The non-optimized .EXE file for JTerm is 32,966 bytes in size. Run it through the optimizing compiler and it shrinks to 16,277. Switching from one compiler to the other is a single command, so you can develop rapidly by using the fast compiler, then generate a user-testable .EXE file by invoking the optimizing compiler.

Exploring the Libraries

Stony Brook's excellent documentation made it lots of fun to explore the libraries. In less than an hour I rigged up a simple interrupt-driven telecomm program that included the ability to capture incoming characters to a text file on disk -- all in about 100 lines of code. The program is shown as Listing One, page 146, and it illustrates several of Stony Brook's utility libraries.

The telecomm session runs inside a bordered window. The window occupies the entire screen in JTERM, but it doesn't have to. The capture file is named "CAPTURE.TXT," and is either created (if it does not already exist) or opened to the end of data when the program is run. Function key F7 begins sending incoming characters to the file, and function key F8 suspends data capture. This allows you to "grab" specific messages of interest on a timesharing system such as CompuServe with minimal command-entry.

CommPort is an example of a superb library not present in any other Pascal or Modula-2 implementation I know of. It implements a suite of interrupt-driven serial port support routines that would be an unholy hassle to implement independently. (I've done it and lost some hair in the process.) Basically, you init a port, turn on interrupts, and check a buffer for incoming characters. Assuming you allocate a large enough buffer and check the buffer regularly, you needn't worry about losing characters. The hidden interrupt machinery saves them in the buffer, where they remain ripe for the picking.

JTerm might have been even simpler had the Screen library supported simple TTY-style output to a window. TTY output is available to the full screen, but within a window I had to fake it by using Screen's various cursor-manipulation procedures. No serious problem, as Listing One will attest. Screen has excellent support of colors and attributes for text windows. Its window borders are color bars rather than line-drawn characters, which imparts an altogether different sort of look to Stony Brook's text windows.

The range of Stony Brook's standard libraries is absolutely stellar, and I've used only the most basic of them in JTerm. Several modules are provided to support all the most useful numeric/string conversions. Another module provides exit procedure support roughly equivalent to Turbo Pascal's. Modules are provided to query time and date from the system clock; to return the command line and the environment variables; to create, remove, set, and search directories; to set and read the logged disk drive and query available disk space; to support the standard Microsoft mouse API; to control the speaker; to shell out to DOS or run DOS programs and commands; to support both long and short heaps; to support variable-length strings (ASCIIZ without a length byte, sadly); and very complete support of long integers and long cardinals, including transcendental operations.

A graphics library is provided, but it doesn't have the range or flexibility of the BGI. Still, for simple graphing and charting it's more than adequate.

Modular Multitasking

One of the most intriguing aspects of Modula-2 is its hooks to multitasking platforms. Both TopSpeed and Stony Brook have similar libraries to support preemptive multitasking of processes. Under DOS this is done out of whole cloth, with a custom scheduler and all the attendant baggage glued onto DOS like antlers on a Doberman; yes, the beast can handle it but it looks funny. Under OS/2 (which both products support) the native facilities of the platform are used. How well multitasking works under DOS I have yet to test, but I will test it and report on it in a future column.

If you need to develop for OS/2, Modula-2 becomes a very attractive language. Products are available immediately (Turbo Pascal's OS/2 schedule has not been announced) and the language itself presents a cleaner interface to multitasking than Pascal or even C. Stony Brook bindings to the OS/2 Presentation Manager SDK are well along and may be complete by the time you read this. Bindings to the Microsoft Windows SDK are already shipping, and while I haven't tried them yet I hope to take a shot at it soon. I don't expect to take back my firm opinion that OOP is the way to develop for both Windows and PM, but certainly Stony Brook can't help but be a step up from the Microsoft SDK under C.

More Modula-2 code next time, along with further experimentation with the Stony Brook compiler.

Products Mentioned

Stony Brook Modula-2 Stony Brook Software 187 East Wilbur Rd., Ste. 9 Thousand Oaks, CA 91360 805-496-5837 QuickMod V2.0 for DOS $95 QuickMod V2.0 for OS/2 $95 Professional Modula-2 (includes DOS and OS/2 optimizing compilers plus both QuickMod compilers) $295

TopSpeed Modula-2 Jensen & Partners International 1101 San Antonio Rd., Ste. 301 Mountain View, CA 94043 415-967-3200 DOS compiler $99.95 OS/2 compiler $195.00

Programming in Modula-2, Fourth Edition Niklaus Wirth Springer-Verlag, 1988 ISBN 0-387-50150-9 Hardcover, 182 pages, $29.95

Modula-2 for Pascal Programmers Richard Gleaves Springer-Verlag, 1984 ISBN 0-387-96051-1 Paper, 145 pages, $23.50

Modula-2 Programming John W. L. Ogilvie McGraw-Hill, 1985 ISBN 0-07-047770-1 Hardcover, 304 pages, $29.95

Advanced Programming Techniques in Modula-2 Terry A. Ward Scott, Foresman & Company, 1987 ISBN 0-673-18615-6 Paper, 300 pages, $21.95 Listings diskette $14.95

Modula-2 Books

I have three shelves full of books on Pascal (and I keep by no means all of them) but only seven books on Modula-2, including a couple of questionable ones. Wirth's own defining volume, Programming in Modula-2, is not terribly useful unless you have nothing else. Too dry, too short, too bereft of useful examples. On the other hand, Richard Gleaves' Modula-2 for Pascal Programmers, while even shorter, has a unique mission: To teach you Modula-2 by way of its similarities to and differences from Pascal. This shouldn't be the only book you use to pick up Modula, but it should certainly be an accessory.

A good, general first book for learning Modula-2 is Modula-2 Programming, by John W.L. Ogilvie. The style is clean, the organization rational, and most of the examples are printed in a dark monospace font that is very easy to read. This book and the Gleaves book will get you there. Once you're there, getting better often comes (in addition to coding like crazy) from reading good code by somebody who knows his stuff. One book to pick up is Advanced Programming Techniques in Modula-2 by Terry A. Ward. This book is mostly code, but it's good code, including set, string, sort, and search libraries, some user interface tools, and a simple expert system. (Publisher details on all books at the end of the column.)

NOTE: I do not recommend the book Modula-2: A Seafarer's Guide and Shipyard Manual by Edward J. Joyce. The type will make your eyes fall out, the writing is indifferent to bad, and it just doesn't contain the quantity and quality of information that the Ogilvie book does.

There's a dearth of good books on Modula-2. Most of the titles I have are between two and five years old. If any publishers reading this have any current Modula-2 titles in print, I'd appreciate seeing them, and perhaps will let the readers of this column know about the better ones.

Halloween II: From Hell to Arizona

Eek! It's Halloween night again, and I just remembered that I finished writing my first DDJ column exactly one year ago today. That makes this my 13th column (double eek!), most appropriate for describing earthquakes and other inappropriate geological behavior. Don't bother looking for outhouses to knock over; the quake beat all of us to it.

Carol and I have quaked our last, however, and as soon as the palace here sells, we are packing up dogs, books, machine shop, radios, and 386 boxes and heading for Scottsdale, where Keith Weiskamp and I will be launching our new magazine PC Techniques this spring. DDJ editor-in-chief Jon Erickson has graciously allowed me to continue this column, and I expect I'll be here again, same time same station ... if here would just sit still long enough for me to make the dash to Arizona!

Write to Jeff Duntemann of MCI mail as JDuntemann, or on CompuServe to ID 76117, 1426.

_STRUCTURED PROGRAMMING COLUMN_ by Jeff Duntemann [LISTING ONE]



MODULE JTerm;

(* Stony Brook Modula-2 --  Last modified 10/31/89 *)
(* by Jeff Duntemann    --  For DDJ 2/90 *)

FROM CommPort IMPORT BaudRate, Parity, InitPort, StartReceiving,
                     StopReceiving, SendChar, GetChar, CommStatus;

FROM FileSystem IMPORT Close, File, Length, Lookup, Reset, SetPos, WriteChar;

FROM Keyboard IMPORT GetKey, CarriageReturn, LineFeed, AltX, F7, F8;

FROM Screen   IMPORT CreateWindow, CloseWindow, Color, DrawBorder,
                     MakeColorAttr, Position, SetCursor, Window,
           WriteString, WriteLn, Xpos, Ypos;

FROM Terminal IMPORT Write, CharAvail, Read;

CONST
  Blink   = TRUE;
  NoBlink = FALSE;
  AsPopup = TRUE;
  NotAsPopup = FALSE;
  CreateFile = TRUE;

VAR
  CaptureOn   : BOOLEAN;
  CaptureFile : File;
  ItsLength   : LONGINT;
  Status      : CommStatus;
  Ch          : CHAR;
  OutString   : ARRAY[0..1] OF CHAR;
  Keystroke   : CARDINAL;
  W           : Window;
  WhiteOnBlue : CHAR;

BEGIN
  (* First set up the window to hold the terminal session: *)
  WhiteOnBlue := MakeColorAttr(White,Blue,NoBlink);
  W := CreateWindow(0,0,80,24,WhiteOnBlue,AsPopup);
  DrawBorder(W,MakeColorAttr(White,LightCyan,NoBlink));
  Position(W,1,0);
  WriteString(W,'\\\\JTERM\\\\  by Jeff Duntemann ',
              MakeColorAttr(Black,LightCyan,Blink));
  Position(W,1,1);
  SetCursor(W);

  (* Here we look for the capture file CAPTURE.TXT; open it if it  *)
  (* exists, and create it if it doesn't:  *)
  Lookup(CaptureFile,"CAPTURE.TXT",CreateFile);
  Length(CaptureFile,ItsLength);  (* Find out how long file is...  *)
  SetPos(CaptureFile,ItsLength);  (* ...and position it to EOF.    *)
  CaptureOn := FALSE;             (* Default to NOT capturing text *)

  (* Next, set up the interrupt-driven serial port and turn it on: *)
  Status := InitPort(0,Baud1200,7,1,Even);
  Status := StartReceiving(0,256);

  (* We check for keystrokes, then check for incoming data: *)
  LOOP  (* EXIT the loop (and the program) on Alt-X *)
    IF CharAvail() THEN
      GetKey(Keystroke);  (* Get a keystroke from the buffer *)
      CASE Keystroke OF
        CarriageReturn:   (* If CR was pressed, send CR *AND* LF: *)
     SendChar(0,CHR(CarriageReturn),FALSE);
     SendChar(0,CHR(LineFeed),FALSE); |
   F7: CaptureOn := TRUE;  |  (* F7/F8 toggle capture *)
   F8: CaptureOn := FALSE; |  (*  on and off *)
   AltX: EXIT;                (* Alt-X quits the program *)
   (* Send any non-command to the serial port: *)
   ELSE SendChar(0,CHR(Keystroke),FALSE);
      END;
    END;
    (* If a char's waiting in the comm buffer, get it and parse it: *)
    IF GetChar(0,Ch) = Success THEN
      OutString[0] := Ch;
      CASE ORD(Ch) OF
         (* This is how we fake TTY backspace: *)
         8: IF Xpos(W) > 0 THEN
         Position(W,Xpos(W)-1,Ypos(W));
         WriteString(W,' ',WhiteOnBlue);
         Position(W,Xpos(W)-1,Ypos(W));
         SetCursor(W);
       END;          |
        13: WriteLn(W);
       SetCursor(W);
       IF CaptureOn THEN
         WriteChar(CaptureFile,Ch)
       END;          |
   10: IF CaptureOn THEN
         WriteChar(CaptureFile,Ch)
       END;
        ELSE WriteString(W,Ch,WhiteOnBlue);
        SetCursor(W);
        IF CaptureOn THEN
          WriteChar(CaptureFile,Ch);
        END;
      END;
    END;
  END;
  (* Finally, we shut down the interrupt-driven input buffer: *)
  Status := StopReceiving(0);
  Close(CaptureFile);
END JTerm.