When the Archimedes was first launched, with half a mega-byte of RAM or more, it looked as if there would never be any need to squeeze Basic programs to fit into the machine. But the Arc is also a voracious eater of RAM, and with screen, sprite and font space allocated, let alone RAM discs, you can soon run out of memory. And if you are trying to multi-task with less than 2 Mbytes of RAM, things feel distinctly tight. So a facility to squeeze Basic programs could be an attractive proposition.
The program listed here will do just that. It has two options: it will remove REMs and/or spaces from any program. The program is itself written in Basic to make it easier to follow, and to customise. This makes it a little slow on very large programs, but it is not likely that you will use it very frequently.
To use the program, type it in carefully, and save it to disc. Next create a short program as follows:
10*KEY1 PAGE=TOP+&200|M CHAIN"Squeeze"|M
20*KEY2 PAGE=&8F00|MOLD|M
and save this under the name "K" for keys. Type *K to set up the function keys, then load the program that you wish to compact (e.g. type LOAD"program").
If you now press function key F1, the Squeeze program will be loaded into memory above the target program, and run. It will prompt you to select REMs and/or spaces as targets for removal, and the program will proceed, indicating its progress as it goes. When it has finished, you will be able to list, run or save the compressed program in the normal way.
Function key F2 is only included for debugging purposes. If the Squeeze program stops with an error before finishing (as it would if the Squeeze program were mis-typed), pressing function key F2 will allow you to list the target program; though in this case it is quite likely that you would get a Bad program error with the target program. Incidentally, you should avoid pressing Escape while the compressor is at work for the same reason - the target program will be corrupted.
If you have opted not to remove REM lines, the program will leave REM lines exactly as it finds them, since removing spaces from such lines would not necessarily be helpful. When REM lines are removed, only those lines which begin with a REM (optionally preceded by spaces and/or colons) will be affected. When the REM option is set, the program generates a table of line number references (e.g. GOTO 100 etc.), and REMs which are pointed to by such lines are not removed (though a message is given in such cases).
When removing spaces, the program will of course avoid removing spaces from anything between quotation marks. It will also leave in the vital space between statements such as:
IFflag b%=TRUE
and will compress DATA statements without the loss of significant spaces. Because the program removes all redundant spaces from the target program, you need to be very careful how you treat a compressed program. You will not be able to edit it in the normal way once spaces have been removed - either by using cursor editing, or an editor such as Twin or the Basic Editor, and for that reason it is advisable always to keep an uncompressed copy of your work.
Essentially the program works by scanning through a Basic program in memory, and shortening it where appropriate. To do this it must take careful account of the way in which Basic programs are stored.
By way of illustration, consider the following short program:
10 REM A
20 REM
This would be stored in RAM as the following hex sequence:
0D
00 0A 08 20 F4 20 41 0D
00 14 06 20 F4 0D
FF
The program starts with byte &0D (13 decimal) - a carriage return. This is followed by the number of the first line, given as a 16 bit number (high byte first), then a byte giving the length of the line (&08 in the first line). There then follows a sequence giving the contents of the line itself, with most keywords being represented as one or two token bytes. In this case &20 (a space) followed by &F4 (the token for REM) plus &20 (a space) and &41 (ASCII A). Finally, a carriage return (&0D) signifies the end of the first line. This coding continues until the end of the program is reached, signified by &0D &FF.
In the present program, two complete scans of the target are made (unless "spaces only" is selected), the first scan is carried out by PROClinerefs. Here the object is to build up a table of line references (GOTO etc.) which will later be checked before REM lines are removed.
The main program scan is carried out by PROCscan. This picks up the length of each program line, storing it in the variable len%. The line is then checked to see if it is a REM line (does FNgetword=&F4?). If REMs are to be removed, PROCskip moves the pointers so as to delete the REM and close up the space; otherwise PROCcopy is called which copies the line across.
Spaces operate in a similar way, except that for each space found, FNremove is used to check whether the characters either side of the space suggest that it is removable or not. The process continues until the end of the program is reached, when the final bytes &0D &FF are copied over.
10 REM >Squeeze
20 REM Program Program Compressor
30 REM Version A 0.2
40 REM Author Lee Calcraft
50 REM RISC User July/August 1989
60 REM Program Subject to Copyright
70 :
80 page%=&8F00
90 MODE12
100 PRINT"P R O G R A M C O M P R E S S O R"
110 PRINT''"Remove REMs ? ";
120 dorems%=GET=ASC"Y"
130 IF dorems% THEN PRINT"Y" ELSE PRINT"N"
140 PRINT"Remove Spaces ? ";
150 dospaces%=GET=ASC"Y"
160 IF dospaces% THEN PRINT"Y" ELSE PRINT"N"
170 IF ?page%<>13 OR page%?1=&FF THEN ERROR 255,"No program in memory"
180 IF dorems% THEN PROClinerefs
190 PROCscan
200 startsize%=source%+len%+2-page%
210 finalsize%=dest%+dlen%+2-page%
220 PRINT TAB(0,12)"Initial size =",startsize%;" bytes"
230 PRINT"Final size =",finalsize%;"
bytes"
240 PRINT"Bytes saved =",startsize%-finalsize%
250 PRINT"Lines removed=",lost%
260 PAGE=page%
270 *FX138,0,79
280 *FX138,0,76
290 *FX138,0,68
300 *FX138,0,13
310 END
320 :
330 DEFPROCscan
340 PRINTTAB(0,8)"Line number"
350 lines%=0:source%=page%:dest%=page%
360 lost%=0:len%=0:dlen%=0:B%=0
370 REPEAT
380 source%+=len%:dest%+=dlen%
390 len%=source%?3
400 IF source%?len%<>13 THEN ERROR 255,"Bad Program"
410 lineno%=&100*(source%?1)+(source%?2)
420 PRINTTAB(15,8)lineno%
430 key%=FNgetword
440 data%=key%=&DC
450 IF key%=&F4 THEN
460 IF dorems% THEN
470 IF FNnoref THEN PROCskip ELSE PROCcopy
480 ELSE PROCcopy
490 ENDIF
500 ELSE
510 IF dospaces% THEN PROCspaces ELSE PROCcopy
520 ENDIF
530 lines%+=1
540 UNTIL source%?(len%+1)=&FF
550 dest%?dlen%=&0D
560 dest%?(dlen%+1)=&FF
570 ENDPROC
580 :
590 DEFPROClinerefs
600 DIM gotos%(1000)
610 PRINTTAB(0,6)"Checking line references"
620 g%=0:source%=page%:len%=0
630 REPEAT
640 source%+=len%:len%=source%?3
650 IF source%?len%<>13 THEN ERROR 255,"Bad Program"
660 A%=3:key%=0:inquotes%=FALSE
670 REPEAT
680 A%+=1
690 key%=source%?A%
700 IF key%=&22 THEN inquotes%=NOT inquotes%
710 IF key%=&8D THEN
720 ref%=FNgetref
730 gotos%(g%)=ref%:g%+=1
740 ENDIF
750 UNTIL A%>len%-5
760 UNTIL source%?(len%+1)=&FF
770 ENDPROC
780 :
790 DEFFNgetref
800 A%+=1:no1%=(source%?A%)<<2
810 A%+=1:no2%=source%?A%
820 A%+=1:no3%=source%?A%
830 =(no2% EOR (no1% AND &C0)) OR ((no3% EOR ((no1%<<2) AND &C0))<<8)
840 :
850 DEFFNnoref
860 D%=0:match%=FALSE
870 WHILE D%<g% AND match%=FALSE
880 match%=lineno%=gotos%(D%):D%+=1
890 ENDWHILE
900 IF match% THEN PRINT TAB(0,10)"Reference to line ";lineno%;" so not deleted";SPC(6):VDU7
910 =NOT match%
920 :
930 DEFFNgetword
940 A%=3:key%=0
950 REPEAT
960 A%+=1:key%=source%?A%
970 UNTIL key%<>32 OR key%>&3A OR A%>len%-2
980 =key%
990 :
1000 DEFPROCcopy
1010 FOR A%=0 TO len%-1
1020 byte%=source%?A%:dest%?A%=byte%
1030 NEXT
1040 dlen%=len%
1050 ENDPROC
1060 :
1070 DEFPROCskip
1080 lost%+=1:dlen%=0
1090 ENDPROC
1100 :
1110 DEFPROCspaces
1120 B%=0:byte%=0:inquotes%=FALSE
1130 last%=0:dlen%=0
1140 FOR A%=0 TO len%-1
1150 byte%=source%?A%
1160 IF A%>3 AND byte%=&22 THEN inquotes%=NOT inquotes%
1170 skip%=A%>3 AND NOT inquotes% AND byte%=32 AND FNremove
1180 IF NOT skip% THEN dest%?B%=byte%:B%+=1
1190 IF A%>3 AND skip%=FALSE THEN last%=byte%
1200 NEXT
1210 dlen%=B%:dest%?3=dlen%
1220 ENDPROC
1230 :
1240 DEFFNremove
1250 next%=source%?(A%+1)
1260 IF data% THEN
1270 result%=(last%=0 OR last%=&DC OR last%=&2C OR last%=&22)
1280 ELSE
1290 result%=FNleftok(last%) OR FNrightok(next%)
1300 ENDIF
1310 =result%
1320 :
1330 DEFFNleftok(v%)
1340 =v%>&7E OR v%<&21 OR v%=&28 OR (v%>&29 AND v%<&30 AND v%<>&2E) OR (v%>&39 AND v%<&3F)
1350 :
1360 DEFFNrightok(v%)
1370 IF v%=43 THEN=FNnotsave
1380 =v%>&7E OR v%<&21 OR (v%>&29 AND v%<&30 AND v%<>&2E) OR (v%>&39 AND v%<&3F) OR v%=&29
1390 :
1400 DEFFNnotsave
1410 E%=3
1420 REPEAT
1430 E%+=1:test%=source%!E%=&5641532A
1440 UNTIL test%=TRUE OR E%>A%-3
1450 =NOT test%