Programmer's Corner: TIPI: A Small Programming Language for Small Computers

TIPI 2.0 is a small structured programming language written to run on small DOS-compatible portables like the HP Palmtops.

By Kent Peterson

Back when I was a tiny little programmer, I dreamed of having a tiny little personal computer I could take with me anywhere. I pictured myself programming on the shores of a secluded lake, a high-tech hermit coding the Great American Program.

Well, life doesn't always work out exactly the way we plan. I got married and acquired two sons, a lazy cat and a whole bunch of computers. I finally got my tiny little personal computer and developed TIPI -- a programming language designed from the ground up for palmtop programming.

 TIPI: A "Threaded Instruction Processing Interpreter"

 TIPI (pronounced "tepee") is a small, structured programming language that runs on any MS-DOS computer, but has been optimized for palmtops. It was originally written to run on the very small Atari Portfolio. I have since ported it over to the HP 100LX and optimized it for the Palmtop.

The name TIPI is an acronym meaning "Threaded Instruction Processing Interpreter." I chose to make TIPI an interpreter rather than a compiler to avoid having a separate compile and link stage. (For an explanation of programming terms see the "Non-programmer's introduction to programming" on this page) I wanted TIPI to be able to let programmers quickly try various ideas. The term "threaded" refers to the way the instruction "building-blocks" link together within the interpreter. The TIPI interpreter takes up about 12K of disk space and TIPI programs are similarly small. TIPI requires about 100K of RAM to run in and is thus well-suited for palmtop computers such as the Poquet PC or the HP-LX series.

 TIPI was created to be:

 Efficient -- TIPI is based on a multi-stack interpreter. This lets numeric and string data pass quickly and consistently between instructions, greatly reducing the need for complex internal parsers and temporary variables.

 Structured -- TIPI is very modular. All TIPI instructions have one entry and one exit point. All instructions are clearly defined and perform most of their interaction via stacks. TIPI does not have the spaghetti-generating GOTO instruction.

 Small -- TIPI is not a language for creating huge programs. The basic philosophy of TIPI is that problems should be broken into small, discreet, easily understood components. In threaded languages, performance degrades as the threads get longer and longer threads are what you get when you write big programs. To keep TIPI reasonably fast, the TIPI interpreter is limited to processing programs of 32K or less.

 Clear -- A program is more than a set of instructions telling a computer how to solve a particular problem, it is also a document precisely describing a set of problem solving procedures. A programming language needs to be comprehensible to the computer the program will run on and clear to the programmers who write the code and future programmers who may have to modify it. With TIPI, every effort has been made to make the language consistent and easy to read.

 Extensible -- Rather than having a complex system of libraries and "includes," TIPI lets you use a text editor to copy commonly used instruction definitions into your code. This means you can define your own instructions in TIPI, so you can add the features you've always wanted in a language.

 A good way to think about this is to think about how the English language is extended by the addition of jargon. Here's an example. Suppose you didn't know what RAM is. You ask someone and they tell you "RAM means Random Access Memory" or "RAM is memory that forgets everything when you shut your computer off". Let's say that one of these definitions makes sense to you and you remember it. From now on when you talk about computers, you can use the word RAM because you know what it means in terms of things you already knew. You use the jargon as a quick and easy shorthand for something more complicated. You have just extended your vocabulary to include the word RAM. Where English has words, TIPI has instructions. In the same way that you add jargon to your English vocabulary by defining a new word in terms of words you already know, you extend TIPI by defining new instructions in terms of existing instructions.

Here is a small chunk of TIPI code defining a new instruction called WAIT, which causes execution of the program to pause until the user presses a key:

 define wait

 begin key until


 Once you have defined WAIT, you can use the WAIT instruction anywhere in your code.

 TIPI contains over 170 built-in instructions for the programmer to use as building blocks. These instructions fall into eleven categories. Table 1 lists these categories and their associated instructions.

 A Bit of History

 TIPI borrows from a computer language called Forth. Forth was invented in the late 1960's by Charles Moore. I did a fair amount of work in Forth in the early 80's and was very impressed by its elegance and power. Forth tends to be cryptic. BASIC, on the other hand, is regarded as being quite legible, but lacking in structure and power. TIPI is my attempt to combine some of the best elements of these two languages together with neat ideas from AWK, C and Assembly language.

