UDG

Date: 198x
Type: Program
Platform(s): TS 2068

This program is a UDG (User Defined Graphics) editor that lets users design up to 21 custom characters by painting individual pixels on an 8×8 grid. An on-screen grid is drawn using PLOT and DRAW commands, and the user enters three-character codes specifying a row letter (A–H), a column number (1–8), and either B (black) or W (white) to set each pixel. Pixel state is tracked in a two-dimensional array and converted from a column-weighted binary representation to decimal byte values using a halving-power technique before being POKEd into UDG memory starting at address 65368. When all UDGs are designed, the program prompts the user to save the raw UDG data block (168 bytes) to tape using the SAVE…CODE command.


Program Analysis

Program Structure

The program is organized into clearly labeled subroutines dispatched from a short main loop at lines 60–100:

  1. Lines 200–270 — Draw the 8×8 pixel grid using PLOT/DRAW.
  2. Lines 400–550 — Print column numbers, row letters, and on-screen instructions.
  3. Lines 600–680 — Accept user input and update the grid display and backing array.
  4. Lines 800–920 — Convert the array to bytes and POKE them into UDG memory; then restart for the next UDG.
  5. Lines 1000–1100 — Save the completed UDG block to tape and optionally repeat.

Grid Drawing (Lines 200–270)

The grid is drawn in INK 5 (cyan) by iterating A from 95 to 159 in steps of 8, plotting one horizontal line and one vertical line per iteration. This efficiently draws a 9×9 grid of lines enclosing an 8×8 cell matrix using a single loop with two PLOT/DRAW pairs, rather than separate loops for horizontal and vertical lines.

User Input Convention

The user types a three-character string such as A1B or C4W. The program decodes it as follows:

  • B$(1) — Row letter (A–H); CODE B$(1)-63 maps A→2…H→9 for the PRINT AT row, and CODE B$(1)-64 maps A→1…H→8 for the array index.
  • B$(2) — Column digit (1–8); CODE B$(2)-37 maps “1”→12…”8″→19 for the PRINT AT column, and VAL B$(2) provides the array column index.
  • B$(3) — Either B (black, CHR$ 143 = solid block UDG graphic) or W (white, CHR$ 128 = blank block graphic).

Special single-character inputs S (stop) and N (next UDG) are checked first before the three-character parsing is attempted. If B$ is shorter than three characters when neither S nor N was entered, accessing B$(3) will cause a BASIC error — a minor robustness issue.

Pixel State Representation

The array A(8,8) stores 143 for a set (black) pixel and 128 for a clear (white) pixel, mirroring the CHR$ codes used for display. Zero is the initial value and is also assigned during the clear-down pass in line 880, which is equivalent to white in this context.

Byte Encoding (Lines 800–920)

Each row of eight pixels is converted to a byte using a classic binary-weighting technique. The variable BINARY starts at 128 and is halved each iteration, so column 1 is the most-significant bit and column 8 is the least-significant bit. If a cell holds 143 (black), its weight is added to DECIMAL. The resulting byte is POKEd to LOCATION, which starts at 65367 and is incremented before each POKE, so the first byte lands at 65368 — the standard ZX Spectrum UDG base address (the address of UDG ‘A’).

UDG Memory Layout

VariableValue / Role
LOCATIONInitialized to 65367; incremented to 65368 before first POKE
SAVE range start65368 (first byte of UDG ‘A’)
SAVE length168 bytes (21 UDGs × 8 bytes each)
POKE 23658,8Caps lock on — forces uppercase INPUT without CAPS SHIFT

Notable Techniques

  • POKE 23658,8 at line 50 sets the FLAGS2 system variable bit to lock the keyboard into uppercase mode, ensuring the letter inputs (A–H, B, W, S, N) are received correctly without relying on the user’s CAPS LOCK state.
  • After each UDG is defined, the subroutine at line 800 zeroes out the array (line 880) and the program jumps back to GO TO 60 (CLS and reinitialize) rather than returning, allowing multiple sequential UDGs to be designed in one session.
  • The SAVE at line 1040 saves only the raw UDG bitmap data as a CODE block, not the BASIC program itself, so the saved file can be loaded independently to install custom graphics.

Bugs and Anomalies

  • The outer FOR A=1 TO 8 loop at line 420 is redundant — its body only uses B, so the PRINT AT header rows and column labels are drawn eight times unnecessarily. The NEXT A at line 470 should likely be removed along with the outer loop.
  • If the user enters N or S, the program branches before checking B$(3), which is safe. However, any other input shorter than three characters will trigger a “Subscript out of range” error when line 640 evaluates B$(3).
  • LOCATION is only reset to 65367 at line 40 during program initialization, not between UDGs. Each call to the line-800 subroutine advances it by 8, so successive UDGs are correctly placed in sequential 8-byte slots — this is intentional rather than a bug.

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Source Code

   10 REM UDG
   20 REM by John Bell
   30 DIM A(8,8)
   40 LET LOCATION=65367
   50 POKE 23658,8
   60 CLS 
   70 GO SUB 200
   80 GO SUB 400
   90 GO SUB 600
  100 STOP 
  200 REM DRAW GRID
  210 INK 5
  220 FOR A=95 TO 159 STEP 8
  230 PLOT 95,A: DRAW 64,0
  240 PLOT A,159: DRAW 0,-64
  250 NEXT A
  260 INK 0
  270 RETURN 
  400 REM PRINT UDG
  410 INK 4
  420 FOR A=1 TO 8
  430 FOR B=1 TO 8
  440 PRINT AT 1,B+11;B
  450 PRINT AT B+1,11;CHR$ (B+64)
  460 NEXT B
  470 NEXT A
  480 INK 0
  490 PRINT AT 15,0;"input row letter"
  500 PRINT AT 16,6;"COLUMN NUMBER"
  510 PRINT AT 17,6;"B for black orW for white"
  520 PRINT AT 18,9;"OR"
  530 PRINT AT 19,6;"N to define next UDG"
  540 PRINT AT 20,6;"S to stop"
  550 RETURN 
  600 REM INPUT BITS
  610 INPUT B$
  620 IF B$="S" THEN GO TO 1000
  630 IF B$="N" THEN GO TO 800
  640 IF B$(3)="B" THEN LET COLOR=143
  650 IF B$(3)="W" THEN LET COLOR=128
  660 PRINT AT CODE B$(1)-63,CODE B$(2)-37;CHR$ COLOR
  670 LET A(CODE B$(1)-64,VAL B$(2))=COLOR
  680 GO TO 600
  800 REM POKE DATA
  810 FOR A=1 TO 8
  820 LET LOCATION=LOCATION+1
  830 LET DECIMAL=0
  840 LET BINARY=128
  850 FOR B=1 TO 8
  860 IF A(A,B)=143 THEN LET DECIMAL=DECIMAL+BINARY
  870 LET BINARY=BINARY/2
  880 LET A(A,B)=0
  890 NEXT B
  900 POKE LOCATION,DECIMAL
  910 NEXT A
  920 GO TO 60
 1000 REM END PROGRAM
 1010 CLS 
 1020 FLASH 1
 1030 PRINT "START RECORDER"
 1040 SAVE "UDG"CODE 65368,168
 1050 FLASH 0
 1060 CLS 
 1070 INPUT "SAVE AGAIN? Y/N ";C$
 1080 IF C$="Y" THEN GO TO 1030
 1090 CLS 
 1100 PRINT "PROGRAM COMPLETE"

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top