|"Variables on a Theme"
|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)
|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
10 DATA "1","2","3","4","5"
20 LET A=PEEK 23639 + 256*PEEK 23640
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
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
It is tempting to use the Basic
|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
10 POKE 23659,0
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
|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
POKE 23675,96: POKE 23676,127
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.
|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
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
IF PEEK 23688<LEN W$+1 THEN PRINT
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,
IF PEEK 23689=3 THEN CLS
|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
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
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
|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:
PRINT PEEK 23695
|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
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
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.
|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.
|INK colour in binary
|temp. PAPER colour
|temp. INK colour
|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 INK 9