cowlark.com :  Ancient BBC Micro software

Ancient BBC Micro software

relics from my childhood

Published: 2015 February 3

So back when I was young, my school had a network of BBC Micro computers (8-bit 6502 based machines; unlike a lot of the 8-bits of the time, they had a real operating system and were really rather nice to use). The file server that drove all this was a beefy twin-processor BBC Master with a whole 20MB Winchester disk. I hate to think how much it cost the school.

I wrote loads of stuff for it and I think I was eventually using over half of that Winchester with my own software.

Then it got struck by lightning.

Recently I found a floppy disk at the bottom of a box which I hadn't seen for years. This disk contained a few programs I'd sent to a computer magazine, hoping to sell them. They'd returned it, of course, with 'does not meet our requirements at this time' checked on the form letter. But it contains pretty much the last remaining vestige of this 10MB or so of terrible BBC Micro software that I'd written. (10MB is over 50 floppy disks, you know!)

The game that will run by default is Snail Trail, an excruciatingly bad Tron clone. But enough from me; let 15-year-old-me describe it in his own words:

The year is 19910, and the barbaric custom of settling disputes with legal
processes has been replaced by the civilised method of trial by snail. You and
your partner are dropped onto your duelling snails in the middle of the arena,
and the fight begins!

Each snail is specially genetically engineered; they travel at a constant speed
of 100 kph, and leave a trail of corrosive slime behind them. If you or your
opponent's snail hits one of these, it will burst into flames and explode.
However, to make things simpler, there are two special features: at the edge of
the arena, there are four teleport gates. Any snail entering one of these will
be instantaneously transported to the opposite gate. Also, each of you has a
device that will either drop a pod (a "mine") full of corrosive slime behind
you, which your opponent can run into, or else blast a hole in a corrosive
trail you are about to hit. However, mining or blasting will cost you. Each
person starts with a certain number of credits; mining uses a small amount,
blasting uses a lot more. If you run out of credits, your device will
deactivate itself. For the really bad crimes, income tax evasion and the such
like, the Referees will withhold the mining and bombing devices...

The program is for one or two players. If the one player option is selected,
the computer will play as Yellow. The keys are as follows:

Up A
Down Z
Left X
Right C
Blast V
Mine CTRL

Yellow
Up *
Down ?
Left <
Right >
Blast M
Mine ]

Options such as sound, music, blasting & mining (special effects), number of
games in a match and so on, can be changed from the menu at the beginning.

On a modern UK PC keyboard, Yellow's up key is @.

If you don't like the game, and I don't blame you, you can press ESCAPE to exit. Press F12 to reinitialise the disk filing system; there are a few other programs on the disk, none of which are of any particular interest (and one doesn't work at all).

Snail Trail is 368 lines of BBC Basic. This is a structured Basic with named procedures, proper control flow expression, etc. It's actually quite well written, and 15-year-old-me thoughtfully provided an overview. Although he didn't get the line numbers right.

Program Description
===================
Procedures used:
  250 DEFPROCinitgame                   Draws screen for start of game
  580 DEFPROCinitprog                   Defines envelopes, characters, etc
  770 DEFPROCwrite(T$,X%,Y%,C1%,C2%)    Displays text T$ at X%,Y% in C1% for
                                        background and C2% for foreground
  890 DEFFNn(A%,P%)                     Pads A% with spaces to string length P%
  930 DEFPROCplayer1                    Moves player 1 (red)
 1130 DEFPROCplayer2                    Moves player 2 (yellow)
 1330 DEFPROCexplode(X%,Y%)             Draws an explosion at X%,Y%
 1600 DEFPROCtitle                      Displays title and options menu
 1990 DEFFNyn(fx%)                      Outputs Y for fx%=TRUE, otherwise N
 2020 DEFPROCbomb(X%,Y%,C%)             Draws a mine of colour C% at X%,Y%
 2070 DEFPROCblast(X%,Y%)               Deletes a square at X%,Y%
 2170 DEFPROCupdate                     Updates credit display at top of screen
 2240 DEFPROCendgame                    Displays GAME OVER, says who won
 2520 DEFPROCchangegames                Changes the number of games in a match
 2640 DEFPROCchangeeffects              Toggles special effects on/off
 2700 DEFPROCchangemusic                Toggles music on/off
 2770 DEFPROCchangeplayers              Toggles players 1/2
 2830 DEFPROCcomputer                   Moves computer (yellow)
 3000 DEFPROCturn                       Detects a free direction for computer
                                        turn to
 3140 DEFPROCchangesound                Toggles sound on/off
 3220 DEFPROCcountdown                  Start of game routine
 3380 DEFPROCtune                       Tests if a note in the tune is
                                        forthcoming, if so plays it
 3470 DEFPROCvolume(V%)                 Defines envelope 4 with volume V%
 3510 DEFPROCmovedown                   Download routine

For those of you who don't know BBC Basic, each DEFPROC statement defines a named subroutine which can be called later with PROC. There are a few slightly clever tricks in there: the background music, which will make you scream if you have to listen to it for more than a few minutes, is driven by PROCtune which polls the audio buffer to see if it needs another note. The music data itself is a single string on line 740. It's poked into a dynamically allocated buffer in memory so individual characters can be read efficiently using BBC Basic's equivalent of PEEK (see lines 3470 to 3490). Probably wasn't worth the effort.

It's quite portable, and I suspect would run almost unmodified on an Archimedes or a modern PC with BBC Basic. (I did try with Brandy, an open source interpreter, but Snail Trail crashed it) The program is, though, too big to fit in an unexpanded BBC Micro with the high resolution graphics and a disk filesystem active, so there's a relocation stage (PROCmovedown). This actually overwrites part of the currently running program! There's then some really hacky code in lines 3660 to 3670 which change Basic's working space base address, then poke characters into the keyboard buffer to reset Basic, reload the program in its new home in RAM, and then run it again...

The main thing of note is the AI for the computer player, which is really stupid but rather interesting to watch. It's in lines 2880 to 3170; 29 actual lines of code.

The whole program listing is included below. I should add that despite the statement in line 30, this program is not the property of The Micro User and is, in fact, © 1991 David Given.

See if you can spot the bugs!

   10 REM Snail Trail
   20 REM by D Given
   30 REM (c) 1991 The Micro User
   40 :
   50 IF PAGE>&E00 MODE7:PROCmovedown
   60 MODE1:VDU23;8202;0;0;0;
   70 PROCinitprog
   80 REPEAT
   90   PROCtitle
  100   REPEAT
  110     PROCinitgame
  120     PROCcountdown
  130     REPEAT
  140       PROCplayer1
  150       PROCtune
  160       IF pl%=2 PROCplayer2 ELSE PROCcomputer
  170     UNTIL p1% OR p2%
  180     IF p1% PROCexplode(X1%,Y1%)
  190     IF p2% PROCexplode(X2%,Y2%)
  200     G1%=G1%-(p2%=TRUE)
  210     G2%=G2%-(p1%=TRUE)
  220   UNTIL G1%+G2%>=max%
  230   PROCendgame
  240 UNTIL FALSE
  250 :
  260 DEFPROCinitgame
  270 CLS:VDU23,0,6,0,0;0;0;
  280 X1%=440:Y1%=425
  290 X2%=840:Y2%=425
  300 D1%=RND(4):D2%=RND(4)
  310 p1%=0:p2%=0
  320 C1%=650:C2%=650
  330 GCOL0,2
  340 MOVE 50,50:DRAW 1230,50
  350 DRAW 1230,800:DRAW 50,800
  360 DRAW 50,50
  370 PROCtune
  380 GCOL0,1
  390 MOVE X1%-12,Y1%+12:VDU5,128,4
  400 GCOL0,2
  410 MOVE X2%-12,Y2%+12:VDU5,128,4
  420 PROCwrite("S N A I L  T R A I L",320,1000,1,2)
  430 PROCwrite("Credits",50,900,1,0)
  440 PROCwrite("Credits",50,850,2,0)
  450 PROCwrite("Games",974,900,1,0)
  460 PROCwrite("Games",974,850,2,0)
  470 PROCwrite(FNn(G1%,2),1166,900,2,1)
  480 PROCwrite(FNn(G2%,2),1166,850,2,1)
  490 MOVE 288,836:GCOL0,3
  500 PLOT1,C2%,0
  510 MOVE 288,886:PLOT1,C1%,0
  520 GCOL0,0:MOVE 590,50:PLOT1,100,0
  530 MOVE 590,800:PLOT1,100,0
  540 MOVE 50,375:PLOT1,0,100
  550 MOVE 1230,375:PLOT1,0,100
  560 VDU23,0,6,32,0;0;0;
  570 ENDPROC
  580 :
  590 DEFPROCinitprog
  600 LOCAL A%
  610 VDU23,128,238,238,238,0,238,238,238,0
  620 VDU23,129,255,255,255,255,255,255,255,255
  630 ENVELOPE 1,1,-6,6,0,2,2,0,110,-2,3,10,110,0
  640 ENVELOPE 2,1,0,0,0,0,0,0,20,0,0,-1,120,0
  650 ENVELOPE 3,5,1,-1,0,1,1,0,127,-10,0,-10,126,0
  660 DIM DX%(4),DY%(4),data% 100
  670 DATA 0,0,0,4,4,0,0,-4,-4,0
  680 RESTORE 670
  690 FOR A%=0 TO 4
  700   READ DX%(A%),DY%(A%)
  710 NEXT
  720 max%=10:fx%=TRUE:pl%=1
  730 snd%=TRUE:*FX210,0
  740 $data%="AAQQAQ]AQQAQAAQQAQ]AQQAQUUeeUeAUeeUeUUeeUeAUeeUeAAQQAQ]AQQAQAAQQAQ]AQQAQA]mm]mI]mm]mUUeeUeAUeeUe"
  750 TIME=0:ptr%=-3:tune%=TRUE
  760 ENDPROC
  770 :
  780 DEFPROCwrite(T$,X%,Y%,C1%,C2%)
  790 GCOL 0,C1%
  800 VDU5
  810 MOVE X%-4,Y%:PRINTT$;:PROCtune
  820 MOVE X%,Y%+4:PRINTT$;:PROCtune
  830 MOVE X%+4,Y%:PRINTT$;:PROCtune
  840 MOVE X%,Y%-4:PRINTT$;:PROCtune
  850 GCOL 0,C2%
  860 MOVE X%,Y%:PRINTT$;:PROCtune
  870 VDU4
  880 ENDPROC
  890 :
  900 DEFFNn(A%,P%)
  910 =RIGHT$(STRING$(P%,"0")+STR$A%,P%)
  920 :
  930 DEFPROCplayer1
  940 LOCAL C%
  950 X1%=X1%+DX%(D1%)
  960 Y1%=Y1%+DY%(D1%)
  970 IF INKEY-66 AND D1%<>3 D1%=1
  980 IF INKEY-98 AND D1%<>1 D1%=3
  990 IF INKEY-67 AND D1%<>2 D1%=4
 1000 IF INKEY-83 AND D1%<>4 D1%=2
 1010 IF INKEY-2 AND fx% AND C1%>50 PROCbomb(X1%,Y1%,1):C1%=C1%-50:PROCupdate
 1020 IF INKEY-100 AND fx% AND C1%>200 PROCblast(X1%,Y1%):C1%=C1%-200:PROCupdate
 1030 IF X1%<50 X1%=1230
 1040 IF X1%>1230 X1%=50
 1050 IF Y1%>800 Y1%=50
 1060 IF Y1%<50 Y1%=800
 1070 GCOL0,1:PLOT 69,X1%,Y1%
 1080 IF D1%=0 ENDPROC
 1090 C%=POINT(X1%+DX%(D1%),Y1%+DY%(D1%))
 1100 IF C%=1 OR C%=2 p1%=TRUE
 1110 ENDPROC
 1120 :
 1130 DEFPROCplayer2
 1140 LOCAL C%
 1150 X2%=X2%+DX%(D2%)
 1160 Y2%=Y2%+DY%(D2%)
 1170 IF INKEY-73 AND D2%<>3 D2%=1
 1180 IF INKEY-105 AND D2%<>1 D2%=3
 1190 IF INKEY-103 AND D2%<>2 D2%=4
 1200 IF INKEY-104 AND D2%<>4 D2%=2
 1210 IF INKEY-89 AND fx% AND C2%>50 PROCbomb(X2%,Y2%,2):C2%=C2%-50:PROCupdate
 1220 IF INKEY-102 AND fx% AND C2%>200 PROCblast(X2%,Y2%):C2%=C2%-200:PROCupdate
 1230 IF X2%<50 X2%=1230
 1240 IF X2%>1230 X2%=50
 1250 IF Y2%>800 Y2%=50
 1260 IF Y2%<50 Y2%=800
 1270 GCOL0,2:PLOT 69,X2%,Y2%
 1280 IF D2%=0 ENDPROC
 1290 C%=POINT(X2%+DX%(D2%),Y2%+DY%(D2%))
 1300 IF C%=1 OR C%=2 p2%=TRUE
 1310 ENDPROC
 1320 :
 1330 DEFPROCexplode(X%,Y%)
 1340 LOCAL C%,D%,E%
 1350 GCOL3,3:VDU5
 1360 SOUND&10,2,6,10
 1370 FOR E%=0 TO 1
 1380   MOVE X%-16,Y%+16
 1390   VDU255
 1400   C%=16:D%=16
 1410   REPEAT
 1420     MOVE X%-16-C%,Y%+16
 1430     PLOT 1,0,-32
 1440     MOVE X%+16+C%,Y%+16
 1450     PLOT 1,0,-32
 1460     MOVE X%-16,Y%+16+C%
 1470     PLOT 1,32,0
 1480     MOVE X%-16,Y%-16-C%
 1490     PLOT 1,32,0
 1500     D%=D%-1:C%=C%+D%
 1510     PROCtune
 1520   UNTIL D%=0
 1530 NEXT:VDU4
 1540 FOR C%=1 TO 25
 1550   *FX19
 1560   PROCtune
 1570 NEXT
 1580 ENDPROC
 1590 :
 1600 DEFPROCtitle
 1610 LOCAL C%,A%
 1620 FOR A%=50 TO 100
 1630   PROCvolume(A%):*FX19
 1640 PROCtune:NEXT
 1650 PROCvolume(100)
 1660 CLS:VDU23,0,6,0,0;0;0;
 1670 PROCwrite("S N A I L  T R A I L",320,1000,1,2)
 1680 PROCwrite("Program by D Given",352,900,3,0)
 1690 PROCwrite("Music by A Clark",384,850,3,0)
 1700 PROCwrite("1.",256,650,1,0)
 1710 PROCwrite("Current options set:",320,750,2,0)
 1720 PROCwrite("Number of games:",336,650,3,0)
 1730 PROCwrite(FNn(max%,2),880,650,2,1)
 1740 PROCwrite("2.",256,600,1,0)
 1750 PROCwrite("Special effects:",336,600,3,0)
 1760 PROCwrite(FNyn(fx%),912,600,2,1)
 1770 PROCwrite("3.",256,550,1,0)
 1780 PROCwrite("Players:",336,550,3,0)
 1790 PROCwrite(FNn(pl%,1),912,550,2,1)
 1800 PROCwrite("4.",256,500,1,0)
 1810 PROCwrite("Sound:",336,500,3,0)
 1820 PROCwrite(FNyn(snd%),912,500,2,1)
 1830 PROCwrite("5.",256,450,1,0)
 1840 PROCwrite("Music:",336,450,3,0)
 1850 PROCwrite(FNyn(tune%),912,450,2,1)
 1860 PROCwrite("Press SPACE to start",320,200,3,1)
 1870 PROCwrite("or a number to change the setup",144,164,3,1)
 1880 VDU23,0,6,32,0;0;0;
 1890 REPEAT
 1900   C%=INKEY(0):PROCtune
 1910   IF C%=49 PROCchangegames
 1920   IF C%=50 PROCchangeeffects
 1930   IF C%=51 PROCchangeplayers
 1940   IF C%=52 PROCchangesound
 1950   IF C%=53 PROCchangemusic
 1960 UNTIL C%=32
 1970 G1%=0:G2%=0
 1980 CLS
 1990 FOR A%=100 TO 50 STEP -1
 2000   PROCvolume(A%):*FX19
 2010 PROCtune:NEXT
 2020 ENDPROC
 2030 :
 2040 DEFFNyn(fx%)
 2050 IF fx% THEN ="Y" ELSE ="N"
 2060 :
 2070 DEFPROCbomb(X%,Y%,C%)
 2080 GCOL0,C%:SOUND &11,1,40,10
 2090 MOVE X%-12,Y%+12:VDU5,128,4
 2100 ENDPROC
 2110 :
 2120 DEFPROCblast(X%,Y%)
 2130 LOCAL A%
 2140 SOUND&10,3,4,7
 2150 FOR A%=1 TO 2
 2160   GCOL0,-3*(A%=1)
 2170   MOVE X%-16,Y%+16:VDU5,129,4
 2180   *FX19
 2190 NEXT
 2200 ENDPROC
 2210 :
 2220 DEFPROCupdate
 2230 GCOL0,3:MOVE 288,836
 2240 PLOT1,C2%,0:GCOL0,0:DRAW 938,836
 2250 GCOL0,3:MOVE 288,886
 2260 PLOT1,C1%,0:GCOL0,0:DRAW 938,886
 2270 ENDPROC
 2280 :
 2290 DEFPROCendgame
 2300 LOCAL A%,A$,tune%
 2310 FOR A%=1 TO 50
 2320   *FX19
 2330 NEXT
 2340 FOR A%=1 TO 25
 2350   COLOUR 3:COLOUR 128
 2360   PRINTTAB(15,10)"GAME OVER"
 2370   SOUND &11,-15,100,3
 2380   SOUND &12,-15,148,3
 2390   *FX19
 2400   *FX19
 2410   COLOUR 0:COLOUR 131
 2420   PRINTTAB(15,10)"GAME OVER"
 2430   SOUND &11,-15,108,3
 2440   SOUND &12,-15,156,3
 2450   *FX19
 2460   *FX19
 2470 NEXT:COLOUR 3:COLOUR 128
 2480 SOUND&11,0,0,0:SOUND&12,0,0,0
 2490 IF G1%>G2% THEN A$="Player 1 wins!":C%=1
 2500 IF G2%>G1% THEN A$="Player 2 wins!":C%=2
 2510 PROCwrite(A$,416,500,C%,0)
 2520 *FX21
 2530 A%=INKEY(500)
 2540 ptr%=-3:TIME=0
 2550 ENDPROC
 2560 :
 2570 DEFPROCchangegames
 2580 PROCwrite(FNn(max%,2),880,650,0,0)
 2590 REPEAT
 2600   MOVE 880,650:GCOL 3,1
 2610   VDU5:INPUT "" max%
 2620   MOVE 880,650:PRINT ;max%;
 2630   VDU4
 2640 UNTIL max%>0 AND max%<100
 2650 ptr%=-3:TIME=0
 2660 PROCwrite(FNn(max%,2),880,650,2,1)
 2670 ENDPROC
 2680 :
 2690 DEFPROCchangeeffects
 2700 PROCwrite(FNyn(fx%),912,600,0,0)
 2710 fx%=NOT fx%
 2720 PROCwrite(FNyn(fx%),912,600,2,1)
 2730 ENDPROC
 2740 :
 2750 DEFPROCchangemusic
 2760 PROCwrite(FNyn(tune%),912,450,0,0)
 2770 tune%=NOT tune%
 2780 PROCwrite(FNyn(tune%),912,450,2,1)
 2790 ptr%=-3:TIME=0
 2800 ENDPROC
 2810 :
 2820 DEFPROCchangeplayers
 2830 PROCwrite(FNn(pl%,1),912,550,0,0)
 2840 pl%=pl%+1:IF pl%=3 pl%=1
 2850 PROCwrite(FNn(pl%,1),912,550,2,1)
 2860 ENDPROC
 2870 :
 2880 DEFPROCcomputer
 2890 LOCAL C%
 2900 IF RND(50)=1 PROCturn
 2910 C%=POINT(X2%+DX%(D2%)*2,Y2%+DY%(D2%))
 2920 IF C% PROCturn
 2930 X2%=X2%+DX%(D2%)
 2940 Y2%=Y2%+DY%(D2%)
 2950 C%=POINT(X2%,Y2%)
 2960 IF C% p2%=TRUE
 2970 IF X2%<50 X2%=1230
 2980 IF X2%>1230 X2%=50
 2990 IF Y2%>800 Y2%=50
 3000 IF Y2%<50 Y2%=800
 3010 GCOL0,2
 3020 PLOT 69,X2%,Y2%
 3030 ENDPROC
 3040 :
 3050 DEFPROCturn
 3060 LOCAL D%,OD%,n%
 3070 n%=0
 3080 D%=SGN(RND)
 3090 OD%=D2%
 3100 D2%=D2%+D%
 3110 n%=n%+1
 3120 IF D2%=0 D2%=4
 3130 IF D2%=5 D2%=1
 3140 C%=POINT(X2%+DX%(D2%)*2,Y2%+DY%(D2%))
 3150 IF C% AND n%=1 D2%=OD%-D%:GOTO 3110
 3160 IF C% AND n%=2 AND C2%>200 AND fx% PROCblast(X2%,Y2%):C2%=C2%-200:PROCupdate:GOTO 3110
 3170 ENDPROC
 3180 :
 3190 DEFPROCchangesound
 3200 PROCwrite(FNyn(snd%),912,500,0,0)
 3210 snd%=NOT snd%
 3220 *FX210,1
 3230 IF snd% THEN *FX210,0
 3240 PROCwrite(FNyn(snd%),912,500,2,1)
 3250 ENDPROC
 3260 :
 3270 DEFPROCcountdown
 3280 LOCAL A%,B%
 3290 PROCwrite("Prepare to start",384,600,1,2)
 3300 FOR B%=5 TO 1 STEP -1
 3310   PROCwrite(STR$(B%+1),624,500,0,0)
 3320   PROCwrite(STR$(B%),624,500,3,0)
 3330   SOUND&11,3,100,20
 3340   FOR A%=1 TO 25
 3350     *FX19
 3360     PROCtune
 3370   NEXT
 3380 NEXT
 3390 PROCwrite("Prepare to start",384,600,0,0)
 3400 SOUND&11,3,100,20
 3410 PROCwrite("1",624,500,0,0)
 3420 ENDPROC
 3430 :
 3440 DEFPROCtune
 3450 IF TIME<25 OR tune%=0 ENDPROC
 3460 TIME=TIME-25:ptr%=ptr%+3
 3470 IF ADVAL-6=15 SOUND 1,4,data%?ptr%,5
 3480 SOUND 2,4,data%?(1+ptr%),5
 3490 SOUND 3,4,data%?(2+ptr%),5
 3500 IF ptr%=93 ptr%=-3
 3510 ENDPROC
 3520 :
 3530 DEFPROCvolume(V%)
 3540 ENVELOPE 4,3,0,0,0,0,0,0,121,-10,-5,-2,V%,V%
 3550 ENDPROC
 3560 :
 3570 DEFPROCmovedown
 3580 PRINTTAB(12,11);CHR$132;CHR$157;CHR$135;"Please wait";CHR$32;CHR$156;
 3590 VDU23;8202;0;0;0;
 3600 PRINTTAB(0,20)STRING$(40,CHR$255)
 3610 *TAPE
 3620 FOR A%=0 TO TOP-PAGE STEP 4
 3630   A%!&E00=A%!PAGE
 3640   VDU31,40*(A%/(TOP-PAGE)),20,32
 3650 NEXT
 3660 PAGE=&E00:*KEY 0 OLD|MRUN|F|M
 3670 VDU21:*FX138,0,128
 3680 END