Your Spectrum
Issue 3, May 1984 - System Variables
Home Contents KwikPik

See also the first half of this article:
Issue 2"Variables on a Theme"

[There were a number of typographical errors in this article. I've corrected all the ones which I spotted.]

big title Continuing on from last issue, Dilwyn Jones completes his investigation of the Spectrum ROM's system variables- those useful housekeeping routines which give the Spectrum its character. And if you fancy delving deeper into your machine, try some of the suggestions Dilwyn has to offer ...

In the last issue of Your Spectrum, an investigation was made of the system variables, 23552 through to 23634. Here we continue with a study of the remaining system variables, 23635 to 23689.
For the uninitiated, the system variables are bytes in the
Spectrum ROM which tell the computer the things it needs to know to act in the way you've come to know and love. Information, such as how the memory map is laid out, is held in the system variables so that the computer can access it and update it as and when required.

The address of the start of the area in memory where the Basic program is stored. This points to the first byte of the line number of the first program line. This may be useful if you're converting programs for other Sinclair Research computers with information held as a REM statement in the first line of a program. See also under VARS above.
If you wish to 'security lock' a line into a program, then by means of this
system variable you could POKE a zero into both bytes of a line number at the start of a program. Program lines start with a two-byte line number.

The address of the start of the next program line. You could use this to enable you to access machine code stored within REM statements anywhere in the program, eg. those loaded with MERGE from a tape library of subroutines. These would have their own local calls to machine code like this:
9000 LET A=USR (PEEK 23637 + 256*PEEK 23638+5)

One constraint to this is that you should not include any colour, flash, brightness, etc, control characters into the REM statement or they may be interpreted as machine code, upsetting
things somewhat. However, if used from a library of subroutines, these would not normally be used anyway.

This contains the address of the comma ending the last item of data. If nothing was read from the list (eg. after RUN or restored, etc) the address held in 23639/40 is the address of the byte before the program area, normally the CHR$ 128 at the end of the channel information area. To demonstrate try RUNing this program:
10 DATA "1","2","3","4","5"
20 LET A=PEEK 23639 + 256*PEEK 23640
40 READ B$
50 GO TO 20

23754 128
23763 44 ,
23767 44 ,
23771 44 ,
23775 44 ,
23779 13

The address in this two-byte system variable can point to the Enter character or the colon signifying the end of the line or statement containing the data - the address of the terminator of the last item of data.

This system variable points to the start of the area above the variables. From this we can gain an idea of how much memory is used in bytes by screen, system variables, program and variables, once the program has been RUN to set up the variables, etc. Type this in, as a direct command:
PRINT PEEK 23641 + 256*PEEK 23642-16384

We can also tell how much room is used for variables once the program
has been RUN to set up the variables. Use the command:
PRINT PEEK 23641 + 256*PEEK 23642 - PEEK 23627 - 256*PEEK 23628

This system variable contains the address of where the spare part of memory starts. From reading this we can gain an idea of how much memory we have left by subtracting it from RAMtop. This will not include memory used for the machine GOSUB stacks but includes the length of the PEEK statement. So this is only a fairly accurate guide ... but one which is adequate from most circumstances.
PRINT PEEK 23730 + 256*PEEK 23731 - PEEK 23653 - 256*PEEK 23654

This system variable contains some flags used (normally) by the computer to indicate certain conditions.
The best use we can make of this is to utilise the flag indicated by bit 3. This being a one indicates Caps Lock on or Caps Lock engaged.
What use is that? Consider in a program using INKEY$; eg. in a menu of options in a filing program,
we often need to know whether the operator is pressing a certain key. If the operator is invited to press 'Y' for Yes or 'N' for No, they may press 'y' for Yes or 'n' for No - mixing up lower case and upper case capitals. Most often this would depend on whether Caps Lock was engaged - people are not interested in upper or lower case and whether they press 'y' or 'Y' they expect the computer to understand as humans would. But the computer doesn't really appreciate that. So if we engage Caps Lock ,automatically, our worries are over and we have a simpler program which doesn't have to check (as far as it's concerned) two separate options for each choice.
It is tempting to use the Basic

small title
  statement POKE 23658,8 to engage Caps Lock and POKE 23658,0 to dis- engage it. But this will affect the other flags, so do check their state first unless you know they are not any particular value. Normally in L mode, 23658 has a value of zero so it is generally OK to use the POKEs above. You are not likely to cause crashes, but some funny effects may be caused in rare cases. When the printer buffer is empty, bit 1 will be zero.

This system variable contains the number of lines in the lower section of the screen, normally used for INPUTs, error reports and so on. Normally this would be a two, except for when a long INPUT prompt is displayed, etc. If a value of zero is POKEd in, normally to attempt to clear this unused part so that we can use the whole 24 lines of the screen, the computer crashes.
However, this can be done within a few restrictions. These are that we must ensure the lower part of the screen is restored to normal before any use is made of this - so to break out of a program would be somewhat catastrophic! Also, errors generated within the course of a program will have the same effect since the error report would have to be printed out.
Here is a short listing to demonstrate the use of line 22 and 23 on the screen. Unfortunately, it only works for PRINT or PRINT TAB as we cannot use PLOT down here and PRINT AT will only work down to line 22. The screen is restored to normal by POKE 23659,2 within the program.
10 POKE 23659,0
20 FOR A=0 TO 23
50 PAUSE 0
60 POKE 23659,2

To demonstrate what can go wrong, let us generate an error by adding this line to the program:
45 PRINT error
Oops!!! If you just want to PRINT on the bottom two lines it is usually better to use PRINT #1; "text" which works just as well if not better, without such a risk of causing a system crash. If you POKE a value greater than two into DF_SZ the upper screen will become smaller than normal. So after POKE 23569,Y the upper screen would be 24-Y rows down and would scroll when the PRINT position got to or beyond 24-Y,0.
This program shows how a part screen scroll can be maintained with DF_SZ and SCR_CT. Here, random numbers appear and scroll up the top 14 lines of the screen only.
10 POKE 23692,0: POKE 23659,10
30 GO TO 10

When RANDOMIZE (number) is used, the number (a constant or a variable) is stored in this system variable. This is the number that determines the next random number. It opens up the possibility of cheating, since you could work out the next (supposedly) random number generated and use the knowledge gained to 'swing' luck your way. For example, after RANDOMIZE 1, the next value of RND would be (0.0022735596, INT (RND *6) + 1) to simulate a die being thrown as a one.

This is a frame counter which can be used as a timer. It counts frames of a TV picture and so is incremented fifty times a second in the UK, or every 0.02 seconds, although the time taken to actually read and evaluate these three bytes of the timer may not allow it to be used to this accuracy. It has a timing range of nearly four days (actually about three days 21¾ hours). The manual (chapter 18) points out that you need to read the value of these three bytes twice in succession and take the high value for full accuracy because of the possibility of the values of the three changing while being read in such a way as to cause large inaccuracies.
It must be emphasised that the timer bytes are in the opposite order to what you might expect - the most significant byte is 23674, so the timer values are read by:
65636*PEEK 23674 + 256*PEEK 23673 + PEEK 23672
which returns time in units of fiftieths of a second.
There are several things that affect the accuracy of this timer. Using BEEP stops the timer. Using the printer and loading/saving, etc, also affect its accuracy. However, the use of PAUSE is OK as this only waits a specified time without re-setting or stopping the timer.

The address of the start of the dot patterns for the user-defined graphics is normally 32600 on a 16K Spectrum or 65368 on a 48K Spectrum. This number is the same as USR "a", so PRINT USR "a" corresponds to:
PRINT PEEK 23675 + 256*PEEK 23676

Compulsive POKEers can have fun with this one. The manual suggests changing this to save space by having fewer user-defined graphics. However, it is also possible to do the reverse, and set up more than one user- defined graphics set if required; however, only one set of 21 can be in use at any one time. Remember that since there are 21 UDGs it is necessary to set aside 21*8 (168) bytes for each separate set of UDGs and POKE the start addresses, into 23675/6, of the character set in use.
For fun, type in the following commands:
POKE 23675,96: POKE 23676,127
(16K Spectrum)
POKE 23675,96: POKE 23676,255
(48K Spectrum)

Then, using the user-defined graphics (they normally appear as capital letters until re-defined) try to type out a message. I'll leave you to find out what happens.
One useful tip: once you've set up a user-defined character set it may be SAVEd on tape. Most people would type something like:
SAVE "chars" CODE 32600,168

Fine, but you have to specify the start addresses. You could use SAVE "chars" CODE(PEEK 23675 + 256 * PEEK 23676), 168, and then
you could happily save the current set of UDGs on tape without knowing the address of where they start. This would, for example, allow you to LOAD a character set SAVEd from a 16K Spectrum back into a 48K Spectrum without having to know the addresses.
To get a character set back into the right place on a machine with different amounts of memory, simply use LOAD "chars" CODE(PEEK 23675 + 256 * PEEK 23676),l68. This would automatically re-locate data to the right address for the machine in use at the time. This is the same as LOAD "chars" CODE USR "a", which saves a bit of typing although it may look a bit strange.

Contains information about how far across the printer buffer the LPRINT position has got to. Contains (33- column number) for columns 0 to 31. You cannot change the LPRINT position by POKEing this alone.

small title
Contains the low byte of the address where the next character is to go into the printer buffer, ie. this will contain (23296 + LPRINT column numberer), being zero for the left column of the printer, 15 for the 15th column etc. Because this is the address of the top row of dots of each character, you can POKE this to change the LPRINT buffer position provided you change the value in P_POSN (23679) to match. It may appear to work if you don't do this, but problems will be encountered at the end of the line.

Unused system variable
This system variable, although strictly speaking unused, usually contains 91. This is the high byte of the LPRINT buffer address (91*256 is 23296 where the buffer starts). This can be POKEd for your own use but using the printer will overwrite it back again to 91. 23680/1 together contain the address of the LPRINT position in the printer buffer. You will not affect the working of the printer if you POKE 23681, but anything stored here may be over- written by the printer routines.

23677 is the system variable that contains the x co-ordinate of the last plotted point. After CLS it starts off at zero and 23678 is the system variable that contains the y co-ordinate of the last point plotted. It contains the actual value, so if the last point plotted was 3,3 both bytes would contain threes.
These two can be POKEd with valid x and y co-ordinates respectively. Since POKEing these does not actually PLOT anything on the screen, this is a convenient way to move the PLOT cursor around. This could be done by PLOT OVER
1;X,Y: PLOT OVER 1;X,Y - but would be messy. Amongst other things this could simulate MOVE found in other BASICs - useful if you wanted to draw lines from around a particular point.

The address in the display file of the PRINT position. It may be POKEd to send the PRINT output elsewhere although this requires an understanding of the way the display file is organised.

23688 contains information concerning how far across the screen the PRINT position has got. It starts off as 33 for the left-hand side of the screen and decreases by one every time the PRINT position moves one place to the right. After using PRINT AT Y,X; (if Y and X are valid PRINT AT co-ordinates) 23688 would contain 33-X. This can be useful when trying to prevent words being chopped in half when printed on the screen. If you imagine the number in 23688 as counting down towards zero as there is no more room on the current line, you can see that comparing this to the length of the word to be printed gives us an idea of whether it is necessary to move to a new line to prevent the word being chopped Suppose the word to be printed was W$:

This only works for words less than 32 characters long.
23689 contains information relating
to how far down the screen the print position has got to. It starts off at 24 for the top line of the screen and decreases by one every time the PRINT position moves down the screen. If you do not want a scrolling display and would rather the screen was cleared when the PRINT position got near the bottom of the screen, then try:

Contains the data for how many scrolls will be carried out plus one before waiting with the message 'scroll?' to give viewing time. In graphics games especially this can be a nuisance, since one is not interested in waiting for viewing. So, if the number in 23692 is anything other than one, the waiting does not occur. So POKE 23692,255 would give you 255 lines of printing before the machine waits with 'scroll?'.
POKE 23692,0 seems to have a similar effect except that you have
one more line of print. If more is needed, then if the printing is done within a loop, it will be necessary to include the POKE statement in the loop as well - time-wasting but necessary.

Contains permanent attributes, or the attributes (FLASH, BRIGHT, PAPER and INK) in effect globally. Local colours in PRINT statements, etc, are dealt with elsewhere. Note that most of the ROM routines use the values of the system variable holding the temporary attributes as these contain the permanent attributes unless a local parameter is specified. CLS, however, clears the screen to the colours, etc, in ATTR_P. The functions of the individual bits are shown in Table 1.
Bit 7 is one for FLASH 1.

Bit 7 is zero for FLASH 0.

Bit 6 is one for BRIGHT 1.

Bit 6 is zero for BRIGHT 0.

Bits 5, 4 and 3 contain the PAPER colour in binary, eg. for PAPER 7,
bits 5, 4 and 3 would be 1, 1 and 1 respectively.

Bit 2, 1 and 0 contain the INK colour in binary, eg. for INK 3, bits 2, 1 and 0 would be 0, 1 and 1 respectively.

Attributes of eight or nine are not dealt with here. If the permanent attributes are eight or nine, then those stored in 23693 may not be valid.

This is the system variable that helps the Spectrum determine the attributes of anything printed when a parameter of eight is specified. So, if you specified BRIGHT 8 globally, bit 6 of 23694 would be set to one to remind the computer in future that BRIGHT 8 has been specified. So, to determine the colour/ flashing/ brightness when printing, the computer looks at what's already there and prints the word in that colour, etc. Or if you like, it only overprints the character on the screen and leaves the attributes alone. You can see what each bit does by looking at Table 2.

Bit 7 is one when FLASH 8 is in effect.

Bit 6 is one when BRIGHT 8 is in effect.

Bits 5, 4 and 3 are normally all one when PAPER 8 in effect, but see below.
Bits 2, 1 and 0 are normally all 1 when INK 8 in effect, but see below.

When there is more than one bit to consider, as in INK and PAPER, then only the bits set have their attributes bit taken from the screen. This can lead to some unexpected effects. Try this:
10 INK 8
20 POKE 23694, BIN 00000011
30 PRINT AT 0,0; INK 5;"5555555"
40 PRINT AT 0,0; "1111"

small title
  As INK 8 is specified, you may expect the ones to be printed in cyan like the fives, but no. Rather than check the INK attribute as a whole, it only checks the bits set in 23694, which were bits 0 and 1. See if you can work out what colour the ones will be printed in. Have fun!  

This system variable contains the current temporary colours as would be set up by local statements within PRINT statements. You could see this for yourself with something like these two direct commands:
That is, include the PEEK in a PRINT statement under the effect of the local colour controls. Normally unless local colour statements are specified, this system variable will contain the global colour values. Colours, etc, to be used for printing on screen are taken from these temporary system variables and things are balanced such that ATTR_T is only different from ATTR_P if local colour attributes and so on so decree. The function of the individual bits can be seen in Table 3.

This is rather like MASK_P (system variable 23694) except that the parameters here are temporary. Normally the same as the equivalent permanent parameter 8s, this is changed while local colour 8s, etc, are in effect. You could study this by using something like:
PRINT PEEK 23696, INK 8; PEEK 23696,
INK 0; FLASH 8; PEEK 23696

The individual bits have the functions illustrated in Table 4.

This system variable contains, as you might expect from its name, flags used during printing. After PAPER 9 has been specified, bits 6 and 7 are set to one. After INK 9 has been specified, bits 4 and 5 are set to one. After INVERSE 1 has been specified, bits 2 and 3 are set to one. And after OVER 1 has been specified, bits 0 and 1 are set to one. The effects are global if the odd numbered bits (bits 1,3,5 and 7) are set to one, and temporary if the even bits (bits 0,2,4, and 6) are set to 1. See Table 5 to see what I mean.

These three bytes in the system variables are not normally used by the Spectrum - you may like to make use of them as 'custom variables' for use in your own programs in which you need to access information. These are particularly useful in machine code routines where you can simply access the information via an address rather than searching for the variable in the variables area. 23728/9 was intended for use by non-maskable interrupts but these don't occur on the bare Spectrum.

This two byte system variable points to the last byte of RAM of the Basic system area. Note that this is not the end of the memory used by Basic, in the sense that the user-defined graphics normally hide up above this address. If you move RAMTOP up above the start of the user-defined graphics they may be overwritten, but you gain quite a few valuable bytes which may be useful for 16K users.
One important thing is that NEW only operates as far as the address
held in 23730/1, so you can store data above this which may be passed between programs loaded into the computer. The same is true if you want to preserve machine code routines, etc.

This contains the address of where RAM ends on the Spectrum. If you acquire a Spectrum whose memory capacity you don't know, then don't bother looking inside it to see if it's an expanded model or not, just enter this expression:
PRINT PEEK 23732 + 256*PEEK 23733 - 16384
The 16384 bytes subtracted is for the ROM since RAM starts at address 16384 and goes up to the address held in P_RAMT.
TABLE 1 The tables alongside are referenced in the main text of the article, and show the functions of the individual bits of specified system variables: table 1 refers to 23693 ATTR_P; table 2 - 23694 MASK_P; table 3 - 23695 ATTR_T; table 4 - 23696 MASK_T; and table 5 - 23697 P_FLAG.
in binary
INK colour in binary
temp. PAPER colour temp. INK colour
temp. PAPER8? temp. INK8?
FLASH8 BRIGHT8 PAPER colour 8? INK colour 8? This article is extracted from Dilwyn Jones' book, Delving Deeper Into Your Spectrum ROM - first published in the UK by Interface Publications, xxxxxxxxxx xxxx xxxxxx, xxxxxx xn nxx, and priced at £7.95.
global PAPER9 temp. PAPER9 global INK 9 temp. INK9 global INVERSE1 temp. INVERSE1 global OVER1 temp. OVER1
Home Contents KwikPik