[Part 1 of the HP Calc series]

I recently started a super-geeky side hobby of collecting vintage calculators and got my hands on a pair of HP calcs. The more I learned about the internals of the devices, the more intrigued I was. Jacques Laporte has an absolutely wonderful site going into crazy detail.

In 1972, Hewlett-Packard amazed the engineering world and killed the slide rule with the release of the HP-35 calculator; the very first hand-held electronic scientific calculator. Most astonishing was that apparently even cute girls loved RPN and were impressed by the device’s ability to do transcendental logarithm and trigonometry functions.

Cute HP-35 User
Photo by Hewlett-Packard

Brilliant Microcode

The secret sauce was the use of the CORDIC algorithm; reducing absolutely everything to just addition/subtraction and shifting. Jacques has an excellent explanation of CORDIC. All of this was implemented by David Cochran in a ridiculously compact 960 bytes (!) of microcode - three ROMs of 256 10-bit instructions.

The microarchitecture is very simple. There’s no memory, just 12 status bits and a field select pointer into seven 56-bit (fourteen 4-bit words) registers each representing 10-digit BCD floating point numbers and a 2-digit exponent, plus signs of each. The instruction set includes extremely primitive things such as the normal compare and jump/branch to a given address (relative to the currently selected ROM), adding, subtracting and shifting of portions of registers, toggling the display, etc.

To get the 40-year-old bits off the ROMs Peter Monta had the crazy idea of cutting off the metal heads and literally looking at them; you can see the bits!

rom1-full rom-example
Photos by Peter Monta

Eric Smith took this further; reverse engineering the instruction set, disassembling and even correlating comments and labels from an HP-45 patent. It's kind of a bummer that the puzzle has already been unraveled because that had to have been some fun! To simulate the hardware, Eric wrote CASMSIM (now Nonpareil) and David Hicks ported it to Java and Jacques Laporte has adapted it to simulate the HP-35. I spent a good bit of time looking at Eric’s and David’s code and could not have done the reverse engineering on my own! By the way, the original microcode is not copyrighted.

JavaScript-based Microcontroller

I had the (silly) idea of building a JavaScript-based emulator. Looking at Peter Monta’s Python-based disassembler and his object code listing, it looked simple enough to automatically generate script from David Cochran’s original bits. I wrote my own little straight forward 100-line disassembler in F#, producting an array of JavaScript functions:

let disassemble code = 
  let n = (code &&& 0b1111111100) >>> 2 
  match code &&& 0b11 with 
  | 0b11 -> sprintf "goto(%d)" n, sprintf "goto %d"
  | 0b01 -> sprintf "jsb(%d)" n, sprintf "jsb %d"
  | 0b10 –
    let instr = [| 
      "ifregzero(b,%s)", "if b[%s] = 0" 
      "regsgte(a,c,%s)", "if a >= c[%s]" 
      "setreg(c,b,%s)",  "b -> c[%s]" 
      "zeroreg(c,%s)",   "0 -> c[%s]" 
      "shiftl(a,%s)",    "shiftl a[%s]" 
      "sub(c,a,c,%s)",   "a - c -> c[%s]" 
      "setreg(a,c,%s)",  "c -> a[%s]" 
      "add(c,a,c,%s)",   "a + c -> c[%s]" 
      "regsgte(a,b,%s)", "if a >= b[%s]" 
      "shiftr(c,%s)",    "shiftr c[%s]" 
      "shiftr(b,%s)",    "shiftr b[%s]" 
      "shiftr(a,%s)",    "shiftr a[%s]" 
      "sub(a,a,b,%s)",   "a - b -> a[%s]" 
      "sub(a,a,c,%s)",   "a - c -> a[%s]" 
      "add(a,a,b,%s)",   "a + b -> a[%s]" 
      "add(a,a,c,%s)",   "a + c -> a[%s]" 
      "zeroreg(b,%s)",   "0 -> b[%s]" 
      "regsgte1(c,%s)",  "if c[%s] >= 1" 
      "negc(%s)",        "0 - c -> c[%s]" 
      "negsubc(%s)",     "0 - c - 1 -> c[%s]" 
      "setreg(b,a,%s)",  "a -> b[%s]" 
      "decreg(c,%s)",    "c - 1 -> c[%s]" 
      "ifregzero(c,%s)", "if c[%s] = 0" 
      "increg(c,%s)",    "c + 1 -> c[%s]" 
      "exchreg(b,c,%s)", "b <-> c[%s]" 
      "regsgte1(a,%s)",  "if a[%s] >= 1" 
      "add(c,c,c,%s)",   "c + c -> c[%s]" 
      "zeroreg(a,%s)",   "0 -> a[%s]" 
      "exchreg(a,b,%s)", "a <-> b[%s]" 
      "decreg(a,%s)",    "a - 1 -> a[%s]" 
      "exchreg(a,c,%s)", "a <-> c[%s]" 
      "increg(a,%s)",    "a + 1 -> a[%s]" |] 
    let f = n / 8 
    let i = instr.[16 * (f &&& 1) + (f / 2)] 
    let field = [| 
      "-1,-1", "p" 
      "3,12",  "m" 
      "0,2",   "x" 
      "0,13",  "w" 
      "0,-1",  "wp" 
      "3,13",  "ms" 
      "2,2",   "xs" 
      "13,13", "s" |] 
    let fld tup = (tup i).Replace("%s", tup field.[n % 8]) 
    fld fst, fld snd 
  | 0b00 –
    let arg = n / 16 
    match n % 16 with 
      | 0 -> "nop", "no operation" 
      | 1 -> if arg < 12 then sprintf "sets(%d,1)" arg, sprintf "1 -> s%d" arg
      | 3 -> sprintf "setp(%d)" arg, sprintf "%d -> p" arg 
      | 4 –
        if arg = 3 then "keyrom", "key -> rom" 
        elif arg%2=0 then
          let r = arg / 2
          sprintf "setrom(%d)" r, sprintf "rom %d"
      | 5 -> if arg < 12 then sprintf "tests(%d)" arg, sprintf "if s%d = 0" arg
      | 6 -> if arg < 10 then sprintf "loadconst(%d)" arg, sprintf "load %d" arg
      | 7 -> if arg = 0 then "decp", "p - 1 -> p"
      | 9 -> if arg < 12 then sprintf "sets(%d,0)" arg, sprintf "0 -> s%d" arg
      | 10 –
        if arg % 2 = 0 then 
          [| "disptoggle",        "disptoggle" 
             "exchreg(c,m,0,13)", "c <-> m" 
             "cstack",            "c -> stack" 
             "stacka",            "stack -> a" 
             "dispoff",           "dispoff" 
             "setreg(c,m,0,13)",  "m -> c" 
             "downrot",           "rotd" 
             "clearregs",         "clearregs" |].[arg / 2] 
      | 11 -> sprintf "testp(%d)" arg, sprintf "if p # %d" arg 
      | 12 -> if arg = 0 then "retn", "return"
      | 13 -> if arg = 0 then "clears", "clearstatus"
      | 14 -> if arg = 11 then "// NOT USED BY HP-35", "data -> c"
      | 15 -> if arg = 0 then "incp", "p + 1 -> p"let dasm = 

  use output = new StreamWriter(@"C:\Users\ashleyf\Documents\HP-35\rom35v2.js")
  output.WriteLine("var rom = [");
  // Disassemble bits from http://www.pmonta.com/calculators/hp-35/35v2.obj
  readFileLines @"C:\Users\ashleyf\Documents\HP-35\35v2.obj"
  |> Seq.map (fun line -> Convert.ToInt32((line.Split ':').[1], 8)) 
  |> Seq.map disassemble 
  |> Seq.iteri (fun i (js, asm) -> output.WriteLine(("    " + js + ",")
                                   .PadRight(25, ' ') + (sprintf "// %s" asm))) 
  output.WriteLine("];");

