Read comics on a TI-84 Plus CE.
Two halves: a reader that runs on the calculator, and a sync page that runs in your browser, converts your comics into something a 48 MHz eZ80 can draw, and pushes them over USB.
calc/ — the reader. C for the CE toolchain.calc/updater/ — CSUP, the tiny program that installs a new reader.web/ — the sync page. Plain HTML/CSS/ES modules, no build step, no server.tools/ — a desktop converter and the host test harness.docs/FORMAT.md — the .csx strip format and the library index.docs/PROTOCOL.md — the link protocol.This was briefly called eOS, and the names went with it. If a calculator is
still holding a library under those — EOSLIB and EO<slot><chunk> — the first
run offers to delete it and reclaim the space; your files on the computer are
untouched and syncing again refills it. On the computer, eos.json is read once
if ebooksync.json is not there and its library identity carried across, so a
library that has already been synced is still recognised as the same one.
The calculator lists your comics grouped by book, opens one, and lets you pan, page and zoom. It remembers where you got to, marks a strip read when you reach the end, and goes straight on to the next strip if you keep pressing down. The book list has a Continue row back to whatever you were last reading, and a Bookmarks list of the strips you have marked, across every book.
The sync page reads a folder of comics off your disk — one folder per book — lets you arrange the library and tick which books and strips you want, converts them, and sends them. It also carries new builds of the reader itself over the same cable. It reads back what you have finished, records that in a metadata file beside your comics, and can clear read strips off the calculator to make room for more.
The library is editable in the page: drag images in to add them, create, rename
and delete books and strips, and drag rows into the order you want to read them.
That order lives in ebooksync.json next to your comics, and it is the order the
calculator shows — not whatever the filenames happen to sort into.
The calculator has a 320x240 screen with 256 colours, about 3 MB of flash and around 50 KB of free RAM. That drives everything:
fit+2x can reach it.docs/FORMAT.md has the measurements behind each of those choices.
dialout group.1. Build and install the reader.
cd calc && make
Send calc/bin/COMICS.8xp to the calculator with TI Connect CE or
ticalc.link. This first transfer uses TI’s own link
protocol; everything after it goes over eBookSync’s own — including later builds of
the reader itself, so this is the only time you need TI Connect.
2. Lay out your comics.
comics/
第一本书/
001 - 标题.jpg
002 - 标题.png
第3话/
1.jpg
2.jpg
A.webp
Another Book/
01.jpg
Folders are books and the images in them are strips, titled by filename. JPEG, PNG, WebP, GIF, BMP and AVIF all work.
A folder inside a book is one strip made of the images in it, read in
alphanumeric order: 0–9, then A–Z, ignoring case, with 10 after 9.
That is the shape a chapter usually downloads in — dozens of slices, often not
all the same width — and it stays that way on disk. The images are only joined
up in memory on the way to the calculator, each scaled to the screen width on
its own, and the calculator reads them one at a time: at the bottom of each it
stops and shows what comes next, and a press of down goes on. In the library it
is a single strip.
A library with no metadata yet starts out in natural order, so 10 comes after
9; after that the order is whatever you arrange in the page.
You do not have to lay it out by hand — you can start from an empty folder and drag comics into the page instead.
3. Open the sync page.
Open web/index.html in Chrome — straight off disk is fine, there is nothing to
build or serve.
The page opens on a splash screen with the latest COMICS.8xp to download, the
steps to get the calculator onto its sync screen, and a Connect calculator
button. The library stays behind it until a calculator is connected, and the
splash comes back if the connection is lost.
The page wears the calculator’s colours: purple, Dark or Light as set under Theme on the calculator. It is Dark until a calculator has connected, then follows whatever the last one reported, and remembers it for next time. Once connected, choose your comics folder, tick what you want, and press Sync.
The page has two tabs. Library is your strips, with the calculator’s status and the Sync button beside them. Settings has the detail level, which strips to send and remove, the space budget, and the lock screen wallpaper.
Everything below happens on your actual files, in the folder you chose:
| add strips | drop images onto a book |
| add a strip of several images | drop a folder of them onto a book |
| add a book | drop a folder onto the list, or press New book — folders inside it become strips of several images |
| reorder | drag a row, or use the ↑ ↓ buttons on it |
| move between books | drag a strip onto another book |
| rename | the ✎ button on a book or strip |
| delete | the ✕ button — this deletes from disk, and cannot be undone |
Renaming and reordering keep a strip’s identity, so they cost a quick index update on the next sync rather than re-sending the comic. Deleting a strip that is on the calculator removes it from the calculator too, on the next sync.
Each book row has its own detail level beside its progress bar. Default follows the setting on the Settings tab; choosing another sends that book’s strips at that level instead, so a text-heavy book can have 2× zoom while the rest of the library stays small. Like the library setting, it applies to strips sent from then on.
The 👁 button on a strip shows it exactly as the calculator will: converted
the way a sync would convert it, then drawn at 320×240 in its 16 colours, with
the reader’s keys — arrows, 2/8 to page, +/- to zoom — and the stop at
the end of each image. The preview has its own detail picker, so you can see
what a level costs and how it looks before choosing it. Converting for a preview
fills the same cache a sync uses, so a strip you have looked at sends without
converting again.
4. Connect the calculator.
Run COMICS, unlock it if you have set a password, press mode on the book
list and choose Sync with a computer, plug in the cable, and press
Connect calculator on the page, then pick the calculator’s serial port from
the browser’s list.
The sync screen shows whether the page has connected, what has arrived, and a
bar that keeps moving for as long as the link is alive. It is drawn in the
operating system’s own screen mode rather than the reader’s: the USB controller
and the LCD contend for the memory bus, and USB loses whenever graphx has the
LCD in its 8bpp mode. docs/PROTOCOL.md has the details.
| key | |
|---|---|
| arrows | pan; hold to speed up |
2 / enter |
page down a screen (a band of the last one stays in view) |
8 |
page up |
+ / - |
zoom in and out |
mode |
jump between fit-width and full zoom |
del |
mark read or unread by hand |
alpha |
bookmark the strip, or take the bookmark off |
clear |
back |
2nd |
lock the calculator |
Reaching the last 5% of a strip marks it read on its own.
A strip made of several images stops at the end of each one, with a bar saying what is next. Press down — a fresh press; holding the key only scrolls to the end — to go on to the next image, or up at the top of an image to go back to the one before. After the last image the bar names the next strip in the book, and pressing down opens it.
The book list starts with up to two extra rows. Continue opens the strip
you last read, where you left it. It goes away once that strip is read, and
del on it dismisses it; it comes back the next time you open a strip. Bookmarks lists every bookmarked strip in
the library. The header shows the battery and how much archive is free.
On the book list: del marks a whole book read or unread, 2nd locks the
calculator, mode opens settings. A book opens on its first unread strip. On
the strip list, del marks one strip and alpha bookmarks it; in Bookmarks,
alpha takes a bookmark off.
A bookmark also keeps a strip on the calculator: Remove read strips never clears a bookmarked one, and it does not use up one of the “keep the most recent” places either. Unticking it on the page still removes it.
Settings (mode from the book list) has five entries:
about.txt, scrollable with the arrow keys.about.txt lives at the root of the repository and is baked into the program on
every build by tools/make_about.sh, since the calculator has no way to read
the repository for itself. Edit the file and rebuild; there is nothing to
regenerate by hand. It is ASCII only. A run of two or more spaces after some
text lines up as a column, which is how the key tables stay aligned in a
proportional font, and a line in capitals is a heading.
The reader’s font is DejaVu Sans, rendered at three sizes into
calc/src/fontdata.c by tools/make_font.py. That file is committed, so
building the reader does not need the fonts; rerun the script only if you change
it.
Set one in Settings and eBookSync asks for it on the way in. Three wrong answers close the reader.
Typing uses the letters printed on the keys: alpha switches between digits and
letters, 2nd switches case, del backspaces. There is no on-screen layout to
learn — the calculator is its own legend.
Be clear about what this is. It keeps a curious classmate out of your comics. It is not security:
The password is stored as a random salt and SHA-256(salt + password), so it is not on the calculator in readable form and two calculators with the same password do not look alike.
Press 2nd anywhere in the reader and the calculator turns off. Press on and
it comes back to your wallpaper, with the date and time along the top; any key
then brings up the password prompt, drawn on a plain background so a
full-screen image is not redrawn behind every keystroke.
It is a real power-off, not an imitation: the reader winds the operating system’s own automatic power-down timer to nothing and lets the OS do it, which is a suspend rather than a kill — the reader is still there, still locked, when you turn it back on. Cesium’s power-on password uses the same trick.
This one is a lock, not a speed bump, and it locks you out too. Three wrong answers turn the screen off again and it comes back still locked — there is no way past it from the keypad, and no “closing” that hands you back the operating system. The ways out are the password, pulling the batteries, or a computer with this page on it. Set a password you will remember before you rely on it.
If no password is set the gesture still works, as a screen blanker: any key brings it back.
It only works while the reader is running. A CE program cannot see a
keypress when it is not executing, and there is no way round that: TI-OS runs a
program called ONSCRPT at power-on, but not a native one, and the other route
– a Flash application, which is what Cesium is – cannot be installed without
TI’s signing key or a privilege-escalation exploit. So quitting to the
homescreen is quitting, and the lock is a lock on your comics rather than on the
calculator.
The sync screen is the one place 2nd does nothing. A transfer is in flight there
and graphics are handed back to the operating system for the duration; locking
would strand a strip half-written. Press clear first.
Choose an image under Lock screen wallpaper in the page. A cropper opens on it: drag the picture to move it, scroll or use the slider to zoom, and what you see is the whole of the calculator’s 320×240 screen. Almost no photograph is that shape, and cropping to the middle is a guess that is usually wrong, so the choice is yours to make where you can see it.
Use this crop writes the result into your library folder as wallpaper.jpg,
and the next sync sends it — reduced to 16 colours like everything else. It is
stored at three times the screen rather than at it, so Adjust the crop…
later starts from something better than an image already thrown away once.
Deleting the library index takes the wallpaper with it. That is deliberate, and it is the same deterrent the password already had: the index is the one thing that can say those bytes are a wallpaper, so a calculator whose table of contents has been thrown away to get past the prompt does not get to keep wearing your picture either. Sync again and it comes back.
A CE program runs in place inside its own variable, so it cannot overwrite itself. eBookSync therefore ships two programs that install each other:
COMICS |
the reader. Installs CSUP. |
CSUP |
the updater. Installs COMICS. |
The page keeps the latest of both next to itself, and offers them when the
calculator says it is behind. The updater is applied during the sync and you
never see it. A reader update is sent and armed: to install it, quit the
reader, run prgmCSUP once, and run COMICS again. The reader says so on the way
in, and the page says so when it has finished sending.
Nothing is replaced until you run prgmCSUP, so an interrupted update costs
only the bytes it moved. The image is CRC-32 checked when it arrives and again
before anything is deleted — a damaged one is thrown away rather than installed.
A calculator with no CSUP at all is given one on its first sync, which is why
COMICS.8xp is the only file you ever have to install by hand.
To cut a build:
sh tools/stage_update.sh # bumps calc/BUILD, builds both, copies to web/comics/
Commit what it puts in web/comics/; that is what the page serves.
Each library folder gets an identifier the first time it is used, stored in
ebooksync.json, and the calculator keeps a copy alongside the comics. If you
connect a calculator holding a different library, the page says so and offers to
erase it rather than mixing the two – the calculator would otherwise end up
with comics the library cannot account for. The calculator’s own sync screen
shows the same warning.
Connecting ticks everything already on the calculator. A tick means “I want this on the calculator”, not “send this now” – so unticking something is how you ask for it to be removed on the next sync.
tools/convert.py does the same conversion as the browser, which is handy for
seeing what a strip will cost and what it will look like before syncing
anything:
sh tools/build.sh # build the ZX0 library first
tools/convert.py assets/strip1.jpg --measure # size at every detail level
tools/convert.py assets/strip1.jpg --preview /tmp/p.png
tools/convert.py assets/strip1.jpg -o out/ --slot 0 # .8xv appvars for CEmu
tools/convert.py comics/第一本书/第3话 --measure # a folder is one strip too
sh tools/stage_update.sh --keep # the update tests check the real build
NODE=/path/to/node tools/hosttest/run_all.sh
That needs a C compiler, Python with Pillow and Flask, and node.
The calculator cannot be emulated without a ROM dump, so instead the harness
compiles the real calc/src code for the host and checks it against
independent implementations of the same formats: the Python converter, the
browser converter, and a third ZX0 decoder. A layout disagreement between any
two of them fails the run.
It also drives the reader’s actual menu loop with scripted keypresses, so “pressing this key closes the app” is something a test can catch rather than something you find out on hardware.
What that does and does not cover is spelled out in docs/FORMAT.md; the USB
layer in particular can only be tested on real hardware.