Type a URL, get a scannable QR code on the TI's screen. Written in TI Extended BASIC — no assembly, no host-side help. The TI does the Reed-Solomon arithmetic itself.
Discussed on r/ti994a: Anyone using Claude Code to program the TI-99?
- TI Extended BASIC — the program uses
::statement separators,LINPUT,ON…GOSUB, and multi-statement lines. It will not run in console TI BASIC without a rewrite (see Porting). - 32K memory expansion — the lookup tables alone are about 8K. Without the
expansion, XB programs live in VDP RAM and the
DIMon line 160 will fail. - Emulator or real hardware, either is fine.
QRGEN.BAS is a plain-text listing with line numbers, so anything that can feed
text to Extended BASIC will work.
Most TI emulators can type a text file or the clipboard into the emulated
keyboard. Start Extended BASIC, get to the > prompt, and paste the listing.
- Swift 99/a (macOS) — Edit → Paste (⌘V)
- Classic99 (Windows) — the Edit menu has a paste-to-XB option
- JS99er (browser) — drop the file onto the window, or use its file loader
Then RUN.
Pasting takes a while. Emulators deliberately pace synthetic keystrokes so the TI's keyboard auto-repeat doesn't produce doubled characters. In Swift 99/a that's 75 ms per character plus half a second after each newline, so this 7 KB listing takes roughly ten minutes to type in. Paste it in a few chunks if you'd rather not watch it in one sitting, and don't switch to maximum speed to hurry it along — that's exactly what trips the auto-repeat.
Once it's in,
SAVE DSK1.QRGENto a mounted disk image so you only pay for this once.
The xdt99 toolchain converts a plain-text listing into a tokenized program file and writes it to a disk image. Per its docs, that's roughly:
xbas99.py -c QRGEN.BAS -o QRGEN
xdm99.py -X SSSD qrgen.dsk -a QRGEN -f PROGRAM
Check the xdt99 documentation for the current flags — they have changed between releases. From there, write the image to a floppy or serve it over TIPI / a CF7+ / an nanoPEB.
It's 183 lines, longest one 93 characters. People have retyped worse.
URL: HTTPS://EXAMPLE.COM
The program picks the smallest QR version that fits, encodes, computes the error correction codewords, and draws the code. Building the tables takes a few seconds; encoding takes from about ten seconds (version 1) up to a minute (version 5) at real TI speed. An emulator's maximum-speed mode makes that instant.
While the code is on screen:
| Key | Action |
|---|---|
M |
Redraw with the next mask pattern |
N |
Start over with a new URL |
Q |
Quit |
If your phone won't scan a particular code, press M a few times. See
Mask patterns below for why.
Scanning off a CRT works, but it's fussier than off an LCD — let the phone's autofocus settle, and avoid a shallow angle where the shadow mask moirés with the modules.
The program supports QR versions 1–5 at error correction level L.
| If your URL is… | Maximum length |
|---|---|
Digits, capitals, and $ % * + - . / : and space |
154 characters |
Anything else (lowercase, ?, =, #, …) |
106 characters |
Uppercase URLs pack roughly 45% more data into the same code, because they use
QR's alphanumeric mode (11 bits per two characters) instead of byte mode (8 bits
each). The program detects this and switches automatically — HTTPS://EXAMPLE.COM
is a version 1 code, while https://example.com needs version 2.
Host names are case-insensitive, so shouting your URL is free. Paths usually aren't.
Beyond 154 characters the program says so and asks again.
The TI's graphics mode gives you 32×24 characters of 8×8 pixels — not enough
cells for a 37×37 QR code at one module per character. So each character cell
holds a 2×2 block of modules, 4×4 pixels each, and the program defines
characters 128–143 as the sixteen possible blocks. Setting one module is a
read-modify-write: CALL GCHAR the cell, add the new quadrant's bit,
CALL HCHAR it back.
The screen colour is set to white, so the border and the unused screen around the code form the quiet zone a scanner needs. Nothing else is printed on the QR screen for that reason.
There is no module matrix in memory. Whether a given position is a function module (finder, timing, alignment, format info) is decided by a single arithmetic expression in line 1570, so the data bits can be walked in the standard zig-zag and plotted straight to the screen as they are computed.
The QR specification says an encoder should build the code eight times, score each masking pattern against four penalty rules, and keep the best. That means eight full matrix evaluations, which is minutes of work in BASIC for a benefit you usually can't see.
This program uses mask 0 and lets you cycle through the rest with M. Over 400
generated URL-style codes, 399 scanned on the first try; pressing M fixes the
rest. The trade is deliberate, and it is the only place this program knowingly
departs from what a desktop encoder would do.
TI BASIC has no bitwise operators, which QR needs in two places:
- XOR is a 256-entry lookup table of nibble-against-nibble results
(
XT), built at startup by bit-doubling. A byte XOR is then two lookups. The obvious alternative — a loop over eight bits — would have made the Reed-Solomon pass roughly ten times slower. - GF(256) multiplication uses log and antilog tables (
LG,AL) over the QR field polynomial 285, so a multiply is two lookups and an add.
The Reed-Solomon remainder is computed with a shift register RW of just
NE codewords rather than dividing a full-length polynomial in place, which
keeps the data codewords intact and saves about 900 bytes.
E=E+255*(E>254) in the inner loops is a conditional subtraction without a
branch: relational expressions in TI BASIC evaluate to −1 or 0. The same trick
drives line 1570's function-module test and the mask predicates at 2100–2170,
where -(condition) becomes a clean 1 or 0.
| Lines | What happens |
|---|---|
| 160–430 | Table construction — XOR nibbles, GF(256) log/antilog, version capacities, format-info bit strings |
| 440–630 | Prompt, mode detection (alphanumeric vs byte), smallest fitting version |
| 640–830 | Data codewords: mode indicator, character count, payload, terminator, pad bytes |
| 840–1060 | Reed-Solomon generator polynomial and remainder |
| 1080–1610 | Drawing: patterns, format info, then the zig-zag data walk |
| 2000–2040 | Plot one dark module (the 2×2 read-modify-write) |
| 2100–2170 | The eight mask predicates |
| 3000–3060 | Bit appender — packs PC bits of PV into the codeword stream |
Versions 1–5, error level L, single Reed-Solomon block, mask 0 by default.
- Version 6+ would need interleaved error correction blocks, and from version 7, version information areas. 41 modules is also the point where the 2×2 packing runs out of screen rows — 21 rows of code plus the quiet zone is about all 24 rows will hold.
- Levels M, Q and H are single-block only through version 3, so supporting them properly means the same interleaving work.
- Kanji and ECI modes aren't implemented and aren't likely to be useful here.
- Automatic mask selection — see above; the four penalty rules are the straightforward part, running them eight times is not.
Patches welcome, especially from anyone who wants to take on interleaving.
Extended BASIC is doing four things here that console BASIC can't:
::multi-statement lines — mechanical to split, but the program grows past what unexpanded console BASIC can hold.LINPUT—INPUTworks if you accept that commas and leading spaces in the URL get eaten.ON MK+1 GOSUB …(line 1590) — replaceable with a chain ofIFs at a real speed cost, since it runs once per data module.CALL SCREEN/CALL COLORwith the same argument sets — console BASIC has these, but the character-set colour groups differ.
The bigger obstacle is memory: without the 32K expansion, console BASIC gives you roughly 14K of program-plus-variable space in VDP RAM, and the tables in line 160 want about 8K of that before the program itself is counted.
Written by Ale Rapoport, with Claude Code, and developed against Swift 99/a — Scott Ryder's TI-99/4A emulator for macOS. That project is separate and separately licensed.
The QR encoding follows ISO/IEC 18004. Thonky's QR Code Tutorial is the clearest free walkthrough of the same material if you want to follow along with the source.
MIT — see LICENSE.