Your Spectrum
Issue 10, December 1984 - Programming
Home Contents KwikPik
Even Ian Beardsmore can be bitten by the games bug - and memory's no problem when you use his base 255 numbering system!
I've been described as 'hard- bitten' and 'jaded' in my time (And a whole lot else I shouldn't wonder.Ed.) ... and you want to know why? Simply because games of any kind - yes, even Jet Set Willy - leave me cold. So, you can imagine the psycho-physical trauma my self-image underwent when a matter of months ago I got hooked on a wargame by CCS called Pacific War. Never heard of it? Don't worry, no- one else has either!
Anyway, the upshot of all this is that I decided to write my own version of this great game.


I'd already worked out how I was going to store the data - using a database as the crux of the program - but it was only when I was explaining to a friend that I would need to use three bytes of storage to store the necessary data that I began to see his eyes glaze over. He was perplexed ... and so was I. It seems that what was obvious to me, namely using the character set as a base 255 numbering system, had not occurred to him - or to anyone else I mentioned it to. How do you think that mega- sized dungeons are produced then?
Most of the techniques I'll explain here are quite simple, but the contraction of storage space is phenomenal. First, think of how numbers are built up in decimal and Hex (using base 10 and base 16 respectively). In decimal, when you reach '9', you add another digit - in Hex, having '0' to 'F', you don't need another digit until you reach 'F'. Well, using the character set, you wouldn't need to add more digits until you reached '255', and even then each character only takes up one byte.
Maybe I ought to explain that a little. Each of the Spectrum's keywords, punctuation, etc, are stored as single-byte tokens. So commands like SAVE, LOAD, etc, take up the same storage space as punctuation, such as ',', '!', etc. Thus, if you could store direct commands as strings and then add them together, you could store numbers up to 16,777,215 in three bytes.
Take a look at the first two programs I've included so that you can recap your knowledge of string handling. The first program allows you to input anything from the Spectrum keyboard and output it as a string. The second program takes two strings and converts them into numerical data, adds
Y O U R them together and outputs them as a string.


OK, now take a look at the third program I've included. This is a decimal to base 255 converter - all you do is input the number you want converted and it prints out the Speccy commands, etc, you'll need to use in your string - and it'll all be stored in just three bytes. Clever, eh?
The program works by finding the larger numbers first. So, variable a and Z$(3) will hold the units of the number, b and Z$(2) will hold the '255's, and c and Z$(1) will hold the '65536's. The example program was written for my own specific purposes. I had four aircraft carriers, each with 100 bytes of data. As I wanted to allocate 175,000 gallons of fuel to the second carrier, the data is stored in three bytes in the field 60 TO 62 - thus, A$ represents the carriers and Z$ the temporary store for the data. In the example printout, you can see that the commands I need to put in a string are '?', ATTR and 'I'.
Number bases are a fairly complex idea, and I am not a mathematician, but the example here does work and can be amended to suit your own particular requirements.