In December of 1992 I began work on TIPI. I wrote the TIPI interpreter on a desktop PC using a shareware compiler called ASIC. ASIC is the work of David Visti and his compiler takes code that is "almost BASIC" and compiles it down to a very small executable. I chose ASIC over other BASIC compilers such as QuickBasic or PowerBasic because it produces such tiny EXEs. I could have chosen to write TIPI using C, Assembler or Forth but I had become intrigued with ASIC and wanted to use it in a full development project.

 Once I had the core interpreter running, I began adding features to the language. Like Forth, TIPI has a numeric stack and does all math operations using a syntax known as Reverse Polish Notation or RPN. RPN was originated by Jan Lukaciewicz, a Polish mathematician. While I knew that RPN can be confusing for new programmers, I also knew that RPN is efficient and unambiguous. Furthermore, using RPN keeps the interpreter fast and simple while making all instruction calls consistent.

TIPI differs from Forth by having an additional stack for string values. In addition to grafting some of BASIC's legibility onto TIPI, I wanted my language to inherit some of BASIC's wonderful string handling capabilities. By giving TIPI a string stack, I was able to create RPN equivalents of MID$, LEFT$, RIGHT$ and other useful string functions. I also created some new functions such as the faintly AWK-like PARSE$.

Throughout the process of developing TIPI, I had to battle the tendency to add features to the language. I wanted my language to remain small enough to fit on the Portfolio, but still be powerful enough to let me write "real" programs. This made some trade-offs necessary.

No Floating Point

 Floating point math is one of the things that I consciously left out of TIPI. Forth purists will tell you that you don't need floating point math. I'll tell you that TIPI doesn't have it because it would make TIPI too big and too slow for what I wanted (but the Forth guys are basically right). I have seen 3-D animation engines written using only integer math and two friends of mine have coded an entire hockey game (including player physics and AI) without using any floating point code.

 The TIPI data stack and all its math instructions work with long (32 bit) signed values. This means TIPI can handle integers from -2,147,483,648 to 2,147,483,647. This is a very wide range and with some creative scaling, you should be able to handle almost any problem that you thought would require floating point math. Of course, I am not a fanatic on this point. If I get a huge number of registrations accompanied by requests for floating point math, I will most probably add it to a future version of TIPI.

 Once I had TIPI running and stable, I experimented with various runtime file compression programs. I tested LZE , PK-Lite , Diet and Compack. Of these, Compack gave the best compression, so I used it to make TIPI even smaller.

 Eek! Bugs!

 Throughout most of 1993, I worked nights and weekends on TIPI. I found myself using TIPI for more and more projects, not just on the Portfolio, but on my desktop PC as well. I also found the big drawback of writing programs in your own language: when your program has a bug, you don't know if the problem is in your program or your language. Of course, I had my share of both types of bugs and tracking these wee beasties down caused me to develop the TIPI debugger.

 The debugger is a very simple debugger that does a surprisingly good job of tracking down problems. Since TIPI code is interpreted, debugging statements can be dropped into the code anywhere to give you a kind of X-RAY of the program as it runs. The debugger consists of an informational window that may be turned on and off via the instructions TRACEON and TRACEOFF. The position of the window is controlled by the instructions TRACEROW and TRACECOL and it can be placed wherever it is convenient. The window also pops up automatically in case of a fatal error to give you a "post-mortem" dump.

 The TIPI debugger displays the contents of the data and string stacks, the last instruction executed and the command TIPI is about to execute. Also, by use of the TRACEVAR instruction, you can tell the debugger to display the contents of any variables. With the debugger displayed you can step through your code until you find the source of your particular problem.

 The debugger has saved me literally days worth of work and on one occasion helped me find a bug not in my TIPI code, nor in my language but rather in Dave Visti's ASIC compiler! A bit of detective work and some E-mail to Dave cured the problem, but it sure proved to me the value of a good debugger.

 TIPI's shareware debut

 In the fall of 1993, I released TIPI as shareware. I had spent much of the summer working on the manual for TIPI, writing sample programs and creating a small integrated environment for programming which I called "The TIPI Programmer's Workbench". I uploaded TIPI to several forums on CompuServe including the Palmtop Forum and the IBM Programmer's Forum. I also uploaded it to the HP-Handhelds forum because I knew that HP had a little DOS palmtop.

 My timing coincided exactly with the virtual death of the Atari Portfolio. The Portfolio, which had been one of the first DOS palmtops, had been basically abandoned by Atari. In the meantime, Sharp, Zeos, HP and others had come out with more powerful palmtops. I vowed I would stick with the Portfolio if even one Portfolio user registered TIPI. Not one did.

 Meanwhile, the HP folks really seemed to like TIPI. Its RPN syntax wasn't strange to anyone who grew up with HP calculators. A lot of folks downloaded TIPI from the HP forum and I got some complimentary E-mail and some intelligent questions. When I got my first registration (from an HP owner, of course!) I raided the family savings account and bought myself a brand new HP-100LX.

 TIPI worked fine on my new toy, but the more I played with the HP, the more I wanted to do with TIPI. A lot could be added by using TIPI's built-in DEFINE instruction, but some of what I wanted to accomplish would require some low-level coding changes.

