Demolition

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

Demolition is a single-player arcade game in which the player launches a ball from the top of the screen to demolish a scrolling brick wall advancing from the bottom. The game supports five selectable difficulty levels, controlled by the variable `d`, which affects how many button presses are required before the wall advances. A high-score table stores the top ten entries across five difficulty levels using parallel string and numeric arrays (`s$` and `s`), with names entered via LINE input. User-defined graphics (UDGs A, B, and C) are used for the brick, ball-trail, and ball characters respectively, and a machine code routine is POKEd into low memory at startup (addresses 0–41) to perform collision detection, with its result read via `USR USR “d”`. The program uses `ATTR` reads at lines 380–390 to detect when the ball contacts the wall, awarding points by calculating the blast radius left and right of the impact column.


Program Analysis

Program Structure

The program is organized into clearly separated functional blocks:

  1. Lines 10–80: Initialization — sound, system setup, subroutine calls, and jump to title/difficulty screen.
  2. Lines 90–130: Game setup — screen colors, wall drawing, score display, and random ball start direction.
  3. Lines 140–200: Ball movement loop — bouncing the ball across the top row, waiting for a keypress to launch.
  4. Lines 210–270: Wall advance logic — incrementing the wall counter c and updating the wall display, then checking for collision via machine code.
  5. Lines 280–470: Ball drop and demolition — the ball falls from the top, hits the wall, and the blast radius is calculated left and right to award points.
  6. Lines 480–530: Game-over sequence — the wall reaches the player; an animated sweep plays across row 0.
  7. Lines 540–600: High-score table insertion — comparison and sorting into the parallel arrays.
  8. Lines 610–690: High-score display — scores shown interleaved by difficulty level.
  9. Lines 700–730: Wall-draw subroutine — prints five rows of UDG-A bricks in random ink colors on a black paper background.
  10. Lines 740–840: Title and difficulty selection screen.
  11. Lines 850–880: One-time initialization subroutine — POKEs machine code, dimensions score arrays, and sets default name strings.
  12. Lines 890–920: Machine code DATA.
  13. Line 930: CLEAR and SAVE with auto-run.

Machine Code Usage

Lines 850 and 890–920 load 42 bytes of machine code into addresses 0–41 via a READ/POKE loop. The routine is invoked at line 270 with the idiom USR USR "d". The inner USR "d" returns the address of UDG “D” in the UDG table, and the outer USR calls the machine code at that address. The return value is compared to 65535 (t<>65535) to detect a collision condition — when the wall has reached the ball’s row. The DATA at line 920 includes a Z80 loop: LD HL,nn, LD BC,nn, AND A, SBC HL,BC, JR NZ, LD BC,0FFFFh, RET — a countdown-style scan returning a sentinel value.

Note: the DATA lines use x as a variable in the DATA statements (lines 890–920), which at runtime holds the last value assigned to x in the ball-movement loop. This is an unusual technique — the machine code bytes that happen to coincide with x‘s current value are inserted dynamically rather than being fixed constants. This could cause subtle bugs if x has an unexpected value during initialization, since GO SUB 850 is called before the game loop sets x.

Difficulty and Wall Mechanics

The difficulty value d (1–5, where 1 is hardest) is selected at line 820. The counter b is incremented each time the player fires (line 190), and the wall only advances when b >= d (line 210). A lower d means the wall advances more frequently. The wall position counter c runs from 0 to 9, wrapping back to 0 at 10 (line 220). When c=5, the wall string w$ is set to a full row of UDG-A bricks; when c=0, it is reset to a color-control-character sequence (line 240), producing a new randomly colored wall row.

Ball Movement and ATTR-Based Collision

The ball bounces horizontally across row 0 using dx (+1 or -1) and reflects at the screen edges (line 170). On a keypress, the ball drops vertically in a loop (lines 300–350), checking ATTR(y,x) at each row. The attribute value a=48 (PAPER 6, INK 0 — the wall’s attribute) is used as the wall-detection sentinel. When the ball lands on the wall, the blast radius is expanded left (lx) and right (rx) simultaneously (lines 380–470), awarding 5 points per brick destroyed and playing a tone pitched by distance from center.

High Score Table

