Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

QR Code Generator for the TI-99/4A

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?

Requirements

  • 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 DIM on line 160 will fail.
  • Emulator or real hardware, either is fine.

Getting it onto the TI

QRGEN.BAS is a plain-text listing with line numbers, so anything that can feed text to Extended BASIC will work.

Emulator, by pasting

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.QRGEN to a mounted disk image so you only pay for this once.

Real hardware, or building a disk image

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.

The slow way

It's 183 lines, longest one 93 characters. People have retyped worse.

Using it

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.

Capacity

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.

How it's drawn

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.

Mask patterns

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.

Arithmetic notes

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.

Reading the source

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

Scope and what's missing

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.

Porting to console TI BASIC

Extended BASIC is doing four things here that console BASIC can't:

  1. :: multi-statement lines — mechanical to split, but the program grows past what unexpanded console BASIC can hold.
  2. LINPUTINPUT works if you accept that commas and leading spaces in the URL get eaten.
  3. ON MK+1 GOSUB … (line 1590) — replaceable with a chain of IFs at a real speed cost, since it runs once per data module.
  4. CALL SCREEN/CALL COLOR with 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.

Credits

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.

License

MIT — see LICENSE.

About

QR code generator for the TI-99/4A, written in Extended BASIC. Reed-Solomon on a 1981 home computer.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages