How To Write ZX Spectrum Games – Chapter 18

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

LOAD ""SCREEN$

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.

POKE 23739,111

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

6 thoughts on “How To Write ZX Spectrum Games – Chapter 18

  1. Thanks for this. Can I ask why RANDOMIZE is used here?

    I looked it up in the manual. Apparently it’s something to do with seeding the RND command? I’m not clear what the link is.

    1. The spectrum, like any other computer, uses a Pseudo-Random Number Generator (PRNG) to create random numbers. The PRNG requires a seed to tell the computer how to start generating the sequence of random numbers. If you give the PRNG the same seed every time, it will generate the same sequence of numbers every time. So let’s say you use RANDOMIZE 1 (this is the command to seed the PRNG). This tells the PRNG to start from this seed every time the program is run (assuming RANDOMIZE is at the start of the program, which it usually is). The result is that you will get the same sequence of numbers on every run. Try this:

      10 RANDOMIZE 1
      20 PRINT RND
      30 GO TO 10

      If you want the computer to generate different sequences of random numbers on every run, you need to seed the PRNG with something that’s not constant. RANDOMIZE (or RANDOMIZE 0) on it’s own does this by seeding the PRNG with the time since computer start up. Since time is always ticking, this seed is never the same on every run thus leading to new sequences every time.

      HTH.

    2. USR is just being used to execute the code, but it also has a return value (the contents of the bc register pair). In the ZX Spectrum BASIC syntax, you need to do something with the return value of USR, or it’ll throw a syntax error.

      RANDOMIZE USR 24576 runs the code at address 24576, then uses the return value to seed the RNG. We don’t actually need to seed the RNG, we’re just using it as a convenient way to swallow that unwanted return value to satisfy BASIC’s syntax checker.

      You can use PRINT, or LET, but RANDOMIZE seems to be a de facto standard.

    3. While RANDOMIZE is used as explained by ADMIN, your question was about what does it have to do with calling the machine code. The answer is – since USR is a *function* which calls machine code and *returns* the value of BC register pair at the end of the called routine, you can’t just write USR 49152. That is why you have to put something in front. There are many possibilities of what you can put in front of USR, for example you can call the routine by typing any of these:

      PRINT USR 49152
      LET a = USR 49152
      RANDOMIZE USR 49152
      IF USR 49152 THEN REM

      If the only purpose of the USR function is to call the m/c from which the code would never return back to BASIC then you can choose any of these. If the routine would return some number you have to print or to calculate with then you might use PRINT or LET to print or to store the returned value.

      USR function has one more meaning. If you pass it a string (a single letter) it will return an address of the corresponding UDG character. For example PRINT USR “A” will not call any machine code but will (probably) print 65368.

Leave a Reply

Your email address will not be published. Required fields are marked *