PETSCII (Commodore 64/128) terminal support
A dedicated rendering path for real C64/128 hardware and PETSCII
terminal emulators (SyncTERM's C64 mode, Novaterm, CCGMS, 64NIC+).
Completely separate from the ANSI telnet/SSH/rlogin path — PETSCII
isn't ANSI, so there's no shared cursor-addressing, no color codes,
no lightbar selectors. Everything here is plain text, numbered
menus, and single-key prompts, built by hand in
anetbbs/features/petscii_ui.py.
There is a matching player-facing wiki page (petscii slug) seeded
by anetbbs/wiki/seed.py — same content, aimed at players connecting
from a real C64 rather than sysops. Update both if you change how
this works.
Connecting
Two dedicated ports, one per screen width — matching Synchronet's own
"40 Column PETSCII Support" / "80 Column PETSCII Support" convention.
Both are off by default; a sysop opts in per-width:
| Width | Port | Enable flag |
|---|---|---|
| 40 columns | PETSCII40_PORT (default 6400) |
PETSCII40_ENABLED |
| 80 columns | PETSCII80_PORT (default 6401) |
PETSCII80_ENABLED |
Every connection on a PETSCII port is PETSCII, at that port's fixed
width, unconditionally — there's no telnet TTYPE auto-detection,
because real C64 clients mostly don't announce themselves usefully
that way. Users just pick the port matching their screen/emulator
setting. See docs/INSTALL.md §10 and docs/PORTS.md for the
enable/firewall steps.
Both listeners run inside the same unified anetbbs.service process
as telnet/SSH/rlogin (anetbbs/main.py starts one PetsciiServer
task per enabled width) — not a separate systemd unit.
What's available
Reuses the same underlying data models (Board/Post,
EchoArea/EchomailMessage, PrivateMessage, FileArea/
FileUpload, UserSession, User) as the ANSI screens — never their
rendering code. Covered:
- Message boards — browse, read threads, post, reply
- Echomail — network-first picker (pick the network, then its
areas — not one combined list across every joined network), read,
compose/reply, XMODEM-free (message bodies only) - Private messages — inbox, read, compose/reply
- File areas — browse, view extended description on demand (
E),
download over XMODEM (reusesfeatures/xfer.py's
send_file()— same transfer code the ANSI side uses; PETSCII
connections are plain telnet sockets, so no protocol difference) - Who's online
- Profile — view and edit
- Number Guessing — the one built-in game offered here. Pure
session I/O (guess a number, get higher/lower feedback), no cursor
addressing at all, so it's the only game from the wider Game Center
catalog that has a PETSCII-safe implementation
(features/games.py'sGameManager.play_number_guess(), reused
directly rather than reimplemented) - Logoff
Deliberately absent (not shown, not marked "unavailable," just not
offered): door games, ANetCRAFT, DOOM/Duke3D, MRC chat, IRC — none of
those have a plain-text-only path, same reasoning that already
excludes Wall/ANSI-art screens from PETSCII sessions.
Text rendering rules
- ANSI-art bodies get stripped, not garbled. File descriptions,
echomail/PM/board bodies containing CP437/Unicode box-drawing art
(common inFILE_ID.DIZ-style banners) collapse to spaces before
display — the PETSCII encoder has no representation for those
characters and would otherwise render walls of?. - Word-wrap, not hard-break. All body text and status/prompt
lines wrap at word boundaries to the session's actual width (40 or
80), never mid-word. - Never fill a row to exactly the terminal width. Real hardware
auto-wraps a full-width line on its own, which eats a blank line if
the software also tries to newline after it — every render budgets
width - 1. - Pagination: long lists/bodies page at a fixed line count with
M=more,B=back (previous page),Q=quit-to-menu.
Custom PETSCII menus (sysop-buildable)
Sysops can replace the hardcoded Phase 1 menu with fully custom
menus at /admin/petscii-menus/ — a completely separate tree
from the ANSI custom-menu system (BbsMenu/BbsMenuItem), not a
shared one. Most ANSI action types (art, sixel, chat, most doors)
have no PETSCII equivalent, so a shared tree would mean items
silently missing on this side; a dedicated PetsciiMenu/
PetsciiMenuItem model pair gives full layout control instead.
- If no
PetsciiMenuhasis_default=True, PETSCII sessions fall
back to the hardcoded Phase 1 menu unchanged — this feature is
opt-in, not a replacement. - Each menu item has a
hotkey,label,min_access,sort_order,
visibility flag, and anaction_type— deliberately a much smaller
set than the ANSI side, limited to whatpetscii_ui.pyactually
has a plain-text handler for:
| action_type | Behavior |
|---|---|
goto |
Jump to another PetsciiMenu by name (action_args = target menu name) |
boards |
Message boards |
echo |
Echomail areas |
pm |
Private messages |
files |
File-area browsing |
who |
Who's online |
profile |
View own profile |
games |
Number Guessing |
logoff |
End the session |
- Admin routes: list (
/), create (/new), edit (/<id>/edit),
delete (/<id>/delete), add/edit/delete item, and spawn-submenu
(/<id>/spawn-submenu— creates a new emptyPetsciiMenuand a
gotoitem pointing to it in one step, for building out a menu
tree without leaving the editor).
Real-hardware lessons baked into this code
Built and refined against real C64/128 hardware over several rounds
of live testing, not just terminal emulators. A few non-obvious
things this rendering path handles that a naive ANSI-style port
would get wrong:
- Keyboard charset timing. A real C64 keyboard's PETSCII byte
code for a letter depends on which charset ROM is selected — a
KERNAL keyboard-decode difference, not just display. The
lowercase-charset switch must be sent beforesession.start()
(before the login screen), or every password typed during login
arrives silently case-flattened to uppercase and auth always fails
— looks exactly like a broken user database if you don't know to
look here. - Auto-wrap blank-line bug — see "Text rendering rules" above.
- Column-width overflow in list rendering (echo area names, file
listings) on both 40- and 80-column screens needed independent
fixes — width and overhead-per-row don't scale linearly.
See also
docs/PORTS.md— port tabledocs/INSTALL.md§10 — enabling PETSCII40/PETSCII80docs/03-menus.md— the ANSI custom-menu system this deliberately
does not share a tree withdocs/24-game-center.md— the wider game catalog (Number Guessing
is the only one also offered here)