Parameter passing saves work at RUN time. (Tandy Gram) Jake Commander.
This month we have a program that should interest most Color Computer and Model I/III/4 owners. This is a tricky way to use the RUN command to pass parameters to a Basic program when you issue the command to run. When you give any command to the Basic interpreter, whether or not it is preceded with a line number, the interpreter encodes that line and changes all the Basic keywords into tokens, doing so in its line input buffer. If you add a line number to a statement which is input to it, Basic simply tucks the whole line away into the current Basic program--sorting it into line-number order as it does so. Then Basic comes back to the prompt. If you don't put a line number at the front, the line is just encoded without a line number and executed immediately. This is exactly what happens when you give a RUN command. Basic sees that the letters R, U, and N spell out a keyword in its dictionary, and it replaces this command with a token, which is the number 142 (hexadecimal 8E) on the Color Computer and Model I/III/4.
To pass operating parameters to your program, all you have to do is place them at the end of the RUN command, causing them to be placed in the buffer along with the tokenized RUN keyword. Basic is even prepared for this kind of thing in its normal mode of operation--when you say RUN followed by a line umber (like RUN 100), the interpreter uses the line number as a parameter. So as Basic does look to see if there is anything after the RUN command, any parameters passed may upset the interpreter if not sent correctly. This means that RUN must be terminated appropriately in the input buffer. Basic recognizes two situations that will prevent it from giving a syntax error. One is a line number as just discussed; the other is an end of statement.
Now if we are going to pass any parameters, we obviously can't send an end of statement in the form of a return character immediately after RUN. But as Basic also treats a colon as an end of statement, all you have to do is type RUN, a colon, and some parameters, and Basic will be happy to take this as a non-syntax error and run your program with the parameters intact int he buffer. At this point we have an option as to how the rest of the parameters are passed. We can either pass them just as we want the program to see them so that if you had, for example, parameters ONE TWO THREE (separated by spaces and spelled out), you could just type RUN:ONE TWO THREE
When using this method, the thing to be aware of is that Basic doesn't know that ONE, TWO, and THREE are parameters when it converts that input line into tokens. Thus, in that example, it would spot the first two letters of that ONE and see ON as in ON GOTO or ON GOSUB. It would therefore tokenize that ON. As long as you are aware of which tokens can possibly be encoded and can deal with those in your program, this is no problem.
Nonetheless it is something of a hassle to worry about the parameters you are passing being converted to tokens, so we could use a way to stop Basic from encoding any parameters at all. There is a method to do just this. It is accomplished with a REM statement or--even better for saving keystrokes--the apostrophe which is shorthand for REM. Any parameters you pass after this will remain untouched by the tokenizing process of the Basic interpreter. The only thing you now have to worry about is to make sure you don't try to type in parameters that will take up more than the 255-character length of logical line. So using this method, you could type RUN'ONE TWO THREE AND YOUR Basic program would (assuming it knew where to look) find the ONE, TWO, and THREE in the input buffer along with the RUN command that invoked your program in the first place.
Having taken a look at the theory, let's see how it works in practice. Both the Color Computer and Model I/III/4 use the same principle, but the address of the Basic input buffer differs for each one. Not only does it differ between the Color Computer and the others, but it can differ on the same Model I/III/4--depending on whether you are running under a disk or a non-disk Basic system. To facilitate this input buffer being moved, the Model I/III/4 stores a pointer to the Basic buffer in a special location in memory. This pointer is always stored at location 16551 (hexadecimal 40A7). By looking at this location, which is a Z80-type, two-byte pointer, you can find out where the RUN command that initiated your program is stored. One extra point: Basic shifts your input line downwards in the buffer so the RUN keyword appears at the buffer-start minus 1.
There is a slight difference on the Color Computer. The location of the input buffer on this machine is "hardwired." Instead of an indirect pointer as on the Model I/III/4, the ROM in the Color Computer takes the input buffer to be at location 733 (hexadecimal 02dd) no matter which version of Microsoft Basic is being used. Also, the RUN keyword is aligned right at the buffer start.
A Closer Look
Now that you know where to look, you can scan the locations following the RUN command to find those parameters. No sneaky machine code tricks have to be used; this can all be done from Basic. Once you have the address of the input line from that input buffer pointer (or the hardwired address on the Color Computer), you must step over the tokenized RUN command to find where the parameters start. This is just a matter of scanning for the RUN token (142 on both the Color Computer and Model I/III/4), checking for the colon after it, and then stepping over the REM or appostrophe token. Once you have found the REM statement and skipped past it, you are now pointing directly at the parameters. Then it is a matter of picking these up character-by-character until you hit the end of the input line, which is signified by a byte of 0.
This what you find in the input buffer on a Model I/III/4 without passing any parameters: RUN' = 142 58 147 251 0 RUNREM = 142 58 1470 Notice that RUN' is actually tokenized into an extra byte over the ordinary REM even though it looks shorter when typed in. Notice also that a colon (the number 58) is inserted after the RUN token even though one wasn't typed in; this is the way Basic delimits the RUN command. The number 147 is the tokenized REM, and a 251 is added (heaven knows why) if you used an apostrophe instead of a REM.
Now for a few examples on the Color Computer. The principle is exactly the same; however, on this machine the REM tokens are 130 for REM and 131 for an apostrophe (hex 82 and 83). RUN' = 142 58 131 0 RUNREM = Syntax Error RUN:REM = 142 58 130 0 RUN:3ONE = 142 58 136 69 0 RUN'ONE = 142 58 131 79 78 69 0
Except for the fact that the Color Computer won't let you get away with RUNREM, the first three lines are as in the Model I/III/4. The fourth example shows what happens when you don't separate the parameters with a REM: the ONE has been tokenized to an ON (the number 136), and a letter E (the number 69). The next line shows O, N, and E intact as the numbers 79, 78 and 69.
Once you have an idea what to look for after the RUN token, you can store any parameters in a long string containing the whole line's worth or you can separate them into substrings by using spaces as delimiters. How you process the parameters is entirely dependent on what you want your program to do. The general idea is that your program can then take its own actions according to which parameters are passed.
This can be a nice shorthand way of making your program take alternative actions. For instance, instead of making it ask such dumb questions as "do you have a line printer," you can pass this as an option along with the RUN command. Of course, to get a really foolproof, watertight version, you should have some action such as asking that question if the parameter wasn't passed or was passed ambiguously.
Listing 1 shows an example for the Model I/III/4 of this very process. It scans for the parameters ONE, TWO, and THREE and simply prints out the corresponding numbers to prove that it really understood. Listing 2 shows the changes needed to run on the Color Computer: the only changes are the input pointer (which is set to 733) and the tokens for REM.
In this program, I decided to parse the parameters into a string array for which I didn't specify a dimension so you could pass a maximum of 11 parameters this way. (Ten being the default dimension for an undeclared array, so we can use elements 0 to 10.)
The program knows it has reached the end of the parameters by checking against a variable PN, which is the number of parameters counted in. Notice that there is also a check in line 2070 for a maximum number of parameters. Without this, it would be possible to crash the program with a dimension error, because the program would just keep scanning for new strings, eventually placing one into an element of the array that wasn't dimensioned. This would be easy to overcome just by dimensioning an array to some maximum number. If you work it out, the maximum number would be the number of characters you could place after the RUN command separated by spaces, which would be four characters for the R, U, N, and apostrophe characters which, on a 255 character logical line length, leaves 251 characters for parameters.
To find the maximum, you must assume the shortest possible parameters that could be input. This would be single characters separated by delimiters such as spaces, leaving you with a maximum of 125 possible parameters. It beats asking 125 questions in the program, but heaven help you if you get one of them wrong.
In practice, most versions of Basic allow only 249 characters from the input prompt so you are left with a maximum of 122. I couldn't foresee many situations in which you would want to deal with more than ten though. Nevertheless, if you want to design a program that allows the direct input of 122 different options from the RUN command line, you now know how to do so.
One interesting parameter that can be pased is a filename. Instead of immediately asking for an input or output filename, your program can scan the RUN command line for that very file-name. There are all kinds of uses for this parameter-passing facility--all you need to know is where to look for that input buffer.