Enter RPN

R47: Programming Model Differences
Login

R47: Programming Model Differences

Motivation

The R47 is considered to be a spiritual descendant of the HP-42S, especially when it comes to the programming model, yet even for short programs, it is possible to run into multiple significant differences. The primary reasons for this are:

  1. While the loosely-organized group of hobbyist developers behind the R47 project did not have a Silicon Valley R&D budget or a marketing department’s mandate to get a product out by some arbitrarily selected date, 14 years is a lot of runway.

    (The first production run of the R47 came out in late 2025, but its software roots go back at least as far as the first public release of the WP34s software in 2011, possibly earlier for the planning and initial R&D.)

  2. Technology advances globally, independently of local development efforts. The R47 and its ancestor projects benefit from huge improvements in off-the-shelf microcontroller technology, swamping through sheer brute force and overall improved efficiency what HP pushed out into the world via an expensive custom silicon development effort. Faster and more efficient hardware then spurs the software developers on, giving the R47 developers third-party efforts to leverage like IBM’s open source decNumber library.

  3. Different minds produce different results. We see this even in the outputs of the old HP Calculator Division, whose designs are mutually incompatible to various extents. It is therefore reasonable to expect third-party outsiders to produce additional differences while they are forging ahead with all the above.

What follows is a catalog of known operational differences affecting RPN programming.

Disclaimer

I do not own an HP-42S. Everything I write below is actually in reference to Thomas Okken’s Free42 version 3.3.𝑥. I am aware that it is a clean-room reimplementation, not based on HP firmware, but because it is sufficiently well-regarded to be used as the basis of SwissMicros’ DM42n and not an extended design like his own Plus42, I am presuming that Free42 is a faithful guide to the historical hardware behavior.

File Formats

The single biggest sign I can show you that the C47/R47 is evolutionarily independent is by giving you a peek into their byte code formats.

The HP “raw” byte code scheme originated with the HP-41C, released in late 1979. A brief study of this reference card shows that all 256 leading byte values allowed in the code have an assigned meaning, but one must look deeper to see the full truth. The scheme uses several runs of 8 to 16 codes for a single basic instruction each; in five of the cases, the leading byte code difference merely encodes the length of the following argument. These should be collectively counted as five instructions with a variable-length argument each, not as dozens of independent instruction byte codes. Another big factor ballooning this count is that there can be 2 or 3 different ways to encode a given op depending on details of its argument type and size.

Now, to be fair, all this is partially counteracted by the expansion ROM calling scheme, shown on that chart at codes 0xA0-0xA7. This scheme offers another 2048 possible subroutine calls: 32 XROM module IDs × up to 64 entrypoints per module.

There was a limit of 4 installed at a time, so until the CX came out in 1983,1 there was a practical limit of 256 total entrypoints above the built-in instruction set. Since few 41-series ROMs used even a large fraction of their total entrypoint space, a kitted-out HP-41C might have half its total instruction set in XROMs, the rest built-in.

The HP-42S dropped the external expansion option, justifying it by incorporating a large chunk of the available XROM calls into its stock firmware. They then extended the 41C scheme by exploiting a limitation of the HP-41C alpha string scheme, allowing another 128 ops beyond this fixed-in-place subset of the old XROM scheme. It came to a rough wash in terms of available operations.

Free42/Plus42 then came along and extended both schemes. Some of these extensions are encoded as XROM entrypoints that HP left undefined in the HP-42S, and others were added via a third extension mechanism atop that of the HP-42S. The total presently defined comes to roughly 450, with a maximum of around 2500, all factors considered.

I tell you all that as prelude to this: if the C47/R47 was a true descendant of the HP-42S, it would’ve followed suit. Instead, it defines its present ~1500 programmable ops using a wholly different — and more rational — scheme, with expansion space left to allow tens of thousands more. Projecting present trends, a fully-maximized far-future R47 descendant might cap out at 15k programmable ops.2

Not only does an R47 not share the HP “raw” byte-coding scheme, it won’t even read those files directly. It requires the aid of an intermediary like rejig to perform the translation.

The R47’s actual byte code format is called P47, and as the author of rejig, it is my considered opinion that P47 is far better designed than the old HP raw format. There are far fewer special cases baked into it, leaving no cause to resort to trickery to reach this spec-fic future. Even after taking into account the fact that rejig uses a 1:1 translation scheme based on P47 byte codes as its IR, easing the encoding/decoding burden, I estimate there to be about twice as much code in rejig dedicated to handling all the special cases involved in raising its HP raw format to the IR form, and lowering it.

