Well, folks, we were scooped by James Grosjean in SYNC magazine on the planned subject for this issue. So I’ll refer you to SYNC 3:5 page 80 for all kinds of nifty ways (some of which I hadn’t thought of) to save memory in your programs. We can thus go on to the next step in saving memory once you’ve used the standard bag of tricks. This is improving the memory-efficiency of entire routines. These will be things that become more useful the bigger and more complex your program is.
If you wrote out a program from start to finish, linearly, I guarantee it will contain redundant code. We all know that if you use ANY code more than once, you should place it in a subroutine. Then just GOSUB whenever you need that routine. But what about sections of code that are not identical but similar? Or what if there is a section of code in our main routine that we wish to access from a subroutine (sounds odd, but sometimes can prove quite useful)? These and similar things can be accomplished easily using logical operators and FLAGS. Flags are simply indicators of some condition. Your computer has some special FLAGS registers that allow conditional statements and generally keep track of things; we’re not talking about these; our flags will be BASIC variables used to control operation of your BASIC program. Clever use of flags can save big chunks of memory and help organize your program. Tom’s series uses flags extensively; if you understand his program you already know most of this,
An example could be the following routine that functions either as a prompted input or annotated print routine. N$(1,N,M) would hold the variable names, (e.g. VCC, PMAX, BETA) and N$(2,N,M) could be the appropriate units (VOLTS, WATTS, A./A.) You could fill this array manually and save the defining lines. Array A(N) holds the data. N=# of entries (up to 20), M=# of characters in labels (up to 9). F is our flag, when F=1 it’s an input routine, and when F=0 it’s a print routine. The section of code is effectively changed by the flag.
1000 CLS
1010 SCROLL
1020 IF NOT F THEN PRINT TAB 8;"TABLE OF VALUES"
1030 IF F THEN PRINT TAB 3;"INPUT VALUES IN UNITS SHOWN"
1040 SCROLL
1050 IF F THEN DIM A(N)
1060 FOR A=1 TO N
1070 SCROLL
1080 PRINT N$(1,A);TAB M+1;"=";TAB 12;("?" AND F);TAB 22;N$(2,A)
1090 IF F THEN INPUT A(A)
1100 PRINT AT 21,12;A(A)
1110 NEXT A
1120 PAUSE 4E4
1130 IF INKEY$="Z" THEN COPY
1140 RETURN
Play with this, I’m sure you can find some use for it. Just for fun, figure out how much memory is saved as opposed to writing it as two routines. For an interesting effect in “print” mode (F = 0), delete the AND F and brackets in line 1080. Notice how we can turn things on and off with the flag and logical operators? For a real challenge, chase down all of Tom’s flags to get an idea how to use sections of code efficiently (hint- start at line 1000). To use a section alternately as a main routine or a subroutine, just put IF F THEN RETURN at the end; set F to 1, GOSUB (start), and it works just like a subroutine. If F=0 the RETURN is ineffective.
If the flag is referred to more than a few times, it is more efficient to use a numeric variable than a string, even though storing a onecharacter string takes five less bytes. This is because of the compactness of such statements as IF F THEN… and GOTO (F AND …) + … (a remarkably useful tool, by the way, especially for those memory-hog menus), But there’s no reason why you can’t use a string as a flag. Strings are especially useful if more than one choice is involved, as it takes more memory to say IF A=2 THEN… than it does to say IF A$=”2″ THEN… Also it depends on your preference for indicating choices; if you prefer the more deliberate approach of inputting a number (as Tom does), you’re better off using a number for your director flag. But if you prefer the faster INKEY$ approach (as I do) you are better off using a stringy flag.
Speaking of strings, that’s our next topic. It would be nice if we could have one-byte integer arrays for such things as screen positions (PLOT or PRINT AT), integer counting, maps, etc. Well as a matter of fact, we have such a thing, but we need $’s to access it. No, not some expensive peripheral, but your ZX81’s excellent string (pron. “$”) handling ability. Most number-grinder type programmers are a little leery of strings, as it sounds suspiciously like data or word processing or something equally boring. But in fact, it’s a great way to store positive one-byte integers. Assigning is easy; to change a number to a string, use CHR$; to convert back to a number, use CODE. Review the manual sections if you’re hazy on these, they’re a worthy part of your BASIC vocabulary.
EXERCISE: (You said you wanted tutorials, and those have exercises.) Convert the F and G arrays in the SE Fit-plot routine into string arrays (E$ and G$). On a 40-point plot we could thus save about 400 bytes. HINTS: Line 5690 becomes DIM F$(PD+A), line 5830 LET F$(PF)=CHR$ TX (no need for INT). Time the difference in PLOTting time between the original and your modification. Was it worth it?
By the way, there’s a good reason to do the exercise; line 5690 & 5700 is where you’re likely to bomb in 16K if you specify a high fit density. Converting this to string arrays gives you a lot more headroom here.
I’ll leave you with a treat; the first 11 lines of CE AMP which are two simple but very useful subroutines if you’re dealing with large numbers of inter-related system parameters (e.g. all the quantities in an electric circuit). The input/print routine above shows one way of doing this; put the values in an array. Easy to input and print using a loop, but hard to follow in calculation lines if the various relationships are arbitrary. Much easier to use named numeric variables, like VCC, R1, IMAX, etc. so we can say things like LET IMAX=VCC/R1 and have the formulas easy to debug. Also, should we wish to read, say VCC, in immediate mode, we can just enter PRINT VCC instead of looking up what Vcc’s array position is.
But all those INPUT and PRINT statements! These routines get around this. Put a 4-character NAME into Z$, and a numeric VALUE into C, and call subroutine 10; it defines a numeric variable named (contents of Z$) and loads it with C. Use this for inputting or assigning values from an array.
Going the other way, subroutine 20 looks up the variable named (contents of Z$) into C. This is useful for printing. Enter the lines exactly as shown, don’t change anything before line 25 unless you modify the POKE addresses (yes, the program changes itself. See Bill Yotter’s ALGEBRA for another example of self-changing code, one that CAN be moved around. Look up the system variable at 16425… Now that wasn’t so scary, was it?) Start your program at line 100, define N1=1 and N4=4 (constants; see last issue). Example might be variables in a kinetics problem, e.g. a space craft. Hope you have a good time with this!
5 GOTO 100
10 FOR B=N1 TO N4
11 POKE 16547+B,CODE Z$(B)
12 NEXT B
13 LET XXXX=C
14 RETURN
20 FOR B=N1 TO N4
21 POKE 16640+B,CODE Z$(B)
22 NEXT B
23 LET C=XXXX
24 RETURN
100 REM INPUT
110 LET N1=1
120 LET N4=4
130 LET R$="MASSDISTV1X ACCXV1Y ACCY"
140 FOR A=N1 TO LEN R$/N4
150 LET Z$=R$(A*N4-3 TO A*N4)
160 PRINT Z$,
170 INPUT C
180 PRINT C
190 GOSUB 10
200 NEXT A
210 STOP
By the way, self modifying code is definitely TABOO in structured programming. Just try to debug it sometime, ed.