DECSYSTEM-20 Assembly Language Guide Edited by: Frank da Cruz Chris Ryland Columbia University Center for Computing Activities New York, New York 10027 3 July 1980 Assembly Language Guide Page 1 Preface This document is intended to be a comprehensive introduction to assembly language programming on the DECSYSTEM-20. It consists of excerpts from various DEC manuals and other documents, with the addition of programming examples and some original material. Appropriate credit is given in each chapter or section in which the material is not original. Chapter 8 attempts to present a programming standard for Macro programs; in a sense it is the most important chapter because unless a program is clear and understandable, it will not be adaptable to new circumstances, and its usefulness and lifetime will be limited. This is a draft. Various sections still need to be filled in or refined, and more material added. This will be done from time to time. Comments are welcome. Introduction Page 2 1. Introduction Assembly language is a tool for writing computer programs consisting, at the source (program text) level, of actual machine instructions. It is sometimes desirable or necessary to use assembly language for two reasons: 1. Only in assembly language can you write a program that can take advantage of all the features of a given machine. Higher-level languages purposely conceal the machine from programmers so that programs may be transported from one machine to another. 2. You have maximum control over every aspect of the operation of your program, especially storage allocation and efficiency. In order to use an assembler, you must first be familiar with the machine's instruction set. This is described in Chapter 2. You will notice that this chapter actually describes three different machines: the KA10, the KI10, and the KL10. You should be aware that DEC-20's are KL10's (2020's are KS10's, but these are identical to KL10's for all practical purposes). There are several assemblers suitable for use on the DEC-20. These include Macro-20 (the standard DEC assembler), Midas (an alternative from MIT), and Fail (a fast 1-pass block structured assembler from Stanford). Only Macro-20 is supported by DEC, but the other two have certain distinct advantages. Macro-20 is described in Chapter 3. Another thing that assembly-language programmers need to know about is monitor calls. On a timesharing system, such as the DECSYSTEM-20, there are many things that you cannot do even in assembly language, such as issue input/output instructions; only the monitor can do such things. You can ask the monitor to perform services for you by issuing a monitor call (a DEC-20 monitor call is called a 'JSYS' (Jump to SYStem)), which amounts to calling a subroutine in the monitor. Information about DEC-20 monitor calls is given in Chapters 4 and 5 There are various aids available to assembly language programmers; these include libraries of helpful macros and routines (Chapters 6, 7), and interactive, symbolic debugging facilities (Chapter 10). In addition, there are chapters on how to write, run, and debug programs on the DECSYSTEM-20 (9), and some sample programs (11). And, as pointed out in the Preface, a very important Chapter on programming style and standards (8). The major intent of this guide is to provide a consolidated resource for those who wish to write assembly language programs, not assembly language subroutines to be called from higher-level languages; such subroutines can only be written with detailed knowledge of the calling conventions and internal data representations of the given language. The reader should have some knowledge of programming and some familiarity with DECSYSTEM-20 commands and procedures. Introduction Page 3 1.1. Basic Concepts Before we can proceed with descriptions of the instruction set, assembler, and monitor calls, some terminology and a few basic concepts of machine organization must be introduced. 1.1.1. Terminology Timesharing One way of running a computer. Timesharing allows many users to use the computer at once, seated at terminals, and to converse via the terminal with various programs on the computer. DECSYSTEM-20 A timesharing computer system, the subject of this manual. DEC Digital Equipment Corporation. The manufacturer of the DECSYSTEM-20, and of its predecessors, the PDP-10 and PDP-6. Operating System A program, or set of programs, that controls the operation of the computer. On a timesharing computer, the operating system functions include scheduling among users, allocating resources to users, controlling devices, and performing various other services for users that could not be done by the users themselves. Tops-20 Timesharing OPerating System-20. This is the name of the DECSYSTEM-20's operating system. Monitor One component of Tops-20. It is a program that is always running, and that performs most operating system functions. Exec Another component of Tops-20. It is the program by which users communicate their desires to the monitor, and through which the monitor communicates to the user. 1.1.2. Machine Organization The computer consists of various entities. You must be aware of what some of them are in order to program in assembly language: Memory This is a device that allows the computer to store and retrieve a limited amount of data very quickly. It is not permanent storage. It is often referred to as "core" memory (it is sometimes made from magnetic cores), to differentiate it from registers, disks, and other kinds of memory. Whether it's made from cores or not, it is solid-state memory, i.e. it has no moving parts. Register (Also called an Accumulator) This is a special kind of solid- state memory that is much faster than ordinary memory, and that allows operations such as arithmetic to be performed on data that is stored there. The DECSYSTEM-20 has 16 registers. Disk A kind of mechanical memory that is much slower than registers Introduction Page 4 or core memory, but which allows long-term storage of data in files whose names are kept in directories. Disks can hold much more data than core memory. CPU Central Processing Unit. This is the part of the computer that does most of the work, and where the major "intelligence" is to be found. It consists of the registers and the logic to move data to and from the registers, as well as logic to operate on the data in the registers (e.g. to do arithmetic). Instruction An instruction is a code to activate some function of the CPU. Typically, it specifies what operation to perform, what data to operate on, and where to put the result. Typical operations include arithmetic (add, subtract, etc.), transfer of control, comparison of numbers, etc. Assembly language programs consist of a sequence of instructions. When the program is being executed, the instructions are kept in memory. Address An address is a number that expresses the location of a quantity (instruction or data) in memory. Used as a verb, "address" means to "refer to". Bit (BInary Digit) The smallest unit of storage in memory. A bit is a quantity whose value can be 0 or 1. Word The major unit of storage in memory. In the DECSYSTEM-20, each word consists of 36 bits, and has its own address. You can address 262144 words of memory on the DECSYSTEM-20. Byte An intermediate unit of storage; any sequence of bits within a word. A byte is often the unit of storage for a character. Input/Output This is the act of transferring data between memory and some device, typically a disk or a terminal. 1.1.3. Instructions and Addressing Modes [ this is mostly taken care of in Ch. 2 ] 1.1.4. Internal Representation of Numbers 1.1.4.1. Binary Numbers [ to be filled in ] Introduction Page 5 1.1.4.2. Two's Complement Representation [ to be filled in ] 1.1.4.3. Integers [ to be filed in ] 1.1.4.4. Floating Point Numbers [ to be filled in ] 1.1.5. Arithmetic [ to be filled in ] 1.1.5.1. Integer Arithmetic [ to be filled in ] 1.1.5.2. Floating Point Arithmetic [ to be filled in ] 1.1.6. Logical Operations [ to be filled in ] 1.1.7. Character String Manipulation [ to be filled in ] 1.1.8. Elementary Data Structures 1.1.8.1. Tables (Arrays) and Indexing [ to be filled in ] Introduction Page 6 1.1.8.2. Stacks [ to be filled in ] Instruction Set Page 7 2. The PDP-10/DECSYSTEM-20 Instruction Set 2.1. Introduction This chapter was written by Ralph E. Gorin at Stanford University and modified slightly at Columbia. The PDP-10 is a general purpose stored program computer. There are four different processors (computers) in the PDP-10 family: the PDP-6, the KA10, the KI10 and the KL10. The newest of these is the KL10 which is the central processor in various DECsystem-10 and DECSYSTEM-20 configurations. (The KS10, found only in the DECSYSTEM-2020, is nearly identical to the KL10.) In general, we shall discuss the KL10 processor. There are three principal aspects of assembly language programming: the machine instructions, the assembler, and the operating system. The machine instructions are the primitive operations with which we write programs. Learning the instruction set means learning what operations are performed by each instruction. Programming is the art or science of combining these operations to accomplish some particular task. The machine instructions, like everything else in a computer, are in binary. The assembler is a program that translates the mnemonic names by which we refer to instructons into the binary form that the computer recognizes. The assembler also does a variety of other chores that are essentially bookkeeping. The operating system, or "monitor", is a special program that handles all input and output and which schedules among user programs. For its own protection and the protection of other users the operating system places various restrictions on user programs. User mode programs are resticted to memory assigned to them by the operating system; they may not perform any machine input-output instructions, nor can they perform several other restricted operations (e.g., HALT instruction). To facilitate user input-output and core allocation the operating system provides various monitor calls ( or JSYS operations) by which a user program can communicate its wishes to the system. Essentially all programs except the operating system itself are run as user mode programs. Editors, assemblers, compilers, utilities, and programs that you write yourself are all user mode programs. The PDP-10 is a word oriented machine. Words contain 36 data bits, numbered (left to right) 0 to 35. Every machine instruction is one word. There are two formats for machine instructions. Most instructions have the format: Bit 000000000 0111 1 1111 112222222222333333 Position 012345678 9012 3 4567 890123456789012345 ________________________________________ | | | | | | | OP | AC |I| X | Y | |_________|____|_|____|__________________| In the diagram the field names are: Instruction Set Page 8 - OP = operation code - AC = accumulator field - I = indirect bit - X = index field - Y = address field Some example intructions are: move 1, @100 ; MOVE is the OP. AC is 1. ; @ sets the I bit. ; X is zero, Y is 100. hrrz 17, 1(3) ; HRRZ is the OP. AC is 17, ; Y = 1, X = 3, I = 0 sos foo ; SOS is OP, FOO is symbolic ; for the Y field. AC, X, I ; are 0. All instructions without exception calculate an "effective address". The effective address may itself be used as data or it may be used to address the data or result word for a particular instruction. The effective address computation is described by the following program. MA means memory address. means program counter. C(MA) means contents of the word addressed by MA. Effective Address Calculation: IFETCH: MA := PC OP := Bits 0:8 of C(MA); AC := Bits 9:12 of C(MA); EACOMP: I := Bit 13 of C(MA); X := Bits 14:17 of C(MA); Y := Bits 18:35 of C(MA); E := Y; IF X <> 0 then E := Bits 18:35 of E+C(X); IF I=0 then go to DONE; MA := E; go to EACOMP DONE: The effective address is an 18 bit quantity. If the I and X fields of the instruction are zero then the effective address is simply the address (Y) field of the instruction. If X isn't zero, then the contents of the word addressed by X (i.e., the contents a register serving as the index register for this instruction) are added to the contents of the Y field (the sum is truncated to 18 bits). This sum serves as the effective address, unless the indirect (I) bit is set. If the I bit is set, a word is read from the address specified by X and Y, and the I, X, and Y fields of that new word are used for repeating the effective address calculation. Note that this calculation will loop until a word is read in which the I field is zero. Instruction Set Page 9 The result of the effective address calculation may be thought of as an instruction word where bits 0:12 are copied from the original instruction, bits 13:17 are zero, and 18:35 contain the effective address. 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 instructions affect some accumulator. These registers are actually implemented in high speed solid state memory rather than in slower main memory. 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 instruction set easy to learn. 2.2. Full Word Instructions 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. 2.2.1. MOVE 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. Instruction Set Page 10 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) 2.2.2. EXCH - Exchange EXCH exchanges the contents of the selected ac with the contents of the effective address. EXCH C(AC):=:C(E) 2.2.3. BLT - Block Transfer The 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. 2.2.4. Programming Examples Using Fullword Instructions In these examples, several standard PDP-10 assembly languange notations are used: [] Square brackets enclose a "literal". The contents of the brackets are assembled in another place, and the bracketed expression is replaced by the address of that place. Instruction Set Page 11 <> Angle brackets enclose an expression. ,, Separates left- and right-half quantities in a word. In , a is right-adjusted in bits 0:17, and b is right- adjusted in bits 18:35. If either quantity is too big, it is truncated on the left. () Parentheses enclose an expression which denotes an index register. . (pronounced "dot") Denotes the current location. ; Save all the accumulators: movem 17, savac+17 movei 17, savac ; Source is 0, destination blt 17, savac+16 ; is SAVAC. ; Restore all the accumulators: movsi 17, savac ; Source is SAVAC, blt 17, 17 ; destination is 0. ; Zero 100 words starting at TABLE. setzm table move t1, [table,,table+1] ; Source and blt t1, table+77 ; destination overlap ; 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 t1, [400076,,table+76] pop t1, 1(t1) ; Store TABLE+76 into jumpl t1, .-1 ; table+77, etc. 2.3. 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. 2.3.1. PUSH - Push on Stack 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 Instruction Set Page 12 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. 2.3.2. POP - Pop Stack 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 for the stack. 2.3.3. ADJSP - Adjust Stack Pointer ADJSP CL(AC) := CL(AC)+E; CR(AC) := CR(AC)+E E is added algebraically, with bit 18 acting as the sign bit, to both halves of AC. If a negative E changes the count in AC left from positive or zero to negative, or a positive E changes the count from negative to positive or zero, set trap 2. 2.4. 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. Instruction Set Page 13 H halfword from |R right of source |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. 2.4.1. HR - Halfword Right 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 Instruction Set Page 14 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) 2.4.2. HL Halfword Left 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 Instruction Set Page 15 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) 2.5. Arithmetic Testing 2.5.1. AOBJ - Add One to Both Halves and Jump The AOBJx (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. 2.5.2. JUMP 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 Instruction Set Page 16 2.5.3. SKIP 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); don't skip 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 2.5.4. AOS - Add One and 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 2.5.5. SOS - Subtract One and 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). Instruction Set Page 17 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); 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 2.5.6. AOJ - Add One and Jump 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 2.5.7. SOJ - Subtract One and Jump 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 Instruction Set Page 18 2.5.8. CAM - Compare Accumulator to Memory 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 2.5.9. CAI - Compare Accumulator Immediate 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 Skipping instructions can be combined to achieve ANDing or ORing of logical expressions, e.g. the sequence cail t1, 1 caile t1, 1000 jrst bad is equivalent to if C(t1) < 1 or C(t1) > 1000 then go to BAD Instruction Set Page 19 2.6. Fixed Point Arithmetic In positive numbers bit 0 is zero. Bit 1 is most significant; bit 35 is least significant. Negative numbers are the two's complement of positive numbers. Results (of ADD, SUB or IMUL) outside the range -2^35 to 2^35-1 will set overflow ( PC bit 0). 2.6.1. ADD 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) 2.6.2. SUB - Subtract 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) 2.6.3. IMUL - Single-Word Multiply 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) 2.6.4. IDIV - Single-Word Divide 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) Instruction Set Page 20 2.6.5. MUL - Multiply 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) 2.6.6. DIV - Divide 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) 2.7. 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. 2.8. 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. Instruction Set Page 21 The format for a double word integer is the same as that produced by MUL, i.e., a 70 bit integer in two's 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 2.9. Floating Point Arithmetic Single precision floating point numbers are represented in one 36 bit word as follows: Bit 0 00000000 011111111112222222222333333 Position 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 two's complement format. The fraction is interpreted as having a binary point between bits 8 and 9. The exponent is a power 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 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, but 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 point is somewhat different. Consult an wizard. Instruction Set Page 22 F floating |AD add | result to AC |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 . 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. 2.10. Other Floating Point Instructions 2.10.1. FSC - Floating Scale 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. 2.10.2. FIX - Convert Floating Point to Integer 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 exponent 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. Instruction Set Page 23 2.10.3. FIXR - Fix and Round FIXR (Fix and round) will convert a 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. 2.10.4. FLTR - Float and Round 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. 2.11. Shift Instructions The following instructions shift or rotate the AC or the 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. Instruction Set Page 24 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. 2.12. 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: Bit 000000 000011 1 1 1111 112222222222333333 Position 012345 678901 2 3 4567 890123456789012345 _________________________________________ | | | | | | | | POS | SIZE |U|I| X | Y | |______|______|_|_|____|__________________| - POS is the byte position: the number of bits remaining in the word to the right of the byte. - SIZE is the byte size in bits. - The U field is reserved for future use and must be zero. - I, X, and Y are the same as in an instruction. 2.12.1. 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. 2.12.2. 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. Instruction Set Page 25 2.12.3. 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, skipping over any bits that may be left over at the end of a word (when the bytesize does not divide evenly into the wordsize, 36). 2.12.4. ILDB - Increment and Load Byte Increment the byte pointer contained at the effective address. Then perform a LDB function using the updated byte pointer. 2.12.5. IDPB - Increment and Deposit Byte Increment the byte pointer contained at the effective address. Then perform a DPB function using the updated byte pointer. 2.12.6. ADJBP - Adjust Byte Pointer Fetch the byte pointer at the effective address, increment or decrement it by the number of bytes specified in the AC, then place the adjusted byte pointer in the AC. The original byte pointer is unchanged. 2.12.7. POINT - Construct a Byte Pointer For convenience, the Macro assembler (and Fail) has a pseudo op for creating byte pointers. The POINT pseudo op has three parameters: size, 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 POINT 7, FOO 440700,,foo Instruction Set Page 26 2.13. 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. Test accumulator |R right half immediate |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 . 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 are one in the mask are made zero in the AC. - If the modification code O appears then bits that are one in the mask are made one in the AC. - If the modification code C appears then bits that are one in the 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. 2.14. 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. Instruction Set Page 27 Table of the Boolean functions 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. 2.15. PC Format JSR, JSP, and PUSHJ all store a full word that contains the PC (Program Counter) 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. Instruction Set Page 28 - 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: 1. An ADDx has added two negative numbers with sum less than -2^35. 2. A SUBx has subtracted a positive number from a negative number and produced a result less than -2^35. 3. 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: 1. 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. 2. 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 positive. 3. An AOJx or AOSx has incremented -1. 4. SOJx or SOS has decremented a nonzero number other than -2^35. 5. A MOVNx has negated zero. - CRY1, CaRrY 1, if set without CRY0 being set will set AROV. This indicates any of the following conditions: 1. An ADDx has added two positive number with a sum greater than 2^35-1. 2. A SUBx has subtracted a negative number from a positive number to form a difference greater than 2^35-1. 3. An AOSx or AOJx instruction has incremented 2^35-1. 4. A MOVNx or MOVMx has negated -2^35. 5. A DMOVNx has negated -2^70 The following conditions are also indicated in the PC word: - FOV, Floating point OVerflow, is set by any of: Instruction Set Page 29 1. In a floating point instruction other than FLTR, DMOVNx, or DFN the exponent of the result exceeds 127. 2. FXU (Floating eXponent Underflow) has been set. 3. 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 the 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: 1. In a DIVx the high order word of the dividend was greater than or equal to the divisor. 2. In an IDIVx the divisor was zero. 3. 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 Instruction Set Page 30 the instruction that stores the PC. Thus, the PC word points at the instruction immediately following the subroutine call. 2.16. Program Control 2.16.1. JSR - Jump to Subroutine JSR C(E):=; 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) 2.16.2. JSP - Jump & Save PC JSP C(AC):=; PC:=E JSP, Jump and Save PC, stores the PC and flags in the selected accumulator and jumps. 2.16.3. JSA - Jump and Save Accumulator JSA C(E):=C(AC); C(AC):=; 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 not reentrant, and it doesn't save flags. 2.16.4. JRA - Jump and Restore Accumulator 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). Instruction Set Page 31 2.16.5. PUSHJ - Push on stack and Jump PUSHJ C(AC):=C(AC)+<1,,1>; C(CR(AC)):=; 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. 2.16.6. POPJ - Pop stack and Jump 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. 2.16.7. Programming Hints Using PUSHJ and POPJ 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 nonzero. 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, ; Restore flags and PC from ; old stack top. 2.16.8. JRST - Jump and Restore 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 Instruction Set Page 32 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). JRST 4, JRST 10, and JRST 12 are illegal in user mode and are trapped as UUOs. 2.16.9. JFCL - Jump on Flag and Clear The JFCL 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. 2.16.10. XCT - Execute 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 executed instruction skips, then that skip is relative to the XCT. The AC field of the XCT should be zero. [In monitor mode a nonzero AC field in an XCT is significant.] The XCT instruction can be used to acheive the effect of a CASE statement, as in the following example: xct @[ call foo move q1, p1 jfcl aos q1 ](t1) where t1 contains the case index, which should have a value (in this case) between 0 and 3. Instruction Set Page 33 2.16.11. JFFO - Jump if Find First One JFFO 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. 2.17. References The following manual presents the instruction set of the PDP-10/DECSYSTEM-20 in complete detail: DECsystem-10/DECSYSTEM-20 Hardware Reference Manual, Volume I, Central Processor, EK-10/20-HR-001, Digital Equipment Corporation (1978). Historical information can be found in: Bell, et al., "The Evolution of the DECsystem-10", CACM Jan 1978. Macro Page 34 3. The DECSYSTEM-20 Macro Assembler 3.1. Introduction This chapter presents excerpts from the DEC Macro-20 Reference Manual. Certain advanced (or rarely used) features - mainly those dealing with storage allocation and polish fixups - have been omitted; see the actual manual if you must have them. Unfortunately, the Macro manual does not present the material in a tutorial fashion; this rendition of it is not much better than the original in that respect, although examples have been added here and there, and explanations are a little more thorough. Given the absence of a good tutorial, the best approach to teaching yourself Macro is to read through this chapter from beginning to end to get an idea of what kinds of things Macro does, and what its syntax is, and then to study some well-written Macro programs. At the time of this writing (3 July 1980), Macro suffers from certain limitations traceable to its origins in Tops-10 (PDP-6, really): symbols are limited to 6 characters in length, and input (.MAC) and output (.REL, usually) files are limited to 6 characters in the filename and 3 in the extension (file type). Macro is the symbolic assembler program for the DECSYSTEM-20. The assembler reads a file of Macro statements and composes relocatable binary machine instruction code suitable for loading by Link, the system's linking loader. Macro is a statement-oriented language; statements are in free format and are processed in two passes. In processing statements, the assembler: 1. Interprets machine instruction mnemonics. 2. Accepts symbol definitions. 3. Interprets symbols. 4. Interprets pseudo-ops. 5. Accepts macro definitions. 6. Expands macros on call. 7. Assigns memory addresses. 8. Generates a relocatable binary program file ( .REL file) for input to Link. 9. Optionally generates a program listing file showing source statements, the corresponding binary code, and any errors found. 10. Optionally generates a universal ( .UNV) file that can be searched during other assemblies. The following conventions are used throughout this chapter: 1. All numbers in the examples are octal unless otherwise noted. Macro Page 35 2. All numbers in the text are decimal unless otherwise noted. 3. The name of the assembler, Macro, is capitalized; references to user-defined macros are in lower case. 3.2. Elements of Macro The character set recognized in Macro statements includes all ASCII alphanumeric characters and 28 special characters (ASCII 40 through 137 (octal)). Lowercase letters are not distinguished from uppercase letters. Macro recognizes several ASCII control codes including horizontal tab (^I), linefeed (^J), formfeed (^L), carriage-return (^M), and control-underscore (^_). Macro accepts any ASCII character in quoted text, or in text argument to the ASCII and ASCIZ pseudo-ops. The line continuation character, ^_, is always effective. Delimiters for certain pseudo-ops (such as ASCII, ASCIZ, and COMMENT) can be any nonblank, nontab ASCII character. A Macro program consists of statements made up of Macro language elements. Separated into general types, these are: 1. Special characters. 2. Numbers. 3. Literals. 4. Symbols. 5. Expressions. 6. Macro-defined mnemonics. 7. Pseudo-ops. 8. Macros. The format of a Macro statement is discussed later. 3.2.1. Special Characters Some characters and combinations of characters have special interpretations. These interpretations apply only in the contexts described. In particular, they do not apply within comment fields or text strings. Uparrow (^) is to be taken literally, e.g. ^B means the uparrow character followed by a B, not control-B. Char(s) (Form) Context and Interpretation B (mBn) Between 2 integer expressions, causes the binary representation of m to be placed with rightmost bit at bit n (decimal). ^B (^Bn) Before an integer expression, shows that n is a binary Macro Page 36 (base 2) number. ^D (^Dn) Before an integer expression, shows that n is a decimal number. E (fE+n,fE-n,fEn) Between a floating-point decimal number and a signed decimal integer, multiplies f by the +nth power of 10 (decimal). ^O (^On) Before an integer expression, shows that n is an octal number. : (sym:) After a symbol, shows that sym is a label, i.e. that its value is to become the current value of the location counter. :: (sym::) After a symbol, shows that sym is a global INTERNAL label. ; (;text) Before the end of a line, shows that text is a comment. ;; (;;text) Before end of line (usually in a macro definition), shows that text is a comment to be printed in the macro definition but not at call, i.e. in macro expansion. . (.) As an expression, is replaced by the current value of the location counter. , (,) Among numbers and symbols, delimits operands, accumulator, arguments. In a macro call, delimits a null argument. ,, (lhw,,rhw) Between two expressions, delimits left halfword from right halfword. ! (A!B) Between two expressions, generates the logical inclusive OR of A and B. ^! (A^!B) Between two expressions, generates the logical exclusive OR of A and B. & (A&B) Between two expressions, generates the logical AND of A and B. ^- (^-A) Before an expression, generates the logical complement of A (NOT A). * (A*B) Between two expressions, generates the product of A and B. / (A/B) Between two expressions, generates the quotient of A by B. + (A+B) Between two expressions, generates the sum of A and B. - (A-B) Between two expressions, generates the difference of A and B. - (-A) Before an expression, generates the two's complement of the value of A. " ("text") In pairs around text, shows that text is a 7-bit ASCII string, to be right justified in a field of five characters. ' ('text') In pairs around text, shows that text is a SIXBIT string, to be right justified in a field of six characters. Macro Page 37 ' (arg'text, text'arg) Adjoing a dummy argument in the body of a macro definition, concatenates the value of the argument to the text during macro expansion. \ (\expr) Prefixed to an expression in a macro call, directs that the argument passed be the string for the ASCII value of expr in the current radix. \' (\'expr) Prefixed to an expression in a macro call, directs that the argument passed be the string whose SIXBIT code is the value of expr. \" (\"expr) Prefixed to an expression in a macro call, directs that the argument passed be the string whose ASCII code is the value of expr. ^_ (Control-underscore, not uparrow underscore) before a CRLF, continues its argument to the next line. Does not operate across end-of-macro. _ (A_B) Between two expressions, shifts the binary representation of A to the left B positions (if B is negative, shift is to the right). @ (@address) Prefixed to an address, sets bit 13 of the instruction word, indicating indirect addressing. % (%arg) As the first character of a dummy argument in a macro definition, directs that %arg be replaced by a created symbol during macro expansion; Macro will substitute a different symbol for it on each invocation of the macro. () Encloses index field, encloses dummy arguments in macro definition or parameters in a macro invocation, quotes characters for macro argument handling, swaps the two halves of a value. <> Nests expressions, encloses conditional assembly code, encloses code in REPEAT, IRP, and IRPC pseudo-ops, encloses macro body in DEFINE pseudo-op, quotes characters for macro argument handling, forces evaluation of a symbol. [] delimits literals (causing the contents of the brackets to be moved to the literal pool, and the bracketed expression to be replaced by the location of its contents); delimits argument in ARRAY, .COMMON, and OPDEF pseudo-ops; quotes characters for macro argument handling. = (sym=expr) Between symbol and expression, assigns the value of expr to sym. =: (sym=:) Between symbol and expression, assigns the value of expr to sym and declares sym to be global INTERNAL. Macro Page 38 3.2.2. Numbers The two properties of numbers that are important in Macro are 1. The radix (base) in which the number is written. 2. How the number should be placed in memory. You can control the interpretation of these properties by using pseudo-ops or special characters to indicate your choices. 3.2.2.1. Integers Macro stores an integer in its binary form, right justified in bits 1 to 35 of its storage word. If you use a sign, place it immediately before the integer (if you omit the sign, the integer is assumed to be positive). For a negative number, Macro first forms its absolute value in bits 1 to 35, then replaces it by its two's complement. Therefore a positive integer is stored with 0 in bit 0, while a negative integer has 1 in bit 0. The largest positive integer that Macro can store is 377777 777777 (octal); the smallest (most negative) is 400000 000000 (octal). 3.2.2.2. Radix The initial implicit radix for a Macro program is octal (base 8). The integers you use in your program will be interpreted as octal unless you indicate otherwise. You can change the radix to any base from 2 to 10 by using the RADIX pseudo-op. The new radix will be in effect until you change it again. Without changing the prevailing radix, you can write a particular integer in binary, octal, or decimal. To do this, prefix the integer with ^B for binary, ^O for octal, ^D for decimal. The indicated radix applies only to the single integer immediately following it. A single-digit number is always interpreted as radix 10. Thus 9 is seen as decimal 9, even if the current radix is 8. For example, suppose the current radix is 8. Then you can write the decimal number 23 as: 27 octal (current radix) ^d23 decimal ^b10111 binary but you cannot write decimal 23 as ^d45-22 since the ^d applies only to the first number, 45; the 22 is still interpreted as octal. However, you can write decimal 23 as ^d<45-22>. Macro Page 39 3.2.2.3. Floating-point Decimal Numbers In your program, a floating-point decimal number is a string of digits with a leading, trailing, or embedded decimal point and an optional leading sign. Macro recognizes this as a mixed number in radix 10. Macro forms a floating-point decimal number with the sign in bit 0, a binary exponent in bits 1 to 8, and a normalized binary fraction in bits 9 to 35. The normalized fraction can be viewed as followed: its numerator is the binary number in bits 9 to 35, whose value is less than 2 to the 28th power, but greater than or equal to 2 to the 27th power. Its denominator is 2 to the 28th power, so that the value of the fraction is always less than 1, but greater than or equal to 0. (This value is 0 only if the entire stored number is 0.) The binary exponent is incremented by 128 so that exponents from -128 to 127 are represented as 0 to 255. For a negative floating-point number, Macro first forms its absolute value as a positive number, then takes the two's complement for the entire word. Examples: The floating point number 17.0 generates the binary word 0 10 000 101 100 010 000 000 000 000 000 000 000 where bit 0 shows the positive sign, bits 1 to 8 show the binary exponent, and bits 9 to 35 show the proper binary fraction. The binary exponent is 133 (decimal), which after subtracting the added 128 gives 5. The fraction is equal to 0.53125 decimal. And 0.53125 times 2 to the 5th power is 17, which is the number given. Similarly, 153. generates 0 10 001 000 100 110 010 000 000 000 000 000 000 while -153. generates 1 01 110 111 011 001 110 000 000 000 000 000 000 These two examples show that a negative number is two's complemented. Notice that since the binary fraction for a negative number always has some nonzero bits, the exponent field (taken by itself) appears to be one's complemented. As in Fortran, you can write a floating point decimal number with a suffixed E+/-n, and the number will be multiplied by 10 to the +/-nth power. If the sign is missing, n is assumed to be positive. Examples: 2840000. can be written 284.E+4 or .284E7; .0000284 can be written .284E-4 or 284.E-7. Using this E notation with an integer (no decimal point) is not allowed, and causes an error. Therefore you can use 284.E4 but not 284E4. Note: Macro's algorithm for handling numbers given with the E notation is not identical for Fortran's. The binary values generated by the two translators may differ in the lowest order bits. Macro Page 40 3.2.2.4. Binary Shifting Binary shifting of a number with Bn sets the location of the rightmost bit at bit n in the storage word, where n is a decimal integer. The shift takes place after the binary number is formed. Any bits shifted outside the range (bits 0 through 35) of the storage word are lost. For example, here are some numbers with their binary representations given in octal: 300000 000000 ^d3b2 000000 042000 ^d17b25 000001 000000 1b17 777777 777777 -1b35 000000 000001 1b35 000000 777777 -1b17 3.2.2.5. Underscore Shifting You can also shift a number by using the underscore operator. If V is an expression with value n, suffixing _V to a number shifts it n bits to the left. If n is negative, the shift is to the right. In an expression of the form W_V, W and V can be any expressions including symbols. The binary value of W is formed in a register, V is evaluated, and the binary value of W is shifted V bits when placed in storage. An expression such as -3.75E4_^d18 is legal, but the shift occurs after conversion to floating point decimal storage format. Therefore the sign, exponent, and fraction fields are all shifted away from their usual locations. This is true also of other storage formats. 3.2.3. Literals A literal is a character string within square brackets inserted in your source code. Macro stores the code generated by the enclosed string in a literal pool beginning with the first available literal storage location, and places the address of this location in place of the literal. The literal pool is normally at the end of the binary program. The statements Macro Page 41 ldb t1, [point 6, .JBVER, 17] LIT are equivalent to ldb t1, foo foo: point 6, .JBVER, 17 A literal can also be used to generate a constant: push p, [0] ; Generate a zero fullword. move q1, [3,,14] ; Generate a word with 3 in ; the left half and 14 in the right. Multiline literals are also allowed: getChr: ildb t2, t1 ; Get a character. cain t2, 0 ; Is it a null? jrst [ move t1, txtPtr ; Yes, use this pointer ildb t2, t1 ; to get a new character. cain t2, "?" ; Is it a question mark? jrst [ move t1, txtPt2 ; Yes, use this pointer ildb t2, t1 ; to get the message character, jrst getHlp ] ; and go to the help routine. ret ] ; Not a question mark, return from getChr. ret ; Not a null, return with char in t2. The text of a literal continues until a matching closing square bracket is found (unquoted and not in a comment field). A literal can include any term, symbol, expression, or statement, but it must generate at least one but no more than 99 words of data. A statement that does not generate data (such as a direct assignment statement or a RADIX pseudo-op) can be included in a literal, but the literal must not consist entirely of such statements. You can nest literals up to 18 levels. You can include any number of labels in a literal, but a forward reference to a label in a literal is illegal. If you use a dot (.) in a literal to retrieve the location counter, remember that the location counter is pointing at the statement containing the literal, not at the literal itself. In nested literals, a dot location counter references a statement outside the the outermost literal. In the sequence jrst [ hrrz t1, v caie t1, op jrst .+1 jrst foo ] skipe t3 the expression .+1 generates the address of skipe t3, not jrst foo. Macro Page 42 Literals having the same value are collapsed in Macro's literal pool. Thus for the statements push p,[0] caml t2,[0] movei t1, [asciz /frotz/] the same address is shared by the two literals [0], and by the null word generated at the end of [asciz /frotz/]. Literal collapsing is suppressed for those literals that contain errors, undefined expressions, or EXTERNAL symbols. 3.2.4. Symbols Macro symbols include: 1. Macro-defined pseudo-ops. 2. Macro-defined mnemonics. 3. User-defined macros. 4. User-defined opdefs. 5. User-defined labels. 6. Direct-assignment symbols. 7. Dummy arguments for macros. Macro stores symbols in three symbol tables: one for opcodes and pseudo-ops, one for macros and opdefs, and one for user defined labels and direct-assignment symbols. An entry in one of these tables shows the symbol, its type, and its value. Symbols are helpful in your program because: 1. Defining a symbol as a label gives a name to an address. You can use the label in debugging or as a target for program control statements. 2. In revising a program, you can change a value throughout your program by changing a single symbol definition. 3. You can give names to values to make computations clearer. 4. You can make values available to other programs. Macro Page 43 3.2.4.1. Selecting Valid Symbols Follow these rules in selecting symbols: 1. Use only letters, numerals, dots (.), dollar signs ($), and percent signs (%). Macro will consider any other character (including blank) as a delimiter. 2. Do not begin a symbol with a numeral. 3. If you use a dot for the first character, do not use a numeral for the second. Do not use dots for the first two characters; doing so can interfere with Macro's created symbols. 4. Make the first six characters unique among your symbols. You can use more than six characters, but Macro will use only the first six. 5. Don't choose symbols composed of 1 to 4 letters followed by a percent sign; names of this form are reserved for new monitor calls. Examples: velocity Legal, only "veloci" is heeded by Macro. chg.vel Legal, only "chg.ve" is heeded by Macro. chg vel Illegal, looks like two symbols to Macro. chgVel Legal. 1stNum Illegal, begins with numeral. .11111 Illegal, begins with dot-numeral. ..1111 Unwise, could interfere with created symbols. 3.2.4.2. Defining Symbols You can define a symbol by making it a label or by giving its value in a direct-assignment statement. Labels cannot be redefined, but direct-assignment symbols can be redefined anywhere in your program. You can also define special-purpose symbols called OPDEFs and macros using the pseudo-ops OPDEF and DEFINE. A label is always a symbol with a suffixed colon. A label is in the first (leftmost) field of a Macro statement and is one of the forms: errFound: Macro uses only "errfou". case1: Legal label. OK:contin: Legal; you can use more than one label at a location. Macro Page 44 case2:: Double colon declares the label to be global INTERNAL. When Macro processes the label, the symbol and the current value of the location counter are entered in the user symbol table. A reference to the symbol addresses the code at the label. You cannot redefine a label to have a value different from its original value. A label is relocatable if the address is represents is relocatable; otherwise it is absolute. You can define a direct-assignment symbol by associating it with an expression. A direct assignment is in one of the forms: symbol=expression - The symbol and the value of the expression are entered together in the user symbol table. symbol=:expression - The symbol and the value of the expression are entered together in the user symbol table and the symbol is declared INTERNAL. You can redefine a direct-assignment symbol at any time; the new direct assignment simply replaces the old definition. Note: If you assign a multiword value using direct assignment, only the first word of the value is assigned to the symbol. For example, A=asciz/abcdefgh/ is equivalent to A=asciz/abcde/, since only the first five characters in the string correspond to code in the first word. 3.2.4.3. Symbol-table Search Order When you use a symbol in your program, Macro looks it up in the symbol tables. Normal Macro searches the macro table first, then the opcode table, and finally the user symbol table. However, if Macro has already found an operator in the current statement and is expecting operands, then it searches the user symbol table first. 3.2.4.4. Symbol Attributes Each symbol in your program has one of the following attributes: local, INTERNAL global, or EXTERNAL global. This attribute is determined when the symbol is defined. A local symbol is defined for the use of the current program only. You can define the same symbol to have different values in separately assembled programs. A symbol is local unless you indicate otherwise. A global symbol is defined in one program, but is also available for use in other programs. Its table entry is visible to all programs in which the symbol is declared global. A global symbol must be declared INTERNAL in the progam where it is defined; it can be defined in only one program. In other programs sharing the global symbol, it must be declared EXTERNAL; it can be Macro Page 45 EXTERNAL in any number of programs. To declare a symbol as INTERNAL global, you can: 1. Use the INTERN pseudo-op, e.g. INTERN flag1 2. Insert a colon after the "=" in a direct assignment statement, e.g. flag2=:200. 3. Use an extra colon with a label, e.g. flag3::. 4. For subroutine entry points, use the ENTRY pseudo-op, e.g. ENTRY foo. To declare a symbol as an EXTERNAL global, you can use the EXTERN pseudo-op, e.g. EXTERN flag4. 3.2.5. Expressions You can combine numbers and defined symbols with arithmetic and logical operators to form expressions. You can nest expressions by using angle brackets. Macro evaluates each expression (innermost nesting levels first), and either resolves it to a fullword value, or generates a Polish expression to pass to Link. 3.2.5.1. Arithmetic Expressions An arithmetic expression can include any number or defined symbol, and any of the following operators +, -, *, /. Examples (in which words, x, y, and z have been defined elsewhere): movei t3, words/5 addi q2, addi q2, <+1>*5 3.2.5.2. Logical Expressions A logical expression can include any number or defined symbol whose value is absolute, and any of the operators &, !, ^!, ^-. Each of the binary operations &, !, and ^! generates a fullword by performing the indicated operation over corresponding bits of the two operands. For example, a&b generates a fullword whose bit 0 is the result of a's bit 0 ANDed with b's bit 0, and so forth for all 36 bits. Macro Page 46 3.2.5.3. Evaluating Expressions Macro has a hierarchy of operations in evaluating expressions. In an expression without nests (angle brackets), Macro performs its operations in this effective order: 1. All unary operations and shifts: +, -, ^-, ^d, ^o, ^b, b (binary shift), _ (underscore shift), E. 2. Logical binary operations (from left to right): !, ^!, &. 3. Multiplication and division (from left to right): *, /. 4. Addition and subtraction (binary operations): +, -. You can override this hierarchy by using angle brackets to show what you want done first. For example, suppose you want to calculate the sum of a and b, divided by c. You cannot do this with a+b/c because Macro will perform the division b/c first, then add the result to a. With angle brackets you can write the expresssion /c to force Macro to add a and b first, then divide the result by c. Expressions can be nested to any level. The innermost nest is evaluated first; the outermost last. Some examples of legal expressions (assuming that a1, b1, and c are defined symbols) are: a1+b1/5 /5 ^-a1&b1^!c ^b101b17-^d98+6 An expression given in halfword notation has each half evaluated separately in a 36-bit register. Then the 18 low-order bits of each half are joined to form a fullword. For example, the expression <4,,6>/2 generates the value 000002 000003. Macro Page 47 3.3. Pseudo-ops A pseudo-op is a statement that gives the assembler information to allow it to assemble your program in the desired way. For example, the pseudo-op RADIX does not generate code itself, but tells Macro how to interpret numbers in your program. The pseudo-op EXP generates one word of code for each argument given with it. To use a pseudo-op in your program, follow it with a space or tab, and any required or optional arguments or parameters. This section describes the use and functions of each pseudo-op. The headings for each description, if applicable, are Format, Function, Examples, and Optional Notations. 3.3.1. ARRAY Format ARRAY sym[expression] Expression is an integer value (to be intterpreted in the current radix, indicating the number of words to be allocated; the expression cannot be EXTERNAL, relocatable, or negative. Note that although the expression must be in square brackets, this use of the brackets is completely unrelated to literals. Function Reserves a block of storage whose length is the value of the expression, and whose location is identified by the symbol. Storage is allocated along with other variable symbols in the program, usually at the end. The allocated storage is not necessarily zeroed. 3.3.2. ASCII Format ASCII dtextd where d = delimiter; first nonblank character, whose second appearance terminates the text, and text = a string of text characters to be entered. Function Enters ASCII text in the binary code. Each character uses seven bits. Characters are left justified in storage, five per word, with bit 35 in each word set to 0, and any unused bits in the last word set to 0. Examples ASCII /This is a string/ ascii !ps:foo.txt! Ascii?foo? Optional Notations: Omit the space or tab after ASCII. Not allowed if the delimiter is a letter, number, dot, dollar or percent sign (i.e. a possible symbol constituent), or if the delimiter character is a control character. Macro Page 48 Right-justified ASCII can be entered by using double quotes to surround up to five characters, as in: movei t1, "A". 3.3.3. ASCIZ Exactly like ASCII, except that a trailing null is guaranteed, even if an extra word of nulls must be added. Most Tops-20 monitor calls expect strings to be in this format. 3.3.4. BLOCK Format BLOCK expression where the expression is not EXTERNAL or relocatable, and evaluates to a positive integer. Function Reserves, at the point of invocation, a block of locations whose length is the value of the expression. The location counter is incremented by this value. The allocated locations are not necessarily zeroed. Note that the BLOCK pseudo-op does not generate or store code; therefore it should not be used in a literal, since this will result in overwriting the reserved space during literal pooling. Examples BLOCK 500 block <^d512/5+1> block <$nPag_9 - fooLen> Optional Notations: Use the pseudo-op Z inside literals. 3.3.5. BYTE Format BYTE bytedef ... bytedef bytedef = (n)expression, ..., expression n = byte size in bits; n is a decimal expression in the range 1 to 36. Function Stores values of expressions in n-bit bytes, starting at bit 0 of the storage word. The first value is stored in bits 0 to n-1; the second in bits n to 2n-1, and so forth for each given value. If a byte will not fit in the remaining bits of a word, those bits are zeroed and the byte begins in bit 0 of the next word. If a value is too large for a byte, it is truncated on the left. If the byte size is 0 or is missing (empty parentheses), a zero word is generated. Examples Macro Page 49 V=2 BYTE (6),5,0,,101,5,V generates the storage value 050000 010502. The two commas indicate a null argument; the 101 (octal) is too large for the byte size and is left truncated. byte (6)7,0,1(9)7,0,1,"A" generates two words: 070001 007000 and 001101 000000. Notice that "A" is right-justified in its 9-bit byte. Note that a comma before a left parenthesis will generate a null byte. 3.3.6. COMMENT Format COMMENT dtextd d = delimiter; the first nonblank character, whose second appearance terminates the text. Function Treats the text between the delimiters as a comment. The text can include carriage returns to facilitate multiline comments. Optional Notations: Omit the space or tab after COMMENT. This is not allowed if the delimiter is a valid character for identifiers, or is a control character. Use a semicolon (;) to make the rest of the line into a comment. Be careful not to use the delimiter in the text of the comment, and avoid the use of nonprinting delimiters. Example : foo: comment | Subroutine Foo This subroutine writes a poem in the style of a given poet. Enter with: t1/ Pointer to asciz name of poet. t2/ Destination designator for poem. t3/ Pointer to asciz string describing desired topic. Returns: +1: always, with t1, t2, and t3 updated appropriately. | Macro Page 50 3.3.7. DEC Format DEC expression, ..., expression Function Defines the local radix for the line as decimal, then enters the value of each given expression in a fullword of code. The location counter is incremented by 1 for each expression. Example DEC 10, 938, 512, 4.5, 6.03E-5 Optional Notation: Use the EXP pseudo-op and prefix ^d to each expression that must be evaluated in radix 10. 3.3.8. DEFINE Format DEFINE macroName(dArgList) where - macroName is a symbolic name for the macro being defined. This name must be unique among all macro and OPDEF symbols. - dArgList is a list of dummy arguments. - macroBody is the source code to be assembled when the macro is invoked. Function defines the macro. See the section on macros. 3.3.9. END Format END expression where expression is an optional operand that specifies the address of the first instruction to be executed, which can be EXTERNAL, in the right half and, optionally, the length of the entry vectory in the left. Function Must be the last statement in a Macro program. Statements after END are ignored. The starting address is optional and normally is given only in the main program (since subroutines are called from the main program, they should not specify a starting address). When the assembler first encounters an END statement, it terminates pass 1 and begins pass 2. The END terminates pass 2 on the second encounter, and unallocated literals and variables (e.g. ARRAYs) are assembled at the current location. Examples: end end start end <3,,entVec> Macro Page 51 end 3.3.10. ENTRY Format ENTRY symbol, ..., symbol Each symbol is the name of an entry point in a library subroutine. Function Defines each symbol in the list following the ENTRY pseudo-op as an INTERNAL symbol, and puts appropriate information in the .REL file to allow the symbols to be included in an index (such as that constructed by the MAKLIB program). Each symbol must correspond to a label of the same name. Programs referring to these symbols must, of course, declare them EXTERNAL. 3.3.11. EXP Format EXP expression, ..., expression Function Enters the value of each expression in a fullword of code. 3.3.12. EXTERN Format EXTERN symbol, ..., symbol Function Identifies symbols as being defined in other programs. EXTERNAL symbols cannot be defined within the current program. At load time, the value of an EXTERNAL symbol is resolved by Link if you load a module that defines the symbol as an INTERNAL symbol; if you do not load such a module, Link gives an error message for the undefined global symbol(s). 3.3.13. IFx Group Gives criterion and code for conditional assembly. A symbol or expression used to define the conditions for assembly must be defined before Macro reaches the conditional statement. If the value of such a symbol or expression is not the same on both assembly passes, a different number of words of code may be generated (resulting in a phase error). The forms of IF pseudo-op are listed below; in the first six forms, n is the value of the given expression. IFE expression, - assemble code if n=0. IFN expression, - assemble code if n not = 0. IFG expression, - assemble code if n>0. Macro Page 52 IFGE expression, - assemble code if n>=0. IFL expression, - assemble code if n<0. IFLE expression, - assemble code if n<=0. IF1 - assemble code on Pass 1. IF2 - assemble code on Pass 2. IFDEF symbol, - assemble code if the symbol is defined as user-defined, an opcode, or a pseudo-op. IFNDEF symbol, - assemble code if the symbol is not defined as user-defined, an opcode, or a pseudo-op. Code is also assembled if the symbol has been referenced, but is not yet defined. This can occur during pass 1. IFIDN , - assemble code if the strings are identical. IFDIF , - assemble code if the strings are different. IFB , - assemble code if the strings contain only blanks and tabs. IFNB , - assemble code if the string does not contain only blanks and tabs. Example: $cc=$cc+1 ; Count a character. ifg <$cc-5>,< ; Word overflowed? $cc=0 ; Yes, reset character counter $wc=$wc+1 ; and count a word. > Optional notations: Omit angle brackets enclosing code for single-line conditionals. For IFIDN, IFDIF, IFB, and IFNB only: use a nonblank, nontab character other than < as the initial and terminal delimiters for a string (as in the pseudo-ops ASCII and ASCIZ); you can then include angle brackets in the string. 3.3.14. INTERN Format INTERN symbol, ..., symbol Function Declares each given symbol to be INTERNAL global; therefore its definition, which must be in the current program, is available to other programs at load time. Each such symbol must be defined as a label, a variable, or a direct assignment symbol. OPDEF symbols can be declared INTERNAL, and thus be made available to other programs at load time. However, if the current program has another symbol (besides the OPDEF symbol) of the same name, the INTERNAL declaration will apply to that symbol rather than to the OPDEF symbol. Macro Page 53 3.3.15. IOWD Format IOWD exp1,exp2 Function Generations a word in a special format for use by all five pushdown stack instructions (adjsp, push, pop, pushj, popj), as well as for i/o instructions (hence its name). The left half of the assembled word contains the 2's complement of the value of exp1, and the right half contains the value exp2-1. Example: Setting up a pushdown stack. stkLen=100 array stack[stkLen] move p, [iowd stkLen,stack] 3.3.16. IRP Format IRP arg, where arg is one of the dummy arguments of the enclosing macro definition; you can only use IRP in the body of a macro definition. Function Generates one expansion of the code enclosed in angle brackets for each subargument of the string that replaces arg. Each occurrence of arg within the expansion is replaced by the subargument currently controlling the expansion (see the section on macros). Example: define sum(a,b)< movei q, 0 irp a, movem q, b > sum (,foo) This invocation of sum is replaced by: movei q, o add q, x add q, y add q, z movem q, foo Macro Page 54 3.3.17. IRPC Format IRPC arg, Function Generates one expansion of the bracketed code for each character of the string that replaces arg (a dummy argument of the enclosing macro definition). Each occurrence of arg within the expansion is replaced by the character currently controlling the expansion. Concatenation and line continuation are not allowed across end-of-IRPC, since a carriage return and linefeed are appended to each expansion. Example: define deposit (string,bp)< irpc string, < movei q, "string" idpb q, bp > > deposit ,x expands to: movei q, "f" idpb q, x movei q, "o" idpb q, x movei q, "o" idpb q, x 3.3.18. LIT Format LIT Function Assembles literals beginning at the current address. The literals assembled are those found since the previous LIT, or since the beginning of the program, whichever is later. The location counter is incremented by 1 for each word assembled. A literal found after the LIT is not affected. It will be assembled at the next following LIT, or at the END statement, whichever is earlier. Literals having the same value are collapsed in Macro's literal pool. 3.3.19. OCT Format OCT expression, ..., expresssion Function Defines the local radix for the line as octal; the value of each each expression is entered in a fullword of code. The location counter is incremented by 1 for each expression. Equivalent to using EXP with each argument prefixed by ^o. Macro Page 55 3.3.20. OPDEF Format OPDEF symbol[expression] Function Defines the symbol as an operator equivalent to the expression, giving the symbol a fullword value. When the operator is later used with operands, the accumulator fields are added, the indirect bits are ORed, the memory addresses are added, and the index register addresses are added. An OPDEF can be declared INTERNAL, using the INTERN pseudo-op. However, if a symbol of the same name exists, the INTERNAL declaration will apply only to that symbol, and not to the OPDEF. Although the expression portion of an OPDEF must be in square brackets, this use of the brackets is completely unrelated to literals or literal handling. 3.3.21. POINT Format POINT byteSize,address,bitPosition Function Generates a byte pointer word for use with the machine language byte instructions adjbp, ldb, ibp, ildb, and idbp. The byteSize gives the decimal number of bits in the byte, and is assembled in bits 6 to 11 of the storage word. Address gives the location of the word containing the byte, and is assembled in bits 13 through 35 (with normal indirect, index, and offset fields). BitPosition gives the position (in decimal) of the rightmost bit in the byte. Macro places the value <35-bitPosition> in bits 0-5 of the storage word. The default bitPosition is -1, so that the byte increment instructions ipb, ildb, and idpb will operate on the first byte in the address word. 3.3.22. PRGEND Format PRGEND Function Replaces the END statement for all except the last program of a multiprogram assembly. PRGEND closes the local symbol table for the current module. You can use PRGEND to place several small programs into one file to save space and disk accesses. The resulting binary file can be loaded in library search mode (refer to the Link and MakLib manuals for more information about this). PRGEND is not allowed in macros. Like END, PRGEND causes the assembly of all unassembled literals. In a file containing PRGENDs, using more than one LIT pseudo-op in any but the last program produces unpredictable results. A starting address for the program terminated by PRGEND may optionally be given as an argument to PRGEND. Macro Page 56 3.3.23. PRINTX Format PRINTX text Function Causes text to be output at the terminal during assembly, normally once for each pass. Example: define prVal (v,msg) if2, < $$len=.-buffer prVal \$$len, ifg <.-20000>,> > 3.3.24. PURGE Format PURGE symbol, ..., symbol Function Deletes symbols from the symbol tables. Normally used at the end of a program to fix multiply defined global symbol errors that occur at Link time, or to remove unwanted symbols from DDT typeout. If you use the same symbol for both a macro name or OPDEF and a label, a PURGE statement deletes the macro name or OPDEF. Repeating the PURGE statement then purges the label. 3.3.25. RADIX Format RADIX expression Function Sets the radix to the value of the expression, which is interpreted in decimal, and must be in the range 2 to 10. An implicit RADIX 8 statement begins each Macro program. All numerical expressions that follow (up to the next RADIX pseudo-op) are interpreted in the given radix unless another local radix is indicated via ^d, ^o, ^b, OCT, DEC, etc. Ordinarily, numbers outside the range of the given radix are not interpreted. For example, in radix 8, the number 99 causes an error. However, a single-digit number is interpreted in any case. For example, in radix 8, the number 9 is recognized as octal 11. 3.3.26. REPEAT Format REPEAT expression, Function Generates the bracketed code n times, where n is the value of the expression, and must be a nonnegative integer. REPEAT statements can be nested to any level. Line continuation is Macro Page 57 not allowed across end-of-REPEAT, since a carriage return and linefeed are appended to each expansion of the code. Note that REPEAT 0, is logically equivalent to a false conditional (and is often used to "comment out" a large section of code), and REPEAT 1, is logically equivalent to a true conditional. 3.3.27. .REQUIRE Format .REQUIRE filespec Function Causes the specified file to be loaded automatically at Link time. The filespec must not include a file type, and it must be a Tops-10 (not Tops-20!) file specification. 3.3.28. SEARCH Format SEARCH tableName(fileName), ..., tableName(fileName) Function Defines a list of symbol tables for Macro to search if a symbol is not found in the current symbol table. A maximum of ten tables can be specified. Tables are searched in the order specified. When the SEARCH pseudo-op is seen, Macro checks its internal UNIVERSAL tagble for a memory-resident UNIVERSAL of the specified name. If no such entry is found, Macro reads in the symbol table using the given file specification. If no file specification is given, Macro reads tableName.UNV from the connected directory, and on failure tries the same filename in UNV: and SYS:, in that order. When all the specified files are found, Macro builds a table for the search sequence. If Macro cannot find a given symbol in the current symbol table, the UNIVERSAL tables are searched in the order specified. When the symbol is found, it is moved into the current symbol table. This procedure saves time (at the expense of core) on future references to the same symbol. A UNIVERSAL file can search other UNIVERSAL files, provided all names in the search list have been assembled. Optional notation: Omit the filename and its enclosing parentheses. Macro then looks on DSK:, UNV:, and SYS: (in that order) for tableName.UNV. 3.3.29. SIXBIT Format SIXBIT dtextd d = delimiter (first nonblank character, whose second appearance terminates the text) Function Enters strings of text characters in 6-bit format. Six characters per word are left justified in sequential storage words. Any unused bits are set to zero. Lowercase letters in Macro Page 58 SIXBIT text strings are treated as uppercase. Otherwise, only the SIXBIT character set allowed. The SIXBIT character set consists of all the printable ASCII characters except lowercase letters, and the following symbols: `{|}~. The values of the SIXBIT characters are 0-77 (octal); they are offset from the corresponding ASCII values by octal 40, e.g. ASCII "A" is 101, and SIXBIT "A" is 41. Historically, this was a popular format for the storage of strings whose maximum length was 6, such as filenames or program names under Tops-10, because it allowed word comparisons and transfers instead of slower byte manipulations. Optional Notation: Right-justified SIXBIT can be entered by using single quotes to surround uf to six characters, as in move t1, ['FOOBAZ'] 3.3.30. STOPI Ends an IRP or IRPC before all subarguments or characters are used. The current expansion is completed, but no new expansions are started. STOPI can be used with conditionals inside IRP or IRPC to end the repeat if the given condition is met. 3.3.31. SUBTTL Format SUBTTL String Function Defines a subtitle (of up to 80 characters) to be printed at the top of each page of the listing file until the end-of- listing or until another SUBBTL statement is found. The initial SUBTTL usually appears on the second line of the first page of the input file, immediately following the TITLE statement. For subsequent SUBTTL statements, the following rule applies: if the new SUBTTL is on the first line of a new page, then the new subtitle appears on that page; if not, the new subtitle appears on the next page. Even if you do not plan to generate listing files, SUBTTL is still a useful documentation device, and is recognized by certain programming aids. 3.3.32. TITLE Format TITLE String Function Gives the program name and a title to be printed at the top of each page of the program listing. The first characters (up to 6) are the program name; the program is saved by this name unless another is explicitly given in the 'save' command. In addition, the name is used when debugging with DDT to gain access to the program's symbol table. Only one TITLE is allowed per module (file or section of file delimited by Macro Page 59 PRGEND's). The TITLE statement usually appears as the first line of a program. If no TITLE statement is used, the assembler inserts the program name ".MAIN". 3.3.33. XWD Format XWD leftHalf,rightHalf Function Enters two halfwords in a single storage word. Each half is formed in a 36-bit register, and the low-order 18 bits are placed in the halfword. The high-order bits are ignored. Optional Notation: leftHalf,,rightHalf 3.3.34. Z Format Z accumulator, address Function Z is treated as if it were the null machine language mnemonic ( opcode). An instruction word if formed with zeros in bits 0 to 8. The rest of the word is formed from the accumulator an address. If the accumulator and address fields are omitted, a zero word is assembled. 3.4. Macro Statements and Statement Processing A Macro statement has one or more of the following: a label, an opcode, zero or more operands, and a comment. The general form of a macro statement is: label: operator operand, operand ; Comment. A carriage return ends the statement. Direct assignment statements receive special handling. Processing of macros is not discussed in this section because a macro call produces text substitution. After substitution, the text is processed as described in this section. Macros are discussed in their own chapter. 3.4.1. Labels A label is always a symbol with a suffixed colon. The assembler recognizes a label by finding the colon. If a statement has labels (you can use more than one), they must be the first elements in the statement. A label can be defined only once; its value is the address of the first word of code generated after it. Macro Page 60 Since a label gives an address, the label can be either absolute or relocatable. A label is a local symbol by default, but you can declare a label to be INTERNAL globel or EXTERNAL global. 3.4.2. Operators After processing any labels, the assembler views the first group of following nonblank, nontab characters as a possible operator. An operator is one of the following: 1. A mnemonic symbol defined by Macro to stand for a machine opcode (see Chapter 2). 2. A user-defined operator, such as an OPDEF or macro invocation. 3. A Macro pseudo-op. If the characters found do not form one of the above, Macro views them as an expression. An operator is ended by the first non-alphanumeric character that is not a ., $, or %. If it is ended by blank or tab, operands may follow; if it is ended by a semicolon, there are no operands and the comment field begins; if it is ended by a carriage return, the statement ends and there are no operands or comments. 3.4.3. Operands After processing labels and the operator, if any, the assembler views as operands all characters up to the first unquoted semicolon or carriage return. Unquoted commas delimit the operands. The operator in a statement determines the number (none, one, two, or more) and kinds of permitted or required operands. Any expected operand not found is interpreted as null. An operand can be any expression or symbol appropriate for the operator. Examples: loop: move t1, x %print , In the first line, t1 and x are operands. In the second line, a macro, %print, is invoked with two operands. Each of these operands is quoted by enclosing it in angle brackets; this is necessary because each operand contains an internal comma. Macro Page 61 3.4.4. Comments The first unquoted semicolon in a statement begins the comment field. You can use any ASCII characters in a comment; however, angle brackets in a comment may produce unpredictable results (such as unexpected termination of a macro definition or conditional-assembly code) and should be avoided. If the first nonblank, nontab character in a line is a semicolon, the entire line is a comment. You can also enter a full line of comment with the pseudo-op REMARK, or a multiline comment with the pseudo-op COMMENT. Comments do not affect binary program output. 3.4.5. Statement Processing Macro processes your program as a linear stream of data. During Pass 1 of an assembly, Macro may find references to symbols not yet defined in the user symbol table. Whenever a symbol is defined, it is entered in the table with its value, so that on Pass 2 all definitions can be found in the table. The values then replace the symbols in the binary code that is generated. Note: Delayed definition is allowed only for labels and direct- assignment symbols. A symbol that contributes to code generation (for example, an OPDEF, a macro, or a REPEAT index) must be defined before any reference to it. Statement processing proceeds as follows: 1. Labels are found and entered in the user symbol table. 2. The next characters up to the first unquoted semicolon, blank, tab, comma, or equal sign are processed: a. Equal sign: the preceding characters form a symbol, and the following characters form an expression. The symbol and the value of the expression are entered in the user symbol table. b. Other delimiter: the preceding characters form an expression or an operator. If an operator, it is found in a table and assembled. If an expression, its value is assembled. If the operator takes operands, the next characters up to the first unquoted semicolon or carriage return form operands. Unquoted commas delimit operands. For each operand, leading and trailing blanks and tabs are ignored. Operands are evaluated and assembled for the given operator. 3. The first unquoted semicolon ends processing of the line. Any further characters up to the first carriage return are comment. 4. The first unquoted carriage return ends the statement. Any following characters begin a new statement. Macro Page 62 3.4.6. Assigning Addresses Macro normally (and by default) assembles statements with relocatable addresses. Assembly begins with the zero storage word and proceeds sequentially. Each time Macro assembles a word of binary code, it increments its location counter by 1. A mnemonic operator generates one word of binary code. Direct assignment statements and some pseudo-ops do not generate any binary code. Some pseudo-ops generate one or more word of binary code. You can control address assignment by setting the assembler's location counter using the pseudo-ops LOC and RELOC. You can also reference addresses relative to the location counter by using the dot symbol (.). For example, the expression .-1 used as an address refers to the location immediately preceding the current location. Use of this construction is not encouraged (see Chapter 8), because you can cause an incorrect address to be assembled by adding or removing statements withing the range of a .+n expression. Labels should be used except when n = plus or minus 1. 3.4.7. Machine Instruction Mnemonics and Formats An instruction is in one of the following forms: mnemonic accumulator, address mnemonic accumulator, mnemonic address where "mnemonic" evaluates to a machine operation code (opcode), "accumulator" is an accumulator (or register) address, and "address" is a memory address, possibly modified by indexing, indirect addressing, or both. The accumulator address can be any expression whose value is in the range 0 to 17 (octal). The memory address gives a location in memory, and can be any expression or symbol whose value is an integer in the range 0 to octal 777777. You can modify the memory address by indirect addressing, indexed addressing, or both. For indirect addressing, prefix an at-sign (@) to the memory address in your program. For indexed addressing, suffix an index register address in parentheses to the memory address in your program. This address can be any expression or symbol whose value is an integer in the range 1 to octal 17. Note: To assemble the index, Macro places the index register address in a fullword of storage, swaps its halfwords (as it does to any expression in parentheses), and then adds the swapped word to the instruction word. Example (in which t1=1, temp=100, and x=3): add t1, @temp(x) This generates the following binary code: Macro Page 63 instruction indirect code bit memory address 010 111 000 0 001 1 0 011 000 000 000 001 000 000 accumulator index register The mnemonic ADD has the octal code 270, and this is assembled into bits 0 to 8. The accumulator goes into bits 9 to 12. Since the @ appears with the memory address, bit 13 is set to 1. The index register goes into bits 14 to 17. Finally, the memory address is assembled into bits 18 to 35. If any element is missing from a primary instruction, zeros are assembled in its instruction word field. 3.4.8. Mnemonics with Implicit Accumulators A few mnemonics set bits in the accumulator field as well as in the instruction field. Therefore these mnemonics do not take accumulator operands, and are of the form mnemonic address For example, JFOV gives the octal code 25504; JFCL gives 255. They both give the opcode 255 in bits 0 to 8, but JFOV also sets the accumulator (bits 9 to 12) to binary 0001. This makes JFOV 100 equivalent to JFCL 1,100. 3.5. Using Macros A macro is a sequence of statements defined and named in your program. When you invoke a macro (by mentioning its name in your program), the sequence of statements from its definition is generated in place of the invocation, possibly with "arguments" plugged in. By using macros with arguments, you can generate passages of code that are similar, but whose differences are control