Spinning Wheel

Developer(s): Charles Eric Goyette
Date: 1987
Type: Program
Platform(s): TS 2068
Tags: Demo

Spinning Wheel renders an animated rotating wheel graphic by combining TS2068 BASIC drawing routines with a Z80 machine code routine loaded into RAM at address 30000. The machine code block (47 bytes) performs fast memory copies between the display file area and a buffer at address 30720 (0x7800), enabling smooth frame updates. The BASIC side draws concentric circles and radial spokes using CIRCLE and PLOT/DRAW, with a phase variable `q` incrementing each frame to simulate rotation. The program loops continuously through line 60, calling the machine code via USR to swap display buffers, creating a double-buffering effect.


Program Structure

The program is divided into three logical phases:

  1. Initialization (lines 20–50): Clears memory to address 29999 and POKEs 47 bytes of Z80 machine code into addresses 30000–30046, then jumps to the main setup loop at line 140.
  2. Animation loop (lines 60–240): Calls machine code at USR 30000 to copy the display buffer, redraws the wheel with an incremented phase variable q, and loops back to line 60 indefinitely.
  3. Save line (line 250): Saves the program with auto-run from line 1 (which does not exist, so execution falls through to line 10/20).

Machine Code Routine

The 47 bytes loaded at address 30000 implement two entry points:

  • Entry at 30000 (called via USR 30000 in line 60): Copies 6144 bytes from address 16384 (0x4000, the display file) to address 30720 (0x7800), preserving the current frame.
  • Entry at 30012 (called via USR 30012 in line 120): Copies 6144 bytes back from 30720 to 16384, restoring the saved frame. This implements a basic double-buffer scheme.

The copy is performed using the Z80 LDIR instruction (opcode ED B0), which is the standard block-move idiom on Z80 systems. The loop within the machine code (bytes at offsets 31–46) includes a delay loop using DJNZ and decrements, likely timed to synchronize with the display or slow the animation.

Display Buffer Address Calculation

Line 70 uses an unusual indirect PEEK pattern to read the destination address from within the machine code itself:

LET r= VAL "PEEK 30004+256* PEEK 30005+6144"

This reads the 16-bit address stored at bytes 30004–30005 (the DE register pair loaded by the second LD DE,nn instruction) and adds 6144 to advance it by one screen-file worth of bytes. Lines 80–90 then POKE the updated address back into the machine code, effectively walking the destination pointer forward each frame.

Wheel Drawing Technique

The wheel is constructed in two parts each frame:

  • Rim: Five concentric CIRCLE calls (radii 80–84) plus an inner hub circle at radius 10, all centered at pixel coordinates (126, 85).
  • Spokes: A FOR loop (line 170) iterates t from q to 95+q in steps of 5, computing spoke endpoints with SIN/COS at angle t/50*PI radians, then drawing each spoke with PLOT 126,85: DRAW sx-126, sy-85.

The phase variable q is incremented by 1 each pass through the loop (line 230), which shifts all spoke angles by a small amount, producing the spinning illusion over successive frames.

Key BASIC Idioms

  • VAL "PEEK 30004+256* PEEK 30005+6144" — evaluates an expression stored as a string, a memory-saving technique that also allows dynamic expression construction.
  • USR 30000 and USR 30012 — calls machine code entry points and returns the BC register value, assigned to the dummy variable l (the return value is not used).
  • The NEXT f at line 100 closes the FOR f loop opened at line 160, with the loop body spanning lines 170–230 and the unconditional branch at line 240 back to line 60 bypassing the NEXT until the FOR loop naturally terminates — though in practice GO TO 60 keeps re-entering the FOR at line 160 via the jump, making f effectively unused as a counter.

Anomalies and Notes

  • Line 130 (STOP) and line 110 (CLS) are present but never reached during normal animation, as the loop at line 240 never terminates naturally.
  • The FOR f=1 TO 5 at line 150 and line 160 both open loops for variable f; the second FOR f at line 160 overwrites the first, so the loop at line 150 is effectively dead code.
  • The destination address manipulation in lines 70–90 walks the machine code’s destination pointer by 6144 bytes each frame; over many iterations this pointer will overflow 16-bit address space unless the machine code itself resets it, which the delay/reset logic in the latter portion of the routine (bytes 31–46, including the DEC A / JR NZ sequence) appears to handle.

Image Gallery

Source Code

  10 REM \{20}\{1}>>  Spinning Wheel  <<\{20}\{0}                                 by Charles Goyette                        \* Time Designs  J/F 87 
  20 CLEAR 29999
  30 FOR a=30000 TO 30046:READ q:POKE a,q:NEXT a
  40 DATA 33,0,64,17,0,120,1,0,24,237,176,201,1,0,40,197,62,5,33,0,120,0,17,0,64,1,0,24,237,176,6,20,14,220,13,32,253,16,249,61,32,235,0,193,16,225,201
  50 GO TO 140
  60 LET l= USR 30000
  70 LET r= VAL "PEEK 30004+256* PEEK 30005+6144"
  80 POKE 30005, INT (r/256)
  90 POKE 30004,r-256* PEEK 30005
 100 NEXT f
 110 CLS 
 120 LET l= USR 30012
 130 STOP 
 140 LET q=0
 150 FOR f=1 TO 5:CLS :FOR a=80 TO 84:CIRCLE 126,85,a:NEXT a:CIRCLE 126,85,10
 160 FOR f=1 TO 5:CLS :CIRCLE 126,85,80:CIRCLE 126,85,81:CIRCLE 126,85,82:CIRCLE 126,85,10
 170 FOR t=0+q TO 95+q STEP 5
 180 LET a=t/50* PI
 190 LET sx=126+79* SIN a
 200 LET sy=85+79* COS a
 210 PLOT 126,85:DRAW sx-126,sy-85
 220 NEXT t
 230 LET q=q+1
 240 GO TO 60
 250 SAVE "Wheel" LINE 1

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