While evaluating these experience-backed claims, keep in mind that the R47 offers roughly six times as many programmable ops as the subset of HP-42S family ones rejig presently knows about. If twice as much code to handle one-sixth the op count does not constitute a key difference between the two, I don’t know what does.

The P.FN Menu and Nesting

The R47 brings up its P.FN menu immediately on entering 🟧 PRGM mode, unlike on the HP-42S, where one must bring up its equivalent 🟧 PGM.FCN explicitly each time it is needed. I believe this to be a considered design choice on HP’s part rather than an oversight. Given the low-res 2-line dot-matrix display on the 42S, the designers naturally wished to use both lines for showing program code whenever possible. Contrast the larger and higher-resolution R47 screen, which allows room for a 3-line menu plus 7 lines of program code simultaneously. Different starting constraints lead to different outcomes.

What is more significant in everyday operation, though, is that the R47 handles nested menus with far greater facility, which plays into how one uses this particular menu.

It is common when writing a program to need to open another menu to access a required function. If this need is a one-off, with none of the other items on that menu needed at the moment, pressing EXIT on the R47 returns you to the P.FN menu page you were previously viewing, popping both the literal on-device stack and your mental one as you return to your prior programming flow.

Contrast the HP-42S, where opening any other menu atop PGM.FCN causes a subsequent EXIT keypress to return you to the default no-menu 2-line program view. Now you must burn mental cycles digging back down to where you previously were. This is true even for closely related menus like 🟧 FLAGS, the practical upshot of which is that one must frequently re-enter the PGM.FCN menu, manually.

There is a brief discussion of nested menus in the HP-42S manual, but it is treated as a synonym of submenus à la 🟧 PGM.FCN X?0. The R47 generalizes this without conflating the concepts: EXIT will not only return to the parent menu from a submenu as on the 42S, it can also track back to an unrelated menu that happened to be on-screen at the time you called it up. The limit appears to be 8 levels of nesting before it begins forgetting whence you came in LIFO fashion, as with the R47’s default 8-level stack.

While all this technically amounts to an HP-42S “incompatibility,” it is difficult to imagine one who would not prefer the R47 design if given the option.

More Programming Functions

Scrolling through the 4 lines of the HP-42S PGM.FCN menu gives access to its 24 programming-related functions.

The R47 also offers 4 chained pages of programming function menus called P.FN thru P.FN3, but because each may have up to 18 items, we have a potential for 3× the function count. Because of certain redundancies and gaps, the actual count is a bit under 2× at the top level. We must then add 6 more looping operations found under the R47’s LOOP sub-menu to those few present in the 42S.

Two of those top-level PGM.FCN items on the HP-42S expand to give access to its 12 tests, identical to those offered by the HP-15C: half comparing X to zero in various ways and half comparing X to Y using the same set of six relational operators. The R47 implements all that plus comparisons against:

…and then another 37 tests (currently) besides, some of which are also in the HP-42S, but not all.5

This might then lead us into looking into the FLAGS menu where similar things occur, but I think the point is sufficiently well made: R47 is no mere clone of the HP-42S.

EDIT Mode

One of the new buttons we get on the R47’s P.FN and P.FN3 menus relative to the PGM.FCN equivalent on the HP-42S is 🟦 EDIT, allowing adjustment of an existing program line’s argument, avoiding the need for outright replacement. You cannot change the function called, but you can change its operand, label value, etc.

A long-press of the ⬅︎ key can invoke EDIT mode,6 but only in certain contexts. This avoids the need for the 🟦, and it works while on P.FN1 or P.FN2 which lack an EDIT in the upper left corner. The thing you must beware of is that it does not work in other menus, including those closely related like FLAG! Any press of backspace there simply deletes the selected line. Long-pressing in the simulator in these contexts allows the host OS’s key repeat feature to take effect, potentially resulting in complete destruction of a short program if you have set the keyboard repeat rate faster than your dismayed reaction time.

(Yeah, go on, ask me how I know! 😳)

Alpha Input Mode

This is another case where broader functionality has special significance in program entry mode:

The Alpha Register

