BELLTOWER

This file is part of and CATS Library Tape 6. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Arcade, Game

“The Haunted Belltower” is a multi-room platform game in which the player guides a rabbit character through five rooms — entrance hall, wine cellar, staircase, bats’ bedroom, and haunted belfry — collecting bells while avoiding bats. The game uses 15 user-defined graphics (UDGs “a” through “o”) whose bitmaps are loaded via DATA statements and POKEd into UDG memory starting at USR “a”, with a guard at address 63000 to avoid reloading them unnecessarily. Two enemy bats move toward the player using attribute-based collision detection via the ATTR function, and a bouncing ball death sequence plays when the player is caught. Room layouts, platform positions, bell locations, and rope data are all encoded in DATA blocks at lines 8100–8503, with each room’s data selected by RESTOREing to offset 8000+100*z.


Program Analysis

Program Structure

The program is organized into clearly separated functional blocks, entered via GO TO 6000 at line 15. The main flow is:

  1. Initialization (6000–6004): Sets up strings, UDGs, room names, then chains to key setup, instructions, and the title screen.
  2. Title screen / attract mode (9000–9175): Draws a detailed PLOT/DRAW illustration of the belltower and loops music from addresses 63000–63063 until a key is pressed.
  3. Instructions (9200–9220): Multi-page text screens with a keypress-to-continue subroutine at 9400.
  4. Key definition (9800–9859): Prompts for three user-defined keys (LEFT, RIGHT, CLIMB ROPE) stored in string m$.
  5. Room setup (8000–8509): RESTOREs to room-specific DATA, draws platforms, ropes, bells, doors, and initializes entity positions.
  6. Main game loop (2000–2999): Alternates between updating the player (k=1) and one bat (k=2), using a FOR k=1 TO 2 / NEXT k structure.
  7. Bell collection (200–320): Checks ATTR for a bell, awards points, animates collection, and adds the bell to the score display.
  8. Death sequence (4000–4399): Plays a descending tone, bounces the player sprite around the screen using POKEd music data, then decrements lives.
  9. Game over (4401–4452): Flashing “GAME OVER” panel with restart prompt.

UDG Setup and Memory Use

Lines 6002 and 6005–6027 handle the user-defined graphics. A total of 15 UDGs (“a” through “o”, chars 144–158) are defined by reading 8-byte bitmaps from DATA and POKEing them into memory starting at USR "a". Address 63000 is used as both a sentinel (checked against value 17 to avoid re-POKEing on restart) and as the start of a 64-byte music table. Line 8800 similarly guards the music data reload with the same sentinel check.

The UDGs used in the game include:

  • \a, \b — rabbit sprite frames (two animation states, each two rows tall)
  • \c, \d — alternate rabbit frames
  • \e — platform block
  • \f — rope segment
  • \j, \k — bat sprites
  • \l — bell collectible
  • \m, \n — door graphic elements
  • \o — additional graphic

Main Game Loop and Entity Movement

The loop at lines 2000–2999 uses a FOR k=1 TO 2 construct to process two entities per iteration, branching at line 2199 with IF k=2 THEN GO TO 2262 (which targets the second bat’s code block at 2300). This is an economical way to share loop overhead between player and enemy updates.

Player movement (lines 2040–2095) is entirely expression-driven without explicit IF branches for direction:

  • x1 = x + 3*((ATTR(x+2,y)=7) - (INKEY$=m$(3) AND ATTR(x-1,y)=4)) — moves down 3 rows if the cell below has ATTR 7 (rope color), or up 3 rows if climbing key is pressed and rope is present above.
  • y1 = y + (INKEY$=m$(2) AND y<31) - (INKEY$=m$(1) AND y>0) — horizontal movement, clamped to screen edges.

Each bat (variables a,b and c,d) tracks the player’s column (b1, d1) and closes horizontally in steps of 3, with ATTR checks (value <12) to avoid walking into walls. Vertical homing also uses a one-step increment/decrement expression. Line 2347 includes collision avoidance between the two bats, randomizing their positions if they coincide.

Collision Detection via ATTR

The game relies heavily on the ATTR function for all collision detection rather than tracking positions in arrays. Key attribute values used:

ATTR valueMeaning
6Bell (collectible) cell — triggers subroutine 200
7Rope — allows vertical movement
4Rope above player — climb condition
12Wall/platform threshold for bat pathfinding