The score table uses two parallel arrays: s$(10,10) for names (10 entries of up to 10 characters) and s(10) for numeric scores. Odd indices (1,3,5,7,9) store the top score for each of the five difficulty levels; even indices store the second score per level. Insertion at lines 590–600 performs a simple two-slot comparison. The display at lines 630–680 uses two interleaved FOR loops with OVER 1 printing to overlay the level labels printed earlier.

Key BASIC Idioms

  • LET dx=(r>5)-(r<=5) — Boolean arithmetic to set direction: evaluates to +1 or -1 depending on the random value of r.
  • POKE 23692,255 — Disables the scroll prompt by setting the system variable SCRCT to 255, preventing “scroll?” interruptions during wall updates.
  • POKE 23658,8 / POKE 23658,0 — Enables and disables CAPS LOCK for name entry.
  • LET lx=lx-(lx>0) and LET rx=rx+(rx<31) — Boundary-clamped left/right expansion using Boolean expressions as increments.
  • PRINT AT PI,0 at line 650 — PI truncates to 3, so this positions the cursor at row 3. An unconventional but valid use of a floating-point constant as a row index.
  • LINE w$ in the INPUT at line 560 — accepts the entire line including spaces without requiring quotes.

UDG Usage

UDGRole
[UDG-A]Brick tile, used to fill the wall rows
[UDG-B]Ball-trail or sweep graphic used in the game-over animation
[UDG-C]Ball character, displayed moving along row 0 and dropping
[UDG-D]Not displayed; its USR address is used to call the machine code routine

Bugs and Anomalies

  • The DATA statements at lines 890–920 use the variable x for several byte values. Since GO SUB 850 is called at line 30 before x is ever assigned in the game loop, x will be 0 at that point, so those bytes are written as 0. This is likely intentional — 0 is a valid Z80 NOP or padding byte in those positions — but it is fragile and not self-documenting.
  • At line 220, when c>=10, it is set to b, which was just reset to 0. This is equivalent to LET c=0 but is an indirect and potentially confusing assignment.
  • Line 570 checks LEN w$ > 10 and displays an error at row 7, column 4. However, the INPUT at line 560 uses LINE input which can accept any length; the error message instructs the user to re-enter, but the GO TO 560 loops back to the full INPUT prompt correctly.

Image Gallery

