Display and Graphics

The Agon's picture is generated by its ESP32, the VDP. VIOS gives you two ways to put things on the screen:

  • The GUI display server on the ESP32: windows, surfaces, menus, widgets, sprites and animation, driven from Forth with the UI- words. This is what you normally use, and it is described in The GUI.
  • Raw VDP access: a small set of words that send VDU commands straight to the VDP and read back characters and pixels, as BBC BASIC does on a stock Agon. They are useful for experiments, for full-screen graphics in other screen modes, and for talking to VDP features VIOS has no wrapper for.

This chapter covers the raw layer and how it coexists with the GUI.

The VIOS display mode

At power-on the ESP32 selects mode 0: 640x480 pixels, 16 colours, 60 Hz and starts the GUI compositor in it, before the eZ80 has even loaded VIOS. The GUI and the console (both the Console window and the full-screen text console) are drawn only in this mode. VDP-INFO asks the VDP what it is doing:

VDP-INFO
vdp width 640 height 480 colours 16 mode 0 console-dropped 0

console-dropped counts console input bytes the eZ80 had to discard because its input buffers were full; it should stay 0.

Raw VDU output

VDU sends one byte to the VDP; VTYPE sends a string of bytes. These are the same VDU byte streams documented for the Agon VDP and BBC BASIC: printable bytes are drawn as text at the VDP's text cursor, and control codes start commands (for example VDU 17,n sets a colour, VDU 25,... plots, VDU 23,... reaches system and graphics functions).

: VDU2 ( u -- ) DUP 255 AND VDU 8 RSHIFT VDU ;   \ send u as a 16-bit little-endian value
CREATE BELL 1 C, 7 C,
BELL COUNT VTYPE

Unlike TYPE, VDU and VTYPE bypass the console: the bytes are not part of the console text, are not echoed to USB, and the console view does not know about them.

The convenience words wrap the common commands:

Word Effect VDU sequence
CLS ( -- ) clear the text area 12
COLOUR ( n -- ) text colour: n < 128 foreground, 128+n background 17, n
MODE ( n -- ) change screen mode and check it 22, n
PLOT ( mode x y -- ) graphics operation 25, mode, x; y;
CHAR@ ( x y -- c ) character at text column x, row y 23, 0, &83, x; y;
PIXEL@ ( x y -- rgb index ) pixel colour at graphics point x, y 23, 0, &84, x; y;
VDP-INFO ( -- ) print the mode 23, 0, &86
BEEP ( freq ms -- status ) a note on audio channel 0 23, 0, &85, ...

CHAR@, PIXEL@, VDP-INFO, MODE and BEEP wait for the VDP's reply and fail with a timeout error (for example VDP pixel timeout) after about one second without one.

Colours

In mode 0 the 16 palette entries are, by default:

Index Colour Index Colour
0 black 8 dark grey
1 red 9 bright red
2 green 10 bright green
3 yellow 11 bright yellow
4 blue 12 bright blue
5 magenta 13 bright magenta
6 cyan 14 bright cyan
7 light grey 15 white

n COLOUR sets the text foreground; 128 n + COLOUR sets the text background. Graphics colours are set with VDU 18,mode,colour (GCOL):

: GCOL ( colour -- ) 18 VDU 0 VDU VDU ;
10 COLOUR  128 COLOUR
12 GCOL

PIXEL@ returns the pixel's colour as a packed RGB cell (red + green256 + blue65536, 8 bits each) and its palette index:

0 0 PIXEL@ . .

Plotting

PLOT sends VDU 25,mode,x;y;. Coordinates are 16-bit. By default the VDP uses BBC BASIC logical coordinates: the screen is 1280 by 1024 units whatever the mode, with (0,0) at the bottom left. VDU 23,0,&C0,0 switches to plain pixel coordinates with (0,0) at the top left, and VDU 23,0,&C0,1 back. PIXEL@ uses the same coordinate system as PLOT; CHAR@ uses text columns and rows.

