Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ti83-link

Drive a TI-83 Plus link port directly from a Raspberry Pi's GPIO pins — no SilverLink, no USB cable, no proprietary software. Send and receive .8xp programs, .8xg groups, pictures and anything else the calculator stores.

Three wires and some Python.

Windows PC ──SSH──> Raspberry Pi 4 ──GPIO──> TI-83 Plus

Contents

Status · Wiring · Install · Usage · Flash applications · Interpreter integration · Notes from building this · Contributing


Status

Everything below works and is used against real hardware.

Variables — send, receive and delete .8xp programs, pictures and any other variable type. .8xg group files too, which is how Ion and multi-file games ship.

Flash applications.8xk apps install over the link, including MirageOS. **TIFL** container, Intel HEX decoding and paged transfer.

Memory — write to the 160 KB Flash archive as well as the ~24 KB user RAM, and delete variables to make room.

Authoring — a TI-BASIC source tokenizer, so you can write a program on a PC and put it on the calculator.

Not implemented, deliberately: OS transfer (.8xu). See Contributing.

Tested on exactly one setup: a TI-83 Plus (OS 1.19, boot 1.01) wired to a Raspberry Pi 4 Model B Rev 1.5 running Raspberry Pi OS Trixie. Everything else is unverified — reports from other hardware are genuinely useful.


Wiring

The link port is two open-collector lines with 10 kΩ pull-ups inside the calculator. You do not need level shifters, transistors or resistors.

Calculator Pi physical pin BCM TI line
TIP 36 GPIO16 D0 — asserted to send a 0 bit
RING 32 GPIO12 D1 — asserted to send a 1 bit
SLEEVE / GND 34 ground

Open-drain is emulated by switching pin direction:

  • released = claimed as input, internal bias disabled (high-Z)
  • asserted = claimed as output at level 0, with SET_OPEN_DRAIN

No pin is ever claimed as a plain push-pull output, and gpio_claim_output defaults to level=0, so there is no window in which a pin drives high.

On safety: worst-case backfeed into an unpowered Pi is (3.2 V − 0.6 V) / 10 kΩ ≈ 260 µA, limited by the calculator's own pull-up. That is not a hazard. Contention is prevented in software rather than hardware.


Install

On the Pi:

sudo apt install python3-lgpio gpiod

Copy the contents of pi/ to ~/ti83/ on the Raspberry Pi.


Usage

Checking the link

python3 bias_test.py       # wiring check - run this FIRST when anything breaks
python3 ti83.py ping       # is the calculator answering?
python3 ti83.py ver        # OS and boot code version
python3 watch.py           # read-only logic monitor on the two lines

Moving files

python3 ti83.py send GAME.8xp             # to user RAM
python3 ti83.py send GAME.8xp --archive   # to the 160 KB Flash archive
python3 ti83.py send SHELL.8xg            # a group: sends every variable in it
python3 ti83.py sendapp MIRAGEOS.8xk      # a Flash application
python3 ti83.py get  GAME                 # pull one back as a .8xp
python3 ti83.py del  GAME                 # delete, to free memory

Inspecting files, without a calculator attached

python3 ti83.py info    GAME.8xp     # variables, sizes, header, checksum
python3 ti83.py appinfo APP.8xk      # Flash pages, load addresses, packet count
python3 ti83.py decode  GAME.8xp     # hex-dump the TI-BASIC token stream

The calculator must be sitting at the home screen. The TI-83+ answers link packets silently in the background, but only while no program is running. Inside Ion or a game, transfers simply time out.

Throughput is roughly 1 KB/s — a 4,895-byte program takes 4.8 s, and MirageOS (124 Flash blocks) takes 41 s.


Flash applications

.8xk files are a different container from .8xp**TIFL** header, and the body is the application encoded as Intel HEX text, which decodes into 16 KB Flash pages.

python3 ti83.py appinfo MIRAGEOS.8xk   # inspect first, sends nothing
python3 ti83.py sendapp MIRAGEOS.8xk

Per 128-byte block:

VAR (10-byte flash header)  ->  ACK  ->  CTS  ->  ACK  ->  DATA  ->  ACK

and a single EOT -> ACK once every block of every page has gone. The 10-byte VAR payload is length(2) | type | length_high(2) | flag | offset(2) | page(2).

Two things the protocol guide gets wrong or omits, both resolved by reading libticalcs instead:

  • The guide says DATA carries "four extra bytes at the beginning". It does not. The load address and page number travel in the VAR packet.
  • A plain 6 MHz TI-83+ needs a 1 s pause after the second page and a 2.5 s pause before the last. Those same pauses break transfers on 15 MHz models (83+SE, 84+), so they must be conditional on hardware.

