|
|||||||
Compiling 64K on a Shoestring by Blaise Wrenn (LexStat Systems Ltd)Those of you who have been programming with R/BASIC for some time will no doubt have encountered the annoying message: "Warning! Source code approaching #@$!%& 34K maximum." (Expletives added), and will ultimately have had a compilation fail due to too much source code in one record. I'm sure I'm not alone in wondering why the object code has to be accumulated in the same 64K memory space as that occupied by the source, but there is a way to work around the limitation. The strategy set forth in this article was developed in desperation after adding a feature to a program which caused the source to exceed the 34K limit and because there was no easy way to strip out a reasonably sized section of the program to an external subroutine. I had already written and used a pre-compile utility which squeezed out most extraneous spaces and removed all comments. Looking at a cross-reference listing of the program I realised that several long internal subroutine names and several long variable names were called many times. If I could shorten them, the resulting code might just squeak by the 34K barrier. Checking the manual for rules on variable/identifier names, I noted that a variable identifier had to start with a letter, but could then be followed by numbers or periods and dollar signs. Several iterations of reducing meaningfully-named variables such as LINE.NUMBER to L$ and SAVE.NUMBER to N$ ensued. (I chose the $ sign as a termination character because I never ordinarily use it, and it would indicate a substituted name.) The scheme worked beautifully. Then another feature was added. Bang! Over the limit again. Another round of swaps. Another feature, another round of swaps, and so it went. I finally reached the point where I had used just about every letter from A$ to Z$. As it would happen, at this point I had to chase a bug -- something to do with LINE.NUMBER. Damn, was it L$ or N$? Well, I changed the wrong one, and the program completely fell over (remember, I am used to writing in a high level language). It was clearly time to maintain a table of changes, and come to think of it, why not automate the whole process? So, here's the drill: As this is a simple-minded solution, in your original source, use meaningful variable names, and avoid the use of variable names which are words which comprise the R/BASIC programming language or @variables or portions thereof. Be sure to have a space after every variable name (i.e., don't use statements such as LINE.NUMBER=1 rather, use LINE.NUMBER = 1 instead). Construct all internal subroutine names in such a way that they can be distinguished from variables. I use the form VERB.ADJECTIVE.OBJECT (e.g. OPEN.DATA.FILES, PARSE.USER.INPUT, GENERATE.REPORT.TOTALS, etc.). This has an obvious beneficial side-effect. Generate a cross- reference listing of your source. (A utility to do this is supplied on the REVMEDIA volume 4 utility diskette and is called XREF_PROG.) Number your subroutines S0$ through S99$ (or even higher). Assign the most often used and longest-named variables two-character identifiers A$ through Z$. Assign other variables three-character identifiers AA$ through ZZ$ (here's a chance to be a bit more descriptive!). 0001 * SWAP - Blaise Wrenn, LexStat Systems 0002 *------------------------------------------------- 0003 EQU BLANK TO ' ' 0004 EQU NUL TO '' 0005 DECLARE SUBROUTINE MSG, FSMSG 0006 0007 SENTENCE = TRIM( @SENTENCE ) 0008 CONVERT BLANK TO @FM IN SENTENCE 0009 IF SENTENCE<1> = 'RUN' THEN 0010 FILENAME = SENTENCE<4> 0011 RECORDNAME = SENTENCE<5> 0012 END ELSE 0013 FILENAME = SENTENCE<2> 0014 RECORDNAME = SENTENCE<3> 0015 END 0016 0017 GOSUB OPEN.DATA.FILES 0018 READ RECORD FROM F.BIG.SOURCE, RECORDNAME THEN 0019 READ SWAPS FROM F.SWAPS, RECORDNAME THEN 0020 COUNT.SWAPS = COUNT(SWAPS, @FM) + (SWAPS # NUL) 0021 FOR INDEX.SWAPS = 1 TO COUNT.SWAPS 0022 LINE = TRIM( SWAPS< INDEX.SWAPS > ) 0023 * Check that swap data actually exists 0024 IF LEN( LINE ) THEN 0025 * Check for comment line 0026 IF LINE[1,1] # '*' THEN 0027 NEW = FIELD( LINE, BLANK, 1 ) 0028 OLD = FIELD( LINE, BLANK, 2 ) 0029 MSG(INDEX.SWAPS : "|" : OLD : "|" : NEW, 'UB', IMAGE, '') 0030 SWAP OLD WITH NEW IN RECORD 0031 MSG('', 'DB', IMAGE, '') 0032 END 0033 END 0034 NEXT INDEX.SWAPS 0035 WRITE RECORD TO F.OUT, RECORDNAME 0036 END ELSE 0037 MSG('Unable to read swap information!','','','') 0038 END 0039 END ELSE 0040 MSG('Unable to read source file!', '', '', '') 0041 END 0042 STOP 0043 0044 OPEN.DATA.FILES: 0045 OPEN 'BIG.SOURCE' TO F.BIG.SOURCE ELSE 0046 FSMSG() 0047 STOP 0048 END 0049 OPEN 'SWAPS' TO F.SWAPS ELSE 0050 FSMSG() 0051 STOP 0052 END 0053 OPEN FILENAME TO F.OUT ELSE 0054 FSMSG() 0055 STOP 0056 END 0057 RETURN Create two files, one called SWAPS and one called BIG.SOURCE. Create a record in the SWAPS file with the same name as your program source which you should place in the BIG.SOURCE file. In this record assign all subroutine swaps first, followed by the variable swaps, one swap per line, with the short name first, followed by at least one space, and then the name of the subroutine or variable identifier. For example: S1$ GENERATE.REPORT.TOTALS S2$ OPEN.DATA.FILES S3$ PARSE.USER.INPUT A$ ANSWER B$ BIN.NUMBER C$ CHARACTER.COUNT Invoke the SWAP utility (see below and also on the REVMEDIA Volume 4 utility diskette) in the form: SWAP destination.source .filename program.name It will apply the substitutions assigned in the program.name record in the SWAPS file against the source contained in the program.name record in the BIG.SOURCE file, and store the resulting smaller source code in the file identified by the destination.source.filename parameter. At this point (if you are a diehard RevG user [there are at least two of us left]) you might want to run the squeeze program against the source to compress it further before compiling. (That utility is also on the REVMEDIA Volume 4 utility diskette and is called SQUEEZE.) By using this strategy I have been able to write source code of up to nearly 64K, however, if your usual choice of variable names tends towards terse cryptic codes anyway, then u won't c s drmtc a cmprsn s I did (nor do u dsrv 2). (Volume 4, Issue 7, Pages 4-6) |
|||||||
| |||||||