This program displays an animated bulletin board splash screen and then scrolls two independent ticker-tape text messages across the screen. It uses PEEK of system variables at addresses 16396–16397 to find the base of the display file, then POKEs display-file bytes directly via a packed decimal offset string (M$) to selectively blank or set pixels for a graphical effect. The border is drawn using block-graphic characters (▛, ▜, ▙, ▟, etc.) and two framed windows scroll strings A$ and B$ with 28-character viewports. Playback controls allow the user to pause (P), scroll backward (B), or resume forward (F) independently for each ticker window.
Program Analysis
Program Structure
The program is divided into several functional blocks:
- Lines 1000–1200: Initialise the display — clear screen by printing
S$24 times, draw a solid block-graphic border around the full 32×24 screen. - Lines 1210–1270: POKE selected bytes in the display file to 0 (clear) and then to 128 (set top pixel row) using offsets packed in string
M$, then pause for effect. - Lines 1280–1350: Animate a “wipe” effect collapsing rows inward from top and bottom, then redraw the border with half-block graphics to create a 3-D-style frame.
- Lines 1400–1430: Print a centred title panel (“THE BULLETIN BOARD FOR SEPTEMBER 1983”) and two subsidiary framed windows.
- Lines 1500–1580: Main scroll loop — independently scroll strings
A$andB$through 28-character viewports at rows 4 and 19 respectively, calling subroutines 2000 and 2500 for keypress handling. Loops back at line 1580. - Lines 2000–2160 / 2500–2660: Two parallel keypress-handler subroutines, one per ticker window, supporting pause (P), backward scroll (B), and resume-forward (F).
- Lines 9998–9999: SAVE with auto-run flag, then restart.
Display File Manipulation
Lines 1000–1010 read the display file base address from system variables 16396 and 16397 (the standard ZX81/TS1000 D-FILE pointer) and store it in X. POKE 16418,0 disables the cursor. The string M$ encodes a sequence of three-digit decimal offsets; lines 1210–1260 step through it in strides of 3, extracting each offset with VAL M$(N TO N+2) and POKEing X + offset first to 0 then to 128. This directly manipulates pixel rows in the display file to produce a custom graphic pattern without any PLOT commands.
Scrolling Ticker Technique
The scrolling is achieved by sliding a 28-character window through a long string. For forward scroll, line 1510 prints A$(N TO N+27) at row 4, column 2, incrementing N each iteration. This is a classic Sinclair BASIC substring-window idiom. The loop bounds (LEN A$-28) ensure the window never overruns the string.
The backward-scroll subroutines (lines 2100–2160 and 2600–2660) reverse the window by decrementing from the current N back to 28, checking INKEY$ on each frame for an “F” (forward) keypress to break out, or “P” to pause. LET N=I on exit resynchronises the main loop counter to wherever the reverse scroll stopped.
Key BASIC Idioms
VAL M$(N TO N+2)— converts a three-character decimal substring to a number, used as a memory-efficient way to store a table of offsets in a string variable rather than a DATA block.GOSUB 2000/GOSUB 2500called inside FOR–NEXT loops for per-frame event polling without blocking.- Immediate
RETURNwhenINKEY$=""(line 2010, 2510) keeps the scroll running at full speed when no key is held. PAUSE 40000for a long pause on “P” — effectively indefinite on a 50 Hz machine (~800 seconds).
Block-Graphic Border Construction
The border is built in four passes (lines 1060–1200) using the \## escape, which represents the solid block character (█). A second, more elaborate border at lines 1310–1350 uses paired half-block characters (\:', \':, \:., \.:, etc.) to produce a shadowed or bevelled frame appearance using standard ZX character-set block graphics (chars 128–143).
Notable Techniques
- Two completely independent ticker channels (A$ top, B$ bottom) with separate scroll positions and separate backward-scroll subroutines — unusual for a single-tasking BASIC program.
- The main loop at lines 1500–1580 interleaves both scrollers: A$ advances one step and calls its handler, then B$ advances one step and calls its handler, giving approximate simultaneity.
- The auto-run SAVE at line 9998 uses an inverse-video character in the filename (“BULLETI%N”), causing the tape loader to jump to line 1000 automatically on load.
Anomalies and Observations
- Line 1060 is numbered 1060 after 1030/1040, skipping 1050 — and similarly 1100, 1140, 1180 skip intermediates. This is cosmetic line-number spacing with no functional effect.
- The backward-scroll loop (line 2100) runs
FOR I=N TO 28 STEP -1. IfNis already less than 28 (early in the string), the loop body never executes and the subroutine falls through toLET N=IwithIholding whatever value it had from the outer loop — potentially a logic edge case ifA$is short. - There is no
INKEY$debounce between the two subroutine calls within the main loop, so a held key will trigger both handlers on the same frame.
Content
Source Code
1000 LET X=PEEK 16396+256*PEEK 16397
\n1010 POKE 16418,0
\n1020 FOR N=1 TO 24
\n1030 PRINT S$
\n1040 NEXT N
\n1060 FOR N=0 TO 31
\n1070 PRINT AT 0,N;"\##"
\n1080 NEXT N
\n1100 FOR N=1 TO 22
\n1110 PRINT AT N,31;"\##"
\n1120 NEXT N
\n1140 FOR N=31 TO 0 STEP -1
\n1150 PRINT AT 23,N;"\##"
\n1160 NEXT N
\n1180 FOR N=22 TO 1 STEP -1
\n1190 PRINT AT N,0;"\##"
\n1200 NEXT N
\n1210 FOR N=1 TO LEN M$-2 STEP 3
\n1220 POKE X+VAL M$(N TO N+2),0
\n1230 NEXT N
\n1240 FOR N=LEN M$-2 TO 1 STEP -3
\n1250 POKE X+VAL M$(N TO N+2),128
\n1260 NEXT N
\n1270 PAUSE 200
\n1280 FOR N=0 TO 11
\n1290 PRINT AT N,0;S$;AT 23-N,0;S$
\n1300 NEXT N
\n1310 PRINT AT 0,0;"\:'\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\':"
\n1320 FOR N=1 TO 22
\n1330 PRINT AT N,31;"\ :";AT N,0;"\: "
\n1340 NEXT N
\n1350 PRINT AT 23,0;"\:.\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.:"
\n1400 PRINT AT 9,5;"\:'\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\':";AT 10,5;"\: THE BULLETIN \ :";AT 11,5;"\: BOARD FOR \ :";AT 12,5;"\: SEPTEMBER 1983 \ :";AT 13,5;"\:.\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.:"
\n1410 PAUSE 200
\n1420 PRINT AT 3,1;"\:'\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\':";AT 4,1;"\: ";AT 4,30;"\ :";AT 5,1;"\:.\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.:"
\n1430 PRINT AT 18,1;"\:'\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\''\':";AT 19,1;"\: ";AT 19,30;"\ :";AT 20,1;"\:.\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\.:"
\n1500 FOR N=1 TO LEN A$-28
\n1510 PRINT AT 4,2;A$(N TO N+27)
\n1520 GOSUB 2000
\n1530 NEXT N
\n1540 FOR N=1 TO LEN B$-28
\n1550 PRINT AT 19,2;B$(N TO N+27)
\n1560 GOSUB 2500
\n1570 NEXT N
\n1580 GOTO 1500
\n2000 LET I$=INKEY$
\n2010 IF I$="" THEN RETURN
\n2020 IF I$="P" THEN PAUSE 40000
\n2030 IF I$="B" THEN GOTO 2100
\n2040 RETURN
\n2100 FOR I=N TO 28 STEP -1
\n2110 PRINT AT 4,2;A$(I TO I+27)
\n2120 IF INKEY$="F" THEN GOTO 2150
\n2130 IF INKEY$="P" THEN PAUSE 40000
\n2140 NEXT I
\n2150 LET N=I
\n2160 RETURN
\n2500 LET I$=INKEY$
\n2510 IF I$="" THEN RETURN
\n2520 IF I$="P" THEN PAUSE 40000
\n2530 IF I$="B" THEN GOTO 2600
\n2540 RETURN
\n2600 FOR I=N TO 28 STEP -1
\n2610 PRINT AT 19,2;B$(I TO I+27)
\n2620 IF INKEY$="F" THEN GOTO 2650
\n2630 IF INKEY$="P" THEN PAUSE 40000
\n2640 NEXT I
\n2650 LET N=I
\n2660 RETURN
\n9998 SAVE "BULLETI%N"
\n9999 GOTO 1000
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
