gl¶
the graphics language: the machine's hardware 3D/2D engine
NAME
gl - the graphics language: the machine's hardware 3D/2D engine
SYNOPSIS
gl <command line> one ASCII GL line from the shell
gl <FILE.GL> stream a .gl scene file to the card
POKE the stream at GLDATA ($FF50) from BASIC or C
DESCRIPTION
Since stage 10d the card speaks ASCII: `gl DRAW3 90,-90,300` at the
shell just works (the command wraps the line in CA ... CX, so the
card is back in hex mode after), and `gl /SCENE.GL` streams a scene
file verbatim -- the file carries its own CA/CX, so it may be text,
raw hex bytes, or both. Keywords come in long and short forms
(DRAW3 / D3, the PG-640A's own abbreviations), any case; numbers
are decimal int16, '-' negates; delimiters are space, tab, comma,
semicolon and newline. Card-queued errors are reported after the
stream ("?GL: unknown command", "?GL: bad parameter", ...).
A PGC-style graphics processor (modelled on the Matrox PG-640A) in
the FPGA fabric: it holds the modelling and viewing matrices, the
window, viewport and clip planes, and draws 2D and 3D primitives
through the display engine -- all driven by a byte COMMAND STREAM,
not registers. Anything that can poke a byte can render: C, BASIC,
even the monitor. Commands are an opcode byte plus little-endian
16-bit parameters (hex mode; an ASCII mode arrives at stage 10d).
The port ($FF50-$FF57):
GLDATA $FF50 write: push one command byte into the FIFO
GLSTAT $FF51 read: bit7 FIFO full (wait before pushing),
bit6 busy, bit1 error pending, bit0 readback
GLRB $FF52 read: pop one read-back byte (stage 10e; GLSTAT
bit0 says one is waiting; low byte first)
GLERR $FF53 read: pop one error byte (0 = none): 1 unknown
opcode, 2 bad parameter, 3 mode not fitted,
4 FIFO overflow
GLID $FF54 read: 'G' ($47) -- the presence probe
The verbs (opcode / parameters):
01 NOOP 02 FLIP 03 PGSYNC 04 RESETF
05 WAIT n 06 COLOR r g b (bytes) 07 FLOOD r g b
08 POINT 09 POINT3 0F CLEARS r g b (BOTH pages)
10 MOVE x y 11 MOVER dx dy 12 MOVE3 x y z 13 MOVER3
28 DRAW x y 29 DRAWR 2A DRAW3 x y z 2B DRAWR3
30 POLY n xy.. 31 POLYR 32 POLY3 n xyz.. 33 POLYR3
34 RECT x y 35 RECTR 38 CIRCLE r 39 ELIPSE rx ry
43 "CA "/"CX " (mode)
70 CLBEG n 71 CLEND 72 CLRUN n 73 CLOOP n cnt
74 CLDEL n 79 CLAPP n (P8X: append to list n)
80 TEXT n ch.. 81 TSIZE s (8.8) 82 TANGLE deg 83 TEXTP n ch..
84 TDEFIN c 85 TJUST h v (1..3, 1..3)
61 FLAGRD n 62 MATXRD 1|2 76 CLRD n (state -> read-back)
63 PIXRD x y (one pixel's colour -> read-back; P8X's own)
64 BLIT x y w h + 2*w*h RAW RGB565 bytes (P8X's own; hex only)
90 MDIDEN 91 MDORG x y z 92 MDSCAL sx sy sz (8.8)
93 MDROTX deg 94 MDROTY deg 95 MDROTZ deg 96 MDTRAN x y z
97 MDMATX 12w A0 VWIDEN A1 VWRPT x y z A3 VWROTX deg
A4 VWROTY deg A5 VWROTZ deg A7 VWMATX 9w A8 DISTH d
A9 DISTY d AA CLIPH f AB CLIPY f AF CONVRT
B0 PROJCT ang B1 DISTAN d B2 VWPORT x1 x2 y1 y2
B3 WINDOW x1 x2 y1 y2 C0 AREA
C1 AREABC r g b E0 PRMFIL f
EA LINPAT p EB LINFUN m
3D pipeline: modelling matrix (MD*, composed in command order about
the MDORG pivot), viewing matrix (VW*, orbiting the VWRPT reference
point, DISTAN backing the viewer off), projection (PROJCT angle;
0 = orthographic; power-up = the classic focal-256 camera), hither/
yon clipping (DISTH/DISTY from the reference point, enabled by
CLIPH/CLIPY), window clip, viewport map. 2D primitives live in
window space: no matrix, just clip and map. PRMFIL 1 fills closed
primitives (POLY3 3 ... is the filled triangle). WINDOW and VWPORT
take PGC parameter order: x1 x2 y1 y2.
LINFUN m (stage 10f) sets the pixel-write mode for lines, points
and outlines: 0 replace (power-up), 1 complement (dest inverted,
pen ignored), 2 OR, 3 AND, 4 XOR. XOR twice restores the ground --
the rubber-band idiom. FILLS ALWAYS REPLACE (the burst filler is
not modal). Applies between primitives, never mid-draw; RESETF
returns the mode to replace. BASIC LINE/PIXELW honour it too --
the mode is engine state that every drawing path shares.
CURVES (stage 10i) draw at the 2D current point and move nothing:
CIRCLE r and ELIPSE rx ry map their radii through the window->
viewport scale and rasterize on the device (radii clamp at 255
device pixels; PRMFIL 1 fills; they clip to the SCREEN, not the
window -- the one curve/window divergence). A negative radius is
error 2. (The PGC's ARC and SECTOR were removed 2026-08-30 for
card placement headroom; opcodes 3C/3D are error 1. Draw partial
arcs as short DRAW chains -- 4-degree steps match what the card
did.)
PATTERNS (stage 10j): LINPAT p sets a 16-bit line pattern in the
DEVICE (like the LINFUN mode): every line from every door consults
it, MSB first, restarting each primitive -- glyph strokes
included. LINPAT -1 restores solid. AREA forces BOTH replace mode
and a solid line pattern (its visited-mark invariant). RESETF
restores everything. (The PGC's AREAPT fill mask was removed
2026-08-30 for card placement headroom; opcode E7 is error 1.)
TJUST h v (stage 10k) justifies TEXT about the current point:
h 1/2/3 = left/centre/right, v 1/2/3 = bottom/middle/top. The
offset is applied in MODEL units before drawing, so TSIZE and
TANGLE scale and rotate the justification with the string. BOTH
are compose ALIASES of MDSCAL/MDROTZ: they COMPOUND per issue
(TSIZE 512 twice = 4x; TSIZE 256 = a no-op, not a restore --
MDIDEN resets), transform the anchor too, and pivot about MDORG.
The PGC's TSIZE was absolute; this divergence is deliberate. TEXTP
is the SAME engine as TEXT (on the PGC, TEXT was the fixed
character generator and TEXTP the programmable stroke text -- P8X
text IS stroke text). TEXT records into command lists and replays
since 10k; TDEFIN still may not.
READ-BACK (stage 10e + the single-interface migration): FLAGRD n
streams state (1 PRMFIL, 2 COLOR, 3 PROJCT, 4 DISTAN, 5 WINDOW,
6 VWPORT, 9 near/far), MATXRD 1|2 the composed modeling/viewing
matrices, CLRD n a stored command list (length halfword then the
bytes -- clsave's path), all as int16 words through GLRB. (CLMOD,
the one-byte in-place list patch, was removed 2026-09-01 -- its
342 LUT4 funded BLIT; CLRD out and re-record instead.) PIXRD
x y (2026-08-31, the first single-interface verb) reads ONE
pixel: window coordinates through the SAME map as every 2D verb
-- it transforms under WINDOW/VWPORT, so PIXELW-then-PIXRD
round-trips in any window -- answering one RGB565 word (0
off-screen, the device rule). All of them record into command
lists and perform their reads at replay time. The PGC had no
read-backs; these are P8X's.
BLIT x y w h (the single-interface DMA verb, 2026-09-01): after
the header, exactly 2*w*h RAW RGB565 bytes follow in the SAME
stream -- rows top-down, little-endian, the P8I file layout
verbatim. (x,y) is the image's BOTTOM-LEFT in window coordinates,
mapped through the current window like every verb; w,h are DEVICE
pixels, unscaled (the CIRCLE-radius precedent), each written as a
real PIXELW: GMODE applies, patterns do not, off-screen clips by
the device rule. HEX MODE ONLY (no ASCII keyword -- a decimal
payload would be absurd). w or h of 0 owes no payload; w or h
over 512 (or negative) is error 2 with NO payload owed -- the
sender must not stream one. Inside CLBEG it is error 2, the
header is not recorded, and the payload is consumed and DISCARDED
so the stream stays in sync; a replayed header (none should
exist) is error 2 with no payload owed. BASIC's IMAGE is this
verb, one BLIT per row.
AREA (stage 10g) seed-fills from the 2D current point (set it with
MOVE) using the pen, bounded by pen-coloured pixels: outline a
shape, MOVE inside it, AREA. AREABC r g b bounds on a stated
colour instead, so the outline and the fill can differ. The fill
is a scanline flood walking actual framebuffer pixels: a seed
outside the window is error 2, a seed ON the boundary (or on
already-pen pixels) is a silent no-op, and a fill too convoluted
for the 16384-seed stack stops at error 8 with a deterministic
partial fill. AREA forces the write mode to replace (a fill under
XOR would erase its own boundary test).
The OS streams /FONT.GL to this port at BOOT (since 2026-09-01,
when PGC TEXT replaced BASIC's GTEXT outright), so TEXT works out
of the box everywhere; the glyph bank survives RESETF -- a font
is INSTALLED, not drawn -- and dies only at power-off.
TEXT (stage 10h) draws vector text: a glyph is a COMMAND LIST of
relative strokes (MOVER3/DRAWR3 ending in a pen-up advance) in a
second 64-slot bank, one slot per ASCII 32..95 (lowercase folds to
upper). TDEFIN c records one exactly like CLBEG...CLEND records a
list; `gl /FONT.GL` loads the shipped font. TEXT draws each char
at the current 3D point, so place with MOVE3 x y 0 -- and use the
ORTHO camera (PROJCT 0) for window-space text, since z=0 sits
behind the perspective camera's near plane. TSIZE s (8.8: 256 =
design size, 512 = double) and TANGLE deg are compose ALIASES of
MDSCAL s s s and MDROTZ deg: they scale and rotate strokes AND
baseline together, they compose like every matrix verb (MDIDEN
resets), and scaling happens about MDORG -- anchor big or tilted
text with MDORG at the same point as the MOVE3. In ASCII mode the
string is quoted: TEXT "HELLO" (one line; the translator emits one
single-char command per char). Chars without a glyph skip
silently; TEXT/TDEFIN inside a command list is refused (error 5,
deferred); RESETF does NOT clear the font -- it is an installed
resource, though power loss does (SDRAM).
COMMAND LISTS (stage 10c): CLBEG n records everything up to CLEND
into list n (0-255, 4KB each, stored on the card) instead of
drawing it; CLRUN n replays the list, CLOOP n cnt replays it cnt
times -- and matrix verbs inside a looped list are DELTAS that
accumulate per pass, so a list that nudges MDROTY, draws, FLIPs and
WAITs is a self-spinning scene with the CPU idle. CLAPP appends to
an existing list; CLDEL forgets one. The shell convention: LIST 0
IS THE SCENE -- tri records/appends it, rotate and camera replay
it. Errors: 5 nesting (a list op inside recording/replay), 6 list
undefined, 7 list full (recording aborted).
From BASIC, a poke loop is the whole driver:
10 REM white filled triangle, house frame
20 POKE 65360,179 : REM WINDOW ... (see the opcode table)
EXAMPLES
gl WINDOW -120 120 -120 120
gl COLOR 31 0 0 PRMFIL 1 POLY3 3 -80 -80 300 80 -80 300 0 40 420
gl MDY 30 CLRUN 0 spin the scene 30 degrees, redraw
gl /DEMO.GL play a scene file (uppercase names)
POKE 65360,2 flip the pages from BASIC (FLIP)
SEE ALSO
tri, page, image, g3d, gfx; man basic (the verbs are also native
BASIC statements: WINDOW, DRAW3, MDROTY, CLBEG ...);
STAGE10-DESIGN.md and the PG-640A manual
(docs/reference/pg640a.pdf) in the repo
See also: tri, page, image, g3d, man, basic
The text of man gl on P8X (os/man/gl in the repository). All commands