This spits out a 770-line script file something like:

var rom = [ 
  jsb(55),         // jsb 55 
  goto(191),       // goto 191 
  sets(8,0),       // 0 –> s8 
  goto(5),         // goto 5 
  sets(5,1),       // 1 –> s5 
  sets(9,1),       // 1 –> s9 
  sets(2,1),       // 1 –> s2 
  setrom(2),       // rom 2 
  jsb(84),         // jsb 84 
  goto(66),        // goto 66 
  goto(23),        // goto 23 
  goto(48),        // goto 48 
  stacka,          // stack –> a 
  goto(217),       // goto 217 
  zeroreg(a,0,13), // 0 -> a[w] 
  increg(a,-1,-1), // a + 1 -> a[p] 
  ... 
  loadconst(5),    // load 5 
  goto(129),       // goto 129 
  setp(5),         // 5 –> p 
  goto(126),       // goto 126 
];

Each of these functions are called once upon initialization of the ‘rom’ array, but with a bit of functional programming trickery, each of these return a new thunk; a closure taking no arguments, capturing the original arguments. Each thunk may be then be called at execution-time causing side effects in the registers. The whole emulator is about 400 lines of JavaScript (you can get the source here). For example, here’s the implementation for just a few of the instructions:

function goto(addr) { return function () { if (prevCarry != 1) pc = addr; }; } 
function jsb(addr) { return function () { ret = pc; pc = addr; }; } 
function sets(num, val) { return function () { s[num] = val; }; }
function setrom(num) { return function () { offset = num * 256; }; }
function loadconst(num) { return function () { c[p] = num; p = (p - 1) & 0xf; }; }

Now, given a selected ROM offset and an instruction pointer, execution of the ROM is quite literally a simple matter of continually calling: rom[offset + pc++](); at some interval.

Execution Model

Two of the registers (A and B) drive the display on the calculator. There are a couple of instructions for turning off and toggling the display. Normally, the display is turned off, then the registers are updated while producing the result and then the display is turned back on. That is why you see the display flash as you interact; it's what happens on the actual device too.

You may think in terms of code executing in response to events but the microcontroller model is completely different. Code is executed continuously. At one point the program, status bit zero is checked. This is a special interrupt-like bit set by hardware when a key has been pressed. The key grid corresponds to ROM addresses and so there’s a “key to ROM” instruction that does a jump based on the pressed key. Each “event handler” is waiting at that particular address. When no keys are being pressed and no active calculation is in progress, the program spins in a loop checking status bit zero.

I initially built the “CPU” with a setInterval(…) constantly running in the background with appropriate timing to accurately match the actual device. I’ve since made an optimization (to save battery life on the iPhone for example) of suspending JavaScript execution once the microcode goes into its status-flag-zero-polling loop and resuming when a keypress event is seen; thus sort of indirectly making the microcontroller event driven.

The Result

With a little UI, the final result turned out nice. It works well on the iPhone too. It is running the v2 ROM with known bugs (which I actually find quaint)

35

Be sure to tap the display to toggle into tracing mode which is slow but lets you watch the registers. Normally it runs at the speed of the original device, but toggling into “fast mode” (green display) will run much faster.

Attribution

The beautiful microcode is Dave Cochran’s work. The marvelous reverse engineering is due to Eric Smith, David Hicks, Peter Monta and Jacques Laporte. Because my little contribution is based heavily on these guys and because some of their work is GPL’d, I’m also releasing the source. Have fun doing whatever you like with it!

More…

In case the above isn’t already riddled with enough links to keep you busy, here are some others I want to share:

 

Next>