Mp December 19, 1980 Mp MP - Macro Processor 1. Introduction 2. What Mp does 2.1 The pass 2.2 The macros 2.3 Treatment of non macros 2.4 Variables and Conditionals 2.5 More on internal statements 2.6 Switches 2.7 Macro definitions and arguments 2.8 The string stack 2.9 Macro argument operators 3. Operating instructions 4. Built in default macros 5. Macro summary 6. Alternate default macro librarys 7. Examples -1- Mp December 19, 1980 Mp 1. Introduction Mp is a macro expander. It came into existance after experience with macro-11 macro libraries failed to produce an EFFICIENT (compile time) language compatible with the supported systems programming language of Rsx11 (which is regetably not C or something similar). This, along with an interest in macro processors, has resulted in a flexible medium level language translator. It is missing many of the features of a truly high level language, but it does allow intimate processor contact and full macro-11 compatibility. It is much faster than macro-11 alone (E.g. Dec's supermac) even though it must go thru 2 processors. While its not Bliss or C, it is convenient and simple to use. The purpose of this document is to explain the use of the macro processor and the language it has implemented (which by the way is the one the processor itself is written in). No attempt is made to explain any thing that a skilled macro-11 programmer should be aware of. In particular it is expected that the reader has had experience with or in developing a similar set of macro definitions such as supermac. The format of input is compatible with the assembler on the PdP-11 (Macro-11). However, the arguments inside of macro definitions are referenced by number rather than symbolically. Labels and comments are compatible (i.e. the `:' and `;' delimeters for labels and comment fields). Parameters passed to macros have the same nesting rules (i.e. <>'s etc.) but there are no Keyword macros. The ^z...z syntax is supported and the form "..." is additionally allowed (although this means that "xx must be specified as <"xx>). Arguments have selector operators which permit extracting portions of a single argument or referencing multiple arguments (e.g. arguments 2 thru arg n, where n is the number of arguments passed to the macro). In addition, there is a string stack which may be accessed globally with operators for referencing absolute elements and elements relative to the top of the stack. The stack may be divided into two stacks with two stack pointers if desired. The elements on the stack may be assigned to the local parameters of the macro. A method is available for generating labels of the form xxxnnn where nnn is a decimal number and xxx is user definable (0-3 chars). There are 50 integer variables 10 of which can be accessed directly, 40 indirectly. They can be used as a numerical stack or as an index into the string stack. Various statements exist for the conditional expansion of code either -2- 1.0 Mp December 19, 1980 Mp 1.0 from within macros or at the statement level. Target assembler directives may be placed in line and will be output unmodified. Macro construction is very convenient. One can simply edit a file with the macro definitions, envoke the processor with both input and output directed to the terminal and proceed to test various expansions. Various dump options exist to interactively see all intermediate results. If a good text editor exists, one can spawn Mp without ever leaving the editor, test a macro, exit Mp, continue editing the macros, etc. In this way, one has almost all the capability of a good interpreter. Many routines I have used were originally written by David Conroy (of C compiler fame). I found many great ideas in his code and recomend his work to anyone with an interest in good technique. 2. What Mp does 2.1 The pass Mp reads the entire source file one time and acts as a filter copying from its input to its output without modification unless it detects an opcode that is either an internal code or a user defined macro. Macros are recursively expanded until a non macro and non internal opcode are detected, at which time the statement is output unmodified. Having been designed primarily for Macro-11 preprocessing, the method of handling listings is tied to the Macro-11 .print statement and how it works in conjunction with a list suppress option. Each input line is immediatly copyied to the output after being modified as follows: ".print ;statement" When the Mp output is run through Macro-11 with listing suppressed, only the .print statements are output. The assembler address of the beginning of the statement is output with the .print statement thereby allowing easy use of the assembler debuggers, while suppressing the actual expansion of the code generated by the macros themselves. If the input is a ".MACRO" (upper case) directive, then the macro-11 macro definition is simply copied from input to the output until a subsequent ".ENDM" is encountered. No .print statements are added here. -3- 2.1 Mp December 19, 1980 Mp 2.1 Note that mostly all string matching has been made case insensitive; where there are exceptions, they will be noted. 2.2 The macros Mp accepts macros from various places. The order in which they are entered into the macro name table determines which definition takes precedence when the macro is multiply defined. The following lists the the search order for opcodes. a. The first set of names are the conditionals (these are hard- coded). b. The file which contains the source to be processed, may have macro definitions (if the /lm switch is specified - see later). They must precede all other source code and be separated from the source by a line with a slash `/' alone in column 1. c. One macro library may be specified and is a file of macros whose file-name is determined by Mp according the following rule. The default file-name is "[1,1]mplib.mac". The /ml switch changes the default name to "[your,uic]mplib.mac". A command-line option allows the setting of any file name in place of the defaults. The /ml switch then is meaningless. d. Built into Mp is a default set of macro definitions (loaded from an overlay makes processing faster). The loading of these may be suppressed by the /ni switch. -4- 2.2 Mp December 19, 1980 Mp 2.3 2.3 Treatment of non macros Normally, any statement not recognized by Mp is copied directly to the output. These statements may be any Target assembler directives provided the label: and ;comment syntax aggrees. Note that the opcode is what determines if the statement is a macro and that opcodes are not limited to the rad50 subset. However, the current implementation of Mp has additional special case statement handling which is geared to the language the author has devised using Mp. This special checking tests for assignment statements and simple machine opcodes such as clr, bit, etc. This level of statement parsing only occurs at statement level (i.e. not if from a macro expansion). It can be switched off if necessary (see /na switch later on). 2.3.1 Automatic macro calls At the start of translation of a file, A macro call is invoked internally with the time and date as parameters. It is called as $$TIM dd-mmm-yyy hh:mm:ss The default library of macros will generate an ident statement using the dd and mmm part of argument 1. The user may redefine this to anything desirable. It may be defined as a local macro which will override the default definition. -5- 2.3.1 Mp December 19, 1980 Mp 2.4 2.4 Variables and Conditionals Mp has three kinds of variables. There is an array of 50 integers, a string array of 60 variable length strings, and each macro invocation gets 20 strings. There are also 32 boolean switches. 2.4.1 Integer variables There is an array of 50 integers the first ten of which can be accessed as V0, V1, .. V9 as well as by address (0..9). There are operations which permit the inclusion of these variables (see section on arguments) and others which set them (see s%n, t%yp, and s%len internal statements). In some instances they may be accessed indirectly (as @n for n 0..9) so they can form a stack. Only the first 10 are directly accessable so the remaining 40 must be accessed in this way also. 2.4.2 Global string array variables There is a string array which can be manipulated using 5 terse statements (all begin with ?). This array of strings can be used as two stacks which is very useful for storing nested structure values such as if statements. 2.4.3 Local String variables Each expansion of a macro gets 20 local string variables which are initialized to the actual parameters of the called macro. These operate recursively like high level language functions with local variables. Each invocation gets a new set. 2.4.4 Switches (global) There are also 32 boolean switches. These may be set or reset and tested. The first few have fixed meanings, but may be set and tested just the same. They may be set from the command line but are initially all off. 2.4.5 Conditionals A variety of conditional statements exist. There is a restriction on these; they cannot have labels on them. But, labels can appear on a line by themselves preceding these statements if desired. a. I%B This expands statement if string is blank. -6- 2.4 Mp December 19, 1980 Mp 2.4 b. I%NB Expands statement if string is nonblank. c. I%IDN Expands statement if both strings are identical. d. I%DF Expands statement if strings are different. e. I%IN Compares key to each of Sn until a match is found, then En is expanded. If no match is found, then the else statement is expanded. Else may be omitted. Null strings may be present where desirable. f. I%SW switch# Expands statement1 or the else statement depending on whether the switch is set. See below for explanation of switches. g. I%R This statement tests arg for spaces, tabs, or commas and expands statement1 if any occur or the else-statement otherwise. Either may be null. This essentially determines if the arg is a `recursive' expression. h. I%EXP This tests arg to see if it is an expression. It is a specific test for the default macro library. An expression is anything that has multiple arguments where the second argument is one of the 4 arithmetic operators. Example: I%EXP I%EXP -7- 2.4 Mp December 19, 1980 Mp 2.4 2.5 More on internal statements The following internal statements are also available. i. I%DOUT This will output statement directly without further recursive expansion. This is useful when one desires to supplement code to an existing target assembler directive. One example might be to output something additional to the .END statement by defining a macro called ".END" and having it output some code and end up with an output of the actual .END directive. Comments may be output in this fassion as well. j. I%ERR This outputs a .error statement to the ti: device as well as to the file. The formats are: .error ;message to the file line n message to the terminal k. S%SW switch# This sets the switch specified. The switch is turned on if switch# is positive and off if preceded with a minus sign. l. E%S n Enters symbol into a symbol table with the integer value of n associated with it. The symbol may be subsequently looked up, see below. Example: e%s abc 5 Places abc in symbol table with value 5. m. T%YP symbol number This directive places the type value (an integer) into the number register specified or zero if the symbol is not present. Example: -8- 2.5 Mp December 19, 1980 Mp 2.5 t%yp abc 1 Places the value of symbol abc (5 from above example) into variable v1. n. D%SYM This directive dumps the symbol table as a series of macro calls. The format is as follows: $$$$$$4 n $$$$$$5 sym val $$$$$$6 n where n is the number of symbols and the $$$$$5 is expanded once for each symbol in the symbol table. The user may wish to define these macros as there is no default definition. o. S%N num0 = num1 op num2 Sets the value of numeric variable num0 (0-9) to num1 and optionally adds subtracts or compares it with num2. The valid op's are: + add - subtract L lesser of the two G greater of the two Num1 and num2 are either signed decimal numbers (16 bits worth) or v# to denote variables. There are only 10 directly accessable numbers: 0..9. However, using indirect notation, 10-49 are accessable. This is done using @n notation (e.g. @9 means indirect thru v9). See also the section on macro operands. Examples: S%N v0 = 6 + 7 ;sets numeric variable 0 to 13 S%N v0 = v6 + 7 ;sets v0 to v6's value + 7 S%N v0 = 5 ;sets v0 to value 5 S%N v6 = @5 ;sets v6 to value in variable ;pointed to by v5. S%N @5 = 6 ;puts 6 into location v5 points ;to (indirect thru v5) s%n v2 = 7 L 6 ;v2 gets the 6 (less than the 7) p. S%len num This computes the length of in characters (may be zero) and puts the value into numerical variable num. E.g. s%len 4 ;v4 gets a 3 -9- 2.5 Mp December 19, 1980 Mp 2.5 p. ** macro parm1 parm2 ... This opcode expands `macro' once for each non-blank parameters. Note that `macro' may be null to execute multiple commands on a single line. 2.6 Switches Mp reads the command line and extracts switches without using the CSI modules. Thus switches can appear anywhere on the line. They are always 2 letters and have no negation or optional data capablilies. Each switch is assigned a number (from 1 to 31) and it is this number which may be referenced in the I%SW and S%SW statements described above. There are 16 switches which are reserved for Mp and the rest can be used by the user. The switches are: 1. /nl No .print statements generated. 2. /da Invoke the debugger. See separate document. 3. /is This will print on ti: internal macros as they are read. This aids in locating a faulty macro that bombs the preprocessor. 4. /me Expands macros one level deep only (for debugging macros). 5. /ml Use local uic for standard macro library name, not [1,1] 6. /op Not implemented. 7. /tr -10- 2.6 Mp December 19, 1980 Mp 2.6 Implements the trace feature. This causes Mp to internally invoke two macro calls for every statement input. The two macro calls are passed the entire input line as parameters. One call occurs before expansion of the statement and one after. Example: input line: LET a = b affect: $$$$$$1 LET a = b (expansion of LET a = b) $$$$$$2 LET a = b The two macros $$$$$$1 and $$$$$$2 must be user defined. 8. /ni No internal macro definitions. 9. /md Expands macros and outputs (as a comment) all intermediate steps during expansion of the macro. 10. /pp Not implemented. 11. /un Outputs to the ti: device, amounts of storage used after macro libraries are read in (to determine how large table sizes should be in the task image). 12. /x2 Turns on unary operator recognition for first operator. It is not recomended that this switch be applied until all macros are read in, or any detection of the operators during macro load time will cause expansion which is not usually desirable. Rather they should be turned on by program control as needed. See see section on Macros for further information. 13. /x3 Turns on unary operator recognition for second operator. 14. /x4 Turns on unary operator recognition for third operator. 15. /x5 Turns on unary operator recognition for quote operator. -11- 2.6 Mp December 19, 1980 Mp 2.6 31. /lm Used to signal that local macros are included in the source file. 32. /na This directs the statement processor to not attempt further statement parsing when the opcode field has an unknown symbol. The remaining switches are unassigned and are as follows: switch 16 as /x6 <----| switch 17 as /x7 | switch 18 as /x8 |---{ used by default switch 19 as /x9 | macros. switch 20 as /x0 | switch 21 as /y1 <----| switch 22 as /y2 switch 23 as /y3 switch 24 as /y4 switch 25 as /y5 switch 26 as /y6 switch 27 as /y7 switch 28 as /y8 switch 30 as /y0 2.7 Macro definitions and arguments Macros have the form: $MACRO name macro text $ENDM Additional information on the $MACRO and $ENDM lines is optional and is treated as comments. Arguments are referenced as #@n where n is a hexidecimal (upper case) digit. For example, the following is a definition of a push macro. $MACRO push -12- 2.7 Mp December 19, 1980 Mp 2.7 mov #@1,-(sp) $ENDM This macro would be called (for example): push this And it would generate the following code: mov this,-(sp) One additional syntax item (not found in macro-11) is the following: An argument of the form A[B] is translated into . Macro arguments are substituted into macro text as follows: A left to right scan is performed copying text into a secondary buffer character for character until an argument reference occurs. Then the argument text from the command line is copied replacing the argument reference. When copying the argument text itself, no interpretation of argument references is made and no second pass on the line is made. Thus recursion within macro argument replacement is disallowed. 2.8 The string stack Mp has a string stack. Which is communicated to using 5 special directives discussed below. These directives may be used anywhere in a program either directly or inside a macro. However, when used outside a macro there are no parameters to receive the values from this stack and thus the results are discarded. The stack appears to be a 60 element string array. The first 24 are generally used for stack 1, 25-30 as a scratch area, and 31-60 for stack 2. Item 29 contains the string used to generate labels. There are 2 forms of this construct as follows: 1. ?? p1,p2,...,pn This format does not reference the string stack directly but generates automatic symbols of the form $$$nnn and places them into the list of parameters specified. Example: ?? 1,2 would generate (assuming no other labels have yet been gen'd) two symbols -13- 2.8 Mp December 19, 1980 Mp 2.8 $$$1 which would replace argument 1 $$$2 which would replace argument 2 If the arguments had been defined (i.e. not null) then the previous contents are lost. The `$$$' is the default and by user control can be changed to some other string. See the absolute string placement below. 2. ?- relative absolute switch and ?+ argument relative absolute switch The above are pushes (-) and pops (+) of the string stack. The `?-' expects a literal string to be supplied as . This is usually done via some reference to an argument. Example: ?- #@1 This will push argument 1 onto the stack. The second parameter (if supplied) specifies a position relative to the stack pointer and ALSO denotes that the operation is NOT actually a push, rather it is a move. Stack relative placement does not modify the stack pointer. Example: ?- #@1,2 This will place argument 1 into the stack at position 2. The top grows downward and begins at location 1 of the string stack. The third parameter (if not null) specifies that the push (actually move) is not relative to anything but is absolute. Example: ?- abc,,29 This will place the string `abc' into absolute position 29 of the string stack, thereby setting the label generation string described above. -14- 2.8 Mp December 19, 1980 Mp 2.8 The fourth argument is to conditionalize the entire operation on the status of a particular switch. This capability is not as important as it was in previous version of mp but it remains. Example: ?- #@5,,,20 This specifies that if switch 20 is set then push argument 5 onto the stack. To specify that the condition is based on the switch NOT being set then supply a negative switch number. The ?+ operation is analgous to the ?- operation with respect to the relative, absolute, and switch parameters. The first parameter is however the argument number in which to move to. This number is a decimal number. Example: Suppose we redefine the push macro such that if the argument supplied is reg0 then we will change it to R0. $MACRO push parm1 ?- R0,,25 ;put an R0 into string element 25 I%IDN reg0,<#@1> ;set switch 26 if arg1 = `reg0' ?+ 1,,25,26 ;if sw 26 set, move str 25 to arg1 mov #@1,-(sp) ;now do the actual push $ENDM The above example demonstrates some of the stack options in a macro but is not very efficient in terms of statements (the overiding concern for speed). The following shows how to do the same thing in one statement. $MACRO push I%IDN <#@1> reg0 $ENDM The second stack pointer is referenced as ++ or --. Example: ?-- abc ;pushes "abc" onto stack 2. ?++ 3,2 ;pops next to top of stack 2 into parm 3 Macros may also be expanded by the use of certain unary operators. When the scanner encounters a token (each parameter of a macro and the opcode itself) it checks to see if the first character is one of 3 operators (defined in the tkb file for mp). If it is and switches 12, 13, or 14 respectively for operators 1, 2, and 3 is also set, then an expansion of the macro $x is performed (x is the unary operator). The token is stripped of the operator and passed onto the $x macro as parameters. The $x macro is user defined. It -15- 2.8 Mp December 19, 1980 Mp 2.8 may generate code or just fiddle with the stack and switches etc. On exit however, it MUST push onto stack one a string which will replace the token which caused the expansion. Example: LET a = _function[a,b] where `_' is the unary operator would cause the following expansion to be invoked (before the LET is invoked): $_ function,a,b IF $_ returns (via a push on stack one) the string `replacement' then the let statement would be expanded as: LET a = replacement A suitable macro definition for `$_' could be: $MACRO $_ ?- replacement $ENDM The quote character (usually " but definable at mp build time) is a special case unary operator. Its interpretation depends on switch 15. If switch 15 is off, then a quoted string is treated as a macro argument in <>'s. For example: "Quoted string" is the same as: However, if switch 15 is on, then it is also treated as a unary operator but the entire argument is passed to the $" macro in one piece. For example: LET a = "Quoted string" would cause "Quoted string" to be passed to $" as: $" This allows an implementation of the $" macro to output a labeled ascii statement (possibly in a data psect) and replacing the "Quoted string" (in the LET statement) with the address of the -16- 2.8 Mp December 19, 1980 Mp 2.8 ascii string. This is basically what the default macros do. See the file mcmd.mac. Note that all the numbers specified in ?-, ?+, etc. statements can be macro parameters or numerical variables as discussed in the next section. 2.9 Macro argument operators Macro arguments may be either simple argument references or they may select portions of the argument. An argument reference which has the following format selects a portion of the argument selected. #@=n(f:l) In this format `n' is the argument number, `f' is the first character in the argument and `l' is the last. The `n', `f', and `l' parameters are all single hexidecimal (upper case) digits or vx where x is 0..9. Digits beyond 15 (`F') are permitted as no range check is performed upto the letter `Y'. Anything not a number or letter (or `Z') (or larger than the argument for the `l' parameter) is converted to the number of characters in the argument. When the format is vx this means use the value contained in numerical variable x. For example: #@=1(2:Z) ;extracts chars 2-end of arg 1 #@=A(B:D) ;extracts chars 11-13 of arg 10 #@=v3(v5:v6) ;v3 specifies which argument, v5 ;specifies 1st character position ;to extract, and v6 the last. To reference multiple arguments the following form is used: #@*(f:l) which concatonates arguments `f' thru `l'. The same rules for f and l as above apply here as well except a value larger than the number of arguments is converted to the number of arguments (not including locally defined labels). For example: #@*(2:Z) ;selects args 2 thru n-args #@*(3:B) ;selects args 3 thru 12 #@*(v1:Z) ;v1 thru last argument selected -17- 2.9 Mp December 19, 1980 Mp 2.9 The `:' is actually a special case for the `separator' character which is output between each character (in extraction of characters) or argument (multiple argument selection). The following table describes the `separator' characters currently in use. ------------------------------------------------------------------- | " double quote separates with >< | | : colon no separation (null) | | _ underline character separates with a space | | x any other char separates with x | -------------------------------------------------------------------- For example, the macro call: TEST arg1 arg2 arg3 The following argument references: #@=1(1,Z) ---> a,r,g,1 #@=1(1:Z) arg1 #@=1(1"Z) a><1 <#@=1(1"Z)> <1> #@*(1,Z) arg1,arg2,arg3 #@*(1_Z) arg1 arg2 arg3 <#@*(1"2)> Note that arguments separated by <><> type pairs do not need a comma or space between them. The >< characters are the corresponding characters defined in the Mp task build file for left and right angle brackets. If these values are changed, the characters used as >< separators in the argument selector also are changed. A special case argument reference exists which permits conditionally appending (r5) to the argument (the string appended is defined in stak.mac). An argument reference of the form: #@{n} where n is of the form used above to reference an argument number, causes the symbol table to be searched for the string defined by argument n. If it is present, and of type 1 or 2 (auto or parameter) then the string (r5) is appended to argument n and is output as the replacement. If the argument contains only digits (0..9) and an optional minus sign (-) then it is converted to #x. to make it a decimal integer. If it is preceded by a 0 then it is only output as #x i.e. octal. The 10 numerical variables discussed in the S%N internal statement can also be referenced inside a macro with a similar syntax. Any argument of the form: -18- 2.9 Mp December 19, 1980 Mp 2.9 #@#x Is replaced by (not an argument at all - rather) the number x. Example: $MACRO test mov #@#9.(r5),-(sp) $ENDM In the above, it is assumed that v9 contains an offset into register r5. If it had contained the value 10 (decimal) then the following would have been output: mov 10.(r5),-(sp) This is a useful mechanism for generating temporaries. -19- 2.9 Mp December 19, 1980 Mp 2.9 3. Operating instructions Mp uses the get-command-line routine with 1 level of indirection. It does not make direct use of the CSI parsing routines; rather it parses the command line itself. The format is: mp>OUT=IN[+MACROLIB][/sw's] or mp>file[+macrolib][/sw's] IN is the input file (def ext=.mac) OUT is the output file (def ext = .mpp) MACROLIB is an optional library of MP macros (def ext = .mac) If there is no `=' present, then "file" is converted into "file=file". In this case the extensions must be defaulted. The choice of .mpp for the intermediate file extension is to allow easy deletion of temporary files. If no command line is entered, repetitive mode is enabled. In this mode a file is translated until an eof on input. Then command mode is re-entered (like pip or tkb etc.). Thus multiple translations are permitted (usually from an indirect file). However, macros are only defined for the first command entered. This allows a set of modules to be translated with out the reloading of macro libraries (and the internal macro definitions). If new macros need to be defined the processor must be reloaded via a new invocation. One useful technique for debugging macros is the following: mcr>mp mp>ti:+lib/sw's -input and output to tty interactively test macros control-z mp>ti:/sw's more interactive tests with different switch settings control-z mp>control-z -to exit mp. This technique allows one to test a macro and switch on the macro expansion if a problem results. It is also a good way to see how a macro expands before actually writing code that uses a particular construct. One may also turn on/off switch 9 interactively to turn dumping on and off. -20- 3.0 Mp December 19, 1980 Mp 3.0 Error reporting is poor in the expander. If messages come out out on the terminal that say things like string buffer overflow or string index out of range, you have exceeded some internal limit. Check the last thing you wrote. Usually the error is caused by some butchered statement or one with unterminated <> pairs. The expander will write the offending line on the terminal. In some cases, a .error statement is generated. Some times it may be necessary to rebuild the task with a larger stack and if many local symbols are generated there is a parameter which can be increased in the tkb file. Stak.mac also has some values which can be increased if you run out of space for macros. -21- 3.0 Mp December 19, 1980 Mp 3.0 4. Built in default macros The following is a description of a language translator easily constructed using Mp. It is supplied with Mp as the default macro library, that is, the one built into the Mp task. The library's control structures were loosely copied from the C and Basic languages. In the following description: repeat* repeated 0 or more times repeat+ repeated 1 or more times thing a syntactic item (non-terminal) OTHER STUFF litteral string if upper case { } optional 0 or 1 time { }* grouping repeated 0 or more { }+ grouping repeated 1 or more Language description: This lauguage was designed to be a middle ground between C and macro-11. The concept of function as in C is maintained with calling sequence compatibility. Assembler compatibility is maintained; however, the lowest levels of machine operations (in particular all operands with side affects - the auto increment/decrement operands) are disallowed. R5 is reserved. An expression is a black box and only the arithmetic interpretation should be used. This permits some optimization. R0 and R1 are always considered volatile. R2, R3, and R4 may be used freely, but in the future notification might be required. Stack relative operands are illegal. Note that illegal sometimes gets thru without detection and just generates incorrect code. To offset the loss of some of the usual address modes, this language defines a few extended modes. In place of some lower level addressing modes the language allows for declared labels which when referenced cause the generation of code which corresponds to the mode of the declared variables. This enables functions with parameters and local variables (stack allocated). The variables local to a function are known only to a function and may be reused in other function. This is equivalent to FORTRAN subroutine or C function local variables. When referenced using the array operator, diferent code is generated depending on the mode of the declared labels. Thus the notion of higher level language variables is implemented. Of course pdp-11 machine opcodes may be placed anywhere and may reference declared variables (but not arrays) with the appropriate mappings performed. -22- 4.0 Mp December 19, 1980 Mp 4.0 Function parameters as in C are implemented as offsets to R5. In the function statement the parameters are declared as in most languages (i.e. function name[parm1,parm2...) but no type statements are required as data is strictly bytes and words. Statements recognize declared names and generate the appropriate code (just variable(r5) is all it takes). The names used as parameters (and auto variables discussed later) are treated as assembler labels with absolute values. The values assigned to these labels are the offsets to r5 which locate the contents of the variables. Thus declared names work just like variables in higher level languages. The FUNCTION statement is both declaritive and executable. It names the parameter names to be assigned r5 offset values corresponding to C function parameters. It also labels the entry point and calls the register save co-routine. The RET statement jumps back to the co-routine which restores r2-r5 and returns to the caller. All storage for local (stack allocated) variables are allocated by the function statement after the return from the register save routine in the same way a C function does this (subtract from sp). The AUTO statment relates names of local variables to offsets to r5 (below parameters) such that their contents are simply name(r5) and their address is #name + r5. Note that all AUTO statements precede the FUNCTION statement just to avoid needing some kind of start statement. Local variables can be reused in other functions since the assembler allows redefinition of labels. But this is not bullet proof and difficult bugs can be produced. Aliasing is not permitted. Branches are allowed across statements but not out of functions or internally set up routines jsr'd to. The regular assembler modes can appear anywhere a variable is permitted or expected. This is actually the only way to express some constants. This is also the method of inserting pure assembler code into a routine, but is usually done with some reference to r2, r3, or r4. Any absolute data can be referenced directly. The jsr pc,routine and rts pc type of internal routines can be used provided these internal routines are defined within the scope of the function they are called from. The scope of a function is defined to be from the first auto statement of a function thru to but not including the 1st auto statement of then next function. The overhead of a function entry is about 10-12 instructions for register save and local allocation and about another 5-6 on the return. The caller also must push all parameters, jsr, and restore the stack. Statement description: o PROGRAM {string} This statement must be the first statement in a program. -23- 4.0 Mp December 19, 1980 Mp 4.0 It initializes the library and some labels used later. It enables lower case, generatess a .title using string, uses the 1st 3 chars of string for label generation, and sets switch 15-21 to their initial values. If string is not entered then the label prefix must be setup using SETLAB (see macros for more info) and no .title is output. o AUTO label+ This optional statement equates its arguments to offsets which are asigned by macro-11. These offsets correspond to local variables defined in a C function. All autos are allocated 2 bytes by default. To allocate a different amount use the form: AUTO var[amount] ... Auto's are implemented as var(r5). The (r5) will be appended automatically. o FUNCTION name[parameter-label*] Specifies a C compatible function with arguments. The arguments are labels which the assembler equates to offsets on r5. This permits C calls and simulation of a C function. The stack is decremented according to how many auto variables are defined. Some extra stack is also allocated for temporaries when computing expressions. Parameters are implemented as var(r5). The (r5) will be appended automatically. All executable code that references autos or parameters must be placed inside the functions which define them. o RET {Expr} Returns via a call thru the C return mechanism and optionally returns a value. Stack storage for autos is deallocated. For example, a complete (nonsense) program: program sample auto i a b j k ;local variables function main[argc,argv] ;function with 2 parameters do while a ne b a := argc - 1 b := a - 1 loop for i := b downto 0 k := k + 1 loop for j := k to a step two[k] -24- 4.0 Mp December 19, 1980 Mp 4.0 if strlen[.argv[i]] := strlen[.argv[j]] printf "(%s) = length of (%s)" .argv[i] .argv[j] fi next j next i od ret 1 function two[j] ;multiplys j by 2 ret j * 2 .end The following diagram shows the overall structure of a program: |------------------------------------------------------------| | | | Required program structure | | | | |------------ | | PROGRAM lab |used once | | |------------ | | All static data here | .word, .byte etc. | |------------ | | AUTO l1 l2 l3 ... | | | FUNCTION name1[p1 p2 p3 ...] | Function 1 | | ... | | | RET [value] | | | | | | |------------- | | AUTO l1 l2 l3 ... | | | FUNCTION name2[p1 p2 p3 ...] | Function 2 | | ... | | | RET [value] | | | .end |------------- | | | |------------------------------------------------------------| Mp variables are implemented as xxx(r5) Parameters are call by value as in C. Labels may be redefined in several functions thus providing a local scope like found in C programs. -25- 4.0 Mp December 19, 1980 Mp 4.0 o STRUCT name o MEMBER name+ o END-STRUCT {name} These three macros provide an easy way to generate offset values for some structure. It simply equates the names (labels) to absolute values (starting at 0). The name of the structure is just another label which is equated to the length of all the labels declared with the MEMBER macro. Example: STRUCT person MEMBER name[20.],address[30.],zip[10.] MEMBER state,sex MEMBER occupation,birthdate[6] END-STRUCT person The following label assignments would occur: (all in decimal) name=0 address=20 zip=50 state=60 sex=62 occupation=64 birthdate=66 and person would be 72 (length of all fields) These lablels may be used as offsets to index registers or member references (see below). The structure name may be used to allocate the necessary amount of storage to accomodate the structure. o LET dest := expr | term expr ::= term {operator term}+ term ::= source | func | array | member source ::= variable | constant | register | string dest ::= variable | register | array | member func ::= function[arglist] arglist ::= argument {,argument}* argument ::= | term array ::= .variable[argument{,B}] member ::= #element[argument{,B}] operator ::= + | - | / | * -26- 4.0 Mp December 19, 1980 Mp 4.0 This statement is central in this language. All other expressions in other statements are actually handled by a recursive expansion of the let statement. For this reason it was found to be too slow if LET was a macro. Thus it has been coded directly (in its own language). See files let*.mac. The keyword LET is optional at source level (it must be there if an entire let statement is passed to another macro). Also, a function may be executed without any assignment statement (at source level). Example: a := b ;same as let a := b a[b] ;same as let r0 := a[b] Note the second example. The assignment of the return value of function `a' would be mov r0,r0 but all `mov' statements are `traped' on output and this would be optimized away. Variables are anything that can appear in the source field of a pdp-11 mov instruction. Dest may not appear on the right side unless its the first term after the `:='. Only 6 terms are allowed. When referencing functions, arrays, and using the `*' and `/' operators, r0 and r1 are used. Evaluation is strictly left to right. The & unary operator may be applied to local and parameter names. More later on & operator. A function call looks like: name[arg1,arg2...] A function generates a call to a C compatible function. The calling sequence is to push all parameters onto the stack and jsr pc,name. After the return, the caller cleans up the stack. The return from the function leaves a return value in r0 which is then used in the LET statement. R1 is also not restored across function calls. Thus references to R0 and R1 in the rest of the let statement will probably generate incorrect code. Array references look like: .array[index] The character `.' is used to distinguish between arrays and functions. An array reference causes a lookup of the symbol (array name) to determine the type. If it is a local variable then the address of the element is -27- 4.0 Mp December 19, 1980 Mp 4.0 assumed to be at index*2 + address of the beginning of the array. If the variable is a parameter to the function then it assumes the variable contains the address of the array in question (i.e. its a pointer - call by reference). If its neither, then it is assumed to be a static memory location and #array is used to compute the address of the array. The index is a C like index, i.e. it goes from 0 to n. Byte addressing is also permitted as follows: .array[index,b] A byte reference still goes into r0 and may cause sign extension. Thus there is an implied conversion of byte to word. When not at source level (i.e. from within a macro) one must know if an assignment is being made to an array. This is because the LET statement will not accept an array reference on the left hand side. Rather, the LETA statement must be used. Auto variables may be declared as pointer variables with respect to how they are treated with the array operator. Normally an auto array is declared with a specified value (in bytes) which causes the stack to be decremented on function entry (by the corresponding amount). Alternatively one can declare a single word variable as *var which only is meaningful with the array operator. In that event, the auto variable is treated exactly as though it were a single parameter variable which contains the address of an array. Example: Auto a,*b,c function test[x,y] b := x + y .b[a] := c ret This would cause the contents of x plus y to be added producing a value which is the address of the beginning of some array. Then, .b[a] would reference the a'th word in that array. (Note that .b[0] would get the first element). The arguments of an array or function can be either a term or an expression in <>'s. It is invalid to enclose a single term (or a function call or array reference) in <>. I.e. the <> pair is used to signify an expression. Member references look like: -28- 4.0 Mp December 19, 1980 Mp 4.0 #member[pointer] or #member[pointer,b] This notation is an extended index mode, that is, it takes the member name as an offset (this may even be a constant) and adds it to the pointer value (which may be an expression etc.) and uses that address to access a word (or a byte if ,b is used). Strings look like: "any string of ascii characters not including d-quote" The string is passed onto macro-11 as: .asciz _ascii string_ one can imbed non-printing characters (or " or _ itself) as follows: _<###>_ is a single character. Example: "This string contains a line-feed here _<12>_" Constants look like: a. #xxx xxx = any assembler expression b. {-}{digit}+ like 52 or -8090 or 0 c. 0{digit}+ like 0377 or 0177777 Examples: LET a := b - c / d LET a := b - fun[a,1,&c] * fun2[loc(r5),par(r5)] LET s := "s gets address of this asciz string" LET r := val[.argv[1],10] + -5 LET r := func[.array[]] LET r := #member[.array[]] Some examples using the assignment statement processor: a := b ;same as let a := b s := "string" ;same as let s := "string" f[x] ;same as let r0 := f[x] ;but f[x] returns to r0 anyway ;so the assignment to r0 is ;optimized away o IF(B) condition {OR|AND condition ... or o IF condition-code -29- 4.0 Mp December 19, 1980 Mp 4.0 relation ::= NE | EQ | LT | HI | LO | LOS | HIS | GT | GE | LE | = condition-code ::= relation | CC | CS | ON | OFF condition ::= value relation value value ::= variable | constant | register | | array | function | member The IFB statement does not support , array, member, or function. This sets up an if-block. Condition is evaluated right to left with respect to OR/AND (because of the recursive expansion technique used). Only four conditions may appear on one line. Example: IF a = b AND c = d OR e = f is equivalent to a ()'ed boolean IF (a=b AND (c=d OR (e=f))) The on/off are used for bit testing. Example: bit #4,variable IF ON ... FI Expressions are the same as in LET statements, but must be inclosed in <>'s. Example: IF GT AND .array[] NE #0 o ELSEIF(B) condition ... alternate if block, conditions as in an IF o ELSE default selection within an IF block (must follow all -30- 4.0 Mp December 19, 1980 Mp 4.0 elseif's) o FI terminate an if block Example: IF this = r0 OR foo NE 5 IF nested = example[function[i]] ... ELSEIF jim GT john ... ELSEIF jim LE john AND i NE 1 ... ELSE ... FI ELSEIF autovar HIS parameter ... ELSE ... FI o SELECT CASE variable OF introduce a case construct o CASE(B) operand {OR} One or more case statements follow the select statement. Each selects a block of code conditionally by comparing the SELECT variable with operand. Multiple case statements may be OR'd with a single block of code by using the OR keyword. The last case in the group does not have the OR. Note that a BREAK or some other transfer of control must follow the block of code (as in C) since `drop-thru' is undefined. The operand is anything permitted in the destination field of a cmp(b) machine instruction. -31- 4.0 Mp December 19, 1980 Mp 4.0 o DEFAULT executes the default code for a case statemtment. This must be the last selector in the SELECT group. o ESAC terminates a case construct. Example: SELECT case (r4) of CASE #'a ... BREAK CASE #'b OR CASE #'c ... BREAK DEFAULT ESAC The select/case group is a more compact form of a multiway IF block. However, the individual selections must be terminated by an explicit transfer of control (usually via a BREAK). o LOOP for indx = ini TO|DOWNTO|THRU|DOWNTHRU end {STEP amt} indx ::= variable | register ini ::= | term amt ::= | term end ::= | term (THRU and DOWNTHRU generate unsigned branches) Negative going loops must supply the step amount. It defaults to #1 (regardless of the existance of a DOWNTO or DOWNTHRU keyword). Loop is top tested; hence, a loop may be iterated zero or more times. -32- 4.0 Mp December 19, 1980 Mp 4.0 o NEXT indx At the end of a loop body, this adds the step value to the indx and returns to the top of the loop. indx is checked for proper nesting. Example: LOOP for i = #alpha THRU #beta STEP #2 ;step by words ... IF ... BREAK ;terminates loop FI NEXT i o DO WHILE(B) if-condition Loop as long as condition is met. if-condition is an IF statement type condition. Loop is top tested. o DO FOREVER sets up an infinite loop which is only ended via a jmp or BREAK. o OD end of the do loop. Example: DO FOREVER DO WHILE a = b ... ... BREAK IF a NE b OD OD -33- 4.0 Mp December 19, 1980 Mp 4.0 o BREAK {IF(B) ...} This causes a controled jump to the first statement following a DO WHILE, DO FOREVER, LOOP, or SELECT CASE construct. It leaves only the innermost loop or case construct currently active. If an IF statement is supplied then it is equivalent to the form: IF(B) ... BREAK FI o PUSH|POP variable These are simple stack push and pop macros. They do however check for the `&' operator (see below). Note that various commands have a (B) appended to the command or a keyword within the command. This causes a byte test as apposed to a word test. The litteral 0 is tested for and will cause a TST or TSTB to be used rather than a cmp or cmpb. If branches do not reach, then the code must be split out into subroutines. One exception is that just after the IF statement, a BEGIN-END construct is allowed which will force jmp instructions to be used. Example: IF condition BEGIN ... END ELSE ... FI The current version of the macro library supports one unary operator macro. It defines the `&' operator to be the address of an Auto variable. Internally it sets switch 14 on and off when it gets to a place where it is ready to access a variable. It only does this for a few macros however, since there is extra overhead which would slow down the processor. The currently supported statements that permit the `&' operator are: LET (including the parameters of a function call) and PUSH/POP. The If macros do not support this. -34- 4.0 Mp December 19, 1980 Mp 4.0 Example: LET a := &b This will generate code which puts the address of b(r5) into a. LET a := function.of[c,d,&e] This invokes `function.of' passing c, d, and the address of e. The function returns a value in r0 which is stored in a. A version of sprintf is found in the Main library which is interfaced via printf. It may also be called as a function directly. These along with the `main' interceptor permit one to create very small programs that enjoy many of the nice features of the C language i/o mechanism. See macros for description. Some stack and switch dump macros exist as well: Dump, dmpstr, dmpsw, peek and poke. These are usually used to test out new macros interactively but poke is sometimes used elsewhere. Setlab allows one to set the string used to make labels. The macros make use of the two stacks internally. Stack one is used for all "IF" constructs and all others use stack 2. This is so that a BREAK can be nested inside an IF block and find the innermost LOOP or CASE branch-to label. There is a macro `optimize with rx' which permits the macros to also use another register (rx can be r2, r3, or r4) to generate somewhat better code. If a lot of functions of functions or parameters that are expressions are used, then an internal temp mechanism is invoked. This is accomplished by allocating a hidden auto variable and references of the form -2.(r5), -4.(r5) etc. are generated. The optimize permits all -2.(r5)'s to be replaced with a register reference. To eliminate the possibility of the user inadvertantly using the register, .dsabl reg is issued and all the register assignments except for the one `given back' are defined. The macros will use the form %x (e.g. %2 instead of r2) thus making rx an undefined which will be caught at tkb time. This could of course be extended to using more than 1 register, but experience shows that rarely is more than 1 temp required in an expression. The assignment statement processor discussed above also checks to see if a one or two operand pdp-11 instruction is at hand. This it determines by the argument count and the fact that it is not a defined macro or an opcode which begins with `.' (dot). If it is a pdp-11 opcode then the same rules for variables and constants apply here as well. That is it is NOT just output untouched but is translated into actual pdp-11 type operands. For example: f: .word 0 ;the `.' protected the 0 ;from becoming #0 Auto a,b,c function test[d,e] -35- 4.0 Mp December 19, 1980 Mp 4.0 bit 0377,f ;generates bit #0377,f bit 25,a ;generates bit #25.,a(r5) ... This means that if you define a macro for macro-11 to process and invoke it with operands like constants and variables they will also be modified just like the bit instruction. If this is not desired then the escape operation of i%dout must be used: i%dout ;generates mymacro 0 Stack usage is as follows: If (stack 1) tos branch to on false nos go here on done (after elseif's etc) case (stack 2) tos break to label nos variable to test 3os skip over to next case test label 4os Case true (the Or label) Do while/forever (stack 2) tos break to nos jmp back to test or continue in loop label loop (stack 2) tos break to nos jmp back to test for more looping label 3os loop index 4os step parameter -36- 4.0 Mp December 19, 1980 Mp 4.0 5. Macro Summary PROGRAM string AUTO label+ FUNCTION parameter-label* RET {Expression} STRUCT name MEMBER name+ END-STRUCT {name} LET destination := term {operator term}* IF|IFB condition {OR|AND condition ... IF condition-code ELSEIF(B) condition ... ELSE FI SELECT CASE variable OF CASE(B) operand {OR} DEFAULT ESAC LOOP FOR indx = ini TO|DOWNTO|THRU|DOWNTHRU end {STEP amt} NEXT indx DO WHILE(B) if-condition DO FOREVER OD BREAK {IF(B) ...} PUSH|POP variable -37- 5.0 Mp December 19, 1980 Mp 5.0 6. Alternate default macro librarys The default library is built in at task build time. The module mcmd.mac contains a macro called "mc". This module is assembled with all text for a macro library as calls to the "mc" macro (one per line of macros in the library). The "mc" macro simply puts some pointers around the text which is assembled as .ascii statements. Then at run time, these are loaded from an overlay and scanned (as if they came from a file). The main reason for this method of loading is speed. Note that the /NI switch suppresses this loading. Useful when testing out a new library. The module mcmd.mac is assembled as: >mac mcmd=mcmd. Then the MP task is rebuilt: >tkb @mp.bld. The mcmd.obj module is NOT put in a library. One other source module is available which has some labels that allocate more or less storage for various mp internals and the area for storage of macro text. Use the /un switch to measure the remaining storage available after a library is loaded. If many levels of recursive macro expansion are required, bump the stack=xxx in the build file. The amount of storage used is kept track of and the lowest sp value detected is output at the end of each run. Running out of space may cause program failures, so be generous. The module stak.mac contains the storage which can be increased. It is assembled thus: >mac stak=ml/ml,stak. It also is not put into a library. If the processor blows up when trying to initialize itself (loading default macros) use the /is switch to have the lines echoed on the tty so one may see what line in the mcmd.mac file caused the problem. If the program runs out of space for macro library text it usually just crashes. Bump the amount of text (in bytes) and the number of lines allowed for macros. -38- 6.0 Mp December 19, 1980 Mp 6.0 The task build file for Mp also contains global definitions for the various characters used to delimit parameters. If one wishes to have > and >= etc. as operators instead of GT or GE then the enclosing brackets would need to be changed from <>'s to something else, say for example {}'s. Any macro library used would of course have to conform to these characters as well. The character definitions for the unary operators also are found there. Note that any change in the delimiter characters would probably result in some Macro-11 incompatibility. -39- 6.0 Mp December 19, 1980 Mp 6.0 7. Examples The following is an example program written using mp macros. The program supplies a system "kill" command which will get all tasks active on the current terminal (except ...mcr, prt... and the kill program itself) and issue aborts for them. This is an old example. It does not use arrays, functions, or expressions thus it knows that r0 and r1 are not destroyed. I probably should modify this example or delete it, but if you ignore the improper use of r0 and r1 it is otherwise useful in that it shows how to write a priveldged program. ;------------------------------------------------------------------; PROGRAM KILL ;outputs .title kill, `kil' used for labels ;------------------------------------------------------------------; .mcall abrt$,exit$s,dir$ abort: abrt$ AAABBB ;to be stuffed later max = 25. ;max tasks to buffer first: .blkw max last: .blkw max nfirst: .rad50 /.../ /PRT/ nlast: .rad50/MCR/ /.../ ;non-abortables (prt... and ...mcr) nabo = <.-nlast>/2 t1: .word ;temp location for task name t2: .word ;temp location tasks: .word ;# tasks found j: .word k: .word ;-----------------------------------------------------------------------; ; Main program just invokes start function. Does not use Standard Main. ; ;-----------------------------------------------------------------------; go: call start ;this is ok if function has no parms exit$s ;------------------------------------------; ; All work is done in function start ; ;------------------------------------------; FUNCTION start ;this is required since it alloc's stack mov $tskhd,r3 ;get the task list header mov $tktcb,r4 ;our tcb address (for KNOWING ourselves) mov t.ucb(r4),r0 ;get our ucb address (for matching ti's) mov #first,r1 mov #last,r2 ;first and last addrs in regs for speed -40- 7.0 Mp December 19, 1980 Mp 7.0 call $lockl clr tasks ;------------------------------------------------------------------; ; check for ucb match (our ti:), not us (tktcb not the tcb we are ; ; looking at) and task active, (negative status) ; ;------------------------------------------------------------------; DO FOREVER | | IF t.ucb(r3) = r0 AND r3 NE r4 AND t.stat(r3) GE #0 | | mov t.nam(r3),(r1)+ | | mov t.nam+2(r3),(r2)+ ;store 2 word rad50 task name | | inc tasks | | BREAK IF tasks GE #max | |...FI | mov t.tcbl(r3),r3 ;next tcb | BREAK IF t.tcbl(r3) = #0 ;null task, quit |...OD call $unlkl ;unlock lists ;---------------------------------------; ; now abort all tasks we found except ; ; for those nonabortables above. ; ;---------------------------------------; mov #first,r0 mov #last,r1 LOOP for j = #1 to tasks | mov (r0)+,t1 ;first 1/2 of tsk name | mov (r1)+,t2 ;second 1/2 | mov #nfirst,r4 ;first 1/2 of non abort tsk name | mov #nlast,r3 ;2nd 1/2 | LOOP for k = #1 to #nabo | | IF (r4) = t1 AND (r3) = t2 ;task namme match | | | jmp nokill | | |...FI | | tst (r4)+ | | tst (r3)+ | |...NEXT k | | LET abort+a.bttn = t1 | LET abort+a.bttn+2 = t2 | dir$ #abort | nokill: | |...NEXT j RET .end go -41- 7.0 Mp December 19, 1980 Mp 7.0 The following is an example program which uses some of the C compatible features though not using much of the C runtime. It references a library (at tkb time) which contains a MAIN program like the one found in the C runtime. (It does not support the Standard i/o features and only talks with the terminal thru qio's). Its purpose is to simulate some of what a C program on Unix would inherit with as small a runtime as possible. It provides a standard program interface which sees each user program being called at its main function with two parameters: argv and argc; the function returns to the main program with the exit status. Argv is a vector of addresses which point into the mcr command line buffer which has been broken into argc asciz strings. Note that any JPL rsx program can be run by simply typing the file name following by arguments exactly the same as if it were an installed task. Other instalations may not support this capability and many restrict a terminal to invoking only one such program at a time. The printf macro formats an asciz string into a scratch buffer allocated from the main library and outputs the string to ti: thru lun 5. The format codes are a subset of the Conroy C system and the portable i/o library on unix. Supported are at least %s (string), %d (decimal) %o (octal) and %c (character). The field width and leading zero suppression codes should all also work as the formating routine was taken directly for the printf routine in Conroy's C runtime. This routine was machine tested. It took 19 seconds to create a new task image from the source. ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; ; The following is just to demonstrate how to place local ; macros into a program. ; $MACRO NOPP I%DOUT <;This macro does nothing useful - but it puts this out> $ENDM / -42- 7.0 Mp December 19, 1980 Mp 7.0 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - program tst auto int,base,a ;note `a' it's not the same `a' as in ;the fact function below. (scoping!) function main[argc,argv] if argc le 1 printf "usage: factorial n" ;if no value given ret 4 ;rsx error exit stat fi a := 20 ;lets compute the base base := a / 2 ;just for this example int := val[.argv[1],base] ;get 2nd cmd line arg ;and convert to binary printf "factorial of %d := %d" int,fact[int] ;does all work here ;with call to fact ret 1 ;successful exit status ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - auto a function fact[i] a := i ;lets just use a local printf "fact recursively called with %d" a ;for this example if i le 1 ret 1 else ret i * fact[] fi ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -43- 7.0 Mp December 19, 1980 Mp 7.0 The following is an example of a Macro created to supply a construct similar to the C and Ratfor FOR loop. Recall the syntax of the FOR is: For (initialize ; test ; reinitialize) statement The easiest way to implement this statement, is to modify the syntax to keep within the allowable constructs of MP. Thus our version will be: FOR ROF Any of the three parts of a FOR loop may be null and will be interpreted as in C and RATFOR. Note the requirement that the keyword WHILE(B) be explicit in the test part of the macro call. This is to avoid needing a FORB macro. The following macro definitions supply such a statement, using the allready existing macros in the default macro library. The initialize part is simply expanded on a line by itself. The test part is converted into a DO WHILE(B) loop. If the test part is null, then it is converted into FOREVER which is consistant with the definition of the FOR loop. This is done by placing either parameter 2 into string element 25 or if it was null, FOREVER is put there. Then element 25 is placed into parameter 2 replacing any previous contents. The reinitialize part of the call is pushed in its entirety onto the string stack. The ROF macro simply pops the statement and expands it. All the leg work for label generation is handled by the DO and OD macros. $MACRO FOR #@1 I%B <#@2> <> > ?+ 2,,25 DO #@2 ?- <#@3> $ENDM $MACRO ROF ?+ 1 #@1 OD $ENDM -44- 7.0 Mp December 19, 1980 Mp 7.0 A valid macro call using this construct could be: FOR ... ROF the end -45- 7.0