⚠️ sendapp refuses OS images (.8xu, data type 0x23) and always will. A failed application transfer is recoverable; a failed OS transfer can brick the calculator.

Interpreter integration

interpreter/tilink.py adds a Send to Calculator action to a TI-BASIC editor: it tokenizes the open buffer, wraps it in a .8xp, and ships it over SSH to the Pi, which clocks it into the calculator.

The editor it was built for is the companion project — ti83-basic-interpreter, a TI-BASIC interpreter with a desktop GUI. Write and debug a program there at whatever speed you like, then press F9 to put it on real hardware.

The tokenizer refuses unknown tokens rather than guessing — see below for why that matters.

DirectUSBTransport is a deliberate stub. A TI SilverLink speaks different framing to the two-wire link port; it is not the same bytes down another pipe, and shipping untested transport code that looks finished would be worse than an honest error message.


Notes from actually building this

Things that cost real time, recorded so they cost you less.

Read token values off hardware, not documentation

Every TI-BASIC token value taken from memory or a web source was wrong often enough to matter. Every value read back off the calculator was right.

Token Commonly cited Actual
ClrHome 0xE3 0xE1
Output( 0xE1 0xE0
getKey 0xE2 0xAD
While 0xD2 0xD1
< 0x6E 0x6B
0x6B 0x6F
space 0x20 0x29

That last one is the nastiest. 0x20 is the randM( token, not a space. A program built with 0x20 as its space transfers with a perfect checksum, round-trips byte-identically, and then prints randM( on screen.

Use ti83.py get + ti83.py decode to read real values off your own calculator. Type the token in a program, pull it back, look at the bytes.

EOT closes each variable, not the transfer

Sending a group as RTS/CTS/DATA ×N followed by a single EOT hangs: the calculator stops answering after the first variable's DATA and never replies to the second RTS. The correct flow is RTS → ACK → CTS → ACK → DATA → ACK → EOT per variable.

Normalise the version and archive-flag bytes

Files from the archives often carry a non-zero version byte and the archive flag set. The calculator may then answer RTS with an undocumented SKIP/EXIT 0x04. Documented codes are only 01 EXIT, 02 SKIP, 03 OUT OF MEMORY.

A non-zero version byte also means the program wants a newer OS. It will transfer happily and then throw ERR:VERSION at run time.

.8xg groups are the same container

A group file is byte-identical in structure to a single-variable file — the data section just holds several entries back to back:

hlen(2) | header(hlen) | datalen(2) | data(datalen)   ... repeat

Also accept the **TI83** and **TI82** signatures. Ion's group files use the former even for the 83+ build, and some level packs are renamed TI-82 files.

A sluggish calculator is a bus symptom

If the TI-83+ feels slow, freezes, or drops keypresses, it is blocked in its link-port routines waiting on a bus it cannot get control of. It is not a calculator fault. Re-seat the header wires and run bias_test.py. One loose jumper cost hours, disguised as stuck lines, ERR:XMIT, and a mysteriously laggy calculator.

Memory

RAM is only ~24 KB, and a program needs working room on top of its own size. The Flash archive is 160 KB but programs must be unarchived to run. Pre-loaded Flash apps can consume nearly all of the archive; GarbageCollect reclaims nothing if there is no garbage.


Contributing

This has been tested against one TI-83 Plus and one Raspberry Pi 4. If you run it on anything else — a TI-84 Plus, a Pi 5, a Pi Zero — results are genuinely unknown, and a report either way is useful.

Particularly wanted:

  • Multi-page Flash apps. The transfer loop handles them and follows the reference implementation, but the only app tested here (MirageOS) is a single page. The inter-page pauses in particular have never actually fired.
  • 15 MHz hardware. The pauses that a 6 MHz TI-83+ requires are known to break 83+SE / 84+ transfers. There is currently no hardware detection.
  • Direct USB / SilverLink transport.
  • More verified token values — a learn mode that types a token on the calculator, reads it back and records the bytes.

⚠️ Never send .8xu OS files. A failed application transfer is recoverable; a failed OS transfer can brick the calculator. This project does not implement OS transfer and should not.


Credits

Protocol reconstructed from the TI-83+/84+ Link Protocol Guide by Tim Singer and Romain Liévin, WikiTI, and ArTICL by Christopher Mitchell.

Token values and protocol quirks verified against real hardware.

Licence

MIT

About

Drive a TI-83 Plus link port directly from Raspberry Pi GPIO. Send and receive .8xp programs, .8xg groups and Flash-archive variables over two wires - no SilverLink, no proprietary software.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages