Making Games Load and Run Automatically
Note: This article was originally written by Jonathan Cauldwell and is reproduced here with permission.
While this is simple enough to achieve for an experienced Sinclair BASIC programmer, it is an area often overlooked. In particular, programmers migrating to the Spectrum from other machines will not be familiar with the way this is done.
In order to run a machine code routine, we have to start it from BASIC. This means writing a small BASIC loader program, which clears the space for the machine code, loads that code, and then runs it. The simplest sort of loader would be along these lines:
10 CLEAR 24575: LOAD ""CODE: RANDOMIZE USR 24576
The first command, CLEAR, sets RAMTOP below the area occupied by the machine code, so BASIC doesn’t overwrite it. It also clears the screen and moves the stack out of the way. The number that follows should usually be one byte below the first byte of your game. LOAD “”CODE loads the next code file on the tape, and RANDOMIZE USR effectively calls the machine code routine at the address specified, in this case 24576. This should be the entry point for your game. On a Spectrum, The ROM sits in the first 16K, and this is followed by various other things such as screen RAM, system variables and BASIC. A safe place for your code is above this area, all the way up to the top of RAM at address 65535. With just a short BASIC loader a start address of 24576, or even 24000 will give you plenty of room for your game.
This loader program is then saved to tape using a command like this:
SAVE "name" LINE 10
LINE 10 indicates that on loading, the BASIC program is to auto-run from line 10.
After the BASIC loader comes the code file. You can save a code file like this:
SAVE "name" CODE 24576,40960
CODE tells the Spectrum to save a code file, as opposed to BASIC. The first number after this is the start address of the block of code, and the last number is its length.
That is simple enough, but what if we want to add a loading screen? Well, that is straightforward enough. We can load a screen using
What this will do is load a block of code up to 6912 bytes long, to the start of the screen display at address 16384. Putting the screen file there is a bit trickier, because we cannot simply save out the screen as a file as the bottom two lines would be overwritten with the Start tape, then press any key message. So we load our picture into a point in RAM – say, 32768 – then use
SAVE "name" CODE 32768,6912
6912 is the size of the Spectrum’s display RAM. When we reload the block from tape using LOAD “”SCREEN$, we are specifying that we want to force the code file to be loaded into screen memory. Under these circumstances it doesn’t matter where the code file was located when it was saved.
Now we have another problem: wouldn’t the Bytes: name message that is printed up on loading the code block overwrite part of the screen? Well, yes it would. We can overcome this by poking the output stream.
Will do the trick for us. So our BASIC loader now looks like this:
10 CLEAR 24575: LOAD ""SCREEN$: POKE 23739,111: LOAD ""CODE: RANDOMIZE USR 24576
Welcome to the third and final edition of this guide to BASIC!
Now that we’ve created the graphics for our game and have the key handling routines in place, we can now finally move on to the fun part of animating stuff and watch the screen come alive with millions of tiny coloured pixels (okay, maybe not millions but what the heck, who’s counting?)!
Firstly, if you remember the plot of our game, our intrepid hero of the game Krapz has to survive as long as he can by collecting little quanta particles until he gains enough power to jump out the warp into… yet another more difficult screen. He also has to avoid touching the “ST fluctuation trails” he leaves behind in his wake plus he must stay clear of the deadly boundaries of the warp. In order to pull this fancy (oh sure! – Ed) stuff off, we’ll break it down to a set of tasks.
Task #1: Set up the playing area. This involves drawing the boundaries of the warp and populating the playing field with randomly distributed set of quanta particles. Since every new screen that Krapz jumps into involves the above steps, it makes sense to package it as a sub-routine. Which is what we’ll do:
115 LET quanta=5*level: LET time=seedtime+(level*30): CLS 120 PRINT AT 1,0; INK 3;"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";AT 20,0; INK 3;"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" 130 FOR f=1 TO 19 140 PRINT AT f,0; INK 3;"\b";AT f,31; INK 3;"\b" 150 NEXT f 160 LET x=10: LET y=10 250 FOR f=1 TO 5*level 260 LET p=INT (RND*17)+2 270 LET q=INT (RND*28)+2 280 IF ((p=y AND q=x) OR (ATTR (p,q)=5)) THEN GO TO 260: REM don't overwrite player or another quanta! 290 PRINT AT p,q; INK 5;"\g" 300 NEXT f 999 RETURN
The first line looks a bit complex but is actually a rather simple way of increasing the difficulty level as Krapz progresses through the game. The first LET statement simply increases the number of quanta particle in a screen by a factor of 5 depending on which level (screen) Krapz is playing on. For the first screen (level = 1), there will be only 5 quanta particles to collect. On the 2nd screen (level = 2) there will be 10, and so on and so forth. The second LET statement controls how much time Krapz has to collect all the quanta particles. This is again a factor of the level Krapz is on – 30 times the level as a matter of fact, which compensates for the fact that there are more quantas on each screen as we progress. You’ll notice that a seedtime has been tacked on in the equation to ensure that the player has a basic minimum time to start with on each level.
After having cleared the screen, it’s time to draw the boundaries of the warp – in our case it’s simply a rectangular arena to delineate the playing field. First we draw the top and bottom edges, which is simply a matter of printing 32 characters of the UDG “b” (incorrectly reproduced as \b in the listing above) that we created earlier on. Line 120 prints the top edge and the bottom edge in lurid purple ink.
To draw the vertical left and right edges we employ a FOR-NEXT loop from line 130 to 150 that print UDG “b” 19 times vertically.
It’s now time to randomly populate the playing area with quanta particles. Line 160 first sets the initial co-ordinates of Krapz on each screen – he always starts on row 10, column 10 on every screen.
We then employ a FOR-NEXT loop to print the actual quanta. The number of particles is simply a factor of the level the player is on, and is in fact exactly the same as the variable quanta we calculated on line 115. In fact we can substitute the 5*level with quanta and the loop will work just as well. Line 260 and 270 calculate the row and column (p,q) to print the quanta on. INT (RND* 17) gives us a random number from 0 to 16. We add a factor of 2 to ensure we don’t print anything on row 0 and 1 – the top edge of the playing arena. Note that if got 16 and added 2 we get 18, which still within the bottom edge (column 20) of the arena. Similarl, we keep the quanta within the left and right edges of the playing arena in line 270.
Line 280 introduces a necessary check that ensures that we aren’t printing a quanta on top of another already existing quanta on screen (we do want all our quantas visible individually on the screen!). Plus we don’t want to print at the position Krapz is already in (10,10). So, what we are checking for is “whether the quanta co-ordinates are the same as Krapz’s co-ordinates OR whether the quanta particle is being printed on top of another quanta” state. This is achieved by the IF statement, where the two expressions are separated by an OR (as in the above statement) condition which returns true if any one of the expression is true (either printing on Krapz or printing on a quanta).
To check for whether the quanta co-ordinates are same as Krapz’s co-ordinates, we simply have to check if the column and row co-ordinates for both match up. If they are the same, then we are at the same location on screen. This is calculated using the expression in the IF statement p=y AND q=x. The AND condition returns true if and only if both the sub-expressions are true.
The next thing we have to check for is the case of a quanta overwrite condition. This is done by using the ATTR command, which given a x & y co-ordinate returns the colour attribute at that co-ordinate. The BASIC manual explains how to interpret this value but suffice to say that for our purpose if ATTR returns a value of 5 it means that a cyan coloured character (INK 5) is present at that position. As you will see on line 290, where we print our quanta particles, they are printed with INK 5. In essence, what we are saying is that if ATTR returns 5, we assume that we have a quanta particle there regardless of what actually might be there. In order to not trip up our assumptions we ensure that we do not print any other stuff in INK 5 anywhere the screen. In our program only the quanta particles can have the cyan colour when playing the game.Period.
If we are overwriting Krapz or another quanta, we simply go back to line 160 and re-calculate a new quanta position until we are satisfied we aren’t overwriting anything we shouldn’t be overwriting! If all is well, we proceed to actually print a quanta particle on the screen at the position we calculated (line 290). Once we’ve printed all the quanta particles required for a level we RETURN from the sub-routine via line 999.
There! We’re all set to actually do some gameplay and stuff! Lets move on to the task of moving Krapz around on the playing area. Remember we set up our control keys in Code Shed Guide 2, so it’s time to use those keys to move Krapz around.The control scheme we’ll follow is a simple one. Pressing one of the four direction keys changes Krapz’s direction instantly (inertia? What’s that?). The key needn’t be held down if one wishes Krapz to continue in that direction though- his inertia (that word again!) will keep him going in that direction until a different direction key is pressed.
Have a look at this code which begins our main game loop (so called because we’ll be executing it repeatedly till something happens to break proceedings) proper:
1010 PRINT AT 21,0;"Lives: ";lives;TAB 20;"Score: ";score 1020 PRINT AT 0,0;"Time: ";time;" " 1030 PRINT AT 0,20;"Level: ";level 2000 LET a$=INKEY$ 2010 IF a$=k$(1) THEN LET dir=1: REM right 2020 IF a$=k$(2) THEN LET dir=2: REM left 2030 IF a$=k$(3) THEN LET dir=3: REM up 2040 IF a$=k$(4) THEN LET dir=4: REM down 2045 IF dir<>0 THEN PRINT AT y,x; INK 1;CHR$ (143) 2050 IF dir=1 THEN LET x=x+1 2060 IF dir=2 THEN LET x=x-1 2070 IF dir=3 THEN LET y=y-1 2080 IF dir=4 THEN LET y=y+1
Lines 1010 to 1030 give information regarding the status of the number of lives left, the current score, the time left and the current level being played. It’s our HUD if you will. Since it’s within the main game loop, it will be updated continously.
Lines 2000-2080 are responsible for our inertial control mechanism. The logic is quite simple. We sample the keyboard for any keypress. If it’s a direction key we change Krapz’s direction of movement. If no directional key is pressed, Krapz continues to move along in the same direction. Here’s how. Line 2000 reads in a key from the keyboard (null if no key is pressed). Lines 20101 to 2040 compare the value in a$ with the value in k$ (our desired control keys). Depending on which key is pressed, a variable called dir is set to a particular value that signifies a direction.
Line 2045 prints the trail behind Krapz. Basically, all it does is see if Krapz is moving (Krapz doesn’t start moving until you press a key initially), we don’t bother printing a trail. If he is moving (dir will have some non-zero value then), we will print a solid block of blue at the current Krapz co-ordinates. CHR$ (143) will print the solid graphic block you see on the numeric 8 key on the Speccy keyboard (Graphic mode + shift + 8). What’s this CHR$ you ask? Well it’s one way of printing a character on the screen. For example,if you do PRINT CHR$(65) it will print the letter “A” on the screen. This is because CHR$ converts a number to its string equivalent from the character set. If you take a look at the character set table in the Speccy manual you’ll see that numbers from 32 to 127 represent characters from the ASCII standard. Which is why 65 corresponds to letter “A”. From 128 to 255, the character set is unique to the Spectrum with some special characters like the Graphic Blocks taking up positions 129 to 143. You can print any of these characters by passing the code number to CHR$. Some unprintable characters (no, not the sort you’re thinking of) can do some fancy print formatting tricks – refer to the manual for more on that.
Coming back to the code, so we print the blue block at the current Krapz co-ordinates to signify a trail. “Hang on!” you say. “Won’t we be overwriting Krapz in the process?”. But of course! But since we’ll be repositioning Krapz at new co-ordinates anyway, it doesn’t matter. In fact, the code to calculate Krapz’s new co-ordinates follows:
2050 IF dir=1 THEN LET x=x+1 2060 IF dir=2 THEN LET x=x-1 2070 IF dir=3 THEN LET y=y-1 2080 IF dir=4 THEN LET y=y+1 2090 IF (ATTR (y,x)=3 OR ATTR (y,x)=1) THEN FOR f=0 TO 4: PRINT AT y,x; PAPER 2; INK 6; FLASH 1;CHR$ (145+f); FLASH 0: BEEP 0.4,RND*f: NEXT f: PAUSE 10: GO TO 6000
Lines 2050 to 2080 recalculate the new x or y position depending on the direction of travel. Simple, eh?
In line 2090 we check whether Krapz has crashed into the boundary walls or walked into his own trail (ST fluctuations are bad remember?). This is handled by a single IF statement that checks whether the attribute colour at the newly calculated Krapz co-ordinate matches INK 3 (the magenta colour with which the walls are drawn) or INK 1 (blue colour of Krapz’s trail). If it is, it’s time to play a sad ditty expressing our condolences and create a nifty looking explosion. Since I’m not musically inclined I’m just using a simple BEEP statement that randomly plays some stuff in a low pitch. Creating the explosion is simplicity in itself – I’ve created 4 explosion UDG’s using BASin’s UDG creator tool. To simulate the explosion effect, I just print the four characters (with full FLASH and stuff for added effect) one atop the other with a small time gap inbetween to slow things down a bit. It’s not the greatest explosion effect you’ll ever see but it’s good enough for our purpose. After the dust has settled down, we send the program off to line 6000 where the last recitals are performed. More on that later.
2095 IF ATTR (y,x)=5 THEN LET score=score+(level*10): LET quanta=quanta-1: BEEP 0.01,0.1: IF quanta=0 THEN PRINT AT 10,3; PAPER 1; INK 5; FLASH 1;"Space-Time Jump! Get Ready!"; FLASH 0: FOR f=1 TO 10: BEEP 0.03,RND*f: NEXT f: PAUSE 50: LET level=level+1: GO TO 6020 3000 PRINT AT y,x; INK 4;"\a"
Line 2095 determines what happens when Krapz picks up a quanta particle (we simply perform an attribute check for that). What happens is this: first the score is increased by 10 times the level we are on (as a measure of difficulty).The number of active quantas on the screen is decreased by one. If there are no more quantas left, it’s time to trigger a Space-Time jump and warp to the next level which is easily done with a simple message, a few random beeps and a jump to Line 6020 that sets a few variables back to initial values and then re-draws the level screen by jumping to line 1010.
If a space-time warp hasn’t been triggered (because there are quantas still left on the screen), we go on to print Krapz at the new locatio in line 3000.
We come to a final bit of code that deals with the player running out of time.
3005 LET time=time-1 3010 IF time=0 THEN PRINT AT 10,5; PAPER 2; INK 6; FLASH 1;"S-T Field Collapsing!"; FLASH 0: FOR f=10 TO 1 STEP -1: BEEP 0.1,f/2: NEXT f: PAUSE 50: FOR f=2 TO 19: PRINT AT f,1; PAPER 1; INK 2;"\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::\::": BEEP 0.05,RND*f/2: NEXT f: GO TO 6000 4000 GO TO 1010 6000 LET lives=lives-1 6010 IF lives=0 THEN PRINT AT 10,10; PAPER 4; INK 1;" GAME OVER! ": PAUSE 100 6012 IF lives=0 THEN IF score>hiscore THEN LET hiscore=score: PRINT AT 12,10; PAPER 5; INK 1; FLASH 1;"New High Score!"; FLASH 0: PAUSE 100 6015 IF lives=0 THEN RETURN
Line 3005 simply decreases the available time by 1 unit.
Line 3010 determines what happens next. If we’ve run out of time it’s time for another dramatic message with annoying random beeps and appropriately dangerous looking flashing text. We up the ante by printing a line of solid block (the odd ::\ is the same GRAPHIC MODE + SHIFT + 8 friend we met a while back) that slowly fills up the entire screen to signify a field collapse. We then jump to line 6000 to finish off the formalities.
If we haven’t run out of time we simply loop back to line 1010 via Line 4000 to continue playing.
Line 6000 is where the case of death is handled. First the number of available lives is decreased by 1. Line 6010 checks if we’ve used up all our lives, in which case it’s really Game Over! Line 6012 next checks if we created a new high score by simply comparing the current score with the high score. If our current score is higher, we save it as the high score and congratulate the player on achieving the same.
Line 6015 proceeds to return from the main game loop routine and dumps us back at the main menu screen from where we’d first come from thus bringing us back full circle to square one.
And that my friends brings us to an end to this guide! Hopefully, you’ll go on to write better and bigger stuff than the Dash-it game described here. If not, don’t forget to send your entry to the annual Crap Games Competition! Cheers!
By default Sinclair BASIC provides very limited string manipulation techniques. However, you can easily add more functionality via the following methods.
The following methods are taken from an original World of Spectrum Forum thread, started by Andrew Owen. However, I’ve added some details for better explanation of each method.
The earlier versions of UPPER$ and LOWER$ didn’t work as expected and Alvin Albrecht was kind enough to provide the corrected versions.
DEF FN L$(s$, x) = s$( to x)
Given a string, the L$ function will return a string starting from the leftmost character (hence called LEFT$ in some BASIC implementations) and up to x characters to the right. Therefore, if you have a string, say “Hello” and wish to extract just “Hell” from it, you can do FN L$(a$, 4), where a$ is the string holding “Hello”. A typical example in BASIC would be:
05 DEF FN L$(s$, x) = s$( to x) 10 LET a$ = "Hello" 20 LET b$ = FN L$(a$, 4) 30 PRINT b$
The above BASIC example is easily modified for the rest of the functions that follow.
DEF FN R$(s$, x) = s$(LEN s$ + 1 – x TO LEN s$)
Given a string, the R$ function will return a string starting from the rightmost character (hence called RIGHT$ in some BASIC implementations) and up to x characters to the left. Therefore, if you have a string, say “Hello” and wish to extract just “ello” from it, you can do FN R$(a$, 4), where a$ is the string holding “Hello”.
MID$(string, number1, number2)
DEF FN M$(s$, x,y) = s$(x TO x – 1 + y)
Given a string, the M$ function will return a string starting from anywhere in the string x and up to y characters to the right (hence called MID$ in some BASIC implementations). Therefore, if you have a string, say “Hello” and wish to extract just “ell” from it, you can do FN M$(a$, 2,4), where a$ is the string holding “Hello”.
DEF FN D$(s$) = VAL$ “CHR$ ((CODE s$)+(32 AND CODE s$ > 64 AND CODE s$ < 92))+FN D$(s$(2 TO ))”( TO 27+12*(LEN s> 1))
Given a string, the D$ function will return a string with all characters converted to lower case (hence called LOWER$ in some BASIC implementations). Therefore, if you have a string, say “Hello” and wish to convert all of it to lower case (“hello”), you can do FN D$(a$), where a$ is the string holding “Hello”.
DEF FN U$(s$) = VAL$ “CHR$ ((CODE s$)-(32 AND CODE s$ > 96 AND CODE s$ < 124))+FN u$(s$(2 TO ))”( TO 28+12*(LEN s$> 1))
Given a string, the D$ function will return a string with all characters converted to upper case (hence called UPPER$ in some BASIC implementations). Therefore, if you have a string, say “Hello” and wish to convert all of it to upper case (“HELLO”), you can do FN U$(a$), where a$ is the string holding “Hello”.
Traditionally, games in BASIC use the keyboard for player input and act accordingly. For eg, the keys Q, A, O and P may be used to move the player character around and M may be used to fire or other action. However, as anyone who has played ‘professional’ games (i.e written using machine code) on the speccy would know, using a joystick is a far better alternative especially in fast action games.
This article will tell you how to use the popular Kempston Joystick in your games for that ‘professional’ touch. 😉
To begin with, inputs from the Kempston Joystick are read via port 31. The Spectrum manual has more information on what a ‘port’ is and so I won’t be covering it here. Suffice to say, any external device connected to the speccy reads through a specific port, numbered 1 to 255. The Kempston Joystick uses port 31.
To read the ‘value’ in port 31 you simply do: LET kj = IN 31. Depending on what state the joystick is in, kj will hold a very specific value that can be interpreted as desired. Now then, the Kempston joystick has only 5 states to deal with, namely ‘Fire’ (any of the fire buttons pressed), ‘Up’ (joystick pulled back), ‘Down’ (joystick pushed forward), ‘Left’ (joystick to the left) and ‘Right’ (joystick to the right).
This information is packed into a single byte in the following format: 000FUDLR. As you can see only the lower 5 bits are of interest to us, which means we can ignore any value of kj above 31. Depending on which bit is set (value of 1) we can assume the joystick is in that state. Multiple states are also possible – firing and moving for example, or moving diagonally for example. Obviously you can’t have states of ‘Up’ & ‘Down’ or ‘Left’ & ‘Right’ occurring simultaneously!
The table of basic values that we need to test is given below:
|kj (IN 31)||Bit pattern (lower 5 bits only)||State|
For the purpose of this tutorial I’m going to ignore multiple directional states because BASIC doesn’t support the kind of bit level manipulation we would like to achieve detection of that. However, it will be useful to detect if we’re firing and moving so that will be on the agenda.
Let’s get the simple detection out of the way first!
10 LET x=10: LET y=10 20 LET kj=IN 31: REM get the state of joystick 30 PRINT AT y,x;"*" 40 IF (kj>31) OR (kj=0) THEN GO TO 20: REM ignore spurious inputs 45 BORDER 1: PRINT AT y,x;" ": REM overwrite at old position 50 IF kj=1 THEN LET x=x+1: REM left 60 IF kj=2 THEN LET x=x-1: REM right 70 IF kj=4 THEN LET y=y+1: REM Down 80 IF kj=8 THEN LET y=y-1: REM Up 90 IF kj=16 THEN BORDER 2: REM Fire 100 GO TO 20
The code is pretty simple. To see it in action we will print an asterisk on the screen that can be moved about by manipulating the joystick. Screen boundaries aren’t checked though to keep the code simple. We’ll also set the border to Red if the fire button is pressed. That’s it really.
To check for the event of fire and moving we’ll have to perform a simple trick. When firing and moving, kj will have bit 5 (for fire) and the bits for movement (any of them from 1 to 4) to be set, leading to a number that is greater than 16 (since fire by alone is itself 16). So all we do then is check for this fact (kj > 16) and if so, note that the fire is being pressed and then subtract 16 from kj so that we can continue to check for the movement keys as normal.
The above code can be modified so (changed lines in blue):
10 LET x=10: LET y=10 20 LET fire=0: LET kj=IN 31: REM note: reset fire event every time! 30 PRINT AT y,x;"*" 40 IF (kj>31) OR (kj=0) THEN GO TO 20 45 BORDER 1: PRINT AT y,x;" " 46 IF kj>16 THEN LET kj=kj-16: LET fire=1: REM Check if firing AND moving 50 IF kj=1 THEN LET x=x+1 60 IF kj=2 THEN LET x=x-1 70 IF kj=4 THEN LET y=y+1 80 IF kj=8 THEN LET y=y-1 90 IF kj=16 OR fire THEN BORDER 2: REM fire by itself or firing and moving? If so red border 100 GO TO 20
That’s about it really. Doing the above in machine code is, in fact, easier because it has all the necessary operators to perform low level bit manipulation which you really need if you want to check for multiple states of the joystick. However, I believe the above will do very nicely for those BASIC games that do not need diagonal movement!
Experiment and enjoy!
In the previous installment we left off at the GO SUB command and how it can be used to invoke a sub-routine. We shall continue that discussion and then some more by examining another bit of code first:
1 REM Dash it! 2 REM by Arjun Nair 2004 3 REM *********************** 5 CLEAR : RANDOMIZE 10 CLS : LET hiscore=0 11 DIM k$(4): LET k$(1) = "p": LET k$(2) = "o": LET k$(3) = "q": LET k$(4) = "s" 15 GO SUB 6060: REM init graphics 20 GO SUB 8000: REM main menu 30 GO SUB 1000: REM game loop 50 GO TO 20
In line 5, we see the first bits of useful code. CLEAR simply wipes out the Spectrum memory and provides you with a clean slate to work on.
NOTE: Actually that’s a simplistic explanation. The CLEAR command actually does a bit more than that. It not only clears all the variables but also clears the display screen, resets the PLOT position and clears the GO SUB stack.
The following statement is a RANDOMIZE keyword, which initialises the random number generator and makes it as random as possible (since computers can’t actually generate truly random numbers for various reasons). Sometimes the RANDOMIZE keyword is followed by a number, which is called the seed. Specifying a seed of zero (using RANDOMIZE on its own is equivalent of saying RANDOMIZE 0) makes the Speccy use an internal timer to seed the generator, which makes things very random indeed.
NOTE: Again, I have not gone in depth into the workings of the RANDOMIZE keyword. But suffice to say that using a number other than zero will create a sequence of random numbers that will repeat every time the program is run. Using zero ensures that RANDOMIZE will be as random as possible.
Coming to line 10 we have the CLS keyword that simply clears the screen, and nothing more. If you have specified any foreground and background colours and/or a border colour, the CLS command will clear the screen to them. We’ll come to that shortly when we start dabbling in colours.
The next statement is a LET statement that assigns the value zero to a variable called highscore. Effectively, a variable is a named placeholder that serves to hold some value. They are of two types – numeric variables and string variables. Numeric variables hold only numeric values (ranging from 0 to 255), while string variables hold alphanumeric data in string format (anything within quotation marks). In our case, highscore is simply a numeric variable that initially holds zero.
Things get a bit more involved in the next line. Before I get into the technical explanation, let me explain what I’m trying to do here. What I want is the player to be able to redefine the control keys from within the game if he so chooses. Therefore, I save the information on control keys in a string array (I’ll come to arrays shortly), which can be manipulated anytime (which we’ll do when we implement the Re-define keys option).
As for arrays, well they are nothing but a collection of similar stuff (or objects as we call them in programming parlance). You can have a collection of months (January, February, et al) or collection of music albums, and they can all be thought of as arrays of months and albums respectively. Like plain variables, an array can be of numeric or string type. In fact, they are called array variables and individual elements of the array behave just like plain variables.
In the code above, we would like to have an array of control keys (an array since they are just a collection of keys). The way to go about is that we first tell the Speccy that we would like to set aside space for so many number of array elements. In our case, we would like to create an array of four control keys (Up, Down, Left & Right). The statement DIM k$(4) does precisely this. DIM is short for DIMension (size of array), k$ is the name of the array variable and 4 signifies the number of elements that we want.
Once you have created an array, you can fill it in using the LET statement like we did for highscore. The difference here is that we access individual elements of the array using the syntax k$(1), k$(2), …. k$(n) – where n is any element number (an index). You cannot do LET k$ = something. You MUST specify the element within the array that you wish to change by giving an index number. So we have stored the default letters that will correspond to our control keys in k$ from 1 to 4.
NOTE: In Sinclair BASIC, all string variables must consist of only one letter followed by the $ sign, which is why we have called our control keys array k$ instead of something meaningful like key. Numeric variables don’t suffer from such limitations, which is why our high-score variable is sensibly named highscore.
Lines 15 to 30 use a bunch of GO SUB’s to transfer control to various part of the program. GO SUB is short for GO to SUBroutine. As we saw earlier, a subroutine is a short piece of code within your program that performs a specific function. Once it is finished, the sub routine RETURNS control to the point in the program from which it was called.
In our case, we have separate sub-routines for initialising the graphics for the game, for presenting a menu to the player and finally for running the actual game. As I explained earlier, by using sub routines I have “compartmentalised” the code in my program so that if I later decide to change the way a certain sub routine is implemented (say, if I change how the menu looks) I don’t have to worry about how it will affect the whole game. Here, line 15 first transfers control to subroutine that starts from line 6060 that sets up the game graphics.
Once the subroutine is done, it RETURNs to control to the point from where it was called, in our case this is back to line 15. The next statement is the REM statement (which I have used to remind me which subroutine the GO SUB is referring to), so the Speccy ignores it and goes to the next line instead. Similarly for the main menu and for running the game (the game loop).
Finally we have line 50 with a GO TO keyword that transfers control to line 20. This is so that once the game loop is done (the player has finished all his lives) we want to go back to the main menu option screen. The difference between GO TO and GO SUB is that the former simply transfers control to the line specified in the program and doesn’t expect any RETURNs to it (we don’t want to come back here from the main menu for instance), while the latter is a call to an actual sub-routine.
NOTE: If you’ve typed the code in and wish to save it, choose the Save-As option from the BASin File option. Late on, I’ll show you how to create an actual tape image so that you can load it in to your Speccy!
Okay, let’s have a look at the main menu sub-routine then. This sub-routine will present 4 options to the user and the user can select any one of the options by pressing the key corresponding to the item of interest. The code then:
8000 REM *** Main Menu *** 8010 BORDER 0: PAPER 0: INK 6: CLS : LET a$="": 8020 PRINT "\f\f\f\f\f\f\f\f\f\f\f\f\Dash it!\f\f\f\f\f\f\f\f\f\f\f\f\" 8030 PRINT AT 5,7; INK 3;"1) Play game" 8040 PRINT AT 7,7; INK 4;"2) Redefine Keys" 8050 PRINT AT 9,7; INK 5;"3) Instructions" 8060 PRINT AT 11,7; INK 6;"4) Credits" 8070 PRINT AT 15,7; INK 7;" High Score: ";hiscore 8080 PRINT #0; INK 2;TAB 5;"(c) 2004 Arjun Nair" 8090 LET a$=INKEY$: 8095 IF a$="" OR a$>"4" THEN GO TO 8090 8096 BEEP 0.3,10 8100 IF a$="0" THEN PRINT #0;"For my parents and Lina, Shreya & Hazel": PAUSE 200: CLS 8110 IF a$="1" THEN RETURN 8120 IF a$="2" THEN GO SUB 8400 8130 IF a$="3" THEN GO SUB 9000 8140 IF a$="4" THEN GO SUB 8300 8150 GO TO 8010
All right! So what do we have here? Line 8000 is our friendly REM statement again informing us that this is the Main Menu sub-routine.
Line 8010 sets up the display screen by setting the background (PAPER) and border (BORDER) colour to black (0) and the foreground (INK) to yellow (6). Doing a CLS then quickly clears the screen of any previous junk and enables our colour settings. We then initialise a string variable called a$ to a null value. I’ll tell you why shortly.
The funny characters you see in line 8020 in the PRINT command are, unfortunately, junk copied over from BASin thanks to some sneaky embedded control characters in the PRINT command. Instead of using the usual INK and PAPER commands to change the colours we can actually use a BASIC trick to embed these colours within the PRINT command itself. You can see that they are embedded because they will then turn up even in the listing of the program on the Speccy. In BASin, the editor replaces these control characters with some symbols, which is why they don’t turn up in the listings. However, do a LIST command and see the results in the emulator window. You will see what I mean. So how do we embed control characters for colours in the PRINT statement? I’m not aware of how to do it in BASin, since the editor doesn’t allow you to do such things. On a Speccy however, you can do this: After the first quotation mark in the PRINT statement, embed the colour information by going into extended mode (flashing E cursor) and pressing the key that corresponds to the required paper colour or holding down the SHIFT key (while still in extended mode) and selecting the required INK colour. For example, if you want to print something in red on a yellow background, you will first enable the yellow background for the text by going in to extended mode and pressing 6. Then enable the red ink by again going into extended mode, hold down shift and press 2. Now type in whatever you want and it will be in the colours you selected (even in listings). When the required text is done, you will have to revert back to the normal colours or your entire program listing will be in this colour! To do this, simply repeat the process after the coloured text but this time choosing white paper and black ink. Apart from colour control characters, there are other control characters that you can embed but I won’t be examining them here.
If you aren’t comfortable with using control characters (and I’m not) stick to normal INK and PAPER commands to get things done.
Lines 8030 to 8060 print out the options for the user. Again note the versatility of the PRINT command. I’m adding an AT qualifier to the PRINT command to tell it where exactly to print text on the screen. The PRINT AT form takes two parameters: one for the column number and the second for the row number that you want to print to. So the PRINT AT command in line 8030 will print the text “1) Play game” at column 5, row 7 on the screen. Similarly we list the other options on the screen.
Line 8070 offers something of more interest. We are printing out the text “High Score: ” followed by the value in the hiscore variable. This way you can print the value of any variable including string variables (we’ll see an example of that in the Key Redefine sub-routine).
Next we come to the ego-feeding copyright message of line 8080. The only thing of interest here is the TAB keyword that inserts blank spaces when printing to screen. The number after the TAB keywords indicates how many blank spaces to insert. Note that this number ranges from 0 to 31 inclusive. So to insert only one blank space, use TAB 0, not TAB 1, which will insert 2 spaces!
Now we come to the interesting part of actually finding out which option the user has selected and then acting on it. The first step is to read in the key pressed by the player when choosing an option.
To find out which key has been pressed is achieved by using the INKEY$ command. When the Speccy encounters this command it immediately (well almost) scans the keyboard to see if a key has been pressed, and if so, it returns the value of that key. If no key has been pressed, it returns a null value. In line 8090, we ask the Speccy to do just that and save that value in a variable called a$ (which you will remember we initialised to a null value in line 8010), so that we can act upon the information.
Line 8095 does a simple check to see if the user has pressed a key that we are interested in. If not we simply GO TO line 8090 to again wait for a key press.
This requires an introduction of our first conditional statement via the IF-THEN statement. It’s simplicity itself, really. The IF statement takes the form, IF <condition> is true, THEN do this otherwise skip this entire line. You can string multiple conditions together in an IF statement by using the OR and AND operators. ORing two conditions together will evaluate to true if any one of the conditions is true. ANDing any two conditions together will evaluate to true only if both conditions are true. It’s pretty similar to how things work in English. For example: IF it’s raining AND I don’t have an umbrella THEN I’ll stay in. Here you will stay in only if both the conditions – rain and lack of umbrella – are true. Another example: IF it’s raining OR it’s windy THEN I’ll stay in. In this case you will stay in if either of the conditions – rain or wind – are true. The BASIC form of IF is similar except that the conditions aren’t in English like above. They take a slightly different form which will become apparent as we examine our code in detail.
Right, so what does line 8095 do? It will GO TO line 8090 IF the value in a$ is null (no key pressed) OR if a$ has a value greater than 4 (we are interested only in keys from 0 to 4 that correspond to the options on the screen). Simple, eh?
In line 8096 we take our first stab at sound effects on the Speccy. This is achieved by using the BEEP command that takes two parameters. The first one is the duration in seconds (so 0.3 here indicates that we want a short note) and the second one is the pitch of the note (in semi-tones above or below middle C depending on whether it’s positive or negative). As I’m not musically inclined all I can say is that lower values of second parameters give you a low guttural sound while higher values give high-pitched “shrieky” sounds. Here we want to emit a short low pitched sound to indicate that a valid key has been pressed by the user and that an option has been selected.
Line 8100 is an interesting one ‘cos it handles the event for key 0 being pressed. If you notice there is no option that corresponds to key 0. It’s just a hidden Easter-egg that pops up with some irrelevant information (to the user), waits for 4 seconds and then clears the screen. In case you are wondering who the other people are, apart from my parents, that I’ve mentioned, they are my cousin-sister, my niece and my ex-crush (don’t ask).
Right ho! Back to the code and we come to line 8110 which checks if the user chose to play the game, in which case it simply RETURNs control to line 20 from where it was called originally, where it just finds the REM statement lurking after the colon symbol and so skips on next to line 30. You can clearly see the sub-routine process in action here. Line 20 called the main menu sub-routine, which after it was done, simply returns back to line 20. You can call this sub-routine from anywhere in the program and after it’s done, it will RETURN control to from wherever it was called from.
Line 8120 calls the Key Redefine sub-routine if the user has selected 2. So once that sub-routine is done it will return here, back to the main menu. Similarly, line 8130 calls the Instructions sub-routine and line 8140 calls the Credits sub-routine.
The last line in the sub-routine, 8150, does a GO TO to the beginning of the sub-routine (line 8010), so that the main menu is displayed again. This is done because the sub-routines may have displayed something else on the screen (instructions for example) and we want to re-display the main menu once control returns from the sub-routine.
And with that we come to an end to this installment. I hope you’ve understood most of the stuff in here if not all. Experiment with the BASIC stuff introduced here and you will get a good hang of things. The one good thing about Sinclair BASIC is that it’s pretty intuitive for the most part and is quite easy to get to grips with.
In the next edition of this guide, we’ll introduce the key-redefinition sub-routine, graphics and the actual game loop where all the action resides. So, until next time, cheerio!
Comments and feedback are most welcome and appreciated! Please note that the article is not meant to replace the Spectrum BASIC manual as a reference tome and as such I would encourage you to look up any keywords that you come across in the article for further insights. Also, please address your queries regarding BASin to the author of the program Paul Dunn, especially regarding bugs, features and operation of the program (the article will only cover parts that are essential to the discussion).
Hello and welcome to to the Beginners guide to BASIC! Are there some amongst you who have always wanted to have a go at writing something with BASIC but never got around to it for whatever reasons (like playing Sabrewulf for instance)? Or some of you who have returned to the “scene” to find that you’ve forgotten all that you knew about the good ol’ ZX Basic? Or maybe you are a fresh faced youngster who would like to know what this BASIC thingy is when everyone else is talking about C++ and C#?
If you fit into one of the above categories then you’ll like what’s coming up, if not, well you best get back to your game then and try improve your high-score!
Right. Are all those sods gone? Good! So are we comfortably sitting with some music in the background and a pint of good beer at hand? Yes? Let’s get the ball rolling then!
To keep things interesting and informative I think the best way will be a “hands-on” approach. What we are going to do is to write a game from scratch, learning concepts and techniques as we go, and I’ll explain not only how to write bits of it but also why we are doing it the way we are doing it. And the game I’ve chosen for this purpose is the rather excellent (and completely crap – Ed) game “Dash-it” written by a rather excellent fellow by the name of “Arjun” by some spooky co-incidence. Why? Well apart from the fact that I’m biased towards it for obvious reasons, it is also a short game with a simple design and is relatively easy to implement. It’s also included as an example program with BASin so you already have the full source code for reference, if you can’t be bothered to type-it all in again – although I would strongly recommend that you do so.
This rather nicely brings me to BASin, which in case you didn’t know, is a full featured IDE (Integrated Development Environment) to develop programs for the ZX Spectrum. There is a review of it somewhere in this edition of ZX Shed that lists its features, so I won’t describe it further except to say that we’ll be using it heavily since it makes life so much easier. If you don’t know first thing about BASin, don’t worry, I’ll explain bits of it too as we go along.
“Dash-it” is a simple game that is a variation on the popular “snake” game. The aim of the game is to eat the dots without crashing into the blue trail that you leave behind. A timer counts down the time on the top left, and once it reaches zero, you are thrown into the next level with even more dots to devour! Finally, there is a force-field around the playing area, and crashing into it will cause the player to lose a life.
So that’s the game design (if I can call it that!). From the game design we can derive the following essential elements that will make up the game:
1) A player sprite that can be moved around by the player.
2) Dots that the player must collect.
3) A force-field that defines the playing area. Colliding with it causes loss of life.
4) A timer that counts down to zero.
5) A blue trail that the player must leave behind as the sprite moves around. Colliding with it causes lose of life.
With the above simple foundations in place, we can always extend the game if we want to (and we will).
Well that’s the core of the game. What other stuff could we want in the game? Hmm… let’s see. How about an instructions screen, a way to redefine the keys, and a credits screen (just like in professional game!)? That way, anybody new to the game can read the instructions on how to play the game, change the keys to suit his or her taste, and maybe have a look at the utterly cool people that the author wishes to thank for some reason!
The logical way to go about it would be to code the “features” as separate sections in the game that exist more or less independently of each other. We can then code the game one section at a time, adding and revising individual sections anytime we want. Typically, one would code the core of the game first then add in the other “features” one at a time. However, in our case, since this is a tutorial and since our wanted features are technically easier to implement and understand, we will code them first and then move on to the actual game core.
As already mentioned, our features will be individual sections of the game. Think of the game as a house, and the sections as individual rooms. Every room stands independent (more or less) of each other, but together they make up the entire house. Similarly for our sections! Going by that logic, our instructions, credits, key redefinition and the actual game can be divided into individual sections. In programming terminology they can be thought of as sub-routines.
For example, in Dash-it I have coded the instructions as a sub-routine that exists from line 9000 to 9070. Lines 8300 to 8380 is the credits sub-routine; lines 8400 to 8540 is the key redefinition sub-routine and finally, lines 4000 to 6000 is the actual game. As you can see, what I have done is carve up the program into logical code blocks. Let’s examine one such code block to understand this sub-routine business:
8300 REM *** Credits *** 8310 CLS 8320 PRINT "Dash it! was originally written": PRINT "especially for the CSS Crap ": PRINT "Games Competition 2004.": PRINT '"This version is an updated one ": PRINT "with some bug fixes and sound!": PRINT "It's still crap though! 🙂 " 8330 PRINT : PRINT "Developed on the BASin IDE written by Paul Dunn." 8350 PRINT : PRINT "Hello to all WoS regulars (and irregulars) and to folks at CSS.Speccy forever! Amen." 8360 PRINT #0; INK 5;" www.yantragames.com “ 8370 PAUSE 0 8380 RETURN
BASin Tip: For the code, you can do either of the following in BASin: type it in, or load the existing code from hard disk. To do the latter, simply choose the LOAD option from the File menu option, navigate to EXAMPLES > BAS Files> CSGCC and choose ‘Dash-It!”. If you go the typing route, make sure you enter the line numbers exactly as you see them here or unpleasant things will happen shortly (like the program not running as it should)!
Hey, there is English in some of the lines of code! There is hope yet eh? A pity you can’t write an entire game in BASIC using English statements, but hey, this is good enough. For starters consider line 8300. The REM keyword is short for REMark and is just a placeholder for comments in your program. You can put any piece of drivel after it and the Spectrum won’t care. In fact, as soon as it sees a REM in the program, it goes into “ignore” mode and just moves on the next line of the program. I have used REM to remind myself that this is the “credits” sub-routine.
The next line features our first ever useful command: CLS, which simply CLearS the display screen. If you had specified any ink or paper colours before this, CLS would have cleared the screen to them (we’ll see an example of that shortly).
Lines 8320 to 8350 print a number of messages on screen. This is achieved via the PRINT command – a very powerful and versatile way to output to the screen. The syntax is simple: Just put whatever you want to output between the quotation marks (“”) following the PRINT command. For example, if you wanted to display “The answer is 42” on the screen you would do:
PRINT “The answer is 42”
In LINE 8320 you will see that we have multiple PRINT commands in one line, all of them separated by a colon (:) symbol. The colon operator serves to separate statements from each other. You can therefore put multiple statements on one line using a colon, which is what we have done.
One problem you will immediately run into when printing to the screen is that words may split at the end of the line resulting in ugly looking on screen text. This is because the speccy can display 32 characters on one line, and if you are trying to display more than a 32 character long message, the speccy will move to the next line to print the next character. There is no fancy word wrapping facility so you’ll have to ensure that your words don’t break unexpectedly when printing on screen. What you have to do is count the number of characters right after the first quotation mark. When you hit 31 (32 characters range from 0 to 31) you know that the next character will spill to the next line so you may need to add white-spaces to move the entire word to the next line (see the second PRINT statement in line 8320).
BASin Tip: BASin makes formatting text output easy. Instead of counting the characters, just watch the character ruler at the bottom of the window. The ruler automatically indicates the 32nd character mark with a red arrow as you type in the words!
Using the PRINT command all by itself prints a blank line, which is what we have done in line 8330 to split our output into paragraphs. However, I’ve been a bit cheeky I must admit. If you look at the fourth PRINT statement in line 8320, you will see that right before the first quotation mark, there is an apostrophe (‘) symbol. This is a shortcut way of making the PRINT command display a blank line! For example, this PRINT command will display the words “King” and “Kong” on successive lines:
Note the apostrophe symbol wedged between the end of the first quotation pair and the beginning of the second quotation pair!
Line 8360 is a very interesting one because it uses a BASIC trick to print on the 22nd line of the screen (which is usually reserved for input by BASIC although it’s part of the display screen. Normally you can only print from lines 0 to 21.). The trick is to simply put #0 after the PRINT command. Honestly, that’s all there is to it! This will simply make BASIC print whatever you want on the 22nd line. Nifty eh?
Okay so there are some more interesting things in line 8360. The first of which is the semi-colon symbol, which serves a very different purpose than a colon in a print statement. Here it serves to separate the data items in the PRINT statement. Examine the following:
PRINT “Hello ”;“there”
This will print “Hello there” on the screen. The semi-colon here merely tells the speccy to continue printing the word “there” right after the word “Hello ”. You can have many data items in just one PRINT statement.
If we had wanted to specify a colour for the text we would have used the INK keyword. The way to do is to simply use the INK keyword followed by the number corresponding to the colour we want (range 0 to 7). Cyan is 5 so INK 5 will print the text in cyan colour. The simplest way to print using cyan colour would be:
PRINT INK 5; “This text is cyan in colour”
Again, we use the semi-colon to split up the information (data item) to the PRINT command – the first being the ink colour and the second being the actual text.
In line 8360 we are trying something complicated: we want to print some text, we want it to be in cyan colour and we want it on line 22, which is why it reads the way it does with multiple semi-colons.
There are other sophisticated things you can do with the PRINT command but we’ll examine them later on as we deal with them in the code.
Line 8370 introduces the PAUSE command, which waits for n number of frames. Since the Speccy displays rate is 50 frames per second (or 60 in North America), you can wait for x seconds by using a value that’s equal to n times x. For example, if you want to put the Speccy on hold for 4 seconds you will use PAUSE 200, since 4 x 50 = 200. For 10 seconds, the value will be 500 and so on and so forth. However, if you use a value of 0, the Speccy will wait indefinitely till you press a key to move things along. This is ideal for “Press any key to continue” situations as you will no doubt realise. And we us this very fact to our advantage by displaying a screen full of information and then waiting for a key press before continuing.
The last line in the code block is a simple RETURN command. This one command though is single-handedly (well almost) responsible for making this code block a sub-routine! You see, a sub-routine can be called from anywhere in the program. That is, it is a re-usable piece of code. It needs to be written once, and if you need to use it anytime just call the sub-routine whereupon the speccy will transfer control to the sub-routine, execute it and then return to the point where it was called. You call a sub-routine with a GO SUB command (as we’ll see in the next code segment).
We shall examine the GO SUB command and other useful stuff in the next installment of this guide. Stay tuned!