Electron User of July 1987 saw Andrew Richards present a scrolling wallpaper program called Automaton. In the first of a two-part series, I now present an improved version of that machine code graphics demo in which the whole screen is filled in one go, as opposed to scrolling a small window, making it faster. Part two will combine this idea with my own Fibonacci method [See EUG #45 - Ed] of generating graphics to provide some interesting background textures.
1. AUTOMAT - Automatic Wallpaper
For those who don't know, Automaton is a pattern-generation program. Designs of amazing complexity are generated using a few simple rules based on the 'Game of Life', so called because it's meant to simulate what would happen when single-celled creatures start to multiply.
In the original program, the fate of a cell is determined by the three neighbouring cells above it. We add up the colours of three pixels in a row, and use the result to pick a colour from a table of rules and plot a point on the line above in that colour.
In Mode 5 there are four colours and each colour is numbered 0-3. For example, consider three pixels coloured red, yellow, and white. They are the left-most three figures on the bottom line of the figure below:
2 3 1 2 3 0
We add up the numbers 1 + 2 + 3 to give 6. This becomes an index into a table which has ten entries numbered 0-9. (The maximum result can be no more than nine since three white pixels of value 3 add up to nine: 3 + 3 + 3=9). In the above example, it is assumed that the 6th entry in the table is 2 and that becomes the colour of the pixel above.
We then move along to the next trio of pixels and add up their numbers, thus 2 + 3 + 0=5, and the 5th rule in the table might contain 3 and so the colour of the pixel above is white.
In the program in Electron User, you could manually set the entries in the table by pressing the corresponding number keys. In my version, the rules are set to random values so you don't have to make much of an effort to get interesting effects. Also, the screen is filled from the top downwards. So the only difference is that the next cell appears on the line below instead of the line above.
The whole process is done in machine code which directly accesses the Mode 5 screen for speed. Each byte holds the data for 4 pixels. A colour number can be stored in 2 bits (0-3), so 2 bits per pixel times 4 equals 8 bits or one byte. The first byte of the Mode 5 screen is located at address &5800, and this is stored in SCR, just after the GO label in line 280.
There are 160 pixels across the Mode 5 screen and the colours of each are held in POLD and PNEW. POLD is initially filled with random values in the loop SLP (line 320). A call to Basic's random number loads the Integer Work Area (IWA) at locations &2A-D with a random number. This is a handy way of choosing random numbers in machine code. Loading location &2A into the A register and ANDing with 3 gives a number between zero and three.
Basic's RND generator, called RNDI, is located at &AF51 in the Basic II ROM (fitted in the BBC B and Electron) and at location &AA7B in Basic IV (fitted in the BBC Master). The program automatically detects which machine you are using (line 30) and sets the RNDI variable to the correct address.
GENERATE examines each triplet of entries in POLD. There are 162 entries, two more than the number of pixels across the screen. This is because when we examine pixel number zero (the leftmost pixel on a line) we need the pixel to the left and to the right. So pixel number 0 on the screen is in fact pixel number 1 in the table, and pixel number 160 (the rightmost pixel on a line) is number 161 in the table.
The triplets are added up (line 470) to give a result which is transfered to the Y register and used as an index into RULES (line 480). The required value from RULES is then stored in the table PNEW (line 490). This is done for all 160 pixels on the current line.
ENCODE composes a four-pixel byte for storage in the Mode 5 screen (four entries from table PNEW make one byte). Unfortunately Auntie Acorn didn't make life easy for programmers, because the bits of each colour are arranged in a peculiar fashion within the byte.
The bits of a byte are traditionally numberd 7 to 0 from left to right, i.e. b7,b6,b5,b4,b3,b2,b1 and b0. The two bits of p3 (pixel number three, the left-most pixel) are stored in b7 and b3, the 2 bits of p2 are stored in b6 and b2, the 2 bits of p1 are stored in b5 and b1, and finally the bits of p0 (the rightmost pixel) are in b4 and b0.
The small table called CCODE helps here. It contains four entries giving the values of a white, yellow, red, and black pixel in p0 position. If, say, we wanted to know the value of red in p2, we get the 3rd number in the table (&10 or 16) and divide it by two. If we want to know the value of red in p2 position, we divide by two again. In other words, dividing the value in the table successively by two gives the value of that pixel in positions further to the right.
In fact, what ENCODE does is to OR the colour at p0 position into BYTE, the byte that is to be stored into the screen, and multiply by two ready for the next colour to be ORed in at p0. COUNT counts how many times this is done - four times as there are four pixels. The current screen address, to which the final value is written, is put into the machine code itself at ADR2+1 and ADR2+2 (See lines 600, 610, 730).
DOWN sets the screen address to the next row of pixels below. Depending on whether we are within a character row or on the boundary between one character row and the next, we add 1 or &139 respectively. ANDing the low byte with seven gives zero if we have to move to the top of the next character row. If it's non-zero we just move to the next row within the current character row. You've probably come across this technique if you are into animated sprites in games.
LINE holds the number of the current horizontal line of pixels. There are 256 lines of pixels in all Modes, a byte holds a number between zero and 255, so when this counter reaches zero we have filled the whole screen.
Christopher Dewhurst, EUG #61