helpline |
Our expert Andrew Hewson offers some hints on storing and tells you how to create new user-defined graphics characters
ONE OF the many useful facilities present on the Spectrum but not on the ZX-81 is the extension to the SAVE command which enables the programmer to SAVE not only programs on cassette but also the screen display, machine code and Basic variables. Unfortunately the syntax of the SAVE command is not so sophisticated as it might be, so that it is possible to SAVE only one variable at a time. It is always possible to SAVE variables one after the other on the same cassette but, as Alex Randall points out, that is most inconvenient because the "Start tape, then press any key" message appears each time a new variable is to be SAVEd.
There are several ways round the problem but probably the simplest is to manipulate the variables area in RAM before SAVEing so that it appears to the relevant ROM routines to consist of one large string variable.
Load and RUN the Basic routine listed in table one and enter "HELPLINE" in response to the input request. The program illustrates the method used to store string variables in memory. The resulting display is shown in table two.
|
The program works by using the VARS system variable held at 23627 and 23628 to identify the beginning of the variables area in memory. Provided that the program is invoked by entering RUN, the string entered by the user, Z$, lies at the bottom of that area because it is the first variable assigned in the routine. Thus the FOR-NEXT loop PEEKS and PRINTS the memory locations which hold the contents of Z$.
It is clear that strings are stored in memory in straightforward fashion. The first byte of the appropriate area contains the character code of the letter which identifies the string, in this case Z. The next two bytes together specify the length of the string in the form:
String length = PEEK first byte + 256*PEEK second byte
In that case the string contains nine characters and so the first byte is 9 and the second is 0. Hence if we ensure that the first character in the variables area is a string, POKE the length of the entire variables area into the two locations which define the length of the string, and use the SAVE command to store the string on cassette, we will have succeeded in SAVEing all the current variables.
The program in table three demonstrates the technique. Line 10 and lines 110 to 160 are the functional part of the SAVEing routine and lines 210 to 240 are the functional part of the LOADing routine. I have included the remaining lines only to prove that the method works.
|
The new length for Z$ is calculated by subtracting the value in VARS from the address of the beginning of the Edit Line area, held in the system variable E_LINE at 23641 and 23642. The result is adjusted to take account of the byte occupied by the string identifier, the two bytes occupied by the string length, the byte containing 128 which marks the end of the variables area, and the six bytes which will be required for the variable L which does not exist at the time the calculation takes place.
A circuitous route via a temporary store - I have chosen to use the printer buffer - must be taken to POKE the result into the appropriate locations, because the routine causes all the Basic variables except Z$ to become temporarily inaccessible.
Spectrum Basic permits only numeric or string arrays to be SAVEd see page 208 of the Spectrum manual and so the syntax checker will not permit the entry of a line such as:
SAVE "ALLCHARS" DATA Z$
Hence the program SAVES Z$ as if it were a string array, i.e., in the form Z$(). It is surprising that the SAVE routine in the ROM does not halt with an error report when that line is encountered. The Spectrum distinguishes string arrays from strings in the variables area by adding 128 to the code for the identifying letter. For example, the code for the string Z$ is 90 and the code for the string array Z$() is 90+128 = 218.
So an inconvenient consequence of SAVEing Z$ as if it were an array is that the contents of the identifying byte is increased by 128. Line 220 in the LOADing routine corrects the value to 90 and lines 230 and 240 reset the length of Z$ to zero. The remainder of the program demonstrates that the data has all been recovered.
I seem often to receive a number of letters all on broadly the same topic. This month several readers have expressed interest in the use of graphics characters on the Spectrum. Emmanuel Willems wants to know: How can one redesign the Spectrum letters? whereas Garry Baker asks: How do you get more than 21 high-resolution graphics characters?
John Row has a specific application in mind. He writes: How can I call up as many as 200 Egyptian hieroglyphs? I might mention in passing that I never cease to be amazed at the uses to which the million or so Sinclair users are putting their machines. Egyptian hieroglyphs - whatever next?
There are two methods for creating new characters, apart from using the DRAW, PLOT and CIRCLE commands which are too slow and cumbersome for most purposes. The simplest is to make use of the user-defined graphics facility in which up to 21 new characters can be defined and assigned, one to each of the letter keys A to U.
The form of each new character is stored in eight bytes of the 168 bytes reserved for the purpose at the top of memory above RAMTOP. The character assigned to a given key can be obtained by pressing the graphics key CAPS SHIFT 9 - before and after pressing the letter key.
The method of encoding and decoding the eight bytes can be understood with the help of some knowledge of binary numbers. Every character in the Spectrum character set, and every new character created by the user, is defined relative to an eight-by-eight grid. Each element in the grid is called a pixel. Each pixel can be set to either the INK or the PAPER colour and it is the precise arrangement of INK- or PAPER-coloured pixels in the eight-by-eight grid which creates each character.
Each of the eight bytes devoted to a character defines the setting of one horizontal line of eight pixels using the following system. The contents of a byte, which necessarily lies in the range 0 to 255 in decimal, is read as an eight-digit binary number so that there is a one-to-one correspondence between pixels and binary digits. A binary number consists of zeros and ones only. All pixels for which the corresponding binary digit is zero are set to the PAPER colour, whereas all pixels for which the corresponding digit is one are set to the INK colour.
Very often the first and last bytes of the group of eight controlling a given character are zero. Those two bytes determine the top and bottom of the character respectively and a zero setting ensures that all the corresponding pixels are set to the PAPER colour; thus, when the character appears on the screen, it is well-separated from other items on the lines above and below.
For a similar reason each byte usually contains an even number which is also less than 128. As a result, all pixels at the right and left are also set to the PAPER colour, so that the character is distinguished easily from its fellows on either side.
When the Spectrum is first switched on the user-defined graphics characters are set to a copy of the capital letters on the corresponding key, it is a simple matter to alter the characters. Table five lists a Basic program which does the job. I have also listed in table six the numeric codes for the letters in the Greek alphabet.
|
The system is designed to provide a set of, at most, 21 new characters but additional sets can be defined by altering the UDG systems variable which is held at 23675 and 23676. The number in UDG is the address of the first byte of the first graphics character, i.e., the character assigned to the A key. When the Spectrum is switched on it is set to 32600 - 16K machine - or 65368 - 48K machine - thus reserving 168 bytes for the 21 characters between the UDG address and the top of RAM.
In principle, UDG can be changed to point to any address in RAM but the simplest approach is to reduce it by 168 for each additional character set required. It is also necessary to reduce RAMTOP by a similar amount so that the graphics characters do not interfere with the stack, thereby causing the machine to crash.
|
RAMTOP normally is set to one less than the value of UDG and the CLEAR instruction must be used to alter it. Thus the procedure to create space for one additional set of graphics characters on the 16K machine is to enter:
CLEAR 32431 POKE 23675,176 POKE 23676,126
The CLEAR command moves RAMTOP down to 32431 and the two POKES reset UDG to UDG =176 + 256*126 = 32432.
That leaves 32768 - 32432 = 336 bytes between the address pointed to by UDG and the top of RAM, which is sufficient space for two tables each 168 bytes long.
The user-defined graphics facility is flexible enough for most purposes, despite the limitation to 21 characters per set, but the user should also be aware of the technique for redefining the ordinary character set. A number of the programs on the market for the Spectrum make use of that facility, because it gives the program more style, including Star Trek by Silversoft, Timegate by Quicksilva and 3D Space Wars by - yes, you guessed it - Hewson Consultants.
|
There are 96 characters in the ordinary Spectrum character set. The set starts with character code 32 - the space or blank character - and ends with the copyright symbol = code 127. They are defined in an analogous fashion to the user-defined characters by a table which is held in ROM at address 15616. Each definition is held in eight bytes and so the table is 768 bytes long.
The address of the beginning of the table is 256 more than the value held in the CHARS system variable which is located at 23606 and 23607. Bearing in mind that the code of the first character in the table is 32 it can be seen that the address of the first of the eight bytes defining a given character is PEEK 23606 + 256*PEEK 23607 + 8*character code.
Creating a new character set from scratch is a complicated task because the shape of each letter or digit must be worked out in detail. The best technique is probably to move RAMTOP down by 768 bytes, copy the entire Sinclair character table into the area above RAMTOP, and then re-set CHARS to point to the new area. That is the function of the program in table seven. New characters can then be created as modifications of the Sinclair originals.