Character Editor

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

This program is an interactive UDG (User Defined Graphics) character editor that allows users to design custom characters on an 8×8 pixel grid. The editor draws a visual grid using PLOT and DRAW commands, supports cursor movement via the numeric arrow keys (5/6/7/8), and allows pixels to be added with “a” or deleted with “d”. Each pixel change updates a byte array c(8) using bit manipulation (powers of two based on column position), and the computed values are immediately POKEd into UDG RAM via USR CHR$(144+c). The program supports 24 UDGs (numbered 0–23) and displays both the current byte values and a live preview of the character being edited.


Program Structure

The program is organized into clearly separated routines, navigated by line-number ranges:

LinesPurpose
1–5Introduction text and keypress gate
60–180Initialization: INK/PAPER, cursor position, character selection, grid setup
300–610Main keyboard input loop and cursor rendering
700–800Add pixel routine
900–950Delete pixel routine
1300–1450Draw grid lines and display byte values
1500–1800Calculate and update UDG character data
2000–2060Draw screen border

There is a notable gap between lines 390 and 410: line 392 routes unrecognised keypresses back to the input loop, but the cursor-bounds clamping at lines 410–440 is only reached via a GO TO 400 from the movement keys. This means the GO TO 400 target does not actually exist as a line — lines jump to 400 but execution falls through to 410, which is the intended behavior in Sinclair BASIC (execution continues at the next available line).

Cursor Rendering

The cursor is drawn using PLOT/DRAW with INVERSE 1 to erase the old cursor position by XOR-inverting pixels, then redrawn at the new position without INVERSE. The cursor shape is a cross: a horizontal segment and a vertical segment, each 4 pixels long, offset by 2 pixels from the cell corner. The screen coordinates are derived as xc = x*8 and yc = (21-y)*8, mapping grid coordinates to pixel coordinates.

Pixel Add/Delete and Attribute Detection

When adding a pixel (line 700), the program checks PEEK(q) against attribute values before writing. Attribute byte 10 corresponds to INK 2, PAPER 1 (used to indicate already-set or forbidden cells), and 8 indicates a special state; if either is found, the add is skipped. When deleting, attribute 56 (INK 0, PAPER 7 — the background) is the guard value. Direct attribute manipulation via POKE q, 10 and POKE q, 56 is used rather than drawing characters, relying on the attribute file at address 22528.

The address calculation q = x + 22528 + (32*y) is the standard Spectrum attribute file formula, confirming these are attribute memory addresses, not display file addresses.

UDG Data Calculation

The subroutine at line 1500 computes which bit within the character byte corresponds to the current pixel:

  • xv = 7-(x-10) converts screen column (10–17) to bit position (7–0, MSB to LSB)
  • z = 2^xv produces the bitmask for that column
  • v = y-5 maps screen row (6–13) to array index (1–8)
  • The flag p selects addition (p=0) or subtraction (p=1) to set or clear the bit in c(v)

No bitwise AND/OR is available in Sinclair BASIC, so arithmetic addition and subtraction serve as bit-set and bit-clear operations, which is correct only if the bit was not already set/cleared — the attribute pre-checks at lines 715–716 and 915 serve as guards to prevent double-toggling.

UDG RAM Poking

At line 1710, USR CHR$(144+c) returns the address of the first byte of the selected UDG in RAM. The loop at lines 1720–1740 then POKEs all 8 bytes from the array c(). There is a subtle bug here: POKE q+(s-1), c(q) should be POKE s+(q-1), c(q) — using q as both the loop index and as part of the address with an off-by-one adjustment (s-1 instead of s) means when q=1 the POKE goes to address s, which is correct, but the expression is needlessly confusing and would only be correct because q+(s-1) = s+(q-1) algebraically — so the result is actually correct despite the unusual form.

Key BASIC Idioms

  • Busy-wait key polling: IF INKEY$="" THEN GO TO 310
  • Screen grid drawn with FOR loops using PLOT/DRAW rather than character graphics
  • DIM c(8) initializes the byte array to zeros, conveniently representing a blank UDG
  • PRINT AT 5,5;CHR$(144+c) provides a live preview of the UDG as it is edited

Content

Appears On

Capital Area Timex Sinclair User Group’s Library Tape.

Related Products

Related Articles

Related Content

Image Gallery

