HP-12C
This is a browser build of the HP-12C, HP's iconic financial RPN calculator - launched in 1981 and still in production today. The HP-12C is a member of HP's Voyager series, built on the HP Nut - a 56-bit serial BCD processor - and boots to its RPN display showing 0.00, ready for entry. The core is written from scratch in JavaScript, ported behaviour-for-behaviour from Eric Smith's nonpareil, so the whole machine, the NUT microcode ROM, the nibble RAM, the memory-mapped Voyager LCD and the scanned keyboard, is emulated with no wasm.
Because the loop is ours, the Nut plugs into the shared debugger: single-step the processor, read and write the A/B/C/M/N field registers and nibble RAM, disassemble Nut code, and set breakpoints and RAM write-watchpoints. Type on the on-screen HP-12C keyboard or your physical keyboard (ESC = ON, ← = CLx), and reach the gold f and blue g financial functions. The HP-12C system ROM is © Hewlett-Packard, self-hosted here (the redistributable nonpareil dump) for emulation.
Runs on: Web browser
HP-12C Online Emulator
Play HP-12C using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| HP-12C RPN | HP-12C | HP-12C | grey | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
There is no upstream runtime to vendor: the HP Nut CPU, the Voyager LCD driver and the keyboard scanner are written from scratch in JavaScript, ported behaviour-for-behaviour from Eric Smith's nonpareil (proc_nut.c, digit_ops.c, voyager_lcd.c; GPL-3.0). Two plain-global scripts load in order:
// 1. the system ROM image, 2. the Nut core + Voyager LCD, 3. our boot loop
<script src="hp12c-rom.js"></script> // window.HP12C_ROM: 6144 10-bit words (page 0)
<script src="hp12c-nut.js"></script> // window.HP12CNut(rom) -> the machine
Boot. Construct the machine, run the Nut from address 0 so the ROM powers on (a cold machine shows the authentic Pr Error), tap a key to clear it so it settles to 0.00, then paint the 11-digit 7-segment LCD each frame from the display registers:
var M = HP12CNut(HP12C_ROM);
runToSleep(); // power-on -> deep sleep
M.press(0x18); runToSleep(); M.release(0x18); // ON -> "Pr Error"
M.press(0x81); runToSleep(); M.release(0x81); // any key clears -> 0.00
(function loop(){
for (var i=0; i<budget; i++) M.step(); // one Nut instruction each
render(M.segments()); // 11 x 7-segment bitmaps -> canvas
requestAnimationFrame(loop);
})();
The machine is ordinary objects - no wasm heap - so the debugger reaches every nibble directly:
| Member | Kind | What it does |
|---|---|---|
M.step() | method | Run exactly one Nut instruction (one or more machine cycles). The single-step primitive. |
M.executeCycle() | method | Run one machine cycle: fire the per-cycle keyboard scan and LCD refresh, then execute one word. |
M.a / M.b / M.c / M.m / M.n | field | The five 56-bit working registers as 14-nibble arrays. Also M.g, M.p, M.q, M.fo, M.s[], M.pc, M.stack[], M.carry, M.decimal. |
M.getUcode(a) | method | Read a 10-bit ROM word (side-effect free), for the hex/disassembly views. |
M.ramPeek/ramPoke(reg,nib) | method | Read/write one RAM nibble with no side effects - the debugger's memory + watchpoint surface. RAM registers 9 and 10 are the live LCD bitmap. |
M.press(kc) / M.release(kc) | method | Press/release a key by Nut hardware keycode - how both the on-screen keyboard and the physical keyboard inject keys. |
M.segments() | method | The 11 current 7-segment display bitmaps (with decimal/comma/annunciator bits) for the canvas. |
Debugger integration
The plug-in (hp12c-debug.js) reads the live core from window.EMU_BOOT and calls EmuKit.defineMachine with a transport, the Nut register set and the memory. The Nut disassembler is the shared /debugger/src/cpus/nut.js - the same decoder the HP-41C uses, because both are the same HP Nut CPU.
The Nut decoder splits on the low two bits of the 10-bit word: 00 the large misc/special family (status, pointer, register moves, RAM & display I/O), 01 the two-word long branch (gosub/golong with a 16-bit absolute target), 10 the field-select arithmetic on A/B/C over one of eight fields (p x wp w pq xs m s), and 11 the short conditional branch. One wrinkle: the shared hex/disasm views mask memory reads to 8 bits, but Nut words are 10 bits - so the embed publishes window.NUT_WORD_READ and the decoder pulls full-width words through it, keeping true word addresses.
What was customised for the debugger. The whole core is authored to be inspected: registers are read and written straight off the machine object, single-step is M.step(), and the run loop is ours. A cold-boot to 0.00 was scripted (power-on, then a CLx keystroke to clear the authentic Pr Error). Breakpoints are a host-side Set of 16-bit PC word addresses: when any are set the loop steps one instruction at a time and compares the PC. Write watchpoints sample the watched RAM nibble addresses after each stepped instruction and halt on a change, so a write anywhere on the RAM bus - including the memory-mapped LCD registers 9 and 10 - trips them. A/B/C/M/N are 56 bits, shown as a low and a high 28-bit half.
Architecture
The HP-12C (1981) is HP's landmark financial RPN calculator - still in production - and a member of HP's Voyager series, built on the HP Nut (a 56-bit serial BCD CPU, HP part 1LF5).
- Nut core - five 56-bit working registers
A B C M N(fourteen 4-bit digits each) operated a FIELD at a time (p x wp w pq xs m s), the two-digit scratchG, two 4-bit digit pointersP/Q, the 8-bit flag-outputFO, fourteen status flagsS0..S13, a 4-level return stack and a carry. Arithmetic runs in BCD decimal or hex mode. Program memory is 10-bit words. - ROM - the HP-12C operating system is a single 6144-word page (0x0000-0x17FF): the NUT microcode that implements the RPN stack, the financial engine (TVM, amortisation, IRR/NPV, bonds, depreciation) and the programmable keystroke language.
- RAM - nibble-register data store on the R2D2 chip (HP part 1LE2): status/user registers 0x00-0x07 and the main store 0xE0-0xFF, each register 14 digits.
- Voyager LCD driver - unlike the HP-41C's Coconut peripheral, the Voyager LCD is memory-mapped: the ROM writes the segment bitmaps straight into RAM registers 9 and 10, and the driver reads them through a per-segment (register, bit) map to light an 11-digit, 7-segment display (segments a-g plus a decimal point, a comma tail and a per-digit annunciator).
- Keyboard - a scanned matrix; each key is a hardware keycode
(column<<4 | row)latched into a buffer the ROM reads. Thef(gold) andg(blue) prefixes select the alternate function printed above/below each key. The ON key (0x18) wakes the Nut from deep sleep.
Every part is a plain JavaScript object, which is what makes the Nut a clear, fully steppable debugging target in the browser.