--- title: "Character Editor" id: 64588 type: "computer_media" slug: "character-editor" url: "http://localhost/computer_media/character-editor/" markdown_url: "http://localhost/computer_media/character-editor.md" published_at: "2026-03-03T01:06:57+00:00" modified_at: "2026-03-30T21:29:27+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2023/06/13-Editor.png" excerpt: "Design custom UDG characters pixel by pixel on a live grid, with instant byte-value readout and automatic RAM updates as you draw." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "User Defined Graphics" slug: "user-defined-graphics" taxonomy: "genre" url: "http://localhost/type/user-defined-graphics/" media_contents: - id: 64513 title: "CATS Library Tape 2" type: "computer_media" url: "http://localhost/computer_media/cats-library-tape-2/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Character%20Editor%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2023/06/13-Editor.png" media_type_tags: "User Defined Graphics" --- # Character Editor 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: | Lines | Purpose | | --- | --- | | 1–5 | Introduction text and keypress gate | | 60–180 | Initialization: INK/PAPER, cursor position, character selection, grid setup | | 300–610 | Main keyboard input loop and cursor rendering | | 700–800 | Add pixel routine | | 900–950 | Delete pixel routine | | 1300–1450 | Draw grid lines and display byte values | | 1500–1800 | Calculate and update UDG character data | | 2000–2060 | Draw 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 ## 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 ```