PETSGI (PET Server Gateway Interface) is an experimental Perl application framework for networked Commodore PET software using unmodified PETdisk MAX.
The first supported target is deliberately narrow: a 40-column Commodore PET and the stock PETdisk MAX network protocol. PETSGI is PET-first, not PET-locked: application semantics, target rendering, and transport are separated so future third-party targets/transports do not have to weaken the PET implementation.
PETdisk already turns Commodore disk operations into a small bidirectional HTTP protocol. PETSGI normalizes that protocol into application operations:
| PETdisk request | PETSGI operation | Application meaning |
|---|---|---|
GET ?d=1&p=N |
DIRECTORY |
discovery / launcher |
GET ?file=X&l=1 |
STAT |
materialize and size a response |
GET ?file=X&s=A&e=B |
READ |
ranged response transport |
PUT ?f=X&n=1&b64=1 |
CREATE |
first write block |
PUT ?f=X&b64=1 |
APPEND |
subsequent write block |
PUT ?f=X&u=1&s=A&e=B&b64=1 |
UPDATE |
random-access update |
PRG is intended to become PETSGI's primary executable UX response; SEQ is useful for live/polled state and submissions, while D64 remains the natural choice for mounted storage and software collections.
PETSGI treats a dynamically generated PRG as an executable view of server-side application state, not merely downloadable content. A running PRG may then use SEQ resources for lighter-weight updates or submissions without replacing the whole application state.
The first semantic application API is intentionally small:
$app->view(
name => 'HOME.PRG',
render => sub {
my ($ctx) = @_;
return $ctx->ui
->title('PETSGI BBS')
->text('WELCOME')
->menu(
{ label => 'MESSAGES', route => 'BOARD.PRG' },
{ label => 'CHAT', route => 'CHAT.PRG' },
);
},
);
$app->resource(
name => 'STATE.SEQ',
type => 'SEQ',
read => sub {
return "NEW=3\nONLINE=2\n";
},
);
$app->action(
name => 'POST.SEQ',
write => sub {
my ($ctx, $bytes, $operation) = @_;
# validate and persist the submitted block
},
);PETSGI::Context gives application callbacks a framework-level object rather
than a CGI or PETdisk implementation object. The initial PETSGI::UI::Screen
builder is a deliberately small intermediate representation for 40-column PET
screens. It currently renders to tokenized BASIC; later native/hybrid renderers
can implement highlighted menus, scrolling regions, forms, and other local
effects without changing application code.
A future template language should compile to the same UI tree rather than become a second runtime model.
examples/petsgi.cgi is a CGI::Tiny deployment which behaves like the original
petdisk.php for PRG/SEQ/D64, ranged reads, writes, and TIME, while adding safer
filename handling and cleaner implementation boundaries.
10,example.com/cgi-bin/petsgi.cgi
Set PETSGI_ROOT to the directory to expose.
examples/fullstack.cgi demonstrates the semantic application API. It exposes a
server-rendered HOME.PRG, a polled STATUS.SEQ, and an ACTION.SEQ submission
endpoint.
PETdisk requests a dynamic object in at least two HTTP operations: first its
length, then one or more ranged reads. Under CGI these are separate Perl
processes. PETSGI::Materializer::File therefore freezes the generated bytes
between STAT and the subsequent reads so an executable view cannot change in
the middle of a load.
LOAD"HOME",10
RUNThe initial BASIC generator targets the PET BASIC program address at $0401 and
only claims the 40-column PET target. It is intentionally a foundation for the
native local-effects/runtime work, not a complete BASIC compiler.
PETSGI::Test::PETdiskMax mocks only the part of PETdisk MAX that PETSGI depends
on: directory pages, stat-before-read, 512-byte range reads, first/append PUTs,
block updates, and base64 write bodies. It deliberately does not emulate IEEE-488,
Wi-Fi, LEDs, or unrelated firmware internals.
prove -lr tThe tests also exercise executable views, dynamic SEQ refreshes, action writes, the semantic UI builder, and cross-request materialization. GitHub Actions tests several Perl versions and runs Devel::Cover on the latest.
PETSGI::Session::SQLite provides an intentionally modest first session model:
a user may claim a name and receive a compact PETSGI token. This is identity, not
strong authentication. PETSGI::Application accepts a session_resolver
callback so transport-specific token carriage does not leak into application
code.
PETdisk MAX does not send an HTTP close/commit request after a sequence of PUT
blocks. An action() callback therefore receives individual CREATE, APPEND,
or UPDATE blocks. Applications needing transactional submissions larger than
one block must define explicit framing/commit semantics at the PETSGI application
layer.
The CGI adapter uses CGI::Tiny; PETSGI operation routing uses Dispatch::Fu;
small request/resource/context objects use Util::H2O::More. DBI and DBD::SQLite
are recommended for sessions rather than required for simple file service.
This is an initial 0.01 design/prototype. The next major work is the PET-native local-effects renderer: highlighted keyboard menus, forms/input, scrolling regions, navigation, and eventually a small reusable 6502 runtime that can be linked into dynamic PRGs while retaining a BASIC fallback.