HP-15C
This is a browser build of the HP-15C, HP's beloved advanced scientific RPN calculator - complex numbers, matrix arithmetic, root-finding (SOLVE) and numerical integration in a slim credit-card-sized case, launched in 1982. The HP-15C is a member of HP's Voyager series, built on the same HP Nut - a 56-bit serial BCD processor - as the HP-12C, and boots to its RPN display showing 0.0000, 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-15C keyboard or your physical keyboard (ESC = ON, ← = back), and reach the gold f and blue g scientific functions. The HP-15C system ROM is © Hewlett-Packard, self-hosted here (the redistributable nonpareil dump) for emulation.
Runs on: Web browser
HP-15C Online Emulator
Play HP-15C using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| HP-15C RPN | HP-15C | HP-15C | 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="hp15c-rom.js"></script> // window.HP15C_ROM: 12288 10-bit words (two ROM chips)
<script src="hp15c-nut.js"></script> // window.HP15CNut(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 continuous-memory message), tap a key to clear it so it settles to 0.0000, then paint the 11-digit 7-segment LCD each frame from the display registers:
var M = HP15CNut(HP15C_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.0000
(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 (hp15c-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 and HP-12C use, because all three 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. The 15C's ROM is two 1.5K-word chips at 0x0000-0x17FF and 0x2000-0x37FF in one flat instruction space; the gap between them reads as 0.
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.0000 was scripted (power-on, then a 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-15C (1982) is HP's beloved advanced scientific RPN calculator - complex numbers, matrix arithmetic, root-finding (SOLVE) and numerical integration in a slim credit-card-sized case - and a member of HP's Voyager series, built on the same HP Nut (a 56-bit serial BCD CPU, HP part 1LF5) as the HP-12C.
- 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-15C operating system is twice the 12C's: two 1536-word pages (
0x0000-0x17FFand0x2000-0x37FF, 12288 10-bit words across two R2D2 chips, 1LE2-0321 and 1LH1-0302). It implements the RPN stack, the transcendental and statistical functions, the complex-number and matrix engines, and the SOLVE / integrate root-finders. - RAM - nibble-register data store spread over both chips: status/user registers 0x00-0x07 and 0x10-0x17, and the main store 0xC0-0xFF, each register 14 digits - the extra bank is what gives the 15C its larger program / data memory.
- 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 (on chip 0), 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). The segment map is identical across the Voyager family.
- 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 scientific 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.