Trailing-Edge
-
PDP-10 Archives
-
SRI_NIC_PERM_SRC_3_19910112
-
midas/midas.info
There is 1 other file named midas.info in the archive. Click here to see a list.
-*-Text-*-
MIDAS Node: Top, Up: (DIR), Next: Invoke
Overview of MIDAS
MIDAS is a PDP-10 assembler. It takes as its input an ASCII file,
and produces a binary file in any of several formats (*Note Out: Output.)
NOTE: Numbers used in this document are assumed to be octal, unless
followed by a "." in which case they are decimal. E.G. 12 = 10. = ten.
* Menu:
* Invoke:: How to invoke MIDAS. Commands strings.
* Switches:: MIDAS command string switches.
* Interrupts:: Terminal Interrupt Characters.
* Basic:: Introduction to MIDAS input syntax.
* Example:: Simple example of MIDAS code.
* Frame:: The beginning and end of a MIDAS program.
* Words:: Syntax of the "Word" -- the fundamental MIDAS construct.
* Fields:: Words are made up of fields, which are made of syllables.
* Syllables:: A syllable is a number, symbol, etc.
* LocnCtr:: The "location counter" is used to assign storage locations
to the words which MIDAS assembled.
* Output:: Output formats.
* Relocation:: Relocatable assemblies.
* Symbols:: Defining symbols.
* DDT:: Communicating to DDT.
* Numbers:: Hairy ways of writing numbers.
* Literals:: Generating constants in memory where you want to use them.
* Bytes:: Pseudos for working with bytes and byte pointers.
* FileInfo:: How filenames can be accessed at assembly time.
* Terminal:: Typing on the terminal. Reading from the terminal.
* Macros:: MIDAS macros: definition and use.
* Loops:: Assembly-time iterations for use with macros.
* Cond:: Assembly conditionals.
* Arithmetic:: .I and .F: the "arithmetic assignment" statements.
* FASL:: Assembling code to be loaded by MacLisp.
* Blocks:: Symbol table block structure.
* Constructs:: Alphabetical index of constructs (special characters).
* Pseudos:: Alphabetical index of pseudo-ops.
* LIB: (LIB) The subroutine libraries for MIDAS programs.
* Outformats:: More obscure details about output formats (RIM, etc)
* Changes: (MIDAS ARCHIV)* MIDAS changes in chronological order.
MIDAS Node: Invoke, Up: Top, Previous: Top, Next: Switches
MIDAS Command Strings
Once you have run the program MIDAS, it will ask for a command line
on which you must specify the names of the input and output files. In
addition to the filenames, the command line usually contains switches.
*Note Switches: Switches.
MIDAS can write four output files for each assembly: a binary
output file, an error file containing whatever would normally appear on the
terminal, a listing file, and a cross-reference table file. The
cross-reference table file (a compact binary file, not a DEC cref listing)
is not really useful now that the @ program exists (*Note @: (@)Top.).
The listing output file is also inferior to an @ listing, but it is
sometimes useful for debugging hairy macros. To facilitate this use, it is
possible to ask for a listing of both passes (some macros cause fatal
errors on the first pass). The binary output file is essential; an error
output file is also very useful as a permanent record of the assembly
errors.
Normally, one specifies either just the input file or the input
file and the binary output file. The error output file name is allowed to
default from them. MIDAS tries hard to default all filename components so
as to minimize your type-in. Not specifying the binary file name is
equivalent to letting each component of the name default based on the input
file name.
Defaulting in MIDAS works bidirectionally; the input file name can
default from the binary file name, and the binary file name can default
from the input file name. This is actually useful. Note how the Twenex
GTJFN-from-the-terminal scheme of things cannot provide this!
The binary file's device defaults to DSK. The directory defaults
to your working directory. The second filename defaults based on the type
of output format used: BIN for SBLK files, FASL for FASL output files, or
REL for relocatable output files. The first filename defaults to the input
files's first filename. One exception: if TTY: is specified as the input
file, and the binary file name is not specified, no binary file is made.
The input file's device and directory default to those of the
binary file. The second filename defaults to ">" for ITS and "MID" on DEC
systems. The first filename defaults to that of the binary file. If
no first filename is specified for either file, the default is "PROG".
This becomes relevant sometimes when TTY: is specified as the input device.
Exception: if the binary file was on device PTP or NUL, the default is DSK.
These defaulting rules make common things very convenient.
To assemble a file FOO;BAR > onto the same directory, you can use
FOO;BAR_
letting the input file names default. To assemble it onto your directory,
just say
FOO;BAR
letting the binary file names default.
* Menu:
* Switches:: Command line switches.
* Interrupts:: Terminal interrupt characters.
MIDAS Node: Switches, Previous: Invoke, Up: Top, Next: Interrupts
Switches:
Switches should be enclosed in parentheses or preceded by slashes,
and may go anywhere in the command string. The effects of a switch in no
way depend on where it appears, although it should not appear in the middle
of a file name as that may confuse the filename parser. Any number of
switches may be enclosed by one pair of parentheses, as in (ET) which is
equivalent to (E)(T) or /E/T. Each slash is good for just one switch.
It is sometimes significant whether a switch occurs once or twice: (TT) is
not the same as (T).
/C Produce a CREF file.
/E Produce an error output file.
/L Produce a listing.
(LL) List on 1st pass as well as 2nd.
/T Read assembly input from the terminal after the TITLE
statement, on pass 1. You can type in parameter assignments
to control conditional assembly.
(TT) Read from the terminal on both passes.
/W Do not print on the terminal.
This implies the (E) switch, so your errors are not lost.
/W works by setting the .TTYFLG variable initially to 1.
MIDAS Node: Interrupts, Previous: Switches, Up: Top, Next: Basic
Terminal Interrupt Characters.
In the ITS and Twenex (I believe) versions, the characters ^H, ^V and ^W
have an instantaneous effect if typed on the terminal while MIDAS is
running.
^H Causes an error message "^H-BREAK"
(which says where the assembly has reached)
followed by the .INSRTion of TTY:.
Thus, this is a sort of "break-loop" you can use
to debug runaway macros. To exit, cause an end-of-file
on the terminal input.
^V Decrements .TTYFLG.
Since type-out on the terminal happens only when .TTYFLG is
negative or zero, this undoes the effect of one ^W.
(some very bad errors that .INSRT TTY:
also zero .TTYFLG before typing an error message.)
^W Increments .TTYFLG, suppressing terminal type-out.
It is not wise to do this if you don't have an error
output file (since after the assembly has been started
it is too late to start writing one).
In the Bottoms-10 version, you can get a ^H-break by typing
^C and then Reenter.
MIDAS Node: Basic, Previous: Interrupts, Up: Top, Next: Example
Machine Instructions in MIDAS
The main body of a MIDAS program is composed primarily of machine
instructions -- just as you would expect.
The PDP-10 machine language instruction breaks down into five
fields of bits: a nine bit opcode field, a four bit accumulator (AC) field,
a one bit indirect field, a four bit index register field, and an 18 bit
memory address field. The instruction set is documented very clearly in
the "DecSystem-10 System Reference Manual" (DEC-10-HGAC-D, and the later
versions), which is published by the manufacturer, Digital Equiptment Corp.
("DecSystem-10" is a pretentious salesman's name for the PDP-10).
In MIDAS, the fundamental syntactic construct is the word. Its
components parallel the fields of a machine instruction, but it is flexible
enough to be used for all other purposes as well. This is because the
subunits or "fields" which compose a word are simply added together, into
parts of the 36-bit word determined by the pattern of spaces and commas
that separate them. Every word of data assembled into the binary program
is written in MIDAS as a syntactical word of some sort or other, with the
sole exception of multi-word text strings. *Note Words: Words, for full
details.
Words are terminated and separated by the "line terminators", which
are Return, Linefeed, and "?". Return and Linefeed should always be used
together, to prevent confusion when editing the program. Strictly
speaking, Return Linefeed contains a blank line, but blank lines are
ignored anyway.
The value of the words that appear in a MIDAS program become the
words of the binary program output. Each word is assembled at the
address specified by the MIDAS "location counter", which increments by one
after each word to put consecutive words in consecutive locations. This is
called "assembling a storage word". The ultimate goal of assembling a
program is to assemble storage words, but word quantities can appear in
other contexts such as to be passed as arguments to functions. Before
each word in the MIDAS input, at the beginning of the line, there may be
one or more "labels", which are symbols which are defined to equal the
current value of the location counter. Thus, a label preceding a word is
defined to be the address of that word. *Note LocnCtr: LocnCtr.
The other most important constituent of a MIDAS program is the
comment. A comment starts with a semicolon ";" and ends with the following
Return. The whole thing is equivalent to a single Return. Another way to
think of it is that you can put a comment at the end of any line in the
file; but "lines" terminated by the "?" character do not count.
A few other MIDAS constructs may appear in place of a word. One is
the parameter assignment, which defines a symbol with an explicitly
specified value at assembly time. *Note Assign: Symbols. Another is the
"Statement". Everything that must be specified in a MIDAS program aside
from the contents of words to be loaded and the values of symbols is
specified by means of statements. A statement begins with a symbol which
is the name of a "pseudo-op"; it is a sort of escape which directs MIDAS
to perform some special function. Some pseudo-ops read arguments. The
syntax of the arguments depends on which pseudo-op it is. An example of a
statement is the END statement, which must appear at the end of every
program. It starts with the symbol END. After that comes the program's
starting address, which is syntactically a word (and thus, terminated by a
"?" or Return or Linefeed). If a Return immediately follows the symbol
END, then there is no argument (and no starting address).
The separation into lines should not be thought of as a
hard-and-fast rule. The "line-separating" characters USUALLY have that
effect, but in some contexts (such as text-string constants) they may have
NO special effect. <cr> and "?" are both "line-separators", but in a
semicolon-comment they do not act the same: a <cr> ends the comment, but a
"?" is ignored as "part" of the comment. Semicolon usually starts a
comment, but not inside a text string. Thus,
MOVE A,B ;COMMENT ? MOVE C,D
assembles only a single instruction. The moral is that the meaning of a
character depends on its context, which depends on the characters IN FRONT
of it. MIDAS does not work by BNF-like grammatical rules ("a MUMBLE can be
either a FOO or three BARs in a row") although a few constructs can be
approximated by them. It works like a finite-state machine: "In the normal
input-reading state, if a <cr> is read, terminate the line and process it;
if a question-mark is read, terminate the line and process it; if a
semicolon is read, enter the comment-reading state. In the comment-reading
state, if anything but a <cr> is read, ignore it; if a <cr> is read, return
to the normal input-reading state and terminate the line and process it."
MIDAS Node: Example, Previous: Basic, Up: Top, Next: Frame
Simple examples of MIDAS code
MOVE C,COUNTA
TLNE B,100
SETZ C,
JUMPGE A,@DISTAB(B)
JRST LABEL1
Each of these lines will generate one storage word in the output.
The first line will generate a MOVE instruction that will load the
contents of memory location COUNTA into accumulator C. This is accomplished
by putting the value of the symbol C in the instruction's AC field,
and the value of the symbol COUNTA in the address field, and then adding
the value of the symbol MOVE (which supplies the appropriate value in the
op-code field). The second line is similar, except that the address field
is specified by the octal number 100 instead of a symbol. That is a
common thing to do with instructions like TLNE which use the address
as a bit-mask. The third line is a SETZ instruction. The address field
has been omitted, because the SETZ instruction ignores its address.
In fact, the address field will be assembled as zero. The SETZ has been
indented one space as a note to humans that the preceding TLNE instruction
can skip over it. The fourth line
is a JUMPGE instruction which demonstrates indexing and indirect addressing.
The "@" turns on the instruction's indirect-bit, selecting indirect
addressing. The "(B)" puts B's value in the index field. Presumably,
B is the number of an accumulator which is to be used as an index register.
The fifth line shows a JRST instruction, for which it is not necessary
to specify an accumulator. In fact, the accumulator field of the instruction
will be assembled as zero; but instructions that actually use accumulator
zero should say so explicitly.
So in the simple cases, MIDAS agrees with the format used in the
"DecSystem-10 System Reference Manual."
MIDAS Node: Frame, Previous: Example, Up: Top, Next: Words
The Framework of Every MIDAS Program
A MIDAS program consists primarily of PDP-10 instructions and
data, together with labels and comments. But other things are usually
or always needed at the beginning and end of the program.
The first thing in every MIDAS program (or every subfile of
one, for that matter) is a line containing ";-*-MIDAS-*-" to tell
EMACS how the file is to be edited.
The second thing in a MIDAS program, which is not actually
needed for small programs, is a .SYMTAB statement which says how large
a symbol table is required. The argument to .SYMTAB is the desired
number of entries in the symbol table. This must include the
predefined symbols, the user-defined symbols, and some extra to make
hashing efficient. It should also be prime. To help you set up your
.SYMTAB, MIDAS prints the symbol table size and the number of entries
used at the end of the assembly.
Next, in every MIDAS program, should come the TITLE statement,
which consists of TITLE followed by a line of text, and performs these
functions:
1) for relocatable assemblies, the first symbol following TITLE
becomes the name of the relocatable program being assembled;
2) the text following TITLE is printed on the terminal on each pass.
3) when the /T switch is used to request input from the terminal,
this input is read when the TITLE statement is reached.
Because of 3), it is sometimes useful to define a few symbols before
the TITLE statement so that they can be used when giving input on the
terminal. If there is no TITLE statement, /T won't do anything!
After the TITLE statement, you should define names for the
accumulators. Single letters starting with A=1 are best. Accumulator
0 should also have a name, but not in sequence with 1, since 0 can
only be used for special purposes. But don't ever write an
instruction which actually USES accumulator 0 without putting in the
name of that accumulator. The stack pointer should be in accumulator
17, which should be named P. Putting the accumulator definitions on
page 1 will produce the best results in @ listings.
After this point, you are on your own. But at the end of the
program, you need an END statement. The END statement consists of END
followed by the starting address of the program (this is an old assembler
tradition). You can omit the starting address if you don't want your
program to have one (in a relocatable program, this is often the case). If
you don't have an END statement, you get an error. This catches many
errors in assembly conditionals and macro definitions which cause the
END statement not to be recognized as such. Any text which follows the END
statement will be ignored completely by MIDAS.
MIDAS Node: Words, Previous: Frame, Up: Top, Next: Fields
Words, and Their Syntax
The "word" is the most commonly used MIDAS construct, as a storage
word to be assembled must be, syntactically, a word, and an ordinary PDP-10
instruction is an example of a word. However, the word construct includes
other things than instructions, and is used in other contexts besides that
of storage words. This section of the manual describes how to put a WORD
together. The concept of a WORD is tied closely to those of SYLLABLES and
FIELDS, out of which WORDS are made. Loosely, a syllable is a number or
symbol, and a field is an arithmetic expression. Words are made up of
fields, and fields are made up of syllables. Do not confuse a MIDAS field
with a "field" of bits in a PDP-10 machine language word.
A word is one or more fields connected by field separators, with
optionally an indirect bit or index field anywhere among them. There
are two field separators, space (or horizontal tab) and comma. (Space and
horizontal tab are identical and will both be referred to as space.) To
improve readability, spaces before and after a word and spaces adjacent to
a comma are ignored, and more than one space in a row are treated as one.
The values of the fields are combined to form the values of the
word according to the number of fields and the pattern of separators. These
formats are described in the following table, in which A, B, and C are
fields. "TR(x)" is used to represent the result of truncating x to 18. bits.
pattern format # value
in octal
====== ======= =====
,,C 13 TR(C)
,B 14 TR(B)
,B C 15 unassigned
,B, 16 unassigned
,B,C 17 unassigned
A 20 A
21,22,23 not possible
A B 24 A++TR(B)
A B C 25 A++TR(B)++TR(C)
A B, 26 A+<<B&17>_23.>
A B,C 27 A+<<B&17>_23.>++TR(C)
A, 30 A
31 not possible
A,, 32 <TR(A)>_18.
A,,C 33 <TR(A)>_18.+TR(C)
A,B 34 A++TR(B)
A,B C 35 unassigned
A,B, 36 unassigned
A,B,C 37 unassigned
Here are some examples of what these formats are most useful for:
A B,C This is the normal instruction format, e.g., CAMN A,FOO
A B This is good for instructions with no accumulator field,
e.g., JRST BAR
A B, and this is for instructions with no address field, e.g.,
SETZ D,
A,,C This is the standard way to specify the contents of a
storage word by half-words, e.g.,
BLETCH: -PDL,,PDB
NOTES:
1) "+" means normal 36-bit addition, but "++" addition is special in
that carry from bit 18 to bit 17 is suppressed. In other words, when the
user specifies a field which is supposed to be a right half-word field, he
can normally rest assured that his quantity didn't carry over into the left
half.
2) A word may have more than three fields. In that case, the
fourth and following fields are added in with the third field. Thus,
if the third field is truncated to 18. bits and added in, so are the
following fields. Only spaces may be used for separating fields after
the third field; a comma after the third field is an error, but will
be treated as a space. Commas are flagged so as to catch places where
the accidental omission of a <cr> has run two lines together.
4) The user can redefine these formats by using the .FORMAT pseudo-op.
That is what the "Format number" of a format is used for, and the pseudo
is documented here:
.FORMA fno,fval
Inserts an entry in the format table (ie replaces old entry) for
format number "fno". The numeric value of field fval is taken as three
12-bit bytes referring to the (up to) three distinctly handled fields in a
word: the left 12 bits refer to the rightmost field, the middle 12 bits to
the field next to the rightmost (if any) and the right 12 bits to the field
2 from the right and any additional fields. (If there is only one field in
a given format it is the rightmost regardless of punctuation which may be
required after it.)
A 12-bit byte describing a particular field is in turn treated as
two 6-bit bytes. The right 6-bit byte specifies a mask and the left 6-bit
byte specifies a shift. The mask number (say M) directs that only the
right M bits of the field value be taken. The shift number (say S) directs
that the bits remaining after masking be shifted left S bits. The fields,
after this masking and shifting are added to give the value of the word.
(Example: the 12-bit specification 2704 describes an accumulator field:
<f&17>_23.)
There are three exceptions to the above procedure. (1) If a field
is specified as 0022 (right half, not shifted) the carry out of bit 18 is
suppressed as the field is added into the word. (2) A virtual quantity may
only occur in a field specified 0044, 0022, 2222, 0504, or 2704. (3) If as
a syllable in the leftmost field of a word appears any of the eight I/O
instructions (DATAO, DATAI, CONO, CONI, BLKO, BLKI, CONSZ, CONSO) then any
field in that word specified 2704 is instead taken as if specified 3211
(I/O device field)
In addition to the fields and separators that make up a word, there
can be an indirect bit and an index field. These subconstructs resemble
the fields of the word in that their values are merged into the value of
the word. They differ from the fields in that they are marked out by their
own special syntax, and are interpreted in the same way no matter where in
the word they appear (unlike fields, which are all the same and are
interpreted according to their position in sequence).
a. The Indirect bit
The character @ (atsign) is a special character. It may occur
anywhere inside a word. Whenever MIDAS encounters an @ inside a word, a 1
is ORed into bit 22. of the word, i.e., the indirect bit. The @ does not
terminate syllables or fields, nor is it taken as part of a syllable or
field.
Its position in the word is totally irrelevant. However, the
normal convention is to put it in front of the field specifying the right
half of the word (the address) if there is one; if not, put the @ where the
address would go.
b. The Index field
To get a certain quantity into the index field, the easy, convntional
way is to use a bracketed word of the parentheses type. For example,
MOVE A,FOO(D)
will put the value of D into the storage word's index field. As explained
in section C.2.d, the parenthesis bracketed word works in its strange way
because the index field of the machine word is the lowest four bits in the
left halfword.
The index field, used this way, may appear anywhere in the word
except in the middle of a syllable or following an arithmetic operator, but
the normal convention is to put it at the end of the field specifying the
right half of the word (the address) if there is one. When "(" appears
following an arithmetic operator, it signifies a type of bracketed word,
rather than an index field.
MIDAS Node: Fields, Previous: Words, Up: Top, Next: Syllables
Fields
A "field" in MIDAS is essentially an arithmetic expression. A
field may be either a single syllable, or two or more syllables combined
arithmetic operators. Many MIDAS pseudo-ops take arguments that are
syntactically fields. These are the arithmetic operators, in their order
by priority:
char. operator
===== ========
highest _ left shift 1st operand by # of bits specified by 2nd
operand. (Negative 2nd operand shifts right)
& bitwise AND
# bitwise XOR
\ bitwise OR
* and / 36. bit integer multiplication and subtraction
lowest + and - 36. bit integer addition and subtraction
Thus all _'s are done first, then &'s, then #'s, then \'s, then *'s
and /'s, and finally +'s and -'s. Operators of the same priority will be
performed in left-to-right order. Examples: (assuming that A, B and FOO
are numerically defined symbols with the values 1, 2, and 53 respectively.)
Field Value
===== =====
3+4 7
1+4&FOO+1 2
1+4&<FOO+1> 5
The first example is rather trivial. The second and third
demonstrate the use of angle brackets as algebraic parentheses (actually,
part of the syntax of syllables). In the second, 4 is anded with FOO,
giving zero, then 1+0+1 equals 2. In the third example, first FOO is added
to 1, giving 54. Then 54&4 gives 4, and then the 4 is added to the 1,
giving 5.
Note that there may NOT be spaces within a field; so "4 * 5" is
not the same as "4*5".
MIDAS Node: Syllables, Previous: Fields, Up: Top, Next: LocnCtr
Syllables
There are several types of MIDAS syllables. A symbol may be a
NUMBER, a NUMERICALLY DEFINED SYMBOL, a QUOTED CHARACTER, a BRACKETED WORD,
or a call to a PSEUDO-OP (but see MACROS) (Note: only "value-returning"
pseudo-ops can be used to make syllables. Other pseudo-ops will either be
ignored by the process of building words and fields out of syllables
(except for their side effects), or illegal to be used except alone on a
line).
Each syllable has a value, which is a 36.-bit quantity.
a. NUMBERS
The simplest form of number is an octal integer, which is just a
string of digits. Following them with a "." makes it a decimal integer
instead.
*Note Numbers: Numbers, for other sorts of numbers, which you won't
need very often.
b. SYMBOLS
A symbol is a string of SQUOZE characters which is not a
number. More precisely, it is a string of characters from the
SQUOZE character set of length 1 or greater which contains at
least one letter, or at least one % (percent) or $ (dollarsign),
or at least two .'s (periods). The SQUOZE set includes all 26.
letters, all 10. digits, and the characters $ (dollar sign), %
(percent sign), and "." (period). (Note that the symbol "." (a
single period) is special; *Note LocnCtr: LocnCtr.).
Here are some examples of symbols:
LOC3
GOHERE
$END
A%LOCATION
35X
1.2.3
.$Z%.G
(The last example is NOT considered an example of good programming style.)
A symbol has no length restriction, but MIDAS only looks at the first six
characters, so the symbols THISLOCN and THISLO, for example, are
effectively identical.
MIDAS symbols can have several sorts of definitions. The symbols
that can appear as syllables are those with numeric definitions (other
sorts of symbols might appear at the same places, but they would be
interpreted differently and would constitute different constructs). MIDAS
provides many predefined numeric symbols (including all the PDP-10
instructions, and others specific to the operating system), and programmer
can define others (*Note Define: Symbols.).
An example of numerically defined symbols:
In the word MOVE B,FOO , there are three symbols: MOVE, B, and FOO.
The symbol MOVE is predefined; the other two must be defined by the
programmer.
c. Quoted Characters:
A quoted character starts with any one of the characters ' (single
quote), " (double quote), or ^ (upasrrow) followed by a character. The
quote or uparrow and the character following are taken as a syllable. A '
followed by a character has the value of the SIXBIT representation of the
character. A " followed by a character has as its value the ASCII
representation of the character. ^ works the same way as " except that the
ASCII value is ANDed with 77 octal; that is, only the low six bits are
kept. ^ is used for generating the ASCII code for "control" characters.
Examples of quoted characters:
Syllable Value
======== =====
'A 41 octal
"+ 53 octal
^C 3 octal
d. Bracketed words:
A word surrounded by ( ) (parentheses), < > (angle brackets), or [
] (square brackets) is a syllable called a BRACKETED WORD. Each works in
its own way:
<word> is simply a syllable whose value is that of the word between the
brackets. Angle brackets act much like algebra's parentheses,
and are usually used that way.
(word) works two different ways. If the preceeding character is an
arithmetic operator, the value of the word has its halves swapped,
and this becomes the value of the syllable, on which the arithmetic
operator acts. If the preceeding character is not an arithmetic
operation, the value of the word is swapped and saved, and at the
end of the outer word, it is added into the word being formed.
This quirk is so that (5) stuck at most places in a word will put 5
in the index field.
[word] is a LITERAL, or CONSTANT. *Note Literals: Literals. The value of
the syllable is the location where MIDAS put the literal.
It is hard to give examples of ( ) and < > until some further
concepts have been introduced, so these have been delayed.
e. Pseudo-ops which return a value:
Pseudo-ops are instruction given by the programmer to MIDAS in the
input ASCII file about how to assemble various things. They are described
in section E, "Pseudo-ops that every programmer needs," and in the section
on pseudo-ops.
They are the "built-in functions" of the MIDAS assembly-time
programming language. Some pseudo-ops are used for their side-effects;
some, to compute values. Calls to value-returning pseudo-ops constitute
syllables, whose value, of course, is the value returned by the pseudo-op.
MIDAS Node: LocnCtr, Previous: Syllables, Up: Top, Next: Output
The Location Counter
Normally, when MIDAS finishes reading a Word at top level in the
input file, the value of that word is assembled into the binary program
output. The place where it will be loaded is specified by the location
counter, which is normally an 18.-bit number. After assembling each such
word, MIDAS increments the location counter.
The value of location counter is explicitly available as the value
of the symbol ".". It refers to the address of the current word, not the
following one. A label defines a symbol to equal the current value of ".".
The location counter can be set with a LOC statement, or by
assigning a value to the symbol "." (*Note .=: Symbols, for how to do
that). These have slightly different effects when an offset is in effect.
In a relocatable program, the location counter can be set to a relocatable
value or to an absolute value.
The usual way to leave space for non-constant data in a program is
the BLOCK statement.
BLOCK 200
leaves 200 words of space, by incrementing the location counter by 200 .
It is an error to ask for a block of negative length.
In relocatable assemblies, the location counter starts out at
relocatable zero. In absolute assemblies, it starts out at absolute 100
octal.
Sometimes it is necessary to assemble code at one location and copy
it to another before using it. When that is done, all references to labels
in the code (whether from within it or outside it) should be arranged so as
to be correct when the code has been moved to its ultimate position. This
can be done by defining an offset. The statement
OFFSET 200
causes all references to the location counter -- the symbol ".", labels,
etc. -- to add 200 to the real value of the location counter. If the code
being assembled is moved 200 words upward, it will be at the addresses at
which the program will refer to it.
Offsets do not affect the actual location counter, which is the
place at which code will actually be loaded. They affect the value of the
location counter as seen by references to it from within the program.
The difference between LOC and assigning a value to "." is that LOC sets
the real location counter, whereas assigning "." sets the value of ".":
that is, it subtracts the offset and then sets the real location counter,
so that when the offset is added in again to get the value of "." it will
equal the value assigned.
An offset can be cancelled by setting the offset to zero.
A frequent use of the offset is for error checking. If there are a
series of symbols FOO, BAR, QUUX ... with values 0, 1, 2 ... intended to be
the values of a particular table index, with MAX being 1 larger than the
largest legal index, the table can be defined with
TABLE: OFFSET -.
FOO:: <table entry for FOO>
BAR:: <table entry for BAR>
QUUX:: <table entry for QUUX
...
MAX:: OFFSET 0
Then, if any entries are missing from the table or in the wrong order, one
of the labels will report a multiply-defined symbol error. The double
colons are for half-killing; *Note Labels: Symbols.
MIDAS Node: Output, Up: Top, Previous: LocnCtr, Next: Relocation
Output formats
MIDAS can produce its binary output in any of several different
formats. The format used is specified by the user with pseudo-ops (see
section on pseudo-ops). The currently available formats are:
SBLK the usual format in ITS.
FASL used for programs which are meant to be loaded by LISP.
.DECREL compatible with the D.E.C. operating system.
.DECSAV SAV files produced directly, without taking time to link.
RELOCATABLE relocatable binary to be loaded by STINK.
Any MIDAS output format consists primarily of 36-bit storage words,
which are intended for loading into memory locations of a job. The output
also includes a symbol table (a list of the names and values of all the
symbols defined by the programmer), and some information on how to load the
file into core memory.
SBLK format is documented in ITSDOC;BINFMT >, and the format of the
SBLK file symbol table in .INFO.;DDTORD >.
MIDAS Node: Relocation, Previous: Output, Up: Top, Next: Symbols
Relocatable Assemblies. Specifying the Type of Output.
Like MACRO-10, MIDAS is capable of making both relocatable and
absolute assemblies. The type of assembly is controlled by pseudos
appearing in the program, with a default which varies with the operating
system. In an absolute assembly, all location counter values (and all
expression values, in fact) are completely known at assembly time, or else
are undefined. In a relocatable assembly, while some location counter
values and expression values may be known at assembly time, most depend on
an unknown relocation which will be determined only by the linking loader.
The location counter value is likely to be a known quantity plus the
unknown relocation. Symbol values can also have that form (and will, when
the symbol is defined as a label when the location counter has that form).
Expression values can take the form of a known quantity plus any number
times the relocation that number being the "relocation factor". Values
completely known at assembly time are called "absolute", and have a
relocation factor of 0. Simply relocatable quantities, such as typical
location counter values, have relocation factors of 1. MIDAS can handle
higher or negative relocation factors internally but cannot write them into
the output file except in STINK format output.
Whether an assembly is absolute or relocatable is tied closely to
what output format is used. The formats that exist are
SBLK Standard ITS absolute format.
This is the default on ITS.
RELOCATABLE STINK format (relocatable).
Used only on ITS, and not much.
.DECREL Standard DEC relocatable, for LINK10.
This is the default on non-ITS systems.
.DECTWO <n> Standard DEC two-segment relocatable.
<n> is the segment boundary, usually 400000 .
.DECSAV Standard DEC SAV file format. Absolute.
.FASL MACLISP FASL file format. Relocatable.
RIM Read-In Mode format for PDP-6. Absolute.
RIM10 Read-In Mode format for PDP-10. Absolute.
The names of the formats are all pseudo-ops which you can put in
the program to specify those formats. If the format you want is the
default for your particular system, you don't need to specify it, but if
you can anticipate that your program will be assembled on other systems it
might be wise to do so anyway.
A few functions are available for manipulation of relocatable
quantities. These include .ABSP, .RELP, and .RL1. .ABSP and .RELP return
the absolute and relocatable parts of a quantity (the relocatable part is
the relocation factor. .RL1 is a symbol whose value is a pure
relocatability of 1; that is, a relocatable zero. You can think of these
as analogous to the the complex number functions Re, Im and the constant i.
.RELP .RL1 is nonzero if the asembly is relocatable.
MIDAS Node: Symbols, Previous: Relocation, Up: Top, Next: DDT
Defining Symbols
You have already been told how to use symbols and what names they
are allowed to have. Here is how to define them.
A symbol can have these types of definition (or none at all):
numerically defined symbols, pseudo-op names (*Note Pseudos:Pseudos.), and
macroinstruction names (*Note Macros: Macros.). Here we are concerned only
with numerically defined symbols. New pseudo-ops cannot be defined by the
user, so the initial supply is all you get, but pseudo-op definitions can
be copied from one symbol to another using EQUALS.
There are basically two ways to define a symbol numerically: as a
LABEL, and as a PARAMETER.
1. Labels.
The primary use of symbols is to hold the address of an instruction
or variable in the program. MIDAS has a special construct, the LABEL, for
defining such symbols. A label is simply a symbol followed by a colon, and it
can appear at the beginning of any line. Its effect is to give the
symbol a value equal to the address where the next storage word will go.
A line can have several labels in it, but a label may not appear after
any other construct has begun. A label may be followed by anything at all,
or it may be the only thing on its line. An example is:
FOO: MOVE A,TABLE(C)
which assembles a storage word containing a MOVE instruction, and
also defines FOO as the address of that instruction.
If a symbol is defined as a label, it can have only one value.
If anything in the program tries to define the symbol with a different
value, either before or after the symbol's appearance as a label, an
error message will be typed. It is legal to define the symbol again
with the SAME value. In fact, that happens to every label, since it
is seen on both passes of the assembly.
The parameter assignment "<symbol>=:." has the exact same
effect as the label "<symbol>:", which is allowed for convenience's sake.
2. Parameters.
There are other uses of numerically defined symbols besides
their use as labels. MIDAS also allows definition of symbols by
PARAMETER ASSIGNMENT. A parameter assignment is a line of the form
<symbol>=<word> or <symbol>==<word>
which tells MIDAS to compute the value of <word> and make that the value
of <symbol>. This is similar to the "assignment" statement of most
mathematical languages, such as FORTRAN. Using the == construction
makes the symbol half-killed in DDT (this is explained in the section
on OUTPUT; for now, suffice to say that the == form is the one you
probably want to use.) Here is one of the ways to use this feature:
Say a programmer is writing a program which knows how to handle
four FOOBAR's. If in the future he should want to modify the program
to handle five or six FOOBAR's, there might be many places where the
program would have to be changed. Now if he had made the number of
FOOBAR's an assembly parameter, by defining a symbol as in:
NFOOBR==4
and writing all of the program to work for NFOOBR FOOBAR's
Whenever there is a table or block of data whose length must
be referred to by the program, that length should be expressed
by a numeric symbol.
Symbol definitions actually have a static scoping or block
structure as in Algol or PL/I. *Note Blocks: Blocks.
MIDAS Node: DDT, Previous: Symbols, Up: Top, Next: Numbers
Communicating Information about Symbols to DDT
One of the most important things about symbols in assembler
programs is that they are passed to DDT. MIDAS has several features
designed specifically for communicating with DDT.
If DDT, needing to print the value 205, chose at random a symbol
whose value was close to 205, it would be likely to find several names for
bits in various registers. It is essential to have a way to tell DDT which
symbols ought to be used for such type-out. This is done by "half-killing"
the symbols which ought not to be used.
The most common way to half-kill a symbol is to duplicate the colon
or equal sign used to define it. Thus, FOO==200 says that FOO is to be
half-killed and should never be printed out, while FOO=200 allows FOO to be
printed out. FOO:: defines FOO as a half-killed label.
If those methods of half-killing are not convenient, the .HKILL
statement is available. .HKILL followed by a list of symbols half-kills
those symbols. If the symbol .HKALL is nonzero, then all labels defined
are half-killed.
.KILL can be used to avoid sending a symbol to DDT at all. It is
followed by a list of symbols to kill. MIDAS does not forget the value of
a symbol when you .KILL it, so .KILL is not the same as EXPUNGE. .KILL
causes the symbol to be forgotten only when the symbol table is written
into the output file.
The NOSYMS statement can be used to avoid outputting any symbol
table at all.
MIDAS Node: Numbers, Previous: DDT, Up: Top, Next: Literals
Details of the Syntax of Numbers
A string of digits in which no digit is preceeded by a period forms
an INTEGER, a type of syllable whose value is the value of the number
interpreted in the CURRENT RADIX (octal by default, but see PSEUDO-OPS).
However, an integer followed by ' (single quote) is interpreted as octal,
and one followed by . (period) is interpreted as decimal, regardless of
the current radix. A string of digits with a . (period) to the left of
some digit forms a FLOATING-POINT NUMBER, interpreted in decimal.
Either of these may be followed by a ^ (uparrow) which works
something like scientific notation. An integer may be followed by an
integer as
A^B
which would have the value of
B
A*R
where R is the radix in which A is expressed. The result is fixed-point.
A floating point number may also be followed by a ^ and an integer, as in
X.Y^A
which is interpreted as
A
X.Y*10.
that is, as scientific notation. The result is still floating-point.
Also, any of the preceeding numeric formats may be followed by a _
(backarrow, or underscore) and an integer, which multiplies the value by
integer
2
The integer is interpreted in the current radix, but may
be forced to decimal or octal by terminating it with . or ' as explained
above. The result is always a fixed-point number, even if the first number
was floating-point!
Examples of NUMBERS:
(the current radix is taken to be octal)
Syllable Value
======== =====
23 23 octal
23. 27 octal
3^3 3000 octal
3.^3 5760 octal (3000. decimal)
1_3 10 octal
23_10. 46000 octal
3.5 3.5 floating-point
3.5^4 35000.0 floating-point
3^3_4 60000 octal
1.5_3 14 octal (Note: NOT floating point!)
MIDAS Node: Literals, Previous: Numbers, Up: Top, Next: Bytes
Literals
Normally, when you write a machine instruction in an assembler, you
specify the memory operand by its address. Sometimes, it is desirable to
refer to "a word containing X" without worrying about where that word is
going to be stored. The LITERAL is a construct that permits just this.
A literal consists of any number of lines enclosed in square
brackets ("[" and "]"). MIDAS assembles the lines enclosed into locations
of its own choosing. The value of the literal, where it appears, is the
address of the location chosen by MIDAS to hold the first word of the
literal. For example,
MOVEI A,[1 ? 2 ? 3]
would load accumulator A with the address of a three-word table whose
contents are 1, 2 and 3 in successive words. It is equivalent to
MOVEI A,FOO1
....
FOO1: 1 ? 2 ? 3
where FOO1 is a non-existent label by which you can imagine that MIDAS
connects the usage of the literal with the location of its contents.
Unless you request otherwise, all literals will actually appear at
the end of your program (that is, at wherever the location counter was set
when the END statement was encountered). However, you can alter this with
the pseudo-op CONSTANTS. Whenever CONSTANTS appears, all saved-up literals
will be "dumped out" or assigned locations starting at the current location
counter. CONSTANTS may appear any number of times (up to 75 or so), but
must appear the same number of times and at the same locations on both
passes.
On pass 1, MIDAS doesn't know where a literal is going to be
located until the CONSTANTS statement is seen. On pass 2, the location of
the CONSTANTS statement is known in advance (remember, it must be the same
as on pass 1), but the location of the literal is still not known until the
end of the literal (so that recursive literals can work). For these
reasons, labels inside literals cannot work, so they are not allowed.
The symbol "." inside a literal refers to the location from which the
literal is being referred to, not the location where the literal will
appear.
If you set the variable .LITSW nonzero, any use of a literal is an
error (until you set the symbol to zero again).
MIDAS Node: Bytes, Previous: Literals, Up: Top, Next: FileInfo
Manipulating Bytes and Byte Pointers.
MIDAS provides several built-in functions for making and using byte
pointers of the form suitable for the LDB and DPB instructions.
When you want to make a byte pointer from a given numeric position and
size, simply write the byte pointer left half as an octal constant. There
is really no way to improve on that, given that you are going to be using
numbers. For example, 440700,,FOO is suitable for ILDB'ing the ASCII
string starting at word FOO. It is common practice to define symbols for
byte pointer left halves and then use them to represent fields, as in
ASCBP==440700
...
MOVE A,[ASCBP,,FOO]
A more sophisticated symbolic way of referring to fields uses the .BP
function. .BP takes as an argument a mask for a field in a word, and
returns a byte pointer to that field (in address zero). Thus, <.BP 7770>
would return 031100,,000000. When the argument to .BP is terminated with a
comma, the comma is not eaten, as it would be with most functions.
Instead, the comma turns into a space. Thus, .BP 7770,FOO returns
031100,,FOO, a byte pointer to the 7770 field in address FOO. In addition,
parentheses inside the argument to .BP are part of the argument to .BP, not
part of the word in which .BP occurs. .BP is most useful with symbolic
names for fields or bits in a word. For example,
LDB A,[.BP (%TOERS),TTYOPT]
would get into A the contents of the %TOERS bit in the left half of TTYOPT
(%TOERS being a bit symbol suitable for use in a TLNE instruction).
The reverse of .BP is .BM. It takes a byte pointer (ignoring the
address) and produces a mask for the specified byte. The left half of a
byte pointer may be passed in the right half of the argument word. This
Is useful when you have defined a symbol for a byte pointer left half and
want the corresponding mask. Unlike .BP, .BM follows the ordinary
conventions for arguments. Example: .BM 030600 returns 770.
Bytes can be extracted from and deposited into quantities at assembly
time using the .LDB and .DPB functions. .LDB takes a byte pointer
(ignoring the address) or a byte pointer left half in the right half, and
also a quantity as second argument. It returns the contents in that
quantity of the specified byte. .LDB 030600,1234 returns 23.
.DPB takes as arguments a byte value, a byte pointer (or left half in right
half, etc) and a quantity, and returns a new quantity made by changing the
specified byte of the old quantity to contant the byte value. For example,
.DPB 11,030600,4444 returns 4114.
The .IBP function increments a byte pointer. It accepts a byte pointer
and returns the incremented pointer. .IBP <440700,,> returns <350700,,>.
The address supplied with the byte pointer is part of the incrementing
process; thus, .IBP <440700,,FOO> returns <350700,,FOO> and
.IBP <010700,,FOO> returns <350700,,FOO+1>. If the left half of the
argument is zero, the argument is swapped, so .IBP 440700 returns
<350700,,>.
Two functions related two byte manipulation are .LZ and .TZ, which return
the number of leading zero bits and trailing zero bits, respectively, in
their arguments. .LZ SETZ is 0 and .TZ SETZ is <35.>. When applied to
zero, both return <36.>.
For assembling words into memory made by packing bytes together, you
could use .DPB, but a special feature exists for this purpose, called
.BYTE. .BYTE takes a list of byte sizes, and enters a special mode in
which "words" assembled at top level are stored, not into successive whole
words, but into successive bytes of the specified sizes. The sizes are
used in the order specified, over and over cyclically. Here is an example:
.BYTE 12.,6
2222
33
1111
44
.BYTE
would assemble a single word, made up of a 12. bit byte, a 6 bit byte, a
12. bit byte, and a 6 bit byte. The contents would be 222233,,111144.
Note that .BYTE with no arguments is used to return to ordinary non-byte
mode.
Byte mode inside literals and other bracketed groupings is independent
of what is going on outside the grouping. That is, groupings always start
out in ordinary (non-byte) mode, even if byte mode was in effect outside;
byte mode may be turned on inside the grouping, but when the grouping ends
the state of byte mode will be restored to what it was before the grouping.
For example, <.BYTE 7 ? ^M ? ^J> is a quantity whose value is a CRLF in
ASCII. It is equivalent to ASCII/
/.
While byte mode is in effect, the location counter is a byte pointer.
Its value is such that an ILDB instruction on it would fetch the next byte
to be assembled. This is historic; it may well be that a better
convention would be that the location counter be such that a LDB would
fetch the next byte to be assembled. As it is, you must use .IBP to get
such a byte pointer. It works to set the location counter to a different
byte pointer, though it is recommended that you make the size correct.
If the byte pointer is inconvenient for you, .BYTC may be more useful. Its
value is the total number of bytes assembled since byte mode was entered.
The insides of literals and groupings do not count.
.WALGN (word-align, not wall-generate) in byte mode advances the location
counter to the beginning of a fresh word. It does nothing in normal
non-byte mode. .BYTE with no arguments automatically does a .WALGN.
MIDAS Node: FileInfo, Previous: Bytes, Up: Top, Next: Terminal
Obtaining Information on Filenames at Assembly Time.
One of the most common things one wants to do is to assemble a program's
version number into the program. This can be done, on ITS and Twenex,
using the symbol .FVERS, whose value is the version number of the main
input file, and .IFVRS, whose value is the version number of the current
input file if that file has done no .INSRTs, or the version number of the
last file .INSRTed by the current input file.
More information is available in the variables .FNAM1 and .FNAM2, which
hold the sixbit filenames 1 and 2 of the main input file, and .IFNM1 and
.IFNM2, which hold similar information for the same file as .IFVRS.
These variables are available on Bottoms-10, where there are no version
numbers, as well as on other systems. On ITS, the version number is a
function of the filename 2, made by taking all the digits in it and
converting them using base ten to a number.
The names of the output file (in sixbit) can be found in .OFNM1 and
.OFNM2. The version number is not available, since on ITS it does not
exist.
MIDAS Node: Terminal, Previous: FileInfo, Up: Top, Next: Macros
Interaction with the Terminal.
The simplest and standard way to print a message on the terminal is with
the PRINTX operation. It takes a string, delimited as for ASCII, and types
it out. For example, PRINTX /FOO/ will type FOO, with no carriage return.
.TYO is a function that takes a single character, as a number, and types
it. .TYO 101 will print an A.
Another useful function is .TYO6, which takes an argument which is
interpreted as sixbit and printed out. .TYO6 is useful primarily with
.FNAM1 and .FNAM2.
Error messages are printed on the terminal, of course. The .ERR function
signals an error, taking the rest of the line as error message. Along with
the error message will go the usual information of filenames, page and line
number, location counter both absolute and relative, and macro depth.
There are no special facilities for putting any variable information in the
error message because the macro features can be used to do this.
There are two ways to request input from the terminal. You can ask a
specific question, or you can give the user the opportunity to enter any
MIDAS code he wants. To allow him to enter arbitrary text, do
.INSRT TTY:
which reads input from the terminal, ended by ^C or ^Z. If you want it to
be done only on pass 1, you must put it in an IF1 conditional.
The /T switch can be used in the command line to cause a .INSRT TTY: to be
done in a program which does not actually contain one. It will be done
right after the TITLE statement.
To ask a specific question, use .TTYMAC, which a sort of macro that reads
its arguments from the terminal. .TTYMAC looks like a macro definition
except that there is no macro name in the define line; just an argument
list, followed of course by the macro body and a TERMIN. Instead of
defining a macro which must be called later with arguments specified
explicitly, .TTYMAC defines a nameless macro which is called immediately,
getting the arguments by reading as many lines as are necessary from the
terminal. *Note TTYMAC: Macros, for more information. Here is an example:
PRINTX /Value of BAR = /
.TTYMAC FOO
BAR=FOO
TERMIN
MIDAS Node: Macros, Up: Top, Previous: Terminal, Next: Loops
MIDAS Macros.
Maybe someday this file will be INFO-ized.
@. GENERALITIES.
It is often useful, when the text to be assembled
has some pattern, to cause it to be computed, rather than
putting it all in the file to be assembled. This reduces the number
of possible typos and makes it easy to change all occurrences
of a particular construct.
To do this, one needs to be able to define a function which will
be evaluated at assembly time, with the result being text to be assembled
in place of the call to the function. Such a function is called a MACRO.
In thes document, "EOL" means either CR or LF;
"open" means "<", "(", "{" or "["; "close" means ">", ")", "}" or "]".
"the EOL or CRLF is thrown away" means that the EOL encountered as
should have been previously described is thrown away, and if the EOL
is a CR and the following character is a LF, the LF is also thrown away.
Thus, either a CR alone, a LF alone, or a crlf may be used, and will be
flushed at that point.
A. MACRO DEFINITIONS.
0. GENERALITIES.
A function definition must specify the name of the function, the
formal parameters (called DUMMY ARGUMENTS when macros are concerned)
and the expression to be evaluated when the function is
called - or in the case of a macro, the text to be assembled when
the macro is called.
In MIDAS, a macro definition is introduced by the pseudoop
DEFINE, which should be followed by the macro name.
After that, on the same line, come the names of the dummy
arguments, perhaps followed by a comment.
The text of the macro starts on the next line, and continues
until the DEFINE is matched be a TERMIN.
The character that ends the TERMIN is gobbled up.
1. MACRO DEFINITION EXAMPLE.
DEFINE FOOBR AA,B
MOVE A,AA
CAIL A,B
POPJ P,
TERMIN
A call to that macro might be:
FOOBR ZZZ,10
which would assemble into
MOVE A,ZZZ
CAIL A,10
POPJ P,
2. THE DEFINE LINE
The "DEFINE" line is the first component of a macro definition.
It beginss with the "DEFINE" pseudo (which needn't actually be at the
beginning of a line). Next comes the name of the macro to be defined,
optionally preceded by an explicit block name (eg DEFINE FOO"BAR to
define a macro BAR in the block named FOO, rather than in the
current block). After the macro name come the dummy argument names,
followed optionally by a comment. In any case, the define line
extends through the first CRLF or EOL after the DEFINE.
a. bindclasses - semantics.
Most higher level languages have several bind classes.
The function definition specifies one of the bind classes for each formal parameter,
which is used in decoding a call to the function,
to decide what value to give to the formal parameter.
Macro dummy arguments also have bind classes, which say three things:
what to do if the dummy is UNSPECIFIED
(that is, if, in a particular call, the argument list runs out before
this dummy is reached)
or NULLSPECIFIED (the argument for this dummy is left out),
the alternatives being NULLIFIED, GENSYMMED and DEFAULTED
(See B.8 for the meanings of the three options);
which argument syntax to use for the dummy when
processing calls to the macro (There are 6 different argument
syntaxes available in MIDAS: NORMAL, WHOLELINE, BALANCED, STRUNG, KEEPSTRUNG,
and EVALUATED. See B.2 through B.7 for descriptions of them.);
and how the actual arguments are to be associated with the dummies,
the alternatives being by ORDER and by KEYWORD.
b. bindclasses - syntax.
In MIDAS, it is not necessary to specify the complete bindclass
of each dummy with that dummy's name. Only the ways in which its
bindclass differs from that of the preceding dummy are specified,
by means of special delimiters between the names of the dummies.
The first dummy is given the default bindclass (nullified normal by order)
unless special delimiters precede it. The delimiters are:
{[(< which cause following dummies to be balanced,
>)]} which cause following dummies to have normal syntax,
* which causes following dummies to be strung, or, if
strung was already the selected syntax, causes them to
be normal (This is called "turning strungness on or off"),
& which turns keepstrungness on or off,
# which turns evaluatedness on or off,
? which turns balancedness on or off,
- which turns wholelineness on or off,
\ which complements gensymmedness,
+ which switches between by order and by keyword,
: which reverts to the default in all ways (normal syntax,
by order (not keyword), and not gensymmed).
"/" is a delimiter which is like "-" except that if it follows
a dummy immediately, wholelineness is complemented before the dummy
rather than after. That is, "FOO/" is equivalent to "-FOO".
This is mainly for compatability with older versions of MIDAS.
A dummy is defaulted iff its name is followed by "=".
The "=" should be followed by the desired default value,
whose syntax is that of a normal macro argument.
Beware: the dummy argument delimiters do not terminate default values,
and default values are read in with the normal argument syntax
regardless of the specified syntax of the dummy being defaulted.
Thus, DEFINE FOO \(A,B=100)C makes B's default value be 100)C, which
is probably not what you wanted.
If a dummy is specified as gensymmed, and is also given an explicit
default value, the explicit default overrides the gensymming.
c. DEFINE line example.
DEFINE FOBR \A#B\C:D,+E=BAR,F,G(H,I=X,)*J*+-K
would give FOBR the dummies A,B,C,D,E,F,G,H,I,J,K as follows:
dummy bindclass (argument syntax, what to do if unspecified).
the first few dummies are by order:
A normal, gensymmed
B evaluated, gensymmed
C evaluated, nullified
D normal, nullified
the next few dummies are by keyword:
E normal, defaulted to "bar"
F normal, nullified
G normal, nullified
H balanced, nullified
I balanced, defaulted to "X"
(note that the comma after "I=X" is necessary because
the ")" would otherwise be part of the default
value of I. after dummy names not given default values
a comma is not necessary, although an extra one can't hurt.)
J strung, nullified
the next dummy is by order.
K wholeline, nullified
3. THE MACRO BODY
The macro body is the text string to be substituted in place
of a call to the macro. The macro body specified in a macro
definition starts with the first character after the CRLF or EOL
that ends the define line, and continues through the character
before the TERMIN that ends the macro definition. Remember that
the character ending the symbol "TERMIN" is thrown away.
WARNING: ANY occurrence of "DEFINE" or "TERMIN" inside a macro body
will affect MIDAS's determination of where the body ends. This is
true regardless of whether you intended them to or not. Just because
the "DEFINE" was in a text string or a comment does not change this.
"Terminate" and "Terminal" must also be avoided. If you need to put a
"DEFINE" or "Terminal" into a macro body, use .QUOTE (See c., below).
The same holds for any other MIDAS pseudo-ops that require a matching
TERMIN, such as IRP and .TTYMAC.
a. dummy argument substitution.
Whenever the name of one of the dummy arguments appears in the macro
body specified in the definition, it represents a request for the value
of tht dummy to be inserted at that point when a macro call
is expanded. Dummy argument names are recognized only when surrounded
by non-squoze characters, so neither of the dummy names "A" and "B" occurs
in the string " AB ", but both occur in " A B ". The way dummy
argument substitution is implemented is tht the dummy argument names
are recognized when the macro is defined, and replaced by special
characters. Therefore, if the name of a dummy argument is created by
the expansion of a macro call (perhaps part of the name came from the
substitution of the value of another dummy) that dummy-name will
not be replaced by the dummy's value. In other words, text produced
by substitution for dummy arguments is not rescanned for occurrences
of dummy argument names.
b. concatenation.
Suppose it is desired to have the substituted value of the dummy
argument FOO followed immediately by the squoze character X.
It will not do to put "FOOX" in the macro body, because "FOO" is
not considered to occur in "FOOX". "FOO X" will cause the value
of FOO to be substituted, but the space will remain between it
and the "X". The way to win is to use the concatenation
character "!". "!" can delimit the names of dummy arguments, as
any other non-squoze character can, but it alone is thrown away after
doing so. For example, "FOO!X" will cause FOO to be recognized,
but followed immediately by an "X".
Similarly, the TERMIN tht ends the macro definition must be
preceded by a non-squoze character, which normally becomes part
of the macro definition. If it is desired for the macro definiton
to end with a squoze character, separate it from the TERMIN with
an "!", which will be thrown away. "!"'s that appear in the macro
body not adjacent to a dummy agument name, a .QUOTE, or the final TERMIN
are not thrown away; this makes it possible to put "!"'s in macros.
c. .QUOTE
It is possible to prevent recognition of dummy argument names
in a part of the macro body by using the .QUOTE pseudoop. The
pseudo is followed by a text string like the argument to ASCII et al.,
which is not scanned for dummy argument names. The first character
of the text string will follow the character before the "." of the
.QUOTE, and the last character before the closing delimiter will
come before the character after tht delimiter, in the macro as it
will be defined. The .QUOTE, to be recognized, must be
preceded by a non-squoze character, so in order to make it
possible for a quoted string to follow a squoze character, an "!"
before a .QUOTE is deleted.
Not only dummy names, but TERMIN, .QUOTE, and the pseudos
matched by TERMINs (DEFINE, .TTYMAC, and the various flavors of IRP)
are not detected or treated specially when within a .QUOTE .
d. inner macro definitions.
In order to make it possible to define a macro which will, when
called, define another macro, it is possible to insert matching
DEFINE - TERMIN pairs in a macro definition. A definition is
ended not by the first TERMIN after the DEFINE, but by the first
TERMIN that is unmatched by previous DEFINEs (or other pseudos
such as IRP and .TTYMAC that also expect to be matched by TERMINs).
(DEFINEs, TERMINs, etc. within .QUOTEs are ignored in the matching-up.)
Remember that when the outer macro is called and the inner one is
defined, its TERMIN will gobble up one character.
For that reason, when a macro definition ends with an inner
macro definition or an IRP, "TERMIN TERMIN" should be used,
rather than "TERMIN!TERMIN". If the latter is used, the main
macro will end with "TERMIN", so when it is called the inner
"TERMIN" will gobble an extra character after the call.
Using the space gives the inner TERMIN a character to gobble,
thus protecting all the other characters from being gobbled.
e. .STOP
The .STOP pseudo is used to exit from a macro.
At macro expansion time, when .STOP is executed, the
rest of the macro body will be ignored that time through.
execution will continue with the first character after the macro
call. For example,
DEFINE FOO A,B
1
IFE A,.STOP
2
TERMIN
FOO 1
; 1
; 2 ;note the .STOP isn't exectuted.
FOO 0
; 1 ;and that's all, because of the .STOP.
Beware of putting a .STOP inside brackets ("[" and "]").
Because brackets are used for both literals and conditionals,
MIDAS must keep a stack with an entry for each unmatched "["
saying what is was for. If a .STOP, which is like a jump to
the end of the macro body, is within brackets inside the macro,
then although those bracket pairs will be exited, the bracket
stack will not be updated because the closebrackets will
not be encountered. When a "]" is next seen, MIDAS may
treat it the wrong way (eg, may think it closes a literal
when it was supposed to close a conditional).
The way to win is to use braces
("{" and "}") instead of brackets when conditionalizing
a .STOP. Since braces are not used for anything but
conditionals, there is no need for MIDAS to maintain such
a stack, and thus no data base is invalidated if
the brace depth is changed.
Thus, this macro may cause problems:
DEFINE FOO A,B
1
IFE A,[.STOP ]
2
TERMIN
and this one should be preferred:
DEFINE FOO A,B
1
IFE A,{.STOP }
2
TERMIN
f. .TAG and .GO
These two pseudos provide arbitrary trasfers at macro
expansion time. That is, they do not assemble into jump
instructions; rather, they tell MIDAS to jump around
while expanding the macro. That can cause parts of the
macro to be expanded more than once or not at all.
The way to use them is to put .GO <tag> at the place
MIDAS should transfer from, and .TAG <tag> at the
place it should transfer to. Thus, .GO FOO will
transfer to a .TAG FOO . Nonlocal transfers are
allowed; if a .GO does not find a matching TAG in the
macro it is in, it will exit that macro and search the macro
which called the macro containing the .GO. Any number
of levels may be popped up this way but popping into a
file will cause lossage. There should be a space after the
<tag> in both the .GO and the .TAG to prevent lossage.
Note that .GO and .TAG may be used in REPEAT's and
IRP's just as in macros, and nonlocal .GO's in macros
are allowed to find .TAG's in IRP's and REPEST's, etc.
Transfering from one level in
brackets to another has dangers associated with it, just as
with .STOP - see the detailed explanation under e.
For example,
DEFINE FOO
BAR==5
.TAG BARF
BAR==BAR-1
BLETCH
IFN BAR,.GO BARF
TERMIN
when called, will do BLETCH 5 times
(of course, a REPEAT could have been used in this
case, and would have meant a simpler macro).
B. MACRO CALLS.
0. GENERALITIES.
A macro call is a request for the body of a macro to be substituted
into the text to be assembled. The call must specify the name of the
macro and the values to be given to the macro's dummy arguments
(if it has any)
1. MACRO CALL SYNTAX.
Every macro call begins with name of the macro. In order for
the macro name to be recognized as such, it must be evaluated.
Therefore, macro calls are possible only in those places where
a symbol will be evaluated. (For example, putting the macro's name
in the middle of an ASCII will not cause the macro to be called).
a. macros with no dummies.
A call to a macro without dummies consists of just the macro name.
The character that terminates the name is left to be reprocessed after
the text of the macro is processed.
b. degenerate calls.
A degenerate call to a macro with dummies consists of the macro
name followed by an EOL. The EOL or CRLF will be thrown away.
All the dummies of the macro will be unspecified (see B.7).
c. normal calls.
A normal call to a macro with dummies follows the macro name with
anything but an EOL or OPEN. The character immediately after the macro
name will be ignored. After it, the scanning for the values of the dummies
will commence.
MIDAS considers "by order" dummies one at a time, in the order
they appeared in the macro definition. Each dummy is given a value
obtained by scanning the text of the macro call according to the
argument syntax determined by the dummy's bindclass (which is
one of normal, balanced and wholeline).
(See B.2 through B.7 for descriptions of the argument syntaxes).
When a run of "by keyword" dummies is reached, MIDAS expects to
see in the macro call expressions of the form <dummyname>=<value>.
MIDAS reads the dummy name and checks that the "=" is there; it then
finds the dummy with that name and reads in the <value> using that
dummy's bindclass. When, instead of a dummy name, a terminator
(comma, EOL, semicolon, CLOSE, etc) is seen, all the "by keyword"
dummies in that particular run of them which have not been specified
in the macro call are considered to be unspecified. If there are
by order dummies after the run of by keyword ones, MIDAS then proceeds
to read in their values.
If the scan for one dummy detects the "end of the call" (see B.2.d)
then the scan for all following dummies becomes trivial: they are
all unspecified, regardless of their designated argument syntaxes,
and no more characters will be read from the input stream for any of them.
If a normal call is ended in this fashion by an EOL, the EOL or CRLF
is thrown away.
d. parenthesized calls.
In these calls, the macro name is followed by an OPEN. The assignment
of values to dummies procedes as in a normal call. At the end, though,
characters are thrown away until and including a CLOSE that matches the
OPEN. In the matching, parens and brackets within the values of
dummies are not considered. Also, only the number of brackets seen is
remembered - not what kind. There is nothing to stop a "(" from matching
a ">" at this stage. Parenthesized calls are most useful with macros whose
dummies are balanced - then the macro call is guaranteed
to terminate precisely at that closeparen that matches the initial
openparen, regardless of whether enough or too many argumentss
are present in between. Note that where. below, the macro call is said
to be "ended", in a parenthesized call that really means
that scanning for the matching closeparen will begin.
2. THE "NORMAL" ARGUMENT SYNTAX.
MIDAS begins scanning for a normal argument by examining
the first character.
a. first character is "[".
All the text up to the matching "]", not including either of them, is part
of the dummy's value, and the scanning of the next dummy
begins with the character after the "]".
If a normal argument is delimited by squarebrackets,
it is never unspecified or nullspecified, even if the value
between the squarebrackets is the null string.
Thus, when a dummy has a default value, it is possible to specify
the null string explicitly in the call, overriding the default.
b. first character is "\".
A field is read (leading spaces ignored)
and the field's value, converted to a string using MIDAS's current
radix, is used as the value of the dummy. In this case the scan
of the next argument begins with the character after the field terminator.
Arguments specified in this way are never unspecified or nullspecified.
c. first char is ",".
In this case, the dummy is nullspecified (see B.7).
The scan for the next dummy, or the text to be handled after
the macro call if there are no more dummies, starts after the comma.
d. first character is an EOL or semicolon.
This dummy is unspecified (see B.7), and so are all remaining dummies.
The scan for the remaining dummies will not attempt to read any
characters. If the terminator was an EOL, and the call is a normal one,
the EOL or CRLF will be thrown away at the end of macro call processing
(see B.1.c).
e. otherwise.
All text, including the first character,
up to the first comma, semicolon or EOL goes into the argument,
except that tabs and spaces before a semicolon are ignored.
Then, depending on the terminating character, action is taken as
in B.2.c or B.2.d, except that in neither case is the
present dummy nullspecified or unspecified, since a nonnull value
has already been specified for it.
3. THE "BALANCED" ARGUMENT SYNTAX.
When a dummy uses the balanced syntax, MIDAS insists that its
value contain no unbalanced brackets.
That is, while scanning for the end of the argument, MIDAS counts
opens and closes, and will not let anything terminate the argument
when the count is nonzero. Also, unmatched closes will not be allowed
into the argument - they will terminate the argument and the macro
call. When the bracket count is zero - that is, when it is permissible
for the argument to terminate - a comma, semicolon, or EOL will
do it, just as if the dummy used the normal syntax.
That is, unspecification or nullspecification of the present dummy
or the remaining dummies may happen, as described in B.2.c-B.2.e.
Termination by an unmatched close is like termination by an EOL (B.2.d).
For balanced dummies, "[" or "\" as the first character have
no special significance.
4. THE "WHOLELINE" ARGUMENT SYNTAX.
The scan for a wholeline argument stops only when an EOL is
encountered. Thus, the value of a wholeline argument is everything
up to but not including the first eol. While an EOL that
terminates a normal or balanced argument ends the macro call, thus
making all remaining dummies unspecified, the EOL that ends a
wholeline argument does not do so. The EOL or CRLF is thrown away,
and scanning for the next dummy starts on the next line.
A wholeline dummy is never unspecified or nullspecified (except
that it will of course be unspecified if the macro call is terminated
by an EOL ending some previous non-wholeline dummy).
5. THE "STRUNG" ARGUMENT SYNTAX.
A strung argument looks just like the argument to ASCII or SIXBIT:
it is bounded on both sides by an arbitrarily chosen delimiter.
When the argument is scanned, first non-space character is taken
to be the delimiter, and everything up to the second occurrence
of that character is part of the argument's value. Semicolon,
EOL, comma, and closebrackets of various kinds, cannot be used as
the delimiter, because if they are seen when the opening delimiter
is expected they will nullspecify the argument and (except for comma)
end the macro call, as they would in the balanced syntax. The
closing delimiter should be followed by an appropriate argument
terminator such as comma, semicolon, EOL, or a closebracket (but
spaces and tabs may intervene); all except comma end the macro
call, while comma allows more arguments to follow.
6. THE "KEEPSTRUNG" ARGUMENT SYNTAX.
A keepstrung argument is exactly like a strung argument, except that the
delimiters are retained. This is useful for passing on such arguments
to other macros or pseudos, without having to worry about
what delimiters to use, since appropriate ones will be included with
the argument itself.
7. THE "EVALUATED" ARGUMENT SYNTAX.
An evaluated argument is passed by value instead of by name.
When it is time to scan for such an argument, a field is read
in and evaluated, and the value is converted to a string by
expressing it as a number in the current radix. This is exactly
like the treatment of a normal argument whose value starts with
"\", except that the "\" is not present itself in the call (see
B.2.b).
8. WHAT HAPPENS TO DUMMIES THAT ARE UNSPECIFIED.
Whether a dummy is unspecified in a particular
call depends on what came in the call before the place
where the dummy's value should have been, and on the variety of
argument syntax used by the dummy. Similarly, the criterion for
being nullspecified depends on the argument syntax used. However,
the consequences of being unspecified or nullspecified are independent of
the argument syntax used. Instead, they depend on the other part
of the bindclass of the dummy: whether it is to be nullified,
gensymmed, or defaulted to a pre-specified value.
a. nullified dummies.
When a dummy defined as nullified is unspecified or nullspecified,
it is given the null string as its value. Most dummies of most macros
used are of bindclass nullified.
b. gensymmed dummies.
If a gensymmed dummy is unspecified,
its value will be a GENERATED SYMBOL
6 characters long - the first character "G"; the rest,
a number in the current radix whose value equals that of .GSCNT,
which starts out as 0 and is incremented before each gensymming.
Thus, in each call, an unspecified gensymmed dummy
will provide a unique label.
If nullspecified, a gensymmed dummy has the null string as a value.
Gensymmed dummies are treated differently when nullspecified
or unspecified for compatability with old versions of MIDAS.
In fact, the distinction between unspecification and nullspecification
is made only to handle this case.
c. defaulted dummies.
If a defaulted dummy is nullspecified or unspecified,
its default value will be used as its value in that call.
C. EXAMPLES OF MACRO DEFINITIONS AND CALLS.
1. NORMAL (NULLIFIED AND DEFAULTED) ARGUMENTS.
DEFINE MACRO1 A,B=FOO,C ;3 DUMMIES; SECOND DEFAULTED.
A ? B ? C TERMIN
MACRO1 1,2,3
; 1 ? 2 ? 3
MACRO1 [FOO,,BAR]2,3
; FOO,,BAR ? 2 ? 3
;Showing how to put commas, etc. in args.
MACRO1 1,,3 ;2nd arg nullspecified and defaulted.
; 1 ? FOO ? 3
MACRO1 1,[]3 ;2nd specified as null, not nullspecified.
; 1 ? ? 3
MACRO1 1,2 ;3rd arg unspecified.
; 1 ? 2 ?
MACRO1 1 ;2nd and 3rd args unspecified; 2nd defaulted.
; 1 ? FOO ?
2. BALANCED ARGUMENTS.
DEFINE MAC2 ?A,B ;2 BALANCED DUMMIES.
A ? B ? BLETCH
TERMIN
DEFINE MAC2(A,,B,,),, ;another way to define the same macro.
A ? B ? BLETCH
TERMIN
DEFINE MAC2A A,B ;SIMILAR BUT DUMMIES NOT BALANCED.
A ? B ? BLETCH
TERMIN
MAC2 <MOVE 1,2>,<MOVE 3,4>
; <MOVE 1,2> ? <MOVE 3,4> ? BLETCH
MAC2A <MOVE 1,2>,<MOVE 3,4>
; <MOVE 1 ? 2> ? BLETCH
;<MOVE 3,4>
;Note that the first dummy was bound to "<MOVE 1"
;and the second was bound to "2>".
;The "<MOVE 3,4>" wasn't even part of the call.
MAC2(FOO,-1(P))+1
; FOO ? -1(P) ? BLETCH+1
;A parenthesized call. Note that the closeparen
;ends the second arg and the macro call, and is thrown away.
MAC2 FOO,-1(P)+1
; FOO ? -1(P)+1 ? BLETCH
DEFINE SQR (X)
<<X>*<X>>TERMIN ;this macro squares its arg.
1+SQR(2)*3 ;a parenthesized call.
;1+<<2>*<2>>*3
;note that 1+SQR(2>*3 would have the same effect.
3*<1+SQR 2>-4
;3*<1+<<2>*<2>>>-4
;Here we have, not a parenthesized call,
;but an ordinary call within parens.
;The ">" ends the argument to SQR and is not thrown away.
3. GENSYMMING.
DEFINE GENMAC \FOO,BAR
FOO,,BAR
TERMIN
GENMAC X+1,Y ;Both args specified nonnull.
; X+1,,Y
GENMAC X+1,, ;Second arg nullspecified.
; X+1,, ;Nullspecified args are not gensymmed.
GENMAC X+1 ;Second arg unspecified.
; X+1,,G00001
GENMAC X+1 ;Note uniqueness of gensyms.
; X+1,,G00002
GENMAC() ;Both args unspecified
; G00003,,G00004
GENMAC(,) ;First arg nullspecified; second, unspecified.
; ,,G00005
4. WHOLELINE ARGUMENTS.
DEFINE FOOWH A-B ;first arg normal; second, wholeline.
A
B
TERMIN
FOOWH 1,2,3,4
; 1
; 2,3,4
FOOWH 1
; 1
;
DEFINE BARWH -A B ;both args wholeline.
A
B
TERMIN
BARWH 1,2,3 ;note that each arg requires a line.
4,5,6 ;a comment on the line is part of the arg.
;that is, semicolon isn't special.
; 1,2,3 ;note that each arg requires a line.
; 4,5,6 ;a comment on the line is part of the arg.
5. "BY KEYWORD" DUMMIES.
DEFINE KWDM +A,B,C=FOO,+ ;all three arguments by keyword.
A
B
C
TERMIN
KWDM B=1, A =2
; 1
; 2
; FOO
KWDM C=1
;
;
; 1
DEFINE KWD1 A=1,+B=2,C=3,+D=4 ;B and C are by keyword; A and D are by order.
A ? B ? C ? D
TERMIN
KWD1 100,,200 ;both A and D but neither B nor C specified.
; 100 ? 2 ? 3 ? 200
KWD1 10,C=11 ;A and C specified.
; 10 ? 2 ? 11 ? 4
KWD1 ,B=20,C=21,,40 ;B, C and D specified; A was nullspecified.
; 1 ? 20 ? 21 ? 40
6. STRUNG AND KEEPSTRUNG DUMMIES.
DEFINE TYPEZ *STR* ;STR is strung.
OUTUUO [ASCIZ ^@STR
^@]TERMIN ;note ctrl-@ used as delimiter of ASCIZ.
TYPEZ /UNDEFINED SYMBOL/
; OUTUUO [ASCIZ ^@UNDEFINED SYMBOL
;^@]
FOO==<TYPEZ /WHY FOO?/> ;Here a closebracket follows the string.
;FOO==< OUTUUO [ASCIZ ^@WHY FOO?
;^@]>
TYPEZ(:MYSTERIOUS ERROR:) ;A parenthesized call, and a strange delimiter.
; OUTUUO [ASCIZ ^@MYSTERIOUS ERROR
;^@]
DEFINE ASCNT &KSTR& ; KSTR is keepstrung.
.LENGTH KSTR,,[ASCII KSTR]!TERMIN
OUTUUO [ASCNT "String=^@"] ; Note that delimiters are kept with argument.
;OUTUUO [.LENGTH "String=^@",,[ASCII "String=^@"]]
DEFINE 2STRS *A=UGH,B* ;Two strung arguments, one defaulted.
ASCIZ /A,B/
TERMIN
2STRS =FOO= ;Call ended after 1st string
; ASCIZ /FOO,/
2STRS .FOO.,-BAR-
; ASCIZ /FOO,BAR/
2STRS ,/BAR/ ;Here no 1st argument is given
; ASCIZ /UGH,BAR/
2STRS //,/BAR/ ;Here an explicit null string is given.
; ASCIZ /,BAR/
7. EVALUATED DUMMIES.
DEFINE TYPECH #CHAR ;CHAR is evaluated.
MOVEI A,CHAR
PUSHJ P,TYO
TERMIN
TYPECH 40 ;Print a space.
; MOVEI A,40
; PUSHJ P,TYO
TYPECH "; ;Print a semicolon
; MOVEI A,73 ;73 is the value of ";.
; PUSHJ P,TYO ;This would not be possible with normal
;or balanced syntax, since the ";" would
;be an argument terminator.
DEFINE TYPEI (CHAR) ;CHAR balanced instead of evaluated
MOVEI A,CHAR
PUSHJ P,TYO
TERMIN
TYPECH "0(B) ;Print C(B) as a digit.
; MOVEI A,"0(B) ;This would not work with TYPECH,
; PUSHJ P,TYO ;Since CHAR would evaluate to B,,"0
;giving MOVEI A,<B,,"0> = MOVEI A,"0.
D. THE "REMOTE MACRO" CONSTRUCTION.
It is often desirable to use a macro as if it were a string variable,
appending text to it little by little and then accessing the whole
text as accumulated at some time. A way to do this is as follows:
;initialization:
IF1 [DEFINE BNKBLK OP
OP
TERMIN ] ;BNKBLK accumulates text.
DEFINE BLCODE NEWCFT
BNKBLK [DEFINE BNKBLK OP
OP]NEWCFT
TERMIN
TERMIN ;BLCODE adds its arg to the end of BNKBLK.
;add some text:
BLCODE [FOO] ;add FOO.
BLCODE [BAR] ;add BAR (note BLCODE inserts CRLF's, too).
;assemble what has been accumulated:
BNKBLK ;which expands into ...
; FOO
; BAR
In understanding this example, it is necessary to realize that MIDAS
is a string-processing language that just happens to produce binary
output as a side effect. It does not matter whether an expression
appears to be properly nested with the various sorts of syntactic
bracket (such as [-] and DEFINE-TERMIN) because the order the brackets
are processed in may not be the order they appear in - especially
since not all phases of processing look for all types of bracket.
For example, when BLCODE is called, it calls BNKBLK with argument
"DEFINE BNKBLK OP<crlf>OP". That there is an unmatched DEFINE in that
argument does not matter because DEFINE is not special at macro
call time or macro expansion time. Since BNKBLK substitutes its arg
in, there will be an unmatched DEFINE in the expansion of BNKBLK.
So, when MIDAS expands BNKBLK, it will begin processing the DEFINE,
and it will keep on reading for the new macro definition past the
end of BNKBLK. That is not a problem, because AFTER the call to
BNKBLK, within BLCODE, there is a TERMIN that will make MIDAS
end the new macro definition. The inner DEFINE and TERMIN appear
to match in a simple minded way, which is necessary since otherwise
it would be difficult to define BLCODE containing them, but when
BLCODE is called they actually match up in a much more complicated way.
To get a full understanding of exactly what expands into what,
assemble such a segment of program with the (L) switch. The listing
will show not only macro calls but what they expand into.
Two other examples using this construct follow.
They use a modification of the remote-macro hack
to accumulate things in the reverse
of the order they are put in.
;Backwards IRP: to get the backwards version of
;IRP FO,BA,[mumble]
; body!TERMIN
;use
;IRPB FO,BA,[mumble][body]
DEFINE IRPB X,Y,Z,BODY ;IRP BACKWARDS
DEFINE 1IRPB1 FOO
FOO!TERMIN
DEFINE 2IRPB2 BAR
1IRPB1 [DEFINE 1IRPB1 FOO
FOO
BAR]TERMIN
TERMIN
IRP X,Y,[Z]
2IRPB2 [BODY]
TERMIN
1IRPB1
TERMIN
;LISP-style PROGs in MIDAS - use these macros as follows:
; PROG [X,Y,Z] ;X, Y, and Z are the locals.
; body of subroutine
; ENDPROG
DEFINE PROG VARS
IRP X,,[VARS]
PUSH P,X
TERMIN
DEFINE ENDPROG FOO
FOO
TERMIN
DEFINE 2PROG2 BAR
ENDPROG [DEFINE ENDPROG FOO
FOO
BAR]TERMIN
TERMIN
IRP X,,[VARS]
2PROG2 [POP P,X]
TERMIN
TERMIN
MIDAS Node: Loops, Up: Top, Previous: Macros, Next: Cond
MIDAS Assembly-time Loops
Maybe someday this node will be INFO-ized.
@. INTRODUCTION.
Loops are useful for many reasons. One might merely wish to
initialize the contents of a table in a simple pattern.
One might also use a loop in conjuntion with macros, in
all the ways loops are used in other programming languages.
The loops that exist in MIDAS are REPEAT and
the various kinds of IRP. Because MIDAS canned loops
all begin with pseudoops, they will only be recognized and
expanded where a symbol would be evaluated
(not within comments, failing conditionals, macro definitions,
text strings, etc.).
A. REPEAT.
0. REPEAT is used to assemble a single string (called
the REPEAT string) several times - like a DO in PL-1
or MACLISP. The REPEAT string need not assemble
into the same thing each time, however,
because each assembly of the string may have side effects
that alter the action of the next.
1. SYNTAX.
The REPEAT pseudoop should be followed by a field whose
value is the number of times the REPEAT string is to
be repeated. That string follows immediately after the field.
The syntax of the REPEAT string is the same as that
of the string in a conditional. If the first character after
the space or comma that ends the field is a "[", then all
characters up to the matching "]", not including either of the
brackets, make up the string to be repeated. Otherwise, that
first character itself and all characters up to the next EOL
make up the REPEAT string. The EOL or CRLF is
thrown away, and a CRLF is added to the end of the REPEAT
string, in the case that square brackets are not used.
2. EXPANSION.
A REPEAT expands by substituting the REPEAT string
into the assembler input stream appropriately many times. For example,
REPEAT 5, SETZ ? 500<CRLF>
is equivalent to
SETZ ? 500
SETZ ? 500
SETZ ? 500
SETZ ? 500
SETZ ? 500
where the REPEAT string is "SETZ ? 500 <CRLF>".
Each time through except the last, the last character
of the string will be followed immediately by the first character
of the string. To illustrate,
0,,REPEAT 3,[.+1
10,,]0
expands into
0,,.+1
10,,.+1
10,,.+1
10,,0
This example also shows that there is no need for the
boundary between one pass through the string and the
next to coincide with any logical
division in the resulting concatenated text (such as a word
or field boundary); the several passes through the string may
all be part of one syllable if they do not contain any syllable
separators! (for example, REPEAT 3,['A] = 'A'A'A = (SIXBIT/AAA/)).
3. .RPCNT.
During the expansion of a REPEAT, the symbol .RPCNT
has the value of the number of completed passes through the REPEAT;
that is, it is 0 the first time through, 1 the second, etc.
When nested REPEATs are used, .RPCNT always refers to the innermost REPEAT.
For example,
REPEAT X,[.RPCNT+]0 is X*<X-1>/2 for X >= 0.
REPEAT X,[<.RPCNT+1>*]1 is FACTORIAL(X).
4. .STOP.
When the pseudoop .STOP is executed in a REPEAT, the
pass through the REPEAT then in progress is ended. .STOP
is like a jump to the label on the END of a DO in PL-1.
The character after the "P" of .STOP is ignored; the next
character asembled will be the first character of the REPEAT
string (if there are more repetitions to go) or the one after
the "]", EOL or CRLF that ended the REPEAT string.
Thus,
REPEAT 5,[
FOO
IFE BAR,.STOP
<lots of garbage>
]
is equivalent to (but assembles faster if BAR is 0 than)
REPEAT 5,[
FOO
IFN BAR,[
<lots of garbage>
]]
Note that it currently loses to put .STOP (or .ISTOP) inside
a bracketed conditional within the REPEAT. For details and
reasons, see the documentation file on macros. The way to
avoid this problem is to use braces instead of brackets in
conditionals that surround .STOP's. The same applies to
.ISTOP, .GO and .TAG.
5. .ISTOP.
.ISTOP is like .STOP, but also inhibits all further
repetitions of the REPEAT string. It is like a jump to a
label after the END of a DO in PL-1. The character after
the "P" is ignored; the next character assembled is the one after
the "]", EOL or CRLF that ended the REPEAT string.
For example,
REPEAT 36.,[
FOO==.RPCNT
IFN BAR&<1_.RPCNT>,.ISTOP
]
sets FOO to the number of trailing zeros in BAR.
6. .GO AND .TAG
The pseudos .GO and .TAG may be used inside REPEAT's
just as in macros. See the documentation file for macros.
B. IRP'S IN GENERAL.
0. IRP's in MIDAS are somewhat analogous to MAPCAR
in LISP. In an IRP, a dummy name is supplied and also
a string to repeat (called the IRP BODY) and a string
(called the IRP STRING) to
take substrings from according to a prespecified syntactical
rule which depends on which IRP pseudoop is used. The IRP
body is assembled several times, with the dummy name bound to
successive substrings of the string being IRP'ed over.
For example,
IRP X,,[1,4,5]
FOO+X ? TERMIN
expands into
FOO+1 ? FOO+4 ? FOO+5
X is the dummy, "1,4,5" is the IRP string,
"1", "4", and "5" are the substrings,
and "FOO+X ? " is the IRP body.
1. SYNTAX OF IRP'S.
All kinds of IRP have the same syntax (except that IRPNC
is slightly different). After the pseudoop itself come any
number of IRP GROUPS, which make up the IRP HEADER
(most IRP's have only one group).
then comes the IRP body, which is just like a macro body
(see MACROS A.3).
2. IRP GROUPS.
Each IRP group specifies one repetition to be done.
It contains one string to take substrings from, and two
dummies to put them in. The dummy names come first, and
should be terminated by commas. If one of the dummies
would not be referred to in the IRP body, it may be omitted
from the IRP group, but the comma in its position may not
be omitted (if a comma is missing, MIDAS will sometimes be
able to detect that fact, in which case MIDAS will print an
error message and assume there is no second dummy. However,
a missing comma is not always detectable). The string to take
substrings from comes after the second comma,
and its syntax is that of a normal macro argument,
except that "\" is not special as the first character.
f the first character encountered after the string is read is
a squoze character or a comma, MIDAS assumes that it begins
another group. Otherwise, the group just ended is the last one.
This means that if the string is terminated by a "]" or comma,
the character following determines whether another group follows,
while if the string is ended by an EOL or ";", there are
automatically no more groups.
The EOL or ";", or whatever character follows the comma or "]",
is thrown away. If the next character is a LF, it too is discarded.
A comment following the IRP header will lose. The best possibility is
that it will merely go in the IRP body. The worst is that the ";"
will be one of the characters thrown away and, causing total lossage.
An example of an IRP with several groups is
IRP X,,[1,2,3]Y,,[4,5]
ASCIZ \X+Y\
TERMIN
which expands into
ASCIZ \1+4\
ASCIZ \2+5\
ASCIZ \3+\
3. THE IRP BODY.
The body of an IRP is just like the body of a macro definition.
The dummy arguments mentioned in the IRP header may be used
in the IRP body, representing requests for the corresponding
substrings of the IRP strings to be substituted in.
Concatenation with "!", and .QUOTE, may be used as in
macro definitions. The IRP body is ended by a TERMIN,
just like a macro body. Because of this, the character after
the "N" of the TERMIN wil be discarded. Also for that reason,
IRP's are expected to be matched by TERMIN's within macro
bodies and IRP bodies. For example, in
DEFINE INSIRP INSN,ADDR
IRP X,,[ADDR]
INSN,ADDR
TERMIN
TERMIN
the first TERMIN matches the IRP; the second, the DEFINE.
When the DEFINE executes, it increments its counter on seeing
the IRP, and decrements it on seeing the first TERMIN, so the
second TERMIN ends the macro body. A call to INSIRP
expands into a string containing the IRP and the first TERMIN,
which closes the IRP body when the IRP executes. The result is that in
INSIRP PUSH P,[A,B,C]
NSN's value is "PUSH P" and ADDR's is "A,B,C".
The macro call expands into
IRP X,,[A,B,C]
PUSH P,X
TERMIN
which expands into
PUSH P,A
PUSH P,B
PUSH P,C
4. IRP EXPANSION
An IRP should be thought of as first defining an internal
macro whose body is the IRP body, and whose dummy args are
the dummies mentioned in the IRP header, and then calling that
macro repeatedly with arguments which are substrings
of the IRP strings, chosen according to the rule for
the particular IRP pseudoop that was used.
Each call made to the internal macro constitutes one pass
through the IRP.
When an IRP has several groups, on every pass through the IRP
each group is stepped to its next substring, independently of
the other groups. The expansion of the IRP stops (that is, no
more passes through it are made) only when ALL of the groups
are exhausted (the ends of all the IRP strings have been
reached).
5. .STOP, .ISTOP, .GO AND .TAG.
These pseudoops, when executed in an IRP expansion,
act the way they do in REPEAT's. That is, .STOP ends only
the current pass of the IRP, while .ISTOP also
prevents any future passes from starting. See A.4 and A.5.
6. .IRPCNT.
While an IRP is being expanded, .IRPCNT has the value
of the number of completed passes through the IRP
(0 the first pass, 1 on the next, etc.). When nested
IRP's are used, .IRPCNT refers to the innermost one. Thus,
IRP AC,,[NIL,A,B,C,D,E,F,G,H,I,J,K,L,M,N,P]
AC==.IRPCNT
TERMIN
defines all the ACs with the specified names.
C. PARTICULAR TYPES OF IRP.
0. The previous section described what all the IRP pseudoops
have in common. This section describes the peculiar details
of each IRP pseudoop. The IRP pseudoops differ in how
they divide the IRP strings into substrings, and in how they
choose the values to be given to the two dummies of each group.
1. IRPC (INDEFINITE REPEAT ON CHARACTERS).
IRPC scans the IRP strings a character at a time.
Each time through the IRP body, the first dummy of each group
is set to the next successive character of the group's IRP string,
(or to the null string if the IRP string is exhausted), and
the second dummy is set to the remainder of the IRP string -
the part that follows the character that the first dummy is set to.
For example, in
IRPC X,Y,[ABCDE]A,B,1234,U,,[+-+-]
ASCIZ /X,Y,A!U!B/
TERMIN
the dummies are X and Y in the first group, A and B in
the second, and U in the third (which has no second dummy).
The IRP strings are "ABCDE", "1234", and "+-+-".
The expansion is
ASCIZ /A,BCDE,1+234/
ASCIZ /B,CDE,2-34/
ASCIZ /C,DE,3+4/
ASCIZ /D,E,4-/
ASCIZ /E,,/
2. IRPW (INDEFINITE REPEAT ON WORDS).
IRPW takes the IRP strings a line at a time (ignoring null lines).
On each pass, the next nonnull line of every IRP string is used up.
The first dummy of each group is set to the part of the line
up to the first semicolon, and the second dummy is set
to the part that follows the semicolon (or to the
null string if there is no semicolon on the line).
The semicolon, if any, does not become part of either dummy.
Unfortunately there
is no way to tell whether the line contained no semicolon, or there
was only one semicolon and it was the last character - in either case
the second dummy will be null. For example,
IRPW X,Y,[
FOO ;BAR
FOO1 ;MUMBLE
]
[X] ;; Y
TERMIN
expands into
[ FOO ] ;; BAR
[ FOO1 ] ;; MUMBLE
3. IRPS (INDEFINITE REPEAT ON SYLLABLES).
IRPS attempts to scan the IRP strings a syllable at a time.
However, it is not smart enough to duplicate the actions of the
MIDAS syllable reader perfectly. In actuality, it divides the IRP
string into syllables which are strings of consecutive squoze characters separated
by strings of consecutive nonsquoze characters. On each pass,
the first dummy of each group is set to the next string of
consecutive squoze characters found in the IRP string of the group,
or to the null string if the group's IRP string is exhausted,
and the second dummy is set to the non-squoze character that
terminates the run of squoze characters (or to the null string
if there is none, because the first dummy's value reaches to the
end of the IRP string or the IRP string is exhausted). For example,
IRPS X,Y,[A+B+C+ D+,E+ ,, &/!,F-G- H-I+]
X==Y!100
TERMIN
sets A, B, C, D, E, and I to +100 and F, G, and H to -100.
The extra spaces, commas, and "&/!" in the IRP string do not matter, because
only the first non-squoze character after each syllable becomes the
value of the second dummy Y. Extra CRLF's or + or - signs
could also have been inserted at the same places without effect.
4. IRP (INDEFINITE REPEAT (ON ELEMENTS)).
IRP regards the IRP string as a list of elements
separated by commas, and scans the IRP string an element at
a time. An EOL or CRLF will also separate elements but since
null elements are not ignored the CRLF or EOL should be used
instead of a comma, not in addition to one.
Square brackets are also special in the scan of the IRP string.
The actual algorithm used by IRP to find the end of the next
element is that a comma, EOL or CRLF (or the end of the IRP string)
ends the element, and an "["
is flushed but causes commas and EOLs (and therefore CRLFs)
not to be special until the matching "]", which is flushed.
Note that this resembles but is distinctly different from the
normal macro argument syntax rules.
On each pass through the IRP, the first dummy of each group is set
to the next element in the IRP string of that group, and the second
dummy is set to the rest of the IRP string (starting after the
comma, EOL or CRLF that ended the element). Note that the commas, EOLs and
CRLFs that separate the remaining elements will be present in the second
dummy's value, as will square brackets that are doomed to be flushed when
the elements they are part of are reached by the scan.
For example,
IRP X,Y,[123 4,56789
ABCD[EF]GH,[IJ,],K]
ASCIZ /X:Y/
TERMIN
expands into
ASCIZ /123 4:56789
ABCD[EF]GH,[IJ,],K/
ASCIZ /56789:ABCD[EF]GH,[IJ,],K/
ASCIZ /ABCDEFGH:[IJ,],K/
ASCIZ /IJ,:K/
ASCIZ /K/
5. IRPNC (INDEFINITE REPEAT ON <n> CHARACTERS).
IRPNC is similar to IRPC, but more general. It allows the
characters of the IRP strings to be taken not only one at a time,
but any fixed number at a time. Also, it may be told to
ignore any number of characters at the beginning of each of the IRP
strings, or to limit the number of passes to be made to
a specified maximum value. IRPNC may be used to take an arbitrary
substring of a string, or to index into a string.
a. Syntactic Differences between IRPNC and Other IRP's
IRPNC's syntax differs from that described in B.1 in that after
the IRPNC pseudoop itself, before the first IRP group, there come
three numeric arguments, terminated by commas.
They specify the number of characters
to ignore at the beginning of each IRP string (to be referred to as
<m>), the number of characters to take from each IRP string per pass
(to be referred to as <n>), and the maximum number of passes allowed
(to be referred to as <p>). <p> may be -1, meaning that the
number of passes is not numerically limited. If <p> is 0,
the IRPNC is not expanded at all. If <n> is less than 1, it is
defaulted to 1. If <m> is negative, it is defaulted to 0.
b. Expansion.
On each pass, the first dummy of each group is set to the next
<n> characters of the group's IRP string (or the whole remainder
of the IRP string if fewer than <n> characters remain).
On the first pass, instead of using the first <n> characters of
the IRP string, which would be like all the other kinds of IRP,
<m> characters are skipped and the next <n> are used. (If the IRP string
contained fewer than <m> characters to begin with, it is considered to
be exhausted starting from the first pass). The second dummy
is set to the remainder of the IRP string, following the <n> characters
forming the first dummy's value, but containing only those
characters that will eventually get into the first dummy. That is, if
the number of passes has been limited, the characters of the IRP
string that will never appear in the first dummy because it
would take too many passes to get that far, will not appear in the
value of the second dummy on any pass.
c. Applications.
To take an arbitrary substring of a string, limit the IRPNC to
a single pass. To select characters
FOO through FOO+10 of the dummy name BAR,
IRPNC FOO,11,1,X,,[BAR]
;in here, use X to refer to the desired substring
TERMIN
To convert a SIXBIT value to text, index into a string of all
the SIXBIT characters in order. This IRPNC will
type on the terminal the version number of the program (using .FNAM2).
REPEAT 6,[
IFE .FNAM2_<6*.RPCNT>,.ISTOP
;GIVE UP IF ONLY SPACES REMAIN.
TEMP==77&<.FNAM2_<6*<5-.RPCNT>>>
;ISOLATE THE NEXT CHAR TO BE TYPED
IRPNC TEMP,1,1,X,,[ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_]
PRINTX aXa ;NOTE LOWER CASE "a" IS NOT SIXBIT
TERMIN
]
To XOR together all the words of an ASCII string,
use this macro:
DEFINE FOO STRING
<
IRPNC 0,5,-1,BAR,,[STRING]
ASCII /BAR/#TERMIN
>TERMIN
6. A COMPLICATED IRP EXAMPLE
This shows how to assign several symbols default values (that is,
set them only if they haven't already been set). It allows
each symbol to accompany its value, and either = or == may be
used for each symbol independently.
IRPW X,,[
FOO==1 ;mumble
BAR==500 ;bletch
]
IRPS Y,,[X]
IFNDEF Y,X
.ISTOP
TERMIN TERMIN
expands into
IRPS Y,,[ FOO==1 ]
IFNDEF Y, FOO==1
.ISTOP
TERMIN
IRPS Y,,[ BAR==500 ]
IFNDEF Y, BAR==500
.ISTOP
TERMIN
which expands into
IFNDEF FOO, FOO==1
IFNDEF BAR, BAR==500
Note that the expansion is described as a two-step process only
to make it easier to understand the effects of the various IRP's.
In fact, it all goes on at once, coroutine-style. The flow of
control is approximately: read in the IRPW's arguments,
set up the first pass through the IRPW, encounter the IRPS
and read its arguments (which come out of the IRPW's body),
begin the first pass through the IRPS, perform the IFNDEF,
hit the .ISTOP which exits from the IRPS, preventing further
passes through it, try again to read from the IRPW body, reach
the end of it and then set up for the next pass through the IRPW.
MIDAS Node: Cond, Up: Top, Previous: Loops, Next: Arithmetic
Conditional Assembly in MIDAS
Maybe someday this node will be INFO-ized.
@. OVERVIEW.
It is often desirable to decide at assembly time, rather than at
editing time, whether a particular piece of code is to be
assembled. One might wish to assemble different versions of a
program, usually based on the value of some symbol. Also, in
order for arbitrary functions to be expressed by macros,
conditionals are needed. When thinking about MIDAS conditionals,
one should always remember that MIDAS is a string-processing
language which simply happens to output the values of most
expressions evaluated into the binary file.
A. CONDITIONALS IN GENERAL.
1. SYNTAX OF CONDITIONALS.
Every MIDAS conditional construct begins with a pseudo which
introduces the condition. This pseudo may require arguments
which are part of the condition - a given conditionalizing
pseudo always requires a particular number of arguments. After
those arguments, if any, comes the text to be conditionalized,
in a format independent of which condition was tested.
2. WHERE CONDITIONALS MAY BE USED.
Because the constructs begin with pseudos, they are only
recognized as such in places where symbols' values are being
examined. Those are exactly the places in which macro calls will
be recognized. explicitly, something that looks like a
conditional but appears inside a macro definition, a text
string, a comment, a macro call, etc. will not be treated as a
conditional but as part of the macro body, text string, comment,
or macro argument, etc. Of course, if the conditional appears
inside a macro definition, when the macro is called the
conditional will be recognized unless there is something else to
stop it. The point is that in
DEFINE FOO
IFN 0,TERMIN
the TERMIN shown does end the definition of FOO even though it
is inside what appears to be a failing conditional, because that
conditional is not recognized at macro definition time. The
definition of FOO is "IFN 0,". When the macro FOO is called, it
will expand into a conditional, which WILL be recognized that
time. Thus,
FOO BAR
will not assemble the BAR because the BAR will be inside a
failing conditional.
3. SYNTAX OF THE CONDITIONALIZED TEXT.
The conditional pseudo, together with arguments if any, are
usually arranged to end with a field-terminator (SPACE or
COMMA). The first character after that field-terminator is the
beginning of the conditionalized text. The extent of the
conditionalized text is determined in one of three ways, depending
on whether that first character is one of "[" and "{".
a. one line conditionals.
If the first character is not "[" or "{", then the
conditionalized text is the rest of the line containing the
conditional. For example,
IFN X,FOO+BAR
conditionalizes the string "FOO+BAR" on the value of X. It does
not matter how many "["'s or "]"'s there are in the line. There
may be other conditionals in the line, but it isn't wise to have
any multi-line bracketed ones start in the conditionalized line.
b. bracketed conditionals.
If the first character is "[", the conditional is called
"bracketed". In this case, the conditionalized text runs up to
the "]" that matches the "[". The opening "[" and the closing
"]" are ignored except for their function as delimiters of the
conditionalized text. Other "["'s and "]"'s in the
conditionalized text act as normal, as well as being counted to
find the end of the conditional. The conditional may span many
lines, or may be only a part of a line. For example,
IFN X,[BAR-]FOO
assembles either FOO or BAR-FOO depending on the value of X.
However,
IFN X, [BAR-]FOO
conditionalizes the string " [BAR-]FOO" because the first
character of the conditionalized text is not a "[".
Note that though when the conditional fails, the brackets inside
the conditional are just those that are visible, since macros,
etc., are not expanded if the condition fails, if the condition
succeeds the expansion of macros may cause other brackets to
appear inside the conditionalized text. If such brackets are
unbalanced, they may cause the opening "[" of the conditional to
match a bracket other than the intended one, which may in
obscure circumstances ruin the assembly.
If the "]" of a failing bracketed conditional is missing, the
assembly will by a complete loss. In that case, the error
message will say where the beginning of the conditional was
located. If the "]" of a successful bracketed conditional is
missing, no harm results unless a literal was in progress.
However, it means that if in another assembly the same
conditional failed (perhaps due to different parameter
settings), lossage would occur. Therefore, MIDAS prints a
message at the end of the assembly stating that there was
an unterminated successful conditional, and says where the
first one was. Fixing that one and reassembling will cause
MIDAS to find the second one, if there are any more.
The reason that MIDAS must remember unterminated successful
bracketed conditionals is that a "]" in MIDAS may terminate
a literal or a conditional, and MIDAS has to be able to
decide which whenever a "]" is seen. This means that when
a .STOP, .ISTOP, .GO or .TAG is inside "[" - "]" pairs,
lossage can result because brackets are skipped over,
causing the "[" stack to get out of phase with the position
in the macro or loop. For this reason, bracketed conditionals
should never be used to conditionalize those four pseudos.
Braced conditionals should be used instead.
c. braced conditionals.
Braced conditionals look just like bracketed conditionals
except that braces ("{" and "}") are used instead of brackets.
Braced conditionals count ONLY braces when matching up -
brackets have no effect on them.
Semantically, braced
conditionals are almost the same as bracketed conditionals
but the few differences are important in some situations.
The differences come from the fact that MIDAS does not keep
a stack of all unterminated successful braced conditionals,
whereas for bracketed conditionals it does. MIDAS can get away
without such a stack because braced conditionals cannot be
confused with literals, unlike brackets. This has two
consequences: first, MIDAS cannot warn the user about
unintentionally unterminated successful braced conditionals;
second, intentionally unterminated braced conditionals are
possible. If that boggles your mind, note that in
IFE X,{.STOP }
the conditional is unterminated if it is successful.
Also, because brackets do not interfere with braced
conditionals, constructions such as
IFE X,{ASCII/
[/}
are possible. While overall unbalanced brackets are unwise,
it may be reasonable to have such constructions
in parts of a macro which as a whole has balanced brackets.
There are reasonable constructions which can sometimes
result in unmatched braces/brackets, and for which therefore
only braces should be used. An example is the hack for OR'ing
two condions:
IFN X,{IFNDEF A,} <stuff wanted if X=0 or A undefined>
If the IFN succeeds but the IFNDEF fails, the "}" will not
be assembled. If brackets were used, that might cause trouble.
4. .SUCCESS, .ALSO, .ELSE.
After the testing of the condition, when it has been decided
whether the conditional has succeeded or failed, the symbol
.SUCCESS is given a value automatically - 0 if the conditional
failed, and -1 if it suceeded. Also, after the "]" terminating a
bracketed conditional, .SUCCESS is again set to the same value.
The conditionals .ALSO and .ELSE test the symbol .SUCCESS's
value, and may be used to generate IF-THEN-ELSE constructs as
follows:
IFN X,FOO
.ELSE BAR ;assembles either FOO or BAR.
IFN X,[ FOO ]
.ELSE [ BAR ]
;similar, but now it works even if
;"FOO" is a macro containing conditionals
;that clobber .SUCCESS.
IFN X,IFN Y,FOO
.ELSE BAR
;assembles FOO if both X and Y are nonzero.
;assembles BAR otherwise
IFN X,[IFN Y,[FOO]]
.ELSE BAR ;if X=1 and Y=0, assembles nothing.
IFN X,FOO ;assembles either FOO and MUMBLE, or BAR
.ELSE BAR ;because .ELSE sets .SUCCESS.
.ELSE MUMBLE
IFN X,FOO
.ALSO BAR ;assembles FOO and BAR, or nothing.
B. THE CONDITIONS AVAILABLE.
This section describes all the conditional pseudos in MIDAS and
their conditions and arguments.
1. ARITHMETIC CONDITIONALS.
These conditionals all test the sign of an expression. They are:
IFN X,FOO ;assemble FOO if X is nonzero
IFG X,FOO ;assemble FOO if X is positive
IFGE X,FOO ;assemble FOO if X is nonnegative
IFL X,FOO ;assemble FOO if X is negative
IFLE X,FOO ;assemble FOO if X is nonpositive
IFE X,FOO ;assemble FOO if X is zero
2. STRING CONDITIONALS.
These take two string arguments and compare them for equality.
IFSE succeeds if the strings are equal; IFSN, if they are not.
The case of alphabetic characters is ignored, so "a" and "A" match.
The syntax of the string arguments is the same as that of normal
macro arguments except that "\" is not special as the first
character. Examples:
IFSE FOO,BAR,[BLETCH]
;assembles BLETCH if FOO and BAR are equal
;strings (which obviously can't be the case
;unless either FOO or BAR is a macro dummy
;argument which will be replaced by something).
IFSE FOO,[BAR][BLETCH]
;has the same meaning, but now "[]" are used to
;delimit the second string argument. This makes
;it possible to win if BAR contains commas or
;CRLF's. Note that, as with bracketed macro
;arguments, a comma is not needed after [BAR].
;In fact, a comma there would be wrong:
IFSE FOO,[BAR],[BLETCH]
;conditionalizes the string ",[BLETCH]", because
;the first character of the conditionalized text
;is the comma, so the "[" in front of the
;"BLETCH" is not recognized.
IFSE [FOO][BAR][BLETCH]
;this is just like the first example, but now
;FOO and BAR may contain commas or CRLF's.
3. CONDITIONALS ON SYMBOL DEFINITION.
These conditionals test whether a specified symbol is defined.
The symbol should follow the conditional name, ended by a
field-terminator, which is followed by the conditionalized text.
The pseudos are IFDEF, which succeeds if the symbol is defined,
and IFNDEF, which has the opposite sense. For example,
IFNDEF FOO,FOO=1
;defines FOO to be 1 if it isn't defined
;already. it is a good idea to define all the
;assembly parameters of a program in this way so
;that it is not necessary to edit the program to
;assemble it with different settings - instead,
;just assemble the program with the "T" switch
;in the command string and then type the desired
;definitions in from the TTY.
IFNDEF FOO,.M"FOO=1
;if FOO isn't defined, defines it in the main
;block (the previous example would define it in
;the current block.
IFNDEF XX"FOO,XX"FOO=1
;if FOO isn't defined in block XX, defines it to
;be 1.
IFDEF FOOBAR,PUSHJ P,FOOBAR
.ELSE JFCL
;call the FOOBAR routine if the user has one.
;this might appear in a package that is
;.INSRT'ed by several programs.
;The .ELSE is needed because on pass 1 it will
;not in general be known yet whether the user
;has a FOOBAR routine.
4. "BLANKNESS" CONDITIONALS.
These conditionals take a single string argument, using macro
argument syntax (just like IFSE), and test whether it contains
any SQUOZE characters (letters, digits, and ".", "$", "%"). The
string is "blank" if it has no SQUOZE characters. The
conditionals are IFB "If Blank" and IFNB "If Not Blank".
Examples:
IFB X,FOO
;assembles FOO unless X contains a SQUOZE
;character. Since "X" IS a SQUOZE character, the
;conditional will always fail unless X is a
;macro dummy argument.
IFB [X]FOO
;has the same semantics, but X may now contain
;commas, CRLF's, etc.
5. SQUOZENESS CONDITIONALS.
These conditionals are somewhat like the blankness conditionals,
but instead of testing whether any of the characters in the argument
is squoze, they test whether all of the characters are squoze. The
conditionals are IFSQ "If all SQuoze" and IFNSQ "If Not all SQuoze".
Examples:
DEFINE FOO X
1+< IFSQ X,[1] >
TERMIN
FOO BAR ;assembles 2
FOO BAR BLETCH ;assembles 1
6. PASS CONDITIONALS.
These two conditionals allow different things to be assembled on
pass 1 and pass 2. IF1 succeeds only on pass 1, and IF2 only on
pass 2. Note that the conditionalized text starts with the
character after the space or comma that ends the name of the
pseudo. One use is to avoid defining macros on pass 2: if a
macro is never to be redefined, there is no need to define it on
pass 2, because it already has the correct definition, given to
it on pass 1. Thus,
IF1 [ DEFINE FOO
....
TERMIN ]
Another use might be: have a macro to make entries in the FOO
table. On pass 1, simply have the macro count up the number of
times it is called. Then, at the end of the file, allocate
enough words for the table. From then on, the address of the
table is known, so on pass 2 the macro can actually assemble
values into the words of the table.
Caution is needed when using pass conditionals, because, by
assembling more words on one pass than on the other, it is easy
to cause all the labels in the rest of the program to yield
"MDT" errors, because their addresses on pass 2 are not the same
as they were on pass 1. Also, one should avoid using any literals
on pass 2 that were not used on pass 1 (but doing the opposite
can't do worse than make the constants area waste some space).
7. .ELSE AND .ALSO.
.ELSE and .ALSO exist to make a generalized IF-THEN-ELSE
construction possible. They act just like "IFE .SUCCESS" and
"IFN .SUCCESS" respectively. The conditionalized text starts
after the space or comma that terminates the pseudo itself. See
section A.4. for more details.
MIDAS Node: Arithmetic, Up: Top, Previous: Cond, Next: FASL
The arithmetic-statement pseudo-ops: .I and .F
B.K.P. Horn
This is a feature of MIDAS which facilitates the rapid writing
and debugging of programs involving much nmerical calculation.
The statements used are ALGOL-like and easy to interpret.
[ Note: This was originally written as AI Memo #179, Aug 1969, and
has been copied into this file with as much versimilitude as possible]
---------------------------------------------------------------------------
An arithmetic-statement expander:
Since the Incompatible Timesharing System (ITS) does not support an ALGOL
style compiler, it is very tedious to perform even the simplest algorithms
of numerical analysis. To alleviate this problem without an inordinate
amount of effort, two pseudo-ops were added to MIDAS (the macro-assembly
language).
The pseudo-ops are .F and .I. The first of these will have the arithmetic
in the arithmetic statement following it performed in floating point,
the latter in fixed point.
Each statement is treated without reference to any of the others. Spaces
may apear in a statement almost everywhere and are ignored. Exceptions are
in the continue part of a continuation statement and in a subscript. (see later
on)
Arithmetic statements are combinations of variable names, numbers, function
names and operators. Normally each statement specifies the calculation of
one or more values and where they are to be stored.
The operators are:
= ( ) < > ^ / * + - # $ ,
A number is a character-string starting with a numberic character (0, 1 ... 9)
followed by non-operators. This number should make sense to MIDAS. The
operator ^ is permitted to appear in the number, being the separator used
in MIDAS for the exponent of a number.
A variable (or function) name is a character-string starting with a character
which is neither numeric nor a operator and consists of up to six non-
operators.
/ * + - have the usual meaning of divide, multiply, add and subtract. ^ is
used for exponentiation. ( and ) are used to force the precedence as usual,
ie normal evalutaion proceeds from left to right, with exponentiation
being performed first, then multiplications and divisions, and additions and
subtractions last, except that expressions in parentheses are evaluated first.
This is strictly adhered to and thus A^B^C = (A^B)^C unlike the FORTRAN
convention A**B**C = A**(B**C). Nested pairs of parentheses are evaluated from
the inside out.
Intermediate results are kept in a stack which has to be in the accumulators
and is defined by the user. These accumulators are called A0, A1, ... A9.
If fixed point arithmetic is used, Ai must not be = Aj+1 if i<j. Most commonly
A0==1, A1==2 etc. Usually only the first few accumulators in the stack are
used.
< and > surround the arguments of a function. The arguments are separated by
commas. Thus a name as defined above is a function name if it is followed by
a <. For example:
MAX<A-B*CD,23.4> and RANDOM<>
If used not directly following a name, < and > act exactly like ( and ).
Functions return a single value in A0. The assembled code includes a PUSHJ P,
to the function, the user being responsible for providing a subroutine which
accepts the arguments as presented in A0, A1, etc., does not disturb any
accumulators other than those in which the arguments were passed and returns
the result in A0 before executing a POPJ P, .
A variable name followed directly by a ( is considered to be a vector. The
subscript between the ( and the matching ) can be of the following form:
ACxNUM
xNUM+AC where "x" is either "+" or "-".
Where AC is the variable name of an accumulator in which the subscript is
assumed to have been loaded. NUM is a number, acting as a displacement.
= indicates that the value available at this point (as calculated by the
portion of the arithmetic statement to he right) is to be stored as the
value of the variable name to its left. More than one = may thus appear
in one arithmetic statement. Fo example:
.F A=B=ARM-LOSS=FOO*BARF
This invokes the multiplication of FOO by BARF, storage of the result in LOSS.
Next LOSS is subtracted from ARM and the result stored in both A and B. More
complicated constructs are possible by making use of parentheses. Some care
is required in arranging the right sequence of storage operations so as not
to overwrite values needed further on. (perhaps a more intuitive structure
could be given to multiple equals if one did not adopt the FORTRAN like
convention of having the statement follow the equals).
= permits the passing of arguments by name rather than value, ie it performs
a quoting action. This is particularly useful for subroutines operating on
vectors (dotproduct for example), or subroutines executed for their effect
rather than their value. It also permits the passing of a function address
as a argument. This is achieved by surrounding the variable name with [ and ].
$ indicates a continuation and must be directly followed by a carriage-return
and linefeed (usually supplied by TECO anyway) and either .I or .F
(which is ignored), a space or tab and the continuation of the statement.
For example:
.F ZANSWER=273.0/T $
.F -IN*VEST*MENT
Unitary + and * are ignored. Unitary / and - are interpreted as 1.0/ and 0.0-
respectively. = and ^ may not appear unitarily.
Since @ and ' may be part of a variable name, one can make full use of MIDAS's
indirect addressing and automatic variable storage assignment conventions. The
use of @ comes in very handy when working with multi-dimensional arrays
addressed through margin-arrays.
^ normally generates a call to a function called EXPLOG, which gets two
arguments. To facilitate generation of fast inline exponentiation one may
follow the ^ directly by the single digits 1, 2, 3, or 4. For example:
.F R=SQRT<X^2 + Y^2>
MIDAS Node: FASL, Up: Top, Previous: Arithmetic, Next: Blocks
Assembling Files to be Loaded by MacLisp
Midas can now assemble FASL files that can be loaded
by LISP in the same manner as LAP FASL output. This mode is
entered by the .FASL pseudo op, which must appear at the
beginning of the file before any storage words.
After .FASL has been seen, the assembly becomes a
two pass relocatable assembly. However, certain
restrictions and "changes of interpretation" apply.
Global symbols (declared as usual with " or .GLOBAL)
are persmissible. However, since the output is to be loaded
with FASLOAD using DDT's symbol table instead of STINK,
there are quite a few differences in detail.
For symbols defined within the current assembly, the
only effect of being declared GLOBAL is that the GLOBAL
information is passed on to FASL when the symbol table is
written at the end of pass 2. This in combination with the
SYMBOLS switch in FASLOAD determines whether the symbol gets
loaded into DDT's symbol table. If SYMBOLS is NIL, no
symbols will be loaded; if SYMBOLS is EQ to SYMBOLS, only
globals will be loaded; and if SYMBOLS is T, all symbols
(local and global) will be loaded. Once the symbol is
loaded (or not), the information as to its GLOBALness is
lost and, of course, makes no further difference. The
initial state when LISP is loaded is NIL.
GLOBAL symbols not defined in the current assembly
are also legal, but there are additional restrictions as to
where in a storage word they may appear and what masking may
be specified (as compared to a normal relocatable assembly).
Briefly, they may appear as in a storage word as a full
word, a right half, a left half, or an accumulator. They may
be negated, but can not be operated on with any other
operator. Error printouts will be produced if they appear
elsewhere. When the symbol is encountered by FASLOAD, DDT's
symbol table is consulted. If it is defined at that time,
OK, otherwise FASLOAD will generate an error.
Any sort of global parameter assignment or location
assignment is Forbidden. .LOP, .LVAL1, .LVAL2, etc are not
available.
New Pseudo OPs Available only in FASL assemblies.
The following pseudos are available to facilitate
the communication between MIDAS assembled programs and LISP
(particularily with regard to list structure).
.ENTRY function type args
Function is an atom and is taken as the name of
a function beginning at the current location. Type
should be one of SUBR, FSUBR or LSUBR, and has the
obvious interpretation. Args is a numeric-valued field
which is passed thru to FASLOAD and used to construct
the args property of the function. If it is zero, no
args property is created. Otherwise it is considered to
be a halfword divided into two 9 bit bytes, each of
which is converted as follows:
byte result
0 nil
777 777
otherwise n n-1
These two items are then CONSed and from the
args property.
The following pseudos may appear in constants!!
.ATOM atom
followed by a LISP atom in "MIDAS" format (see below).
May only appear in right half (or entire word) of a
storage word. Assembles into a pointer to the atom
header of the specified atom.
.SPECI atom
similar to .ATOM but assembles into a pointer to the
SPECIAL value cell of the specified atom.
.FUNCT atom
similar to .ATOM, but invokes special action by FASLOAD
in case the PURESW is on. Normally used in function
calls. Briefly, if FASLOAD is going to purify the
function it is loading, it must "snap the links" first.
If .FUNCT is used, the location will be examined by
FASLOAD and the link snapped if possible before
purification.
Typical usage:
CALL 2,.FUNCT EQUAL ;calls equal as a function of 2 args
; note: the CALL is not defined
; or treated specially by MIDAS.
.ARRAY atom
similar to .ATOM, but assembles into a pointer to the
Array SAR.
.SX S-expression
similar to .ATOM, but handles a LISP S-expression.
(See below).
.SXEVA S-expression
reads S expression. This S expression is EVALed (for
effect presumably) at FASLOAD time. The resulting
value is thrown away. Does not form part of storage
word.
.SXE S-expression
Similar to .SX but list is EVALed at FASLOAD time. The
resulting value is assembled into storage word.
The MIDAS "LISP READER"
By a conspiracy between MIDAS and FASLOAD, a version
of the LISP reader is available. However, due to historical
reasons (mostly, i.e. the FASLOAD format was originally
intended only to deal with COMPLR type output), there are a
number of "glitches" (see below for list). These will
probably tend to go away in the fullness of time.
a) numeric ATOM
The first character of a LISP atom is examined
specially. If it is a # or &, the atom is declared to be
numeric and either fixed (#) or floating (&). Midas then
proceeds to input a normal numberic field (terminated, note,
by either space or comma). This value is then "stored" in
the appropriate "space" (fixnum space or flonum space).
b) other ATOMs (also known as PNAME atoms or (LISP) SYMBOLS)
If the first character of the atom is not # or &,
the atom is a "PNAME" atom. / becomes a single character
quote character as in LISP. The atom may be indefinitely
long. The atom will be terminated by an unquoted space,
carrige return, tab, (, ), or semicolon. Unquoted linefeeds
are ignored and do not become part of the atom. The
character that terminates the atom is "used up" unless it is
a ( or ). Note that period is a legal constituent of a atom
and does not terminate it or act specially.
c) lists.
Work normally, but note following caution relative
to dot notation: . does not terminate atoms. Thus, to
invoke dot notation, the dot must be left delimited by a
space, tab, parenthesis, or other character that does
terminate atoms.
Glitches:
1) Restriction on pass dependant list
structure -- In any list reading operation, no new
atoms not previously encountered may be
encountered for the first time on pass 2.
However, this restriction does not apply to
atom-only reading operations (.ATOM, .SPECI,
.FUNCT etc).
2) Single quote for quoting does not exist (no
other macro characters exist either.)
3) Numbers must be flagged as above always.
MOVEI A,.ATOM 123 ;LOSES - gives pointer
; to PNAME type atom
; with PNAME 123. it is
; not numeric.
use:
MOVEI A,.ATOM #123 ;WINS
4) No provision exists to reference "GLOBALSYMS"
in FASLOAD. This mostly means only that DDT must
be present to load a MIDAS assembled FASL file.
(some simple COMPLR and LAP FASL files can
successfully be FASLOADed by, for example, a
disowned LISP running without a DDT.
5) LOC is illegal in a FASL assembly. BLOCK of a
non-relocatable quantity is ok.
6) Currently, symbol loading is VERY slow. Thus
use SYMBOLS nil, (the initial state) unless
symbols are necessary.
7) Midas does not know about any LISP symbols or
UUOs specially. Use them as globals until someone
gets around to fixing up a .INSRT file with the
appropriate defs.
8) .ATOM "should" be a special case of .SX .
However, it is handled separately because of the
following "reasons":
a) The previously noted restriction on pass
dependent LISTS.
b) Midas can do constants optimization on
atoms ppearing in constants (on both pass one
and pass two) but not on LISTS. Therefore,
each list is guaranteed to take a separate
word in the constants area even if it is
identical to some other list which also
appears in a constant.
c) Each list takes an additional entry in
FASLOAD's "atom" table. This is a temporary
table that is flushed after the FASLOADing is
complete. Of course, .SX still works for
atoms modulo the above noted restrictions and
inefficencies.
MIDAS Node: Blocks, Up: Top, Previous: FASL, Next: Constructs
Symbol Table Block Structure
The MIDAS symbol table allows blocks of code to be defined within
which local definitions of symbols can be made. Local definitions are
normally visible within the blocks they belong to, including other smaller
blocks included in them, but are normally invisible outside. However, it
is possible to examine or set the local value of any symbol in any block
explicitly, whether inside the block or outside it. The intended
application of block structure is for libraries that are to be assembled
into other programs with .INSRT. Because there is a natural tendency to
use block structure in assemblers for purposes that do not merit it, any
temptation to use block structure for any other reason should be
entertained only with great self-restraint.
A block is entered with a .BEGIN and exited with a .END. Both the
.BEGIN and the .END should be followed by the name of the block. It is
not usually wise for two blocks to have the same name, although it is legal
if the two are contained in different blocks. .BEGINs and .ENDs must
match. .BEGINs and .ENDs serve as a sort of parentheses, and impose a tree
structure on all blocks, so that each block has a direct superior and any
number of direct inferiors. Initially, even if you never use .BEGIN and
.END, there are two blocks in the structure: .INIT, which contains the
predefined MIDAS symbols, and .MAIN, which is the one which your program is
in. .MAIN is a subblock of .INIT, and any other blocks you .BEGIN are
direct or indirect inferiors of .MAIN. .END should not be confused with
END, which ends the whole assembly. Block names do not conflict with any
other kinds of names.
A simple example of block structure will explain much:
A=1
A ;Value of A is now 1.
.BEGIN FOO ;Enter a block named FOO.
A ;Value of A is 1 on pass 1, but 2 on pass 2.
A=2
A ;Value of A is now 2.
.END FOO ;Exit block FOO. Now back in main block.
A ;Value of A is now 1 again.
END
This program will assemble four words: 1, 2, 2 and 1. This is because, on
pass 2, the local definition of A in block FOO becomes visible as soon as
the .BEGIN FOO is passed, and ceases to be visible when the .END FOO is
passed. The definition of A as 1 in the main block is always visible,
except that within FOO it is "shadowed" by a more local one.
Whenever a symbol's value is used, the most local definition which
is visible determines the value. That is, within block FOO, where both the
local definition in FOO and the definition in the main block are visible,
the more local one (in FOO) wins out. When a symbol is defined, however,
it is always defined in the current block unless you specify otherwise.
All operations that affect the symbol's value in any way are counted as
definitions in this regard; this includes such things as .HKILL, .KILL,
EQUALS, DEFINE, :, and =.
When you wish explicit control of shadowing, you can use .BIND.
.BIND is followed by a list of symbols, and forcibly markes them as local
to the current block, if they were not already defined locally therein.
Definitions in outer blocks are hidden by this, so that a symbol defined
outside the current block can become undefined inside it, after a .BIND,
until a definition in that block is seen. .BIND can be used to force
shadowing of some predefined symbols which it is normally an error to
shadow.
One use of .BIND is to force forward references in a one pass
assembly to a symbol defined in an outer block which is going to be defined
locally in the current block later on. One pass assemblies and block
structure are a can of worms anyway. Normally, a symbol defined in an
outer block is NOT visible in lower blocks, unless it is a macro or pseudo,
or you mention it in a .DOWN statement. If it is a macro, and you don't
want to see it in an inner block, you must .BIND it.
The distinctive feature of MIDAS block structure is the ability to
look at or set the definition of any symbol in any block at any time. This
makes it possible for a block to define symbols in its containing block, or
in the main block (a practice whose wisdom is currently a subject of
debate). It also allows an outer block to define and refer to symbols in
an inner block, which is the main way of communicating with .INSRTed
libraries, and is essential.
The way to refer to a symbol in a specific block is to prefix the
symbol's name with the block name and a doublequote. Thus, FOO"BAR means
the value of BAR in block FOO. This construct can be used wherever a
symbol name can appear, in both references and definitions. If there is or
might be more than one block named FOO, it can be qualified by the name of
the containing block, as in UGH"FOO"BAR, which means the value of BAR in
that block named FOO which is directy inside a block named UGH. In
addition, there are a few special "block names": .C signifies the current
block, and .U signifies the direct superior of the current or specified
block. Thus, .U"BAR means the value of BAR in the block containing the
current one. FOO".U"BAR means the value of BAR in the direct superior of
the block FOO. .U"FOO"BAR means the value of BAR in the block FOO which is
a sibling of the current one (that is, it and the current one have the same
direct superior).
MIDAS Node: Constructs, Up: Top, Previous: Blocks, Next: Pseudos
MIDAS constructs, in alphabetical order:
! is the concatenation character inside macro definitions,
IRP bodies, and .TTYMAC's. An ! next to either a dummy name
or the final TERMIN will be thrown away. For example,
inside DEFINE FOO A,B, A!!B will concatenate A's value and B's,
as will just A!B. A!!!B will leave one ! between them (the
middle ! is not adjacent to either A or B). That may be what
you want to do if you are expanding an inner macro definition
and A or B wil expand into one of that definition's dummies.
" has four constructs:
<symbol>"
makes the <symbol> global, as well as referring
to the <symbol>'s value.
<block>"<symbol>
refers to the value of <symbol> in the specified
block. Also, .C refers to the current block,
.M to the main block, and .U to the block containing
the current one. Multiple use of this construct is
allowed: FOO".U"BAR"BLETCH refers to BLETCH
in the block named BAR which is contained in the
block which is the father of the block FOO.
"<text>
assembles right-justified ASCII for <text>.
<text> contains at least one character in any case,
and all following squoze characters.
This construct exists only if .QMTCH is zero
(as it initially is); otherwise, the next construct
exists instead:
"<text>"
assembles right-justified ASCII for <text>.
<text> may contain anything; doublequotes may be
put in using the PL/1 quoting convention
("""" gives the ASCII for a doublequote).
# XOR operator.
<x>#<y> gives the XOR of <x> and <y>.
#<x> gives the complement of <x>.
#<x>#<y> therefore gives "neither <x> nor <y>".
& AND operator.
' has several constructs, syntactically distinguished.
<symbol>'
makes <symbol> a variable; like
< .SCALAR <symbol> ? <symbol> >
This construction is a bad one because it makes
it easy to assign a storage word without commenting
what its contents will mean. It exists for historical
reasons.
<number>'
forces the use of base 8 in the number, regardless
of the current radix.
'<text>
like "<text>, but generates SIXBIT instead of ASCII.
'<text>'
like "<text>", but generates SIXBIT instead of ASCII.
(,) (<expression>) has the value of <expression> with its halves
swapped, if preceded by an arithmetic operator.
Otherwise, it takes that halves-swapped value and adds it into
the current word, and is invisible to its neighbors.
For example, 1(2)+3 == 1+3(2) == 4(2), and all are the same as
4 except that 2 is added to the left half of the current word.
* Multiplication operator.
+ Addition operator.
, Field terminator. It is used for separating the fields of a word,
and also terminates the arguments to many pseudos and macros.
- Subtraction operator, and unary negation operator.
/ Division operator.
: indicates a label.
<symbol>:
defined <symbol> to be equal to the current value of ".",
which is equal to the location being loaded into
plus the offset (normally 0, but see .OFFSET).
There may not be any spaces before the colon.
Once a symbol has been defined as a label, it is an error
to give it a different value in any way.
<symbol>::
is similar but "half-kills" <symbol>, so DDT will not
use it for type-out.
; begins a comment, which is ended by the following carriage return.
That carriage return is not gobbled; it has its normal effect.
";", like all other MIDAS constructs, is not recognized inside
of text strings; also, it is recognized in macro argument scanning
only in certain specific situations (see MACROS >).
<,> in MIDAS are like parentheses in algebra.
They for a "grouping" (other groupings are made by (,) and [,]).
They are generalized to allow more than one expression to be
within them; the last one's value is the value of the grouping.
This makes it possible to do assignments, etc. before computing
the ultimate value.
= is the assignment operator.
<symbol>=<expression>
sets <symbol>'s value to <expression>'s.
If there are undefined symbols in <expression> the
assignment is not performed.
This construct is illegal where a value is needed,
but if it is the last thing in a grouping it does
supply the value of the grouping. Thus,
FOO=<BAR=1> is legal, though FOO=BAR=1 is not.
<symbol>==<expression>
is similar but half-kills <symbol>
<symbol>=:<expression>
is like "=" but makes it an error if <symbol> ever
gets (or previously had) a different value (this is
what labels do; <symbol>=:. is just like <symbol>:).
<symbol>==:<expression>
makes <symbol> half-killed and unredefinable.
? separates words. ? is just as good as a carriage-return
for most purposes, the exceptions being termination of
macro arguments and conditionals (? does not terminate them).
That facilitates constructs like
IFN FOO, MOVE A,B ? JRST BAR
which conditionalizes both instructions.
@ sets the indirect bit of the word it is in.
[,] delimit a literal. Their value is the address of the space
MIDAS allocates to contain whatever is assembled inside them.
There may be any number of lines or words in the literal.
Example: MOVEI A,[.BYTE 7 ? ^M ? ^J]
loads A with the address of a word containing the ASCII
for carriage-return linefeed.
\ Inclusive-or operator.
^ has several constructs:
^<char>
has the value of the control-character associated
with <char>. Thus, ^M is 15 .
<number>^<expt>
multiplies <number> by <number>'s own radix to the
<expt> power. Thus, 1.^6 is 1 million.
777^11 is the op-code field.
This construct works for floating point numbers as well:
1.0^6 = 1000000.0 .
_ left-shift operator.
NOTE: this is an arithmetical, not logical, shift!
MIDAS Node: Pseudos, Up: Top, Previous: Constructs, Next: Changes
Alphabetical list of MIDAS pseudo-ops
$. Location being loaded into. Works only via STINK.
$L. REAL LOCATION (WITHOUT OFFSET) (only in STINK format)
$O. GLOBAL OFFSET (only in STINK format)
$R. The relocation factor (only in STINK format)
. = address of current code word. In a literal, . refers
to the location of the word containing the literal, not the
word in the literal.
The offset is included in the value of ..
.1STWD put before a text-generating pseudo (SIXBIT, ASCII,
ASCIZ, ASCIC, .ASCII) to throw away all but the first
word of text.
.ABSP <arg>,
returns the "absolute part" of <arg>.
.ALSO conditional: "If the previous conditional succeeded".
(See *Note Cond: Cond, for conditional info)
.AOP is like .OP, but returns no value. However, all the
information is still available in .AVAL1 and .AVAL2.
.ARRAY reference a lisp array (.FASL feature)
.ASCII /text/
Like ASCIZ, but when the character "!" is encountered in the
text, the <exp> following it is evaluated and the value
inserted into the assembled string as a sequence of digits in
the current radix, replacing the "!" and the expression.
Terminate <exp> with a space or comma, which unfortunately will become
part of the assembled string.
.ASCVL /<char>
(Yes, only one "/") returns the ascii value of <char>.
.ASKIP -1 if instruction executed by most recent .OP or .AOP
skipped; 0 if it didn't skip.
.ATOM <atomname>
refer to header of named LISP atom (see *Note Fasl: Fasl.)
.AUXIL does nothing - it exists for the sake of the listing program "@".
.AVAL1 What was left in the ac by the instruction executed by
the most recent .OP or .AOP
.AVAL2 What was left in the memory location by the .OP or .AOP.
.BEGIN <blockname>
begin a symbol-scope block. <blockname> defaults to
most recent label.
.BIND <symbol1>,...
Create bindings for these symbols in the current block.
Must be used when it is desired to shadow a built-in
symbol under certain circumstances. Also sometimes needed
in 1-pass assemblies when symbol is defined in higher block
and will be defined later in current block, to force a
forward reference to be made.
.BM <bp>,
returns a mask to the byte pointed to by the specified
byte pointer (address field ignored). If arg's LH is 0,
the arg will be swapped first to get a byte pointer.
The comma is thrown away except for ending the arg.
.BP <mask>,
returns a byte pointer to the byte which the argument is
a mask to. The address field of the value is 0. The comma
which terminates the argument is not flushed, so that
.BP <bit>,<addr> will produce a byte pointer to <bit> in
location <addr>. For those who understand .FORMAT, that
contrives to use the format field-space-field rather than
the problematic field-comma-field format, which many users
like to redefine.
.BYTC The number of bytes assembled since .BYTE mode was entered.
.BYTE N assemble N bit bytes. That is, take the assembled "words"
and pack them into N-bit bytes to get what is really output.
When another byte won't fit in the current word, the word is
output and a new one is started.
.BYTE M,N,O,P,...
assemble an M bit byte, then an N bit one, then an O bit one, etc.,
returning to an M bit one after using the last of the specified sizes.
If a negative "byte size" is specified, it causes a block of zero bits
to be assembled right after the previous byte; the number of zero bits
is the abs. value of the specified number. This can cause an extra
zero word to be assembled if .byte mode is left right away.
In byte mode, "." is a byte pointer which could be ILDB'd to get
the next byte.
.BYTE with no arg leaves byte mode, returning to the initial state.
.C Current block (as in .C"FOO).
.CRFIL
.CRFOFF CREF off
.CRFON CREF on
.CURLN current line number minus 1
.CURPG current page number minus 1
.DECREL selects DEC relocatable output format
.DECSAV selects DEC/TNX SAV output format. Symbols are deposited
where the location counter points when END is reached,
and location 116 (.JBSYM) given a pointer to them.
Location 120 (.JBSA) is given the start address unless
the instruction furnished to the END pseudo has something
in the LH, in which case it is treated as an entry vector pointer
(only meaningful on TNX systems).
.DECTW <hibot>
Two-segment DEC relocatable output format.
Arg is address of bottom of high segment (normally 400000).
.DECTXT /text/
Like .TEXT in MACRO. .DECREL format only, outputs an ASCIZ REL
block consisting of the given text, which LINK interprets as a
command string.
.DOWN <symbol1>,...
Causes the symbols to be visible in subblocks in 1PASS mode.
In 2-pass assemblies, symbols are always visible in subblocks
of the blocks they are defined in. In 1PASS mode, they are
usually not (except for macros), in case the same name is
defined later on in the subblock itself.
.DPB <newbyte>,<bp>,<word>,
deposits <newbyte> into the byte in <word> specified by <bp>,
and returns the result. Thus, .DPB 0,30300,-1, returns -1,,777707
.ELDC End a load-time conditional - STINK format only.
Any storage words appearing between a load-time conditional (e.g.
.LIFS, .LIFE, etc) and its matching .ELDC are passed on to the
loader, which will load them only if the conditional is true at
the time it is encountered during loading. .ELDC should always
be followed by a location assignment (eg LOC pseudo). Sometimes
if there is a series of load-time conditionals with no intervening
non-conditional words the LOC need only be used after the last.
Notice that LOC $." may be used to continue loading storage words
into subsequent locations where the effect of the load-time conditional
is not necessarily known. Load-time conditionals may be nested.
.ELSE conditional: "If the previous conditional failed". (*Note Cond:Cond.)
.END <blockname>
terminate symbol-scope block, if its name is <blockname>. Error
if the current block's name isn't <blockname>.
.ENTRY .FASL feature - declare a LISP entry point (SUBR beginning, etc).
.ERR <line>
Causes an error with error message <line>
.ERRCNT Number of errors seen in entire assembly thus far.
.F floating mode Fortran arithmetic statement.
*Note .F: Arithmetic, for details.
.FASL Selects FASL output format, loadable by MACLISP.
.FATAL <line>
Causes a fatal error with <line> as the error code.
The output buffers are written out and the output files
are closed, though only the error output file is renamed.
.FNAM1 numeric value of sixbit for first file name of main input file.
.FNAM2 numeric value of sixbit for second file name of main input file.
.FORMAT fno,fval Specify interpretation of fields in a word.
See description in *Note Forma: Words.
.FUNCT <function name>
refers to the specified function, in FASL format. *Note Fasl: Fasl
.FVERS version number of main input file.
.GLOBAL <symbol1>,...
Makes the specified symbols global.
.GO <tag>
assembly continues at .TAG tag (within macro body)
Non-local .GO's outward are allowed.
.GSCNT The value of the generated symbol counter - may be read or set.
.GSSET <count>
same as .GSCNT=<count>
.HKALL If nonzero, causes ":" to be treated as "::".
.HKILL <symbol1>,...
Half-kills the specified symbols. Does not define them.
Acts only on the last pass.
Does shadow definitions in outer blocks, like .BIND.
.I integer mode Fortran arithmetic statement
*Note .I: Arithmetic, for details.
.IBP <bp>
returns <bp> incremented a la IBP instruction.
.IFNM1 numeric value of sixbit for first file name of insert file
(the file most recently .INSRT'ed by the current input file,
or the current file's name if it hasn't .INSRT'ed any others yet).
.IFNM2 numeric value of sixbit for second file name of insert file
.IFVRS version number of insert file.
.INEOF In an .INSRT file, acts just like EOF.
.INIT The outermost block (as in .INIT"MOVE). All the predefined
symbols are defined in this block. Symbol definitions in this
block are not output to the binary file.
.INSRT <filespec>
Pushes the current input file and begins reading from the
specified file. After the end of that file, reading from
the inserting file will resume. If the sname of the file to
be inserted is not specified, that of the file being read
will be used. If the file is not found, the user will be
asked to respecify the names. .INSRT'ing the TTY is a
good way to let the user make arbitrary redefinitions at
some point in the assembly.
.IRPCNT # of completed iterations of innermost indefinite repeat
.ISTOP stop REPEAT or IRP - see *Note loops: Loops
.KILL <symbol1>,...
The specified symbols will not go in the symbol table.
.LDB <bp>,<word>,
returns as a value the contents of the byte in <word>
specified by <bp>. <bp> may be either a byte pointer
or the left half of one, as in .BM.
.LENGTH /text/
returns the number of characters in the specified string.
.LIBRA namelist (linking loader pseudo - STINK format only)
This must occur before any storage words. It tells the loader
that the program to follow is a "library program". The entries
in the namelist, separated by commas, are either (a) names, or
(b) groups of names separated by space, +, and -. An entry is
said to be "satisfied" by a list of symbols if either (a) the entry
is a name and that name appears in the list of symbols; or (b) the
entry is a group of names, ALL of which names preceded by space or
+ are in the lst of symbols, and NONE of which names preceded by
= are. STINK will omit to load a library program unless one or
more entries in its namelist are satisfied by the list of
undefined global symbols used in programs already loaded.
.LIBRQ nam1,nam2,... (linking loader pseudo - STINK format only)
This will output the given names to STINK such that the loader
"sees" them in this program when queried by load-time conditionals.
No definition is given to the loader, and a use of .LIBRQ does not
cause the names to be seen at all by the assembler.
.LIFE word Load if word = 0. (Load-time conditional, see .ELDC)
.LIFG word Load if word > 0. (Load-time conditional, see .ELDC)
.LIFGE word Load if word >= 0. (Load-time conditional, see .ELDC)
.LIFL word Load if word < 0. (Load-time conditional, see .ELDC)
.LIFLE word Load if word =< 0. (Load-time conditional, see .ELDC)
.LIFN word Load if word not 0. (Load-time conditional, see .ELDC)
.LIFS namelist Load If Seen. (Load-time conditional, see .ELDC)
This conditional is true only if one or more entries in the namelist
are satisfied by the list of defined AND undefined global symbols
used in programs already loaded. See .LIBRA for an explanation of
the namelist.
.LITSW if nonzero, using a literal causes an error message.
.LNKOT (linking loader pseudo - STINK format only)
If output is relocatable (STINK) format, causes immediate output
of all accumulated linking pointers.
.LOP <op>,<C(ac)>,<C(mem)>
like .OP, but the instruction is executed in STINK rather
than in MIDAS. Has no value in MIDAS. The loader sets the value
of the global symbols .LVAL1 and .LVAL2 to the resulting contents
of AC and memory respectively. (Initially .LVAL1 and .LVAL2 have
value 0).
.LSTOF listing off
.LSTON listing on
.LVAL1 .LOP value left in accumulator.
.LVAL2 .LOP value in memory location.
.LZ <arg>,
the number of leading zeros in the value of <arg>.
The comma serves only to terminate <arg>.
.M Main block (as in .M"FOO)
.MLLIT set positive to allow multi-line [], (), and <>.
Set negative for the old-fashioned mode where they didn't
need to be terminated. Zero selects "error mode" useful
in converting an old-fashioned program to multi-line mode.
Now initially positive.
.MRUNT MIDAS's runtime so far, in milliseconds.
.NSTGW sets .STGSW to cause error message if any storage words are assembled.
.NTHWD <n>,<text string>
returns the <n>'th word of the text string.
Thus, .NTHWD 1, is equivalent to .1STWD. By <text string>
is meant an invocation of ASCIZ, ASCII, ASCIC, .ASCII or SIXBIT.
.OFNM1 = SIXBIT/<output file's fn1>/
.OFNM2 = SIXBIT/<output file's fn2>/
.OP <insn>,<acdata>,<memdata>
executes <insn> on an AC containing <acdata> and a memory
location containing <memdata>, and returns what this leaves
in the AC. Thus, <.OP SUB,5,2> equals 3. This value is also
made the value of .AVAL1, while .AVAL2 contains what was left
in the memory location: .OP SUBM,5,2 sets .AVAL2 to 3, and
returns 5. .ASKIP will be nonzero if the instruction skipped.
If an instruction is supplied with a nonzero AC field, that
AC field will be used unchanged, and the number of the AC
used for the argument and value will not be substituted.
Similarly, if the address field or index field in <insn> is
nonzero, the address of .AVAL2 will not be substituted.
This is useful for immediate instructions, including such
ITS UUOs as .RDATE which equals .OPER 46: ASDATE=.OP .RDATE
sets ASDATE to today's date in SIXBIT.
Note that <insn> is read in as a field, and thus cannot contain
any spaces or commas (unless they are inside brackets).
.OSMIDAS
is the sixbit name of the operating system MIDAS is running
under. It is SIXBIT of ITS, TENEX, SAIL, CMU or DEC.
Twenex is considered the same as Tenex.
Programs that have versions for more than one operating
system should by default assemble to run on the one
in .OSMIDAS, but they should make it possible to override
that default with the use of the T switch:
IFNDEF RUNOS, RUNOS==.OSMIDAS
DEFINE IFITS
IFE RUNOS-SIXBIT/ITS/TERMIN
.PASS is 1 or 2, depending on which pass MIDAS is in.
.PPASS is 1 in a 1-pass assembly; 2, in a 2-pass assembly.
.QMTCH if set nonzero, causes ' and " text constants to use the newer
fail-style syntax.
.QUOTE /text/ inhibit checking for TERMIN and macro dummies.
.RADIX <rdx>,<value>,
evaluates <value>, using <rdx> as the radix.
.RELP <arg>,
returns <arg>'s relocation. Always 0 in an absolute
assembly.
For any X, X = .ABSP X,+.RL1*.RELP X,
.RL1 in a relocatable assembly, returns a relocatable 0.
In an absolute assembly, returns 0.
.ABSP .RL1 is always 0. .RELP RL1 is 0 iff the assembly is
absolute.
.RPCNT = # iterations of REPEAT completed
.RSQZ <bits>,<symbol>
generates right-justified SQUOZE such as the DEC system likes.
.SBLK Specifies ITS SBLK output format. Same as SBLK but doesn't type
warning message. For details on format, see ITSDOC;BINFMT >
and the "Symbol table format" section of .INFO.;DDTORD >.
.SCALAR <symbol>(<size>),<symbol2>,...
makes the symbols "variables", like the <symbol>' construct,
causing them to have storage words allocated for them later
(at the time of the next VARIAB or END). An optional <size>
may be specified in parentheses after a symbol, reserving that
many words for it rather than the default of just one.
See also .VECTOR, which is identical except for the way sizes default.
.SEE <symbol1>,...
Has no effect except to make cref entries for the specified symbols.
.SITE <n>
returns word <n> (origin 0) of a SIXBIT string that says
the name of the machine MIDAS is running on. If <n> is
out of range 0 is returned. The format of the string is
operating system dependent; on I.T.S. .SITE 0, will return
the standard I.T.S. "machine name" which is SIXBIT of
"AI", "ML", "DM", or "MC".
Programs with different versions for different sites should
by default assemble to run on the one specified by .SITE,
but they should make it possible to override that default
using the T switch:
IFNDEF RUNSITE,RUNSITE==.SITE 0,
then later on
IFE RUNSITE-SIXBIT/ML/,...
.SLDR selects SBLK output format, but outputs a loader in front
of the file.
.SPECI .FASL special variable reference.
.STGSW set nonzero => it is illegal to generate storage words.
.NSTGW and .YSTGW act by setting this flag.
.STOP stop current iteration of REPEAT or IRP, go on to next.
.STPLN Set to line # to break assembly at (see below)
.STPPG Set to page # to break assembly at. By setting .STPLN and .STPPG
you can break assembly (i.e. .INSRT the TTY) at an arbitrary point.
.SUCCESS
flag, used to make .ELSE and .ALSO work.
.SX .FASL quoted S-expression reference.
.SXE .FASL S-expression load time evaluated and value assembled in.
.SXEVA .FASL S-expression load time evaluated and value thrown away.
.SYMCNT returns the number of symbol table entries in use. This is the
number of user-defined symbols plus the number of initial symbols
(not counting symbols that have been expunged). It is useful for
determining what argument to give to .SYMTAB (below);
.SYMTAB <nsyms>,<nlitwds>
makes sure the symbol table can hold <nsyms> symbols
and the literal table can hold <nlitwds> words of literal.
If either table actually needs to be enlarged, both are
re-initialized, so that all user symbol definitions are lost.
For this reason, a .SYMTAB should come at the beginning of the
program. If both tables are already big enough (for example,
when the same .SYMTAB is seen on pass 2), .SYMTAB is a no-op.
The normal version of MIDAS starts out with space for 2700.
symbols, and has about 1200. initial symbols, so only
programs using more than 1500. symbols need a .SYMTAB.
To decide what symtab you need, try a very large value (10000.).
The number of symbols including initial symbols, printed at
the end of the assembly, is the minimum value you can use;
for best results choose an arg at least 20% larger.
The literal table size you need is usually the size of the largest
constants area; this can be computed from the constants
area addresses printed at the end of the assembly.
Sometimes, that size may cause a "Constants Global Table Full"
error, and a larger size must be used.
.TAG see .GO
.TTYFLG if greater than zero, TTY typeout is inhibited
(but not output to error output file if any).
.TTYMAC allows the program to read a few arguments from the TTY at
assembly time, and refer to them as dummy arguments.
.TTYMAC is used the way DEFINE is used (see *Note macros: Macros.)
but without a macro name; the macro definition is read in and
then immediately evaluated, with arguments read in from the TTY.
Try .TTYMAC FOO
BAR=FOO TERMIN
.TYO <code>,
Like TYO n MACLISP; prints on the TTY the character whose ASCII
code is <code>. Thus, .TYO 61 prints a "1".
.TYO6 <sixbit word>,
Prints the word, regarded as a sixbit.
Try .TYO6 .FNAM1,.TYO 40,.TYO6 .FNAM2,PRINTX/
/
.TYPE <sym>, Find definition status of symbol.
Value is one of the following:
-1 <sym> is really a number
0 common
1 pseudo or macro
17 not seen (except in this .TYPE)
12-16 either impossible or not documented yet.
Defined/Undefined
2 3 Local symbol (not global, not a var)
4 5 Local variable
6 7 Global variable
10 11 Global symbol (not a var)
.TZ <arg>,
is the number of trailing zeros in the value of <arg>.
.U containing block (as in .U"FOO)
.VECTOR <symbol>(<size>), <symbol2>(<size2>),...
Makes <symbol> be the name of a vector <size> words long.
The space is actually allocated by the next VARIAB, or by
the END statement. Like .SCALAR, more than one vector can
be specified at a time. If no size is specified, or size is zero,
the default size is used (initially 1, always set to last size used).
e.g. in ".VECTOR FOO(3),BAR" the vector BAR will have size 3.
This defaulting scheme is the only difference from .SCALAR.
.WALGN word-align; in byte mode, move up to a word boundary.
.XCREF <symbol1>,...
Suppress creffing of the specified symbols.
.YSTGW OK to generate storage words
1PASS one pass assembly. Implies RELOCA
ASCIC /text/ like ASCIZ but fill with ^C.
ASCII /text/ generate ascii character string.
ASCIZ /text/ ascii character string, 0 byte at end.
BLOCK <n>
reserve <n> words - increment "." by <n>.
COMMEN /text/
ignores the text.
CONSTA dump out literals seen so far.
DEFINE define a macro. See *Note macros: Macros
END <start>
terminates tha assembly, and sets the program starting address
to the argument (which is optional).
EQUALS <sym1>,<sym2>
sym1 gets same meaning as sym2.
EXPUNGE <symbol1>,...
forgets the definitions of the specified symbols. The symbols are
actually deleted from the symbol table altogether.
IF1 ifbody Assemble if pass 1.
IF2 ifbody Assemble if pass 2.
IFB string,ifbody Assemble if string blank (has no squoze chars).
IFDEF sym, ifbody Assemble if sym defined.
IFE exp, ifbody Assemble if = 0.
IFG exp, ifbody Assemble if > 0.
IFGE exp, ifbody Assemble if >= 0.
IFL exp, ifbody Assemble if < 0.
IFLE exp, ifbody Assemble if <= 0.
IFN exp, ifbody Assemble if ^= 0.
IFNB string, ifbody Assemble if string not blank.
IFNDEF sym, ifbody Assemble if sym not defined.
IFNSQ string, ifbody Assemble if string is not all squoze chars.
IFSE string,string,ifbody Assemble if strings equal.
IFSN string,string,ifbody Assemble if strings not equal.
IFSQ string, ifbody Assemble if string is all squoze chars.
IRP indefinite repeat (like macro args sort of). See *Note loops: Loops
IRPC indefinite repeat (characters).
IRPNC indefinite repeat (groups of characters).
IRPS indefinite repeat (symbols).
IRPW indefinite repeat (words - i.e. code lines).
LOC <arg>
set value of "." to <arg>.
NOSYMS don't put symbols in output.
NULL ifbody
This a "conditional" that always fails.
OFFSET <amount>
offset . and labels by specified amt (code to be moved before run).
PRINTC /text/ type out the text.
PRINTX /text/ Same as PRINTC, but ignores any "!" chars in the text.
RADIX <rdx>
set number radix to <rdx>.
RELOCA relocatable assembly.
REPEAT <n>,[<body>] repeat <body> <n> times. See *Note loops: Loops
RIM Readin mode output format. This is what the PDP-6 used.
A series of 2-wd pairs (DATAI PTR,loc ? val); last pair is a
transfer block with 1st wd an instruction taken from END stmt
and executed when transfer block is read. 2nd wd is a dummy.
RIM10 Readin mode output format, for KA-10 hardware bootstrap readin.
RIM10 format is a single block where the 1st word is IOWD n,,loc and
n = # words in rest of block, loc = location to load these words at.
Last loaded word is executed after readin is complete, so it should be
a JRST somewhere. MIDAS makes it unnecessary to do anything about
this as its strategy for RIM10 is to first output a
RIM10-format SBLK loader, followed by the code in normal SBLK format,
except that no symbols are provided.
SBLK Simple Block loader output format (this is the default).
Starts with a SBLK loader in RIM (not RIM10) format, followed by
code in SBLK format.
SIXBIT /text/ generate sixbit character string.
SQUOZE <code>,<symbol>
value is a word containing the squoze-code for <symbol>
with <code>/4 put in the top 4 bits.
SUBTTL <line>
ignores the line. This pseudo is for @'s sake.
TERMIN terminate macro body or indefinite repeat.
TITLE <pname> <junk>
specify name of program as <pname> (relocatable only).
Types <pname> and <junk> on the TTY.
It is at the TITLE that TTY will be .INSRT'ed by
the (T) switch.
VARIAB leave space for, and define, the "variables" seen so far.
"Variables" are symbols not defined and seen with singlequotes
and symbols seen in .SCALAR and .VECTOR pseudos.
WORD <arg>
outputs the argument directly to the binary file.
Allows writing of nonstandard binary formats.
XWD <lh>,<rh>
returns a word with the specified halfwords.
MIDAS Node: Outformats, Up: Top, Previous: Pseudos, Next: Changes
This node documents some obscure details of assembler output formats.
Much of the wording is taken from old DEC manuals.
----------------- RIM -----------------
This format is (was) primarily used in PDP-6 systems and
consists of a series of paired words. The first word of each pair is
a paper-tape read instruction giving the memory address of the second
word. E.g.
DATAI PTR,<loc>
<data word>
The last pair of words is a transfer block; the first is an
instruction obtained from the END statement and executed when the
transfer block is read, and the second is a dummy word to stop the reader.
The loader that reads this format is normally toggled into memory and
started at location 20:
LOC 20
CONO PTR,60
A: CONSO PTR,10
JRST .-1
DATAI PTR,B
CONSO PTR,10
JRST .-1
B: 0
JRST A
----------------- RIM10 -----------------
The PDP-10 has a hardware readin mode which can read in one
block of data. Programs which can be loaded using this readin mode
are said to use RIM10 format. The format of this block is:
-n,,loc-1 ; equiv to IOWD n,,loc
<n data words> ; the last data word is executed after readin.
In MIDAS, "RIM10" causes the output binary to start with a RIM10-format
SBLK loader provided by MIDAS. The assembled code then follows in SBLK
format. Only data blocks and the final transfer block are output (no
symbol blocks or anything else). This is very similar to MACRO's "RIM10B"
(see below). The RIM10 loader code can be found at label LDR10 in MIDAS.
In MACRO, "RIM10" causes the assembled code to be output exactly as it
is produced; i.e. the first data word is the first output word. No
blocking, checksumming, or anything else is done; in particular, no
loader is furnished by MACRO. This functionality can be achieved in
MIDAS by means of the WORD pseudo-op, which writes an arbitrary word
to the output file.
----------------- RIM10B -----------------
This is a MACRO format, not MIDAS, but is documented here anyway.
It is very similar to what MIDAS produces for "RIM10". That is, MACRO will
first output a loader in RIM10 format, followed by the assembled code in a
"simple-block" format. This simple-block format is identical to DECSAV
except that there is a checksum word following each block. In this respect
it is similar to SBLK, however it also differs in the way the checksum is
computed; RIM10B just adds words, whereas SBLK rotates the checksum 1 bit
before each add.
The following is the loader inserted by MACRO for RIM10B:
R1BLDR:
PHASE 0
IOWD $ADR,$ST
$ST: CONO PTR,60
HRRI $A,$RD+1
$RD: CONSO PTR,10
JRST .-1
DATAI PTR,@$TBL1-$RD+1($A)
XCT $TBL1-$RD+1($A)
XCT $TBL2-$RD+1($A)
$A: SOJA $A,
$TBL1: CAME $CKSM,$ADR
ADD $CKSM,1($ADR)
SKIPL $CKSM,$ADR
$TBL2: JRST 4,$ST
AOBJN $ADR,$RD
$ADR: JRST $ST+1
$CKSM:
DEPHASE
----------------- SBLK -----------------
When SBLK format is selected, MIDAS will (presumably for historical
reasons) first output a SBLK loader in RIM (not RIM10!) format, followed by
the assembled code in SBLK format. The latter consists of data blocks, a
transfer block, symbol table blocks, and any extra blocks.
SBLK is to ITS what DECSAV is to DEC systems.
SBLK format is documented in ITSDOC;BINFMT >, and the format of the
SBLK file symbol table in .INFO.;DDTORD >. The RIM loader code can
be found at label SLOAD in MIDAS.
----------------- DECSAV -----------------
DECSAV is a simple absolute format which can be used for
immediately executable programs on TOPS-10, TENEX, and TOPS-20.
It consists only of data blocks followed by a 2 word transfer block.
Each data block has the format:
IOWD n,,loc
<n words of data>
The transfer block is:
JRST start-address ; This word is specified by END
JRST reenter-address ; MIDAS always leaves this 0
----------------- DECREL -----------------
This format produces DEC relocatable format (.REL) files
which the DEC linking loader (LINK) can then use to put a program
together. It is not necessary to use this unless you need to load
in some already assembled modules or use other loader features; if the
MIDAS program is self-contained it is easier to use DECSAV format.
DEC relocatable format is documented in the DEC LINK Reference
Manual (TOPS-20 version is AD-4183C-T1).
----------------- STINK -----------------
The MIDAS "RELOCA" pseudo generates ITS relocatable format,
also known as STINK format because STINK is the name of the ITS linking
loader. This format is not well documented anywhere, which is partly
why very few people use STINK or RELOCA any more, especially when MIDAS
is fast enough that it is much easier and simpler to always produce
self-contained absolute assemblies. (Library routines are shared by
using .INSRT)
MIDAS Node: Changes, Up: Top, Previous: Outformats, Next: (MIDAS ARCHIV)*
Changes are catalogued in an uninfo-ized file. Do "L" to get back here.
* Menu:
* Changes: (MIDAS ARCHIV)* MIDAS changes in chronological order.
Local Modes:
Fill Column:75
Page Delimiter:
End: