Four Stroke Engine is an animated educational demonstration that illustrates the four stages of an internal combustion engine — induction, compression, ignition, and exhaust — using user-defined graphics (UDGs) to render a cylinder, piston, valves, and spark plug. Eleven UDGs are defined at runtime by reading DATA values and POKEing them into memory via USR. The animation loop cycles through the four strokes continuously, with the piston position controlled by a FOR-NEXT loop iterating rows 9 to 15 and back, and a PAUSE 15 delay between frames to regulate speed. A pause routine triggered by the “P” key allows the user to freeze the animation, continue with “C”, or quit with “S”. Labels for the engine components (spark plug, inlet manifold, exhaust manifold, cylinder, piston) are printed as static text around the animated diagram.
Program Structure
The program is organized into clearly delineated sections, each preceded by a REM comment using decorative block-graphic borders. The main flow is:
- Lines 10–60: Title screen with flashing text, then call UDG setup subroutine.
- Lines 70–180: Draw the static cylinder outline using PLOT/DRAW, print static labels (via subroutine at 540), and display the control hint bar.
- Lines 190–470: Main animation loop cycling through four strokes — Induction (200–250), Compression (270–310), Ignition (330–380), Exhaust (400–460) — then GO TO 190 to repeat.
- Lines 480–530: Pause/stop subroutine.
- Lines 540–610: Static label printing subroutine.
- Lines 615–830: UDG definition subroutine with DATA.
- Line 840: SAVE with autorun.
User-Defined Graphics
Eleven UDGs are defined by the subroutine starting at line 620. The character string a$ at line 630 lists the UDG characters in the order their DATA rows appear: \\e\\f\\g\\h\\i\\j form the piston body (top-left, top-middle×3, top-right, bottom-left, bottom-middle×3, bottom-right), \\a\\b are the connecting rod or valve graphics, \\k is the spark flash, \\d is the intake/exhaust flow indicator, and \\c is a valve graphic. DATA is read sequentially (8 bytes per UDG) and POKEd via USR b$, which resolves to the base address of that UDG’s bitmap in the system font area.
Notably, many DATA lines contain c as a literal value (lines 740–830). Within the DATA context this is the numeric variable c, which is uninitialized and defaults to 0 in Sinclair BASIC. This is an intentional technique to embed zero bytes without typing “0” repeatedly — it reads as valid BASIC and evaluates to 0 at runtime.
Animation Technique
Each of the four stroke phases uses a FOR-NEXT loop on variable n ranging from 9 to 15 (piston descending) or 15 to 9 STEP -1 (piston ascending). On each iteration, the previous piston row is erased with spaces and the piston UDG block is reprinted at the new row, creating smooth vertical movement within the cylinder. PAUSE 15 (roughly half a second) paces the animation; the hint line tells the user any keypress speeds it up (via INKEY$ shortcutting the PAUSE).
Cylinder Drawing with PLOT/DRAW
The cylinder walls are drawn using pixel-level PLOT and DRAW commands rather than character graphics, giving a precise outline independent of the character grid. The left wall is drawn from (96,130) and the right from (152,130), with short horizontal segments representing valve apertures. The values NOT PI evaluate to 0 (since PI is non-zero, NOT PI = 0 in Sinclair BASIC), so DRAW NOT PI, -84 draws a vertical line of 84 pixels — a common idiom for embedding zero without the digit.
Key BASIC Idioms
NOT PIevaluates to 0; used extensively in DRAW, PRINT AT column, and INK arguments as a compact zero literal.SGN PIevaluates to 1 (since PI > 0); used inBRIGHT SGN PIandINK SGN PIto set attributes to 1 without the literal digit.POKE 23658,8at line 30 enables CAPS LOCK (sets the caps-shift flag), ensuring consistent keyboard input behavior.RESTORE 620at line 620 resets the DATA pointer to line 620 — since there is no DATA before line 730, this effectively resets to the first DATA line. It ensures the UDG routine works correctly if called after any prior READ.
Pause and Stop Subroutine
The pause subroutine (lines 480–530) uses PAUSE 0 to wait indefinitely for a keypress, then checks INKEY$ for “C” (continue) or “S” (stop). The loop at line 530 (GO TO 490) re-enters the pause and re-issues PAUSE 0 if neither key is pressed, preventing spurious returns. The “P” key check in the main animation loops (lines 240, 440) is a simple IF INKEY$="P" THEN GO SUB 480; it is only polled once per animation frame rather than continuously.
Stroke Phase Details
| Stroke | Lines | Piston Direction | Valve/Effect |
|---|---|---|---|
| Induction | 200–250 | Down (9→15) | Inlet valve open (\\c), fuel symbol (\\d\\d) |
| Compression | 270–310 | Up (15→9) | Valve closed, label “Compression” |
| Ignition | 330–380 | Down (9→15) | Spark UDG (\\k) with FLASH 1 |
| Exhaust | 400–450 | Up (15→9) | Exhaust valve indicator (\\d\\d), label “Exhaust” |
Potential Anomalies
- Line 740 contains
DATA 255,0,255,0,255,0,c,c— the trailingcvalues rely on the variablecbeing 0 (from the POKE loop at line 700 wherecis the READ target, last assigned the final byte of the previous UDG). This is fragile but works in practice because after the first UDG is fully read,cholds the last byte (128 from line 730), not 0. However the DATA for UDG \\f (line 740) ends with twocbytes which would be 128, not 0 — this may result in slightly incorrect bitmaps for those UDGs, giving unexpected pixel rows, though the visual effect may be minor or intentional. - The “PRESS P TO PAUSE / ANY KEY TO SPEED UP” hint at line 180 is only 64 characters and wraps onto two screen rows as intended, but the P-key check is absent in the Compression and Ignition loops (lines 280–310, 340–380), meaning pausing is only possible during Induction and Exhaust strokes.
Source Code
10 REM ZX-Appeal June/88, p.15
20 \{20}\{1} REM \{20}\{0}<>four stroke engine<>
30 BORDER 1:PAPER 6:INK 2:CLS :POKE 23658,8
40 PRINT FLASH 1; PAPER 7; AT 7,14; INK 3;"FOUR"; AT 9,13; INK 1;"STROKE"; AT 11,13; INK 4;"ENGINE"
60 GO SUB 620
70 \{20}\{1} REM \{20}\{0}<><>build cylinder<><>
80 BORDER PI:PAPER 7:CLS
90 PRINT BRIGHT SGN PI; AT NOT PI,7; INK SGN PI;"FOUR STROKE ENGINE"
100 PLOT 96,130
110 DRAW 10, NOT PI:DRAW NOT PI,-10:DRAW -3, NOT PI:DRAW NOT PI,-84
120 PLOT 96,134
130 DRAW 14, NOT PI:DRAW NOT PI,-14:DRAW 28, NOT PI:DRAW NOT PI,14:DRAW 14,0
140 PLOT 152,130
150 DRAW -10, NOT PI:DRAW NOT PI,-10:DRAW 2, NOT PI:DRAW NOT PI,-84
160 PRINT INK NOT PI; AT 6,15;"\a"; AT 7,15;"\b"
170 GO SUB 540
180 PRINT AT 20, NOT PI; INK 1; PAPER 6;"********PRESS P TO PAUSE**************ANY KEY TO SPEED UP*******"
190 \{20}\{1} REM \{20}\{0} <><><>induction<><><>
200 PRINT AT 7,17; INK NOT PI;"\c"; INK 7; PAPER 1; AT 9,1;"Induction"; AT 5,10; INK 2; PAPER 7;"\d\d"; AT 5,19;" "
210 FOR n=9 TO 15
220 PRINT INK 1; PAPER 7; AT n-1,13;" "; AT n,13; PAPER 6;"\e\f\f\f\g"; AT n+1,13; PAPER 6;"\h\i\i\i\j"
230 PAUSE 15
240 IF INKEY$="P" THEN GO SUB 480
250 NEXT n
260 \{20}\{1} REM \{20}\{0}<><><>compression<><><>
270 PRINT AT 7,13; INK 0;"\c"; AT 9,1; INK 7; PAPER 1;"Compression"; AT 5,10; PAPER 7;" "
280 FOR n=15 TO 9 STEP -1
290 PRINT INK 1; AT n+2,13; PAPER 7;" "; AT n,13; PAPER 6;"\e\f\f\f\g"; AT n+1,13; PAPER 6;"\h\i\i\i\j"
300 PAUSE 15
310 NEXT n
320 \{20}\{1} REM \{20}\{0} <><><>ignition<><><>
330 PRINT AT 9,1; INK 7; PAPER 1;"Ignition "
340 FOR n=9 TO 15
350 PRINT AT 8,15; FLASH 1; INK 2; PAPER 6;"\k"
360 PRINT INK 1; AT n-1,13; PAPER 7;" "; AT n,13; PAPER 6;"\e\f\f\f\g"; AT n+1,13; PAPER 6;"\h\i\i\i\j"
370 PAUSE 15
380 NEXT n
390 \{20}\{1} REM \{20}\{0} <><><>exhaust<><><>
400 PRINT PAPER 7; AT 7,17;" "; AT 8,15;" "; AT 9,1; INK 7; PAPER 1;"Exhaust "; AT 5,19; INK 2; PAPER 7;"\d\d"
410 FOR n=15 TO 9 STEP -1
420 PRINT INK 1; AT n+2,13; PAPER 7;" "; AT n,13; PAPER 6;"\e\f\f\f\g"; AT n+1,13; PAPER 6;"\h\i\i\i\j"
430 PAUSE 15
440 IF INKEY$="P" THEN GO SUB 480
450 NEXT n
460 PRINT AT 7,13; INK 0;" "
470 GO TO 190
480 \{20}\{1} REM \{20}\{0} <><>pause routine<><>
490 PRINT AT 21,0; INK 1; PAPER 6;"*PRESS C TO CONTINUE S TO STOP**"
500 PAUSE 0
510 IF INKEY$="C" THEN PRINT AT 21,0; INK 1; PAPER 6;"********PRESS P TO PAUSE********":RETURN
520 IF INKEY$="S" THEN PRINT AT 21,0; INK 1; PAPER 6;"****HAPPY MOTORING - GOODBYE****":STOP
530 GO TO 490
540 \{20}\{1} REM \{20}\{0}<><><><>labels<><><><>
550 PRINT AT 3,13; INK 0;"spark"
560 PRINT AT 4,1; INK 0;"inlet plug exhaust"
570 PRINT AT 5,1; INK 0;"manifold"
580 PRINT AT 5,22; INK 0;"manifold"
590 PRINT AT 9,19; INK 0;"cylinder"
600 PRINT AT 19,13; INK 0;"piston"
610 RETURN
615 \{20}\{1} REM \{20}\{0}<>user defined graphics
620 RESTORE 620
630 LET a$="\e\f\g\h\i\j\a\b\k\d\c"
640 LET n=0
650 LET n=n+1
660 IF n>11 THEN RETURN
670 LET b$=a$(n)
680 FOR m=0 TO 7
690 READ c
700 POKE USR b$+m,c
710 NEXT m
720 GO TO 650
730 DATA 255,128,255,128,255,128,128,128
740 DATA 255,0,255,0,255,0,c,c
750 DATA 255,1,255,1,255,0,1,c
760 DATA 128,c,c,192,224,176,152,255
770 DATA 0,c,c,c,c,c,c,255
780 DATA 1,c,c,3,7,13,25,255
790 DATA 16,c,56,c,c,124,c,255
800 DATA 56,c,c,40,c,32,56,0
810 DATA 128,200,108,54,11,1,0,c
820 DATA 24,12,254,255,254,12,24,0
830 DATA 60,c,0,c,c,c,c,c
840 SAVE "engine" LINE 10
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
