The instruction set of the PDP-11 was designed towards a clean, general, symmetric instruction set. It can be used as a register-based, stack-based, or memory-based machine, depending on the programmer's preferences. Interrupt responsiveness is also important, supported with multiple interrupt levels for real-time computing as well as allowing for a separate interrupt handler for each device that generates interrupts.
Word length is 16 bits with the leftmost, most significant bit (MSB) being bit 15. There are eight general registers of 16 bits each. Register 7 is the program counter (PC) and, by convention, Register 6 is the stack pointer (SP). There is also a Processor Status Register/Word (PSW) which indicates the 4 condition code bits (N, Z, V, C), the Trace Trap bit, processor interrupt priority, and 4 bits for current and previous operating modes. Addressing on the -11 is linear from memory address 0 through 177777. Memory management allows access to physical memory with addresses of up to 22 bits (17777777). All I/O devices, registers etc are addressed as if they were part of memory. These live in the 4KW of reserved memory space at the top of the addressing range. Additionally, on most implementations of the PDP-11 architecture, the processor's registers are memory-mapped to the range 17777700-17777717 (there are many control registers beyond just the general registers, the specifics vary between implementations). Thus Register 2 (R2) has an address of 17777702. All word memory addresses are even, except for registers. In byte operations, an even address specifies the least-significant byte and an odd address specifies the most-significant byte. Specifying an odd byte in a word operation will return an odd address trap. Memory addresses from 0 to 400 octal are reserved for various exception traps such as timeouts, reserved instructions, parity, etc., and device interrupts.
Addressing for the Single Operand, Double Operand and Jump instructions is achieved via six bits:
_ _ _ _ _ _ |x|x|x|_|_|_| |Mode |Reg |
where the modes are as follows: (Reg = Register, Def = Deferred)
Mode 0 Reg Direct addressing of the register Mode 1 Reg Def Contents of Reg is the address Mode 2 AutoIncr Contents of Reg is the address, then Reg incremented Mode 3 AutoIncrDef Content of Reg is addr of addr, then Reg Incremented Mode 4 AutoDecr Reg is decremented then contents is address Mode 5 AutoDecrDef Reg is decremented then contents is addr of addr Mode 6 Index Contents of Reg + Following word is address Mode 7 IndexDef Contents of Reg + Following word is addr of addr
Note that the right-most bit of the mode is an indirection bit.
Although not special cases, when dealing with R7 (aka the PC), some of these operations are called different things:
_ _ _ _ _ _ |x|x|x|1|1|1| |Mode | R7 | Mode 2 Immediate Operand follows the instruction Mode 3 Absolute Address of Operand follows the instruction Mode 6 Relative Instr address+4+Next word is Address Mode 7 RelativeDef Instr address+4+Next word is Address of address
Mainstream instructions are broken into Single operand and Double operand instructions, which in turn can be word or byte instructions.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |b|i|i|i|s|s|s|s|s|s|d|d|d|d|d|d| | | | : | : | | | Op | Source | Dest |
Bit 15, b, generally selects between word-sized (b=0) and byte-sized (b=1) operands. In the table below, the mnemonics and names are given in the order b=0/b=1.
The double operand instructions are:
Subtracts the source from the destination, storing the results in the destination.
Note that this is a special case for b=1, in that it does not indicate that byte-wide operands are used.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |b|0|0|0|i|i|i|i|i|i|d|d|d|d|d|d| | | | : | : | | | |Instruction| Dest |
Bit 15, b, generally selects between word-sized (b=0) and byte-sized (b=1) operands. In the table below, the mnemonics and names are given in the order b=0/b=1. Unless otherwise stated, the operand is read for the data to operate on, and the result is then written over that data.
The single operand instructions are:
The branch (b=1) is described in the section on branches, below.
Note that SWAB is actually a bit pattern from the range reserved for branches. This particular pattern is otherwise unused, as it would be a modification of BR, Branch Always, which has no obvious semantics.
SP <<- PC + 2 * nnnnnn PC <- R5 R5 <- (SP)+
Also, for the instruction to work, the stack must be mapped into I-space.
MTPS is only on LSI-11s, and is used to move a byte to the processor status word. This is needed because the LSI-11 does not support accessing registers via memory addresses.
MFPS copies the processor status byte to the indicated register. This only exists on LSI-11s, and is needed there because those systems don't support accessing registers via memory addresses.
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |b|0|0|0|b|b|b|b|d|d|d|d|d|d|d|d| | Branch Code | Destination |
The destination of a branch is +127 to -128 words from the word following the branch instruction itself. This seems slightly odd, until you realize the sequence of events: the branch instruction is read from memory and the PC incremented. If the branch is to be taken, the offset is then added to the current value of the PC. Since the PC has already been incremented, the offset is thus relative to the following word. Note that all branch instructions are one word long.
The various branches test the values of specific condition codes, and if the tests succeed, the branch is taken. The condition codes are N (negative), Z (zero), C (carry), and V (overflow). In the table below, the branch tests are shown as boolean expressions. `x' stands for exclusive-OR. `v' stands for inclusive-OR.
_ _ _ _ _ _ _ _ _ _:_ _ _:_ _ _ |0|0|0|0|0|0|0|0|1|0|1|s|N|Z|V|C| | O p c o d e | | Mask |
General opcode 000240x. Set/Clear corresponding bits depending on sense of bit 04 (set=1, clear=0). Codes 240 and 260 set/clear no bits and are, thus, used as NOP. Although specific mnemonic are provided for each flag and all flags, any combination may actually be set or cleared at a time.
General mnemonics are:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |0|0|0|0|1|0|0|s|s|s|d|d|d|d|d|d| | Opcode |Stack|Destination|
MOV <source>,-(R6) MOV PC,<source> JMP <destination>
Thus, it saves the current contents of the specified source register on the stack, then copies the current program counter to the just-saved register, and finally jumps to the indicated destination. The fun part is (as usual with the PDP-11) that the PC is a general register (R7), and using it as the source results in:
MOV <PC>,-(R6) ; push return address onto stack MOV PC,PC ; no-op JMP <destination> ; jump to destination
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |0|0|0|0|0|0|0|0|1|0|0|0|0|s|s|s| | Opcode |Stack|
The actual operations involved are:
MOV <source>,PC MOV (R6)+,<source>
This is the reverse of JSR. Obviously, the finesse here too is that you can use the PC, to get what people normally consider a CALL/RETURN function. In that case, the first MOV is a no-op and the second pops the return address off of the stack and into the program counter.
Why is it done like this then? Well, consider this example:
... JSR R0,FOO .WORD A .WORD B MOV R1,C ... FOO: MOV @(R0)+,R1 ADD @(R0)+,R1 RTS R0
This type of in-line parameter passing is used extensively in the PDP-8 and PDP-10, for example. Also, the FORTRAN runtime system on the PDP-11 does it this way. (It is fairly easy to write a compiler who generates such a calling sequence, and then have a library of functions which expect this calling convention.)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |0|0|0|0|0|0|0|0|0|1|d|d|d|d|d|d| | Opcode |Destination|
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |0|0|0|0|0|0|0|0|0|0|0|0|0|i|i|i| | | | | | | Op |
The following opcode ranges are all unused (using three bits per digit):
Other arithmetic and floating point instructions were added to the basic set over the years, but those listed above form the core PDP-11 instruction set.
There is a comparison of PDP-11 and 80x86 floating point formats available at: ftp://ftp.dbit.com/pub/pdp11/info/fpp.txt