GrafProgs is a multi-part demonstration and utility suite for displaying enlarged, pixel-scaled text on screen using a machine code routine loaded at address 32256. The BASIC programs (numbered 1 through 12) showcase various text-scaling effects, including ripple layouts, stacked titles, UDG-based lettered graphics, word-wrapped paragraphs, and full-screen character fills. Program 1 uses direct PEEK of the ROM character set at address 15360 to manually render scaled characters via PLOT and DRAW, while Programs 2–12 delegate rendering to the machine code routine via USR 32256, passing parameters through memory addresses 23306 onward. UDGs are loaded by PEEKing ROM glyph data at fixed addresses such as 16024 and 15896, and the word-wrap routine in Program 12 walks a string backward to find space characters for clean line breaks. Several REMs at the top document a relocation utility for moving the machine code block to a user-specified address, with address fixup DATA for five internal pointer patches.
Program Structure
GrafProgs is organized as a sequence of twelve self-contained demonstration programs sharing a common subroutine framework. Each numbered program (100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 2050) runs independently and chains to the next via a direct RUN call to the following program’s entry point after a “repeat?” prompt. Lines 10–70 are pure REM commentary containing a relocation utility for the companion machine code file.
Machine Code Interface
The core rendering engine is a 300-byte machine code block loaded at address 32256 (decimal) via LOAD "GrafProgsC" CODE. BASIC communicates with it by POKEing a parameter block starting at address 23306:
| Offset | Variable | Purpose |
|---|---|---|
| +0 | xx | X pixel position (computed to center the string) |
| +1 | yy | Y pixel position |
| +2 | xs | Horizontal scale factor |
| +3 | ys | Vertical scale factor |
| +4 | 8 | Fixed value (glyph height in pixels) |
| +5… | character codes | String data, terminated by 255 |
The routine is invoked with LET w= USR 32256, discarding the return value. Addresses 23606–23607 are also manipulated (e.g., POKE 23606,216: POKE 23607,250) in Programs 4 and 10 to control an animation loop offset, stepped through a FOR f loop before calling the routine repeatedly.
Subroutine at Line 260
The central rendering subroutine at line 260 computes horizontal centering with LET xx=(256-8*xs*LEN p$)/2, builds the parameter block via sequential POKEs, appends the string character codes, writes the 255 terminator, and calls USR 32256. It is called by every demonstration program.
Program 1 — Pure BASIC Pixel Renderer
Program 1 (lines 110–190) is the only demo that does not use the machine code routine. It reads character bitmaps directly from ROM using PEEK (15360 + CODE a$(n)*8 + f), then extracts individual bits with successive INT(p/2) divisions, using PLOT and DRAW to render scaled pixels. The inner FOR i=i TO t construct is a deliberate trick: when a bit is set, the loop variable i is advanced to t, effectively skipping the remaining bit-extraction steps for that pixel block. Line 160 handles screen wrapping when the rendered string overflows 255 pixels wide.
UDG Loading from ROM Glyphs
Programs 3, 4, and 10 load UDGs by PEEKing ROM addresses that contain pre-drawn glyph data. Program 4’s DATA at line 430 maps UDG letters to ROM addresses such as 16024, 15896, 16016, 15992, and 15968. Program 10 extends this to eleven UDGs (a–l), using a variable d$ still in scope from the DATA read — a potential hazard if execution order is disrupted, since d$ holds the address string for UDG “i” and is reused for UDG “j” (PEEK (VAL d$+n)).
Word-Wrap Routine (Program 12)
Lines 2060–2160 implement a word-wrapping paragraph renderer for a Shakespeare quotation. The algorithm scans each 32-character slice backward for a space (lines 2100–2110), then feeds the trimmed substring to the machine code renderer, advancing yy by y = ys*8 after each line. Edge cases for short trailing strings are handled at line 2080.
Notable Techniques and Idioms
SGN PIis used throughout as a compact literal for 1 (since PI > 0), andNOT PIevaluates to 0 — both save bytes over typing the digits directly.INT PI(≈ 3) is used at line 1020 to setys, again saving a byte.VAL b$in line 1010 converts DATA strings to numbers, a common memory-saving technique.- Line 290 (
POKE 23606,0: POKE 23607,60) resets the system variable pair, likely restoring an attribute or display pointer modified by the animation sequence. - The relocation utility in REMs 10–60 provides a five-entry DATA table of byte offsets and patches self-referential address bytes within the machine code block using two helper functions
FN a(x)andFN b(x)for high and low bytes respectively.
Bugs and Anomalies
- Line 170 contains
PRINT AT 21,31'— the trailing apostrophe issues an extra newline after theATposition, which may produce an unintended blank line during the overflow scroll in Program 1. The REM on line 70 acknowledges a fix is needed. - The double colon
::at line 730 (LET yy=f*5::) is a harmless empty statement artifact. - In line 1010, the DATA entry
"i",d$uses the BASIC variabled$rather than a string literal; this relies ond$retaining the value"15968"from the immediately preceding READ iteration, which is correct but fragile. - Line 2130 advances
a$witha$(n+1 TO)but does not guard againstnexceedingLEN a$when the last segment ends exactly at the string boundary, potentially causing a subscript error on edge-case input.
Save/Load Structure
Line 2180 saves both the BASIC program (SAVE "GrafProgs" LINE 2140) and the machine code block (SAVE "GrafProgsC" CODE 32256,300). Line 2170 provides a bootstrap loader that clears RAM to 32255, loads the code file, and runs the program.
Source Code
10 REM Screen Utilities ZX COMPUTING April/May 85 Utility; use to relocate starting address of m/c in memory, original=32256,300
20 REM INPUT "Start address? ";s:CLEAR s-1:PRINT "Load code":LOAD "p2"CODE
30 REM DEF FN a(x)=INT ((s+x)/256)
40 REM DEF FN b(x)=s+x-256*FN a(x)
50 REM RESTORE :FOR i=1 TO 5:READ x,x:POKE s+x,FN b(x):POKE s+1+x,FN a(x):NEXT i
60 REM DATA 106,32,127,164,86,3,154,48,251,156
70 REM this utility needs fix-ing
100 REM Program 1
110 PAPER 7:BORDER 7:INK 9:CLS
120 LET w=4:LET t=4:LET a=7*w:LET d=168:LET a$="ABCDE":REM w max=8, min=32/48; d max=21, min=1; try d=0
130 FOR n=1 TO LEN a$:FOR f=0 TO 7:LET p= PEEK (15360+ CODE a$(n)*8+f)
140 FOR i=0 TO 7:IF p-2* INT (p/2) THEN FOR i=i TO t:PLOT a-i*w,d-f*t-1:DRAW 1-w,0:NEXT i:REM fool around with i & 1 here
150 LET p= INT (p/2):NEXT i:NEXT f:LET a=a+w*8
160 IF a>255 AND d-t*8>t*8-1 THEN LET d=d-t*8:LET a=w*8-1
170 IF a>255 AND d-t*8<t*8 THEN PRINT AT 21,31':FOR f=1 TO t:PRINT :NEXT f:LET a=w*8-1
180 NEXT n:PRINT AT 21,4;a$:GO SUB 380
190 IF z$="y" OR z$="Y" THEN RUN 100
200 REM Program 2
210 INPUT "times wider? ";xs'"times taller? ";ys
220 LET yy=0
230 LET p$="H"
240 GO SUB 260:GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 200
250 RUN 300
260 LET xx=(256-8*xs* LEN p$)/2
270 LET i=23306:POKE i,xx:POKE i+1,yy:POKE i+2,xs:POKE i+3,ys:POKE i+4,8:LET i=i+4:LET w= LEN p$:FOR n= SGN PI TO w:POKE i+n, CODE p$(n):NEXT n:POKE i+w+1,255:LET w= USR 32256:RETURN :REM pause any amount; cls screen after each TITLE if you wish
290 POKE 23606,0:POKE 23607,60:RETURN
300 REM Program 3
320 FOR f= USR "a" TO USR "u" STEP 8:POKE f,255:POKE f+7,255:NEXT f
330 LET p$="[UDG-A][UDG-B][UDG-C][UDG-D][UDG-E]":POKE 23606,216:POKE 23607,250
340 LET yy=50:LET xs=2:LET ys=2:GO SUB 260:GO SUB 290
350 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 300
360 RUN 400
370 FOR n= SGN PI TO 176:PRINT "PPPP";:NEXT n:RETURN
380 INPUT "Repeat This Example (Y/N) ";z$:IF z$="N" OR z$="n" OR z$="y" OR z$="Y" THEN CLS :RETURN
390 GO TO 380
400 REM Program 4
420 RESTORE 420:FOR f= SGN PI TO 5:READ a$,a:FOR n= NOT PI TO 7:POKE USR a$+n, PEEK (a+n):POKE USR a$+n+8, NOT PI:NEXT n:NEXT f
430 DATA "a",16024,"c",15896,"e",16016,"g",15992,"i",15968
440 LET xs=2:LET ys=3:LET yy=50:LET p$="[UDG-A][UDG-C][UDG-E][UDG-G][UDG-I][UDG-I]":FOR F=0 TO 8:POKE 23606,216+F:POKE 23607,250:REM P$ in GRAPHICS mode
450 GO SUB 260:NEXT f:GO SUB 290
460 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 400
500 REM Program 5
510 RESTORE 500:LET yy=50
520 FOR f=2 TO 8 STEP 2
530 LET xs=f:LET ys=f:READ p$,i:INK i:LET yy=yy-8:GO SUB 260:NEXT f
540 DATA "THIS",2," IS ",4,"YOUR",6,"LIFE",1
550 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 500
600 REM Program 6
620 LET p$="TITLE":FOR f=6 TO SGN PI STEP - SGN PI
630 INK f- SGN PI:LET yy=100-f*10:LET xs=f:LET ys=f:GO SUB 260:NEXT f
635 PAUSE 300:CLS :FOR f=6 TO SGN PI STEP - SGN PI:INK f- SGN PI:LET yy=100-f*4:LET xs=f:LET ys=f:GO SUB 260:NEXT f
640 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 600
700 REM Program 7
720 LET p$="RIPPLE":FOR f=5 TO 1 STEP -1
730 INK f-1:LET yy=f*5::LET xs=f:LET ys=f:GO SUB 260:NEXT f
740 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 700
800 REM Program 8
830 GO SUB 370:LET yy= NOT PI:LET xs=32:LET ys=22:LET p$=" ":GO SUB 260
840 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 800
900 REM Program 9
930 GO SUB 370:LET yy= NOT PI:LET xs= SGN PI:LET ys=22:LET p$=" ":GO SUB 260
940 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 900
1000 REM Program 10
1010 RESTORE 1000:FOR f= SGN PI TO 6:READ a$,b$,c$,d$:FOR n= NOT PI TO 7:POKE USR a$+n, PEEK (VAL b$+n):POKE USR c$+n, PEEK (VAL d$+n):NEXT n:NEXT f:DATA "a","16024","b","16032","c","15896","d","15944","e","16016","f","16032","g","15992","h","15968","i",d$,"j","15912","k",b$,"l","16204"
1020 LET xs=2:LET ys= INT PI:LET yy=50:LET p$="[UDG-A][UDG-C][UDG-E][UDG-G][UDG-I][UDG-K]":FOR f= NOT PI TO 8:POKE 23606,216+f:POKE 23607,250:REM p$ is graphics mode
1030 GO SUB 260:NEXT f:GO SUB 290
1040 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 1000
2000 REM Program 11
2030 GO SUB 370:LET yy=48:LET xs=16:LET ys=8:LET p$=" ":GO SUB 260
2040 GO SUB 380:IF z$="y" OR z$="Y" THEN RUN 2000
2050 REM Program 12
2060 LET a$="The quality of mercy is not strained. It droppeth as gentle rain from the heaven up on the place beneath. It is twice blest. It blesseth him that gives, and him that takes."
2070 CLS :LET ys=1:LET yy=0:LET y=ys*8:LET xs=1
2080 IF LEN a$<33 THEN LET n= LEN a$+1:LET b$=a$:GO TO 2120
2090 LET b$=a$( TO 32):IF a$(33)=" " THEN LET n=33:GO TO 2120
2100 FOR n=32 TO 1 STEP -1:IF b$(n)=" " THEN GO TO 2120
2110 NEXT n
2120 LET p$=b$( TO n-1):GO SUB 260:IF a$="" THEN GO TO 2140
2130 LET yy=yy+y:LET a$=a$(n+1 TO ):GO TO 2080
2140 INPUT "You have seen all my examples. Do you wish to go through this program again? (Y/N) ";z$
2150 IF z$="y" OR z$="Y" THEN RUN
2160 STOP
2170 CLEAR 32255:LOAD "GrafProgsC"CODE :RUN
2180 SAVE "GrafProgs" LINE 2140:SAVE "GrafProgsC"CODE 32256,300Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
