DIY Screens

By Dave E

Originally published in EUG #67
This article and utility is just one of the many included on Chris Dewhurst's LUTIUS PROJECT, reviewed in EUG #66, 'The Bible' for any discerning programmer
If you have played top-selling games such as Superior's Palace Of Magic or Audiogenic's Ransack, you may have wondered how they managed to fit all the coding into the BBC's pint-sized memory. Well, you may have noticed that in nearly all cases, the size of the screen is somewhat smaller than usual. The author has decided that some of the display area could be sacrificed for extra code - more memory for sprites and programming. Exactly how to do this has been shrouded in mystery, so in this article I'll be telling you how - in a no-nonsense, practical way so that you can easily incorporate the techniques involved into your own programs.

When you switch on your BBC, a set amount of RAM is reserved for the screen. While this is neglible in Mode 7, which uses only 1K, high resolution Modes like Mode 2 consume an enormous 20K! This leaves only 8K or so for programming, and when you take into account the operating system and disc drive, it's down to a measly 5K or so.

Reducing the Mode 2 screen to a quarter of its original size (half the length and half the width) would leave the other three quarters spare for programming. That's a saving of over 15K. What we have to go through to achieve this is complicated, but it's worth learning.

You might think that we could tell the computer how many characters we want across the screen and how many down it. But it's not as simple as defining text windows like that.

For a start, as you might know, the computer doesn't, for the best part of the affair, work in characters, but in bytes. Different amounts of bytes constitute the text characters in different Modes, as shown in Table 1:

Table 1 - how bytes are used to make up the display in various Modes
MODE Bytes per char Chars per row Bytes per char row Columns per char Columns per char row
0 8 80 8*80 = 640 1 1*80 = 80
1 16 40 16*40 = 640 2 2*40 = 80
2 32 20 32*20 = 640 4 4*20 = 80
4 8 40 40* 8 = 320 1 1*40 = 40
5 16 20 16*20 = 320 2 2*20 = 40