Line 2080 checks ATTR(x1+1,y1)=6 (one row below the proposed position) to trigger bell pickup, effectively detecting when the player lands on a bell.

Room Data System

Five rooms are encoded entirely in DATA statements. Line 8015 executes RESTORE 8000+100*z where z is the room number (1–5), selecting the correct block. Each room’s data section contains multiple sublists terminated by sentinel value 99, read sequentially for: platform positions, door positions, wall patches, rope positions, and bell positions. The room name strings are stored in a DIMensioned string array q$(5,15).

Music and Sound

A 64-byte table at addresses 63000–63063 stores note values (offset by 40 or 28 depending on context) used for background music and death sequences. The title screen plays this table in reverse (line 9150: FOR i=63063 TO 63000 STEP -1) as an attract-mode loop. The death sequence at line 4041 plays it forward with BEEP .05,(PEEK u)-28. The room-transition fanfare at line 8715 plays the first 8 bytes only.

Animation Technique

The rabbit uses two-row, two-frame animation. Arrays a$() and b$() each hold three entries (two animation frames plus a blank). Frame alternation is achieved by printing a$(k) and a$(3-k) — when k=1 frame 1 is shown at the new position and frame 2 (blank) erases the old, and vice versa for k=2. This XOR-free approach works because positions are tracked explicitly.

Key Definition Subroutine

Lines 9800–9859 implement a flexible key-definition system. Key names are read from DATA at line 9860 ("LEFT","RIGHT","CLIMB ROPE"). Each chosen key is appended to m$, and a duplicate-key check loop (lines 9832–9834) prevents assigning the same key twice. The flashing ? prompt uses FLASH 1 combined with CHR$ 8 (cursor left) to animate in place while waiting for input via PAUSE 0.

Notable Anomalies

  • Line 2199 branches to GO TO 2262 which does not exist; execution falls through to line 2300 (the second bat’s update block). This is intentional — skipping line 2261’s GO TO 2361 (also non-existent, falling to line 2999 NEXT k) for the first bat.
  • Line 9455 uses the idiom IF INKEY$<>"" THEN GO TO 9455 to wait for key release after PAUSE 0, preventing accidental skipping of instruction pages.
  • The GO SUB 9400 entry point is at line 9402 (line 9400 does not exist), a standard Sinclair BASIC memory optimization using GO TO / GO SUB to a non-existent line.
  • Line 8074 is referenced by GO TO 8074 at line 8072 but does not exist in the listing; execution would fall through to line 8080. This appears intentional.

Content

Appears On

Capital Area Timex Sinclair User Group’s Library Tape.

Related Products

Related Articles

Related Content

Image Gallery

BELLTOWER

