ELEMENTARY GRAPHICS
part 2 of 3
ZX Computing, August 1986

Toni Baker continues her graphics series with
a look at the layout of the screen display.


In this article I want to talk aboUt the possibilities of creating
graphics by directly POKEing the screen, instead of "printing". To
master this art we must first understand how the screen works. The
mysterious layout of the TV screen is therefore today's topic. Let us
proceed.


The screen display

The area of memory which is normally used for the TV screen lies
between addresses 4000 and 5AFF. Those between 4000 and 57FF store the
"black and white" version of the picture (ie. with all the colour
taken out), whereas addresses 5800 to 5AFF store the colours. Those of
you without a 128K Spectrum are restricted to using these addresses
only - the screen area cannot be moved. Those of you with a Spectrum
128 will find that there is a second area of memory which may be used
to store a screen image (addresses 7C000 to 7DAFF) and we'll be
looking at that possibility later on in the article. For now though,
we'll just take the simple case of the 48K machine.

The first (hex) address for the screen is 4000. In decimal this is
16384. Type CLS followed by POKE 16384,255 (Spectrum 128 owners would
be advised to use the Screen option from the menu to move any program
to the bottom of the screen first, if they are operating in 128K
mode). Watch what happens - you will see a little bar appear in the
top left hand corner of the screen. Now type BORDER 5 so that you can
see where the middle part of the screen begins and ends - you should
notice that the little bar you've just printed is in the very top left
hand corner. Now try typing POKE 16384,15 - this will give you a
little bar half the length of the first, and not quite in the corner.