Source Code

  10 BEEP .5,10
  20 POKE 23658,0
  30 GO SUB 850
  40 PAPER 7:INK 0:BORDER 7:CLS 
  50 PRINT #1; AT 0,0; TAB 8;"PRESS ANY KEY",
  60 PAUSE 0
  70 LET s=0:LET b=s:LET c=-1:LET a=48
  80 GO TO 740
  90 INK 0:PAPER 6:BORDER 7:CLS 
 100 GO SUB 700
 110 PRINT #1; AT 0,0;"SCORE:0","HIGH SCORE:";s(d*2-1)
 120 LET r= INT (RND*10+1)
 130 LET dx=(r>5)-(r <=5):LET x=31*(r <=5)
 140 LET x1=x
 150 LET k$= INKEY$
 160 LET x=x+dx
 170 IF x>31 OR x<0 THEN LET dx=-dx:GO TO 160
 180 PRINT AT 0,x;"[UDG-C]"; AT 0,x1;" "
 190 IF k$ <>"" THEN LET b=b+1:GO TO 210
 200 GO TO 140
 210 IF b<d THEN GO TO 280
 220 LET b=0:LET c=c+1:IF c >=10 THEN LET c=b
 230 IF c=5 THEN LET w$="[UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A]"
 240 IF c=0 THEN LET w$= CHR$ 17+ CHR$ 6+ CHR$ 16+ CHR$ 0+ CHR$ 6+ CHR$ 6
 250 POKE 23692,255:PRINT #1; AT 0,0,,#2; AT 21,0' PAPER 0; INK INT (RND*6+1);w$;#1; AT 0,0;"SCORE:";s,"HIGH SCORE:";s(d*2-1)
 260 PRINT AT 0,x;"[UDG-C]"
 270 LET t= USR USR "d":IF t <>65535 THEN GO TO 480
 280 BEEP .03,12
 290 LET y=0
 300 LET y1=y
 310 LET y=y+1
 320 IF y>21 THEN PRINT AT y1,x;" ":GO TO 120
 330 IF ATTR (y,x) <>a THEN GO TO 360
 340 PRINT AT y,x;"[UDG-C]"; AT y1,x;" "
 350 GO TO 300
 360 PRINT AT y1,x;" "
 370 LET lx=x:LET rx=x
 380 LET l= ATTR (y,lx)
 390 LET r= ATTR (y,rx)
 400 IF l=a AND r=a THEN GO TO 120
 410 IF l <>a AND lx <>rx THEN LET s=s+5:BEEP .01,10+x-lx
 420 IF r <>a THEN LET s=s+5:BEEP .01,10+rx-x
 430 PRINT #1; AT 0,6;s
 440 PRINT AT y,lx;" "; AT y,rx;" "
 450 LET lx=lx-(lx>0):LET rx=rx+(rx<31):LET y=y-1
 460 IF y<0 THEN GO TO 120
 470 GO TO 380
 480 FOR i=0 TO 31-t
 490 PRINT INK 0; PAPER 7; AT 0,i; OVER 0;"[UDG-B]"
 500 BEEP .05,31-i
 510 NEXT i
 520 PRINT AT 0,i; OVER 1; INK 8;" "
 530 LET x=d*2:LET y=x-1
 540 IF s<s(x) THEN GO TO 610
 550 POKE 23658,8
 560 INPUT PAPER 7; INK 0; AT 0,0;"You made it to the SCORE TABLE","ENTER NAME: ?                   ", AT 1,12; LINE w$
 570 IF LEN w$>10 THEN PRINT AT 7,4;"ONLY 10 Letters PLEASE!":GO TO 560 
 580 POKE 23658,0
 590 IF s >=s(y) THEN LET s$(x)=s$(y):LET s(x)=s(y):LET s$(y)=w$:LET s(y)=s:GO TO 610
 600 IF s >=s(x) THEN LET s$(x)=w$:LET s(x)=s
 610 CLS 
 620 PRINT AT 1,10;"HIGH SCORES:"
 630 PRINT AT 4,0;
 640 FOR i=1 TO 5:PRINT TAB 5;"LEVEL ";i''':NEXT i
 650 PRINT AT PI,0
 660 FOR i=1 TO 10 STEP 2:PRINT OVER 1; TAB 14; BRIGHT 1;s$(i); TAB 25;s(i)''':NEXT i
 670 PRINT AT 4,0
 680 FOR i=2 TO 10 STEP 2:PRINT OVER 1; TAB 14;s$(i); TAB 25;s(i)''':NEXT i
 690 GO TO 50
 700 LET w$="[UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A][UDG-A]"
 710 FOR i=17 TO 21
 720 PRINT PAPER 0; INK INT (RND*6+1); AT i,0;w$
 730 NEXT i:RETURN 
 740 PAPER 7:INK 0:BORDER 7:CLS 
 750 PRINT AT 1,11; INK 0; PAPER 7;"DEMOLITION"
 760 PRINT ''"  Try to gain points as you"''" DEMOLISH the advancing wall"''"    before it reaches you."
 770 PRINT '''"     To launch the ball-"
 780 PRINT '"      PRESS the SPACEBAR"
 790 PRINT #1; AT 0,0; INK 0; PAPER 6;"SELECT DIFFICULTY ? (1-5):1=HARD"
 800 LET w$= INKEY$
 810 IF w$<"1" OR w$>"5" THEN GO TO 790
 820 LET d= VAL w$(1)
 830 PRINT #1; AT 0,0; INK 0; PAPER 6;"  PRESS ANY KEY TO START GAME   ":PAUSE 0
 840 GO TO 90
 850 RESTORE 890:FOR i=0 TO 41:READ x:POKE i,x:NEXT i
 860 DIM s$(10,10):DIM s(10)
 870 FOR i=1 TO 10:LET s$(i)="..........":NEXT i
 880 RETURN 
 890 DATA 0,126,x,x,x,x,x,0
 900 DATA 96,24,06,255,x,06,24,96
 910 DATA 24,36,24,60,126,x,60,24
 920 DATA 33,0,65,1,32,0,126,254,126,200,35,13,32,-8,1,255,x,201
 930 CLEAR :SAVE "Demolition" LINE 10:BEEP .2,15

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