Common plot codes (all with absolute coordinates):

Mode Operation
4 move to x, y
5 draw a line to x, y in the graphics foreground colour
69 plot a single point
85 filled triangle using the last two points and x, y
101 filled rectangle from the last point to x, y
149 circle outline through x, y around the last point
157 filled circle
: MOVE-TO ( x y -- ) 4 -ROT PLOT ;
: LINE-TO ( x y -- ) 5 -ROT PLOT ;
: RECT ( x0 y0 x1 y1 -- ) 2SWAP MOVE-TO 101 -ROT PLOT ;
: TRIANGLE ( -- ) 100 100 MOVE-TO 400 100 LINE-TO 250 400 LINE-TO 100 100 LINE-TO ;
TRIANGLE
600 600 900 800 RECT

Reading the screen back

x y CHAR@ returns the character code shown at text column x, row y, or 0 if the VDP does not recognise the glyph. The VDP matches glyphs against the current text colours, so set the colours the text was drawn with first (on the full-screen console, 3 COLOUR 128 COLOUR); otherwise even correct text reads back as 0.

0 0 CHAR@ .

Screen modes

MODE changes the VDP's screen mode and then asks the VDP which mode it is in; if the VDP refused the change, MODE fails with VDP mode rejected (the VDP then stays in, or restores, its previous mode). The modes of the VDP firmware VIOS ships:

Mode Resolution Colours Notes
0 640x480 16 the VIOS display mode (GUI and console)
1 640x480 4
2 640x480 2
3 640x240 64
4 640x240 16
5 640x240 4
6 640x240 2
7 640x480 16 teletext
8 320x240 64
9 320x240 16
10 320x240 4
11 320x240 2
12 320x200 64
13 320x200 16
14 320x200 4
15 320x200 2
16 800x600 4
17 800x600 2
18 1024x768 2
129-134, 136-143 as mode n-128 double-buffered variants

A mode change clears the screen and resets the palette, text and graphics colours, viewports and cursor.

13 MODE
4 0 0 PLOT  157 640 512 PLOT

Leaving mode 0 suspends the GUI and the console view. The GUI compositor and the console renderer only draw in mode 0, so in any other mode the screen shows only what you draw with VDU commands. VIOS keeps running normally: the console still works over USB and the keyboard still types into the REPL, but you will not see your typing on the screen. Type 0 MODE (blind, if necessary) to return; the GUI notices the new framebuffer and repaints everything, console included.

Raw drawing and the GUI

In mode 0 the GUI compositor owns the screen. It redraws any part of the screen where something changed: a window moved, text arrived in the Console window, a widget updated. Whatever you plot with raw VDU commands underneath it stays only until the compositor next repaints that area. Raw drawing is fine for quick experiments; for anything that should stay on the screen alongside windows, draw into a GUI surface or use sprites with the UI- words (see The GUI). For a full-screen raw graphics program, switch to another mode, draw, and return with 0 MODE when you are done.

The DISPLAY-WRITE binding

DISPLAY-WRITE ( addr u -- ) is a deferred word bound to VTYPE at boot. Programs that send raw display bytes through DISPLAY-WRITE instead of VTYPE can later be pointed elsewhere (a log, a test buffer, another renderer) with IS, without recompiling. The console never goes through it, so rebinding it cannot hide the console.

VARIABLE DISPLAY-BYTES
: COUNT-DISPLAY ( addr u -- ) DUP DISPLAY-BYTES +! VTYPE ;
' COUNT-DISPLAY IS DISPLAY-WRITE
S" Hi" DISPLAY-WRITE  DISPLAY-BYTES ?
' VTYPE IS DISPLAY-WRITE

A beep

BEEP plays a note on VDP audio channel 0 at a fixed volume and returns the VDP's status byte (non-zero when the note was accepted). Both the frequency in Hz and the duration in milliseconds must fit in 16 bits. For music, envelopes and the VIOS audio engine, see Audio.

440 250 BEEP DROP