--- title: "EASYsprite DEMO" id: 59178 type: "computer_media" slug: "easysprite-demo" url: "http://localhost/computer_media/easysprite-demo/" markdown_url: "http://localhost/computer_media/easysprite-demo.md" published_at: "2025-02-28T02:09:58+00:00" modified_at: "2026-03-30T21:41:47+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2025/02/Sprite-Demo-1.png" excerpt: "A 1987 sprite engine demo with animated train, aquarium, and boxing scenes, showcasing a machine code utility driven entirely through a custom PRINT channel." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Byte Power" slug: "byte-power" taxonomy: "post_tag" url: "http://localhost/tag/byte-power/" - 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: "Eric Boisvert" slug: "boisvert-eric" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-eric/" genre: - name: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" media_contents: - id: 50506 title: "Byte Power Fall 1987" type: "computer_media" url: "http://localhost/computer_media/byte-power-fall-1987/" media_type: "Program" programmers: - name: "Eric Boisvert" slug: "boisvert-eric" taxonomy: "indiv" url: "http://localhost/indiv/boisvert-eric/" download_url: "https://archive.org/download/timex-sinclair-software-archive/EASYSprite%20%281987%29%28Byte%20Power%20Fall%201987%29%28TS2068%29%28CA%29%28Program%29.zip" mediadate: "1987" producer_company: - id: 25181 title: "Byte Power" type: "company" url: "http://localhost/company/byte-power/" images: - url: "http://localhost/wp-content/uploads/2025/02/Sprite-Demo-1.png" - url: "http://localhost/wp-content/uploads/2025/02/Sprite-Demo-2.png" - url: "http://localhost/wp-content/uploads/2025/02/Sprite-Demo-3.png" article_media: - id: 58998 title: "EASYsprite DEMO" type: "article" url: "http://localhost/article/easysprite-demo/" related_content: - id: 59182 title: "EASYsprite" type: "computer_media" url: "http://localhost/computer_media/easysprite/" - id: 59181 title: "EASYsprite Maker" type: "computer_media" url: "http://localhost/computer_media/easysprite-maker/" media_type_tags: "Demo, Programming" --- # EASYsprite DEMO This program is a demonstration showcase for a sprite utility called “OPEN SPRITE,” written by Eric Boisvert and published by Byte Power in 1987. It activates a machine code sprite engine via RANDOMIZE USR 65112, then uses PRINT #3 as the sprite output channel, passing AT row,col and CHR$ n to position and select sprite images. The demo presents three animated scenes — a title screen with colored sprites, a train simulation, an aquarium simulation, and a boxing match — all driven by the sprite system alongside regular PRINT statements. Sprites are controlled via POKEs to addresses such as 65286, 65292, 65298, and 65304, which appear to set clipping boundaries (left, right, top, bottom) for the sprite display area. The program loads its machine code payload separately as “DEMO CODE” starting at address 62414, and a speech-bubble style text scroller at line 1020 animates dialogue by sliding strings through a fixed-width window using substring indexing. *** ## Program Analysis ### Program Structure The program is organized into a title/splash section, a main narrative sequence, and several self-contained demo scenes, all glued together with a reusable dialogue subroutine. Control flows roughly as follows: 1. Lines 10–80: Initialization, machine code setup, title screen with colored sprites, pause. 2. Lines 90–210: Introduction sequence — the “host” character (Eric) slides in and delivers explanatory text. 3. Lines 220–370: Train demo — a train sprite scrolls across the screen with smoke/wheel animation. 4. Lines 380–490: Aquarium simulation — fish sprites move left and right with boundary wrapping. 5. Lines 500–600: Boxing match simulation — two sprites exchange punches in a loop. 6. Lines 610–990: Final sequence — sprite attributes demonstrated, clipping boundary showcase, ceiling gag, sign-off. 7. Lines 1000–1110: Subroutines — `GO SUB 1000` draws the paper-7 background window; `GO SUB 1020`/`GO SUB 1030` run the speech-bubble text animator. 8. Lines 9998–9999: Bootstrap loader and SAVE block. ### Machine Code Integration The sprite engine is loaded as a separate binary (“DEMO CODE”) at address 62414 and activated at line 30 with `RANDOMIZE USR 65112`. This call opens the sprite system and patches channel 3 (`PRINT #3`) so that subsequent output to that channel is interpreted as sprite commands rather than text. Two additional machine code entry points appear later: - `RANDOMIZE USR 65370` — called from the dialogue scroller (line 1070/1090) to scroll or clear the speech-bubble area. Lines 35–36 perform a 16-bit address poke to store the value 62414 at address 65236/65237, presumably patching a jump or data pointer inside the machine code: ``` LET a=62414: LET b=65236 POKE b,a-256*INT(a/256): POKE b+1,INT(a/256) ``` This is a standard low/high byte split technique to write a 16-bit address into two consecutive memory locations without using assembly. ### PRINT #3 Sprite Command Protocol Once the sprite channel is open, `PRINT #3` accepts a mini command language via `CHR$` codes. Observed usage suggests the following protocol: | Sequence | Meaning | | --- | --- | | `CHR$ 16; CHR$ n` | Set ink/color index to *n* | | `AT row, col` | Set sprite position | | `CHR$ n` (1–33+) | Select and draw sprite number *n* | | `CHR$ 14` | Draw standing/default host sprite | | `CHR$ 15` / `CHR$ 16` | Animation frames (walk/erase cycle) | | `CHR$ 17` | Place sprite at off-screen/ceiling row 255 | | `CHR$ 21; CHR$ 1` | Speech bubble sprite | | `CHR$ 11` | Clear/erase sprite at position | The use of `CHR$ 16` and `CHR$ 17` mirrors the standard Spectrum embedded color control codes, suggesting the sprite engine intercepts and reinterprets them before they reach the display file. ### Clipping Boundary POKEs The sprite engine exposes four boundary registers via POKE: | Address | Role | | --- | --- | | `65286` | Left boundary (column) | | `65292` | Right boundary (column) | | `65298` | Top boundary (row) | | `65304` | Bottom boundary (row) | Lines 740 and 840 use these to confine sprite movement to a highlighted rectangle on screen, visually demonstrating the clipping feature as part of the demo’s tutorial narrative. ### Speech-Bubble Text Scroller (Lines 1020–1110) The dialogue subroutine at line 1020 implements a smooth 7-character sliding window over the message string. The string is padded at both ends (`" "+s$+" "`), then a `FOR` loop extracts `s$(x TO x+6)` for display at the bubble position, creating a horizontal scroll effect. A secondary cursor (`cx`, `cy`) advances through the remaining characters via `PRINT INK 8; PAPER 8` — invisible ink on invisible paper — apparently to keep the Spectrum’s system PRINT position synchronized with the sprite channel output so that `SCREEN$` interrogation at line 1080 returns meaningful data. The check `IF SCREEN$(CX-1,CY)<>" "` detects whether the cursor has reached occupied screen content and bumps the row accordingly. ### Aquarium Simulation (Lines 400–490) Three fish are tracked in a `DIM F(3,5)` array where each row stores: sprite number, row position, column position, direction (+1/-1), and current column offset. Fish moving off either edge have their direction reset to zero (`F(X,4)=0`), at which point the re-initialization branch at line 450 assigns new random values. The column value is stored via `POKE 23681` and retrieved with `PEEK 23681` — this is the system variable `SEED` (RAND seed) being repurposed as a temporary signed-byte register, exploiting the fact that POKEing a value ≥128 stores it as a negative offset when read back, enabling off-screen left positioning. ### Signed Coordinate Trick The use of `POKE 23681, x` followed by `PRINT #3; AT row, PEEK 23681` appears throughout the program (lines 260, 470). Since BASIC variables hold floating-point numbers, negative column values cannot be passed directly to `AT`. By poking into a byte location and peeking it back, negative values wrap to 256-offset equivalents (e.g., -1 → 255), which the sprite engine presumably interprets as off-screen-left positions, enabling smooth entry from the right and exit to the left. ### Bootstrap Loader (Lines 9998–9999) Line 9998 performs a `CLEAR 62000` to protect the machine code area, loads “DEMO CODE” to address 62414, then `RUN`s the program from line 10. Line 9999 saves both the BASIC program (with auto-run pointing to 9998) and the machine code block as a companion file. This two-file distribution pattern is standard practice for BASIC+machine code software of this era. ### Notable Anomalies and Points of Interest - Line 1010 uses `PRINT AT x,0; PAPER 7; INK 0,,:` — the double comma after `INK 0` is unusual; the second comma likely suppresses a newline or passes an empty parameter to a stream, though on a standard Spectrum this would be a syntax oddity worth testing. - Line 600: `IF PEEK 23560=0 THEN NEXT X: PAUSE 60` — address 23560 is the FLAGS system variable. Polling bit 0 (or the whole byte for zero) here appears to be waiting for a keypress or interrupt condition set by the machine code, acting as a frame-synchronization or “press any key” gate. - Line 880: `PRINT #3;AT 255,15;CHR$ 17` — row 255 is far outside the visible screen, used intentionally to move the host sprite off-screen instantly as the “ceiling gag” setup. - The variable `I` is used in a fractional form (`LET i=6: ... LET i=i-1: IF i=0 THEN LET i=6`) at lines 970–980, cycling through ink colors for the spider/ceiling sprite as a simple animation loop. ## Source Code ``` 10 REM SPRITE DEMO 20 BORDER 0: INK 7: PAPER 0: CLS 30 RANDOMIZE USR 65112: REM OPEN SPRITE 35 LET a=62414: LET b=65236 36 POKE b,a-256*INT (a/256): POKE b+1,INT (a/256) 40 FOR I=1 TO 7: FOR X=1 TO 6: PRINT #3;CHR$ 16;CHR$ I;AT 5,4+X*2;CHR$ X;: NEXT X: NEXT I 50 FOR I=1 TO 6: FOR X=7 TO 10: PRINT #3;CHR$ 16;CHR$ I;AT 9,X*3-6;CHR$ X; 60 PRINT AT 15,4; INK I;"WRITTEN BY ERIC BOISVERT";AT 17,3;"COPYRIGHT ©1987 BYTE POWER" 70 NEXT X: NEXT I 80 PAUSE 300: CLS 90 REM MAIN 100 GO SUB 1000 110 FOR X=32 TO 15 STEP -1 120 PRINT #3;AT 10,X;CHR$ 16;: PAUSE 5: PRINT #3;AT 10,X;CHR$ 15;: PAUSE 5: NEXT X 130 LET cx=16: LET cy=0: PRINT #3;AT 10,15;CHR$ 14: PAUSE 60: LET bx=6: LET by=17 140 LET S$="Hi, I'm Eric, your host for the next few minutes...": GO SUB 1020 150 LET S$="I wrote this demo program to show you how to use the SPRITE utility...": GO SUB 1020 160 FOR X=15 TO 2 STEP -1 170 PRINT #3;AT 10,X;CHR$ 16;: PAUSE 5: PRINT #3;AT 10,X;CHR$ 15;: PAUSE 5: NEXT X 180 PRINT #3;AT 10,2;CHR$ 14;: LET by=4 190 LET s$="First I would like to define what a SPRITE is.": GO SUB 1020 200 LET s$="A sprite is simply a graphic like a UDG but it can be much bigger...I am a perfect example of sprite and so is this..." 210 GO SUB 1020 220 REM TRAIN DEMO 230 GO SUB 1000: PLOT 0,130: DRAW 255,10 240 PRINT #3;AT 3,4;CHR$ 31;#3;AT 2,27;CHR$ 31;#3;AT 7,5;CHR$ 32;#3;AT 8,27;CHR$ 32; 250 FOR x=0 TO 31 STEP 4: PRINT #3;CHR$ 17;CHR$ 4;AT 14,x;CHR$ 30;: NEXT x 260 FOR t=1 TO 3: FOR x=32 TO -15 STEP -1: POKE 23681,x: PRINT #3;CHR$ 16;CHR$ 2;AT 12,PEEK 23681;CHR$ 29;: PAUSE 5: IF x=10 THEN PAUSE 60 270 IF t=3 AND x=1 THEN GO TO 290 280 NEXT x: NEXT t 290 FOR X=32 TO 16 STEP -1 300 PRINT #3;AT 11,X;CHR$ 16;: PAUSE 5: PRINT #3;AT 11,X;CHR$ 15;: PAUSE 5: NEXT X 310 PRINT #3;AT 11,16;CHR$ 14;: LET bx=7: LET by=18 320 LET s$="Hi again... As you may already have noticed, this image was mostly formed with sprites of different size, color, and shape...": GO SUB 1020 330 OVER 1: FOR x=1 TO 10: PRINT #3;AT 7,8;CHR$ 12;#3;AT 3,6;CHR$ 12;#3;AT 12,13;CHR$ 12;#3;AT 12,18;CHR$ 12;: PAUSE 10 340 PRINT #3;AT 7,8;CHR$ 12;#3;AT 3,6;CHR$ 12;#3;AT 12,13;CHR$ 12;#3;AT 12,18;CHR$ 12;: PAUSE 10: NEXT x: OVER 0 350 LET s$="Yes, even me!": GO SUB 1020 360 LET s$="Now that you have a good idea of what is a sprite...Watch the next few examples...": GO SUB 1020 370 PAUSE 180 380 REM AQUARIUM DEMO 390 PAPER 0: INK 7: CLS 400 POKE 23560,0: POKE 23672,0: POKE 23673,0 410 FOR X=1 TO 5: PRINT #3;CHR$ 16;CHR$ 4;AT 18,X*5;CHR$ 24;: NEXT X 420 DIM F(3,5): PRINT AT 21,0; PAPER 4;TAB 6;"AQUARIUM SIMULATION", 430 RESTORE 440: FOR Y=1 TO 3: FOR X=1 TO 5: READ F(Y,X): NEXT X: NEXT Y 440 DATA 20,8,4,1,4,21,14,3,-1,27,23,3,6,1,26 450 FOR X=1 TO 3: IF F(X,4)<>0 THEN GO TO 470 460 LET F(X,1)=INT (RND*4)+20: LET F(X,2)=5+INT (RND*10): LET F(X,3)=INT (RND*4)+4: LET F(X,4)=1: LET F(X,5)=-5: IF F(X,1)=21 OR F(X,1)=22 THEN LET F(X,5)=32: LET F(X,4)=-1 470 POKE 23681,F(X,5): PRINT #3;CHR$ 16;CHR$ F(X,3);AT F(X,2),PEEK 23681;CHR$ F(X,1); 480 LET F(X,5)=F(X,5)+F(X,4): IF F(X,5)>31 OR F(X,5)<-4 THEN LET F(X,4)=0 490 IF PEEK 23673<10 AND PEEK 23560=0 THEN NEXT X: GO TO 450 500 REM BOXING SIMULATION 510 POKE 23560,0: CLS 520 PRINT PAPER 6; INK 0;AT 2,7;TAB 24;AT 3,7;" LE BOXING MATCH ";AT 4,7;TAB 24; 530 PRINT AT 20,0; PAPER 4;TAB 5;"BOXING MATCH SIMULATION", 540 PRINT AT 12,4; INK 2;"▗";AT 12,27;"▖": FOR X=13 TO 16: PRINT AT X,4; INK 3;"▐";AT X,27;"▌": NEXT X: PRINT AT 17,4; PAPER 2;TAB 28; 550 FOR X=1 TO 8 560 PRINT #3;AT 11,13;CHR$ 25;#3;AT 11,16;CHR$ 27;: PAUSE 1+INT (RND*45) 570 FOR T=1 TO 10 580 PRINT #3;AT 11,13;CHR$ 26;#3;AT 11,15;CHR$ 28;: PAUSE 5: PRINT #3;AT 11,13;CHR$ 25;#3;AT 11,16;CHR$ 27;: PAUSE 5: NEXT T 590 IF X=1 THEN PRINT AT 21,0; PAPER 5; FLASH 1;TAB 9;"WELL ALMOST...", 600 IF PEEK 23560=0 THEN NEXT X: PAUSE 60 610 REM GO ON... 620 CLS : GO SUB 1000 630 FOR X=1 TO 50: PRINT #3;AT 10,15;CHR$ 33;#3;AT 10,15;CHR$ 14;: NEXT X 640 FOR X=1 TO 50: FOR Y=16 TO 14 STEP -2: PRINT #3;AT 10,15;CHR$ Y;: NEXT Y: NEXT X 650 LET cx=16: LET bx=6: LET S$="Gee Wiz, appearing from nowhere is tougher than I thought...": GO SUB 1020 660 LET I=0: FOR x=1 TO 10: PRINT #3;CHR$ 16;CHR$ I;AT 2,x*3-2;CHR$ x;: LET I=I+1: IF I=5 THEN LET I=0 670 NEXT X 680 LET s$="Sprites can be printed INVERSE, FLASH, BRIGHT, OVER and in any COLORS...": GO SUB 1020 690 LET s$="The sprite utility uses a normal PRINT #3 function so it should be fairly easy to learn.": GO SUB 1020 700 LET s$="A typical example is... PRINT #3;AT 10,10;CHR$ 1": GO SUB 1020 710 LET s$="This would print at position 10,10 the sprite no. 1": GO SUB 1020 720 LET s$="You can also limit the printing area by poking the maximum positions possible, lets see an example...": GO SUB 1020 730 PRINT AT 5,8; PAPER 5;TAB 24: FOR x=6 TO 13: PRINT AT x,8; PAPER 5;" ";AT x,23;" ": NEXT x: PRINT AT x,7; PAPER 4;TAB 25 740 POKE 65286,9: POKE 65292,23 750 FOR X=15 TO 5 STEP -1 760 PRINT #3;AT 10,X;CHR$ 16;: PAUSE 5: PRINT #3;AT 10,X;CHR$ 15;: PAUSE 5: NEXT X 770 LET i=2: FOR X=23 TO 5 STEP -1 780 PRINT #3;CHR$ 16;CHR$ i;AT 9,x;CHR$ 19;: PAUSE 5 790 PRINT #3;CHR$ 16;CHR$ i;AT 9,x;CHR$ 18;: PAUSE 5: LET i=i+1: IF i=6 THEN LET i=0 800 NEXT x 810 FOR X=23 TO 13 STEP -1 820 PRINT #3;AT 11,X;CHR$ 16;: PAUSE 5: PRINT #3;AT 11,X;CHR$ 15;: PAUSE 5: NEXT X: LET bx=7: LET by=15 830 LET s$="You can do the same for the top and the bottom...": GO SUB 1030 840 POKE 65298,6: POKE 65304,14 850 FOR x=1 TO 3: PRINT #3;AT 3+x,11;CHR$ 14;#3;AT 14-x,19;CHR$ 14;: PAUSE 10: NEXT x 860 FOR x=3 TO 0 STEP -1: PRINT #3;AT 3+x,11;CHR$ 14;#3;AT 14-x,19;CHR$ 14;AT 6+x,11;" ";AT 13-x,19;" ": PAUSE 10: NEXT x 870 POKE 65298,0: GO SUB 1000 880 PRINT #3;AT 255,15;CHR$ 17;#3;AT 10,15;CHR$ 14; 890 PAUSE 60: LET bx=6: LET by=17: LET s$="Oh! Oh! What's that on the ceiling?": GO SUB 1020: PAUSE 50 900 FOR x=0 TO 10: PRINT #3;AT x,15;CHR$ 17;: NEXT x 910 FOR x=6 TO 0 STEP -1: PRINT #3;CHR$ 16;CHR$ x;AT 5,15;CHR$ 13;: PAUSE 5: NEXT x 920 FOR x=1 TO 20: PRINT #3;CHR$ 21;CHR$ 1;AT 5,15;CHR$ 13;: PAUSE 10: NEXT x 930 FOR X=32 TO 18 STEP -1 940 PRINT #3;AT 10,X;CHR$ 16;: PAUSE 5: PRINT #3;AT 10,X;CHR$ 15;: PAUSE 5: NEXT X 950 PRINT #3;AT 10,18;CHR$ 14;: LET bx=6: LET by=20 960 LET i=6: LET S$="That was just a joke! I'm safe and sound back at BYTE POWER's headquarters... See you next month!": GO SUB 1020: POKE 23560,0 970 PRINT #3;CHR$ 16;CHR$ INT i;AT 5,15;CHR$ 13;: LET i=i-1: IF i=0 THEN LET i=6 980 IF PEEK 23560=0 THEN GO TO 970 990 STOP 1000 REM WINDOW 1010 FOR X=0 TO 14: PRINT AT x,0; PAPER 7; INK 0,,: NEXT X: PAPER 7: INK 0: RETURN 1020 REM SAY SOMETHING! 1030 POKE 65286,0: POKE 65292,32: POKE 65298,0: POKE 65304,22 1040 PRINT #3;CHR$ 21;CHR$ 1;AT BX-1,BY-1;CHR$ 11; 1050 PRINT #1;AT 1,0; INK 0; PAPER 8,, 1060 LET s$=" "+s$+" " 1070 FOR x=1 TO LEN s$-7: PRINT AT bx,by;s$(x TO x+6);: PRINT INK 8; PAPER 8;AT cx,cy;s$(x+7);: LET cy=cy+1: IF cy=32 THEN LET cy=0: LET cx=cx+1: IF cx>21 THEN LET cx=21: RANDOMIZE USR 65370: PRINT #1;AT 1,0; INK 0; PAPER 8,, 1080 PAUSE 5: NEXT X: LET CY=0: LET CX=CX+1: IF SCREEN$ (CX-1,CY)<>" " THEN LET CX=CX+1 1090 LET CY=0: IF CX>21 THEN LET CX=CX-1: RANDOMIZE USR 65370: PRINT #1;AT 1,0; INK 0; PAPER 8,,: GO TO 1090 1100 PRINT #3;CHR$ 21;CHR$ 1;AT BX-1,BY-1;CHR$ 11; 1110 PAUSE 30: RETURN 9998 CLEAR 62000: LOAD "DEMO CODE"CODE : RUN 9999 SAVE "DEMO" LINE 9998: SAVE "DEMO CODE"CODE 62414,2978 ```