HP Palmtop spurs further development

 From HP's forum on CompuServe, I got the form that would let me apply to become an HP Software Developer, filled it out and sent it off.

 A few weeks later I got a very fat envelope from HP. This was the Rosetta Stone of the HP hacker, the HP-100LX Developer's Guide. This contains all kinds of useful information such as what interrupts do what as well as cool semi-useless stuff like what the HP internal codenames for the palmtops are. (Editor's Note: A new 100LX/200LX technical reference manual will be available soon. Check the new issue of The HP Palmtop Paper for details.)

 I set to work on some serious hacking. First I added control of the software power toggle and battery timeouts and then I began to dig into the graphics routines.

Boy, I'm Sure Glad I Didn't Quit My Day Job

 Meanwhile, the registrations weren't exactly pouring in. In fact, they weren't exactly trickling in either. Sure, I got a few registrations (all from HP owners by the way), but by and large TIPI had not made a huge impact on my bank account. If you figured in what I'd spent on my new HP, my increased CompuServe bill and all the coffee I was drinking, I was way in the hole. Still, I was having loads of fun. I checked with other shareware authors, especially language authors, and I found out that most of these folks aren't getting rich either. Those that are doing the best are the ones who stick with it, give their users an incentive to register and do a lot to pitch their product. Those all sounded like good ideas and they all sounded like work, so I poured myself another cup of coffee and got back to programming.


 One of the requests I'd gotten from users was to give TIPI the ability to make stand-alone EXEs from their programs. Under the existing method, you would run a TIPI program (for example HANGMAN.TPI) by typing TIPI HANGMAN.TPI at the DOS command line. If a user wanted to share a TIPI program with a friend (or sell it), he had to include the TIPI.EXE with his program. If TIPI could make its own EXEs, then users could distribute just those EXEs. Furthermore, running the program would be as simple as typing HANGMAN at the command line.

 Because the TIPI interpreter is so small, I didn't have to rewrite my entire program to make a compiled version of TIPI. Instead I created a slightly modified version of the TIPI interpreter that could be bound together with a user's source code to create a single EXE. Using this method, I got the MakeEXE function up and running in one evening.

I put this feature into the shareware version of TIPI, but I added one small element -- a registration code. When the MakeEXE is used in an unregistered version of TIPI, the EXE will be made, but it will display a small shareware notice before the user's program runs. By setting things up in this way, I let the users see the usefulness of the MakeEXE function while also giving them an incentive to register.

Help System

 I also went to work on improving the workbench and the TIPI user's manual. I knew that TIPI would be new to everyone, so they would need good reference materials. The HP-100LX has a built-in database engine, so I created a help file in that format for HP users. I also knew that some people were using TIPI on regular PCs, so I set to work on writing a PC-based help system for TIPI. Naturally, I chose to write this in TIPI.

Since the help system had a lot of text, this quickly grew beyond the 32K limit I had imposed on TIPI, a bit of a quandary. I knew that my original reasons for keeping TIPI small were good and that if I went against my instincts I would soon have something as bloated, ugly and slow as Microsoft's Foundation Classes (heh, heh, just kidding there, Bill). Still, I had to do something.

I fell back on my core belief of busting a problem into byte-sized chunks. The TIPI workbench is a batch file that calls a TIPI program. The help system takes that one step further by calling multiple TIPI programs. The whole thing works, no single chunk is larger than 32K and the help system itself serves as an example of how to write a fairly large program in TIPI.

A non-programmer's introduction to programming

Table 1 -- TIPI Instructions