CHAPTER 10 Mathematical functions Summary PI, EXP, LN, SIN, COS, TAN, ASN, ACS, ATN This chapter deals with the mathematics that the ZX Spectrum can handle. Quite possibly you will never have to use any of this at all, so if you find it too heavy going, don't be afraid of skipping it. It covers the operation (raising to a power), the functions EXP and LN, and the trigonometrical functions SIN, COS, TAN and their inverses ASN, ACS, and ATN. ^ and EXP You can raise one number to the power of another - that means 'multiply the first number by itself the second number of times'. This is normally shown by writing the second number just above and to the right of the first number; but obviously this would be difficult on a computer so we use the symbol instead. For example, the powers of 2 are 21=2 22=2*2=4 (2 squared) 23=2*2*2=8 (2 cubed) 24=2*2*2*2=16 (2 to the power four) Thus at its most elementary level, 'ab' means 'a multiplied by itself b times', but obviously this only makes sense if b is a positive whole number. To find a definition that works for other values of b, we consider the rule a(b+c) = ab*ac (Notice that we give a higher priority than * and / so that when there are several operations in one expression, the s are evaluated before the s and /s. ) You should not need much convincing that this works when b and c are both positive whole numbers; but if we decide that we want it to work even when the y are not, then we find ourselves compelled to accept that a0 = 1 a(-b) = 1/ab a(1/b) = the bth root of a, which is to say, the number that you have to multiply by itself b times to get a and a(b*c)=(ab)c If you have never seen any of this before then don't try to remember it straight away; just remember that at (-1)=1/a and at (1/2)=SQR a and maybe when you are familiar with these the rest will begin to make sense. Experiment with all this by trying this program: 10 INPUT a,b,c 20 PRINT a(b+c),ab*ac 30 GO TO 14 Of course, if the rule we gave earlier is true, then each time round the two numbers that the computer prints out will be equal. (Note - because of the way the computer works out t, the number on the left - a in this case - must never be negative.) A rather typical example of what this function can be used for is that of compound interest. Suppose you keep some of your money in a building society and they give 15% interest per year. Then after one year you will have not just the 100% that you had anyway, but also the 15% interest that the building society have given you, making altogether 115% of what you had originally. To put it another way, you have multiplied your sum of money by 1.15, and this is true however much you had there in the first place. After another year, the same will have happened again, so that you will then have 1.15*1.15=1.1512=1 .3225 times your original sum of money. In general, after y years, you will have 1.15y times what you started out with. If you try this command FOR y=0 TO 198: PRINT y,10*1.15Y: NEXT y you will see that even starting off from just £10, it all mounts up quite quickly, and what is more, it gets faster and faster as time goes on. (Although even so, you might still find that it doesn't keep up with inflation.) This sort of behaviour, where after a fixed interval of time some quantity multiplies itself by a fixed proportion, is called exponential growth, and it is calculated by raising a fixed number to the power of the time. Suppose you did this: 10 DEF FN a(x)=ax Here, a is more or less fixed, by LET statements: its value will correspond to the interest rate, which changes only every so often. There is a certain value for a that makes the function FN a look especially pretty to the trained eye of a mathematician: and this value is called e. The ZX Spectrum has a function called EXP defined by EXP x=ex Unfortunately, e itself is not an especially pretty number: it is an infinite non-recurring decimal. You can see its first few decimal places by doing PRINT EXP 1 because EXP 1 = e1 = e. Of course, this is just an approximation. You can never write down e exactly. LN The inverse of an exponential function is a logarithmic function: the logarithm (to base a) of a number x is the power to which you have to raise a to get the number x, and it is written logax. Thus by definition alogax=x; and it is also true that log (ax)=x. You may well already know how to use base 10 logarithms for doing multiplications; these are called common logarithms. The ZX Spectrum has a function LN which calculates logarithms to the base e; these are called natural logarithms. To calculate logarithms to any other base, you must divide the natural logarithm by the natural logarithm of the base: logax = LN x/ LN a PI Given any circle, you can find its perimeter (the distance round its edge; often called its circumference) by multiplying its diameter (width) by a number called p. (p is a Greek p, and it is used because it stands for perimeter. Its name is pi.) Like e, p is an infinite non-recurring decimal; it starts off as 3.141592653589....The word PI on the Spectrum (extended mode, then M) is taken as standing for this number - try PRINT PI. SIN, COS and TAN; ASN, ACS and ATN The trigonometrical functions measure what happens when a point moves round a circle. Here is a circle of radius 1 (1 what? It doesn't matter, as long as we keep to the same unit all the way through. There is nothing to stop you inventing a new unit of your own for every circle that you happen to be interested in) and a point moving round it. The point started at the 3 o'clock position, and then moved round in an anti- clockwise direction. We have also drawn in two lines called axes through the centre of the circle. The one through 9 o'clock and 3 o'clock is called the x-axis, and the one through 6 o'clock and 12 o'clock is called the y-axis. To specify where the point is, you say how far it has moved round the circle from its 3 o'clock starting position: let us call this distance a. We know that the circumference of the circle is 2p (because its radius is 1 and its diameter is thus 2): so when it has moved a quarter of the way round the circle, a=p/2; when it has moved halfway round, a=p; and when it has moved the whole way round, a=2p. Given the curved distance round the edge, a, two other distances you might like to know are how far the point is to the right of the y-axis, and how far it is above the x-axis. These are called, respectively, the cosine and sine of a. The functions COS and SIN on the computer will calculate these. F9V£/(PBrush0 0 341 3298BM8>(UI8Q;%rrl Note that if the point goes to the left of the y-axis, then the cosine becomes negative; and if the point goes below the x- axis, the sine becomes negative. Another property is that once a has got up to 2p, the point is back where it started and the sine and cosine start taking the same values all over again : SIN (a+2*PI) = SIN a COS (a+2*PI) = COS a The tangent of a is defined to be the sine divided by the cosine; the corresponding function on the computer is called TAN. Sometimes we need to work these functions out in reverse, finding the value of a that has given sine, cosine or tangent. The functions to do this are called arcsine (ASN on the computer), arccosine (ACS) and arctangent (ATN). In the diagram of the point moving round the circle, look at the radius joining the centre to the point. You should be able to see that the distance we have called a, the distance that the point has moved round the edge of the circle, is a way of measuring the angle through which the radius has moved away from the x-axis. When a=p/2, the angle is 90 degrees; when a=p the angle is 180 degrees; and so round to when a=2p, and the angle is 360 degrees. You might just as well forget about degrees, and measure the angle in terms of a alone: we say then that we are measuring the angle in radians. Thus p/2 radians= 90 degrees and so on. You must always remember that on the ZX Spectrum SIN, COS and so on use radians and not degrees. To convert degrees to radians, divide by 180 and multiply by p; to convert back from radians to degrees, you divide by p and multiply by 180. CHAPTER 11 Random numbers Summary RANDOMIZE RND This chapter deals with the function RND and the keyword RANDOMIZE. They are both used in connection with random numbers, so you must be careful not to get them mixed up. They are both on the same key (T); RANDOMIZE has had to be abbreviated to RAND. In some ways RND is like a function: it does calculations and produces a result. It is unusual in that it does not need an argument. Each time you use it, its result is a new random number between 0 and 1. (Sometimes it can take the value 0, but never 1.) Try 10 PRINT RND 20 GO TO 10 to see how the answer varies. Can you detect any pattern7 You shouldn't be able to; 'random' means that there is no pattern. Actually, RND is not truly random, because it follows a fixed sequence of 65536 numbers. However, these are so thoroughly jumbled up that there are at least no obvious patterns and we say that RND is pseudo-random. RND gives a random number between 0 and 1, but you can easily get random numbers in other ranges. For instance, 5*RND is between 0 and 5, and 1.3+0.7*RND is between 1.3 and 2. To get whole numbers, use INT (remembering that INT always rounds down) as in 1 +INT (RND*6), which we shall use in a program to simulate dice. RND*6 is in the range 0 to 6, but since it never actually reach es 6, INT (RND*6) is 0,1,2,3,4 or 5. Here is the program: 10 REM dice throwing program 20 CLS 30 FOR n=1 TO 2 40 PRINT 1+1NT (RND*6);" "; 50 NEXT n 60 INPUT a$: GO TO 20 Press ENTER each time you want to throw the dice. The RANDOMIZE statement is used to make RND start off at a definite place in its sequence of numbers, as you can see with this program: 10 RANDOMIZE 1 20 FOR n=1 TO 5: PRINT RND ,: NEXT n 30 PRINT: GO TO 10 After each execution of RANDOMIZE 1, the RND sequence starts off again with 0.0022735596. You can use other numbers between 1 and 65535 in the RANDOMIZE statement to start the RND sequence off at different places. If you had a program with RND in it and it also had some mistakes that you had not found, then it would help to use RANDOMIZE like this so that the program behaved the same way each time you ran it. RANDOMIZE on its own (and RANDOMIZE 0 has the same effect) is different, because it really does randomize RND - you can see this in the next program. 10 RANDOMIZE 20 PRINT RND: GO TO 10 The sequence you get here is not very random, because RANDOMIZE uses the time since the computer was switched on. Since this has gone up by the same amount each time RANDOMIZE is executed, the next RND does more or less the same. You would get better randomness by replacing GO TO 10 by GO TO 20. Note: Most dialects of BASIC use RND and RANDOMIZE to produce random numbers, but not all use them in the same way. Here is a program to toss coins and count the numbers of heads and tails. 10 LET heads=0: LET tails=0 20 LET coin=lNT (RND*2) 30 IF coin=3 THEN LET heads=heads+1 40 IF coin=1 THEN LET tails=tails+1 50 PRINT heads;",";tails, 60 IF tails<>6 THEN PRINT heads/tails; 70 PRINT: GO TO 20 The ratio of heads to tails should become approximately 1 if you go on long enough, because in the long run you expect approximately equal numbers of heads and tails. Exercises 1. Test this rule: Suppose you choose a number between 1 and 872 and type RANDOMIZE your number Then the next value of RND will be (75*(your number+1) -1) /65536 2. (For mathematicians only.) Let p be a (large) prime, and let a be a primitive root modulo p. Then if bi is the residue of ai modulo p (l £ bi £ p- l ), the sequence bi -1 p-1 is a cyclical sequence of p-1 distinct numbers in the range 0 to 1 (excluding 1). By choosing a suitably, these can be made to look fairly random. 65537 is a Fermat prime, 2l6+1. Because the multiplicative group of non-zero residues modulo 65537 has a power of 2 as its order, a residue is a primitive root if and only if it is not a quadratic residue. Use Gauss' law of quadratic reciprocity to show that 75 is a primitive root modulo 65537. The ZX Spectrum uses p=65537 and a=75, and stores some bi-l in memory. RND entails replacing bi-1 in memory by bi+1 -1, and yielding the result (bi+1-l) (p-l). RANDOMIZE n (with 1£n£65535) makes bi equal to n+1. RND is approximately uniformly distributed over the range 0 to 1. CHAPTER 12 Arrays Summary Arrays (the way the ZX Spectrum handles string arrays is slightly non-standard). DIM . . . Suppose you have a list of numbers, for instance the marks of ten people in a class. To store them in the computer you could set up a single variable for each person. But you would find them very awkward. You might decide to call the variable Bloggs 1, Bloggs 2, and so on up to Bloggs 10, but the program to set up these ten numbers would be rather long and boring to type in. How much nicer it would be if you could type this: 5 REM this program will not work 10 FOR n=1 TO 10 20 READ Bloggs n 30 NEXT n 40 DATA 10,2,5,19,16,3,11,1,0,6 Well you can't. However, there is a mechanism by which you can apply this idea, and it uses arrays. An array is a set of variables, its elements, all with the same name , and distinguished only by a number (the subscript) written in brackets after the name. In our example the name could be b (like control variables of FOR - NEXT loops, the name of an array must be a single letter), and the ten variables would then be b(l), b(2), and so on up to b(l0). The elements of an array are called subscripted variables, as opposed to the simple variables that you are already familiar with. Before you can use an array, you must reserve some space for it inside the computer, and you do this using a DIM (for dimension) statement. DIM b(10) sets up an array called b with dimension 10 (i.e. there are 10 subscripted variables b(1),...,b(10)) and initializes the 10 values to 0. It also deletes any array called b that existed previously. (But not a simple variable. An array and a simple numerical variable with the same name can coexist, and there shouldn't be any confusion between them because the array variable always has a subscript). The subscript can be an arbitrary numerical expression, so now you can write 10 FOR n=1 TO 10 20 READ b(n) 30 NEXT n 40 DATA 10,2,5,19,16,3,11,1,0,6 You can also set up arrays with more than one dimension. In a two dimension al array you need two numbers to specify one of the elements - rather like the line and column numbers to specify a character position on the television screen - so it has the form of a table. Alternatively, if you imagine the line and column numbers (two dimensions) as referring to a printed page, you could have an extra dimension for the page numbers. Of course, we are talking about numeric arrays; so the elements would not be printed characters as in a book, but numbers. Think of the elements of a three-dimensional array v as being specified by v (page number, line number, column number). For example, to set up a two-dimensional array c with dimensions 3 and 6, you use a DIM statement DIM c(3,6) This then gives you 3*6=18 subscripted variables c(1 ,1),c(1 ,2), . . .,c(1,6) c(2, 1 ),c(2,2), . . .,c(2,6) c(3, 1 ),c(3,2), . . .,c(3,6) The same principle works for any number of dimensions. Although you can have a number and an array with the same name, you cannot have two arrays with the same name, even if they have different numbers of dimensions. There are also string arrays. The strings in an array differ from simple strings in that they are of fixed length and assignment to them is always Procrustean - chopped off or padded with spaces. Another way of thinking of them is as arrays (with one extra dimension) of single characters. The name of a string array is a single letter followed by $, and a string array and a simple string variable cannot have the same name (unlike the case for numbers). Suppose then, that you want an array a$ of five strings. You must decide how long these strings are to be - let us suppose that 10 characters each is long enough. You then say DIM a$(5,10) (type this in) This sets up a 5*10 array of characters, but you can also think of each row as being a string: a$(1)=a$(1,1) a$(1,2) . . . a$(1,10) a$(2)=a$(2,1) a$(2,2) . . . a$(2,10) : : : : a$(5)=a$(5,1) a$(5,2) . . . a$(5,10) If you give the same number of subscripts (two in this case) as there were dimensions in the DIM statement, then you get a single character; but if you miss the last one out, then you get a fixed length string. So, for instance, A$(2,7) is the 7th character in the string A$(2); using the slicing notation, we could also write this as A$(2)(7). Now type LET a$(2)="1234567890" and PRINT a$(2),a$(2,7) For the last subscript (the one you can miss out), you can also have a slicer, so that for instance a$(2,4 TO 8)=a$(2)(4 TO 8)="45678" Remember: In a string array, all the strings have the same, fixed length. The DIM statement has an extra number (the last one) to specify this length. When you write down a subscripted variable for a string array, you can put in an extra number, or a slicer, to correspond with the extra number in the DIM statement. You can have string arrays with no dimensions. Type DIM a$(10) and you will find that a$ behaves just like a string variable, except that it always has length 10, and assignment to it is always procrustean. Exercises 1. Use READ and DATA statements to set up an array m$ of twelve strings in which m$(n) is the name of the nth month. (Hint: the DIM statement will be DIM m$(12,9). Test it by printing out all the mS(n) (use a loop). Type PRINT "now is the month of ";m$(5);"ing";" when merry lads are playin g" What can you do about all those spaces? CHAPTER 13 Conditions Summary AND, OR NOT We saw in Chapter 3 how an IF statement takes the form IF condition THEN . . . The conditions there were the relations (=, <, >, <=, >= and <>), which compare two numbers or two strings. You can also combine several of these, using the logical operations, AND, OR and NOT. One relation AND another relation is true whenever both relations are true, so you could have a line like: IF a$="yes" AND x>0 THEN PRINT x in which x only gets printed if a$=''yes" and x>0. The BASIC here is so close to English that it hardly seems worth spelling out the:details. As in English, you can join lots of relations together with AND, and then the whole lot is true if all the individual relations are. One relation OR another is true whenever at least one of the two relations is true. (Remember that it is still true if both the relations are true; this is something that English doesn't always imply.) The NOT relationship turns things upside down. The NOT relation is true whenever the relation is false, and false whenever it is true! Logical expressions can be made with relations and AND, OR and NOT, just as numerical expressions can be made with numbers and +, - and so on; you can even put them in brackets if necessary. They have priorities in the same way as the usual operations +, -, *, / and , do: OR has the lowest priority, then AND, then NOT, then the relations, and the usual operations. NOT is really a function, with an argument and a result, but its priority is much lower than that of other functions. Therefore its argument does not need brackets unless it contains AND or OR (or both). NOT a=b means the same as NOT (a=b) (and the same as a<>b, of course). <> is the negation of = in the sense that it is true if, and only if, = is false. In other words, a<>b is the same as NOT a=b and also NOT a<>b is the same as a=b Persuade yourself that >= and <= are the negations of < and > respectively: thus you can always get rid of NOT from in front of a relation by changing the relation. Also, NOT (a first logical expression AND a second) is the same as NOT (the first) OR NOT (the second) and NOT (a first logical expression OR a second) is the same as NOT (the first) AND NOT (the second). Using this you can work NOTs through brackets until eventually they are all applied to relations, and then you can get rid of them. Logically speaking, NOT is unnecessary, although you might still find that using it makes a program clearer. The following section is quite complicated, and can be skipped by the faint hearted! Try PRINT 1=2,1 < >2 which you might expect to give a syntax error. In fact, as far as the computer is concerned, there is no such thing as a logical value: instead it uses ordinary numbers, subject to a few rules. (i) =, <, >, <=, >= and <> all give numeric results: 1 for true, and 0 for false. Thus the PRINT command above printed 0 for '1=2', which is false, and 1 for '1<>2', which is true. (ii) In IF condition THEN . . . the condition can be actually any numeric expression. If its value is 0, then it counts as false, and any other value including the value of 1 that a true relation gives) counts as true. Thus the IF statement means exactly the same as IF condition <>0 THEN . . . (iii) AND, OR and NOT are also number-valued operations. x AND y has the value {x if y is true (non-zero) {0 (false), if y is false (zero) x OR y has the value {1 (true), if y is true (non zero) {x, if y is false (zero) NOT x has the value {0 (false), if x is true (non-zero) {1 (true), if x is false (zero) (Notice that 'true' means 'non-zero' when we're checking a given value, but it means '1' when we're producing a new one.) Read through the chapter again in the light of this revelation, making sure that it all works. In the expressions x AND y, x OR y and NOT x, x and y will usually take the values 0 and 1 for false and true. Work out the ten different combinations (four for AND, four for OR and two for NOT) and check that they do what the chapter leads you to expect them to do. Try this program: 10 INPUT a 20 INPUT b 30 PRINT (a AND a>=b)+(b AND ab$ THEN LET c$=a$: LET a$=b$: LET b$=c$ 30 PRINT a$;" ";("<" AND a$B OR C nor is it IF A<>B OR A<>C CHAPTER 14 The Character Set Summary CODE, CHR$ POKE, PEEK USR BIN The letters, digits, punctuation marks and so on that can appear in strings are called characters, and they make up the alphabet, or character set, that the ZX Spectrum uses. Most of these characters are single symbols, but there are some more, called tokens, that represent whole words, such as PRINT, STOP , <> and so on. There are 256 characters, and each one has a code between 0 and 255. There is a complete list of them in Appendix A. To convert between codes and characters, there are two functions, CODE and CHR$. CODE is applied to a string, and gives the code of the first character in the string (or 0 if the string is empty). CHR$ is applied to a number, and gives the single character string whose code is that number. This program prints out the entire character set: 10 FOR a=32 TO 255: PRINT CHRS a;: NEXT a At the top you can see a space, 15 symbols and punctuation marks, the ten digits, seven more symbols, the capital letters, six more symbols, the lower case letters and five more symbols. These are all (except £ and ) taken from a widely-used set of characters known as ASCII (standing for American Standard Codes for Information Interchange); ASCII also assigns numeric codes to these characters, and these are the codes that the ZX Spectrum uses. The rest of the characters are not part of ASCII, and are peculiar to the ZX Spectrum. First amongst them are a space and 15 patterns of black and white blobs. These are called the graphics symbols and can be used for drawing pictures. You can enter these from the keyboard, using what is called graphics mode. If you press GRAPHICS (CAPS SHIFT with 9) then the cursor will change to G. Now the keys for the digits 1 to 8 will give the graphics symbols: on their own they give the symbols drawn on the keys; and with either shift pressed they give the same symbol but inverted, i.e. black becomes white, and vice versa. Regardless of shifts, digit 9 takes you back to normal (L) mode and digit 0 is DELETE. After the graphics symbols, you will see what appears to be another copy of the alphabet from A to U. These are characters that you can redefine yourself. although when the machine is first switched on they are set as letters - they are called user-defined graphics. You can type these in from the keyboard by going into graphics mode, and then using the letters keys from A to U. To define a new character for yourself, follow this recipe - it defines a character to show p. (i) Work out what the character looks like. Each character has an 8x8 square of dots, each of which can show either the paper colour or the ink colour (see the introductory booklet). You'd draw a diagram something like this, with black squares for the ink colour: We've left a 1 square margin round the edge because the other letters all have one (except for lower case letters with tails, where the tail goes right down to the bottom). (ii) Work out which user-defined graphic is to show n- let's say the one corresponding to P, so that if you press P in graphics mode you get p. (iii) Store the new pattern. Each user-defined graphic has its pattern stored as eight numbers, one for each row. You can write each of these numbers as BIN followed by eight 0's or 1 's - 0 for paper, 1 for ink - so that the eight numbers for our p character are BIN 00000000 BIN 00000000 BIN 00000010 BIN 00111100 BIN 01010100 BIN 00010100 BIN 00010100 BIN 00000000 (If you know about binary numbers, then it should help you to know that BIN is used to write a number in binary instead of the usual decimal.) These eight numbers are stored in memory, in eight places, each of which has an address. The address of the first byte, or group of eight digits, is USR "P" (P because that is what we chose in (ii)), that of the second is USR "P" +1, and so on up to the eighth, which has address USR "P"+7. USR here is a function to convert a string argument into the address of the first byte in memory for the corresponding user-defined graphic. The string argument must be a single character which can be either the user-defined graphic itself or the corresponding letter (in upper or lower case). There is another use for USR, when its argument is a number, which will be dealt with. Even if you don't understand this, the following program will do it for you : 10 FOR n=0 TO 7 20 INPUT row: POKE USR "P"+n,row 30 NEXT n It will stop for INPUT data eight times to allow you to type in the eight BIN numbers above - type them in the right order, starting with the top row. The POKE statement stores a number directly in memory location, bypassing the mechanisms normally used by the BASIC. The opposite of POKE is PEEK, and this allows us to look at the contents of a memory location although it does not actually alter the contents of that location. They will be dealt with properly in Chapter 24. After the user-defined graphics come the tokens. You will have noticed that we have not printed out the first 32 characters, with codes 0 to 31. These are control characters. They don't produce anything printable, but have some less tangible effect on the television, or they are used for controlling something other than the television, and the television prints ? to show that it doesn't understand them. They are described more fully in Appendix A. Three that the television uses are those with codes 6, 8 and 13; on the whole, CHR$ 8 is the only one you are likely to find useful. CHR$ 6 prints spaces in exactly the same way as a comma does in a PRINT statement for instance PRINT 1; CHR$ 6;2 does the same as PRINT 1,2 Obviously this is not a very clear way of using it. A more subtle way is to say LET a$="1"+CHR$ 6+"2" PRINT a$ CHR$ 8 is 'backspace': it moves the print position back one place - try PRINT "1234"; CHR$ 8;"5" which prints up 1235 CHR$ 13 is 'newline': it moves the print position on to the beginning of the next line. The television also uses those with codes 16 to 23; these are explained in Chapters 15 and 16. All the control characters are listed in Appendix A. Using the codes for the characters we can extend the concept of 'alphabetical ordering' to cover strings containing any characters, not just letters. If instead of thinking in terms of the usual alphabet of 26 letters we use the extended alphabet of 256 characters, in the same order as their codes, then the principle is exactly the same. For instance, these strings are in their ZX Spectrum alphabetical order. (Notice the rather odd feature that lower case letters come after all the capitals: so "a" comes after "Z"; also, spaces matter.) CHR$ 3+"ZOOLOGICAL GARDENS" CHR$ 8+"AARDVARK HUNTING" "AAAARGH!" "(Parenthetical remark)" "100" "129.95 inc. VAT" "AASVOGEL" "Aardvark" "PRINT" "Zoo" "[interpolationl" "aardvark" "aasvogel" "zoo" "zoology Here is the rule for finding out which order two strings come in. First, compare the first characters. If they are different, then one of them has its code less than the other, and the string it came from is the earlier (lesser) of the two strings. If they are the same, then go on to compare the next characters. If in this process one of the strings runs out before the other, then that string is the earlier, otherwise they must be equal. The relations =, <, >, <=, >= and <> are used for strings as well as for numbers: < means 'comes before' and > means 'comes after', so that "AA man"<"AARDVARK" "AARDVARK">"AA man" are both true. <= and >= work the same way as they do for numbers, so that "The same string"<="The same string" is true, but "The same string"<"The same string" is false. Experiment on all this using the program here, which inputs two strings and puts them in order. 10 INPUT "Type in two strings:", a$, b$ 20 IF a$>b$ THEN LET c$=a$: LET a$=b$: LET b$=c$ 30 PRINT a$;" "; 40 IF a$ STR$ VAL c$ THEN LET m$="Type it properly, as a number,": GO TO 30 That will fool them. After a few more days, however, one of them may discover that they can get round this by rubbing out the string quotes and typing in STR$ (2*3). To stop up this loophole, you can replace c$ in line 30 by LINE c$. CHAPTER 16 Colours Summary INK, PAPER, FLASH, BRIGHT, INVERSE, OVER BORDER Run this program: 10 FOR m=4 TO 1: BRIGHT m 20 FOR n=1 TO 14 30 FOR c=4 TO 7 40 PAPER c: PRINT " ";: REM 4 coloured spaces 50 NEXT c: NEXT n: NEXT m 60 FOR m=0 TO 1: BRIGHT m: PAPER 7 70 FOR c=0 TO 3 80 INK c: PRINT c;" "; 90 NEXT c: PAPER 0 100 FOR c=4 T0 7 110 INK c: PRINT c;" "; 120 NEXT c: NEXT m 130 PAPER 7: INK 0: BRIGHT 0 This shows the eight colours (including white and black) and the two levels of brightness that the ZX Spectrum can produce on a colour television. (If you r television is black and white, then you will just see various shades of grey.) Here is a list of them for reference; they are also written over the appropriate number keys. 0 - black 1 - blue 2 - red 3 - purple, or magenta 4 - green 5 - pale blue, technically called cyan 6 - yellow 7 - white On a black and white television, these numbers are in order of brightness. To use these colours properly, you need to understand a bit about how the picture is arranged. The picture is divided up into 768 (24 lines of 32) positions where characters can be printed, and each character is printed as an 8x8 square of dots like that below for a. This should remind you of the user-defined graphics in Chapter 14, where we had 0s for the white dots and 1s for the black dots. The character position also has associated with it two colours: the ink, or foreground colour, which is the colour for the black dots in our square, and the paper, or background colour, which is used for the white dots. To start off with, every position has black ink and white paper so writing appears as black on white. The character position also has a brightness (normal or extra bright) and something to say whether it flashes or not - flashing is done by swapping the ink and paper colours. This can all be coded into numbers, so a character position then has (i) an 8x8 square of 0s and 1 s to define the shape of the character, with 0 for paper and 1 for ink, (ii) ink and paper colours, each coded into a number between 0 and 7, (iii) a brightness - 0 for normal, 1 for extra bright and (iv) a flash number - 0 for steady, 1 for flashing. Note that since the ink and paper colours cover a whole character position, you cannot possibly have more than two colours in a given block of 64 dots. The same goes for the brightness and flash number: they refer to the whole character position, not individual dots. The colours, brightness and flash number at a given position are called attributes. When you print something on the screen, you change the dot pattern at that position; it is less obvious, but still true, that you also change the attributes at that position. To start off with you do not notice this because everything is printed with black ink on white paper (and normal brightness and no flashing), but you can vary this with the INK, PAPER, BRIGHT and FLASH statements. Try PAPER 5 and then print a few things: they will all appear on cyan paper, because as they are printed the paper colours at the positions they occupy are set to cyan (which has code 5). The others work the same way, so after PAPER number between 0 and 7 INK number between 0 and 7 BRIGHT 0 or 1 } Think of 0 as off or } and 1 as on FLASH 0 or 1 } any printing will set the corresponding attribute at all the character positions it uses. Try some of these out. You should now be able to see how the program at the beginning worked (remember that a space is a character that has INK and PAPER the same colour). There are some more numbers you can use in these statements that have less direct effects. 8 can be used in all four statements, and means 'transparent' in the sense that the old attribute shows through. Suppose, for instance, that you do PAPER 8 No character position will ever have its paper colour set to 8 because there is no such colour; what happens is that when a position is printed on, its paper colour is left the same as it was before. INK 8, BRIGHT 8 and FLASH 8 work the same way for the other attributes. 9 can be used only with PAPER and INK, and means 'contrast'. The colour (ink or paper) that you use it with is made to contrast with the other by being made white if the other is a dark colour (black, blue, red or magenta), and black if the other is a light colour (green, cyan, yellow or white). Try this by doing INK 9: FOR c=0 TO 7: PAPER c: PRINT c: NEXT c A more impressive display of its power is to run the program at the beginning to make coloured stripes, and then doing INK 9: PAPER 8: PRINT AT 0,8;: FOR n=l TO 1080: PRINT n;: NEXT n The ink colour here is always made to contrast with the old paper colour at each position. Colour television relies on the rather curious fact that the human eye can only really see three colours - the primary colours, blue, red and green. The other colours are mixtures of these. For instance, magenta is made by mixing blue with red - which is why its code, 3, is the sum of the codes for blue and red. To see how all eight colours fit together, imagine three rectangular spotlights, coloured blue, red and green, shining at not quite the same place on a piece of white paper in the dark. Where they overlap you will see mixtures of colours, as shown by this program (note that ink spaces are obtained by using either SHIFT with 8 when in G mode): 10 BORDER 0: PAPER 0: INK 7: CLS 20 FOR a=1 TO 6 30 PRINT TAB 6; INK 1; "nnnnnnnnnnnnnnnnnn": REM 18 ink squares 40 NEXT a