Setting aside the AIM differences above, there’s one huge difference remaining: on the HP-42S, there is a singular “alpha register,” whereas on the R47, every register and every variable is capable of storing alpha strings. This is not a mere convenience; it has a huge impact on how any program dealing with text I/O is best written.

The first thing to realize is that ops on the HP-42S dealing with the singular alpha register frequently lack an explicit referent. For example, the HP-42S op ATOX has its referents right there in the name: alpha register to X, making it a command, not a proper function.

On the R47, that becomes the α→𝑥 op, but this is not a mere naming difference. The key distinction here7 requires the R47 version to take its referent as an argument, being the name of the register or variable whence comes the first letter to turn into a character code. There is no implicit single source of that code on the R47 as on the HP-42S.

The second thing we want to focus on here is that many of the limitations of the HP-42S alpha register have been lifted in the R47. Again, this ends up not being a pure benefit, because there likely exist HP-42S programs that depend on these limitations, which fail when ported to the R47 because it does not drop data where the HP-42S did when running it up against these limits.

Third, this distinction can cause HP-42S ops to “disappear” entirely on the R47. What is actually happening is that the HP-42S had special-casing for the alpha register which is unnecessary on the R47 now that any register or variable can fill its role. An excellent example of this is the ASTO/ARCL pair, which simply become data type dependent cases of STO/RCL on the R47. This not only reduces the mental burden — one thing to learn — it means there are fewer surprises in how these merged cases operate.

Case in point, ARCL surprisingly appends to the alpha register, whereas to get that with an alpha string in X on the R47, you’d say:

STO+ ‘VarName’

And no, that’s not a typo! From the perspective of the stack, ARCL/ASTO on the HP-42S work backwards because they use a different target/source. This is what I mean about having only one set of behaviors to memorize. It is my humble opinion that the HP-42S alpha features are a hot mess, which have been made properly orthogonal in the R47. The sole difference between storing/recalling real numbers and alpha strings on the R47 is the type and size of the data being copied.

All this having been said, Didier Lachieze added a large number of HP-42S compatibility ops that landed in C47/R47 release 00.109.03.03b0 — early June 2026 — dominated by those for emulating this singular alpha register in terms of a user-selected named global register. It defaults to the lightly-overloaded K register, and when used this way, it obeys HP-42S length limits and such. The old rejigα hackery has been removed starting with rejig version 0.27.0; if for some reason you continue to find uses for this unlovely feature set, the source remains available. 🤓

These new ops are compatibility aids, not a retrenchment to a singular alpha register as the proper way of writing R47 programs. They help rejig provide faithful 1:1 conversions, and for that their addition is most welcome, but new programs should be written using the improved R47 mechanisms.

Local Labels

The HP-42S offers local GTO/XEQ labels, distinguished by being single-character and limited to A-J, a-e.8

The R47 expands the HP-42S limits to A-M, a-l. That gives us 25 local labels, nearly as many as the HP-32S gives us in its more limited A-Z global label scheme.

You may now be wondering why the R47 does not go all out and give us the full English alphabet, upper and lower.

Evening up the lists to A-L, a-l could be done as a matter of symmetry, but why?

This is a hard limitation of the underlying firmware,9 owing the way the allowed single-letter labels are packed into an 8-bit value. For this limit to be lifted, the R47 developers would need to split the label set awkwardly, akin to what they did to extend the HP-42S register set.

Variables and Named Registers

Despite offering local labels, neither the R47 nor the HP-42S offer named local variables. Internal program bookkeeping is liable to pollute the global namespace via named alpha variables or the registers, which includes the stack.

At the same time, important differences exist. We relegate most of that to the prior links, but to take a single example, this is legal on the R47:

STO M

Lacking an M register, the HP-42S disallows that.

This generalization of stack registers to include other types of special and general-purpose named registers means the HP-42S “STO ST Z” syntax drops the ST bit. Down at this level of the R47, registers are not distinguished by purpose, but purely by name.

Numbered Registers

The R47 allows immediate access to all 100 two-digit numbered global registers, R00-99.

Because memory is far more constrained on the HP-42S, the default limit is 25, giving R00-24 until you call 🟧 MODE ⬇︎ SIZE to change it.

The R47 adds the LocR function to allocate a block of subroutine-scoped10 numbered registers, which partially gets us around the lack of named local variables. They are prefixed with a . to distinguish them:

LocR 04
STO .03

