This month our expert correspondent, Andrew Hewson, deals with some fairly straightforward software queries before attempting to explain something more complicated about machine code routines
BY ANSWERING a few fairly straightforward software questions and then finishing with a reasonably lengthy machine code routine for those who like that type of thing, I have tried this month to provide something of interest to everyone. Peter Bankes asks: Is it possible to poke the Spectrum to get caps lock?
The caps lock condition is stored in bit 4 of FLAGS2 at address 23658 in the system variables area. When bit 4 is set, all entries will appear in capitals. Hence a program can determine whether caps lock is set by checking the status of that bit and altering it if required.
The condition of bit 4 may be checked from Basic in a somewhat cumbersome fashion as illustrated by:
10 IF INT (PEEK 23658/8)=2*INT (INT(PEEK 23658/8)/2) THEN PRINT "CAPS LOCK NOT SET": GO TO 30 20 PRINT "CAPS LOCK SET" 30 STOP
To set bit 4 from Basic and hence turn on the caps lock enter
POKE 23658, PEEK 23658+8
To turn it off again, enterPOKE 23658, PEEK 23658-8
Alternatively you may wish to use the ROM routine which "toggles" the caps lock. The routine is located at address 4317 (10DD hexadecimal) and successive calls of the form
RAND USR 4317
turn the caps lock on and off.
Peter Hollis has another problem concerning the Spectrum. He writes: I have seen a number of program listings which have user-defined graphics characters embedded within them but with no accompanying explanation of how the characters are formed. I have no idea of how to reproduce them on my machine. Can you help?
The 21 user-defined graphics characters available on the Spectrum are accessed by entering SHIFT 9 followed by one of the 21 letters A to U followed by SHIFT 9. When the Spectrum is turned on the 21 characters are set to copies of the appropriate letter and so entering SHIFT 9 before and after pressing a letter key makes no apparent difference to what is displayed. Thus the command PRINT "A" causes a letter A to appear in the top left-hand corner of the screen, regardless of whether the SHIFT 9 is entered before and after the letter A.
If the user-defined graphics character assigned to the " A " key is redefined the new character will always subsequently be used. The new character will appear at the appropriate time when the program is executed and it will also appear in listings of the program, both on the display and on the Sinclair printer.
Hence there is a problem when programs are published, because the new characters are shown in the listing with no reference to the key to which they are assigned.
A possible solution is to study the listing to try to deduce which keys have been used. Usually the author includes a loop into his program of the form:
10 DATA 0,12,14,4,60,124,60,0 20 FOR I=0 TO 7 30 READ A 40 POKE USR "D"+I,A 50 NEXT I
Such a loop reads the eight values in turn which are used to define the shape of the new character, from the DATA statement into the variable A and then POKE them into the relevant locations in memory. In this case it is the graphics character assigned to the letter "D" which is being redefined.
If you can locate similar loops in the problem program you can identify which keys have been used. You can then RUN the program, BREAK into it, and then press the keys in turn preceded and followed by SHIFT 9 to discover which shape has been assigned to which key.
Unfortunately there are several ways of redefining graphics characters, that technique being one of the simplest. Therefore you may not be able to determine which keys have been used, in which case trial and error using the keyboard is your only remedy.
John Brookes bought his first home computer - a Spectrum - and read several books on the machine but he is having difficulty with the concept of an array. He writes: The books are generally easy to read but none of them explains arrays sufficiently clearly for us woodenheaded types. Can you help?
Most tasks performed by computers comprise reading information into memory, manipulating the information according to a program of stored instructions, and writing the information out of memory.
The information stored in memory must be organised in some convenient fashion so that the person who writes the program of instructions can do his job. Most high-level languages allow the programmer to declare variables of various kinds so that different types of information can be stored and treated in appropriate ways.
The ZX-81 and the Spectrum each allow six types of variables - numeric with a single character name; numeric with a multiple character name; control for a FOR-NEXT loop; string; string array; numeric array.
The first two types are identical in use, as they can each store only a single positive or negative number. Most programmers try to give a name to a variable which reminds them of the information it holds. For example, a bank account program might hold the current balance in a variable called BALANCE.
In many programs similar information is to be stored concurrently and in that situation the program is also likely to become unnecessarily cumbersome because the same operation must be performed on many variables, each with a different name and therefore requiring a separate piece of code.
The bank account program might be required to store the amount spent using each of 30 cheques in a cheque book. The amount spent using the first cheque could be stored in a variable called CHEQUE. The same variable could not be used to store the value of the second cheque because only one value can be stored in a numeric variable.
The act of entering the second value "over-writes" the first value, causing it to be lost, hence the programmer must think of a new name for the second cheque. Most programmers would run out of inspiration long before they had named all 30 cheques.
One way of naming all 30 which would not require too much effort would be to call them CHEQUE1, CHEQUE2, CHEQUE3. That is the idea of an array. Sinclair arrays are restricted to single-character names only but that is a small price to pay for the flexibility they provide.
Table one shows a simple cheque book program which uses an array, C, of length 30 to hold the value of each cheque as it is entered. The program also shows the use of another variable, I, to count through the array selecting each element of the array in turn. When all the cheques have been entered the program prints the value of each in turn.
Simon Smith has a more complex question concerning arrays. He writes: I have a ZX-81 and recently bought a 64K memory to replace the 16K RAM pack I had previously. I now want to enlarge the array in a program which indexes my record collection to make use of the extra memory space. Is there any way of doing so, short of copying the data to another array?
Defining a new array and copying the data across using a FOR-NEXT loop would certainly be the easiest way of dealing with the problem. The disadvantage is that the new array can occupy only the space which is not used by the old array; hence when the old array is deleted - by DIMensioning it to zero - the memory space it occupies remains unused. If the old array was 12K bytes long and a further 32K bytes of memory was added, the new array could occupy only 32K of the total of 44K bytes available.
There is no Basic command to make an array grow to fill the remaining memory space and so I have written the machine code routine listed in table two to do the job. The routine can be loaded into a REM statement forming the second line in a ZX-81 program using a hexadecimal loader. For example:
10 REM XA$ 20 REM AT LEAST 108 CHARACTERS 30 FOR I=16523 to 16630 40 INPUT Z$ 50 IF Z$="S" THEN STOP 60 PRINT Z$;" "; 70 POKE I,16*CODE Z$+CODE Z$(2)-476 80 NEXT I
To load the machine code routine, run the loader and enter each pair of hexadecimal codes in turn. Be very careful not to make mistakes because the program makes no error checks.
To use the routine to double the size of a string array called A$ make the first program line a REM statement containing an 'X' followed by the name of the array, i.e., A$ as in the loader. Then POKE the factor by which you want the array to grow into the first byte of the REM statement. In that case the array is to grow by a factor of two so you should enter:
Then call the machine code routine by entering:
RAND USR 16523
Both the total length of the array and the size of the first dimension will grow by the factor specified at address 16514.
The routine makes a number of error checks. It will detect if the named variable does not exist or is not an array. It also ensures that there is sufficient room in RAM to enlarge the array by the factor specified.
The routine makes use of two ROM routines. The first is LOOKVARS which is located at 4380 decimal 111C hexadecimal. That routine finds the location in the variables area of the variable pointed to by CH-ADD and returns the address in the HL register pair. The second ROM routine, MAKE SPACE, inserts BC bytes at the address pointed to by HL.
The operation of the routine can be checked by adding the following lines to the hexadecimal loader:
1000 DIM A$(2,3) 1010 LET A$(1)="ABC" 1020 LET A$(2)="DEF" 1030 GO SUB 2000 1040 POKE 16514,2 1050 RAND USR 16523 1060 GO SUB 2000 1070 STOP 2000 LET W=PEEK 16400+256*16401 2010 FOR I=W TO W+21 2020 PRINT I,PEEK I 2030 NEXT I 2040 PAUSE 32768 2050 CLS 2060 RETURN
Execute the test program by entering:
The program declares an array A$ and PRINTs the contents of the first 22 bytes of the variables area where the array is held. The user can note the contents of the display and compare it to the format of an array as shown on pages 173 and 174 of ZX-81 Basic Programming.
The program then doubles the size of the array and displays the first 22 bytes again. The user will see that the total length of the array has increased appropriately and that the size of the first dimension has doubled.
With a little care the routine can be adapted to run on the Spectrum. The ROM routines LOOKVARS and MAKE SPACE are located at 10418 and 5717 respectively - 28B2 and 1655 in hexadecimal. CH-ADD is at 23645 - 5C5D hexadecimal. It is also necessary to alter the addresses at which the scale factor and the name of the array are stored.