The Museum of HP Calculators
In the late 1980s, HP was developing a new series of extremely powerful calculators that needed a new programming language. For earlier calculators, memory was very expensive and bulky and the challenge for firmware writers was to pack a set of scientific functions into very little space. They used machine language exclusively and carefully refined and re-refined their algorithms until they fit. However, as memory became denser and less expensive, the major challenge shifted away from saving space to implementing and testing the many sophisticated functions that newer calculators contained.
HP examined existing languages such as BASIC but found them to be unsuitable both because of their limitations and their resource demands which were still rather high for pocket devices. Instead, HP combined elements of RPN, Lisp and Forth and came up with a language called Reverse Polish Lisp or RPL. From Lisp, RPL inherited concepts such as atomic and composite objects, temporary (lambda) variables, garbage collection, the ability to pass unevaluated objects as arguments etc. and from Forth, RPL inherited threaded execution and RPN notation with an unlimited stack.
RPL was developed both for HP's internal programmers and for calculator users. It was first used internally in the HP-18C. Since this model was non-programmable and algebraic, RPL was not presented to the user. The HP-18C is listed here as an "honorary member" of the RPL calculator family because it was the first calculator to be programmed internally in RPL, but most calculator users wouldn't think of it as an "RPL calculator".
The HP-28C was the first calculator to be both internally programmed in RPL and to make RPL available to the user. While the needs of both types of programmers overlapped, they also had their differences. Most users expect their calculators to be relatively "bullet-proof". Many calculator users learn by experimentation so programming errors should be handled gracefully without any loss of data.
Internal programmers needed speed and efficiency. Their job was to carefully debug their code and reload their calculator memory when bugs occurred in development. Thus, HP's programmers were allowed to access powerful and quick aspects of RPL that were off-limits to the casual user. As an example, the user-visible RPL command for multiply checks that there are two arguments on the stack, checks their types, and then calls an appropriate multiplication routine for real numbers, complex numbers, integers etc. An HP programmer who is sure that the arguments will always be correct at some point inside a program can call the appropriate internal multiplication routine directly. If the programmer is wrong, the calculator will behave unpredictably and might need to be reset and reloaded.
There are also internal commands which provide combined operations for saving space and time. A few examples of added functions for binary integers are: #1+ which adds 1, dup#1+ which duplicates x on the stack and then adds 1, and #-#2/ which subtracts y from x and divides by 2.
Eventually, the term System RPL was used to describe the RPL system as available to internal programmers and User RPL was used to describe the safe subset with parameter validation etc. User RPL was documented in the manuals and could be entered in the calculator via keystrokes. System RPL programs were meant to be created with HP's RPL compiler and then downloaded.
While originally meant for HP's programmers, HP left a window to System RPL open via the SYSEVAL command. SYSEVAL simply calls the address (user binary) provided in the first level of the stack. Brave users who weren't deterred by memory corruption experimented with SYSEVAL and unlocked many of the secrets of the RPL calculators. Later, James Donnelly, an HP-48 developer, wrote "An Introduction to HP 48 System RPL and Assembly Language Programming." It was not an HP publication but was done with the company's blessing and they allowed their HP-48 development tools to be distributed on a disk that came with the book. This opened to door to faster more powerful programming.
(User) RPL calculators have the following characteristics:
RPL stacks are dynamically allocated and deallocated as needed. This allows for arbitrary length expressions with no need to approach them in an optimal manner. It also allows modern structured programming because unlimited numbers of parameters can be passed to subroutines via the stack and subroutines may be nested indefinitely.
The large stack requires a new labeling scheme so level 1 replaces what used to be called X, level 2 replaces Y etc. SWAP replaces X⇔Y, LAST replaces LAST X, DROP effectively replaces CLX and additional stack manipulation commands were added.
The first RPN calculators understood numbers and programs. These two information types were kept separate by having a "program mode" and a "run mode". The evolution of RPN calculators added more modes as more types were added. (For example, alpha mode, complex mode...)
RPL calculators do away with these modes while expanding the types. Everything is an object and there are operations that act on simple and complex objects. The object types that RPL calculators understand include:
Objects are entered on the command line, parsed and stored on the stack. At any point, an RPL stack may contain an arbitrary number of arbitrary objects. Rather the sliding a switch to "Program", the user enters the program onto the stack just like any other object.
Instead of numbered registers, RPL calculators have variables with alphanumeric names. Any of the above data types can be stored on the stack or in a variable. To store an object on level 1 of the stack, the user presses the ' key, types a name and presses STO. This is a true postfix (RPN) notation. (RPL does not require the user to include the closing quote so for example 'NAME STO is the same as 'NAME' STO.)
RPL calculators have RCL keys but they are not often needed. To recall a variable, the user can just enter the name without the ' which causes it to be evaluated. In the case of a data type, evaluation of the name recalls the data. In the case of a program, evaluation means running the program.
RPL is stack-based but users can enter algebraic expressions such as (A+B)*C. For example, a user might set the values of A and B by entering 25 ENTER 'A' STO 10 ENTER 'B' STO. Then the user can enter the algebraic expression '(A+B)*C' and press EVAL. The calculator would evaluate as much of the expression as possible leaving the algebraic object '35*C' on the stack. The user might then multiply it by 10 (10 *) leaving '35*C*10' on the stack and then enter 5 'C' STO and EVAL resulting in 1750. This method allows algebraic expression with the interactiveness of RPN.
RPL calculators can do math even when some (or all) variables are not defined. In the above example '35*C' was multiplied by 10 leaving '35*C*10'. The user might simplify it further by pressing COLLECT which would result in 350*C. Additional symbolic features of RPL calculators allow for algebraic isolation as well as differentiation, integration etc. This allows many complex problems to be solved completely in the calculator.
RPL calculators can symbolically compute integrals and derivatives for many expressions. For example, the user can enter the expression 'TAN(X^2-1)+SIN(X)', indicate differentiation with respect to X by entering 'X' and then press d/dx. The calculator responds with '(1+SQ(TAN(X^2-1)))*(2*X)+COS(X)'. The HP-28C/S could also integrate symbolically but only for polynomials. Other expressions required numeric integration which was also built-in. Later RPL calculators provide a wider range of of functions that can be symbolically integrated.
RPL calculators require a somewhat different way of thinking about programs. In previous calculators, programs were stored in a separate space with its own rules. In RPL calculators, a program is an object which is delimited by << and >>. To enter a program, the user can press << followed by the sequence of instructions and then ENTER. Pressing the EVAL key runs the program. (This process also consumes the program and any arguments it requires but there are ways to bring the consumed objects back such as the UNDO command.)
The program can also be saved in a variable just like any other object. For example, with a program on level 1, the user can enter 'PROG1 STO. The user can then execute the program by typing PROG1, or pressing the PROG1 label that automatically appears in the VAR menu. The program can be recalled to the stack by typing 'PROG1 RCL, and the user can then edit it on the stack by pressing EDIT. Alternately, the user can edit the program in the variable by pressing 'PROG1 VISIT.
RPL programs make use of named variables (both local and global) making them more readable and, of course, variables can hold any object type including arrays, expressions etc. In place of conditional skip instructions, RPL supplies a full complement of high-level language features including IF/THEN/ELSE, FOR/NEXT, DO/UNTIL, WHILE/REPEAT as well as error trapping. Since RPL calculators have multi-line displays, a DISP instruction allows data to be displayed on any line.
The following program computes the sum of the squares of the numbers 1 through 10:
Program: Comments: << 0 Start with a sum of zero 1 10 FOR i For i = 1 to 10 (start end FOR loop_var) i SQ + Add i2 to the sum NEXT Add 1 to i and loop back to the FOR i >>
Here is the modification of the program above that takes the range of numbers from the stack. This program demonstrates the use of a local variables fst and lst. The variables fst and lst are visible only to the procedure that follows their definition. Using lower case for local variables is a common convention but is not required. (The variable i created by the FOR function is also local.)
Program: Comments: << -> fst lst Save the #s on the stack in local variable fst and lst << This is the block for which fst and lst are defined 0 Start with a sum of zero fst lst FOR i For i = fst to lst (start end FOR loop_var) i SQ + Add i2 to the sum NEXT Add 1 to i and loop back to the FOR i >> >>
After typing this program into the stack, it can be saved in the variable SSQS by pressing 'SSQS STO. Then the start and end values can be entered on the stack and the user can either invoke SSQS by typing SSQS or choosing it from the USER menu.
Here is a modification of the above program that deals with the case of the the user entering the first and last values in reverse order.
Program: Comments: << DUP2 Duplicate the two inputs because > will consume them IF > THEN SWAP END If the order is reversed then swap them on the stack -> fst lst Save the #s on the stack in local variable fst and lst << This is the block for which fst and lst are defined 0 Start with a sum of zero fst lst FOR i For i = fst to lst (start end FOR loop_var) i SQ + Add i^2 to the sum NEXT Add 1 to i and loop back to the FOR i >> >>
Here is a modification of the above program that does the same thing except that the conditional is expressed in a more "postfix way". The only difference is on the second line. This one computes the test > which results in 1 or 0, places a procedure on the stack (containing only SWAP) and then executes IFT which executes the procedure on level 1 of the stack if the condition on level 2 is true (non-zero).
Program: Comments: << DUP2 Save the arguments (> will consume them) > << SWAP >> IFT If the order is reversed then swap them on the stack -> fst lst Save the #s on the stack in local variable fst and lst << This is the block for which fst and lst are defined 0 Start with a sum of zero fst lst FOR i For i = fst to lst (start end FOR loop_var) i SQ + Add i^2 to the sum NEXT Add 1 to i and loop back to the FOR i >> >>