In Mode 4, a character consists of 1*8 = 8 bytes, each byte being an eight pixel combination of two colours (usually black or white unless you've changed the colours with VDU19). But in Mode 2, a character is made up of 4*8 = 32 bytes, each byte representing a two-pixel combination of the 16 colours.

You can see that a character for any Mode can be described by the number of columns that it is built from. In Mode 4 it is 8, and in Mode 2 it is 4. The screen width is a measure of how many of these columns are arranged side by side in a single, horizontal line.

Taking Modes 2 and 4 as examples again, the screen width for Mode 2 is 4*20 = 80 and for Mode 4 it is 1*40 = 40. That is, the number of columns per character multiplied by the number of characters per row, as show in the far right column of the Table 1.

For our new Mode 2 screen, we want half the width. That's just 4*10, the number of columns per character multiplied by half the usual number of characters per row.

To program this width, we use the first of several screen registers, shown in Table 2:

Table 2 - Screen Registers and values to put in them
Register Function Value to put in
R1 Screen width b%*w% DIV 8
R2 Horizontal screen position (b%*c%-b%*w%) DIV 16
R6 Screen length l%
R7 Vertical screen position (36+l%) DIV 2
R12 High byte of start of screen s% DIV 8 MOD 256
R13 Low byte of start of screen s% MOD 8 MOD 256


where:

b% = bytes per character
c% = default characters per row
d% = default R2*
l% = new screen length in characters
s% = start of screen in RAM, calculated by &8000-w%*b%*l%
w% = new screen width in characters


* The default register values are displayed in the following table:

Table 3 - Default screen register values
Mode R1 R2 R6 R7 R12 R13
0 80 97 32 34 6 0
1 80 97 32 34 6 0
2 80 97 32 34 6 0
4 40 49 32 34 &B 0
5 40 49 32 34 &B 0

R1 controls the screen width. The screen registers aren't located in the BBC's RAM, so we access them with the VDU 23 statement:

   VDU 23,0,register_number,value,0,0,0,0,0,0

which can, in Basic, be abbreviated to:

   VDU 23;register_number,value;0;0;0;


Type in this short program:

10 MODE 2
20 VDU 19,0,1;0;
30 VDU 23,1,40;0;0;0;

Line 30 is our new VDU 23 statement, and line 20 simply puts on a red background so that you can see how we are progressing. The left half of the screen should be red and the right half black.

The next thing to do is tell the BBC the length of our screen. This goes into R6, and, thankfully, it's not as complicated, because it's simply the number of characters (or columns, if you like) down the screen. (You can't have a length of several characters-and-a-bit, like you can the width; it's either one extra or none extra.) Add:

40 VDU 23,6,16;0;0;0;

to the program and run it again. You'll notice from the table that R6 is always 32 because there are always 32 rows of characters down the screen in every graphic Mode.

The red box doesn't look very good sitting at the top left corner of the screen, does it? We'll centralize it with registers 2 and 7. R6 controls the horizontal screen position, and R7 the vertical position on your monitor. Increasing R2 moves the screen to the left and decreasing it moves it to the right. Increasing R7 moves the display up and decreasing it moves it down.

You'll have to experiment to see which values suit your particular TV or monitor, though the formulas I've worked out in Table 2 should work with most. Using certain values will cause the screen to go crazy: to get rid of it, simply change Mode.

Our values, then:

R2   R7
= 97-(32*20-32*10)/16
= 97-20
= 77
= (36+16)/2
= 26


Add these lines:

50 VDU 23;2,77;0;0;0;
60 VDU 23;7,26;0;0;0;

We now have our screen centralized, but we're not even half way! We need to tell the BBC the size of our screen and where it is located in RAM. The necessary pokes are shown in Table 4:

Table 4 - Locations in RAM whose contents are changed for new screens
Location Name Basic commands
&E0/&E1 Pointer to screen lookup table ?&E0=table MOD 256:
?&E0=table DIV 256
&34E High byte of the top of the screen address ?&34E=s% DIV 256
&352/&353 Bytes per character row ?&352=(b%*w%) MOD 256
?&353=(b%*w%) DIV 256
&354 High byte of screen size ?&354=(w%*b%*l%) DIV 256

The high byte of the size of the screen is held at location &354. Notice it's only the high byte. That means you can't have any old size of screen - they must be multiples of &100 bytes. For example, the screen could be &3000 bytes large, but not &2ABC because &2ABC isn't a multiple of &100.
Our screen is 32*10*16 = &1400 bytes, that is (bytes per character) * (bytes per row) * (number of rows). So the value we poke in to &354 is the high byte of &1400, &14:

90 ?&354=&14

From this we can work out where the screen should start in RAM by subtracting &1400 from &8000 (the end of RAM) to give &6C00. You could, of course, locate the screen anywhere in RAM, but unless you know what you're doing, stick to this method. The high byte of &6C00 is &6C, and this must be poked in at &34E:

100 ?&34E=&6C

We have to tell the operating system about the start-of-screen address as well, and we do this with registers 12 and 13. Strangely, we have to feed in the screen size divided by 8. In our example it is &6C00/8 = &D80. The high byte of &D80 is &D and the low byte is &80. The high byte is put into R12 and the low byte into R13 (contrary to the usual low byte-high byte fashion).

70 VDU 23;12,&D;0;0;0;
80 VDU 23;12,&80;0;0;0;

The last value that must be adjusted in page 3 is at locations &352/&353. These simply contain the number od bytes per row. Multiply the number of bytes per character by the number of characters across the screen. In our case it's 10*32 = &140

110 ?&352=&40
120 ?&353=1

Finished? No! There's still something wrong, because when you press Return, the prompt is always on the next line but one, and when you reach the bottom of the screen you can't see what you're typing.

As you might have guessed, the BBC doesn't know you want the next line of characters to begin 10 characters (or rather 320 bytes) after the previous line, and that the display is to be scrolled after the 16th line.
Unfortunately, there's no way around this on the BBC Master. But on the BBC B, we can create a table of 'look-up addresses', offsets from the top of the screen (&&C00) pointing to the address of each of the 16 lines. Usually the BBC refers to a 640-times table, located at &C375 in the OS ROM. This address is stored in zero page at &E0/&E1. We simply substitute the address for the address of our own table:

130 table=&900
140 FOR L%=0 TO 15
150 B%=L%*&140
160 ?(table+L%*2)=B% DIV 256
170 ?(table+1+L%*2)=B% MOD 256
180 NEXT
190 ?&E0=0:?&E1=9

Our table begins at &900 (line 110). Line 120 sets up a loop for the 16 lines down our Mode 2 screen. Line 130 assigns the actual offset of line L% to B%. For some reason, if you're in Mode 4 or 5, this number must be multiplied by 2, that is:

  150 B%=L%*&280

Line 190 pokes the values of the low and high bytes of the table into &E0/&E1. The table consists of 32 bytes of 16 addresses, each two bytes long. Yours will be different depending on the screen size. If, say, your screen was 10 by 32 characters, there would be 64 bytes of 32 addresses in the table.
My table begins at &900, but you could use other areas of memory, provided it isn't the disc RAM, &D00-&10FF, and steer clear of the area above PAGE where your Basic program may sit. A good alternative is to store it in the extra memory created from shrinking the screen.
If you are programming in Basic, we can raise HIMEM to make use of the extra memory:

200 HIMEM=&6C00

Finally, we must set up a text window:

210 CLS
220 VDU 28,0,15,9,0

And if you're going to be drawing in the new Mode, set the graphics origin:

230 VDU 29,0;512;

Phew! We've done quite a bit of programming to set up our new screen, haven't we? After all that, I've made life easier by writing an utility, DIYSCR. After being given the screen dimensions and the Mode it is derived from, it will do all the necessary calculations, and assemble a machine code program to do all the initialization I've just talked through. It wll be saved as a small file, and all you do is *RUN it in the relevant Mode.

All that you have to to is input the source Mode and width and length of the screen in characters. Screen sizes larger than the standard size for the Mode you typed in won't be accepted. You can have larger screens if you really want, but you'll have to program them by hand.

DIYSCR will inform you of the new HIMEM if you're programming in Basic. You'll be asked for the filename under which to save the code. It must, as usual, be seven characters or less.

The lookup table is always placed at the beginning of page 9, and the machine code at page &A. This means you mustn't use the first half of page 9, but you can overwrite the code once it is run - everything has been initialised and it isn't needed any more.

Christopher Dewhurst, EUG #67