What Are Registers?
The distinction between “register” and “variable” goes back into computing antiquity. There are specialized meanings in calculators atop this.
Consider the term “cash register” for a moment. What possible connection is there between that and a data storage register?
Go on; I’ll wait while you ponder.
Answer: The earliest models stored a single multi-digit number, totaling the amount of cash that should be in the drawer given the sequence of additions and subtractions made over the course of a day of retail transactions.
We immediately understand that this leads directly into the notion of mechanical adding machines, thence more powerful mechanical calculators, inspirations for the electronic ones we study here.
Even today, we continue to make a distinction between a “processor register” as a super-fast storage word inside the CPU itself, as compared to the slower but bulkier CPU cache levels, plus the still slower external RAM, where we call storage locations “variables” instead.
The Hewlett-Packard Register Design
HP’s first electronic RPN-based calculator — the 9100A — had just three stack registers, sensibly called {X, Y, Z}.1
When they added a fourth in the HP-35 in 1972, they chose to call it T as in “top” rather than rework the scheme to {W, X, Y, Z}.
There were other registers in these primordial HP designs, including the semi-hidden LAST𝑥 register introduced by the HP-45 in 1973.
Skipping forward by another decade, we find things like the parallel set of stack registers that allowed the HP engineers to tack complex number support onto the HP-15C as partial justification for dubbing it their high-end offering, a feature missing from the mid-range 11C. All members of the Voyager family also included a set of numbered storage registers, so-called because they were used with the STO/RCL operations. The quantity of these varied across models, but we need not go deeper here because these details get paved over by further developments, as we shall shortly see.
HP-42S Registers
The HP-42S continues using the basic stack register design above, but unlike on the HP-15C, there isn’t a hidden parallel set of registers to support complex numbers. It supports them directly, within the stack proper.
The HP-42S generalized the concept of numbered storage registers as well, relative to the HP-15C. To save RAM, it started out with 25 allocated, but a program could call the SIZE function to ask for that to be increased up to 100. (Or decreased, to free up RAM!) These are numbered R00 thru R99.
Like the HP-41 series before it, the HP-42S allows an arbitrary number of alpha-named values, but these are considered variables, not registers. It is a somewhat arbitrary distinction, but because it is one HP makes in the manual, we will honor that and say no more on the topic here.
WP43 Registers
The WP43 implements a superset of that, except that you get all 100 numbered storage registers2 from the start. There is no SIZE function to call, there being no need for it.
The set of named registers are:
{X, Y, Z, T} + {A, B, C, D} + {L} + {I, J, K}
This idiosyncratic order is how they are defined in the code, which is then reflected in on-calculator register lists.
The purpose of the first group is obvious from the above, but the second group came when the WP43 project added the SSIZE8 mode. Collectively, these are the stack registers proper.
Next up is L, being the internal register backing the LAST𝑥 function on this calculator. It is an adjunct to the stack, not a direct part of it.
That final group {I, J, K} is overloaded. In the main, you may use them as general-purpose storage registers in user programs, but they also have special purposes in certain contexts. The I and J registers are used for 2D indexing in certain matrix operations. The K register has a few obscure special purposes in the WP43, but I will not list them here as this has changed in the C47 line.
Because these named registers are distinct from variables with a single alpha letter, this stores the X value in two different locations:
STO J
STO ‘J’
The WP43 will also let you STO values into and RCL values from the stack registers and L directly, but because they may change value and move about with each stack-based operation, one is advised to be careful about how one employs this power. The safest option is to switch back to SSIZE4 mode to free up A-D, else each stack push/pop changes their contents; worse, it does so off-screen where you may not realize it is happening.
Local Registers
One of the innovations made in the WP34s project3 is worth special attention. The LocR command allocates up to 100 additional local registers within the current subroutine scope. Proper engineering mandates that one keep this allocation as small as immediate needs dictate:
LocR 01
…compute…
STO .00 ; hold a copy in this first-and-only local register
This not only isolates housekeeping values to each subroutine in the same way that local variables do in more powerful computer programming languages, it can produce a measurable speedup since accessing a register by number bypasses the by-name lookup code the interpreter must otherwise do when using variables instead. If your program has a loop with many iterations, it may be worthwhile to use local registers exclusively. For a program with nested loops (example) you might do this within the innermost loop at the least, trading off the clarity of by-name variable lookups in favor of speed where it counts most.
The LocR feature is per-subroutine, not per-program! This means each LBL introduces a new opportunity for local registers which do not conflict with those from its calling scope. The unstructured nature of RPN programming means locating this boundary requires careful code tracing: merely falling through from one line to a new LBL does not hide preexisting local registers away:
LBL ‘foo’
LocR 02
…perform wonders with these two local registers…
LBL ‘bar’
…continue amazing onlookers with those same registers!
Locality is tied to the call stack, which means you only get the opportunity to allocate a fresh set of local registers following an XEQ referencing the preceding label:
LBL ‘foo’
LocR 02
…perform local wonders…
XEQ ‘bar’
RTN
LBL ‘bar’
LocR 04
…perform fresh wonders…
RTN
This feature implicitly creates the “global” register distinction, being those shared with all programs on the machine. In the early days of programmable calculators, the stringent space restrictions made irreconcilable register conflicts between programs rare. It might take a certain amount of rearrangement to keep everyone happy, but it was generally possible.
This in turn explains why the advent of local registers had to wait for this point in history: when program RAM increases from mere hundreds of steps to practically unlimited while at the same time the number of registers does not increase accordingly, the chance of conflicts must inevitably rise. Programs that “tuck their elbows in” while moving through this crowd are less likely to get spun around and knocked over, metaphorically speaking.
C47/R47 Registers
Sometime after the fork from the WP43 that ended up being called the C47, they lifted the restriction on register naming to allow the remaining 14 English-language letters. They are defined in this strange order in the source code:
{M, N, P, Q, R, S} + {E, F, G, H} + {O, U, V, W}
Part of the reason for this is all the holes shot in the sequence by the history detailed above, yet mysteries remain. I believe I have managed to explain a few of them in the following, but not all. For instance, why are {E, F, G, H} not listed first? No idea.
These additional registers are not strictly general-purpose. I have identified the following uses in the code:
| Register(s) | Usage |
|---|---|
| G, H | scratchpad in 🟦 X.FN FACTORS |
| I, J | same as WP43: matrix indexing |
| K | scratchpad in the solver, graphing, and simulator clipboard |
| L | already used as backing storage for the LAST𝑥 operation |
| M, N, P, Q, R, S | input parameters for statistical distribution functions |
As long as you avoid those sections of the code, you are free to use these registers for your own purposes, with the following understanding: I am aware or no commitment from the developers that this current scope of assignments is guaranteed static forever.
Notice that the last line of that table matches one of the groupings given at the top of this section. It is for this reason that I believe O is safe for use as a general-purpose register. In fact, my strong suspicion is that it was rejected from consideration for all uses because it is too easily confused with zero. It is a junk variable, suited to no better purpose than ad hockery in end-user programs. It is for this reason that I highly doubt the core team will ever produce a new operation design requiring its use, though I could of course be wrong.
I was surprised to learn from this exploration that {U, V, W} are currently unused by the calculator internals, because this pair have many common uses in mathematics. (For instance, alternate coordinates when {X, Y, Z} are already in use, as with 3D vectors where one pair gives the starting location and the other the end, with the magnitude implied by the resulting geometry.) The only reason I have hope that these might not be reassigned at any future time is that they are grouped with O in the code, as shown above. Dare we hope this means that they are also considered junk by the devs, thus ceded to our individual purposes?
The reason register E remains unassigned is easier to guess at. It is the short spelling of EEX on many other calculators, both as a numeric input key and in E notation outputs. Although case distinguishes Euler’s number e, making a separate use of capital-E as a function parameter cannot help but create confusion. This feels like another junk variable, ceded by the devs.
No argument this strong occurs to me for F, putting it squarely on the “endangered” list, in my estimation.
Getting Beyond A-M
This full generalization of named registers on the R47 creates a conflict. How do you refer to register Q from a program? If you try to type…
STO ÷
…with the intent of getting the Q printed in white next to the division operator key, what you will actually get is the storage arithmetic form, STO÷, leaving the R47 still wanting to know what you want to divide X into. The core problem here is that only the overloaded alpha keys A-M have an unambiguous meaning while the R47 is prompting for a command’s register or variable referent. Most of N-Z overlaps the numeric entry keys needed for entering a register number; we have seen above that STO 42 and STO .69 are both legal inputs. The rest overlap the operators.
The solution is to pick these troublesome named registers from the soft menu:
STO 🟦 REG F4
The top-level menu you get on pressing STO or RCL includes a shortcut for {X, Y, Z, T} that avoids the side trip through the 🟦 REG submenu, but for N-S and U-W, this is your only option.
(You may now wish to return to my R47 article index.)
License
This work is © 2026 by Warren Young and is licensed under CC BY-NC-SA 4.0
- ^ Why that few? Because each bit literally cost several US dollars’ worth of transistors and support components in 1968, when it was first manufactured. Ten BCD digits costs roughly forty times that amount, which encourages a stingy economy in the count of full-precision registers. Bits did not become cheap until the development of the LSI digital IC.
- ^ Here we see the distinction between variables and registers breaking down. None of the distinctions made at the top of this article apply to the software-defined WP43, nor its R47 descendant. While there are distinct registers in the hardware underpinning the WP43, R47, or C47 — even in their simulator forms — that has no bearing on what we call “registers” in this article’s context. These calculators use RAM for all ephemeral storage, blurring the lines.
- ^ Ancestor to both the WP43 and the R47.