CHAPTER 17 Graphics Summary PLOT, DRAW, CIRCLE POINT pixels In this chapter we shall see how to draw pictures on the ZX Spectrum. The part of the screen you can use has 22 lines and 32 columns, making 22*32=704 character positions. As you may remember from Chapter 16, each of these character positions is made of an 8 by 8 square of dots, and these are called pixels (picture elements). A pixel is specified by two numbers, its coordinates. The first, its x coordinate, says how far it is across from the extreme left-hand column. (Remember, x is a cross), the second, its y coordinate, says how far it is up from the bottom (wise up). These coordinates are usually written as a pair in brackets, so (0,0), (255,0), (0,175) and (255,175) are the bottom left-, bottom right-, top left- and top right-corners. The statement PLOT x coordinate, y coordinate inks in the pixel with these coordinates, so this measles program 10 PLOT INT (RND*256), INT(RND*176): INPUT a$: GO TO 10 plots a random point each time you press ENTER. Here is a rather more interesting program. It plots a graph of the function SIN (a sine wave) for values between 0 and 2p. 10 FOR n=0 TO 255 20 PLOT n,88+80*SlN (n/128*PI) 30 NEXT n This next program plots a graph of SQR (part of a parabola) between 0 and 4 : 10 FOR n=0 TO 255 20 PLOT n,80*SQR (n/64) 30 NEXT n Notice that pixel coordinates are rather different from the line and column in an AT item. You may find the diagram in Chapter 15 useful when working out pixel coordinates and line and column numbers. To help you with your pictures, the computer will draw straight lines, circles and parts of circles for you, using the DRAW and CIRCLE statements. The statement DRAW to draw a straight line takes the form DRAW x,y The starting place of the line is the pixel where the last PLOT, DRAW or CIRCLE statement left off (this is called the PLOT position; RUN, CLEAR, CLS and NEW reset it to the bottom left hand corner, at (0,0)), and the finishing place is x pixels to the right of that and y pixels up. The DRAW statement on its own determines the length and direction of the line, but not its starting point. Experiment with a few PLOT and DRAW commands, for instance PLOT 0,100: DRAW 80,-35 PLOT 90,150: DRAW 80,-35 Notice that the numbers in a DRAW statement can be negative, although those in a PLOT statement can't. You can also plot and draw in colour, although you have to bear in mind that colours always cover the whole of a character position and cannot be specified for individual pixels. When a pixel is plotted, it is set to show the full ink colour, and the whole of the character position containing it is given the current ink colour. This program demonstrates this: 10 BORDER 0: PAPER 0: INK 7: CLS: REM black oue screen 20 LET x1=0: LET y1=0: REM start of line 30 LET c=1: REM for ink colour, starting blue 40 LET x2=lNT (RND*256): LET y2=lNT (RND*176): REM random finish of line 50 DRAW INK c;x2-x1,y2-y1 60 LET x1=x2: LET y1=y2: REM next line starts where last one finished 70 LET c=c+1: IF c=8 THEN LET c=1: REM new colour 80 GO TO 40 The lines seem to get broader as the program goes on, and this is because a line changes the colours of all the inked in pixels of all the character positions that it passes through. Note that you can embed PAPER, INK, FLASH, BRIGHT, INVERSE and OVER items in a PLOT or DRAW statement just as you could with PRINT and INPUT. They go between the key word and the coordinates, and are terminated by either semicolons or commas. An extra frill with DRAW is that you can use it to draw parts of circles instead of straight lines, by using an extra number to specify an angle to be turned through: the form is DRAW x,y,a x and y are used to specify the finishing point of the line just as before and a is the number of radians that it must turn through as it goes - if a is a positive it turns to the left, while if a is a negative it turns to the right. Another way of seeing a is as showing the fraction of a complete circle that will be drawn: a complete circle is 2n radians, so if a= p it will draw a semicircle, if a=0 5*p a quarter of a circle, and so on. For instance suppose a=p. Then whatever values x and y take, a semicircle will be drawn. Run 10 PLOT 100,100: DRAW 50,50, Pl The drawing starts off in a south-easterly direction, but by the time it stops it is going north-west: in between it has turned round through 180 degrees, or p radians (the value of a). Run the program several times, with Pl replaced by various other expressions e.g. -PI, PI/2, 3*PI/2, PI/4, 1,0. The last statement in this chapter is the CIRCLE statement, which draws an entire circle. You specify the coordinates of the centre and the radius of the circle using CIRCLE x coordinate, y coordinate, radius Just as with PLOT and DRAW, you can put the various sorts of colour items in at the beginning of a CIRCLE statement. The POINT function tells you whether a pixel is ink or paper colour. It has two arguments, the coordinates of the pixel (and they must be enclosed in brackets); and its result is 0 if the pixel is paper colour, 1 if it is ink colour. Try CLS: PRINT POINT (0,0): PLOT 0,0: PRINT POINT (0,0) Type PAPER 7: INK 0 and let us investigate how INVERSE and OVER work inside a PLOT statement. These two affect just the relevant pixel, and not the rest of the character positions. They are normally off (0) in a PLOT statement, so you only need to mention them to turn them on (1). Here is a list of the possibilities for reference: PLOT; - this is the usual form. It plots an ink dot, i.e. sets the pixel to show the ink colour. PLOT INVERSE 1; - this plots a dot of ink eradicator, i.e. it sets the pixel to show the paper colour. PLOT OVER 1; - this changes the pixel over from whatever it was before: so if it was ink colour it becomes paper colour, and vice versa. PLOT INVERSE 1; OVER 1; - this leaves the pixel exactly as it was before; but note that it also changes the PLOT position, so you might use it simply to do that. As another example of using the OVER statement fill the screen up with writing using black on white, and then type PLOT 0,0: DRAW OVER 1;255,175 This will draw a fairly decent line, even though it has gaps in it wherever it hits some writing. Now do exactly the same command again. The line will vanish without leaving any traces whatsoever. This is the great advantage of OVER 1. If you had drawn the line using PLOT 0,0: DRAW 255,175 and erased it using PLOT 0,0: DRAW INVERSE 1;255,175 then you would also have erased some of the writing. Now try PLOT 0,0: DRAW OVER 1;250,175 and try to undraw it by DRAW OVER 1;-250,-175 This doesn't quite work, because the pixels the line uses on the way back are not quite the same as the ones that it used on the way down. You must undraw a line in exactly the same direction as you drew it. One way to get unusual colours is to speckle two normal ones together in a single square, using a user-defined graphic. Run this program: 1000 FOR n=0 TO 6 STEP 2 1010 POKE USR "a"+n, BIN 01010101: POKE USR "a"+n+1, BIN 10101010 1020 NEXT n which gives the user-defined graphic corresponding to a chessboard pattern. If you print this character (graphics mode, then a) in red ink on yellow paper, you will find it gives a reasonably acceptable orange. Exercises 1. Play about with PAPER, INK, FLASH and BRIGHT items in a PLOT statement. These are the parts that affect the whole of the character position containing the pixel. Normally it is as though the PLOT statement had started off PLOT PAPER 8; FLASH 8; BRIGHT 8; . . . and only the ink colour of a character position is altered when something is plotted there, but you can change this if you want. Be especially careful when using colours with INVERSE 1. Because this sets the pixel to show the paper colour, but changes the ink colour and this might not be what you expect. 2. Try to draw circles using SIN and COS (if you have read Chapter 10, try to work out how). Run this: 10 FOR n=0 TO 2*PI STEP Pl /180 20 PLOT 100+80*COS n,87+80*SlN n 30 NEXT n 40 CIRCLE 150,87,80 You can see that the CIRCLE statement is much quicker, even if less accurate. 3. Try CIRCLE 100,87,80: DRAW 50,50 You can see from this that the CIRCLE statement leaves the PLOT position at a rather indeterminate place - it is always somewhere about half way up the right hand side of the circle. You will usually need to follow the CIRCLE statement with a PLOT statement before you do any more drawing. 4. Here is a program to draw the graph of almost any function. It first asks you for a number n; it will plot the values from -n to +n. It then asks you for the function itself, input as a string. The string should be an expression using x as the argument of the function. 10 PLOT 0,87: DRAW 255,0 20 PLOT 127,0: DRAW 0,175 30 INPUT s, e$ 35 LET t=0 40 FOR f=0 TO 255 50 LET x=(f-128)*s/128: LET y=VAL e$ 60 IF ABS y>87 THEN LET t=0: GO TO 100 70 IF NOT t THEN PLOT f,y+88: LET t=1: GO TO 100 80 DRAW 1,y-old y 100 LET old y=INT (y+.5) 110 NEXT f Run it and, as an example, type in 10 for the number n and 10*TAN x for the function. It will plot a graph of tan x as x ranges from -10 to +10. CHAPTER 18 Motion Summary PAUSE, INKEYS, PEEK Quite often you will want to make the program take a specified length of time, and for this you will find the PAUSE statement useful. PAUSE n stops computing and displays the picture for n frames of the television (at 50 frames per second in Europe or 60 in America). n can be up to 65535, which gives you just under 22 minutes; if n=0 then it means 'PAUSE for ever'. A pause can always be cut short by pressing a key (note that a CAPS SHlFTed space will cause a break as well). You have to press the key down after the pause has started. This program works the second hand of a clock 10 REM First we draw the clock face 20 FOR n=1 TO 12 30 PRINT AT 10-10*COS (n/6*PI),16+10*SIN (n/6*PI);n 40 NEXT n 50 REM Now we start the clock 60 FOR t=0 TO 200000: REM t is the time in seconds 70 LET a=t/30*PI: REM a is the angle of the second hand in radians 80 LET sx=80*SIN a: LET sy=80*COS a 200 PLOT 128,88: DRAW OVER 1;sx,sy: REM draw second hand 210 PAUSE 42 220 PLOT 128,88: DRAW OVER 1;sx,sy: REM erase second hand 400 NEXT t This clock will run down after about 55.5 hours because of line 60, but you can easily make it run longer. Note how the timing is controlled by line 210. You might expect PAUSE 50 to make it tick one a second, but the computing takes a bit of time as well and has to be allowed for. This is best done by trial and error, timing the computer clock against a real one, and adjusting line 210 until they agree. (You can't do this very accurately; an adjustment of one frame in one second is 2% or half an hour in a day.) There is a much more accurate way of measuring time. This uses the contents of certain memory locations. The data stored is retrieved by using PEEK. Chapter 25 explains what we're looking at in detail. The expression used is (65536*PEEK 23674+256*PEEK 23673+PEEK 23672)/50 This gives the number of seconds since the computer was turned on (up to about 3 days and 21 hours, when it goes back to 0). Here is a revised clock program to make use of this: 10 REM First we draw the clock face 20 FOR n=1 TO 12 30 PRINT AT 10-10*COS (n/6*PI),16+10*SIN (n/6*PI);n 40 NEXT n 50 DEF FN t()=lNT (65536*PEEK 23674+256*PEEK 23673+ PEEK 23672)/50): REM number of seconds since start 100 REM Now we start the clock 110 LET t1=FN t() 120 LET a=t1/30*PI: REM a is the angle of the second hand in radians 130 LET sx=72*SIN a: LET sy=72*COS a 140 PLOT 131,91: DRAW OVER 1;sx,sy: REM draw hand 200 LET t=FN t() 210 IF t<=t1 THEN GO TO 200: REM wait until time for next hand 220 PLOT 131,91: DRAW OVER 1;sx,sy: REM rub out old hand 230 LET t1=t: GO TO 120 The internal clock that this method uses should be accurate to about .01% as long as the computer is just running its program, or 10 seconds per day; but it stops temporarily whenever you do BEEP, or a cassette tape operation, or use the printer or any of the other extra pieces of equipment you can use with the computer. All these will make it lose time. The numbers PEEK 23674, PEEK 23673 and PEEK 23672 are held inside the computer and used for counting in 50ths of a second Each is between 0 and 255, and they gradually increase through all the numbers from 0 to 255; after 255 they drop straight back to 0. The one that increases most often is PEEK 23672. Every 1/50 second it increases by 1. When it is at 255, the next increase takes it to 0, and at the same time it nudges PEEK 23673 by up to 1. When (every 256/50 seconds) PEEK 23673 is nudged from 255 to 0, it in turn nudges PEEK 23674 up by 1. This should be enough to explain why the expression above works. Now, consider carefully: suppose our three numbers are 0 (for PEEK 23674), 255 (for PEEK 23673) and 255 (for PEEK 23672). This means that it is about 21 minutes after switch-on - our expression ought to yield (65536*0+256*255+255)/50= 1 31 0.7 But there is a hidden danger. The next time there is a 1/50 second count, the three numbers will change to 1, 0 and 0. Every so often, this will happen when you are half way through evaluating the expression: the computer would evaluate PEEK 23674 as 0, but then change the other two to 0 before it can peek them. The answer would then be (65536*0+256*0+0)l50=0 which is hopelessly wrong. A simple rule to avoid this problem is evaluate the expression twice in succession and take the larger answer. If you look carefully at the program above you can see that it does this implicitly. Here is a trick to apply the rule. Define functions 10 DEF FN m(x,y)=(x+y+ABS (x-y))/2: REM the larger of x and y 20 DEF FN u()=(65536*PEEK 23674+256*PEEK 23673+PEEK 23672)/50: REM time, may be wrong 30 DEF FN t()=FN m(FN u(), FN u()): REM time, right You can change the three counter numbers so that they give the real time instead of the time since the computer was switched on. For instance, to set the time at 10.00am, you work out that this is 10*60*60*50=1800000 fiftieths of a second, and that 1800000=65536*27+256*1 19+64 To set the three numbers to 27, 119 and 64, you do POKE 23674,27: POKE 23673,119: POKE 23672,64 In countries with mains frequencies of 60 Hertz these programs must replace '50' by '60' where appropriate. The function INKEY$ (which has no argument) reads the keyboard. If you are pressing exactly one key (or a SHIFT key and just one other key) then the result is the character that key gives in L mode; otherwise the result is the empty string. Try this program, which works like a typewriter. 10 IF INKEY$ <>"" THEN GO TO 10 20 IF INKEY$ ="" THEN GO TO 20 30 PRINT INKEYS; 40 GO TO 10 Here line 10 waits for you to lift your finger off the keyboard and line 20 waits for you to press a new key. Remember that unlike INPUT, INKEY$ doesn't wait for you. So you don't type ENTER, but on the other hand if you don't type anything at all then you've missed your chance. Exercises 1. What happens if you miss out line 10 in the typewriter program? 2. Another way of using INKEYS is in conjunction with PAUSE, as in this alter native typewriter program. 10 PAUSE 0 20 PRINT INKEY$; 30 GO TO 10 To make this work, why is it essential that a pause should not finish if it finds you already pressing a key when it starts? 3. Adapt the second hand program so that it also shows minute and hour hands, drawing them every minute. If you're feeling ambitious, arrange so that every quarter of an hour it puts on some kind of show - you could produce the Big Ben chimes with BEEP. (See next chapter.) 4. (For sadists.) Try this: 10 IF INKEY$ ="" THEN GO TO 10 20 PRINT AT 11,14;"0UCH!" 30 IF INKEYS <>"" THEN GO TO 30 40 PRINT AT 11,14;" " 50 GO TO 10 CHAPTER 19 BEEP Summary BEEP If you haven't already discovered that the ZX Spectrum has a loudspeaker built into it, read the Introductory booklet before carrying on. The loudspeaker is sounded by using the BEEP statement, BEEP duration, pitch where, as usual, 'duration' and 'pitch' represent any numerical expressions. The duration is given in seconds, and the pitch is given in semitones above middle C using negative numbers for notes below middle C. For example, type: 10 PRINT "Frere Gustav" 20 BEEP 1,0: BEEP 1,2: BEEP .5,3: BEEP.5,2: BEEP 1,0 30 BEEP 1,0: BEEP 1,2: BEEP .5,3: BEEP.5,2: BEEP 1,0 40 BEEP 1,3: BEEP 1,5: BEEP 2,7 50 BEEP 1,3: BEEP 1,5: BEEP 2,7 60 BEEP .75,7: BEEP .25,8: BEEP .5,7: BEEP .5,5:BEEP .5,3: BEEP.5,2: BEEP 1,0 70 BEEP .75,7: BEEP .25,8: BEEP .5,7: BEEP .5,5: BEEP .5,3: BEEP .5,2 : BEEP 1,0 80 BEEP 1,0: BEEP 1,-5: BEEP 2,0 90 BEEP l,0: BEEP 1,-5: BEEP 2,0 When you run this, you should get the funeral march from Mahler's first symphony, the bit where the goblins bury the US Cavalry man. If you want to change the key of the piece, the best thing is to set up a variable key and insert key+ before each pitch value: thus the second line becomes 20 BEEP 1,key+0: BEEP 1,key+2: BEEP s,key+3: BEEP.5,key+2: BEEP 1,key+0 Before you run a program you must give key the appropriate value - 0 for C minor, 2 for D minor, 12 for C minor an octave up, and so on. You can get the computer in tune with another instrument by adjusting key, using fractional values. You also have to work out the duration of all the notes. Since this is a fairly slow piece, we have allowed one second for a crotchet and based the rest on that, half a second for a quaver and so on. More flexible is to set up a variable crotchet to store the length of a crotchet and specify the duration in terms of this. Then line 20 would become 20 BEEP crotchet,key+0: BEEP crotchet,key+2: BEEP crotchet/2,key+3: BEEP crotchet/2,key+2: BEEP crotchet,key+0 (You will probably want to give crotchet and key shorter names.) By giving crotchet appropriate values, you can easily vary the speed of the piece. Remember that because there is only one loudspeaker in the computer you can only play one note at a time, so you are restricted to unharmonized tunes. If you want any more you must sing it yourself. Try programming tunes in for yourself - start off with fairly simple ones like 'Three Blind Mice'. If you have neither piano nor written music, get hold of a very simple instrument like a tin whistle or a recorder, and work the tunes out on that. You could make a chart showing the pitch value for each note that you can play on this instrument. Type: FOR n=0 TO 1000: BEEP .5,n: NEXT n This will play notes as high as it can, and then stop with error report B integer out of range. You can print out n to find out how high it did actually get. Try the same thing, but going down into the low notes. The very lowest notes will just sound like clicks; in fact the higher notes are also made of clicks in the same way, but faster, so that the ear cannot distinguish them. Only the middle range of notes are really any good for music; the low notes sound too much like clicks, and the high notes are thin and tend to warble a bit. Type in this program line: 10 BEEP .5,0: BEEP .5,2: BEEP .5,4: BEEP .5,5: BEEP .5,7: BEEP .5,9: BEEP .5,11: BEEP .5,12: STOP This plays the scale of C major, which uses all the white notes on the piano from middle C to the next C up. The way this scale is tuned is exactly the same as on a piano, and is called even-tempered tuning because the pitch interval of a semitone is the same all the way up the scale. A violinist, however , would play the scale very slightly differently, adjusting all the notes to make them sound more pleasing to the ear. He can do this just by moving his fingers very slightly up or down the string in a way that a pianist can't. The natural scale, which is what the violinist plays, comes out like this: 20 BEEP .5,0: BEEP .5,2.039: BEEP .5,3.86: BEEP .5,4.98: BEEP .5,7.02 : BEEP .5,8.84: BEEP .5,10.88: BEEP .5,12: STOP You may or may not be able to detect any difference between these two; some people can. The first noticeable difference is that the third note is slightly flatter in the naturally tempered scale. if you are a real perfectionist, you might like to program your tunes to use this natural scale instead of the even-tempered one. The disadvantage is that although it works perfectly in the key of C, in other keys it works less well - they all have their own natural scales - and in some keys it works very badly indeed. The even-tempered scale is only slightly off, and works equally well in all keys. This is less of a problem on the computer, of course, because you can use the trick of adding on a variable key. Some music - notably Indian music - uses intervals of pitch smaller than a semitone. You can program these into the BEEP statement without any trouble; for instance the quartertone above middle C has a pitch value of .5. You can make the keyboard beep instead of clicking by POKE 23609,255 The second number in this determines the length of the beep (try various values between 0 and 255). When it is 0, the beep is so short that it sounds like a soft click. If you are interested in doing more with sound from the Spectrum, like hearing the sound that BEEP makes on something other the internal speaker, you will find that the signal is present on both the 'MIC' and the 'EAR' sockets. It will be at a higher level on the 'EAR' socket, but otherwise they are the same. You may use this to connect an earphone or a pair of headphones to your Spectrum. This will not cut out the internal loudspeaker. If you are really keen to make a lot of noise you could connect it up to an amplifier - the 'MIC' socket will probably give about the right level - or you could record the sound onto tape and get the Spectrum to play along with itself . You will not damage the Spectrum even if you short-circuit the 'MIC' or 'EAR' sockets, so experiment to find which gives the best output for what you want to do. Exercise 1. Rewrite the Mahler program so that it uses FOR loops to repeat the bars. 2. Program the computer so that it plays not only the funeral march, but also the rest of Mahler's first symphony. CHAPTER 21 The ZX printer Summary LPRINT, LLIST, COPY Note: None of these statements is standard BASIC, although LPRINT is used by some other computers. If you have a ZX printer, you will have some operating instructions with it. This chapter covers the BASIC statements needed to make it work. The first two, LPRINT and LLIST, are just like PRINT and LIST, except that they use the printer instead of the television. (The L is an historical accident. When BASIC was invented it usually used an electric typewriter instead of a television, so PRINT really did mean print. If you wanted masses of output you would use a very fast line printer attached to the computer, and an LPRINT statement meaning 'Line printer PRINT'.) Try this program for example. 10 LPRINT "This program".' 20 LLIST 30 LPRINT '"prints out the character set."' 40 FOR n=32 TO 255 50 LPRINT CHR$ n; 60 NEXT n The third statement, COPY, prints out a copy of the television screen. For instance, type LIST to get a listing on the screen of the program above, and type COPY Note that COPY doesn't work with one of the listings that the computer puts up automatically, because that is cleared whenever a command is obeyed. You must either use LIST first, or use LLIST and forget about COPY. You can always stop the printer when it is running by pressing the BREAK key (CAPS SHIFT and SPACE). If you execute these statements without the printer attached, it should lose all the output and carry on with the next statement. Try this: 10 FOR n=31 TO 3 STEP-1 20 PRINT AT 31-n,n; CHRS (CODE "3"+n); 30 NEXT n You will see a pattern of characters working down diagonally from the top right-hand corner until it reaches the bottom of the screen, when the program asks if you want to scroll. Now change AT 31-n,n in line 20 to TAB n. The program will have exactly the same effect as before. Now change PRINT in line 20 to LPRINT. This time there will be no scroll?, which should not occur with the printer, and the pattern will carry straight on with the letters F to O. Now change TAB n to AT 31-n,n still using LPRINT. This time you will get just a single line of symbols. The reason for the difference is that the output from PRINT is not printed straight away, but arranges in a buffer store a picture one line long of what the computer will send to the printer when it gets round to it. The printing takes place (i) when the buffer is full, (ii) after an LPRINT statement that does not end in a comma or semicolon, (iii) when a comma, apostrophe or TAB item requires a new line, or (iv) at the end of a program, if there is anything left unprinted. (iii) explains why our program with TAB works the way it does. As for AT, the line number is ignored and the LPRINT position (like the PRINT position, but for the printer instead of the television) is changed to the column number. An AT item can never cause a line to be sent to the printer. Exercise 1 . Make a printed graph of SIN by running the program in Chapter 17 and then using COPY. CHAPTER 22 Other Equipment There is other equipment that you will be able to attach to the Spectrum. The ZX Microdrive is a high speed mass storage device, and is much more flexible in the way it can be used than a cassette recorder. It will operate not only with SAVE, VERIFY, LOAD and MERGE, but also with PRINT, LIST, INPUT and INKEYS. The network is used for connecting several Spectrums so that they can talk to each other - one of the uses of this is that you then need only one Microdrive to serve several computers. The RS232 interface is a standard connection that allows you to link a Spectrum with keyboards, printers, computers and various other machines even if they were not designed specifically for the Spectrum. These will use some extra keywords that are on the keyboard, but cannot be used without the extra attachments: they are OPEN#, CLOSE#, MOVE, ERASE, CAT and FORMAT. CHAPTER 23 IN and OUT Summary OUT IN The processor can read from and (at least with RAM) write to memory by using PEEK and POKE. The processor itself does not really care whether memory is ROM, RAM or even nothing at all; it just knows that there are 65536 memory addresses, and it can read a byte from each one (even if it's nonsense), and write a byte to each one (even if it gets lost). In a completely analogous way there are 65536 of what are called I/O ports (standing for Input/Output ports). These are used by the processor for communicating with things like the keyboard or the printer, and they can be controlled from the BASIC by using the IN function and the OUT statement. IN is a function like PEEK. IN address It has one argument, the port address, and its result is a byte read from that port. OUT is a statement like POKE. OUT address, value writes the given value to the port with the given address. How the address is interpreted depends very much on the rest of the computer; quite often, many different addresses will mean the same. On the Spectrum it is most sensible to imagine the address being written in binary, because the individual bits tend to work independently. There are 16 bits, which we shall call (using A for address) A15, A14, A13, A12, . . . . . . , A2, Al, A0 Here A0 is the 1 s bit, Al the 2s bit, A2 the 4s bit and so on. Bits A0, Al, A2, A3 and A4 are the important ones. They are normally 1, but if any one of them is 0 this tells the computer to do something specific. The computer cannot cope with more than one thing at a time, so no more than one of these five bits should be 0. Bits A6 and A7 are ignored, so if you are a wizard with electronics you can use them yourself. The best addresses to use are those that are 1 less than a multiple of 32, so that A0,...A4 are all 1. Bits A8, A9 and so on are sometimes used to give extra information. The byte read or written has 8 bits, and these are often referred to (using D for data) as D7, D6,...., Dl, D0. Here is a list of the port addresses used. There is a set of input addresses that read the keyboard and also the EAR socket. The keyboard is divided up into 8 half rows of 5 keys each. IN 65278 reads the half row CAPS SHIFT to V IN 65022 reads the half row A to G IN 64510 reads the half row Q to T IN 63486 reads the half row 1 to 5 IN 61438 reads the half row O to 6 IN 57342 reads the half row P to 7 IN 49150 reads the half row ENTER to H IN 32766 reads the half row SPACE to B (These addresses are 254+256*(255-21 n) as n goes from 0 to 7.) In the byte read in, bits D0 to D4 stand for the five keys in the given half row - D0 for the outside key, D4 for the one nearest the middle. The bit is 0 if the key is pressed, 1 if it is not. D6 is the value at the EAR socket. Port address 254 in output drives the loudspeaker (C4) and the MIC socket (D3), and also sets the border colour (D2, D1 and D0). Port address 251 runs the printer, both in reading and writing: reading finds out whether the printer is ready for more, and writing sends out dots to be printed. Port addresses 254, 247 and 239 are used for the extra devices mentioned in Chapter 22. Run this program 10 FOR n=0 TO 7: REM half-row number 20 LET a=254+256*(255-2 n) 30 PRINT AT 0,0; IN a: GO TO 30 and play around by pressing keys. When you get bored with each half-row, press BREAK and then type NEXT n The control, data and address busses are all exposed at the back of the Spectrum, so you can do almost anything with a Spectrum that you can with a Z80. Sometimes, though, the Spectrum hardware might get in the way. CHAPTER 24 The memory Summary CLEAR Deep inside the computer, everything is stored as bytes, i.e. numbers between 0 and 255. You may think you have stored away the price of wool or the address of your fertilizer suppliers, but it has all been converted into collections of bytes and bytes are what the computer sees. Each place where a byte can be stored has an address, which is a number between 0 and FFFFh (so an address can be stored as two bytes), so you might think of the memory as a long row of numbered boxes, each of which can contain a byte. Not all the boxes are the same, however. In tile standard 16K RAM machine, the boxes from 8000h to FFFFh are simply missing altogether. The boxes from 4000h to 7FFFh are RAM boxes, which means you can open the lid and alter the contents, and those from 0 to 3FFFh are ROM boxes, which have glass tops but cannot be opened. You just have to read whatever was put in them when the computer was made. 10 PRINT "Address"; TAB 8; "Byte" 20 FOR a =0 TO 20 30 PRINT a; TAB 8; PEEK a 40 NEXT a All these bytes will probably be quite meaningless to you, but the processor chip understands them to be instructions telling it what to do. To change the contents of a box (if it is RAM), we use the POKE statement. It has the form POKE address, new contents where 'address' and 'new contents' stand for numeric expressions. For instance, if you say POKE 31000,57 the byte at address 31000 is given the new value 57 - type PRINT PEEK 31000 to prove this. (Try poking in other values, to show that there is no cheating.) The new value must be between -255 and +255, and if it is negative then 256 is added to it The ability to poke gives you immense power over the computer if you know how to wield it; and immense destructive possibilities if you don't. It is very easy, by poking the wrong value in the wrong address, to lose vast programs that took you hours to type in. Fortunately, you won't do the computer any permanent damage. We shall now take a more detailed look at how the RAM is used but don't bother to read this unless you're interested. The memory is divided into different areas (shown on the big diagram) for storing different kinds of information. The areas are only large enough for the information that they actually contain, and if you insert some more at a given point (for instance by adding a program line or variable) space is made by shifting up everything above that point. Conversely, if you delete information then everything is shifted down. The display file stores the television picture. It is rather curiously laid out, so you probably won't want to PEEK or POKE in it. Each character position on the screen has an 8x8 square of dots, and each dot can be either 0 (paper) or 1 (ink) and by using binary notation we can store the pattern as 8 bytes, one for each row. However, these 8 bytes are not stored together. The corresponding rows in the 32 characters of a single line are stored together as a scan of 32 bytes, because this is what the electron beam in the television needs as it scans from the left hand side of the screen to the other. Since the complete picture has 24 lines of 8 scans each, you might expect the total of 172 scans to be stored in order, one after the other; you'd be wrong. First come the top scans of lines 0 to 7, then the next scans of lines 0 to 7, and so on to the bottom scans of lines 0 to 7; then the same for lines 8 to 15; and then the same for lines 16 to 23. The upshot of all this is that if you' re used to a computer that uses PEEK and POKE on the screen, you'll have to start using SCREEN$ and PRINT AT instead, or PLOT and POINT. The attributes are the colours and so on for each character position, using the format of ATTR. These are stored line by line in the order you'd expect. The printer buffer stores the characters destined for the printer. The system variables contain various pieces of information that tell the computer what sort of state the computer is in. They are listed fully in the next chapter, but for the moment note that there are some (called CHANS, PROG, VARS, E LINE and so on) that contain the addresses of the boundaries between the various areas in memory. These are not BASIC variables, and their names will not be recognized by the computer. The calculator is the part of the BASIC system that deals with arithmetic, and the numbers on which it is operating are held mostly in the calculator stack. The spare part contains the space so far unused. The machine stack is the stack used by the Z80 processor to hold return addresses and so on. The GOSUB stack was mentioned in Chapter 5. The byte pointed to by RAMTOP has the highest address used by the BASIC system. Even NEW, which clears the RAM out, only does so as far as this so it doesn't change the user defined graphics. You can change the address RAMTOP by putting a number in a clear statement: CLEAR new RAMTOP This (i) clears out all the variables (ii) clears the display file (like CLS) (iii) resets the PLOT position to the bottom left hand corner (iv) does RESTORE (v) clears the GOSUB stack and puts it at the new RAMTOP assuming that this lies between the calculator stack and the physical end of RAM; otherwise it leaves RAMTOP as it was. RUN also does CLEAR, although it never changes RAMTOP. Using CLEAR in this way, you can either move RAMTOP up to make more room for the BASIC by overwriting the user defined graphics, or you can move it down to make more RAM that is preserved from NEW. Type NEW, then CLEAR 23800 to get some idea of what happens to the machine when it fills up. One of the first things you will notice if you start typing in a program is that after a while the computer stops accepting any more and buzzes at you. It means the computer is chock a block and you will have to empty it slightly. There are also two error messages with roughly the same meaning, 4 Memory full and G No room for line. The buzz also occurs when you type in a line longer than 23 lines then your typing is not being ignored, though you cannot see it; but the buzz sounds to discourage you from doing any more. You can adjust the length of the buzz by poking a number into address 23608. The usual length has number 64. Any number (except 0) can be written uniquely as + m x 2e where + is the sign, m is the mantissa, and lies between and 1 (it cannot be 1), and e is the exponent, a whole number (possibly negative). Suppose you write m in the binary scale. Because it is a fraction, it will have a binary point (like the decimal point in the scale of ten) and then a binary fraction (like a decimal fraction); so in binary: a half is written .1 a quarter is written .01 three quarters is written .11 a tenth is written .000110011001100110011 ... and so on. With our number m, because it is less than 1, there are no bits before the binary point, and because it is at least , the bit immediately after the binary point is a 1. To store the number in the computer, we use five bytes, as follows: (i) write the first eight bits of the mantissa in the second byte (we know that the first bit is 1), the second eight bits in the third byte, the third eight bits in the fourth byte and the fourth eight bits in the fifth byte; (ii) replace the first bit in the second byte which we know is 1 by the sign: 0 for plus, 1 for minus; (iii) write the exponent +128 in the first byte. For instance, suppose our number is 1/10 1/10 =4/5x2-3 Thus the mantissa m is .11001100110011001100110011001100 in binary (since the 33rd bit is 1, we shall round the 32nd up from 0 to 1), and the exponent e is 3. There is an alternative way of storing whole numbers between 65535 and +65 535: (i) the first byte is 0. (ii) the second byte is 0 for a positive number, FFh for a negative one, (iii) the third and fourth bytes are the less and more significant bytes of the number (or the number +131072 if it is negative), (iv) the fifth byte is 0. CHAPTER 25 The system variables The bytes in memory from 23552 to 23733 are set aside for specific uses by the system. You can peek them to find out various things about the system, and some of them can be usefully poked. They are listed here with their uses. These are called system variables, and have names, but do not confuse them with the variables used by the BASIC. The computer will not recognize the names as referring to system variables, and they are given solely as mnemonics for we humans . The abbreviations in column 1 have the following meanings: X The variables should not be poked because the system might crash. N Poking the variable will have no lasting effect The number in column 1 is the number of bytes in the variable. For two bytes, the first one is the less significant byte the reverse of what you might expect. So to poke a value v to a two byte variable at address n, use POKE n,v-256*1NT (v/256) POKE n+1,lNT (v/256) and to peek its value, use the expression PEEK n+256*PEEK (n+1) Notes Address Name Contents N8 23552 KSTATE Used in reading the keyboard. Nl 23560 LAST K Stores newly pressed key. 1 23561 REPDEL Time (in 50ths of a second in 60ths of a second in N. America) that a key must be held down before it repeats. This starts off at 35, but you can POKE in other values. 1 23562 REPPER Delay (in 50ths of a second in 60ths of a second in N. America) between successive repeats of a key held down: initially 5. N2 23563 DEFADD Address of arguments of user defined function if one is being evaluated; otherwise 0. Nl 23565 K DATA Stores 2nd byte of colour controls entered from keyboard . N2 23566 TVDATA Stores bytes of coiour, AT and TAB controls going to television. X38 23568 STRMS Addresses of channels attached to streams. 2 23606 CHARS 256 less than address of character set (which starts with space and carries on to the copyright symbol). Normally in ROM, but you can set up your own in RAM and make CHARS point to it. 1 23608 RASP Length of warning buzz. 1 23609 PIP Length of keyboard click. 1 23610 ERR NR 1 less than the report code. Starts off at 25 5 (for 1) so PEEK 23610 gives 255. X1 23611 FLAGS Various flags to control the BASIC system. X1 23612 TV FLAG Flags associated with the television. X2 23613 ERR SP Address of item on machine stack to be used as error return. N2 23615 LIST SP Address of return address from automatic listing. N1 23617 MODE Specifies K, L, C. E or G cursor. 2 23618 NEWPPC Line to be jumped to. 23620 NSPPC Statement number in line to be jumped to. Poking first NEWPPC and then NSPPC forces a jump to a specified statement in a line. 2 23621 PPC Line number of statement currently being executed. 1 23623 SUBPPC Number within line of statement being executed. 1 23624 BORDCR Border colour * 8; also contains the attributes normally used for the lower half of the screen. 2 23625 E PPC Number of curren