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!