Trailing-Edge
-
PDP-10 Archives
-
mit_emacs_170_teco_1220
-
info/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.
* Opcodes:: PDP-10 Instruction set
* 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 MEANINGLESS
,B, 16 MEANINGLESS
,B,C 17 MEANINGLESS
A 20 A
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
A,, 32 <TR(A)>_18.
A,,C 33 <TR(A)>_18.+TR(C)
A,B 34 A+TR(B)
A,B C 35 MEANINGLESS
A,B, 36 MEANINGLESS
A,B,C 37 MEANINGLESS
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) All addition done here 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 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. See PSEUDO-OPS.
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.
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.
There are also the RIM and RIM10 formats, which I may document some
day if I figure out once again what they do.
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
Maybel 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 verisimilitude 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.
MIDAS Node: Pseudos, Up: Top, Previous: Constructs, Next: Opcodes
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 wrd 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 MIDAS;COND >.
.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 MIDAS;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).
.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 - RELOCATABLE format only.
.ELSE conditional: "If the previous conditional failed". See MIDAS;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>
.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 specify interpretation of fields in a word.
See the old MIDAS manual (AI memo 90).
.FUNCT <function name>
refers to the specified function, in FASL format. See MIDAS;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 MIDAS;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 ??
.LIBRQ ??
.LIFE, .LIFG, .LIFGE, .LIFL, .LIFLE, .LIFN, .LIFND, .LIFS
Load-time conditionals. Matched by .ELDC
.LITSW if nonzero, using a literal causes an error message.
.LNKOT ??
.LOP <op>,<C(ac)>,<C(mem)>
like .OP, but the instruction is executed in STINK rather
than in MIDAS. The results are available as the eventual
values of the global symbols .LVAL1 and .LVAL2.
.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.
.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.
.SBLK Specifies ITS SBLK output format. For details, see
ITSDOC;BINFMT > and the "Symbol table format" section of
.INFO.;DDTORD >.
.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 SBLOCK 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.
.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 MIDAS;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>,
is -1 if the "symbol" is really a number. Otherwise, it says
what the definition status of the symbol is.
(-1 number, 0 common, 1 pseudo/macro, 2 defined,
10 global and defined, 17 or other odd number undefined)
.TZ <arg>,
is the number of trailing zeros in the value of <arg>.
.U containing block (as in .U"FOO)
.WALGN word-align; in byte mode, move up to a word boundary.
.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.
.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 MIDAS;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.
IF1 ifbody assem if pass 1.
IF2 ifbody assem if pass 2.
IFB assemble if string blank (has no squoze chars).
IFDEF assemble if sym defined.
IFE exp, ifbody assem if = 0.
IFG exp, ifbody assem if > 0.
IFGE exp, ifbody assem if >= 0.
IFL exp, ifbody assem if < 0.
IFLE exp, ifbody assem if <= 0.
IFN exp, ifbody assem if ^= 0.
IFNB assemble if string not blank.
IFNDEF assemble if sym not defined.
IFNSQ arg,
Assemble if arg is not all squoze chars.
IFSE string,string, ifbody assem if strings equal.
IFSN string,string, ifbody assem if strings not equal.
IFSQ arg,
Assemble if arg is all squoze chars (that is, has no non-squoze).
IRP indefinite repeat (like macro args sort of).
see MIDAS; MIDAS 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 is 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/ type out the text.
RADIX <rdx>
set number radix to <rdx>.
RELOCA relocatable assembly.
REPEAT <n>,[<body>]
repeat <body> <n> times. See MIDAS;LOOPS >
RIM readin mode output format.
RIM10 readin mode output format.
SBLK simple block loader output format (this is the default).
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: Opcodes, Up: Top, Next: Changes, Previous: Pseudos
In programming the PDP-10 it is convenient to imagine that your
program occupies contiguous virtual memory locations from 0 to some
maximum address. All memory locations are equivalent for most
purposes (but some operating systems reserve some of your space for
their own purposes).
Sixteen memory locations (addresses 0 to 17 - note that addresses
will appear in octal) are distinguished by their use as general
purpose registers (also called accumulators or index registers).
Most PDP-10 instructions address one memory operand and one
accumulator (so-called "one and a half address" architecture). This
means that nearly all instruction affect some accumulator. These
registers are actually implemented in high speed solid state memory
rather than in slower core. For any purpose where it is convenient
to do so, a user may reference an accumulator as memory.
Instruction classes are formed by a mnemonic class name and one or
more modifier letters. The modifiers usually signify some
transformation on the data or the direction of data movement or the
skip or jump condition. Some functional duplications and some no-ops
result from this scheme. However, despite these drawbacks, this
notion of instruction classes and modifiers makes the opcode set easy
to learn.
Full word instructions. MOVE, BLT, EXCH, PUSH, POP
These are the instructions whose basic purpose is to move one or more
full words of data from one location to another, usualy from an
accumulator to a memory location or vice versa. In some cases, minor
arithmetic operations are performed, such as taking the magnitude or
negative of a word.
The MOVE class of instructions perform full word data transmission
between an accumulator and a memory location. There are sixteen
instructions in the MOVE class. All mnemonics begin with MOV. The
first modifier specifies a data transformation operation; the second
modifier specifies the source of data and the destination of the
result.
|E no modification | from memory to AC
MOV |N negate source |I Immediate. Source is 0,,E to AC
|M magnitude |M from AC to memory
|S swap source |S to self. If AC<>0 to AC also
C(E) signifies contents of E (effective address) prior to the
execution of the instruction. C(AC) signifies contents of the AC
specified. CS(E) and CS(AC) signify the contents of E or AC with
left and right halves swapped. CR(AC) and CL(AC) signify the 18 bit
right and left contents of the AC. PC signifies the 18 bit contents
of the program counter.
MOVE C(AC) = C(E)
MOVEI C(AC) = 0,,E
MOVEM C(E) = C(AC)
MOVES C(E) = C(E); if AC<>0 then C(AC) = C(E)
MOVN C(AC) = -C(E)
MOVNI C(AC) = -E
MOVNM C(E) = -C(AC)
MOVNS C(E) = -C(E); if AC<>0 then C(AC) = -C(E)
MOVM C(AC) = |C(E)|
MOVMI C(AC) = 0,,E
MOVMM C(E) = |C(AC)|
MOVMS C(E) = |C(E)|; if AC<>0 then C(AC) = |C(E)|
MOVS C(AC) = CS(E)
MOVSI C(AC) = E,,0
MOVSM C(E) = CS(AC)
MOVSS C(E) = CS(E); if AC<>0 then C(AC) = CS(E)
EXCH exchanges the contents of the selected ac with the contents of
the effective address.
EXCH C(AC)><C(E)
The BLT (Block Transfer) instruction copies words from memory to memory.
The left half of the selected AC specifies the first source address.
The right half of the AC specifies the first destination address.
The effective address specifies the last destination address. Words
are copied, one by one, from the source to the destination, until a
word is stored in an address greater than or equal to the effective
address of the BLT.
Caution: BLT clobbers the specified AC. Don't use the BLT AC in
address calculation for the BLT, results will be random. If source
and destination overlap, remember that BLT moves the lowest source
word first. If the destination of the BLT includes the BLT AC, then
the BLT AC better be the last destination address.
Programming examples:
;Save all the accumulators:
MOVEM 17,SAVAC+17
MOVEI 17,SAVAC ;Source is 0, destination is SAVAC
BLT 17,SAVAC+16
;Restore all the accumulators:
MOVSI 17,SAVAC ;Source is SAVAC, destination is 0
BLT 17,17
;Zero 100 words starting at TABLE.
SETZM TABLE
MOVE AC,[TABLE,,TABLE+1] ;Source and destination overlap
BLT AC,TABLE+77
;Move 77 words from TABLE thru TABLE+76 to TABLE+1 thru TABLE+77: BLT
;can't be done here because the source and destination overlap. (See
;the description of POP)
MOVE AC,[400076,,TABLE+76]
POP AC,1(AC) ;Store TABLE+76 into TABLE+77, etc.
JUMPL AC,.-1
PUSH, POP Stack instructions.
These two instructions insert and remove full words in a pushdown
list. The address of the top of the list is kept in the right half
of the AC referenced by these instructions. The program may keep a
control count in the left half of the AC. There are also two
subroutine calling instructions (PUSHJ and POPJ) that use this same
format pushdown list.
PUSH C(AC)=C(AC)+<1,,1>; C(CR(AC))=C(E)
The specified accumulator is incremented by adding 1 to each half (in
the KI10 and KL10 carry out of the right half is suppressed). If, as
result of the addition, the left half of the AC becomes positive, a
pushdown overflow condition results (but the instruction procedes to
completion). The word addressed by the effective address is fetched
and stored on the top of the stack which is addressed by the right
half of the (incremented) accumulator.
POP C(E)=C(CR(AC)); C(AC)=C(AC)-<1,,1>
POP undoes PUSH as follows: the word at the top of the stack
(addressed by the right half of the selected AC) is fetched and
stored at the effective address. Then the AC is decremented by
subtracting 1 from both halves (in the KI10 and KL10 carry out of bit
18 is suppressed). If the AC becomes negative as a result of the
subtraction a pushdown overflow results.
Often the accumulator used as the pushdown pointer is given the
symbolic name P. To initialize a pushdown pointer (e.g., for N words
starting at PDLIST), one might do the following:
MOVE P,[IOWD N,PDLIST]
where the IOWD pseudo op assembles -N,,PDLIST-1. Elsewhere in the
program should appear:
PDLIST: BLOCK N
which defines the symbolic label PDLIST and reserves N words
following it.
Halfword instructions
The halfword class of instructions perform data transmission between
one half of an accumulator and one half of a memory location. There
are sixty-four halfword instructions. Each mnemonic begins with H
and has four modifiers. The first modifier specifies which half of
the source word; the second specifies which half of the destination.
The third modifier specifies what to do to the other half of the
destination. The fourth modifier specifies the source of data and
the destination of the result.
H halfword from |R right of source to
|L left
|R right of destination
|L left
| no modification of other half
|Z zero other half
|O set other half to ones
|E sign extend source to other half
| from memory to AC
|I Immediate
|M from AC to memory
|S to self. If AC<>0 to AC also.
HRR CR(AC) = CR(E)
HRRI CR(AC) = E
HRRM CR(E) = CR(AC)
HRRS CR(E) = CR(E); if AC<>0 then CR(AC) = CR(E)
HRRZ C(AC) = 0,,CR(E)
HRRZI C(AC) = 0,,E
HRRZM C(E) = 0,,CR(AC)
HRRZS C(E) = 0,,CR(E); if AC<>0 then C(AC) = 0,,CR(E)
HRRO C(AC) = 777777,,CR(E)
HRROI C(AC) = 777777,,E
HRROM C(E) = 777777,,CR(AC)
HRROS C(E) = 777777,,CR(E); if AC<>0 then C(AC) = 777777,,CR(E)
HRRE C(AC) = 777777*C18(E),,CR(E);
HRREI C(AC) = 777777*E18,,E
HRREM C(E) = 777777*C18(AC),,CR(AC)
HRRES C(E) = 777777*C18(E),,CR(E);
if AC<>0 then C(AC) = 777777*C18(E),,CR(E)
HRL CL(AC) = CR(E)
HRLI CL(AC) = E
HRLM CL(E) = CR(AC)
HRLS CL(E) = CR(E); if AC<>0 then CL(AC) = CR(E)
HRLZ C(AC) = CR(E),,0
HRLZI C(AC) = E,,0
HRLZM C(E) = CR(AC),,0
HRLZS C(E) = CR(E),,0; if AC<>0 then C(AC) = CR(E),,0
HRLO C(AC) = CR(E),,777777
HRLOI C(AC) = E,,777777
HRLOM C(E) = CR(E),,777777
HRLOS C(E) = CR(E),,777777; if AC<>0 then C(AC) = CR(E),,777777
HRLE C(AC) = CR(E),,777777*C18(E)
HRLEI C(AC) = E,,777777*E18
HRLEM C(E) = CR(AC),,777777*C18(AC)
HRLES C(E) = CR(E),,777777*C18(E);
if AC<>0 then C(AC) = CR(E),,777777*C18(E)
HLR CR(AC) = CL(E)
HLRI CR(AC) = 0
HLRM CR(E) = CL(AC)
HLRS CR(E) = CL(E); if AC<>0 then CR(AC) = CL(E)
HLRZ C(AC) = 0,,CL(E)
HLRZI C(AC) = 0
HLRZM C(E) = 0,,CL(AC)
HLRZS C(E) = 0,,CL(E); if AC<>0 then C(AC) = 0,,CL(E)
HLRO C(AC) = 777777,,CL(E)
HLROI C(AC) = 777777,,0
HLROM C(E) = 777777,,CL(AC)
HLROS C(E) = 777777,,CL(E); if AC<>0 then C(AC) = 777777,,CL(E)
HLRE C(AC) = 777777*C0(E),,CL(E);
HLREI C(AC) = 0
HRREM C(E) = 777777*C0(AC),,CL(AC)
HRRES C(E) = 777777*C0(E),,CL(E);
if AC<>0 then C(AC) = 777777*C0(E),,CR(E)
HLL CL(AC) = CL(E)
HLLI CL(AC) = 0
HLLM CL(E) = CL(AC)
HLLS CL(E) = CL(E); if AC<>0 then CL(AC) = CL(E)
HLLZ C(AC) = CL(E),,0
HLLZI C(AC) = 0
HLLZM C(E) = CL(AC),,0
HLLZS C(E) = CL(E),,0; if AC<>0 then C(AC) = CL(E),,0
HLLO C(AC) = CL(E),,777777
HLLOI C(AC) = 0,,777777
HLLOM C(E) = CL(E),,777777
HLLOS C(E) = CL(E),,777777; if AC<>0 then C(AC) = CL(E),,777777
HLLE C(AC) = CL(E),,777777*C0(E)
HLLEI C(AC) = 0
HLLEM C(E) = CL(AC),,777777*C0(AC)
HLLES C(E) = CL(E),,777777*C0(E);
if AC<>0 then C(AC) = CL(E),,777777*C0(E)
Arithmetic testing. AOBJP, AOBJN, JUMP, SKIP, CAM, CAI, AOS, SOS, SOJ, AOJ
The AOBJ (Add One to Both halves of AC and Jump) instructions allow
forward indexing through an array while maintaining a control count
in the left half of an accumulator. Use of AOBJN and AOBJP can
reduce loop control to one instruction.
AOBJN C(AC)=C(AC)+<1,,1>; If C(AC)<0 then PC=E;
AOBJP C(AC)=C(AC)+<1,,1>; If C(AC)>=0 then PC=E;
Example. Add 3 to N words starting at location TAB:
MOVSI 1,-N ;Initialize register 1 to -N,,0
MOVEI 2,3 ;register 2 gets the constant 3.
ADDM 2,TAB(1) ;add 3 to one array element.
AOBJN 1,.-1 ;increment both the index and the control.
;Loop until the ADDM has been done N times.
By the way, for the sake of consistency, AOBJN should have been called
AOBJL and AOBJP should have been called AOBJGE. However, they weren't.
The JUMP instructions compare the selected accumulator to zero and
jump (to the effective address of the instruction) if the specified
relation is true.
JUMP Jump never.
JUMPL If C(AC) < 0 then PC=E;
JUMPLE If C(AC) <= 0 then PC=E;
JUMPE If C(AC) = 0 then PC=E;
JUMPN If C(AC) <> 0 then PC=E;
JUMPGE If C(AC) >= 0 then PC=E;
JUMPG If C(AC) > 0 then PC=E;
JUMPA PC=E;
The SKIP instructions compare the contents of the effective address
to zero and skip the next instruction if the specified relation is
true. If a non-zero AC field appears, the selected AC is loaded from
memory.
SKIP If AC<>0 then C(AC)=C(E);
SKIPL If AC<>0 then C(AC)=C(E); If C(E) < 0 then skip
SKIPLE If AC<>0 then C(AC)=C(E); If C(E) <= 0 then skip;
SKIPE If AC<>0 then C(AC)=C(E); If C(E) = 0 then skip;
SKIPN If AC<>0 then C(AC)=C(E); If C(E) <> 0 then skip;
SKIPGE If AC<>0 then C(AC)=C(E); If C(E) >= 0 then skip;
SKIPG If AC<>0 then C(AC)=C(E); If C(E) > 0 then skip;
SKIPA If AC<>0 then C(AC)=C(E); skip;
The AOS (Add One to memory and Skip) instructions increment a memory
location, compare the result to zero to determine the skip condition,
If a non-zero AC field appears then the AC selected will be loaded
(with the incremented data).
AOS C(E) = C(E)+1; If AC<>0 then C(AC)=C(E);
AOSL C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); If C(E) < 0 then skip;
AOSLE C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); If C(E) <= 0 then skip;
AOSE C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); If C(E) = 0 then skip;
AOSN C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); If C(E) <> 0 then skip;
AOSGE C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); If C(E) >= 0 then skip;
AOSG C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); If C(E) > 0 then skip;
AOSA C(E) = C(E)+1; If AC<>0 then C(AC)=C(E); skip;
The SOS (Subtract One from memory and Skip) instructions decrement a
memory location, compare the result to zero to determine the skip
condition, If a non-zero AC field appears then the AC selected will
be loaded (with the decremented data).
SOS C(E) = C(E)-1; If AC<>0 then C(AC)=C(E);
SOSL C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); If C(E) < 0 then skip;
SOSLE C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); If C(E) <= 0 then skip;
SOSE C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); If C(E) = 0 then skip;
SOSN C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); If C(E) <> 0 then skip;
SOSGE C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); If C(E) >= 0 then skip;
SOSG C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); If C(E) > 0 then skip;
SOSA C(E) = C(E)-1; If AC<>0 then C(AC)=C(E); skip;
The AOJ (Add One to AC and Jump) instructions increment the contents
of the selected accumulator. If the result bears the indicated
relation to zero then the instruction will jump to the effective
address.
AOJ C(AC) = C(AC)+1;
AOJL C(AC) = C(AC)+1; If C(AC) < 0 then PC=E;
AOJLE C(AC) = C(AC)+1; If C(AC) <= 0 then PC=E;
AOJE C(AC) = C(AC)+1; If C(AC) = 0 then PC=E;
AOJN C(AC) = C(AC)+1; If C(AC) <> 0 then PC=E;
AOJGE C(AC) = C(AC)+1; If C(AC) >= 0 then PC=E;
AOJG C(AC) = C(AC)+1; If C(AC) > 0 then PC=E;
AOJA C(AC) = C(AC)+1; PC=E;
The SOJ (Subtract One from AC and Jump) instructions decrement the
contents of the selected accumulator. If the result bears the
indicated relation to zero then the instruction will jump to the
effective address.
SOJ C(AC) = C(AC)-1;
SOJL C(AC) = C(AC)-1; If C(AC) < 0 then PC=E;
SOJLE C(AC) = C(AC)-1; If C(AC) <= 0 then PC=E;
SOJE C(AC) = C(AC)-1; If C(AC) = 0 then PC=E;
SOJN C(AC) = C(AC)-1; If C(AC) <> 0 then PC=E;
SOJGE C(AC) = C(AC)-1; If C(AC) >= 0 then PC=E;
SOJG C(AC) = C(AC)-1; If C(AC) > 0 then PC=E;
SOJA C(AC) = C(AC)-1; PC=E;
The CAM (Compare Accumulator to Memory) class compare the contents of
the selected accumulator to the contents of the effective address.
If the indicated condition is true, the instruction will skip. The
CAM instruction is suitable for arithmetic comparision of either
fixed point quantities or normalized floating point quantities.
Needless to say, for the comparison to be meaningful both C(AC) and
C(E) should be in the same format (i.e., either both fixed or both
floating).
CAM no op (references memory)
CAML If C(AC) < C(E) then skip;
CAMLE If C(AC) <= C(E) then skip;
CAME If C(AC) = C(E) then skip;
CAMN If C(AC) <> C(E) then skip;
CAMGE If C(AC) >= C(E) then skip;
CAMG If C(AC) > C(E) then skip;
CAMA skip;
The CAI (Compare Accumulator Immediate) class compare the contents of
the selected accumulator to the effective address. If the indicated
condition is true, the instruction will skip. Note than an effective
address is an 18 bit quantity that is always considered to be
positive.
CAI no op
CAIL If C(AC) < E then skip;
CAILE If C(AC) <= E then skip;
CAIE If C(AC) = E then skip;
CAIN If C(AC) <> E then skip;
CAIGE If C(AC) >= E then skip;
CAIG If C(AC) > E then skip;
CAIA skip;
Fixed point arithmetic ADD, SUB, IMUL, IDIV, MUL, DIV
In positive numbers bit 0 is zero. Bits 1 is most significant; bit
35 is least significant. Negative numbers are the twos complement of
postive numbers. Results (of ADD, SUB or IMUL) outside the range
-2^35 to 2^35-1 will set overflow (PC bit 0).
ADD C(AC) = C(AC) + C(E);
ADDI C(AC) = C(AC) + E;
ADDM C(E) = C(AC) + C(E);
ADDB C(AC) = C(AC) + C(E); C(E) = C(AC);
SUB C(AC) = C(AC) - C(E);
SUBI C(AC) = C(AC) - E;
SUBM C(E) = C(AC) - C(E);
SUBB C(AC) = C(AC) - C(E); C(E) = C(AC);
The IMUL instructions are for multiplying numbers where the product
is expected to be representable as one word.
IMUL C(AC) = C(AC) * C(E);
IMULI C(AC) = C(AC) * E;
IMULM C(E) = C(AC) * C(E);
IMULB C(AC) = C(AC) * C(E); C(E) = C(AC);
The IDIV instructions are for divisions in which the dividend is a
one word quantity. AC+1 signifes the quantity (AC+1 modulo 20
octal). If the divisor is zero set overflow and no divide; don't
change AC or memory operands. The remainder will have the same sign
as the dividend.
IDIV C(AC) = C(AC) / C(E); C(AC+1) = remainder
IDIVI C(AC) = C(AC) / E; C(AC+1) = remainder;
IDIVM C(E) = C(AC) / E;
IDIVB C(AC) = C(AC) / C(E); C(AC+1) = remainder; C(E) = C(AC);
The MUL instructions produce a double word product. A double word
integer has 70 bits of significance. Bit 0 of the high order word is
the sign bit. In data, Bit 0 of the low order word is ignored by the
hardware. In results, bit 0 of the low word is the same as bit 0 in
the high word. MUL will set overflow if both operands are -2^35.
MUL C(AC AC+1) = C(AC) * C(E);
MULI C(AC AC+1) = C(AC) * E;
MULM C(E) = high word of product of C(AC) * C(E);
MULB C(AC AC+1) = C(AC) * C(E); C(E) = C(AC);
The DIV instructions are for divisions in which the dividend is a two
word quantity (such as produced by MUL). If C(AC) is greater than the
memory operand then set overflow and no divide.
DIV C(AC) = C(AC AC+1) / C(E); C(AC+1) = remainder;
DIVI C(AC) = C(AC AC+1) / E; C(AC+1) = remainder;
DIVM C(E) = C(AC AC+1) / E;
DIVB C(AC) = C(AC AC+1) / C(E); C(AC+1) = remainder; C(E) = C(AC);
Double word Move instructions (KI10 and KL10)
There are four double word move instructions. These are suitable for
manipulating KI10 and KL10 double precision floating point numbers,
and for KL10 double precision integers.
DMOVE C(AC AC+1) = C(E E+1)
DMOVEM C(E E+1) = C(AC AC+1)
DMOVN C(AC AC+1) = -C(E E+1)
DMOVNM C(E E+1) = -C(AC AC+1)
Note that the DMOVN and DMOVNM are NOT to be used for KA10 double
precision floating point numbers!
If a program is written that may be have to be run on a KA10, the use
of all double word instructions should be avoided.
Double Precision Integer Arithmetic (KL10 only)
There are four instructions for double precision integer arithmetic.
None of these instructions have any modifier: they all operate on
double (or quadruple) accumulators and double words in memory with
results to double (or quadruple) accumulators.
The format for a double word integer is the same as that produced by
MUL, i.e., a 70 bit integer in twos complement, with bit 0 of the
most significant word is the sign; in operands, bit 0 of the low
order word is ignored. A quadruple word has 140 bits; bit 0 of the
most significant word is the sign; in operands, bit 0 in all other
words is ignored. In double (and quadruple) arithmetic results bit 0
of the low order word(s) is stored with the same value as bit 0 of
the high order word.
DADD C(AC AC+1) = C(AC AC+1) + C(E E+1);
DSUB C(AC AC+1) = C(AC AC+1) - C(E E+1);
DMUL C(AC AC+1 AC+2 AC+3) = C(AC AC+1) * C(E E+1);
DDIV C(AC AC+1) = C(AC AC+1 AC+2 AC+3) / C(E E+1);
C(AC+2 AC+3) = remainder;
Floating Point Arithmetic
Single precision floating point numbers are represented in one 36 bit
word as follows:
0 00000000 011111111112222222222333333
0 12345678 901234567890123456789012345
______________________________________
| | | |
|S| EXP | Fraction |
|_|_______|____________________________|
If S is zero, the sign is positive. If S is one the sign is negative
and the word is in twos complement format. The fraction is
interpreted as having a binary point between bits 8 and 9. The
exponent is an exponent of 2 represented in excess 200 (octal)
notation. In a normalized floating point number bit 9 is different
from bit 0, except in a negative number bits 0 and 9 may both be one
if bits 10:35 are all zero. A floating point zero is represented by
a word with 36 bits of zero. Floating point numbers can represent
numbers with magnitude within the range 0.5*2^-128 to
(1-2^-27)*2^127, and zero.
A number that in which bit 0 is one and bits 9-35 are zero can
produce an incorrect result in any floating point operation. Any
word with a zero fraction and non-zero exponent can produce extreme
loss of precision if used as an operand in a floating point addition
or subtraction.
In KI10 (and KL10) double precision floating point, a second word is
included which contains in bits 1:35 an additional 35 fraction bits.
The additional fraction bits do not significantly affect the range of
representable numbers, rather they extend the precision.
The KA10 lacks double precision floating point hardware, however
there are several instructions by which software may implement double
precision. These instructions are DFN, UFA, FADL, FSBL, FMPL, and
FDVL. Users of the KL10 are strongly advised to avoid using these
intructions.
In the PDP-6 floating pointing is somewhat different. Consult a wizard.
|AD add | result to AC
F floating |SB subtract |R rounded |I Immediate. result to AC
|MP multiply | |M result to memory
|DV divide | |B result to memory and AC
|
|
| no rounding | result to AC
|L Long mode
|M result to memory
|B result to memory and AC
|AD add
DF double floating |SB subtract
|MP multiply
|DV divide
Note: In immediate mode, the memory operand is <E,,0>. In long mode
(except FDVL) the result appears in AC and AC+1. In FDVL the AC
operand is in AC and AC+1 and the quotient is stored in AC with the
remainder in AC+1.
Other floating point instructions:
FSC (Floating SCale) will add E to the exponent of the number in AC
and normalize the result. One use of FSC is to convert an integer in
AC to floating point (but FLTR, available in the KI and KL is better)
To use FSC to float an integer, set E to 233 (excess 200 and shift
the binary point 27 bits). The integer being floated must not have
more than 27 significant bits. FSC will set AROV and FOV if the
resulting exponent exceeds 127. FXU (and AROV and FOV) will be set
if the exponent becomes smaller than -128.
DFN (Double Floating Negate) is used only to negate KA10 software
format double precision floating point numbers. DFN treats AC and E
as a KA10 double floating point number which it negates and stores
back. AC is the high order word. Usually the low order word is in
AC+1, so the instruction most often appears as DFN AC,AC+1.
UFA (Unnormalized Floating Add) is used in KA10 to assist in software
format double precision arithmetic. UFA will add C(AC) to C(E) and
place the result in AC+1. The result of UFA will not be postnormalized
unless in original operands the exponents and signs were the same and
a fraction with magnitude greater than or equal to 1 was produced. Only
in this case will a one step normalization (right shift) occur.
UFA will overflow in the same circumstances as FAD. Underflow is not
possible.
FIX will convert a floating point number to an integer. If the
exponent of the floating point number in C(E) is greater than
(decimal) 35 (which is octal 243) then this instruction will set AROV
and not affect C(AC). Otherwise, convert C(E) to fixed point by the
following procedure: Move C(E) to AC, copying bit 0 of C(E) to bits
1:8 of AC (sign extend). Then ASH AC by X-27 bits (where X is the
exonent from bits 1:9 of C(E) less 200 octal).
FIX will truncate towards zero, i.e., 1.9 is fixed to 1
and -1.9 is fixed to -1.
FIXR (Fix and round) will convert a floating point number to an
integer by rounding. If the exponent of the floating point number in
C(E) is greater than (decimal) 35 (which is octal 243) then this
instruction will set AROV and not affect C(AC). Otherwise, convert
C(E) to fixed point by the following procedure: Move C(E) to AC,
copying bit 0 of C(E) to bits 1:8 of AC (sign extend). Then ASH AC
by X-27 bits (where X is the exponent from bits 1:9 of C(E) less 200
octal). If X-27 is negative (i.e., right shift) then the rounding
process will consider the bits shifted off the right end of AC. If
AC is positive and the discarded bits are >=1/2 then 1 is added to AC.
If AC is negative and the discarded bits are >1/2 then 1 is added to
AC. Rounding is always in the positive direction: 1.4 becomes 1, 1.5
becomes 2, -1.5 becomes -1, and -1.6 becomes -2.
FLTR (FLoaT and Round) will convert C(E), an integer, to floating
point and place the result in AC. The data from C(E) is copied to AC
where its is arithmetic shifted right 8 places (keeping the bits that
fall off the end) and the exponent 243 is inserted in bits 1:8. The
resulting number is normalized until bit 9 is significant
(normalization may result in some or all of the bits that were right
shifted being brought back into AC). Finally, if any of the bits
that were right shifted still remain outside the AC the result is
rounded by looking at the bit to the right of the AC.
Shift instructions
The following instructions shift or rotate the AC or the double word
formed by AC and AC+1. The number of places to shift is specified by
the effective address which is considered to be a signed number
modulo 256 in magnitude. That is, the effective shift is the number
composed of bit 18 (the sign) of the effective address and bits 28:35
of the effective address. If E is positive, a left shift occurs.
If E is negative a right shift occurs.
LSH Logical Shift. C(AC) is shifted as specified by E. Zero
bits are shifted into the AC.
LSHC Logical Shift Combined. C(AC AC+1) is shifted as a 72 bit
quantity. Zero bits are shifted in.
ASH Arithmetic Shift. Bit 0 is not changed. In a left
shift zero bits are shifted into the right end of AC.
In a left shift, if any bit of significance is shifted
out of bit 1, AROV (overflow) is set. In a right shift,
bit 0 is shifted into bit 1.
ASHC Arithmetic Shift Combined. AC bit 0 is not changed. If
E is non zero, AC bit 0 is copied to AC+1 bit 0.
C(AC AC+1) is shifted as a 70 bit quantity. In a left
shift zero bits are shifted into the right end of AC+1.
In a left shift, if any bit of significance is shifted
out of AC bit 1 then AROV is set. In a right shift AC
bit 0 is shifted into AC bit 1.
ROT Rotate. The 36 bit C(AC) is rotated. In a left rotate bits
shifted out of bit 0 are shifted into bit 35. In a right
rotate, bit 35 is shifted into bit 0.
ROTC Rotate Combined. AC and AC+1 are rotated as a 72 bit
quantity. In a left rotate AC bit 0 shifts into AC+1
bit 35 and AC+1 bit 0 shifts into AC bit 35. In a right
rotate, AC+1 bit 35 shifts into AC bit 0, etc.
Byte instructions
In the PDP-10 a "byte" is some number of contiguous bits within one
word. A byte pointer is a word that describes the byte. There are
three parts to the description of a byte: the word (i.e., address) in
which the byte occurs, the position of the byte within the word, and
the length of the byte.
A byte pointer has the following format:
000000 000011 1 1 1111 112222222222333333
012345 678901 2 3 4567 890123456789012345
_________________________________________
| | | | | | |
| POS | SIZE |U|I| X | Y |
|______|______|_|_|____|__________________|
POS is the byte position: the number of bits from the right end of
the byte to the right end of the word.
SIZE is the byte size in bits. The U field is ignored by the
hardware. I, X, and Y are the same as in an instruction.
LDB - Load byte. The contents of the effective address of the LDB
instruction is interpreted as a byte pointer. The byte described
there is loaded, right adjusted, into the AC. The rest of the AC is
cleared.
DPB - Deposit byte. The contents of the effective address of the DPB
instruction is interpreted as a byte pointer. The byte described
there is deposited from the byte of the same size at the right end of
the AC. AC and the remainder of the word into which the byte is
deposited are left unchanged.
IBP - Increment byte pointer. The AC field must be zero. The
contents of the effective address are fetched. The POS field is
changed by subtracting the size field from it. If the result of the
subtraction is greater than or equal to zero, store the difference in
the POS field. If the difference is negative, add 1 to the Y field
(in the KA10 and PDP-6 if Y contains 777777 then this will carry into
the X field; in the KI10 and KL10 the carry out is suppressed) and
set POS field to 44-SIZE (44 is octal). The effect of this is to
modify the byte pointer to address the next byte (of the same size)
that follows the byte addressed by the original pointer.
ILDB - Increment and Load Byte. Increment the byte pointer contained
at the effective address. Then perform a LDB function using
the updated byte pointer.
IDPB - Increment and Deposit Byte. Increment the byte pointer contained
at the effective address. Then perform a DPB function using
the updated byte pointer.
For convenience, the assembler has a pseudo op for creating byte
pointers. The POINT pseudo op has three parameters, the size, the
address, and position. In the POINT pseudo op, the position argument
specifies the bit number of the right most bit in the byte. If the
position field is omitted, bit number "-1" is assumed (this assembles
44 in the POS field) which doesn't address any byte, but which, when
incremented once, will address the first byte in the specified word.
POINT 7,100(1) 440701,,100
POINT 36,@2000,35 004420,,2000
Logical Testing and Modification.
The Test instructions are for testing and modifying bits in an
accumulator. There are 64 instructions. Each mnemonic begins with a
T and is followed by three modifiers.
|R right half immediate
Test accumulator |L left half immediate
|D direct mask
|S swapped mask
|N no modification
|Z zero selected bits
|O set selected bits to One
|C complement selected bits
| never skip
|N skip unless all selected bits are zero
|E skip if all selected bits are zero
|A skip always
The test operation considers two 36 bit quantities. One of these is
the contents of the selected AC. The other quantity, called the
mask, depends on the first modifier letter. For R the mask is
<0,,E>; for L it is <E,,0>. For D the mask is C(E), and for S the
mask is CS(E), the swapped contents of E.
If the skip condition N is specified, then the test instruction will
skip if the AND of the mask and the AC operand is Not equal to zero.
If the skip condition E is specified, then the test instruction will
skip if the AND of the mask and the AC operand is Equal to zero.
If the modification code Z appears then bits that one in mask are
made zero in the AC.
If the modification code O appears then bits that one in mask are
made one in the AC.
If the modification code C appears then bits that one in mask are
complemented in the AC.
Note that the skip condition is determined on the basis of the
contents of the AC before it is modified.
The principle use for the Test instructions is in testing and
modifying single bit flags that are kept in an accumulator.
Boolean Logic
There are 16 possible boolean functions of 2 variables. The PDP-10
has 16 instruction classes (each with 4 modifiers) that perform these
operations. Each boolean function operates on the 36 bits of AC and
memory as individual bits.
C(AC) 0 0 1 1
C(E) 0 1 0 1
SETZ 0 0 0 0 SET to Zero
AND 0 0 0 1 AND
ANDCM 0 0 1 0 AND with Complement of Memory
SETA 0 0 1 1 SET to AC
ANDCA 0 1 0 0 AND with Complement of AC
SETM 0 1 0 1 SET to Memory
XOR 0 1 1 0 eXclusive OR
IOR 0 1 1 1 Inclusive OR
ANDCB 1 0 0 0 AND with Complements of Both
EQV 1 0 0 1 EQuiValence
SETCM 1 0 1 0 SET to Complement of Memory
ORCA 1 0 1 1 OR with Complement of Memory
SETCA 1 1 0 0 SET to Complement of AC
ORCA 1 1 0 1 OR with Complement of AC
ORCB 1 1 1 0 OR with Complements of Both
SETO 1 1 1 1 SET to One
Each of the 16 instructions above have four modifiers that specify
where to store the result. No modifier means result to AC. Modifier
I means Immediate: the memory data is <0,,E> and the result goes to
AC. M as a modifier means result should be stored in memory. B
means store the results in both memory and AC.
PC format.
JSR, JSP, and PUSHJ all store a full word that contains the PC and
various flags. The format of a PC word is:
0 0 0 0 0 0 0 0 0 0 1 1 1 11111 112222222222333333
0 1 2 3 4 5 6 7 8 9 0 1 2 34567 890123456789012345
__________________________________________________
|A|C|C|F|F|U|I|P|A|T|T|F|D| | |
|R|R|R|O|P|S|O|U|F|R|R|X|C|00000| PC |
|O|Y|Y|V|D|E|T|B|I|A|A|U|K| | |
|V|0|1| | |R| |L| |P|P| | | | |
| | | | | | | | | |2|1| | | | |
|_|_|_|_|_|_|_|_|_|_|_|_|_|_____|__________________|
AROV, ARithmetic OVerflow, is set by any of the following:
A single instruction has set one of CRY0 or CRY1 without
setting them both.
An ASH or ASHC has left shifted a significant bit out of
AC bit 1.
A MULx instruction has multiplied -2^35 by itself.
A DMUL instruction has multiplied -2^70 by itself.
An IMULx instruction has produced a product less than
-2^35 or greater than 2^35-1.
A FIX or FIXR has fetched an operand with exponent
greater than 35.
FOV (Floating Overflow) has been set.
DCK (Divide ChecK) has been set.
CRY0, CaRrY 0, if set without CRY1 being set will set AROV. This
indicates any of the following conditions:
An ADDx has added two negative numbers with sum less
than -2^35.
A SUBx has subtracted a positive number from a negative
number and produced a result less than -2^35.
A SOSx or SOJx has decremented -2^35.
If CRY0 and CRY1 are both set, this indicates that one of the
following non-overflow events has occurred:
In ADDx both summands were negative, or their signs differed
and the postive one was greater than or equal to the
magnitude of the negative summand.
In SUBx the sign of both operands was the same and the AC
operand was greater than or equal to the memory
operand, or the AC operand was negative and the
memory operand was postive.
An AOJx or AOSx has incremented -1.
A SOJx or SOS has decremented a non zero number other
than -2^35.
A MOVNx has negated zero.
CRY1, CaRrY 1, if set without CRY0 being set will set AROV. This
indicates any of the following conditions:
An ADDx has added two positive number with a sum greater
than 2^35-1.
A SUBx has subtracted a negative number from a positive
number to form a difference greater than 2^35-1.
An AOSx or AOJx instruction has incremented 2^35-1.
A MOVNx or MOVMx has negated -2^35.
A DMOVNx has negated -2^70
FOV, Floating point OVerflow, is set by any of:
In a floating point instruction other than FLTR, DMOVNx,
or DFN the exponent of the result exceeds 127.
FXU (Floating eXponent Underflow) has been set.
DCK (Divide ChecK) has been set by FDVx, FDVRx, or DFDV.
FPD, First Part Done, is set when the processor responds to a
priority interrupt, after having completed the first part of a two
part instruction (e.g., ILDB). This flag is not usually of interest
to the programmer.
USER is set while the processor is in user mode. In user mode,
various instruction and addressing restrictions are in effect.
IOT, User IN-Out mode, (also called IOT User), is a special mode in
which some of the user mode instruction (but not addressing)
restrictions are removed. In this mode a user program may perform
the hardware I/O instructions.
PUBL, Public mode, signifies that the processor is in user public
mode or in exec supervisor mode [KI10, KL10 only].
AFI, Address Failure Inhibit, if this flag is set, address break is
inhibited for during the execution of teh next instruction [KI10,
KL10 only].
TRAP2 - if bit 10 is not also set, pushdown overflow has occurred.
If traps are enabled, setting this flag immediately causes a trap.
At present no hardware condition sets both TRAP1 and TRAP2
simultaneously. [KI10 KL10 only]
TRAP1 - if bit 9 is not also set, arithemetic overflow has occurred.
If traps are enabled, setting this flag immediately causes a trap.
At present no hardware condition sets both TRAP1 and TRAP2
simultaneously. [KI10 KL10 only]
FXU, Floating eXponent Underflow, is set to signify that in a
floating instruction other than DMOVNx, FLTR, or DFN, the exponent of
the result was less than -128 and AROV and FOV have been set.
DCK, Divide ChecK, signifies that one of the following conditions has
set AROV:
In a DIVx the high order word of the dividend was greater than
or equal to the divisor.
In an IDIVx the divisor was zero.
In an FDVx, FDVRx, or DFDV, the divisor was zero, or the
magnitude of the dividend fraction was greater than
or equal to twice the magnitude of the divisor fraction.
In either case, FOV is also set.
Bits 13 through 17 of the PC word are always zero to facilitate the
use of indirect addressing to return from a subroutine.
Bits 18 through 35 store an address that is one greater than the
address of the instruction that stores the PC. Thus, the PC word
points at the instruction immediately following the subroutine call.
Program Control: JSR, JSP, JSA, JRA, PUSHJ, POPJ, JRST, JFCL, XCT, JFFO
JSR C(E)=<flags,,PC>; PC=E+1;
JSR, Jump to SubRoutine, stores the PC in the word addressed by the
effective address and jumps to the word following the word where the
PC is stored. This is the only PDP-10 instruction that stores the PC
and flags without modifying any ACs; however, it is non-reentrant, so
PUSHJ is favored in most cases. The usual return from a subroutine
called by a JSR is via JRST (or JRST 2,) indirect through the PC
word. (See JRST)
------
JSP C(AC)=<flags,,PC>; PC=E;
JSP, Jump and Save PC, stores the PC and flags in the selected
accumulator and jumps.
------
JSA C(E)=C(AC); C(AC)=<E,,PC>; PC=E+1;
JSA, Jump and Save AC, stores the AC in word addressed by the
effective address. Then the left half of the AC is set to the
effective address and the right half of AC is set to the return PC.
Then the PC is set to one greater than the effective address. The
JRA instruction unwinds this call. The advantage of this call is
that a routine may have multiple entry points (which is difficult to
do with JSR) and it's easy to find (and later to skip over) arguments
that follow the calling instruction (which is possible to do with
PUSHJ, but not quite so convenient). Among the disadvantages of this
call is that it is non reentrant, and it doesn't save flags.
------
JRA C(AC)=C(CL(AC)); PC=E;
JRA, Jump and Restore AC, is the return from JSA. If, e.g., a
subrountine is called with JSA AC, then the return is made by:
JRA AC,(AC).
------
PUSHJ C(AC)=C(AC)+<1,,1>; C(CR(AC))=<flags,,PC>; PC=E;
PUSHJ (PUSH return address and Jump) is like PUSH except the data
that is pushed onto the top of the stack is the PC and flags word.
The PC that is stored is the PC of the instruction that follows the
PUSHJ. Then the PC is set to the effective address of the
instruction. Pushdown overflow results if the AC becomes positive
when it is incremented.
------
POPJ PC=CR(CR(AC)); C(AC)=C(AC)-<1,,1>
POPJ (POP return address and Jump) undoes PUSHJ. The right half of
the word at the top of the stack is loaded into the PC (the flags are
unchanged). Then the stack pointer is decremented as in POP. The
effective address of POPJ is ignored. Pushdown overflow obtains if
the AC becomes negative as a result of the subtraction.
Programming hints:
If a subroutine called by PUSHJ AC, wants to skip over the
instruction following the PUSHJ, the following sequence accomplishes
that result:
AOS (AC) ;AC better be non zero.
POPJ AC,
If you must restore the flags that PUSHJ saved, the following
sequence should be used instead of POPJ:
POP AC,(AC) ;Adjust the stack
JRST 2,@1(AC) ;Restore flags and PC from old stack top.
------
JRST, Jump and ReSTore, is an unconditional jump instruction. In
JRST, the AC field does not address an accumulator. Instead, the AC
is decoded to signify various things.
JRST PC=E;
JRST 2, PC=E; flags are restored (see text);
JRST 10, PC=E; Dismiss current priority interrupt;
JRST 12, PC=E; restore flags and dismiss priority interrupt;
If the AC field is zero, only a jump occurs. JRST is everyone's
favorite unconditional jump instruction (the only other one is JUMPA
which is more typing, also, on older machines JUMPA was slower than
JRST).
JRST 2, (i.e., JRST with AC field set to 2) signifies jump and
restore flags. (The assembler also recognizes the mnemonic JRSTF for
JRST 2,). If indirection is used in JRSTF, then the flags are
restored from the last word fetched in the address calculation. If
indexing is used with no indirection, the flags are restored from the
left half of the specified index register. If neither indexing nor
indirection is used in the address calculation the flags are restored
from the left half of the JRSTF itself! In a user mode program JRSTF
cannot clear USER nor can it set IOT User (it can however, clear IOT
User).
The following are all illegal in user mode and are trapped as UUOs.
JRST 4, (alternate mnemonic HALT) sets the PC from E and stops the
processor.
JRST 10, is used to dismiss the current priority interrupt. Usually
JRST 12, is used for this purpose since JRST 10, fails to retore
flags.
JRST 12, (an alternate mnemonic is JEN, jump and enable priority
interrupts) combines the functions of JRST 10, and JRST 2,.
------
The JFCL (Jump on Flag and CLear) instruction is another case in
which the AC field is decoded to modify the instruction. The AC
field selects the four flags in PC bits 0 through 3. PC bits 0 to 3
correspond to bits 9 to 12 in the JFCL instruction. JFCL will jump
if any flag selected by the AC field is a 1. All flags selected by
the AC field are set to zero.
JFCL 0, since it selects no PC bits, is a no-op.
JFCL 17, will clear all flags, and will jump if any of AROV, CRY0,
CRY1, or FOV are set.
JFCL 1, (JFOV) jumps if FOV is set and clears FOV.
JFCL 10, (JOV) jumps if AROV is set and clears AROV.
------
XCT, the eXeCuTe instruction, fetches the word addressed by the
effective address and executes that word as an instruction. In the
case of XCTing an instruction that stores a PC, the PC that is stored
is the address of the instruction that follows the XCT. If the
executd instruction skips, then that skip is relative to the XCT.
The AC field of the XCT should be zero. [In exec mode a non zero AC
field in an XCT is significant.]
------
JFFO (Jump if Find First One) tests the selected AC. If C(AC)=0 then
set C(AC+1) to zero and execute the next instruction. If C(AC)<>0
then set C(AC+1) to the count of the number of zero bits in C(AC) to
the left of the first one bit and jump to the effective address.
C(AC) is unchanged.
MIDAS Node: Changes, Up: Top, Previous: Opcodes, 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: