TI-83 Apps

A desktop interpreter and a solver on the calculator

I have a TI-83 Plus, and I got into programming it from two directions. One is a desktop interpreter for TI-BASIC, so I can paste a program in and watch it run without copying it onto the calculator. The other is a native app that runs on the calculator itself: a quadratic and complex-number solver for my Algebra 2 coursework.

TI-BASIC in one Python file

It's a single file, about 2,700 lines, with a Tkinter GUI and nothing outside the standard library. A hand-written lexer and a recursive-descent parser build a tree. Control flow gets precomputed into jump tables at parse time, so a Goto out of a loop needs no stack unwinding. The emulated 16-by-8 text screen and a 95-by-63 graph screen are drawn by a worker thread into shared state that the GUI polls.

The part I'm proudest of is the speed control. Playable games need roughly 80 to 120 statements a second. That's about ten times slower than my first guess, and it's why the slider runs on a log scale from 10 to 200,000 statements a second, with unlimited at the far right.

  • Never touch the GUI from the worker thread. Tk isn't thread-safe. Results go through a queue that the main thread drains.
  • Don't use sleep(0) as a yield. It starves the waiting thread. A small real sleep every couple of thousand statements keeps the Stop button clickable, and it still runs about two million statements a second.
  • Don't cap the sleep. Capping each wait put a hard floor under the speed, so every setting below that quietly ran at the same rate. Sleep the full remaining time in small pieces, checking the stop flag each time.
  • Match keywords longest-first across real and ignored commands together. Separate passes made the ignored Horiz swallow Horizontal, and the real Disp swallow DispTable.
  • Never register a bare capital letter as a function. Using T for transpose shadowed the ordinary variable T and broke ordinary programs.
Pasted programs get broken names. Things like Px, VX or a list name with its prefix lost in copy-paste are invalid on a real TI-83 too. I report them and never map them silently, because a silent fix hides a real typo.

It's published on GitHub under the MIT licence. The coursework programs I wrote for it aren't.

A Z80 app for the calculator

The solver is called ALGEBRA. It's a MirageOS program of about 16 KB, written in C and compiled with SDCC. It has thirteen algebra tools, exact 16-bit rational arithmetic, complex numbers and fixed-point decimals. I targeted the Z80 because MirageOS is Z80-only, and the calculator I actually own is a TI-83 Plus.

I tested it in an emulator. A script builds the host version, runs the real Z80 binary under an emulator with a stubbed ROM, and compares 62 scenarios against the host build. That caught more than the unit tests, and most of what it caught was compiler flags.

  • Constants from memory were wrong. The GetCSC routine lives at 4018h, not the 4972h I had in my head. Every hardware constant now gets checked against the include file that ships with the OS.
  • SDCC 4.2 miscompiles stack arguments when you combine frame-pointer omission with reserving the IY register. The startup object has to be linked first.
  • The spec's example was wrong. The worked complex-division example in the spec doesn't match the math. The implementation was right, so the test fixture went with the correct answer.
  • RAM is the open risk. MirageOS copies archived programs into RAM, and an 18.8 KB program already failed on this calculator. I'm holding the target to 14 to 16 KB.

Built, tested, and waiting on hardware

The interpreter is done and published. The solver is built and verified in the emulator, but it hasn't been installed on a real calculator yet. The install goes through my Raspberry Pi link, which needs to be running again before the file can be sent to the calculator. That's the last step, and it's the one I'm least sure of until I see it run on the hardware.

← back to projects