RANDOM MEMORY
ZX Computing, March 1987

Clyde Bish presents tips on full screen graphics.

[There were several mistakes in this article, which I've corrected. JimG]


This month we're moving into the big time! That is as far as the size
of illustrations goes. No more little minipics.. Now we're talking
about full screen illustrations.

But first the bad news. Big pics mean big memory blocks to store them
- 6912 bytes (memory spaces) to be precise if you want them in
glorious Sinclaircolor. If you're envisaging a "Mugsy"-style comic
strip, driven by a relatively short program, this may not matter.
Let's assume for the moment it doesn't, and see how we call store and
recall the illustrations.

First of all, how many can you store in memory? Answer - 5, with about
6K left for the driver program ("128" owners will obviously do much
better than this). Next, how do you store them? In principle what you
have to do is to transfer the contents of each byte of the display
file (D_FILE) and attributes area (ATTR) where the on-screen pictured
is stored into high memory, knowing where you're putting it. You could
do this by PEEKing each byte, POKEing the contents up, but this would
be quite slow. The solution is a short machine code routine which uses
the instruction LDIR. This stands for load / decrement / increment /
repeat which probably doesn't make you much the wiser. In essence, you
set one counter to the number of bytes to be transferred, another to
the start of the D_FILE, and a third to the destination address. LDIR
does the rest, continually transferring bytes until the first counter
is reduced to zero.

The necessary code is included in Program 1 [file: picsave], which
will make the transfers for you, modify the code to make it work in
reverse, then save it along with the picture bytes. Type it in, and
let's try it out. If you have some pics ready made with a drawing
utility (such as Toni Baker's Light Screen Designer) you could use
those. Otherwise the "rainbow" and "logo" SCREEN$s at the beginning of
your "Horizons" tape will be quite satisfactory.

RUN the program, and you'll be asked how many pics you want to save.
Let's try two. Answer the prompt for a title with the first pic, then
play the tape. The picture will load in more or less instantly and be
transferred to high memory, the address of which will be given. Make a
note of this, and the POKE number as well. You'll need this
information later. Repeat for the second picture, and there you are.
You can then save the data for the two pics plus the machine code to
drop them back down.

To make use of this routine you will need a subroutine in your program
such as: 9999 POKE 65358,h: RANDOMIZE USR 65356: RETURN Where you have
set variable h to the POKE number you noted for that picture, before
you GOSUB 9999. Try it, but don't blink at the wrong moment. You'll
miss it!


Memory squeeze

Now all this is very well if you have a "128" or don't need many pics.
How can you squeeze the proverbial quart into the Spectrum's pint pot?
One way is to have a smaller "quart". In other words, perhaps you
would be happy with a two colour drawing (INK and PAPER), which takes
up 768 bytes less. You could go further by having only a top third, or
two two-thirds screen picture (many adventures use this system,
leaving the bottom of the screen clear for text). The REMs given in
Program 1 will tell you the changes you will need to make for these
variations. If you want to mix various types of illustrations in one
program you'll need to POKE 65364 with the appropriate value shown in
the line 100 REM before you call the routine.

This technique obviously doesn't help if you really want full screen
illustrations, but there is a way around that problem if you're
prepared to sacrifice a little speed for a great saving in space. If
you type in the following line:
LOAD "" SCREEN$: FOR f=16384 TO 23295:
PRINT AT 21,0;"Address ";f:" holds ";PEEK f:
PAUSE 20: NEXT f
Press ENTER, then LOAD in a picture. You'll see a series of numbers
appear at the bottom of the screen as the program PEEKs its way
through the D_FILE, and later the ATTR if you wait long enough. You'll
notice that the numbers 0 and 255 (and later the permanent attribute;
for example 56 if the background is black on white) occur more often
than any others. This is because most of a picture is either blank
space (0) or inked in (255), whilst much of the attributes area
remains unchanged. Knowing this, it is possible to compact the data
for a picture by storing, for example, a line of 32 zeros as 0,32.
Using this technique even a complex picture, such as the "rainbow"
SCREEN$ is compacted to about half its usual length. This is what the
compactor routine (Program 2 [file: compactor]) does. Type it in and
let's try it out.

RUN the program, and answer the "attribute" prompt. If you haven't got
the Table I supplied in the January column, you can calculate it
yourself. Say the picture is drawn in blue ink on yellow paper the
value would be 1 (ink) + 6 (paper) * 8 = 49. Now load in the SCREEN$
and wait. From now on the program takes over, and the compacting may
take some time, so go and make a cup of coffee, or have a stroll
around the garden. You've been hunched over that VDU for too long
anyway! When the transfer is complete the number of bytes that the
picture has been compacted into is displayed. Make a careful note of
this, and the title you use to save the compacted code to tape. Repeat
with a new attribute value and SCREEN$ until you have all you need,
then just reply to the "attribute" prompt with ENTER.

[In the next few paragraphs he completely confused using 'T']
[between the length of data and the load address, so I've   ]
[corrected all the references.                          JimG]

Now you have your compacted codes separately on tape you need to save
them as one long code length. Do this by adding up all the code
lengths you noted, and adding 67. (This is for the machine code you'll
need later to "uncompact" them). Let's call the answer T. CLEAR
65367-T, then load in the first compacted code to [the start] address
using:
LOAD "title" CODE 65367-T+1
Load in subsequent compacted codes, adding the length of that code to
the previous address, and noting the new load address. So if the first
code was 2000 bytes long, the next would load in at [start
address]+2000, and so on.

Hopefully, when all the codes are in you'll have 67 bytes left below
the start of the UDGs. Load in the data from Table A, reading across
each line, with:
FOR f=65301 TO 65367: INPUT i: POKE f,i: NEXT f
Now save the whole data and machine code block with:
SAVE "unpack" CODE 65367-T+1,T

To use this compacted code in your programs you need to
have a subroutine such as:
9999 RANDOMIZE a: POKE 65305,PEEK 23670: POKE 65306,PEEK 23671:
     POKE 65326,c: INK c-INT (c/8)*8: PAPER INT (c/8):
     RANDOMIZE USR 65301: RETURN
where variable a is the data start of the picture you want to call
(noted when you made the one long code length), and c is the
background attribute value used in the compactor program (49 in my
earliest example).

Table B gives an annotated disassembly of the machine code so that
readers who want smaller / two-tone / line drawings with no filled
areas, (AND who understand what they are doing!) can alter the machine
code to operate over less of the screen / ignore the attributes /
ignore 255s and so make it run faster. (Program 2 will also need
adaptation. Refer to the REMs).


Table B

65301 210040           LD   HL,16384
65304 1150C3           LD   DE,datastart ;POKEd before call
65307 1A       LOOP:   LD   A,(DE)
65308 FEFF             CP   255          ;check for filled byte
65310 282E             JR   Z,FILL
65312 FE00             CP   000          ;check for blank
65314 2820             JR   Z,MISS
65316 77               LD   (HL),A
65317 23               INC  HL
65318 13       RET:    INC  DE
65319 7C               LD   A,H
65320 FE58             CP   088          ;80 for 2/3 screen, 72 for 1/3 screen
65322 38EF             JR   C,LOOP
65324 1A       LOOP1:  LD   A,(DE)       ;replace with RET if D_FILE only
65325 FE00             CP   attribute    ;attributes start here; POKE attr value
65327 2809             JR   Z,MISS1
65329 77               LD   (HL),A
65330 23               INC  HL
65331 13       RET1:   INC  DE
65332 7C               LD   A,H
65333 FE5B             CP   091
65335 38F3             JR   C,LOOP1
65337 C9               RET
65338 13       MISS1:  INC  DE           ;routine to skip attributes not to
65339 1A               LD   A,(DE)       ;be altered
65340 D5               PUSH DE
65341 1600             LD   D,000
65343 5F               LD   E,A
65344 19               ADD  HL,DE
65345 D1               POP  DE
65346 18EF             JR   RET1
65348 13       MISS:   INC  DE           ;routine to skip D_FILE bytes not
65349 1A               LD   A,(DE)       ;to be altered
65350 D5               PUSH DE
65351 1600             LD   D,000
65353 5F               LD   E,A
65354 19               ADD  HL,DE
65355 D1               POP  DE
65356 18D8             JR   RET
65358 13       FILL:   INC  DE           ;routine to fill bytes
65359 1A               LD   A,(DE)
65360 47               LD   B,A
65361 36FF     BACK:   LD   (HL),255
65363 23               INC  HL
65364 10FB             DJNZ BACK
65366 18CE             JR   RET


Kingdom came

Next time we'll be looking at make strip-cartoon-type adventure
graphics, "Redhawk"- style, but before I go I'll keep the promise I
gave in an earlier article, and supply a simple program of the
"Kingdom" kind for those who want something to work on to use as a
driver for their graphics. The listing is given in Program 3 [file:
Trader]. The purpose of the game is to accumulate #100,000 by astute,
if somewhat shady trading practices. The scenario is the South China
Seas, but could just as easily be smuggling along the Cornish coast,
or whatever. Your ship can hold 50 units of cargo, the buying /
selling price of which fluctuates. You start with #500 of your own,
plus #5,000 you have borrowed and must ultimately pay back. Interest
is added to this whenever you change ports. Oh yes. You may run into
storms en route and lose part of your cargo.

The program, which will run as listed, is in a very simple format with
a simple text screen display. As listed it takes up some 3K, but this
could be shortened considerably using the byte-saving tricks I
demonstrated earlier in this series. There are a few error checks.
Writing these in is a good programming exercise. I leave it to your
imagination to add the scenes using picsave, compactor, or any of the
other techniques I've explained earlier.

One last bit of help, though, with PRINTs and INPUTs. Printing to the
screen (with speech bubbles if you wish) is quite easy. Simply use
PRINT AT r,c;"text" where r=the row, and c=the column you wish the
text to appear.

For inputs you'll need to use a subroutine to simulate the normal
input routine, but wherever you want on the main screen. Add Program 4
[file: input] to your main driver program, and set r and c to the
row/column you want the input characters to appear, before you call
the sub- routine. Code 12 is delete (see p.183 of your manual) so CHR$
8 (cursor left) is used to backspace before printing the replacement
character. Code 13 in line 9995 is the code for ENTER, so the
subroutine returns.

Now away to the pixel paper, and get sketching!
