TI-59 Fast Mode: Running Programs at ROM Speed

What fast mode is

The TI-59 runs some programs that are not in RAM. The built-in functions Σ+, x̄, P→R, D.MS and Op 11–15 are short keycode programs stored in ROM, and Solid State Software modules hold up to 5000 more steps. TI built a faster way to run these programs. Between two steps the firmware skips work that a user program normally needs:

For a ROM routine such as P→R this is exactly right: 20–40 internal steps should look like a single operation, with no flicker and no way to interrupt them halfway. The same steps run in about 40 % less time.

The hack

The firmware keeps a program source digit that says where the running program comes from: 0 for RAM, 1 for the module, 8 for the ROM routines. The digit is tested in two different ways:

The values 2, 4 and 6 are never set by TI's code. They pass the first test but match neither bit of the second. With one of them in place, a user program in RAM runs on the fast path. No normal key writes this digit, so getting such a value there takes a firmware bug. TI-59 users' clubs found and published such bugs.

Rules for fast-mode programs

Allowed:

Not allowed. Each of these leaves fast mode and loses the result:

Differences you must expect:

Ending the program. This is where most fast-mode programs go wrong. The program must end with RST or R/S, and at that moment X must be 0, a number still being typed, or a value just shown by Pause or Prt. If X holds any other result, the calculator hangs with a dark display, and only switching it off helps. Switching off erases the program.

Ways in

All methods put X = 1.000000000002 (or …04, …06) on the display first; 1 + 2 EE 12 +/- = produces it exactly.

Flags and Fix after entry

Entry overwrites all ten user flags and the Fix setting. Set any flags you need inside the fast-mode program.

How the hack works

You need three facts about the calculator's internals to follow it.

  1. The status register. The firmware keeps its bookkeeping in one 16-digit scratch register. That register holds the Fix setting, the current program step, the user flags, and the program source digit.
  2. Operations come in pieces. A key such as St flg needs a second key, the flag number. The firmware stores the half-finished operation, waits for the digit, and then jumps to the code that completes it. It computes that jump target by combining address bits.
  3. HIR registers. HIR STO n copies X, digit for digit, into internal register n. HIR normally accepts only n = 1–8. Register 0 would be the status register.

The bug. Run from a program, St flg expects a two-digit flag number. If a key such as STO, EE or x⇄t arrives after the first digit, the firmware ends the operation early. But the code that decoded that key leaves one stray address bit behind, and St flg's completion adds its own bits on top of it. The jump lands in the wrong routine. After one more misrouted jump, execution ends in the code of HIR STO, with register number 0.

The payload. X is now copied into the status register, digit for digit. 1.000000000002 is laid out so that each digit lands in a useful field:

Why it starts running. Like every operation, the misrouted St flg ends in the display routine. That routine sees a non-zero source, assumes a ROM routine such as P→R is between two of its steps, and fetches the next step instead of formatting X. This is TI's own mechanism for running ROM routines; from here on every step takes the fast path, and the fetch still reads RAM.

Why the program starts at step 008. The RAM chips transfer only whole registers of 16 digits. A register is either one data register or 8 program steps (000–007, 008–015, …). The firmware therefore copies a whole block of 8 steps into a buffer, a shift register with the current step at the bottom. Within a block it only shifts the buffer down by one step; it reads memory only when the step counter moves on to the next block. Each shift moves an empty step (keycode 00) in at the top. Each regular jump (GTO, RST, SBR, BST, label search) marks the buffer stale, so counter and buffer always agree. The next fetch reads the target's block and shifts it down until the target step is at the bottom. After GTO 007, for example, the buffer holds step 007 and seven empty steps above it.

HIR STO knows nothing about the buffer. The hack sets the counter to 000 but leaves the buffer as it was, at the step that held St flg's flag number. Until the counter leaves block 000–007, the two disagree:

St flg therefore goes on the last step of a block: 007, 015, … (8k+7). Then nothing is left over: GTO 007 already filled the buffer with empty steps above St flg, so steps 001–007 are all empty, and the real program starts at 008. With St flg on step 003 and 1 2 3 4 on steps 004–007, the run types 234000 before step 008.

Acosta's "hi2" reads the flag number from the program. With St flg at 135 (8·16+7), that read already moves into the block 136–143. Steps 001–007 then run 137–143, and Acosta puts GTO 062 there.

Steps 000–006 are never read during the run, so they can hold anything.

Why the restrictions follow. All the other places that look at the source digit also ask only "non-zero?", and each draws the conclusion that suits a module program:

Limits

Everything here was observed on Calc-U-59, which runs the original TI-59 ROM unmodified, and not on a real TI-59. Op 09 and card reading or writing were not tested in fast mode.