This program is a demonstration of UDG (User Defined Graphics) character rendering modes on the TS2068, using machine code invoked via RANDOMIZE USR to display characters in different screen positions and modes. The machine code routine is stored in line 1’s REM statement and located via a DEF FN that reads the system variable PROG pointer (PEEK 23635 + 256*PEEK 23636 + 5). Three POKE addresses — 23728 (column), 23729 (row), and 23723 (mode) — act as parameters passed to the machine code before each RANDOMIZE USR call. The program cycles through multiple UDG rendering demonstrations, including three character modes (modes 1, 2, and 3), and concludes with a TS2068-specific SAVE that stores both the BASIC program and the UDG data as a separate CODE file at address 65368 for 168 bytes.
Program Analysis
Program Structure
The program is divided into several functional phases:
- Lines 1–2: Machine code payload embedded in a REM statement; a
DEF FN a()that locates the machine code dynamically. - Lines 10–200: First demonstration sequence — renders UDG characters at various positions and modes, spelling out fragments of text (“ONG”, “SLAND”, “inclair”, ” SPECTRUM “, “IMEX”, etc.).
- Lines 202–220: Draws a border frame using
PLOT/DRAWand waits for a keypress before continuing. - Lines 500–540: Renders “GROUP” text, then falls through to the frame-drawing sequence.
- Lines 600–690: A nested loop over three modes and multiple column positions, rendering “TS2068” UDG text across the screen, followed by another keypress pause.
- Lines 700–799: Demonstrates three character modes with labels, then STOPs.
- Lines 810–820: Load entry point — loads the UDG CODE block and jumps to line 2.
- Line 9998: Save block — saves the BASIC with autorun at line 810, then saves the UDG data as a separate CODE file.
Machine Code Location Technique
Line 2 defines DEF FN a()=PEEK 23635+256*PEEK 23636+5. System variables at addresses 23635–23636 hold the PROG pointer — the address of the start of the BASIC program in RAM. Adding 5 skips past the line number (2 bytes), line length (2 bytes), and REM token (1 byte) of line 1, landing directly on the first byte of the embedded machine code. This is a standard self-relocating machine code idiom that makes the routine position-independent regardless of where BASIC is loaded in memory.
Parameter Passing Convention
Before each RANDOMIZE USR FN a() call, three system-variable locations are POKEd as parameters for the machine code routine:
| Address | Purpose |
|---|---|
23728 | Column / X position for the character |
23729 | Row / Y position for the character |
23723 | Rendering mode (1, 2, or 3) |
The machine code reads these locations directly rather than receiving arguments on the stack, which is a common pattern when calling short ML routines from BASIC where passing multiple parameters cleanly is awkward.
UDG Data Storage
The UDG graphics data lives at address 65368 for 168 bytes — this is near the top of the TS2068’s 64 KB address space. 168 bytes accommodates 21 UDG character definitions (8 bytes each), matching the full set of UDGs (\a through \u). The SAVE "udg" CODE 65368,168 at line 9998 preserves this data separately from the BASIC listing so it can be reloaded at line 810 with LOAD "udg" CODE before execution begins.
REM Statement as Code Carrier
Line 1 contains the machine code as a REM statement. The zmakebas source shows it begins with !VAL \FREE !CODE \ — these are token sequences embedded directly in the REM body. Because BASIC never interprets REM content, arbitrary byte sequences including tokenised keywords and raw machine code bytes can coexist safely. The trailing \bxxxxxxxxx... sequence at the end of line 1 represents a run of UDG \b characters used as padding or data filler.
Rendering Demonstration Loop
The nested loop at lines 610–680 iterates over three modes (m = 1 TO 3) and columns (n = 0 TO 18 STEP 2). A subtle offset is applied at line 635: when m=1, the column is set to nx=n+1, staggering mode-1 characters by one pixel column relative to modes 2 and 3. This likely demonstrates sub-character horizontal positioning capability of the machine code renderer.
Notable Idioms and Anomalies
PAUSE 4e4is used as a long pause (40000 frames ≈ 13 minutes) standing in for an effective “wait for keypress” — though no explicitINKEY$check is paired with it; thePAUSEon the TS2068 is interruptible by a keypress, so this is the intended idiom.- Line 201 is jumped over by
GO TO 500at line 201 andGO TO 202at line 540, so thePLOT/DRAWborder-drawing code at 202–216 is shared between the two entry paths. - The REM strings scattered through the listing (e.g.,
REM "ONG",REM "SLAND") are the text data that the machine code reads from the REM body immediately following eachRANDOMIZE USRcall — a well-known technique where ML code usesPCadvancement past the following BASIC statement to consume inline string data. - Line 820 jumps to line 2, not line 10, ensuring
DEF FN a()is re-registered after loading before the demo begins.
Content
Source Code
1 REM !VAL \FREE !CODE \ RETURN ( RETURN ( THEN ▚ THEN AND *U\>"PEEK #( IF RESTORE !LEN \N!@ RESTORE !VAL \FREE NEW o>& THEN % THEN gSTR$ RESTORE !VAL \FREE LLIST RETURN 8 RETURN "TAB \ LLIST RESTORE FREE = OR RETURN \a8p\aSTR$ X COPY OR \ao> THEN % THEN gSTR$ RESTORE !ATTR \FREE LLIST RETURN (r CAT >2AT \ RESTORE FREE W!ATTR \FREE RETURN z(ON ERR*TAB \ OR STR$ r#s+:AT \ RETURN (% RETURN x RETURN RESTORE !CODE \ THEN F LLIST OR LPRINT PI GO SUB BSTR$ LPRINT PI GO SUB BSTR$ "TAB \ LLIST #:AT \=2AT \ POINT !CODE \ THEN F*TAB \(PI GO SUB BA▄PI GO SUB B\ RETURN #"TAB \ LLIST #>"PEEK >= DATA ▐! THEN ? THEN THEN - THEN ? THEN THEN ,T]!ATTR \FREE RETURN ( FLASH OR RESTORE FREE VERIFY *TAB \r#s+"TAB \ LLIST #STR$ *TAB \ RETURN PI GO SUB B"TAB \ LLIST #>"PEEK >=\bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2 DEF FN a()=PEEK 23635+256*PEEK 23636+5
10 POKE 23728,4: POKE 23729,2
20 POKE 23723,3
30 RANDOMIZE USR FN A()
40 REM "\l"
50 POKE 23728,5: POKE 23729,4
54 POKE 23723,1
60 RANDOMIZE USR FN a()
62 REM "ONG"
70 POKE 23728,6: POKE 23729,3
72 POKE 23723,3
80 RANDOMIZE USR FN a()
90 REM "\i"
92 POKE 23728,7: POKE 23729,5
100 POKE 23723,1
110 RANDOMIZE USR FN a()
120 REM "SLAND"
130 POKE 23728,8: POKE 23729,4
140 POKE 23723,3
150 RANDOMIZE USR FN a()
160 REM "\s"
170 POKE 23728,9: POKE 23729,6
180 POKE 23723,1
185 RANDOMIZE USR FN a()
190 REM "inclair"
192 POKE 23728,10: POKE 23729,5
194 POKE 23723,3
195 RANDOMIZE USR FN a()
196 REM " SPECTRUM "
197 POKE 23728,11: POKE 23729,7
198 POKE 23723,1
199 RANDOMIZE USR FN a()
200 REM "IMEX"
201 GO TO 500
202 PLOT 0,152: DRAW 255,0
203 PLOT 0,153: DRAW 255,0
204 PLOT 0,66: DRAW 255,0
206 PLOT 0,65: DRAW 255,0
210 PLOT 0,66: DRAW 0,86
212 PLOT 1,66: DRAW 0,86
214 PLOT 255,66: DRAW 0,86
216 PLOT 254,66: DRAW 0,86
220 PRINT AT 21,0;" Hit any Key to Proceed": PAUSE 4e4: CLS : GO TO 600
500 POKE 23728,6: POKE 23729,20
510 POKE 23723,3
520 RANDOMIZE USR FN a()
530 REM "GROUP"
540 GO TO 202
600 DATA 0,13,20
610 FOR m=1 TO 3
620 READ s
630 FOR n=0 TO 18 STEP 2
635 LET nx=n: IF m=1 THEN LET nx=n+1
640 POKE 23728,nx: POKE 23729,s: POKE 23723,m
650 RANDOMIZE USR FN a()
660 REM "TS2068"
670 NEXT n
680 NEXT m
690 PRINT AT 21,0;" Hit any Key to Proceed": PAUSE 4e4: CLS
700 PRINT AT 4,0;"Regular Characters"
710 POKE 23728,6: POKE 23729,0: POKE 23723,2
720 RANDOMIZE USR FN a()
730 REM "Mode 2 Character"
740 POKE 23728,9: POKE 23723,1
750 RANDOMIZE USR FN a()
760 REM "Mode 1 Char$"
770 POKE 23728,11: POKE 23723,3
780 RANDOMIZE USR FN a()
790 REM "Mode 3 Char$"
799 STOP
810 LOAD "udg"CODE
814 CLS
820 GO TO 2
9998 SAVE "udg-demo" LINE 810: SAVE "udg"CODE 65368,168
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