Character Editor

Source Code

    1 REM character editor
    2 PRINT "This character editor lets you  design UDGs easily and reports  the values so that you can      include them in a program.  The arrow keys move the cursor, ""a"" adds a pixel, ""d"" deletes, and  ""n"" moves you to the next UDG,  which are numbered 0-23." 
    3 PRINT : PRINT : PRINT "Press any key to begin."
    4 IF INKEY$="" THEN GO TO 4
    5 CLS 
   60 INK 0: PAPER 7
  110 LET x=10: LET y=6
  120 DIM c(8)
  125 CLS : GO SUB 2000
  130 PRINT AT 20,6;"Input character number";
  140 INPUT c: PRINT c
  150 CLS 
  160 GO SUB 2000
  170 GO SUB 1300
  180 GO TO 550
  300 REM keyboard input
  310 IF INKEY$="" THEN GO TO 310
  320 LET xo=x: LET yo=y
  330 IF INKEY$="a" THEN GO TO 700
  340 IF INKEY$="d" THEN GO TO 900
  350 IF INKEY$="5" THEN LET x=x-1: GO TO 400
  360 IF INKEY$="6" THEN LET y=y+1: GO TO 400
  370 IF INKEY$="7" THEN LET y=y-1: GO TO 400
  380 IF INKEY$="8" THEN LET x=x+1: GO TO 400
  390 IF INKEY$="n" THEN GO TO 100
  392 GO TO 300
  410 IF x<10 THEN LET x=10
  420 IF x>17 THEN LET x=17
  430 IF y<6 THEN LET y=6
  440 IF y>13 THEN LET y=13
  500 REM draw cursor
  510 LET xc=xo*8: LET yc=(21-yo)*8
  520 PLOT INVERSE 1;xc+2,yc+4
  530 DRAW INVERSE 1;4,0
  540 PLOT INVERSE 1;xc+4,yc+2
  550 DRAW INVERSE 1;0,4
  560 LET xc=x*8: LET yc=(21-y)*8
  570 PLOT xc+2,yc+4
  580 DRAW 4,0
  590 PLOT xc+4,yc+2
  600 DRAW 0,4
  610 GO TO 300
  700 REM add point
  710 LET q=x+22528+(32*y)
  715 IF PEEK (q)=10 THEN GO TO 300
  716 IF PEEK (q)=8 THEN GO TO 300
  720 POKE q,10
  730 LET p=0
  740 GO SUB 1500
  800 GO TO 300
  900 REM delete point
  910 LET q=x+22528+(32*y)
  915 IF PEEK (q)=56 THEN GO TO 300
  920 POKE q,56
  930 LET p=1
  940 GO SUB 1500
  950 GO TO 300
 1300 REM display grid
 1310 FOR g=64 TO 128 STEP 8
 1320 PLOT 80,g
 1330 DRAW 64,0
 1340 NEXT g
 1350 FOR g=80 TO 144 STEP 8
 1360 PLOT g,64
 1370 DRAW 0,64
 1380 NEXT g
 1390 FOR q=1 TO 8
 1400 PRINT AT q+5,20;c(q)
 1410 NEXT q
 1420 PRINT AT 20,8;"Character #-";
 1430 PRINT c
 1440 PRINT AT 5,5;CHR$ (144+c)
 1450 RETURN 
 1500 REM calculate character values
 1510 LET xv=7-(x-10)
 1520 LET z=2^xv
 1530 LET v=y-5
 1540 IF p=1 THEN GO TO 1600
 1550 LET c(v)=c(v)+z
 1560 GO TO 1650
 1610 LET c(v)=c(v)-z
 1650 FOR q=1 TO 8
 1660 PRINT AT q+5,20;"   "
 1670 PRINT AT q+5,20;c(q)
 1680 NEXT q
 1710 LET s=USR CHR$ (144+c)
 1720 FOR q=1 TO 8
 1730 POKE q+(s-1),c(q)
 1740 NEXT q
 1750 PRINT AT 5,5;CHR$ (144+c)
 1800 RETURN 
 2000 REM draw border
 2010 PLOT 0,0
 2020 DRAW 255,0
 2030 DRAW 0,175
 2040 DRAW -255,0
 2050 DRAW 0,-175
 2060 RETURN 

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

People

No people associated with this content.

Scroll to Top