What you have to do to understand this is to think of the numbers in
binary. If we imagine that the zeroes are blank squares, and the ones
are filled- in squares, then we can easily see the picture given by
Figure One [ELEMG2_1.GIF]. According to this Figure One, the
instruction POKE 16384,85 should give a speckled bar in the top left
hand corner (if it doesn't then try tuning your TV in a bit better).

In other words, each bit of the screen memory corresponds to one pixel
on the TV. Have a look at Figure Two [see "part2" in ELEMGRPH.TAP] -
this contains two machine code programs (very short), and a BASIC
program (also very short), which I'd like you to try. The first of
these prints a symbol on the screen, but without using RST 10; the
second prints a lower case letter "a". The third program in the set is
in BASIC, and this too manages to get a whole character-sized symbol
on the screen - and all without using PRINT.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Program One
     211000   START     LD   HL,0010
     09                 ADD  HL,BC         ;HL points to data given
     110040             LD   DE,4000       ;DE points to first square on screen
     0608               LD   B,08
     7E       LOOP      LD   A,(HL)        ;A= next byte of data
     23                 INC  HL
     12                 LD   (DE),A        ;Poke into screen
     14                 INC  D
     10FA               DJNZ LOOP          ;Repeat for all eight bytes
     C9                 RET
00 38 54 92 BA D6 54 38                    ;Data to print

Program Two
     210040   START     LD   HL,4000       ;HL points to first square on screen
     11083F             LD   DE,3F08       ;DE points to expansion of "A"
     0608               LD   B,08
     1A       LOOP      LD   A,(DE)        ;A= next byte of data
     77                 LD   (HL),A        ;Poke into screen
     24                 INC  H
     13                 INC  DE
     10FA               DJNZ LOOP
     C9                 RET

Program Three
10 FOR i=0 TO 7
20 INPUT a$
30 LET x=FN h(a$)-16*FN h(a$(2))
40 POKE 16384+256*i,x
50 NEXT i
60 DEF FN h(x$)=CODE x$-7*(x$>":")-48
RUN this then input (for example): "FF","81","BD","A5","A5","BD","81","FF"

                                    Figure 2.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


Graphics in ROM

All of the keyboard characters are stored in pixel expansion form in
the ROM. To find the pixel expansion for any given character just
multiply its character code by eight and add 3C00. For instance, "a"
has character code 61h, and 8*61 + 3C00 = 3F08. This is the address we
gave to DE in the program in Figure Two.

You may have noticed that the machine code programs used INC D (and
INC H) to locate the next row of pixels on the screen. Now this is
very interesting because INC H increases the value of H by one, and
therefore increases the value of HL by 0100h. Also the BASIC program
used an increment of 256 (note 256d = 0100h). This tells us something
about the layout of the screen. If HL contains the address of the
first (topmost) pixel-row of a character square then the other seven
pixel-row addresses can all be obtained by an appropriate number of
INC H instructions (see Figure Three [ELEMG2_3.GIF]).

So now we know the effect of adding 100h to the address, what about
adding one? Try typing in the following BASIC program and running it
to see what happens:

10 LET x=16384
20 FOR i= 0 TO 7
30 POKE x,255
40 LET x=x+1
50 NEXT i

You should see a thin bar lining out across the top of the screen.
Changing line 20 to FOR i=0 TO 31 extends the line right across the
full width. So far it seems simple, but is there more to it than that?
Change line 20 again to read FOR i=0 TO 255 (after all, we already
know what happens if we add 256). Try it and see.

All well and good - the first eight rows of characters are lined out.
But - surely there's something wrong here? After all, we've added 255
to the original address to get the last address, and yet we know that
if we added instead 256 (ie. if we added one more) then we'd be one
pixel-row down from our original position. You can try it if you like
(just change line 20 again). Common sense, on the other hand, would
lead us to expect that the next pixels would be at the left of the
screen, one more character square down. Common sense, unfortunately,
doesn't mean much in the world of the Spectrum. Change line 20 to read
FOR i=0 TO 2047 and see what happens.

If you can imagine that the first eight lines of character squares are
a completely isolated and separate part of the screen, having no
relation to the rest of it, then the problem disappears and it all
seems rather boring and sensible once more. Adding one moves you one
character square to the right (skipping down to the left hand edge of
the next line of character squares if you go off the right hand edge),
and adding 256 (100h) moves you down by one pixel-row (provided you
don't try to move down below the bottom of a character square). Figure
Four [ELEMG2_4.GIF] shows that if we write the address of any screen
position within these top eight rows in binary, and assign such a
number to HL then it looks very sensible indeed, with H recording the
row number within a character square, and L recording which character
square. If you split the value in L into two parts, as I have done in
the diagram, then we actually come up with the PRINT AT coordinates of
the character square in question. This is just one way of looking at
the correlation between what you see on the screen, and what the
Spectrum sees in its memory.


Screen lines

Figure Four also shows us a second picture. You see - so far we've
only looked at the first eight lines of the screen. We now need to
look at the other 16. The first eight lines use up all the addresses
from 4000 to 47FF. The next eight lines are organised in exactly the
same way! They occupy addresses 4800 to 4FFF. Finally, the third
eight-line segment occupies addresses 5000 to 57FF, and once again is
organised in exactly the same way. What this means in practice is that
we have to envisage the screen as being divided up into three "thirds"
or "segments", with segment zero being the top eight lines, segment
one being the middle eight lines, and segment two being the bottom
eight lines. Thus - although in general adding one to a screen address
will move you one square to the right (or onto the left hand edge of
the next line) the procedure will not work if you try to cross from
one segment to the next. For instance, the last square in segment zero
has address 40FF whereas the first square in segment one has address
4800. Confusing though this may seem it is still very straightforward
if we look at the address in binary. The second diagram in Figure Four
gives the general picture - the address of any pixel-row on the
screen.

I use the terms "square", "row", "line", etc with precise meaning.
Figure Five [ELEMG2_5.GIF] shows you the best way to visualise this
breakdown.

Now this way of representing screen addresses is fine for computers,
since computers work in binary, but it's not all that good for humans.
The diagram in Figure Six [ELEMG2_6.GIF] looks at screen addresses
directly in hex. All you have to do is read off the first three hex
digits from the left or right edge of the screen (whichever is closer)
and the fourth digit from the top. Thus the square on the diagram
which is filled in has address 509A. I hope you can see how to read
this from the picture. This means that the eight addresses which
together comprise this square are 509A, 519A, 529A, 539A, 549A, 559A,
569A and 579A.

And now, a quick little subroutine to turn PRINT AT coordinates into
screen addresses. This is the subroutine in Figure Seven [see "part2"
in ELEMGRPH.TAP], which assumes that B contains the screen's y
coordinate, and C contains the x. The final address is left in HL.


Attribute bytes

Now that we've looked at the screen in black and white, we need to
consider the colour aspect of it. Consider the following BASIC program:

10 PAPER 6: INK 0: BORDER 6: CLS
20 FOR i=1 TO 22
30 PRINT ,"SYNCHRONICITY"
40 NEXT i
50 FOR i=22528 TO 23295
60 POKE i,15
70 NEXT i

Watch what happens when you run it. First of all some text is printed
on the screen (in black on yellow), and then, one square at a time,
the screen changes colours (to white on blue) without altering the text.

To understand exactly why the program works it is much easier to think
in hex. The area of memory between 5800 and 5AFF is called the
attributes file (as opposed to the display file which is the screen
area). The contents of the attributes file determine the colours on
the screen. It's very simple. Every character square on the screen has
one attribute byte all to itself. The contents of such an attribute
byte determine the colours of the corresponding square. This means
that if you POKE an address in the attributes file then you will
change the colours of one character square. We discussed attribute
bytes in last month's article, so I won't go over them here in too
much detail, but just a reminder: an attribute byte stores the FLASH
status (off or on), the BRIGHT status (off or on), the PAPER colour (0
to 7), and the INK colour (0 to 7). The byte value is 128*F + 64*B +
8*P + I, or in binary: F B P P P I I I.

In machine code, of course, it is useful to know precisely which byte
in the attributes file corresponds to which square on the screen. You
can work out the address of any individual attribute byte in BASIC by
the formula 22528 + 32*Y + X (where X and Y are the PRINT AT
coordinates of the corresponding square). You see, unlike the main
screen, the attributes file is laid out completely sensibly - left to
right, top to bottom. In fact - if you go back to Figure Six I'll show
you an easy way to visualise it. Pick a square (for instance the one
marked), and read off the first three digits from the left or right of
the screen (whichever is closer), but use the figures given in
brackets! Finally read the fourth digit from the top. This gives the
full address in hex - for instance the attribute byte for the square
marked has address 5A9A. Got it?


Changing the BORDER colour

There are essentially two steps involved in changing the colour of the
screen border in machine code. This is because the ROM uses the border
colour in two different ways. The actual border colour is the colour
you see on the screen, right now, with your very eyes. The recorded
border colour is a separate record kept by the Spectrum amongst the
system variables. Every time you press a key whilst in command mode
the actual border colour is changed to that of the recorded border
colour. The problem for the programmer is that changing the actual
border colour does not alter the ROM's permanent record - so you may
find that the border colour changes back to what it was before, the
next time you press a key. Alternatively, simply changing the
permanent record will not affect the actual colour on the screen (not
immediately anyway). You must change both of these.

To change the actual border colour on screen you may use either the
BASIC instruction OUT 254,colour or the machine code OUT (FE),colour.
These instructions look identical - in fact they are.

To change the permanent record you must POKE the system variable
BORDCR (address 5C48) with an attribute byte for the border. It is the
PAPER colour of this attribute which will be used for the border. The
rest of the attribute byte is used to specify the colours of the lower
part of the screen used for INPUT etc.

There is, as always, an easy way of doing both jobs at once: the
machine code instruction CALL BORDER_A at address 2297 (in hex
CD9722). This will change both the actual border colour and the
recorded border colour to whatever colour you desire - the choice of
colour must first be loaded into the A register.


Additional Information for Spectrum 128 owners

The Spectrum 128 has not one, but two areas of memory which may be
used to store a screen image. Ordinarily addresses 4000 to 5AFF are
used, but the second possible location is addresses C000 to DAFF on
RAM page seven. The first region is called screen zero and the second
region is called screen one. Obviously only one of these screens may
appear on the television at any one time.

The addresses of individual bytes within screen one are obtained by
calculating the address for the corresponding byte within screen zero
and then "setting" bit fifteen (ie. change the initial 4 into a C, or
the 5 into a D). Remember though that this screen area resides in RAM
page seven, not RAM page zero (the normal page). Note that it is
possible for screen one to be active irrespective of whichever RAM
page is paged in - in other words, it is not necessary for page seven
to be paged in for screen one to work.

Changing from screen zero to screen one (or vice versa) may be
achieved by calling either label SCR_0 or SCR_1 from the machine code
program of Figure Eight [see "part2" in ELEMGRPH.TAP]. Note that the
ROM is only designed to print onto screen zero, not screen one.
Neither PRINT nor RST 10 will work on screen one, nor will PLOT or
DRAW, neither will the automatic listing, and neither will command
editing or INPUT. You must select screen zero before the end of a
program.

Another word of warning. The Spectrum, being a machine of very many
bugs, not only displays a complete lack of ROM software to use screen
one (not even in the new ROM), but worse - the software it does
contain clashes directly with any possibility of using screen one. The
problem is this: whenever you SAVE! something into RAMdisc using the
new SAVE! command, the files saved are stacked one above the other.
The first file saved will begin at address 1C000, and the stack will
build upwards through consecutive memory areas: 1C000 to 1FFFF, 3C000
to 3FFFF, 4C000 to 4FFFF, 6C000 to 6FFFF, then 7C000 upwards. At the
same time a second stack is built, beginning at address 7EBFF and
growing downwards - this stack stores the NAMES of the files saved (as
used by CAT!) and other information about the files. These two stacks
are not allowed to meet - if there is any danger of this happening
then error report "4 Out of memory" is given. Furthermore, the
directory stack is not allowed to grow downward beyond address 7C000
(this is why it is impossible to SAVE! more than 562 files, even if
they don't add up to 69K). It turns out that even though these stacks
are not allowed to meet, they are allowed overwrite screen one. In
fact, screen one may be overwritten by either the file stack, or the
directory stack. Conversely, if enough programs are SAVE!d in RAMdisc
then it is equally possible that POKEing into screen one may corrupt
some of the files - a far more serious prospect. Machine code
programmers should beware! Screen one may only be used if you don't
save too much into RAMdisc. You can safely save 64K or 216 files,
whichever comes first and still make use of screen one.

Well that's it for this month. Next month I shall conclude this short
mini-series by telling you how anything BASIC can do, machine code can
do better - and faster. Happy Lughnasadh - see you next month.
