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!