Partly as a digression and also to get your grey matter on the boil, try changing line 95 to read 'STOP' in the decimal to base convertor, and enter the number 17000. No problem - apart from the fact that there's nothing on the screen. Ah, but being a clever YS reader you've probably guessed that CODE 16 is unprintable - try changing the program back the way it was and you'll get your error message.
What I've stumbled on to here is 'protection' - where the PAPER and INK colours are the same ... so nothing much shows up on-screen. Put line 95 back to 'STOP' and enter the number 1052688. The CHR$ would appear to be just visible ... the only problem is that we're seeing the CODE. Now try entering the numbers 1052689 and 1052690; the last number should really put the cat amongst the pigeons!
Next month, I'm going to be dealing with a number of small problems that some YS readers seem to be labouring with. But, in the meantime, if there's anything bugging you (literally!) then write to me, Ian Beardsmore, c/o Your Spectrum, 14 Rathbone Place, London W1P 1DE.
10 LET z$=CHR$ 65: LET y$=CHR$ 200
20 PRINT z$,y$
30 REM
40 LET z$=CHR$ 65 AND 66
50 PRINT z$
60 REM
70 LET z$=" THEN "
80 PRINT z$,: PRINT LEN z$
90 LET z$="* THEN *"
100 PRINT z$,: PRINT LEN z$
110 REM
120 LET z$=CHR$ 148: LET y$=CHR$ 69: LET x$=CHR$ 137: LET w$=CHR$ 10
130 PRINT z$;"..";y$,x$;"..";w$
140 REM
150 LET z$=CHR$ 20
160 PRINT z$
This program allows you to input anything from the Spectrum keyboard and output it as a string.
10 LET z$="!@£$567:£"
20 LET y$=" THEN NOT GO TO qweASD"
30 LET a=CODE z$(5)
40 LET b=CODE y$(2)
50 PRINT a,CHR$ a,b,CHR$ b
60 LET c=a+b
65 PRINT "
---- +          ---- +
70 PRINT c,CHR$ c
80 LET x$=CHR$ c
90 PRINT : PRINT x$,CODE x$;"..";LEN x$
110 LET d=CODE z$(1 TO 4)
120 PRINT d,CODE z$(1)
This routine takes two character strings, converts them to numeric data, adds them together and outputs the result as a character string.
4 DIM A$(4,100)
5 DIM Z$(3)
10 LET a=0
12 LET b=0
14 LET c=0
20 INPUT a
25 PRINT a
30 IF a>255 THEN LET b=INT (a/256)
40 IF b>255 THEN LET c=INT (b/256)
50 LET Z$(1)=CHR$ c
55 PRINT CHR$ c,c
60 LET b=INT (b-(c*256))
70 LET Z$(2)=CHR$ b
75 PRINT CHR$ b,b
80 LET a=INT (a-(b*256+c*65536))
85 LET Z$(3)=CHR$ a
90 PRINT CHR$ a,a
100 LET a$(2,60 TO 62)=Z$
110 PRINT a$(2,60 TO 62)
120 STOP
?                       2
ATTR                    171
i                       152

A decimal to base 255 converter; simply input the number you want converted and it prints out the relevant Spectrum characters you need in your character string. An easy way to store any number up to 16,777,215 in just three bytes!

Mike Leaman shows you how to structure your programs in YS MegaBasic. And, at last, there's an opportunity to be first in the queue for the program itself!
When a REPEAT command is executed, the value is removed from the stack. When a REPEAT command is processed, the line and statement number are placed on the stack (something similar to a procedure call). When UNTIL is executed, if the expression is true, then a value is removed from the stack and program execution continues as normal. But if the expression is false, the top value from the stack is copied and a jump is made to the correct statement.
REPEAT ... UNTIL loops and procedures can be nested together. There is more than enough room on the stack for 10 values, but if you try to place more than 10 values on the stack you'll get a 'PROC stack overflow' error message. Likewise removing a value from the stack when it's already empty produces a 'PROC stack underflow' error message.
There are three commands to alter the stack without causing a program jump: POP, PUSH and PCLEAR. POP is used to remove a value from the stack, PUSH specifies the statement and line numbers to be placed on the stack, and PCLEAR clears the stack; the command PCLEAR should always be used at the beginning of any programs involving the use of procedures or REPEAT ... UNTIL loops in order to prime the stack.


