--- title: "Pablo Pixelo" id: 55861 type: "computer_media" slug: "pablo-pixelo" url: "http://localhost/computer_media/pablo-pixelo/" markdown_url: "http://localhost/computer_media/pablo-pixelo.md" published_at: "2024-07-06T17:27:01+00:00" modified_at: "2026-03-30T21:44:24+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20240706-nmtl.png" excerpt: "Design custom 8×8 pixel characters, pack them into pictures, and watch a self-verifying machine code routine power a \"Big-Bits\" display feature in this complete UDG toolkit." 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/" indiv: - name: "Michael Carver" slug: "michael-carver" taxonomy: "indiv" url: "http://localhost/indiv/michael-carver/" genre: - name: "Art" slug: "art" taxonomy: "genre" url: "http://localhost/type/art/" - name: "User Defined Graphics" slug: "user-defined-graphics" taxonomy: "genre" url: "http://localhost/type/user-defined-graphics/" media_contents: - id: 55879 title: "ISTUG Public Domain Library 7" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-7/" media_type: "Program" programmers: - name: "Michael Carver" slug: "michael-carver" taxonomy: "indiv" url: "http://localhost/indiv/michael-carver/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Pablo%20Pixelo%20%281985%29%28Carver%2C%20Michael%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "1985" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240706-nmtl.png" - url: "http://localhost/wp-content/uploads/2024/07/SCR-20240706-nmop.png" article_media: - id: 37673 title: "Pablo Pixel-O" type: "article" url: "http://localhost/article/pablo-pixel-o/" media_type_tags: "Art, User Defined Graphics" --- # Pablo Pixelo Pablo Pixel-O is a user-defined graphics (UDG) character designer and display utility that lets users draw 8×8 pixel characters on a grid, store them in memory, and compose pictures by assembling those characters into multi-character layouts. The program uses direct POKEs into the UDG area and system variables—particularly the UDG pointer at addresses 23606–23607 and character count storage at 23728–23729—to manage a dynamically allocated character set below the BASIC program. A CLEAR command is calculated at runtime to reserve space for the custom character bitmaps, and binary pixel values are converted to byte data using a VAL/CHR$ idiom at line 315 that parses a binary string with CHR$ 196 (the “BIN” token). A machine code routine is embedded via a hex string in variable a$ at lines 9000–9135, self-verified by checksum against expected value 7488, and used for the “Big-Bits” display feature; the author notes in the REM at line 7 that this MC routine crashes. The program also supports LPRINT hardcopy, SCREEN$ saving, and a paper/ink override mode for each character cell in the picture layout. *** ## Program Analysis ### Program Structure Pablo Pixel-O is organized as a menu-driven application centered on a main menu at line 600. Navigation is handled by a single computed `GO TO` at line 670 that dispatches to one of eight program sections based on the digit entered. The main functional sections are: 1. **Lines 100–330:** Character definition — draw 8×8 characters one row at a time using block graphics input. 2. **Lines 400–595:** Picture coding and display — assemble a grid of custom characters into a full picture with optional per-cell paper/ink colors. 3. **Lines 700–740:** Re-define a single character (edit mode). 4. **Lines 800–995:** “Big-Bits” feature — displays characters at double (or larger) size using a machine code routine. 5. **Lines 1000–1050:** Save routines for SCREEN$ and full program + character data. 6. **Lines 2000–2010:** Loader stub. 7. **Lines 9000–9135:** Machine code poke-in and verification. ### Menu Dispatch Technique The dispatch at line 670 is a single arithmetic `GO TO` expression: ``` GO TO 650-(550 AND z$="1")-(450 AND z$="2")- ... +(150 AND z$="8") ``` Each menu option subtracts or adds a fixed offset from 650 to land on the correct target line. This is a compact Spectrum BASIC idiom that avoids a chain of `IF` statements, though it is somewhat fragile if line numbers shift. Note that option “7” (Continue Picture Coding) has a dual offset: a base `-(230 AND z$="7")` landing on line 420, and an additional `-(20 AND (z$="7" AND l=0))` that subtracts a further 20 (landing on line 400) when no picture has been defined yet (`l=0`). ### UDG Memory Management The program manages UDG memory manually rather than relying on the fixed UDG area. Key system variables used: | Address | System Variable | Purpose | | --- | --- | --- | | 23606–23607 | UDG pointer (CHARS+1) | Points to the custom character set base minus 256 | | 23728–23729 | Used to store `c` (char count) | 16-bit persistence of character count across operations | | 23617 | FLAGS | Bit 1 controls graphics cursor mode for block-graphic input | | 23689 | ATTR_T | Checked to detect current attribute state during display | | 23692 | MASK_T | Set to 23 to control transparency masking | At line 125, a `CLEAR` is issued to allocate memory below BASIC for the character bitmaps: `CLEAR 65367-(8*c)-384`. The base address `ch` is then calculated at line 145 as `65367-8*c-384`. The UDG pointer at 23606–23607 is updated per-character during display (lines 525–543) to point the font rendering engine at the custom character data. ### Binary-to-Byte Conversion Idiom Line 315 uses a well-known Spectrum BASIC trick to convert an 8-character binary string (composed of “0” and “1” characters) into a byte value:`POKE ch+..., VAL (CHR$ 196+b$(p,1 TO 8))` `CHR$ 196` is the token for `BIN`. Concatenating it with the 8-character binary string and passing the result to `VAL` causes the interpreter to evaluate it as a `BIN` literal, yielding the numeric byte value. This avoids any explicit bit-shifting loop. ### Character Input and Validation Characters are entered row by row using block graphics. `POKE 23617,2` at line 220 sets the graphics cursor mode so the user can type block graphic characters directly. Each cell of `i$(a,b)` is validated at lines 235–245: only `CHR$ 128` (space block), `CHR$ 143` (solid block), or `CHR$ 32` (space) are accepted; any other character forces a re-entry. The pixel array `b$` is then built at line 295 by mapping solid blocks to “1” and spaces/empty blocks to “0”. ### Picture Display and Attribute Handling Lines 520–555 render the assembled picture. For each character cell, the UDG pointer is updated so that printing `CHR$ 32` (space, which uses the font) actually renders the custom glyph. Lines 542–543 handle a special case: if `PEEK 23689 < 4` (indicating certain attribute states), the code backs up one position with `CHR$ 8` and redraws to ensure correct attribute application, with separate paths for the `op` (per-cell paper/ink) mode. ### Machine Code Routine (Big-Bits) Lines 9000–9135 poke a 63-byte machine code routine into address 26715. The hex string in `a$` is decoded two nibbles at a time using arithmetic on character codes (lines 9110–9120). A checksum is verified: the sum of all 63 bytes must equal 7488. The routine at 26731 is called via `RANDOMIZE USR 26731` (line 965), and a copy routine at 26771 via `RANDOMIZE USR 26771` (line 985). As noted in the REM at line 7, the author acknowledges this machine code crashes — the Big-Bits section at lines 905–930 also contains a likely bug: `c1` is referenced but `c2` was assigned at line 905, meaning `c1` retains its last value from the picture display section rather than the current character address. ### Bugs and Anomalies - **Line 585:**`IF INKEY$<>"m" OR INKEY$<>"M" THEN GO TO 575` — this condition is always true (a key cannot simultaneously equal both “m” and “M”), so the menu is never reached from this loop. It should use `AND` instead of `OR`. - **Lines 905–930 (Big-Bits):** Variable `c2` is assigned at line 905 but `c1` is used in lines 915 and 925 for POKEing character data. This is almost certainly a copy-paste error causing the wrong character bytes to be poked, contributing to the crash noted by the author. - **Line 435:**`IF q>1 AND r>w THEN GO TO 600` — the condition requires both `q>1` and `r>w`, which would only trigger mid-loop if somehow both were true simultaneously; the intent was likely to allow early exit when `p(q,r)=0`, which is handled separately at line 472. - **Line 670 (option “8”):** The offset `+(150 AND z$="8")` targets line 800 (650+150), correctly reaching the Big-Bits section. - **Line 670 (option “5”):** The offset `+(380 AND z$="5")` targets line 1030, which handles the full program save — but line 1020 is the SCREEN$ save. The save menu option goes to 1030 directly, bypassing the SCREEN$ save path (which is reached only from line 575). ### Save and Load Architecture The program saves in three parts (lines 1040–1045): the BASIC program itself with `LINE 2000` autostart, then the character bitmap data as a CODE block starting at `ch` with length `c*8`. The loader stub at line 2000 loads the CODE block back and returns to the menu. A note at line 1030 warns the user to issue `CLEAR` to the correct address before reloading, since the character data lives in RAM below BASIC. ## Source Code ``` 1 REM aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 5 REM THIS PROGRAM BY Michael E. Carver taken from Time Designs magazine for Sept/Oct '85 6 REM Entered for LIST by John Leary 10/85 7 REM Debugging: I added line 126 and made a correction to line 670. The MC routine in Big Bits crashes. Good luck! J.Leary 10 LET l=0: LET h=0 60 GO TO 600 100 CLS 105 PRINT AT 2,0;"Do you wish to set up a blank file?" 110 INPUT a$ 115 IF CODE a$<>89 AND CODE a$<>121 THEN GO TO 600 120 PRINT AT 2,0;"How many characters do you wish to define?" 125 INPUT c: POKE 23728,c-INT (c/256)*256: POKE 23729,c/256: CLEAR 65367-(8*c)-384: LET c=PEEK 23728+256*PEEK 23729 126 LET l=0: LET h=0 130 DIM i$(8,8): DIM b$(8,8): LET h=1: LET ed=0: DIM a$(10) 135 DIM d$(32): LET hc=0: LET op=0 145 LET ch=65367-8*c-384 200 FOR f=1 TO c 202 PAPER 7: INK 0: BORDER 7: CLS : DIM i$(8,8) 205 PRINT AT 0,0;"Plot out character ";(f AND NOT ed)+(h AND ed) 210 PRINT AT 20,0;d$;d$;AT 5,6;"1";AT 21,1;"12345678";AT 4,7;" 12345678" 215 FOR a=1 TO 8 220 POKE 23617,2: INPUT i$(a,1 TO 8) 225 FOR b=1 TO 8 227 PRINT AT 20,0;d$;" 12345678" 230 IF i$(a,1)="m" OR i$(a,1)="M" OR CODE i$(a,1)=156 THEN GO TO 600 235 IF i$(a,b)=CHR$ 128 OR i$(a,b)=CHR$ 143 OR i$(a,b)=CHR$ 32 THEN GO TO 250 240 PRINT AT a+4,7;"RE-ENTER" 245 GO TO 220 250 NEXT b 255 PRINT AT a+4,7;i$(a,1 TO 8);: IF a<8 THEN PRINT TAB 6;a+1 260 NEXT a 265 PRINT AT 20,0;d$;d$;AT 20,0;"Are you satisfied?" 270 POKE 23617,0: INPUT a$ 275 IF CODE a$<>89 AND CODE a$<>121 THEN GO TO 335 280 FOR a=1 TO 8 290 FOR b=1 TO 8 295 LET b$(a,b)=("1" AND i$(a,b)=CHR$ 143)+("0" AND (i$(a,b)=CHR$ 128 OR i$(a,b)=CHR$ 32)) 300 NEXT b 305 NEXT a 310 FOR p=1 TO 8 315 POKE ch+((f-1 AND NOT ed)+(h-1 AND ed))*8+(p-1),VAL (CHR$ 196+b$(p,1 TO 8)) 320 NEXT p 322 IF ed THEN GO TO 600 325 NEXT f 330 GO TO 600 335 PRINT AT 20,0;d$;d$;AT 20,0;"Do you wish to change individuallines?" 340 INPUT a$ 345 IF CODE a$<>89 AND CODE a$<>121 THEN GO TO 202 350 PRINT AT 20,0;d$;d$;AT 20,0;"What line do you wish to change?" 355 INPUT a 360 PRINT AT 20,0;d$;d$;AT 20,0;"working on line ";a;AT 21,0;" 12345678" 365 POKE 23617,2: INPUT i$(a,1 TO 8) 370 PRINT AT a+4,7;i$(a,1 TO 8) 375 GO TO 265 400 LET re=0: LET op=0: CLS : INPUT "How many characters across will your picture be?";w: IF w>32 THEN GO TO 400 405 INPUT "How many lines will be in your picture?";l 410 INPUT "Do you wish to input paper and ink for each character?"; LINE z$: IF CODE z$=CODE "y" OR CODE z$=CODE "Y" THEN LET op=1 415 LET re=1: DIM p(1,w): IF op THEN DIM a(2,1,w) 420 CLS : PRINT AT 2,0;"Enter code numbers for new","character set in the order you wish them to be displayed": IF z$="7" AND re THEN GO TO 435 425 FOR q=1 TO l 430 FOR r=1 TO w 435 IF q>1 AND r>w THEN GO TO 600 440 PRINT AT 19,0;d$;d$;AT 20,0;"Line ";q;" / Space ";r: INPUT p(q,r) 445 IF p(q,r)>=0 AND p(q,r)<=c THEN GO TO 465 455 PRINT AT 19,0;"Invalid input, re-enter!" 460 GO TO 440 465 IF op THEN INPUT "Paper? #";a(1,q,r): IF a(1,q,r)>9 OR a(1,q,r)<0 THEN GO TO 465 470 IF op THEN INPUT "Ink? #";a(2,q,r): IF a(2,q,r)>9 OR a(2,q,r)<0 THEN GO TO 470 472 IF p(q,r)=0 THEN GO TO 600 475 NEXT r: NEXT q 480 INPUT "Paper color? #";pa: IF pa>9 OR pa<0 THEN GO TO 480 485 INPUT "Border color? #";bo: IF bo>7 OR bo<0 THEN GO TO 485 490 INPUT "Ink color? #";in: IF in>7 OR in<0 THEN GO TO 490 500 BORDER bo: PAPER pa: INK in: CLS 510 INPUT "Do you wish a hardcopy? "; LINE z$: IF CODE z$=CODE "y" OR CODE z$=CODE "Y" THEN LET hc=1 520 FOR a=1 TO l: FOR b=1 TO w 525 LET c1=ch+(p(a,b)-1)*8: POKE 23606,c1-INT (c1/256)*256: POKE 23607,INT (c1/256)-1 530 IF PEEK 23689<3 THEN POKE 23606,0: POKE 23607,60 535 IF op THEN PRINT PAPER a(1,a,b); INK a(2,a,b);CHR$ 32; 540 IF NOT op THEN PRINT CHR$ 32;: IF hc THEN LPRINT CHR$ 32; 542 IF NOT op AND PEEK 23689<4 THEN PRINT CHR$ 8;: POKE 23606,c1-INT (c1/256)*256: POKE 23607,INT (c1/256)-1: PRINT CHR$ 32 543 IF op AND PEEK 23689<4 THEN PRINT CHR$ 8;: POKE 23606,c1-INT (c1/256)*256: POKE 23607,INT (c1/256)-1: PRINT PAPER a(1,a,b); INK a(2,a,b);CHR$ 32; 545 NEXT b: PRINT 550 IF hc THEN LPRINT 555 NEXT a 560 POKE 23606,0: POKE 23607,60: POKE 23692,23 565 PRINT #1;"Z=COPY M=MENU S=SCREEN$ SAVE" 570 IF INKEY$<>"" THEN GO TO 560 575 IF INKEY$="s" OR INKEY$="S" THEN GO TO 1000 580 IF INKEY$="z" OR INKEY$="Z" THEN COPY 585 IF INKEY$<>"m" OR INKEY$<>"M" THEN GO TO 575 590 LET hc=0 600 BORDER 1: PAPER 1: INK 9: CLS : LET ed=0 605 POKE 23617,0 610 PRINT AT 2,0; BRIGHT 1;"**** PABLO PIXEL-O ****" 620 PRINT AT 4,4;"DEFINE CHARACTERS.......1";AT 6,4;"CONTINUE DEFINITIONS....2";AT 8,4;"PICTURE CODING..........3";AT 10,4;"PRINT OUT PICTURE.......4";AT 12,4;"SAVE....................5";AT 14,4;"RE-DEFINE CHARACTER.....6";AT 16,4;"CONTINUE PICTURE CODING.7";AT 18,4;"BIG-BITS................8" 630 PRINT BRIGHT 1;AT 20,5;"ENTER ONE OF THE ABOVE" 650 INPUT LINE Z$ 660 IF CODE Z$<49 OR CODE Z$>56 THEN GO TO 650 670 GO TO 650-(550 AND z$="1")-(450 AND z$="2")-(250 AND z$="3")-(150 AND z$="4")+(380 AND z$="5")+(50 AND z$="6")-(230 AND z$="7")-(20 AND (z$="7" AND l=0))+(150 AND z$="8") 700 LET ed=1 710 CLS 720 PRINT AT 2,0;"Which character do you wish to re-define? (enter #)" 730 INPUT h 740 GO TO 202 800 CLS : DIM b(6,8) 805 INPUT "How many lines? (6 max.) ";j 810 IF j<1 OR j>6 THEN GO TO 810 815 INPUT "How many across? (8 max.) ";k 820 IF k<1 OR k>8 THEN GO TO 820 830 PRINT AT 2,0;"Enter code numbers in the order you wish them displayed." 835 FOR a=1 TO j 840 FOR b=1 TO k 845 PRINT AT 19,0;d$;d$;AT 20,0;"Line ";a;" / Space ";b 850 INPUT b(a,b) 855 IF b(a,b)>=1 AND b(a,b)<=c THEN GO TO 870 860 PRINT FLASH 1;AT 19,0;"Invalid input. Re-enter." 865 GO TO 850 870 NEXT b 875 NEXT a 900 PAPER 7: BORDER 7: INK 0: CLS : PRINT #1;"Note pad is being loaded. Z=COPY M=MENU" 902 LET no=64983: POKE 23659,0 905 FOR a=1 TO 6: FOR b=0 TO 7 STEP 2: FOR d=1 TO 8: LET c2=ch+(b(a,d)-1)*8 910 IF NOT b(a,d) THEN POKE no,0 915 IF b(a,d) THEN POKE no,PEEK (c1+b) 920 IF NOT b(a,d) THEN POKE no+1,0 925 IF b(a,d) THEN POKE no+1,PEEK (c1+b+1) 930 LET no=no+2 960 NEXT d: NEXT b: NEXT a 965 PRINT AT 0,0;: RANDOMIZE USR 26731 970 POKE 23659,2: POKE 26773,j*32 980 IF INKEY$<>"" THEN GO TO 980 985 IF INKEY$="z" OR INKEY$="Z" THEN RANDOMIZE USR 26771 990 IF INKEY$<>"m" AND INKEY$<>"M" THEN GO TO 985 995 GO TO 600 1000 REM save screen$ 1010 INPUT "Picture title: ";a$ 1020 SAVE a$SCREEN$ : GO TO 600 1030 CLS : PRINT AT 10,0; FLASH 1;"please note that you will have to enter CLEAR ";PEEK 23730+256*PEEK 23731;" before loading this program after saving! " 1035 INPUT "What is the title? ";a$ 1040 SAVE a$ LINE 2000 1045 SAVE a$CODE ch,c*8 1050 GO TO 600 2000 LOAD a$CODE 2010 GO TO 600 9000 LET ad=26715 9100 LET a$="8084888c8185898d82868a8e83878b8f21d7fd06c0c5060456235e23e5afcb1217cb1217cb1317cb1317215b68856f7ed710eae1c110dec9f306c0cd050ac9" 9105 IF LEN a$<>126 THEN PRINT "Error in a$ please correct.": STOP 9110 FOR x=1 TO LEN a$-1 STEP 2 9115 POKE ad+INT ((x-1)/2),(CODE a$(x)-(48 AND CODE a$(x)<58)-(55 AND CODE a$(x)>64))*16+CODE a$(x+1)-(48 AND CODE a$(x+1)<58)-(55 AND CODE a$(x+1)>64) 9120 NEXT x 9125 LET ck=0: FOR x=0 TO 62: LET ck=ck+PEEK (26715+x): NEXT x 9130 IF ck<>7488 THEN PRINT "Look for errors in a$.": STOP 9135 IF ck=7488 THEN PRINT "Machine code checks out." 9899 VERIFY "PIXEL-O" 9999 SAVE "PIXEL-O" LINE 1 ```