This allows as many as 100 additional registers, which disappear on returning control to the subroutine’s caller via RTN or falling off the END.

Single-Stepping and Program Line Navigation

The R47 has its single-stepping keys in the same location as on the HP-42S, though this is not obvious from a first look at the bezel. HP preserved the abbreviations from the 10-series machines — SST/BST — which are admittedly somewhat cryptic, but at least familiar to their user base. The R47 manages to outdo them in terms of obscurity by using little pictograms: ☰⬆︎/☰⬇︎. In the short time of the hardware’s availability in production form, I have seen them missed entirely and confused with page up/down keys.

The “why” of it all will take some explaining.

From normal mode, these 🟧-shifted keys functions operate as on the 42S: step forward/back from the current program line, previously set. That can be by direct selection in PEM, by GTO from normal mode, or by relying on default run/stop behavior, where the program you are trying to debug likely just ran to completion and now has its program pointer back up on the entrypoint LBL. The R47 also implements the long-press behavior to display the to-be-executed line until you release the key, when the step actually occurs.

Where it gets tricky is that the arrow keys are overloaded by the R47’s menu system. While in PEM, the unshifted arrows navigate through the program line by line, as do the 🟧 ⬆︎/⬇︎ combos unless you have called up a scrollable or multi-page menu. This necessarily causes the unshifted arrows to move through the menu instead, which is why the R47 then allows the 🟧-shifted versions to go on navigating through the program. As a bonus, the R47 makes the shift “sticky” in this case so that you don’t have to keep hitting it while you have that interfering menu up; 🟧 effectively acts as a toggle between menu and program navigation, akin to CAPS LOCK.

This, then, is why the R47 does not use the SST/BST abbreviations: the meaning of these keys varies by context.

This also explains why the R47 departs from its own conventions here with the P.FN thru P.FN3 chained menu design. If they implemented it as a single big multi-page menu, you could not navigate through the program with the unshifted arrows. It would force the user to rely solely on the 🟧-toggled behavior, which is inconvenient enough that one wishes to avoid it where possible.

The END

The HP-42S makes a visible distinction between “.END.” and “END” in program entry mode.

The first is the end of program memory, but only the 42S shows it explicitly. The R47 obviously does have an end of program memory, but it’s implicit. This is a permanent instruction on the 42S, unable to be deleted, making one wonder what practical value it has which might make us lament its lack on the R47.

Both machines have END without the dots. It has the highly practical purpose of dividing one program from another, but whereas END is surfaced at the top level of the P.FN menu on the R47, it is hidden away in 🟧 CATALOG FCN on the HP-42S, not exposed via PGM.FCN as you might suppose. The documented alternative on the 42S is the cryptic 🟧 GTO . . which implicitly adds an END to the last program in memory in preparation for starting a new one after it.

And that is our end, for this article, at least.

(You may now wish to return to my R47 article index.)

License

This work is © 2025-2026 by Warren Young and is licensed under CC BY-NC-SA 4.0


  1. ^ The CX included the content of two earlier ROMs, built-in. These collectively added 92 more instructions, 60 in the extended functions set and 32 more in the time/date/stopwatch set. Since it continued to offer 4 expansion slots like its forebears, it effectively allowed another 256 add-on instructions, if the user picked wall-to-wall packed modules.
  2. ^ The highest allowed item ID code in the R47 is `2^15-1`, and over half the present codes are given to things other than programmable ops.
  3. ^ This either left stack litter or forced the programmer to add a subsequent DROP𝑥 call to clean up afterward.
  4. ^ For instance, in a loop where you want 1-based indexing. Forcing a zero comparison to terminate the loop requires scattering +1 and -1 around the program to juggle these competing requirements.
  5. ^ Even fewer are in vaunted lesser machines like the HP-15C.
  6. ^ It cycles BKSPCEDITNOP, giving a short window where you can invoke that middle function.
  7. ^ There are other differences between ATOX and α→𝑥, but let us not become distracted.
  8. ^ One wonders why it is not the other way around for compatibility with the HP-11/15C: A-E plus a-j. Oh well! 🙄
  9. ^ I’m reporting a fact both from the C47/R47 source code and from testing it out with a custom build of rejig. My attempt to force the wish through against this reality made my R47 most upset! Garbage characters in PEM, crashes…
  10. ^ Not to each program, mind. Nested subroutine calls can each have their own LocR allotment of local registers.