Version 0.44.0, 2026-11-xx
The emulation of
𝑥²on HP-12C is now done in terms of𝑦ˣto allow it to be reversible in the face of R47's eRPN default.Because the HP-16C lacks this as well, the
ENTER-then-×workaround remains a one-way (lossy) translation when lowering to that target. Raising will not turn it back into𝑥². Details.Plus42
XSTRops now round-trip throughraw2txt.Decoupled DM32 input from the output format.
Version 0.43.0, 2026-11-06
Alpha strings now use UTF-8 as the lingua franca internal form upon being raised, then lower to the various character sets we know about on lowering.
This quiets some noisy log paths, leaving only genuine encoding loss being logged.
HP-42S
COMPLEXnow lowers to HP-41C when the Advantage or Math Pac is selected, along the lines of DM32 before it.(The raising path already existed via the R47 IR.)
Synchronized ops table with Free42 3.5.2 / Plus42 1.5.2, bringing in a single mappable op,
ARG→∠.Allowing the
MAGZXROM with the HP-41C Advantage Pac selected.Before, we had it for Math Pac alone.
Lambert
Wₘ/Wₚnow use Unicode subscripts as the primary names, with C47 font glyphs and TeXW_m/W_pas aliases.
Version 0.42.1, 2026-11-01
Mapped more missing forty-family ops, which should have been included in the prior version:
CONJ/IM/REnew in Free42 3.5.1, Plus42 1.5.1NSTK→SSIZE8, with a warning4STK→SSIZE4, silentlyDUP→ENTERwith a warning that the raise direction is correct only in eRPN mode; an explicitENTERdoes not become aDUPcallDUPN→nDUP, silently, but only outside do-if-true context due to the arg handling differenceGROW/WRAP→SF/CF/FS?‘WRAP’SC/CC/C?→SF/CF/FS?‘CARRY’R<>R→M.R⇄R
BREAKING CHANGE: Fixed more cases where the input rule set was being copied over to the output side instead of remaining decoupled, as previously documented. Scripts reliant on this unintended and unwanted coupling might require an explicit
--syntaxon the output side to restore the prior behavior.This occurred when output was to stdout for lack of
--output, where the file name would pin the output syntax rule set, suppressing the problem. It could occur with UTF-8, NutEm, and DM32 output types.
Version 0.42.0, 2026-10-30 — the "spooky 42" release
BREAKING CHANGE: Reduced cascading
rejig --fmtindentation, causing existing programs to reformat:- The indent level of global
LBLis now zero, always; this means named labels in forty-family programs, and all labels in the lesser machines. - Explicit
ENDin output only when it marks a break between separate programs. - No more
.END.in UTF-8 output; it continues to be included in binary formats that use it to signal a hard termination
- The indent level of global
BREAKING CHANGE: Dropped the confounding
--flavorflag, used solely to flag idiosyncrasies of NutEm/PC text exports. For input, the shebang line at the top of those files suffices. Rather than lose NutEm/PC flavor output as a result, I've added--to nuteminstead, roughly paralleling the way we treat the Manz and RLM simulator formats, which also embed Voyager key codes.It is appropriate — if unplanned — that our forty-second feature release decides all remaining Free42/Plus42 extension gaps. New mappings close several gaps:
- comparison ops with named variables
- seven log-sum recall ops,
ΣLNXthruΣYLNX - complex
E^X-1andLN1+Xvariants (real previously done) BESTbecomes R47BestF 15; the reverse mapping rejects any other arg as needing manual translation- the subset of the Plus42 TVM functionality that maps cleanly to the work in progress on the R47 FIN-12C branch
All other gaps are now documented as intentional for lack of suitable R47 IR targets.
Added support for the recent Okken simulator state file format versions 55-57 as of Free42 3.5 and Plus42 1.5. New outputs use the version 57 format.
The oldest supported version remains 54 on input, corresponding to Free42 3.3 and Plus42 1.3. Round-trip conversions will upgrade that to 57.
Added several missing HP forty-family time ops:
HMS+andHMS-ATIME,ATIME24, andADATEDATE+,DDAYS, andDOW
Lack of an R47 IR form kept these out of
rejigto this point, but the "opaque XROM" concept allows safe mapping for forty-to-forty conversions. Some might later be added under the R47's "42" menu, but there are good reasons to believe not all will. TakeDOW, which is arguably not needed on the R47 because it always reports the day of week when showing a typed Date value. For programmatic cases, the R47 offersWDAY, but it isn't a match because HP chose US-style DoW numbers — 0=Sunday, 6=Saturday — while R47 took the ISO path with 1=Monday, 7=Sunday.Some of these were in the HP-41C Time module, later built into the HP-41CX as built-ins, but skipped in the HP-42S for lack of an RTC.
HP-41C family programs can now include a "
modules:" declaration in a non-coding comment (@), allowingrejigto process a program containing calls to XROMs without needing explicit--modules.Taught the HP-41C family the
DROP𝑥trick.HP-41C family MOD output now exposes every global alpha label in a FOCAL segment as its own FAT entry, not just the first.
This allows converting programs to a form the Nonpareil HP-41C family support will accept even though it does not presently offer HP raw input, the normal lingua franca for forty-series machines.
Fixed HP-41CX built-in time routine calls from XROM 26,23 onward, where HP inserted a routine relative to the separate TIME module.
HP raw is no longer entangled with the input/output rule sets.
Before, a program could pass as Plus42 even though you specified HP-42S or Free42 constraints. Plus42 remains the default rule set, but if you give
--syntax, that is now obeyed.No longer "upgrading" builtin math ops to their complex forms when you give
--modules advantage/math.HP-15C dotted labels no longer pass through the R47 IR into R47 or forty-family outputs. They're remapped like other "foreign" labels.
All other output paths already remapped this case, but they now emit a warning as well.
Voyager output formats were listing numeric literals and numeric args consumed by ops in R47 pretty-print style, not digit-by-digit to reflect how the machine stores and interprets them.
This problem did not affect the Manz or RLM formats, since they give the encoding for each digit.
End-of-line comments are no longer eaten in cases like
PAUSE/PSEtranslation, where a conversion becomes a 1:N lowering; ditto the matching N:1 raise. Details.No longer dropping non-coding comments (
@) from HP-32SII program listings on input.HP 35s took a different path, leaving it unaffected by this problem.
Version 0.41.0, 2026-10-25 — the "pure coincidence" release
It is appropriate — if unplanned — that our forty-first feature release reworks the 41-family XROM module handling to better model the behavior of actual machines:
- Added the
--modulesoption to list HP-41C family XROMs "installed" into the--syntax/validatemachine type. Added more machine types, including a model of preinstalled XROMs, if any.
For instance, specifying
--validate hp41cxnow implies an approximation of--validate hp41c --module extfcn,time. It is not an exact equivalence becauserejignow models details including the subtle differences between the HP-41CX built-in time routines and the ones shipped in the separate HP 82182A module for the HP-41C and CV.Sharding the XROM set is about more than preventing indiscriminate use of all first-party HP module calls in a single program. We now model the module count limits for each machine type so that it is no longer possible to emulate that broken behavior merely by giving
rejiga long--moduleslist that claims a situation that could never occur.These checks occur separately for
--syntaxand--validate, allowing a translation to verify the program representation when the machine types differ. For the same reason, we do not allow specifying separate input and output--moduleslists; that would make a mockery of the compatibility check.XROM ID compatibility checks allow
rejigto reject impossible configurations such as:--modules advantage,hpildev --validate hp41cx,timeAn exception is made for cases like this:
--validate hp41c --modules time --syntax hp41cxRather than try to model an HP-41CX with the old HP-41C time module redundantly installed, creating an XROM ID conflict, it checks that the old HP-41C program given as input can still be represented in terms of the built-in HP-41CX time routines.
- Added the
Added DM41X state file support, including
--frameintegration and multiple input concatenation.Added MOD file I/O. Details.
Improved lowering:
- Added
STO/RCLarithmetic emulation in terms of the base forms plus the dyadic arithmetic ops. Details. - Complex number handling on the HP-41C family now works much as on the HP-32SII via either the HP Advantage Pac or Math Pac 1C. Details.
- Taught the HP-41C family the
DROP𝑥emulation trick. - Suppressed
DROP𝑥emulation in do-if-true context to prevent theCL𝑥from being conditional even as the+always runs. - Pure entry-point
LBLno longer breaks HP-12C output; only interior ones do, and then because of theGSB/XEQcall, not the label itself. Details. - The
𝑥²op becomesENTER ×in HP-12C and HP-16C output, when found outside do-if-true context. Details. - DM32 state file lowering wasn't using the same rules as the
pretty-printed UTF-8 one, causing
tools/lowering d32to give much worse results than…dm32. Now they're properly equal.
- Added
When the
raw2txtheader line (00 { …-Byte Prgm }) is found at the top of a text input file and no--validateflag or machine specific file type extension was given to override it, it now sets--validate plus42as its best guess.Previously, a lack of an explicit
--validaterule set would leaverejigfree to interpretraw2txtstyle input under its default R47 rule set, allowing it to accept code not actually legal in such files.It is no longer required to give
-on the command line when you want input taken fromstdin. Simply list no input file names.This aligns
rejigwith the Pandoc command line scheme, at the small cost that it no longer gives--helpwithout being asked.Removed the warning about the HP-42S ops
[MIN]and[MAX]being lossy conversions to the R47 IR form. The R47 equivalents follow their operating convention now.Added
--from/--to d32as the extension-aligned alias for DM32 statefiles; useful when not giving--output *.d32but still wishing to have a statefile, as with the stdout default.Fixed multiple DM32 pretty-print vs statefile output discrepancies:
DMX nnow expands to a literal then/con--syntax dm32listings, matching--to dm32statefiles, matching the on-device display.- Programmed
\integ;FNlistings now include thedtoken before the label, matching DM32 statefiles and on-screen display. - The
--from utf8 --to d32combo no longer misapplies the UTF-8 pretty-print line handler in a path meant to produce statefiles. - Complex stack decompose on
--syntax dm32omits a trailing radix on bare integers, matching statefiles. - Date, time, and DMS literals in
--syntax dm32UTF-8 use HP decimal forms —YYYY.mmdd,HH.mmss,DD.MMSSfff— matching statefiles. - Non-DMS angle literals (radian, grad, degree, multπ) are
rejected on
--syntax dm32emit, matching statefiles; DMS still converts to HP decimal.
Version 0.40.0, 2026-10-21
Mapped the hidden HP-42S matrix ops:
[FIND]→M.FIND[MIN]→M.CMIN(lossy)[MAX]→M.CMAX(ditto)
Also mapped
OLD→RCLEL, an on-screen menu alias with a different underlying byte code, because HP-42S.Restored use of registers A-E in HP-15C programs.
See the new matrix section of the HP-15C docs for the details behind this decision.
The
--flavor nutemqualifier can now be given on the input side as well. If a NutEm/PC shebang line exists at the top,--validateis not required.Allowing
--syntax hp12cwith--flavor nutem.
Version 0.39.0, 2026-10-19
Added read-only
--from emu1?cfor VoyagerCalc "Export Listing as Text…" output, implied by file extension.emu1?c.Improved UTF-8 output formatting:
- The
--line-numbersflag is now always enabled for HP-12C outputs, without whichGTObecomes unreadable. - Blank line insertion before whole-line comment blocks no longer
depends on
LBL. That is correct for all machine types, but it is especially important for HP-12C since it lacks labels. - Automatic line numbering now skips blank lines and non-coding
(
@) comments. This is most acutely seen in HP-12C UTF-8 output, but numbering such lines is wrong, period; they aren't program steps.
- The
NutEm/PC style input parsing now requires either
--flavor=nutemor a shebang line in the format that emulator writes.(That line also includes the machine type, but
rejigallows one to force that independently via, e.g.--validate/syntax=hp15c.)Our prior heuristic approach to detecting its key code columns could break HP-12C parsing of digit entries when combined with end-of-line comments. This mirrors the preexisting requirement to give this for NutEm/PC flavor output.
Fixed a build error in the new VoyagerSave feature set.
(It was depending on files present only on the build host.)
Improved triangle testing for HP-12C to allow the new Albillo trig example to round-trip.
Version 0.38.0, 2026-10-18
Experimental HP-12C support, raising to the financial R47 subset:
- added restrictions like no
LBL/GSB/XEQ,GTO 00isRTN… - HP-16C-like handling of
FIX/SCI;f DIGITandf . - canonical UTF-8 format is like other Voyagers
- canonical "binary" format is RLM-12C JSON
More work is needed upstream to complete this; there are HP-12C ops we cannot raise until there is an R47 op to use in our IR form.
- added restrictions like no
Added the VoyagerSave binary memory dump format, also used by the new VoyagerCalc emulator and our
voyemtool. The HP-12C pattern is--from/to hp12cmem, implied by file extension.12cmem, but it extends also to HP-15C and HP-16C.Our HP-11C support cannot map to this for lack of firmware pulled from an ARMv2 hardware calculator, which we could then use with
voyem. A future "HP-11c Collector's Edition" might provide that.The fidelity of this format is proven out by the Albillo trig suite, which fills the entire 99-step memory and passes the article's test suite under voyem.
Dropped the "X" from the end of the RLM Voyager emulator names. That indicates the iOS versions, but
rejigworks with program files from the desktop simulators equally well.Factored the arg range checking added to the prior release into a shared scheme, then extended it to cover more {opcode, machine} tuples.
Version 0.37.0, 2026-10-17 — the "I'm thirty-seven; I'm not old!" release
Mapped the missing bitwise ops in Free42/Plus42.
These were previously excluded because they need special argument handling. The R47 takes these as parameters to the ops themselves, while the Okken simulators take them from X.
Our argument range checking is loose, suited only to ruling out wildly-wrong argument values. We overlook quibbly details like whether you're targeting the Binary builds — limited to 1-53 rather than 1-64 — or whether 0 is an alias for 64.
Added "alpha" aliases to R47 ops spelled canonically with "α" and lacking an ASCII alias.
HP-15C I/O now allows indirect flag access, while it continues to be disallowed on HP-11C/16C, which lack this facility.
HP-16C output now maps “
SCI 6” to “FLOAT .” on output, and vice versa when raising. Given a digit arg instead,FLOATmaps toFIXmode. Lowering to HP-16C rejectsSCI≠ 6.Disallowing
A-Eas HP-11C/15C register references. They never were valid on the 11C, and on the 15C they only work with matrices, for which there is little direct support inrejig. The sole exception isDIM A–E, which we allow.Voyager UTF-8 now raises and lists
(i)— indirect through register I — instead of rejecting the spelling or collapsing it to→I.Expanded and tightened application of the I/O validation rules, driven by the adoption of
voyem, but not limited to Voyagers:- Restricted the register/variable remapper to the output stage, triggered only when lowering to an output machine type lacking sufficient referent range. Input validation is now strict.
The number display mode ops now check their operand's range.
It is now illegal to say “
FIX .1” with--validate hp15c, for example. For thirty-series or HP-42S family input, “SCI 19” is illegal even as it is legal on R47.On HP-15C (only, among the Voyagers) the numeric display ops can now take I as an argument, to get the value indirectly. This is rejected for HP-11C/16C.
Ditto the
WSIZEop, where mapped.Unlike the bitwise ops generally, this one's arg is checked strictly per machine type.
Tightened handling of
R.0–R.9on Voyagers, and fixed a confusion between this global register subrange and the R47’s superficially similarLocRlocal register block.HP-11C
DSE/ISGand HP-16CDSZ/ISZnow reject numbered registers. They takeIonly.Voyager UTF-8 now raises and lists
(i)(indirect throughI) instead of rejecting the spelling or collapsing it to→I.The IR raised form of the HP-11C
𝑥⇄Iop incorrectly referred to R4. It's a finished instruction, with the I arg implied. Numbered register args are now rejected on input.
Version 0.36.2, 2026-10-12
Renamed the programmable plot-functions op from
PLTFCNStoPLFUNCper a late-breaking change in R47 firmware 00.109.05.00b1. The old name (PLTFCNS) remains as an alias.Non-coding comments (
@) now cuddle with the followingLBL, same as coding (#) ones.
Version 0.36.1, 2026-10-11
Tweaks to the mnemonic column in Manz simulator output:
- better use of Unicode
- added
CLEARreferences - added "f" and "g" shift annotations where missing
Added the missing “
f CLEAR REG” op for HP-16C.Straightened out the allowed
LBLvalues on the Voyagers.No two are alike!
Added checks on the Voyager flag ops to ensure the arguments are in-range for the machine type.
RCLarithmetic is no longer allowed for HP-11C, and storage arithmetic is entirely disallowed on the HP-16C.Copy-pasta from our HP-15C support previously allowed these.
Fixed handling of
STO RAN#in RLM-11CX files.Fixed test op mismappings in the HP-11C and 16C.
HP-16C round-trip fixes, caught by better triangle testing.
Version 0.36.0, 2026-10-09
Tracked upstream R47 changes:
- Added display-rounding ops
DRMandDRM# - Dropped the direct rounding-mode ops that were briefly marked as programmable, but no longer are.
- Added display-rounding ops
The UTF-8 output formatting logic is now smart enough to recognize unconditional
GTO/XEQat the end of a subroutine — i.e. before anotherLBL— as equivalent toRTNfrom an indentation perspective: return to the prior level.Relaxed the rules about non-coding (
@style) comments to allow them to be more useful:- The end-of-line form is no longer special. It canonicalizes to
;form in text-to-text transforms. - The whole-line form is preserved in the IR as a non-coding
REMso thatrejig --fmtno longer drops them. - Blank comments are no longer eaten;
.lines now unneeded. - Round-trip tests now ignore
@annotations in paths that cannot preserve them. (Before, we could not include them because they’d trigger bogus failures anywhere non-coding comment stripping occurred.)
- The end-of-line form is no longer special. It canonicalizes to
Mapped the matrix indexing (
I+/I-andJ+/J-) ops for the forty series.These are HP-42S built-ins, but although they are not strictly part of the stock HP-41C, we’re presently mapping all first-party HP-41C ROMs through indiscriminately. Because this includes the Advantage Pac,
rejigwill also allow these ops with HP-41C family program files.HP-41C family XROM bytes that resolve to an IR opcode now produce text output using that opcode’s name, not an opaque “
XROM id,fn” call, maintaining a clean round trip.HP-42S ops implemented as XROM calls which are not mapped to R47 IR ops now pass through as opaque “
XROM id,fn” ops to avoid an early stop in the conversion.This occurs only for forty-family outputs. Attempting to raise an opaque “
XROM id,fn” call to an R47 output will emit a diagnostic.Added the new R47
FINISHop, aliasDIE.Added more op aliases:
x<>?for𝑥≠?2^Xfor2ˣ
Suppressing false “charset discrepancy in input text” and “unmappable C47 glyph on P47 wire” warnings involving alpha strings. What’s left:
- P47 to UTF-8 still warns on encountering discrepant
glyphs in case the user is not already aware that the output is
not expected to match the input’s on-screen
PRGMdisplay. - UTF-8 to P47 continues to warn about glyphs not in the C47
custom font. The change is that it no longer warns when the
user puts a discrepant glyph in their UTF-8 source file, as
that is now presumed an intentional choice to make the on-device
PRGMview look right.
Despite the looser rules, do realize they do not wholly overlap. The first rule’s remaining warning will gripe about a source file that takes advantage of the second rule. For files that must round-trip without warning, stick to the subset of characters common to both UTF-8 and the C47 custom font.
The same rules apply to every alpha string in the program, not only to plain alpha string literals. This includes named variables, global labels, and args to the HP-42S singular alpha emulation ops.
- P47 to UTF-8 still warns on encountering discrepant
glyphs in case the user is not already aware that the output is
not expected to match the input’s on-screen
Fixed an HP raw decoding error where
X<>with a register was read as the following program step.The same basic problem also affects the two-byte form of
RCL,STO,ASTO,ARCL,ISG,DSE,VIEW,FIX,SCI,ENG,TONE,SF,CF,FS?,FC?,FS?C,FC?C, andLBL, but only when the argument value is above 14.(Yes, really! This type of special-casing is rife in HP raw.)
Version 0.35.0, 2026-10-02
Tracked upstream R47 changes:
- Added
ATEXT,GRFNT#, andGRFNT - Added
LP%/DP%getters and setters - Added
mW→dBmanddBm→mW - Added
POLYhelpersx→POLYandV→Σ=0 - Added
αREG*ops - Added
🖨XFNop (print XFN value) - Added flag export ops
EXPFLn,EXPLFLn, andEXPSFL - Renamed these ops, keeping the old name as an alias:
RMODE#→RM#SIG0→TRL0TICKS→TICKS#
- Replaced obsolete
RMODEwith programmableRMand added the successor set:½E,½↑,½↓,←0→,→0←,⎡x⎤, and⎣x⎦ - Realigned our charset mapping table to the custom font
- Added
Added machine type
dm41xabovehp41cin the forty family to allow the user to opt into SwissMicros DM41X additions such asTRNG, its time-based random number op extension to the HP-41CX time module.This intentional distinction causes
-o foo.raw --syntax hp41cto flag use of these extensions as errors, since stricter HP-41C family simulators/emulators with.rawsupport won’t understand them.Because few of these extensions map to R47 ops, most will still cause errors if tried under
rejig --validate dm41x. Fixing that requires adding equivalent ops to the R47 first so thatrejighas an IR form to which it can raise these DM41X extensions.Added HP raw checksum byte support. If found after an
ENDit is compared to the running tally, but no report is made unless there is a mismatch.Seen only in DM41X output so far, but not treated as exclusive.
Read-only; no checksum byte is added to HP raw output.
Added the opaque HP-41C family
XROM id,fncall form, legal in forty-to-forty conversions only. Implicitly, this means the default output mode rejects them; you must add a suitable--syntax machineflag to allow them to pass from the IR into the output.(Exception: the
--fmtfeature will pass them since it sets the output machine type equal to the input. Under this scheme, the only way forrejigto reject an opaqueXROMcall is if it’s found in a file detected as non-forty-family.)This is meant to allow access to unmapped ops, either due to lack of time/interest or because there is no R47 equivalent to which they can be mapped.
Mapped the HP raw encoding of the
ℯˣ-1op through, aliasE^X-1.Added a program file line number or a step count to error messages where they were unaccountably missing before.
This mainly benefits the Voyager and forty-series paths.
Straightened out a few messes in the new HP-41C support:
- String encoding reworked into a clean cascade mirroring the forty-series family history.
AVIEW/ATOX/XTOAnow work as expected.- Synthetic “W” op encoding broke certain cases of regular
forty-series string handling via IR op
42𝛼. Emitting memory-end (
.END.) even if it is not given on input.(HP-41CX manual calls it “permanent END”.)
The
PSEtoPAUSEcollapse inherited from the HP-42S family is now expanded on HP-41C family output.TONEnow allows register references as the arg, not just 0-9.Tolerating a lack of NULL byte terminator in certain cases where DM41X appears not to emit it, though Free42 does.
Allowing „German quoting“ for labels and other string literals in UTF-8 input. (Normalized per
--syntaxin effect.)GTO IandGSB Iare now allowed in HP-11C, 15C, and 16C.(Albillo’s “Towers of Hanoi” program for the HP-16C demos the
GTOform. This fix allows raising the export output of the JRPN 16C demo of that same program.)Added all-caps aliases where case-folding is suppressed:
E^X→𝑒ˣX²andX^2→𝑥²Y^X→𝑦ˣ
Relented on accepting the
R^andRvspellings of the roll up/down ops. They’re ugly, but they keep reappearing in simulator/emulator text output.Similar accommodation for the ASCII
X#Y?combined form of the op that R47 spells𝑥≠? Y.No longer leaking R47 “42” ops in forty-to-forty round trips by making these symmetric lossless transformations in a few cases where they were not before.
Fixed a decoding bug for 3-byte
GTO/XEQforms in HP raw files.Suppressing
PSEcollapse in do-if-true context.Removed hundreds (!) of non-programmable R47 ops. These should never have been exposed through rejig, as using them allows creating a P47 file that the firmware cannot interpret as intended.
A small number of these remain for use at the IR level when raising non-R47 programs, but they won’t show up in
--opsoutput.Removed several forty-family ops for lack of backing R47 IR functionality:
ANUM,DATE+,DDAYS,PASN,RCLFLAG, andSTOFLAG.For R47 inputs, referencing a bare single-letter label beyond the legal
A-Lrange (e.g.GTO M) now guesses you meantGTO :M:, a local named label, aligning our behavior more closely to what happens in PEM. Disambiguating colons remain required for referencing:A:-:L:as distinct fromAthruL.Fixed an
A-Moverreach in RePIG caught while doing prior.Flag ops like
FS?Fare no longer omitted from--opsoutput.
Version 0.34.2, 2026-09-28
- Straightened out single-instruction looping op handling on Voyagers. Details
Version 0.34.1, 2026-09-25
Straightened out the
CLLCDop variants.That name historically meant the R47-specific version taking (x,y) parameters for the start coordinate, but once all the “42” ops were added, that name should have been ceded to the HP-42S style “clear entire LCD” op.
To disambiguate the cases, the R47 one is now canonically called
CLLCDxy, with ASCIIMath aliasCLLCD_(xy). There is also anXPORTPcopy-paste alias, since this is one of the areas where the R47 charset necessarily papers over a Unicode lacuna; in this case, lack of a Latin subscripty.No change is needed to existing R47 programs, but it is possible that
rejig --to hp42soutput might have incorrectly loweredCLLCDto the R47 form, needing re-generation from.raw.
Version 0.34.0, 2026-09-21
Experimental support for the HP-41C family, done largely as a subset of the preexisting HP-42S feature set:
- Added machine validation rule sets to enforce known limits.
- Factored the operation and XROM tables to separate what HP delivered starting in late 1979 as separate plug-in modules from what they built into the HP-42S in 1988, which Thomas Okken then built atop in Free42/Plus42.
- Added a few judiciously chosen forms of synthetic programming: the {a-e, M-Q, W} registers, a wider character set, zero-length string as a faux NOP instruction…
While the HP-41C rule set is active, text program listings are formatted much the same as HP-42S, plus aliases where the
PRGMview differs.Factored the HP-42S family support into a strict superset scheme:
plus42 ⊃ free42 ⊃ hp42s ⊃ hp41cThe “machine” parameter can now be any of those values, with the effect varying on the option:
--ops: each value in that series lists more legal ops for that machine type than the prior one, implicitly documenting our understanding of the dividing lines between them; this includes legalXROM N,Mpairs for each machine type--syntax: with the default UTF-8 output or now with--to hpraw, limits the lowering of the R47 IR form to legal ops for the target machine type; before,--to hprawwould tolerate anything up to Plus42 features, but now giving--syntax hp42swith it restricts the output to legal HP-42S byte codes--validate: inverse of that with--from utf8orhpraw, for checking that a given UTF-8 or HP raw input conforms to the given machine restrictions
Renamed vague references to
hp42stoplus42to reflect the fact that as of the prior release, there wasn’t much of a distinction. The generic term for all this is now the “forty” series.Plus42-specific test files now use
.p42state file wrappers instead of.f42for this same reason, though.rawremains the favored interchange format.
Version 0.33.0, 2026-09-11
Added support for the new R47
STRUCTmenu ops:Recognize these new structured programming ops on input:
- bare:
IF - with subscript partner number (PN):
IF₁ - literal byte-coded form of PN-as-arg:
IF 001
- bare:
Indent these structured programming blocks in UTF-8 output similarly to PEM, but aligned with prior
rejig --fmtnorms.Normalize PNs on input as the R47 does on
READP. By grace of this,rejig --fmtgenerates missing PNs.Render PNs as subscripts on UTF-8 output rather than as 8-bit args separated from the op by a space.
Make explicit that RePIG looping and branching ops shadow these
STRUCTops when found in RPL-style «blocks».
Added a few new R47 decibel conversion ops.
Version 0.32.0, 2026-09-05
Tracked R47 changes since 00.109.04.00b0:
- Several new conversion ops
- Added
HXGRPandBNGRPHEX/BIN digit-group spacing settings Finance features ahead of the unmerged
FIN12Cbranch:PmtBegi%reserved variableCLRTVMandRSTTVM- Renamed
CLTVMtoCLFIN, keepingCLTVMas an alias
Replaced the
GRAMODreserved variable withGRMOD#/GRMODPOLY / vector helpers:
V→EQandRₓnn→VSystem flags / display:
AVALID,M.ALL,PL2lin,2LINEMatrix concatenation replaces
CNCATwithM.CNCTB
Aligned use of two different middle-dot characters (·/⋅) to match the R47.
This is not a Unicode charset discrepancy purposefully fixed by
rejig. It was simple drift in canonical forms.This affects the
MULT·,RWDOT⋅,IWDOT⋅, andFWDOT⋅ops.rejigcanonicalizes these without the fixed-width em spacing as used on-device. That’s purely a presentation matter, to accommodate an interaction between the C47 custom font and the LCD rendering. No one expectsrejigusers to make fine distinctions between an ASCII space, U+2004, etc. in actual programs. The only reason we accept them as aliases is on the chance that you have copy-pasted one of these non-ASCII space characters fromXPORTPRTF output.Since this difference in dot rendering might not even be apparent in certain fonts, much less worth making a programming disinction over, I am choosing to keep the old spellings as aliases in the hope that
rejigcanonicalization will expunge this misuse both from existing programs and new ones.
Version 0.31.0, 2026-08-16
Added the output-only
--to latexformat. Details.Thanks go to Ralf Ahlbrink (@raprism) for this, his first contribution to
rejig.Added
--reduced-charsetto rewrite op spellings to aliases more likely to render acceptably without the full range of glyphs the defaultrejigoutput format presumes exists in the output font.This flag is off by default. When enabled, it affects all UTF-8 outputs, including the new LaTeX one.
It does not affect HTML output because that is only indirectly based on the standard UTF-8 output path, owing to the MathML translation options. Stacking one atop the other would fight that preexisting design choice.
Added Pandoc-like
--list-{input,output}-formatsCLI options.Removed the stale ad hoc lists from the other
--helpoutput.Added
--list-machinesCLI option to summarize the valid arguments to the--syntax/validateflags.We now tolerate the “
STO + REG” style of spelling the storage arithmetic ops, but only for Voyager inputs, to avoid creating a parsing ambiguity in the broader Unicode input handling.Added experimental parsing of NutEm/PC program listings, as an extension to our existing Voyager text input parsing.
We infer the
--validaterule set from the shebang line at the top of these files.Added experimental NutEm/PC output styling via
--flavor=nutem, which must be paired with one of the Nut CPU--syntaxoptions to specify the exact machine dialect.Broadened our Unicode handling to include cases common on Windows but rare elsewhere. Details.
Added
--from/to unicodealiases forutf8to allow the caller to avoid overspecifying the encoding. No actual restrictions occur, but it does warn on--from utf8when it detects a non-UTF-8 BOM.Made tests conditional where they depend on either decNumber or intelDecimal to build. This is a supported configuration, so we do not wish a lack of a working CGO setup to be a hard blocker.
Version 0.30.2, 2026-08-12
Removed
#suffix from theαLENG,αPOS, andULPops. These are functions, not getters.This sets us up for a complete upstream alignment with the R47 firmware, near-term, presumably with their next release.
No aliases were added to ease the transition, for the same reason as in 0.30.0.
Version 0.30.1, 2026-08-11
Round-tripping the R47 system flags
1024ⁿandCPX𝒿triggered a bogus warning about a mismatch with the calculator’s nonstandard character set.While that warning is correct as far as it goes, it is misapplied in this instance since system flag references are encoded by number, not by name. In PEM and in our own UTF-8 output form, it appears otherwise, but because this is a mere presentation matter, system flag names no longer run through the charset checker.
In tracking the upstream
𝑥→αop replacement, therejigaliases were not moved from the old one to the new, causing use of any of those aliases to inadvertently give the old op.
Version 0.30.0, 2026-08-07
BREAKING CHANGE: Matched the upstream style change for getter ops to have a trailing
#instead of our earlier attempt toward the same end, a C pointer-like leading*for “dereferencing” these values. No backward compatibility aliases have been added to ease the transition, because I wish to discourage the continuation of this brief rift.BREAKING CHANGE: Removed the data type heuristics based on HP decimal date/time/angle rules. They could easily misfire.
Tracked the remaining changes in R47 00.109.04.00b0:
Local named labels.
This uses the PEM-inspired
:local:syntax in UTF-8 I/O, which then gets translated to the P47 byte code format for--to p47output. The primary difference is that in PEM on-device, a leading alpha mode change determines whether the colons can be safely dropped, whereasrejiglacks this signal, requiring it to always treat colons as significant.Since
rejigdoes not currently support any non-R47 target with a similar feature, it sends local labels found in inputs through the existing “illegal label” mapping mechanisms rather than reject them outright. In the same way that an HP-32S program making use of global labelFdoes not fail to lower to the HP-15C’sA-Erange until the mapping table fills,rejigtries to map these local labels into the closest available machine-specific form, causing these local labels to become unique global labels.In practice, programs complicated enough to justify use of local labels are likely to fill this mapping table quickly, but
rejigtries valiantly regardless, not giving up without a struggle.1st and 2nd derivative op replacements.
Beware that
--fix-obsoletewill refuse to upgrade these due to their change in arg type.New vector conversion ops:
𝑡𝑧𝑦𝑥→𝑣̄₄,𝑡𝑧𝑦𝑥↔𝑣̄₄,𝑣̄₄→𝑡𝑧𝑦𝑥New
PGMDRV,PLTFCNS,SLVPops.
Added missing R47 matrix ops that landed in May, thus which should have appeared in rejig shortly after, ideally in release 0.23.0:
M.CMAX,M.CMIN,M.FINDMapped these HP-42S matrix normalization ops:
FNRM→ENORMRNRM→RNORM
Further tweaks to the special-casing for the
#Bop, to prevent it from being misinterpreted asREM ‘B’.RPL-style
@comments are now recognized in plain UTF-8, outside RePIG «…» blocks.Inherent value aside, the fact that we treat them consistently between the cases means we now have a comment form that gets stripped on input parsing, neither preserved as
REMin the IR for output forms that support it when the comment is given alone on a line, nor as an end-of-line comment when given after an op. This addresses the wish for verbose block comments in source code that do not bloat the resulting P47 binary with longREMop args.Consequently,
rejig --fmtdrops@comments by design.End-of-line comments are no longer redundantly attached to each op given on a single line, only to the last one, avoiding output duplication.
Added
op_aandop_Aaliases forELEC-𝛼andELEC-A.Voyager-style line number parsing allows
001- STOto be treated as an unadornedSTO, but it overreached, preventing ops like16-BITfrom being accepted when not disambiguated with indentation. It now requires whitespace after the line number to avoid grabbing up the rest of the op name in cases like this, misparsing it as a nonexistentBITop, resulting in a confusing diagnostic.Fixed a few cases where two space-separated reals on a single line were being misinterpreted as a complex number. This was never an intended syntax for expressing them.
Version 0.29.0, 2026-07-16
Tracked several upstream R47 changes that have landed on master since the prior release:
- Added new settings and ops.
- Making use of the new HP-42S charset compatibility code points in the C47 custom font, rather than strip them on R47 output for lack of any suitable glyph.
- Tracked a change in how a few new R47 flags work.
Our implementation of the Plus42
XSTRextension is now handled as a canonicalizing step on input and as a fallback to the HP-42S legacy encoding form on output, when required to avoid losing data.This means it does not strictly round-trip outside canonical forms. As in all other cases where
rejigsupports HP-42S family variant encodings, it prefers the most compact one.Fixed the HP-42S mapping of its small-caps
ʏcharacter (0x81) which was previously mistaken as a lowercase gamma.
Version 0.28.1, 2026-07-05 — our after-the-fireworks release
Fixed
\uescapes in op aliases.The intent was to document Unicode special character use in a more visible way than literal renderings in the Go code, to distinguish tricky cases like
𝑥⇄𝑦vsx⇋yfor the swap op. The problem is, those Unicode escapes were themselves inside Go backticks, causing them to be compiled literally.An attempt was made to add a runtime
\uinterpreter, but this was plain wrong-headed.The proper solution is to write the strings in a way that puts the interpretation burden on the Go compiler so that
rejigneed only do simple literal string matching at runtime.Part of the resistance to doing this from the start was that our
Opstable was meant as a form of user documentation for available aliases and such, but now that we have the--opsflag, we can give this goal up. This is an odd case where coding clarity harms casual readability, but for a purpose: ambiguity reduction.Added
XthRtYalias for the arbitrary root op.Added
x⇄yalias for the swap op.The
𝑥⇄𝑦spelling remains canonical, but the only variant we had with ASCIIxandywasx⇋y, with the “harpoon” arrows.Including machine-specific aliases in the default
--opsoutput since they are accepted as default-mode input.
Version 0.28.0, 2026-06-28
Added R47 plot ops
PLTfandPGMPLT.Added the
--opsflag to print all programmable ops in TSV form, then exit.Without an argument, it prints the R47 lingua franca, but when given a machine type of the same form that
--syntax/validatetake, it gives a machine-specific table, including its preferred op spellings, the byte-coding prefix(es) it uses…This feature functionally obsoletes the
opcodetool and the--num-opsflag, which is why they have been removed.Simplified HP-42S
.rawI/O by taking advantage of structural truths about the encoding across three eras:- HP-41C original design
- HP-42S extensions
- Free42/Plus42 extensions
This does not wholly eliminate all the special cases plaguing prior versions of our HP-42S support, because alas, the evolution we’re tracking has several of these baked in.
Validated all this against a newly published reference on the Free42/Plus extensions to the HP raw file format.
Several improvements immediately result from this alignment improvement for HP-42S family inputs:
- added support for the Plus42
XSTRextension - better handling of indirect register references
- broader input tolerance of variant instruction formats
- improved output normalization of same-size variant forms
- output compaction by choosing the shortest variant when there are multiple matches with the intended semantics
- added support for the Plus42
When the
SIZEop is found in HP-42S inputs, it is dropped with a warning since the R47 has no equivalent mechanism, and we find ourselves unwilling to ask for this trammel to be added. We are glad that the R47 always gives access to the full R00-R99 range.Added a three-way charset mapping scheme to accommodate the UTF-8 versus R47 versus HP-42S extended ASCII schemes, no two of which overlap completely.
This allows
rejigto handle all the non-ASCII extensions in the HP-42S, squatting on the control character spots down below 0x20, plus those at 0x7F-0x81. That in turn allowed us to accommodate the 0x82+ extensions in Free42/Plus42.Diagnosing a few classes of HP-42S alpha string misuse as hard errors, which were previously allowed.
Changed the spellings of the HP-42S append op from a misuse of the box-drawing glyph
├to the appropriate mathematical right-tack operator⊢. The old spellings remain valid via aliases.
Version 0.27.4, 2026-06-23
More R47 printer ops mapped to HP-42S equivalents:
⎙ON/⎙OFFtoPRON/POFF⎙rtoPRV
Fixed another case of special (ASCII < 0x20) HP-42S characters.
Version 0.27.3, 2026-06-21
- Added mappings for several more HP-42S system flags.
Version 0.27.2, 2026-06-20
Mapped HP-42S system flag 25 to R47’s
IGN1ERflag for listing translation in both directions.Fixed HP-42S raw decode of multi-char string args:
ASTO/ARCLmapping to42ASTO/42ARCLINDreferences involvingVARMENU,GTO, andXEQ.- Improved
--syntax hp42soutput in these cases to better match HP-42S PRGM display.
Version 0.27.1, 2026-06-13
Fixed the
KEY+GTO/XEQmapping for HP-42S inputs to make proper use of the new42KEYop.Additional upstream sync with R47 firmware
00.109.03.03b0:- HP-42S raising uses the new
42PRMPTand42VRMNUops instead ofPROMPTandVARMENU. - Renamed getter ops from trailing
?to#to avoid confusion as do-if-true tests, with legacy spellings kept as aliases. - Added
M.CNCTR(a.k.aCNCATR).
- HP-42S raising uses the new
Version 0.27.0, 2026-06-07
Ripped out the faux alpha register hackery (
rejigα) in favor of the R47’s new HP-42S style alpha register ops, backed by a user-selected singular alpha register.No attempt is made to upgrade prior conversions. Those who made use of our prior workaround for the lack of these features will want to reprocess their HP-42S originals, ideally producing 1:1 conversions.
Added new R47 ops that landed in 00.109.03.03b0:
- Separate getter/setter op pairs replacing prior reserved
variable scheme:
ADM,DMX,ISM,NDEC,REALDF IMPLT(plot imaginary part only)M.DIMN#(get matrix dimensions)
- Separate getter/setter op pairs replacing prior reserved
variable scheme:
Fixed mapping of the
ⅅ→xop alias.
Version 0.26.0, 2026-05-25
Added HP-16C support, initially using the JRPN 16c program file format, plus UTF-8 pretty-printed variants.
Several improvements to line number handling:
- HP-42S style line number stripping now occurs only when one is found on the first substantial line of a program. Details.
Combining
--line-numberswith--syntax dm32now gives HP-32SII style line numbers, as seen on-device.(It previously fell thru to the R47 style for no good reason.)
Added support for the Voyager trailing-dash style.
Fixed a case where thirty-style
--line-numberscould end up using HP-42S style.
Added the
RWDOTASCII-only alias for the R47 setting.Fixed inconsistent handling of 8-bit args at the IR level, especially in the way we handle raising legacy
PSEinstructions to R47PAUSEand vice versa on lowering.
Version 0.25.3, 2026-05-22
Mapping
KEY+GTO/XEQmenu numbering between HP-42S and R47 to account for the differing number of menu rows (1 vs 3) which then shifts the special values for the⬆︎,⬇︎, andEXITkeys.Programs that stick within the first menu line now round-trip between the two.
The special HP-42S character handling (ASCII control code range) now covering literal string cases like
MVARnames.Preserving the
STO ‘rejigα’(faux alpha register) through the P47 I/O chain, whether written directly to disk or piped with that as the transport format.
Version 0.25.2, 2026-05-18
Non-R47 machine types now use the “HP” variants of the R47 to-polar and to-rectangular conversion ops.
Tightened the thirty-series and Voyager handling of lowercase LBL.
Fixed handling of HP-42S local labels A-J, a-e.
Fixed handling of the
CLD/MSG 000mapping for HP-42S.Removed a bogus mapping between HP-42S flag 29 and a nonexistent R47 flag
COM,. There is an alias for an op of that name, but not actually a named system flag. The best we can do is preserve the historic flag value through a round-trip.Expanded test coverage.
Version 0.25.1, 2026-05-16
Improved the roundtrip triangle testing model to cover paths that were not tested before. All bugs tickled into presenting themselves by this are now fixed.
Fixed a problem with string literal encoding from HP-42S to R47.
Fixed a problem with HP-42S to R47 for ops taking 8-bit args, such as
FIX.
Version 0.25.0, 2026-05-16
Free-form RPN is now allowed:
LBL ‘Foo’ RCL Q + / RTN ENDIt uses the known argument type for each op to allow word-by-word parsing, rather than be restricted to line-by-line input.
We already had a weak form of this to handle
KEY+GTO/XEQpairs. All this does is extend it to the general case.Extended that to allow an experimental RPL-inspired syntactic sugar layer called RePIG that compiles down to RPN.
Added the
--mapflag to ease RePIG output understanding. Details.Third-party tooling exists to read this, and although some of it is intimately bound up in the JavaScript world it emerged from, some tools are tolerant enough to work with our niche
rejigformats.This is a generic facility. It can also show the minor framing differences expected to result from a
--from p47u --to rlm11cxconversion, for instance.Mapping R47 local registers to Free42 local variables.
STO R.00becomesLSTO "R.00".R47 local registers are remapped to global registers on lowering to Voyager and thirty-series targets, with no anti-collision attempts made. Warnings are therefore emitted.
Mapping HP-15C system flag 8 to the R47 named flag ‘CPXRES’.
Fixed an over-eager initialization of the faux alpha register (
rejigα) for HP-42S inputs containing no alpha strings but only numeric literals. (Bug tickler)
Version 0.24.0, 2026-05-07
When
--outputis a directory, inputs are written to files in that directory, named after the firstLBL.When there are multiple inputs and a single output file, those inputs are chained together with
ENDseparators.Mapping HP-42S flags 16 and 21 to the new printer control flags on the R47.
Fixed the byte count at the top of
--to hp15coutput, making it match documented HP-15C hardware behavior.
Version 0.23.1, 2026-05-03
Assorted accommodations to aid importing copy-pasted
XPORTPtext:- added many op aliases
- parsing
1.2345°as decimal degrees - narrow no-break space (U+202F) as a digit separator
- broadened Unicode character tolerance (e.g. ⅈ in complex numbers in place of 𝑖, 𝕖 in place of ℯ as Euler’s number…)
This is nearly enough to allow parsing upstream
AllOps.rtffile once its text has been copied into a.p47ufile. The remaining blockers are intentional, as with our spelling of thea-b/cop to avoid the need for dodgy parser heuristics.Fixed parsing of registers in
ARG_VALUE_8ops likeMSG X.New
RCLVEL/STOVELops taking 16-bit args instead of 8-bit ones.
Version 0.23.0, 2026-05-02
Added all the new R47 ops and flags that landed in 00.109.03.02b0:
- Printer
- MATX menu
- TVM amortization
Updated HP-42S printer op mappings to make use of the new R47 ops.
Updated the R47
AllOpstest to a close variant of the generated demo among theRESETstate programs, then added ops found to be missing beyond those added since the prior sync with the upstream “items” table:ASSIGN(useless because argless?)ELECmenu Ohm’s law and balanced conversionfloz_(US)→gal_(US)andgal_(US)→floz_(US)→INT(required renaming prior op of that name to→BASE)cp/aTVM reserved variableKEYSmenu ribbon presetsk→mandm→kELLIPTmenu𝑥_(IQR)D→x
Also renamed a bunch of ops to better match their on-device presentation. Demoted their former names to aliases for backwards compatibility.
Demoted the “getFoo” forms of getter ops to aliases, then adopted a C-like “pointer dereferencing” notation to solve the ambiguous op name problem. Leading
!for the third category, neither a proper getter nor a test, showing temporary info only. Details.Fixed the arg type of these R47 ops:
OPENM(label, not none)SOLVE(register, not label)‖𝑥‖_𝑝(16-bit value, not 8)
Added R47 op name aliases:
STDERRfors_m(sample mean standard error)±INF?demoted to alias of canonical±∞?name- conversions involving liters now use ℓ in the canonical name (as the R47 does) with the L form demoted to aliases
Fixed diacritics on some Chinese units in conversion ops.
Added support for typed angle literals from P47 inputs, rendered in UTF-8 output with mathematical small italic {𝑑,𝑟,𝑔,𝜋𝑟} affixes.
(Not the superscript letters of the R47 for lack of uniform Unicode equivalents in common fonts.)
Added support for Julian-coded date strings in R47 inputs.
This does not round-trip because we still emit P47 output as before. The IR does not remember which date format was used on input. All that matters for our purposes is that it produces the right date, irrespective of means.
Added the
--num-opsoption to report the size of our internal R47 ops table, less the system flags and obsolete ops. This gives a much lower number than the length of theindexOfItemsarray, which includes those plus various gaps left for future expansion, non-op items like “bufferized” keyboard inputs, etc. This reports a count of usable “operations” available to an R47 RPN programmer.No longer updating
>SDIGS<with--fix-obsolete. The arg types are incompatible.Fixed the naming of the R47 op
>→HR<to allow--fix-obsoleteto replace it with→HR.Renamed a few ops to prevent them from being misinterpreted as numeric literals. The
1024ⁿsetter was missing its trailing superscript, and our version of the new HP printer mode op “82240” gains a leading “🖨️” emoji.Fixed indirect variable parsing for
ARG_VALUE_16ops likeBestF.Synchronized “step” value in log messages with the R47 display. The biggie is that it counts
KEY+GTO/XEQop pairs as a single step, but there are additional tweaks.Fixed P47 byte decoding of string-coded date literals.
Fixed mapping of the HP-42S op
AONto R47AIM.
Version 0.22.1, 2026-05-01
The new mapping of the HP-42S “append-to-alpha” op to a visible Unicode glyph (
├) caused misinterpretation of programs using this op.Fixed arg type handling for the R47 “integrate Y-X” op.
Fixed the canonical name of the R47 op
W→hp_(UK)so that it shows a subscript in HTML output, like the other US/UK specific conversion op names.Also brought the non-canonical
AllOps1.p47ufile we distribute into alignment. This wasn’t noticed earlier because we don’t ship a.p47version for round-trip testing, on purpose; that’s why we haveAllOps2.Backed some of the charset checks off or at least deferred them until more path-critical checks were done.
- It was griping about “𝒾” in pretty-printed complex numbers.
- It erroneously checked single-letter labels (e.g.
GTO X) for legality under the charset rules, causing them to be incorrectly interpreted down the pipeline as alpha labels,GTO ‘X’. - Similarly, it was conflating the “thirty” series A-Z label and variable range with the R47 A-M label range, register names…
Improved warnings in the new charset checking.
Added several missing R47 ops:
- reserved variable ops (TVM recalc magic, matrix editing…)
qt_(US)→Lconversion
Version 0.22.0, 2026-04-30
Smarter handling of the R47 character set:
- Characters outside its presently defined range found in input now cause warnings. One no longer needs to inspect the program on-device to check for “missing character” glyphs.
- Discrepant characters are mapped to their proper values where making use of the full 21-bit Unicode range allows.
- Those mappings are undone on input, lowering to the discrepant values so they display properly on the R47.
Extended that mechanism for the HP-42S, which is mostly 7-bit ASCII with a few nonstandard things that we can map to proper Unicode to display as intended in UTF-8 output.
This round-trips, allowing you to type things like “␊” in UTF-8 input to get ASCII 10 in the output, as when you want a line feed in printer output. Prior to this improvement, the LF would break round-tripping by emitting a literal linefeed in the output; the rejig text input parsers are all line-oriented, unable to continue string literals over multiple input lines.
The
--fmtoption does a better job of inferring the output format rules from the input. In particular, you should no longer need to supply--syntax, the sensible value now implied.Adjustments to
--fmt --syntax hp*to improve fidelity with on-device presentation for HP-42S and HP-15C.Fixed DM32 statefile comment duplication and formatting regressions.
Added a
make installtarget.Restrict use of HP 35s compact format complex numbers on output to
--syntax hp35s.It continues to accept it on input regardless.
Version 0.21.0, 2026-04-23
Many improvements to the HP 35s handling of 32S family extensions:
- Added HP 35s rect/polar mode ops
𝑥𝑖𝑦and𝑟𝜃𝑎. UTF-8 inputs now recognize its infix-𝑖 notation for complex numbers: 0𝑖 and 0𝑖0 mean the same as 0+0𝑖.
This works for ASCII “
i” as well.Emit infix-i form in
--syntax hp35soutput. The imaginary part is dropped when it is zero to match the HP 35s display.GTO/XEQnow allow the 35s numeric offset to the label. Raising to the R47 intermediate form causes “XEQ L001” to be treated as “XEQ L”, but then on lowering to--syntax hp35sthe number returns.This is potentially lossy: doing the conversion in a single step allows “
XEQ L003” to jump to the third line in subroutineL, but doing it in two steps via another RPN flavor will change it to 001.The upside is that converting programs from other formats to
--syntax hp35sadds the “001” the HP 35s needs. It also means lowering 35s programs to other formats will work as long as you restrict your input to “001” offsets.
- Added HP 35s rect/polar mode ops
Machine-specific UTF-8 file extensions (e.g.
.hp35s) now imply--validate EXTon UTF-8 input. Added a few missing ones to make the mapping 1:1.The new UTF-8
--validateoptions for non-R47 and non-HP-42S now include remappings like theDROP𝑥→ “CL𝑥then add” applied to the machine-specific output formats.Brought
CMPLXop handling to HP-32SII and DM32 UTF-8 flavors. (Previously only existed in DM32 statefile I/O handlers.) This is lossy because the R47 does not have separate ops, but that is not new; the DM32 statefile paths had the same restriction.The
--syntax hp42sflavor now uses<>in swap ops to match the on-calculator display.The new machine-specific UTF-8 validation type guessing produced an unintended coupling to the output format rules. The rejig design of raising to R47 intermediate form and then lowering it was intended to decouple these matters.
An empty shell style comment no longer gets misinterpreted as the R47’s confusingly-named
#op, the constant “zero”:# A comment might need a blank line… # # …like this, which should not be misinterpreted!One must now spell this op as
ZEROto avoid the confusion.The canonical output of
REMops in R47 flavor UTF-8 is now a shell style comment, not a literal REM.Doing this also in
--syntax hp42smode since the HP-42S family lacksREM, allowing us to preserve comments compatibly.Fixed handling of zero-length string literals in P47 input, tickled by previous.
Version 0.20.0, 2026-04-11
BREAKING CHANGE: Reworked the UTF-8 handling to separate the notion of “file format” from input validation rules and output syntax rules. Prior uses like “
--from hp42s” are now “--from utf8 --validate hp42s”. Leaving these new options off causesrejigto presume R47 rules for UTF-8 I/O.Unified machine-specific UTF-8 I/O rules under this new scheme:
- Added UTF-8 pretty-printing for all supported machines, along
the lines previously taken by
--to hp42sbut now called--syntax hp42swhere the default--to utf8is implied. While we had semi-readable file formats like--to hp15cand--to dm32in prior releases, those are intended for machine parsing first, readability second. - File name extensions continue to imply
--from/toformats, but now also the--syntax/validatewhere sensible. Added the.hp11c,.hp15c, and.dm32extensions to this end, uniformly applying the naming scheme set up in prior releases. - The
--from/to hp11cpair is no longer an alias forrlm11cx.
The end result is that it’s now possible to use
rejigwhile programming for these historical machines in a text editor, with full awareness of the rules through the I/O chain. Before, one had to either write in terms of a calculator-specific program file format (e.g..15c) to get machine-specific input validation or maintain discipline when using I/O paths intended for the R47.- Added UTF-8 pretty-printing for all supported machines, along
the lines previously taken by
Improved display of swap ops for
--to hp11cthruhp42sto better match the on-calculator display in PRGM mode. The all-uppercase ASCII approximation rule givesX<>Ywhich isn’t appropriate here.
Version 0.19.1, 2026-04-08
Tracking label usage better in the output lowering paths to avoid conflicts between the input program’s use of labels and the regig mapping logic for legalizing names given as input.
The following now produces unambiguous output under
--to hp15c:# This should become LBL A, lowering the alpha string LBL ‘A’ # This then takes up the next slot… LBL B # …so that this must become LBL C to avoid a conflict LBL ‘B’Prior versions incorrectly reused “
LBL B” at the end, both for Voyager outputs and for 30-series machines, which share a similar limitation in the output namespace.No change occurs for the R47 and HP-42S family machines because the input program isn’t ambiguous under their rules. Labels
Band‘B’are distinct.Defined formal “properties” for all this, enforced with PBT.
Fixed output of comments in UTF-8 outputs. They’re encoded as literal strings at the R47 level, but that is no reason to wrap the output in quotes.
Warning about DM32 remote-XEQ form if found in all UTF-8 outputs, none of which support it. (Other output formats quietly drop the affix, treating the single-letter label as in the same namespace.)
Added diagnosis for the obsolete
>ABS<and>YY<ops, including a mapping to their proper replacements under--fix-obsolete, being|𝑥|andYY.Assorted logging improvements in the DM32 input module.
Version 0.19.0, 2026-03-31
The existing DM32 and UTF-8 features got together and produced a pair of 30-series paternal twins:
--from/to hp35sis a UTF-8 only I/O path for the HP 35s.--from/to hp32siiis a UTF-8 only I/O path for the HP-32SII.
There are no immediate plans to extend this down to the HP-32S by adding further restrictions nor to fill the 33s gap.
Combining these with the
--line-numbersoption gives “native” numbering: “L01” for HP-32SII output, “L001” for 35s. Extended the input parsers to cope.Combining that new feature with
--to hp42sapproximates the variable-width line numbering scheme of the HP-42S.Added support for remote-XEQ statefile affixes in DM32 I/O.
Added the new R47 temp conversion ops, headlined by the new degrees Rankine and eV/kB units.
Input parsing tolerance improvements:
Real and complex numbers now share implementations across more input formats, allowing you to use R47-isms in HP 35s programs, the Free42 “ᴇ” in HP-32SII programs, etc.
We normalize outputs meant to be read directly by a calculator to work within its limitations, which gives us yet another good reason to recommend
rejig --fmt: canonicalize inputs in cases like DM32 statefiles where the format is human-writable but also machine-readable.Unicode superscripts are now allowed in the exponents of real numbers expressed in scientific notation:
1×10⁹means the same thing as1e9. This includes complex numbers:2e9.+3×10²𝒾is now accepted.Time literals need only a single colon to be recognized as such in UTF-8 input. A “:00” is appended in canonicalization.
Emitting combined comparison op form (
X≤Y?) for--to hp42s.The DM32 op code tables were claiming it supports the
2ˣop.
Version 0.18.2, 2026-03-30
The
--to hp11cand--to hp15coutputs no longer drop binary coded reals (including the two halves of a complex number in the HP-15C case) from the R47 IR, provided the exponent is within ±99. The mantissa is truncated to 10 digits.Added an
ENTERkeystroke between the real and imaginary parts of complex numbers in--to hp15coutput. Giving the second exponent digit does not close NIM automatically.In
--to hp11cmode, it now fails if the input contains a complex number. Previously, it just dropped it silently and continued.
Version 0.18.1, 2026-03-29
Fixed an off-by-one error in the op code tables, affecting the interpretation of
RCL.SKPandXEQ.SKPin R47 inputs.Improved logging in the P47 interpretation loop.
Added property-based testing where it was deemed profitable, mainly where example-based tests are insufficient and exhaustive testing is too expensive. Stochastic testing fills some of the gaps.
Additional non-PBT testing where the audit to decide where to add PBT turned up a coverage gap, but where PBT wasn’t the solution.
Version 0.18.0, 2026-03-13 — our “Friday the Thirteenth” release 👻
HP-42S programs referring to the singular alpha register now create a
‘rejigα’variable to use in its stead. That allows the following ops to map in somewhat lossy fashion:- ├ or + before a string literal in .txt form (append)
- AIP
- ALENG
- AVIEW
- ATOX/XTOA
- ASTO/ARCL
- CLA
- PRA
- PROMPT
Mapped the Free42 extension ops
RCOMPLXandPCOMPLXalong the same lines as the more genericCOMPLEXop.Because the high-level R47 program format does not tag complex numbers with a coordinate system, it treats
COMPLEXandRCOMPLEXthe same except for a round-tripping allowance. ForPCOMPLEX, it converts the two reals preceding the op into rectangular form, then proceeds, which is what Free42 also does. It assumes you want the conversion done in degrees, the default angle mode of the Free42. These being necessarily lossy processes,rejigwarns about all these translations.Polar conversions are done with the same Intel Decimal Math library that Free42/Plus42 uses, but beware that complex string literals are interpreted with the R47’s fork of the IBM decNumber library. We can be off in the last digit due to rounding behavior differences.
For
--to hp42soutput, complex numbers are decomposed back into the real literal, the imaginary literal, and aCOMPLEXop.Added mappings for more “missing” HP-42S family ops:
EXITALL,→OCT,→DEC,CLD, andNOP.Mapped the HP-42S ops
PRON/PROFF→ R47PRTACTflag set/clear.Mapping the argless Free42 op
SKIPto the R47’sSKIP 001and back. All other arg values give an error.Added Free42 type tests
CPXMAT_TandTYPE_T.The output of
--to hp42sis now more similar to the on-calculator display and to the output ofraw2txt, reverting our R47 inspired Unicode stylings:- all-caps ASCII op names where available
- straight ASCII double quotes for alpha strings
The
--from hp42sinput now allows bothSTandINDattributes on a single argument, as with “RCL IND ST X” to mean “RCL →X” in R47 syntax.Renamed the
--from/to free42-rawformat tohprawboth because it’s shorter and because it is reportedly the actual HP-42S byte code format, not something invented for Free42.Several
.rawdecoding and round-tripping fixes.More improvement to error diagnostics atop the new logging scheme.
Version 0.17.0, 2026-03-09
Taught the HP-11C and 15C outputs the
DROP𝑥trick formerly known only to the DM32 output. (Emulated asCL𝑥then “add”.)Taught the HP-42S I/O paths how to expand/collapse
PAUSE/PSEops from/to the abstract R47 layer, as with DM32 since release 0.9.0.Reworked all stderr output in terms of Go structured logging (slog):
- Added several new warnings wherever
rejigis making what might be considered a semantic change to the program which is better done by the programmer. A good example is theDROP𝑥hack since it has the undesired side effect of clearingLASTx. - Added the
--fail-if-warningsand--quietoptions to control the handling of these new warnings. (Pandoc option scheme.) - Added the
--no-fail-on-errorflag to make it bull forward on errors, to give a complete punch list of items to address. - Reworked our
--verbosemode in terms of this new scheme, increasing the visibility into the parsing process.
The default logging level is “info,” used sparingly for “FYI” notifications that don’t rise to the level of warnings. By this same means, stdout is reserved for program output in the default mode, when
--outputis not given.Many existing errors now have more detail as a result, and the stderr output can now be reliably parsed by third-party tools, as when rejig is used in an automation pipeline.
- Added several new warnings wherever
Applied colorization on POSIX platforms and under Windows Terminal when stderr is not redirected, distinguish log messages from program output on stderr; not to mention, “Pretty!”
DM32 outputs will now map numbered registers from the HP-15C and HP-42S evolution lines to single-lettered global variables. The first one is called “R” by preference, but beyond that it starts from “A” and works upward until it finds an unused variable.
The
--to hp42soutput now uses the HP-42S “ST” and “IND” styling instead of the canonicalized R47 form.Mapped more of the “missing” HP-42S ops, including:
- remaining bitwise ops that map 1:1 to R47 ops, including several Free42 extensions (see TODO for the exceptions)
YMD/MDY/DMYandCLK12/CLK24mode ops- current
DATE/TIMEgetters the Free42
DATE+andDDAYSextension opsThese map to R47
+/-on the presumption that there is an R47 date value (𝔻) in X or Y, allowing type promotion to do the right thing here. We issue a warning because this is iffy and potentially lossy. Because it is a one-way translation, this op is not checked for regressions in theAllOps.rawtest file.For
.p47uinputs, you must pass--from hp42sto get this mapping. Otherwise, it is treated as an R47 program, where ops with these names do indeed not exist.
Fixed a stack register round-tripping bug in
.rawoutput.Improved handling of
ENDvs.END.in the HP-42S paths.
Version 0.16.0, 2026-03-07
Generic HP-42S support, building atop the Free42/Plus42 features added in the prior release, plus the preexisting UTF-8 input format:
--from hp42s: UTF-8 input in eitherrejigcanonical form or the Free42raw2txtformat.--to hp42s: Same as--to utf8with an added restriction for HP-42S family ops.- Raw2txt spellings added to
program.Ops[].Aliaseswhere needed; more can be added as needed for full AllOps.raw roundtrip (seetests/hp42s/roundtrip).
This has the nice side benefit that our UTF-8 input processor can now handle HP-42S style “IND” syntax for indirections in addition to the arrow syntax it previously required. It will also tolerate the HP-42S quirk of marking stack register references with “ST”.
The UTF-8 input format now allows underscores in numeric literals as a separator, as for “thousands” grouping or to break up long hex or binary constants.
Complex numbers given from an R47 input program are translated to three ops in the HP-42S output formats per HP’s rules for entering them: real, imaginary,
COMPLEX.The UTF-8 input path reverses this, producing a complex literal when it sees that real + imaginary +
COMPLEXop pattern.Mapping Free42 ops to equivalent R47 flags and back:
- HP-42S
CPXRES/REALRESops become R47SF/CF‘CPXRES’ - Free42
CC/SC/C?becomeCF/SF/FS?‘CARRY’ ops
- HP-42S
Added several HP-42S ops that didn’t make it into 0.15.0:
- hyperbolic trig
- clear LCD, user menus (
CLMENU), all menu assignments (CLKEYS) - clear numbered registers (
CLRG)
All input paths now have
--verboselogging of decoded ops, and those that had it before produce clearer results.The UTF-8 text input now ignores leading line numbers.
Fixed parsing of single-letter referents in several forms. It all stems from the R47 format allowing just about everything — A-Z as registers, distinct from ‘A’-‘Z’ as global variables, etc. — while the HP-42S is considerably more restrictive.
Local single-letter LBL on HP-42S I/O now follow the {A-J, a-e} rules. All others become global (alpha) labels.
Fixed the way the
𝑥⇄op (swap X with named referent) was mapped on the HP-42S.Fixed a few boundary cases where the Free42
.rawencoder switches to a more efficient 2-byte form of an op that otherwise takes 3:- single-digit integers, both “1” and “1.”
- short form of GTO/LBL for targets under 15
- long (3-byte) form for STO/RCL targets over 15
Don’t ask me why the thresholds differ by 1 in each case. I don’t know, either. It is possible I am misunderstanding the Free42 code.
Version 0.15.0, 2026-03-04
Added
--to/from free42for Free42 state files (.f42) andfree42-rawfor the program-only.rawformat at their core. This should also work with SwissMicros’ DM42 family.Also added support for the Plus42
.p42state files.This first version is functional but very beta. 180 of the roughly 500 ops defined in Free42 are round-tripping, but that overstates the completeness of the feature set. I expect that there are bugs and unwarranted limitations remaining to be discovered in that limited subset.
The HP-11C/15C outputs are now smart enough to suppress the “1” mantissa when
EEXis used without a preceding number. We could do this in the other outputs, but this optimization has its primary value in the key-coded Voyager program formats, where it saves one of the precious few instruction slots.The program step+line reporting feature in 0.13.0 affected the output of
--line-numbersas well, which was not the intent. Restored the prior functionality.The HP-11C/15C inputs were dropping standalone CHS ops, being those not tied to any numeric literal.
Sorted out a tangle in
DEGandRADop handling for DM32 and HP-11C/HP-15C.
Version 0.14.0, 2026-03-01
Added the
--to htmlformat, plus its companion--mathjaxflag for overriding the default usage of theASCIIMathML.jsvariant used within the Enter RPN repo.Added a
--to html-staticvariant of this which produces the MathML internally using a Go port of that sameASCIIMathML.jsinstance. The--mathjaxflag therefore does not apply, but the upside is that the output is completely self-contained, without use of any JavaScript.For all test programs I have here in my collection, this output format produces no meaningful DOM differences relative to the first after JS rendering. If there is a difference, it is in ops I do not test against.
Added the
--cssflag affecting both, allowing overriding of the purposefully spare default CSS they emit with an external stylesheet supplied by the end user.Added a mapping for
SOLVEops found in HP-15C and DM32 inputs to a rough equivalent in the R47 form based onPGMSLVandMVAR. This experimental feature appears to work for simple cases, but feedback is solicited from those who can point out missed corner cases and better mappings.Added a similar mapping for numeric integration. Prior releases mismapped DM32 inputs to the interactive
∫fdop on the R47, but we wantPGMINTfor programmed numeric integration.Fixed a bug in UTF-8 outputs from DM32 inputs which caused it to nondeterministically emit either
ENDor.END.as the last op.Fixed a bug in named register mapping in DM32 inputs: “
RCL X” refers to the user register in the A-Z set, not the stack’s X register. It now becomes “RCL ‘X’” at the R47 layer.
Version 0.13.0, 2026-02-26
Added
--fmtflag (default off) for in-place reformatting.Added
--comma-radixoption to override the default usage of period as the decimal separator in cosmetic outputs, with limited support for it on input, in accord with the program file format rules.STO RAN#ops in HP-11C/15C programs now become calls to the R47’sSEEDop, orSDon DM32. This replaces the prior literal-minded behavior: storing to a regular named variable called‘RAN#’which could have no effect on the R47’s PRNG behavior. We useSDon the DM32 instead, and we undo these transformations in lowering.Added mappings for numbered system flags on the HP-15C and DM32 to raise them to the named form used by the R47, then back down on output. Numbered system flags with no mapping are passed through unchanged, as are the always-numbered user flags.
The process of appending a trailing radix to mark numbers as real now occurs only on UTF-8 (
.p47u) outputs. All other paths are either explicitly data typed or are weakly-typed but BCD-only and thus do not need to have pointless (hah!) bytes injected.Improved handling of literals in do-if-true context:
Suppressing automatic addition of “.” for these literals under a do-if-true condition when outputting to formats that encode literals key-by-key — currently the HP-11C and 15C — since the conditional affects the next instruction only.
(This goes beyond the trailing radix reduction improvement.)
We still add them on raising inputs to the R47 form since it stores literals whole and needs the disambiguating dot to indicate that the input value was a real number.
The DM32 must straddle this divide: it adds the “.” on input unconditionally since it is not affected by the key-by-key restriction, but it strips them on output because the DM32 only supports real numbers.
The HP-15C and 11C input modules now issue a fatal error if they find a numeric entry key op (0-9, EEX, or “.”) guarded by such a test which is then followed by another piece of the literal. We cannot translate these split literals into R47 form. (Not doing it for CHS since that translates 1:1 in this situation.)
Line numbers referenced in output messages can now give both the file line and the RPN step index when they differ.
Giving
--output/ono longer produces an empty output file for the--to hp15ccase if there is a processing error.
Version 0.12.0, 2026-02-24
Added support for the RLM-15CX simulator’s file format, implemented as a simple JSON parser/generator and data massager in front of the existing hp15c format handlers.
Added support for the RLM-11CX simulator’s file format, implemented as a partial but hard fork of the existing HP-15C handling due to the significant amount of differences between the two machines.
Refined support for the several uses of “
I” in HP-15C I/O:- “
RCL (i)” raises to “RCL →I” in R47 form - “
I” alone (as an operation) maps to the R47 opCOMPLEX - “
FIX I” sets the display mode from the register indicated by the contents ofI
These had to be teased apart from 11C handling which lacks complex arithmetic. Plus, 11C ops like
𝑥⇄Iare more generic on the 15C.- “
Added round-tripping for “
STO/RCL RAN#” on HP-15C. This becomes a named variable at the R47 layer due to lack of a reserved variable of that name having the same effect.Added the HP-15C
DIMop. (MATRIXgenerally not yet working.)REMops found in input become inline comments in--to hp15coutput, and vice versa for--from hp15ccontaining such.Programs read in via
--from hp15cnow include implicitENDand.END.statements so that when writing them out to an R47 format, they show up. This improves round-tripping fidelity in chains that start with R47 formats.Tolerating comma in place of dot for registers in
--from hp15c. They’re normalized to R.0 and such on output.Numeric labels in HP-15C input no longer become single-character string labels with that digit in R47 output. It maps 0-9 to R47 00-09 and .0-.9 to 10-19, with lossless round-tripping.
File format guessing based on extension now does case-insensitive matches. (Some files in the Manz example collection use
*.15C)Reals starting in a string-coded form (as with the HP-15C input) are kept in that form unless they are long enough to benefit from being translated into the R47’s version of decNumber 34-digit values. This keeps input forms lossless through round-tripping.
Values that look integer-like in DM32 input have a decimal radix appended to force them to be considered reals in R47 output. We do this for the same reason as our HP-15C I/O path always has: the HP-32S line doesn’t have a distinct integer data type, so any value sourced from it must be treated as a real.
DM32 output now includes a more detailed header comment modeled on the style invented for our
--to hp15coutputs.Improved handling of
CMPLXop mapping in DM32 I/O.The
--from/tooptions now unconditionally override the file type guessing based on extension. It guesses only when forced. With “-” for stdin/stdout, it continues to use the existing defaults when these options are not given since guessing is impossible then.Added
.txtas another alias for our pretty-printed version of R47 programs in file extension guessing.Fixed handling of ops like
FIXin DM32 output when the op originated from another format.
Version 0.11.0, 2026-02-21
Added experimental HP-15C support via the Manz simulator’s program format, which gives us interop with four different calculators.
DM32 output now halts if the input includes a short integer in a base it cannot support with the numeric display mode ops.
DM32 output now warns if it sees
ENDwithout anything beyond it other than one last optional.END.op.Special-cased the output for the DM32’s oddball “
∫FN d F” op.
Version 0.10.0, 2026-02-19 (yes, later that same day)
DM32 output can now include date, time, and DMS angle literals from the input, lowered from R47 typed form into untyped HP decimal form. This includes a fix to allow recognition of fractional seconds in DMS angle literals in R47 inputs.
Emulating the
DROP𝑥op on DM32 output as CL𝑥 then add, making use of the additive identity: the prior Y + 0 = Y as the new X.When giving multiple input files (
./rejig tests/r47/*) it now infers the input file type from the file name extension separately for each.Complex decomposition in DM32 output now works for the binary case as well.
Improved handling of LBL conflicts after smashing them down to a single uppercase letter.
Added translations for the DM32 linear regression slope (
m) and Y-intercept (b) ops by making use of the R47’s newLR:a?ops, which expose these values in a generic way, not tied to L.R.Added the new R47 energy conversion ops: erg, foe, joe.
Improved handling of op name aliases in DM32 I/O.
Added the
x⇋yalias. (Old style used on my site before I settled on the R47 style,𝑥⇄𝑦.)Fixed a bug with “
GTO D” type ops in DM32 output.
Version 0.9.0, 2026-02-19
DM32 I/O recognizes
CMPLXops and attempts to map R47 complex literals to the DM32 scheme where the real and imaginary parts take a stack register each.Op names with fewer than 3 ASCII characters are now case-sensitive in lookups to avoid confusions between
Rthe molar gas constant andrthe linear regression correlation coefficient. Three English letters gives 26³=17576 possible op names without adding in all the other characters one could use to make the name unique.And yes, this does mean
ris now supported as an R47 op alias forCORRand as a direct op name in DM32 programs.(Plus the
x̂andŷprediction ops.)Variables and labels can now be multi-lettered and non-alpha in DM32 inputs and outputs. These otherwise illegal referents are mapped to an unused A-Z, which is then remembered in case it reappears, in which case it reuses that mapping. It still tries to use the first letter, capitalized, but a clash will override that.
The DM32
/cop is now transformed on input into aDMXwith the literal preceding it becoming the op arg, and vice versa for output.Sequences of DM32 PSE ops are now counted and divided by 10 to give an approximately equivalent PAUSE op arg, and vice versa on DM32 output, with the PAUSE arg producing an equivalent PSE count.
Whole-line comments in DM32 input now produce
REMops.Fixed round-tripping
INPUTops thru the DM32 chain.Issuing a diagnostic in DM32 output when it encounters a string literal provided as input.
The
--versionflag now pulls its info from VCS info embedded by the Go compiler if present, which it should be if run from a Fossil checkout. (The prior solution required that, too.) The release builds still pass the Taskfile.yaml fileVERSIONvariable in, but for normal builds, the commands are shorter and clearer.
Version 0.8.0, 2026-02-18
- Added experimental DM32 statefile support, both input and output. There is much left TODO, but it’s functional within its limitations.
Version 0.7.0, 2026-02-17
Improved automatic blank lines in UTF-8 output. It now works on any number and ordering of
LBLandREM, with the whole group treated as one.Comments now align in UTF-8 output four spaces past the end of the longest line.
Added the
--fix-obsoleteoption to try upgrading>OBSOLETE<ops found in inputs to ones having the same name without the brackets, as long as the argument types are the same.Updated the op names to track R47 firmware 00.109.03.00b2. Changed names become aliases.
Using the canonicalized
tests/AllOps2.*to produce an alternative code coverage report viamake intcov. Ideally, this integration test should run all the code tested by the happy-path unit tests, plus things like file I/O, option parsing, etc. It will not test known error conditions since we want it to run to a successful conclusion every time.Straightened out a confusion between system flags — always named like ‘TDM24’ in UTF-8 output — and the numbered global flags 00-99. There is no such thing as “SF00” but there is “F00”. Without the leading F, it is treated as a numbered global flag: “SF 00”.
System flags do have underlying numeric values, but we see no value in allowing you to specify them as SF00 = ‘TDM24’ given that the R47 doesn’t do this itself.
This sorts out the single biggest remaining unintended difference between the upstream “AllOp’s” program and our local version, which now differs primarily in the added tests it offers.
UTF-8 output of the
SKIPandBACKops now uses zero-padded three-digit formatting, as the R47 does.Fixed handling of 3-digit
MSGargs.Added more op aliases.
Version 0.6.0, 2026-02-15
MILESTONE: This release round-trips the AllOps program.
Well, mostly!
If you
RESETyour R47 and save out a copy of this program to ourtestsdirectory and then run “make test” it will give several bytes of difference. As far as I can tell, these are due to the hand-coded nature of this program viagenerateTestPgmsupstream.Fixing these breaks other known-good tests, but if you instead say:
./rejig .../path/to/AllOps.p47 -o tests/AllOps.p47u ./rejig tests/AllOps.p47u -o tests/AllOps.p47...the resulting canonicalized program then round-trips reliably.
I’m calling that success pending proof that this process overlooks something important.
Added support for binary reals by vendoring the C47/R47’s custom fork of decNumber, built with the same options for 100% fidelity.
This also adds support for binary complex numbers.
WARNING: This only works for native builds, which ipso facto excludes five of the six release binaries I cross-compile on the release build machine. To fix that, I would have to maintain six different build boxes, then build a single native binary on each one. (Yes, yes, VMs blah blah, I know. It’s still a PITA I am unwilling to tolerate.) If you need this to work reliably, build from source.
P47 files with string literals marked as real but without a radix mark or “e” notation are normalized by appending a decimal in UTF-8 output so that they are not misinterpreted as long integers by the UTF-8 input parser.
There is no way to cause this to happen in PEM: entering a “0” will cause it to be coded as a long integer, same as in NIM. It only occurs in the C47
src/generateTestPgmswhich emits the programs you get onRESET, because it writes the program bytes directly, allowing it to declare a single-digit value as real.If you then
WRITEPone of these out, it won’t round-trip throughrejigbecause it changes these 1-character string-formatted reals to 2-character values, but with the proper type. This is why we now carry a local copy oftests/ReMp261.p47: to serve as an example of the difference. This one will round-trip because I went into PEM and manually re-entered the “1.” value on line 0006.Obsolete ops are now recognized as such, but it causes an immediate cessation of input processing for the same reason the R47 stops XEQ on encountering one of these.
The case-insensitive op name matching now works for ASCII letters only to avoid various confusions:
- visual: math italic 𝑥 to 𝑋, confusable with Greek χ
- semantic: σx to ΣX, changing its meaning to statisticians
Replaced the method of discovering the Fossil checkin hash used to compile the binary for use in the
--versionoption with one that does not require that you are using a recent trunk version.Fixed UTF-8 I/O of single-letter flags:
SF CString-formatted number literals of the form “1e3” are now considered reals, not integers.
Short integers of base > 10 now use uppercase A-F to match the P47 convention for string-formatted literals.
Fixed a conflict between shell-style comment parsing, the
#(zero) op, and short integer base tags.
Version 0.5.0, 2026-02-14 — the “Valentine’s Gift” release
Added support for date, time-of-day, time interval, and DMS angle string-formatted literals.
Added support for “
×10^” and “*10^” scientific notation in parsed UTF-8 inputs. (Previously only supported “e” notation.) This extends to complex numbers, where we must be careful not to be confused between the optional sign in the exponents vs the+/-separating the real and imaginary parts.Added handling for the shuffle (
⇄) op’s special arg type, which takes a register-mapping string like “TZYX” to mean “reverse the four main stack registers.” (Leave them as-is would be “XYZT”.)Added several missing ops:
CASE,DAY,DECOMP,MVAR,SINC,Δ%x̄₁(delta% mean),ζ(ϕ,𝑚)(Jacobi zeta)Added a large number of op aliases, most of which were needed to allow
PROGRAMS/AllOp's.txtfrom the C47 distribution to parse.In certain cases, it went the other way: our local version of that file has changes to conform to the
rejigconvention.Op name and alias lookup is now case-insensitive.
UTF-8 inputs can now use “bare” values instead of the less ambiguous references we emit on UTF-8 output:
- registers can be 00-99 or .00-.99 (“R” elided)
- system flags can be 00-99 (“SF” elided)
Prior versions already allowed local flags to be referenced as .00-.31, without the leading “F” we emit on UTF-8 output.
The UTF-8 short integer parser now uses
uint64internally for positive numbers so it can get the full 64-bit range. It drops back toint64only for negative numbers.UTF-8 input error messages now include the line number.
Fixed argument handling for several ops:
STO↑,STO↓,RCL↑,RCL↓PGMSLV,SOLVE∫fd
Fixed recognition of the
→INTop in P47 inputs and its name-to-op mapping in UTF-8 inputs.Fixed several cases of indirect access syntax, for both UTF-8 and P47 byte-coded inputs.
Fixed handling of args > 249 for the
SKIPandBACKops. These values normally mark a “special” arg (e.g. indirect access) but these two R47 ops allow the full 8-bit arg range.Removed the
?from theαLENGandαPOSop names. They are not do-if-true ops. They are also not getters, which is why they did not gain a “get” prefix at the same time.Op name parsing no longer succeeds if the arg follows without any separating whitespace. Previously,
PAUSE99would work, which might seem okay to a certain type of person, but it also meant it would try to treat a line likeLocR?as aLocRallocation with value “?” and fail with an obscure message.We have to flag cases like this clearly owing to our decision to drop the trailing “?” and prefix it with “get” to avoid treating these as do-if-true ops.
Bogus op names were being passed through as string literals. They now cause an error meant to aid diagnosis.
Version 0.4.0, 2026-02-12
Added the
--line-numbersoption to include 4-digit line numbers in UTF-8 outputs, as the R47 does in RTF and PEM outputs.Added support for string-formatted complex literals:
3.14-2.5𝒾Short integers in UTF-8 output use Unicode subscripts for the base. On parsing UTF-8 program text as input, it recognizes the new syntax while allowing the plain-ASCII (e.g.
#10) alternative syntax. “173₈” and “173#8” are both valid octal expressions of decimal 123.Improved handling of short integers:
- P47 input: improvements to recognition of binary vs short integer data byte code formats
UTF-8 input:
- use string-formatted short integers for values under 1e6
- use binary (
uint64_tLE) short integers for all else
P47, UTF-8 output: updated to track the above changes
Added special-case handling for the
KEYop, which cuddles the requisiteXEQ/GTOfollowing it on the same line, per the HP-42S.Straightened out a confusion regarding 𝜋:
- Used as an op name in UTF-8 program input text, that math italic character refers to the 🟧-shifted R↓ key press. We also accept as aliases “pi” and the unstyled “π”.
- The mathematical bold italic character “𝝅” refers to the predefined constant backing this, which has a separate op code.
Added several op aliases:
- All predefined CNST ops except 𝝅 lest we break the above. In a few cases, the primary op name changed instead, and what we were using as the name became the alias.
- Added trig aliases to match on-calculator names:
COSforcos(𝑥)and so forth. Also added ones likearccosh(x)foracosh(x) - Added
CEILandFLOORaliases for⌈𝑥⌉and⌊𝑥⌋.
Changed
ViewandTestop names to all-caps to match their display on the calculator.Added “
p47u” as an alias for “utf8” in--from/tooptions, though we continue to prefer the latter as more visually distinct.The
--from/tooptions can now be left off when giving file names using the supported format names as extensions.Extended the blank-before-LBL in UTF-8 output to the common
REM+LBLpairing. (Does not yet handle multipleREM.)No longer doing indent reduction on
RTNif it is itself being indented extra under do-if-true logic.Fixed a bug in the decoding of “
STO M” registers and beyond due to a bounds check error in the range of local registers: they run to R.98 max, not R.99.Fixed UTF-8 output for indirect variables, e.g. “
STO →‘foo’”
Version 0.3.0, 2026-02-11
Added operation name aliases:
- mathematical italics: DROPx = DROP𝑥
- blackboard letters: T->zyx = 𝕋→𝑧𝑦𝑥 (also ASCII arrows!)
- inverses like
cos⁻¹(𝑥)may be given asarccos(x)oracos(x) - Unicode subscripts now allow LaTeX equivalent; e.g.
log_2(x)forlog₂(𝑥) - ops may have multiple spellings, e.g.
lb(x)forlog₂(𝑥) - and more!
This then allows more extensive use of mathematical italics in op names, making the P47U language (such as it is) prettier without being less type-able. Type in plain ASCII, then run your program thru a
rejig -f utf8 -t utf8pass to “upgrade” it to the canonical form of the op names.Added a blank line before labels after the first in pretty-printed output to visually break up routines containing subroutines.
The P47U pass-thru mode now preserves end-of-line comments and transforms comments alone on a line to
REMops. Comment styles are normalized to the assembly language style (;) and are aligned four spaces past the end of the longest line seen in the input, clamped to column 20 as a minimum. This makes it less painful to use rejig to “upgrade” existing text program files.Fixed the extra indent applied after a do-if-true test op: it was being applied to that line instead of the next.
Fixed the definition of the “∥” (is parallel to) op.
Version 0.2.0, 2026-02-10
Added support for unsigned binary short integer literals.
Consequently, shell-style comments now require a space before them to be interpreted as such, else the parser would truncate the base annotation. (e.g.
7f#16) Applied the same rule to assembly style comments (;) just in case; needed for matrix literals?Added handling of “
-” as the input file name to read from stdin. It also allows this for--output/-ofor symmetry, but all this does is make it as if the option was not given, since this is the default behavior.Added code coverage reporting, and expanded the self-tests to bring that metric up to ~90%. The bulk of what’s missing are the known error cases.
Added a
Makefileas a wrapper for the Task build system, for the convenience of those used to it.Implemented several MIA ops.
Fixed parsing of several ops:
- matrix index inc/decrement:
I+,I-,J+,J- - matrix dimensioning:
M.DIMandM.DIMgr
- matrix index inc/decrement:
Fixed UTF-8 parsing for indirect register/variable access using a Unicode arrow (as opposed to the ASCII “->” alternative)
Using the same Unicode character to indicate swaps (⇄) as in the C47 custom font. I mistakenly thought this was a nonstandard custom glyph, but it is merely giving a different appearance to something semantically identical, U+21C4. We can therefore safely use it as-is across fonts.
Tightened up parsing of quoted strings: the closing quote style must match the opening quote. Not only does this mean single vs double, it means if you start a string with a curly quote, you must use the opposite-curled version to close that quote.
Version 0.1.1, 2026-02-08
The
.END.marker in P47 files is now preserved: if present on input, it appears on output, and if not, then not. This has a few side effects:- Removed the diagnostic for a missing “end marker.” It isn’t a
bug in the R47 but simply an indication that the program
happened not to be the last one in memory at
WRITEPtime. - The
ENDop’s hard-coded indent level is now 1, not 0, because.END.must now be hard left-aligned. - Updated all the
../*.p47ufiles with the new UTF-8 output to allow the round-trip testing to pass. These differences are now considered part of the test.
- Removed the diagnostic for a missing “end marker.” It isn’t a
bug in the R47 but simply an indication that the program
happened not to be the last one in memory at
Improved handling of string literals:
- strings as inline program values now work
- length is now preserved; Go’s notion of string length does not match that of the R47, and it is the latter that matters here
Fixed processing of op codes 128-255 on P47 input.
Version 0.1.0, 2026-02-07
Added the
--output/-o filenameoption for writing output to a named file instead of to stdout.Implemented many more op codes:
- 2, 3, 4, and N-register stack ops
- data type checks
- equation solve/plot
- information unit multipliers
- matrix row and column manipulations
- specialized STO-to-register and STO reserved variable variants
- system flag getters
- unit conversions
- week-of-year get/set
- uniform distributions
- vector manipulation
- XFCNS (3-register FMA arithmetic)
...plus a whole range of grab-bag ops
Renamed the xth-root-of-y operation name from
root(x,y)toroot(x)(y)to conform to LaTeX math notation.Renamed all operations and constants with multi-character subscripts to put them in parens per LaTeX notation rules, without which the second and later letters render normally.
Windows console output is now UTF-8 when it is redirected to a pipe instead of using the default code page. Without this, one is most likely to get UTF-16 with a BOM, causing the R47 to fail to read our P47 output as it is expecting UTF-8.
The end markers in P47 files are now optional. A diagnostic is emitted even though we have a report of this happening in a real file. It’s still something the user should be aware of, if only as a hint that they should use “-f p47 -t p47” to clean it up.
REMops are marked as having no “argument” in the R47 code, but of course we must still consume the following label in this program, even if just to discard it lest we interpret it as program bytes.Fixed parsing of ops with “value” typed arguments, which use one of two different (!) 16-bit encodings.
Fixed several problems in the way -t utf8 printed system flags.
Minor fixes to
NOPhandling.Better diagnostics.
Version 0.0.1, 2026-02-06
- Initial release, announced on the SwissMicros forum