EASYsprite DEMO

Developer(s): Eric Boisvert
Date: 1987
Type: Program
Platform(s): TS 2068

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:

SequenceMeaning
CHR$ 16; CHR$ nSet ink/color index to n
AT row, colSet sprite position
CHR$ n (1–33+)Select and draw sprite number n
CHR$ 14Draw standing/default host sprite
CHR$ 15 / CHR$ 16Animation frames (walk/erase cycle)
CHR$ 17Place sprite at off-screen/ceiling row 255
CHR$ 21; CHR$ 1Speech bubble sprite
CHR$ 11Clear/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:

AddressRole
65286Left boundary (column)
65292Right boundary (column)
65298Top boundary (row)
65304Bottom 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 RUNs 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.

Content

Appears On

Tape-based magazine.

Related Products

Related Articles

This program will give a general idea of what sprites are and how they can be used. Loading name DEMOProgram...

Related Content

Image Gallery

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

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

Scroll to Top