There are two commands that can affect program flow - these are MTASK and BRANCH. MTASK provides the Spectrum with a simple form of multi-tasking; it splits a program into two parts, executing a line from the first part and then a line from the second. This 'multi-tasking' continues until either one part of the program finishes or it's turned off. MTASK is always followed by a number which defines the start line of the second part of the program (the first part of the program is assumed to continue after the MTASK command). If you want to turn it all off, execute MTASK 0. Both sections of the program must use the same variables, but each can access different windows and character sets.
The second command, BRANCH, causes a jump to be made to a subroutine after a line has been finished. Bear in mind though that MTASK has priority over BRANCH, so if you want to multitask, don't try to branch as well. The number after the BRANCH command specifies the line number at which the subroutine starts; the subroutine is terminated by an ENDPROC statement. You'll find branching especially useful when debugging a program; take, for example, when a programmer wishes to monitor the value of a particular variable during the execution of a program, this is when a BRANCH subroutine can be used to display its value.
From looking around at other Basics for home computers - sort of sounding out the opposition, if you like - one aspect I was particularly keen to incorporate in YS MegaBasic was a means of helping users to structure their own programs. The 'experts' are constantly suggesting that you should structure your Basic for efficiency, but ZX Basic is hardly conducive to that!
However, I'm more than happy to report that a YS MegaBasic program can be written in structured sections - called 'procedures'. The main advantage is, of course, that each separate procedure can be tested on its own before it's added to the main program. Another useful aspect of working in procedures is that you can name each one; although you may understand each separate subroutine call in one of your own programs, it's not always true that it'll be a piece of cake for someone else who's attempting to debug it later. Using my system of structuring your programs each procedure has a name allocated to it - which should ideally describe its true purpose within the main program. Then, when a particular section is to be executed, all you have to do to bring the procedure into play is to use the name of the section in a program line - as opposed to using a GO SUB statement which doesn't give you any clues as to what's going on!


In YS Megabasic, the start of a procedure is defined by a '@' symbol along with its specific title - then, if required, this is followed by an underline character and a list of parameters. Note that all the procedure names are converted to upper case letters, and that any existing Basic commands can't be used to specify the procedures, so don't call a procedure 'PRINT' ... use a bit of imagination!
A number of other Basics which include the use of procedures also allow the use of 'local' variables. With that system, variables used within a procedure don't affect the value of the variable of the same name outside that procedure. I'm sorry to say this is not available with YS MegaBasic, so take care when choosing the names of each procedure's variables.
A procedure's end is defined by the command ENDPROC; when a program encounters the ENDPROC command, processing is continued from the statement after the procedure call. Note too that the procedure name can be tagged on to the end of the ENDPROC statement to aid the program's legibility, so 'ENDPROC_DISPLAY' would define the end of the procedure 'DISPLAY'.
I've already mentioned in passing that parameters can be passed to
procedures. This system of 'parameter passing' allows values to be assigned to variables at the beginning of a procedure for use in the rest of the procedure.


Yet another feature of YS MegaBasic is the inclusion of the REPEAT ... UNTIL structure. The REPEAT command marks the beginning of a loop and UNTIL signifies its end. The UNTIL command is always followed by an expression - if this expression is false then a jump is made to the statement after the last REPEAT command and if it's true, then program execution continues from the statement after the UNTIL command.
Both procedures and the REPEAT ... UNTIL commands use a stack to store line and statement numbers. So that when a procedure is called, the line and statement number of the command after the call is placed on the stack - then, when an ENDPROC command is executed, the value is removed from the stack.
9000 @DISPLAY_A$
9010 DIM Z$(64)
9020 LET Z=(64-LEN A$)/2
9030 LET Z$(Z TO )=A$
9040 PRINT 'CHR$ 1;Z$
This example program can be used to print a string on-screen in the centre of a line using the procedure 'DISPLAY_A$'. For example, 'DISPLAY_"YS MegaBasic"' is equivalent to the ZX Basic 'LET A$="YS MegaBasic": GO SUB 9010'. Which one would you prefer to use?
20 OUT 254,RND*7
This YS MegaBasic listing causes the border to flash until the 'Z' key is pressed.
10 OUT 254,RND*7
20 IF INKEY$<>"z" THEN GO TO 10
Now look at the bog-standard ZX Basic equivalent - frightening isn't it!
10 MTASK_9000
20 FONT_0
30 PRINT "Task 1 ";
40 GO TO 30
9000 CURRENT_0
9010 FONT_1
9020 PRINT "Task 2 ";
9030 GO TO 9020
This program causes 'Task 1' to be printed in window 2 using the standard Spectrum character set, and 'Task 2' to be printed in window 0 using the alternate character set.

The YS MegaBasic manual is on the Spot's Pourri page.

Home Contents KwikPik