Source Code

    1 REM by T Sherwood of           West Bromwich,West Midlands
   15 GO TO 6000
  200 IF ATTR (x1,y1)<>6 THEN PRINT INK 5;AT x1+1,y1;"\l": GO SUB 300: LET e=e+1: PRINT INK 6; PAPER 0; FLASH 1; OVER 0;AT 0,e*3;"\l"
  210 IF x1=1 AND y1=31 AND e=9 THEN LET z=z+1: LET s=s+100: GO TO 8000
  299 RETURN 
  300 LET s=s+45: PRINT #0; OVER 0;AT 0,15-LEN STR$ s; INK 6; PAPER 1;s
  310 FOR j=x1+1 TO 0 STEP -1: PRINT INK 8; FLASH 8;AT j,y1;"\l": BEEP .005,40-j*2: PRINT INK 8; FLASH 8;AT j,y1;"\l": NEXT j
  320 RETURN 
  992 PLOT 64,0: DRAW 17,112: DRAW 2,-24: DRAW 24,22
 2000 FOR k=1 TO 2
 2040 LET x1=x+3*((ATTR (x+2,y)=7)-(INKEY$=m$(3) AND ATTR (x-1,y)=4))
 2070 LET y1=y+(INKEY$=m$(2) AND y<31)-(INKEY$=m$(1) AND y>0)
 2080 IF ATTR (x1+1,y1)=6 THEN GO SUB 200
 2090 PRINT AT x,y;a$(k);AT x+1,y;b$(k)
 2095 PRINT AT x1,y1;a$(3-k);AT x1+1,y1;b$(3-k): LET x=x1: LET y=y1
 2199 IF k=2 THEN GO TO 2262
 2200 LET b1=b+(y>b)-(y<b)
 2230 LET a1=a+3*(((x+1)>a AND ATTR (a+1,b)<12)-((x+1)<a AND ATTR (a-2,b)<12))
 2242 IF y=b1 THEN IF x+1=a1 THEN GO TO 4000
 2245 IF b1=b AND RND>.8 AND b1<27 THEN LET b1=b1+5
 2250 PRINT AT a,b;"\k";AT a1,b1;"\k"
 2260 LET a=a1: LET b=b1
 2261 GO TO 2361
 2300 LET d1=d+(y>d)-(y<d)
 2330 LET c1=c+3*(((x+1)>c AND ATTR (c+1,d)<12)-((x+1)<c AND ATTR (c-2,d)<12))
 2342 IF y=d1 THEN IF x+1=c1 THEN GO TO 4000
 2345 IF d1=d AND RND>.8 AND d1>4 THEN LET d1=d1-5
 2347 IF d1=b1 AND a1=c1 THEN LET d1=INT (RND*32): LET c1=(3*(2+(INT (RND*6))))-1
 2350 PRINT AT c,d;"\k";AT c1,d1;"\k"
 2360 LET c=c1: LET d=d1
 2999 NEXT k: GO TO 2000
 4005 PRINT AT a,b;"\k";AT c,d;"\k"
 4010 FOR j=41 TO 1 STEP -4: PRINT AT x,y;a$(3-k);AT x+1,y;b$(3-k): BEEP .014,j: NEXT j
 4030 INK 8: PAPER 8: FLASH 8
 4040 LET v=1: LET w=1: LET i=x: LET j=y: PRINT AT 1,j;"\k"
 4041 FOR u=63031 TO 63000 STEP -1: BEEP .05,(PEEK u)-28
 4044 IF i>20 OR i<1 THEN LET v=-v
 4045 IF j>30 OR j<1 THEN LET w=-w
 4046 PRINT AT i,j;"\k"
 4047 LET i=i+v: LET j=j+w
 4048 PRINT AT i,j;"\k"
 4050 NEXT u
 4051 PRINT AT i,j;"\k"
 4052 INK 7: PAPER 0: FLASH 0
 4060 LET l=l-1: IF l<1 THEN GO TO 4400
 4080 FOR i=7 TO 0 STEP -1: BORDER i: PAUSE 2: NEXT i
 4399 GO TO 8500
 4401 LET t=0
 4405 PRINT OVER 0; PAPER 2; INK t;AT 7,4;"                        ";AT 8,4;"          GAME  OVER     ";AT 9,4;"                        ";AT 10,4;" PRESS KEY 0 TO RESTART ";AT 11,4;"                        "
 4410 PRINT #0;AT 0,26; PAPER 1;" ";AT 1,26; PAPER 1;" "
 4430 LET t=t+1: IF t>7 THEN LET t=0
 4450 IF INKEY$<>"0" THEN GO TO 4405
 4452 CLS : GO TO 8800
 6000 LET p$="\h\g\h\i\o": LET a$="\c\a": LET b$="\d\b": LET s=0: LET h=0
 6001 RESTORE : PAPER 0: BORDER 0: INK 7: OVER 0: CLS : DIM q$(5,15)
 6002 IF PEEK 63000<>17 THEN FOR i=USR "a" TO USR "o"+7: READ j: POKE i,j: NEXT i
 6003 LET q$(1)="entrance hall": LET q$(2)="wine cellar": LET q$(3)="staircase": LET q$(4)="bats' bedroom": LET q$(5)="haunted belfry"
 6004 GO SUB 9800: GO SUB 9400: GO SUB 9200: GO TO 8800
 6005 DATA 112,154,159,61,93,117,124,56,8,62,93,157,21,116,119,7,14,89,249,188,186,174,62,28,16,124,186,185,168,46,238,224
 6010 DATA 187,187,187,0,238,238,238,0,16,8,24,16,8,24,16,8,24,24,60,126,98,98,98,126
 6015 DATA 255,231,255,0,0,0,0,0,255,0,16,56,124,84,68,108,255,255,183,221,107,170,84,0
 6020 DATA 129,219,255,126,24,0,0,0
 6025 DATA 0,24,60,126,126,126,255,0
 6026 DATA 60,126,255,255,255,255,255,159,255,0,0,0,0,0,0,0
 6027 DATA 56,186,186,252,60,30,15,3
 7700 LET z=1: IF s>h THEN LET h=s
 7710 LET l=3: LET s=0
 8005 IF z>5 THEN LET z=1
 8006 OVER 0: INK 7: PAPER 0: CLS 
 8011 PRINT PAPER 4; INK 0;AT 21,0;" Room ";z;" .... The ";q$(z)
 8014 FOR i=2 TO 20 STEP 3: PRINT INK 5;AT i,0;"                                ": NEXT i
 8015 RESTORE 8000+100*z
 8017 PRINT INK 6;AT 1,31;"\m";AT 2,31;"\  "
 8020 READ x,y: IF x=99 THEN GO TO 8050
 8025 PRINT INK 3; PAPER 6;AT x,y;"\e\e\e\e\e\e": GO TO 8020
 8050 READ x,y: IF x=99 THEN GO TO 8061
 8060 PRINT AT x,y; INK 0; PAPER 6; INVERSE 1;p$(z); INK 2; PAPER 6;"\e\e\e\e"; INK 0; PAPER 5; INVERSE 1;p$(z): GO TO 8050
 8062 READ x,y: IF x=99 THEN GO TO 8065
 8064 PRINT INK 4; PAPER 1;AT x,y;"\j\j\j\j\j\j": GO TO 8062
 8070 READ x,y: IF x=99 THEN GO TO 8072
 8071 FOR i=0 TO 4: PRINT PAPER 5; INK 0; INVERSE 1;AT x,y+i;p$(z): NEXT i: GO TO 8070
 8072 READ x,y: IF x=99 THEN GO TO 8074
 8073 PRINT INK 4;AT x,y;"\f";AT x+1,y;"\f";AT x+2,y;"\f";AT x,y; OVER 1;"\n": GO TO 8072
 8080 LET e=0
 8093 PRINT AT 0,0; INK 0; PAPER 4; INVERSE 1;"\h\h\h\f\h\h\f\h\h\f\h\h\f\h\h\f\h\h\f\h\h\f\h\h\f\h\h\f\h\h\h\h";AT 1,3;"\f  \f  \f  \f  \f  \f  \f  \f  \f"
 8095 INK 4: FOR w=1 TO 4: LET i=3*((INT (RND*9))+1): LET u=14+(INT (RND*7)): FOR j=2 TO u: PRINT AT j,i;"\f": NEXT j: NEXT w: INK 7
 8097 FOR i=1 TO 9: READ x,y: PRINT INK 6;AT x,y;"\l": NEXT i
 8100 DATA 3,23,3,26,6,24,9,6,12,25,15,15,18,0,18,2,99,0
 8105 DATA 6,0,6,2,6,8,6,20,9,11,12,20,18,10,18,12,99,0
 8110 DATA 9,26,12,2,12,6,12,12,18,19,99,0
 8115 DATA 3,0,3,5,15,10,15,21,99,0
 8120 DATA 18,19,3,8,3,28,6,6,6,22,9,13,9,22,12,24,12,10,15,19,18,11,18,24,99,0
 8121 DATA 2,0,2,24,5,12,8,16,8,28,11,30,14,16,17,4,17,14
 8200 DATA 9,2,12,6,12,7,12,24,12,26,13,24,13,26,19,8,19,12,99,0
 8201 DATA 6,16,9,8,15,0,15,2,15,16,15,18,18,24,18,26,99,0
 8202 DATA 3,18,3,20,3,26,6,26,18,8,18,12,99,0
 8203 DATA 6,6,6,11,9,18,9,23,12,1,12,13,20,8,20,13,99,0
 8204 DATA 3,18,6,20,6,27,9,13,9,25,12,29,15,4,15,17,15,29,18,4,99,0,5,7,5,31,8,8,8,23,11,1,11,30,14,0,20,7,20,31
 8300 DATA 9,13,18,3,18,13,18,23,99,0
 8301 DATA 3,3,3,13,3,23,3,26,12,13,99,0
 8302 DATA 6,8,6,18,15,8,15,18,99,0
 8303 DATA 99,0
 8304 DATA 3,8,3,13,3,23,6,13,9,18,12,18,15,23,18,28,99,0
 8305 DATA 2,4,2,17,5,10,8,16,11,13,14,13,17,4,17,13,20,31
 8400 DATA 3,26,6,24,6,25,9,1,9,11,15,16,15,26,18,0,18,2,99,0,3,16,9,5,12,21,13,0,13,4,15,20,18,20,18,23,99,0
 8401 DATA 3,20,6,0,6,11,12,0,12,4,99,0,6,6,12,16,12,26,14,0,14,5,99,0
 8402 DATA 3,16,6,1,6,27,9,16,9,27,12,31,15,21,18,5,18,27,99,0,2,20,5,0,5,29,8,12,11,6,11,24,14,16,17,1,17,28
 8500 DATA 3,24,3,26,6,20,9,3,9,5,12,19,15,2,18,18,18,26,99,0,6,25,9,15,9,18,12,0,12,3,15,16,18,0,99,0
 8501 DATA 3,2,3,5,3,14,3,16,12,11,15,24,15,25,18,10,99,0,6,0,6,6,6,11,12,27,15,9,99,0
 8503 DATA 3,10,3,20,3,29,6,22,9,16,12,16,15,4,15,20,18,3,18,20,99,0,2,2,2,24,5,15,8,10,11,1,11,20,11,28,14,2,17,29
 8509 LET c=3*(2+(INT (RND*5)))-1: LET d=INT (RND*32): LET x=19: LET y=0: LET x1=x: LET y1=y: LET a=2: LET b=INT (RND*32)
 8510 PRINT #0; PAPER 1; INK 4;AT 0,0;"\ :                              \ :\ :                              \ :"
 8515 PRINT #0; INK 6; PAPER 1;AT 0,4;"SCORE 00000"; INK 5;AT 1,1;"HI SCORE 00000"; INK 4;AT 1,20;"LIVES"
 8516 PRINT #0;AT 0,15-LEN STR$ s; INK 6; PAPER 1;s;AT 1,15-LEN STR$ h; INK 5; PAPER 1;h
 8590 OVER 1 : INK 8: PAPER 8
 8700 LET i=1: PRINT AT x,y;a$(i);AT x+1,y;b$(i);AT a,b;"\k";AT c,d;"\k"
 8705 PRINT #0;AT 0,25;: FOR j=1 TO 1: PRINT #0; PAPER 1;" \a";: NEXT j
 8710 PRINT #0;AT 1,25;: FOR j=1 TO 1: PRINT #0; INK 5; PAPER 1;" \b";: NEXT j
 8715 IF g=1 THEN FOR j=63000 TO 63007: BEEP .1,(PEEK j)-40: FOR i=1 TO 20: NEXT i: NEXT j: LET z=z+1
 8716 IF g=1 THEN LET f=f+1: IF f<5 THEN GO TO 8000
 8717 IF f=5 THEN GO TO 8800
 8720 FOR i=63000 TO 63063
 8721 LET n=(PEEK i)-40
 8725 BEEP .11,n
 8730 IF INKEY$=m$(2) THEN GO TO 2000
 8780 NEXT i: GO TO 8720
 8800 IF PEEK 63000<>17 THEN RESTORE 9540: FOR i=63000 TO 63063: READ n: POKE i,n: NEXT i
 9000 OVER 0: CLS 
 9004 INK 5: RESTORE 9520
 9010 PRINT AT 1,3;"HE";AT 2,2;"HAUNTED";AT 3,2;"BELLTOWER"
 9013 PLOT 11,169: DRAW 27,0: PLOT 21,168: DRAW 0,-7
 9015 PLOT 86,145: DRAW 7,-7: DRAW 3,0: DRAW 0,3: DRAW -85,0
 9090 PLOT 0,0: DRAW 255,0: DRAW 0,175: DRAW -255,0: DRAW 0,-175
 9092 PLOT 64,0: DRAW 17,112: DRAW 2,-24: DRAW 24,22
 9100 PLOT 102,0
 9110 FOR m=1 TO 5: READ i,j: DRAW i,j: NEXT m
 9130 PLOT 165,44: DRAW 90,-44
 9132 FOR m=1 TO 6: READ i,j: PLOT 64+i,j: DRAW -2,12: DRAW 4,4: DRAW 4,-8: DRAW 2,-12: DRAW -7,3: NEXT m
 9140 FOR m=1 TO 2: READ i,j: PLOT i+64,j: DRAW 1,12: DRAW 3,8: DRAW 3,-4: DRAW -1,-12: DRAW -5,-4: NEXT m
 9144 PRINT INK 2;AT 11,21;"\k"
 9145 PLOT 164,60: DRAW -35,44,4.5
 9147 LET f=0: LET g=0: INK 7
 9148 PRINT #0;AT 1,1; INK 2;"0=SCREEN DEMO     1=START GAME"
 9150 FOR i=63063 TO 63000 STEP -1: LET n=(PEEK i)-40
 9157 BEEP .13,n
 9158 IF INKEY$<>"" THEN GO TO 9160
 9159 NEXT i: GO TO 9150
 9160 IF INKEY$="0" THEN LET g=1: GO TO 6005
 9170 IF INKEY$="1" THEN GO TO 6005
 9175 GO TO 9150
 9200 CLS : PRINT INK 3;AT 0,0;"\k    THE HAUNTED BELLTOWER    \o"
 9201 PRINT INK 5;AT 4,0;"The mischievous ghosts have     taken  down the bells and left   them lying all around."
 9205 PRINT INK 6;"Help the rabbit to collect them.Each bell he picks up will fly  to it's proper place."
 9206 PRINT INK 4;"If he collects them all, he can pass through the door on the topplatform to the next room."
 9207 PRINT INK 5;'"There are 5 different rooms."
 9208 GO SUB 9400
 9210 PRINT INK 5;AT 4,2;"Don't let the bats bite him or  he will turn into a bat too !"
 9211 PRINT INK 4;AT 11,10;"\k  \k  \k"
 9220 GO SUB 9400: RETURN 
 9402 PRINT #0;AT 0,1;"\c";AT 1,1; INK 5;"\d  Press any key to continue"
 9440 PAUSE 1: PAUSE 0
 9455 IF INKEY$<>"" THEN GO TO 9455
 9460 CLS : RETURN 
 9520 DATA 6,138,5,-28,38,-21,2,24,18,-112
 9521 DATA 51,84,66,76,81,68
 9522 DATA 110,20,125,12,140,4
 9525 DATA 16,63,29,73
 9540 DATA 17,29,41,53,53,41,29,17,19,31,43,55,55,43,31,19,22,34,46,58,58,46,34,22,24,36,48,60
 9541 DATA 60,48,36,24,24,60,48,36,22,58,46,34,19,55,43,31,17,53
 9542 DATA 41,29,53,17,29,41,55,19,31,43,58,22,34,46,60,24,36,48
 9804 INK 3
 9805 RESTORE 9860: READ nk
 9807 LET m$="": CLS 
 9808 PRINT AT 4,3;"CHOOSE USER DEFINED KEYS:"''''
 9810 FOR i=1 TO nk
 9811 READ d$: PRINT "         ";d$'': NEXT i: INK 6
 9815 RESTORE 9860: READ nk
 9816 PRINT AT 0,0;: PRINT '''''''
 9818 FOR i=1 TO nk: READ d$
 9819 LET m$=m$+CHR$ 0
 9820 PRINT '"         ";d$;
 9822 FOR j=1 TO 12-LEN d$: PRINT "_";: NEXT j
 9825 PRINT FLASH 1;"?";CHR$ 8;
 9827 PAUSE 1: PAUSE 0
 9830 LET k$=INKEY$
 9832 FOR j=1 TO LEN m$
 9833 IF m$(j)=k$ THEN GO TO 9827
 9834 NEXT j
 9840 LET m$(i)=k$: PRINT k$: BEEP .05,30: NEXT i: INK 7
 9858 IF INKEY$<>"" THEN GO TO 9858
 9859 RETURN 
 9860 DATA 3,"LEFT","RIGHT","CLIMB ROPE"
 9990 STOP 
 9995 STOP 
 9997 SAVE "Belltower" LINE 1
 9998 STOP 
 9999 VERIFY "Belltower"

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

People

No people associated with this content.

Scroll to Top