The PDP-11 Instruction Set

The PDP-11 processor has a small but powerful set of instructions. There are instructions to add and subtract numbers, move values between registers and memory locations, compare values, branch to other instructions if certain conditions are met, call subroutines, pause the processor until an interrupt occurs, or even halt the processor altogether.

When a PDP-11 processor is ready to execute an instruction, it looks at the value of the program counter register (R7, aka PC). It then reads the instruction word from the address indicated by the program counter and increments PC by two, so it points to the next word.

The instruction is a word. The format of the word determines which operation to perform. If the value is 005303, the processor will decrement the value of R3. If the value is 005203, the processor will increment the value of R3. If the value is 005204, the processor will increment the value of R4. If the value is 005214, the processor will increment the value of the memory at the location stored in R4. There is a pattern to the instruction values. Much of the processors' circuitry is dedicated to decoding the instruction format and determining what to do.

Since it would be difficult for a programmer to memorize all of these patterns, the instructions are given a human-readable format, called "mnemonics" with optional operands. When taken together, they form a primitive language, called "assembly language". Programs called "assemblers" can take assembly language programs and convert them to the instruction values as seen above.

Examples of these mnemonics:

DEC R3
INC R3
INC R4
INC (R4)

The PDP-11 instructions can be categorized into a few sets:

  • Instructions that take two operands, like addition.
  • Instructions that take one operand, like increment.
  • Instructions that change the PC based on the processor status register.
  • Instructions that change the process status register condition bits.
  • Instructions that call and return from subroutines.
  • Instructions that handle interrupts.
  • Instructions that halt or reset the computer.

Instructions that take operands need to tell the processor what those operands are. The operands are either register values, or memory locations based on those register values. For the general registers R0 through R6, there are eight ways to find the operand based on a register. These are called "addresing modes". Each of them has a number, which is embedded in the instruction along with the register number.

  • Mode 0: Register: The operand is the value of the register. In assembly language, this is written as Rn: INC R3 will increment the value of R3.

    InstructionBeforeAfter
    INC R3R3: 001000R3: 001001
  • Mode 1: Register Deferred: The operand is the value of the memory location stored in the register. This is written as (Rn) or @Rn: INC (R3) will increment the memory value pointed to by R3.

    InstructionBeforeAfter
    INC (R3)R3: 002000
    002000: 012345
    R3: 002000
    002000: 012346
  • Mode 2: Auto Increment: Like register deferred mode, the operand is the value of the memroy location stored in the register. Once the operand is read, the value of the register is incremented by one if this is a byte instruction, or two if the register is a word instruction. This is written as "(Rn)+": INC (R3)+ will increment the memory value of the word pointed to by R3, then adds two to R3, which points R3 to the next word. INCB (R3)+ will increment the memory value of the byte pointed to by R3, then adds one to R3, pointing it to the next byte. This useful when operating within a loop.

    InstructionBeforeAfter
    INC (R3)+R3: 002000
    002000: 012345
    R3: 002002
    002000: 012346
    INCB (R3)+R3: 002000
    002000: 125
    R3: 002001
    002000: 126
  • Mode 3: Auto Increment Deferred: The operand is the value at the memory address in the location given in the register. The register is then incremented by two. Written as "@(Rn)+". The instruction INC @(R3)+ will increment the value the memory address in the location pointed to by R3, and adds two to R3. This is useful while iterating over a list of pointers.

    InstructionBeforeAfter
    INC @(R3)+R3: 002000
    002000: 004444
    004444: 000000
    R3: 002002
    002000: 004444
    004444: 000001
  • Mode 4: Auto Decrement: Like auto increment mode, except the value of the register is decremented before it is used to look up a value in memory. Written as "-(Rn)". The instruction INC -(R3) will subtract two from R3, then increments the value at the location given by R3.

    InstructionBeforeAfter
    INC -(R3)R3: 002000
    001776: 012345
    R3: 001776
    001776: 012344
    INCB -(R3)R3: 002000
    001777: 125
    R3: 001777
    001777: 124
  • Mode 5: Auto Decrement Deferred: The value of the register is decremented by two. The operand is the value at the memory address in the location given in the register. Written as "@-(Rn)". The instruction INC @-(R3) will subtract two from R3, then increment the value at the memory location that appears in the memory location pointed to by R3.

    InstructionBeforeAfter
    INC @-(R3)R3: 002000
    001776: 004444
    004444: 000000
    R3: 001776
    001776: 004444
    004444: 000001
  • Mode 6: Indexed: The operand is at the location given by the register plus the index value in the word after the instruction. Written as "X(Rn)". The instruction INC 6(R3) will increment the value at the memory location given by 6+R3. This is useful when dealing with structucred data.

    InstructionBeforeAfter
    INC 6(R3)R3: 002000
    002006: 000000
    R3: 002000
    002006: 000001
  • Mode 7: Indexed Deferred: The operand is the value in the memory address in the location given by the register plus the index word, which appears after the instruction. Written as "@X(Rn)". The instruction INC @6(R3) will increment the value at the memory address that appears at memory location 6+R3.

    InstructionBeforeAfter
    INC @6(R3)R3: 002000
    002006: 004444
    004444: 000000
    R3: 002000
    002006: 004444
    004444: 000001

If an instruction uses the program counter (R7), some of the modes work a little differently.

  • Mode 2: Immediate: The operand is the value in the word that follows the instruction word. The PC is then incremented by two to point to the next word. This is written as #nnnnnn: ADD #42,R2 adds 42 (octal, of course) to R2. Typically only useful as a source operand

    InstructionBeforeAfter
    ADD #42,R2R2: 000200R2: 000242
  • Mode 3: Absolute: The operand is the value in the memory address pointed to by the word that follows the instruction. The PC is then incremented by two to point to the next word. This is written as @#nnnnnn: ADD @#2000,R2 adds the value of memory location 2000 to R2.

    InstructionBeforeAfter
    ADD @#2000,R2R2: 00100
    002000: 001234
    R2: 001334
    002000: 001234
    ADD @#2000,@#3000002000: 001234
    003000: 000100
    002000: 001234
    003000: 001334
  • Mode 6: Relative: The operand is the value at the address of the current instruction plus the word after the instruction. Some assemblers use this mode when referring to a symbolic label in the code, like INC CTR.

    InstructionBeforeAfter
    INC CTRCTR: 001000CTR: 001001
  • Mode 7: Relative Deferred: The operand is the value at the address that appears at the current instruction plus the word after the instruction. This appears as INC @CTR.

    InstructionBeforeAfter
    INC @CTRCTR: 001000
    001000: 012345
    CTR: 001000
    001000: 012346

The mode numbers and the registers they refer to are encoded into instruction words. For example, instructions that take two operands are encoded as

+-+---+---+---+---+---+
|  op | sm| sr| dm| dr|
+-+---+---+---+---+---+

where "op" is the operation (1=move, 2=compare, etc.), "sm" is source addressing mode, "sr" is source register, "dm" is destination addressing mode, and "dr" is destination register. For example, the instruction MOV (R4)+,R5 is encoded as

012405

The operation is 01, the source is R4, autoincrement, the destination is R5 register.

The instruction MOV #123456,2(R3) is encoded as three words

012763
123456
000002

The first word says that this is MOV (op=01), the source is an immediate value (mode 2, register 7), and the destination is using indexed addressing (mode 6, register 3). The second word is the immediate value for the source operand, the third word is the index value for the destination operand.

The double operand instructions are

OperationMnemonicOpcodeDescription
moveMOV01Sets the destination operand to the value of the source operand.
compareCMP02Subtracts the source from the destination, discards the result.
test bitsBIT03Performs a logical AND of the source and destination, discards the result.
clear bitsBIC04Clears the desitnation bits that are set in the source.
set bitsBIS05Sets the bits in the destination that are set in the source.
addADD06Adds the source to the destination.
subtractSUB16Subtracts the source from the destination.

There are also instructions that operate on byets:

OperationMnemonicOpcodeDescription
move byteMOVB11Sets the destination byte to the value of the source operand.
compare byteCMPB12Subtracts the source byte from the destination byte, discards the result.
test bits in byteBITB13Perform a logical AND of the source and destination bytes, discards the result.
clear bits in byteBICB14Clears the destination bits that are set in the source byte.
set bits in byteBISB15Sets the destination bits that are set in the source byte.

Each of these instructions sets the processor status word bits:

Z is set if the result is zero. N is set if the most significant bit is zero. V is cleared for all instructions other than ADD, SUB, and CMP. For these instructions this flag is set if the operands had opposite signs and the sign of the result is the same as the destination, indicating an arithmetic overflow. If this did not happen, V is cleared. C is untouched for all instructions other than ADD, SUB, and CMP. For these instructions, this bit is set if there was a carry from the most significant bit.

The single operand instructions are encoded as

+-+---+---+---+---+---+
|       op    | dm| dr|
+-+---+---+---+---+---+
OperationMnemonicOpcodeDescription
clearCLR0050Clears the destination to zero.
complimentCOM0051Flips the bits of the destination.
incrementINC0052Increments the destination by one.
decrementDEC0053Decrements the destination by one.
negateNEG0054Replace the destination with its twos' compliment value.
add carryADC0055Adds the value of the carry bit to the destination.
subtract carrySBC0056Subtracts the value of the carry bit from the destination.
testTST0057Compare destination with zero.
rotate rightROR0060Rotate destination bits to the right through the carry bit.
rotate leftROL0061Rotate destination bits left through the carry bit.
arithmetic shift rightASR0062Shift bits the the right, without modifying the high bit.
arithmetic shift leftASL0063Shift bits the the left, setting low bit to zero.
jumpJMP0001Set the program counter to the destination. Destination cannot use register mode.
swap bytes in wordSWAB0003Exchange the high and low bytes in a word.

And their corresponding byte-oriented instructions

OperationMnemonicOpcodeDescription
clear byteCLRB1050Clears the destination to zero.
compliment byteCOMB1051Flips the bits of the destination.
increment byteINCB1052Increments the destination by one.
decrement byteDECB1053Decrements the destination by one.
negate byteNEGB1054Replace the destination with its twos' compliment value.
add carry to byteADCB1055Adds the value of the carry bit to the destination.
subtract carry from byteSBCB1056Subtracts the value of the carry bit from the destination.
test byteTSTB1057Compare destination with zero.
rotate right byteRORB1060Rotate destination bits to the right through the carry bit.
rotate left byteROLB1061Rotate destination bits left through the carry bit.
arithmetic shift right byteASRB1062Shift bits the the right, without modifying the high bit.
arithmetic shift left byteASLB1063Shift bits the the left, setting low bit to zero.

Branch operations change the program counter if certain bits of the processor status word are set or clear. These operations do not use registers, but encode the number of bytes to add (or subtract) if the conditions are met directly in the instruction. These instructions have the form

+-+---+---+---+---+---+
|     op   ><   offset|
+-+---+---+---+---+---+

Since there are eight bits for offset, and is treated as a singed value, the lowest offset is 200 (-128 decimal), the highest is 177 (127 decimal). If a program needs to branch further away, branch should be used with a jump instruction. Some assemblers provide a pesudo-mnemonic that handles this automaticaly.

OperationMnemonicOpcodeDescription
branch unconditionallyBR0004Branch regardless of the flags.
branch if not equalBNE0010Branch if Z=0
branch if equalBEQ0014Branch if Z=1
branch if greater or equalBGE0020Branch if N and V are different.
branch if less thanBLT0024Branch if one of N or V is set.
branch if greater thanBGT0030Branch if Z=1 or if N and V are different.
branch if less than or equalBLE0034Branch if Z=1 or if one of N or V is set.
branch if plusBPL1000Branch if N=0
branch if minusBMI1004Branch if N=1
branch if higherBHI1010Branch if both C or Z are 0.
branch if lower or sameBLOS1014Branch if C or Z is 1.
branch if overflow clearBVC1020Branch if V=0
branch if overflow setBVS1024Branch if V=1
branch if carry clearBCC1030Branch if C=0. Also "branch if higher or same" (BHIS)
branch if carry setBCS1034Branch if C=1. Also "branch if lower" (BLO)

Condition Code instructions can directly modify the processor status bits. Most assemblers have mnemonics that set or clear one bit at a time, but a clever programmer can set or clear multiple status bits with a single instruction. These instructions have the format

+-+---+---+---+---+---+
|0|  0|  0|  2|1SN|ZVC|
+-+---+---+---+---+---+
OperationMnemonicInstruction
clear carryCLC000241
clear overflowCLV000242
clear zeroCLZ000244
clear negativeCLN000250
set carrySEC000261
set overflowSEV000262
set zeroSEZ000264
set negativeSEN000270

The next two instructions which implement subroutine calls have special formats.

The Jump to Subroutine instruction has the format

+-+---+---+---+---+---+
|0|  0|  4|reg| dm| dr|
+-+---+---+---+---+---+

where reg is the "link register", and "dm" and "dr" are destination mode and registear.

OperationMnemonicOpcodeDescription
jump to subroutineJSR004Push the link register to the stack, and put the PC into the register. Set the PC to the destination. Destination cannot be used with register mode.

The Return from Subroutine instruction has the format

+-+---+---+---+---+---+
|0|  0|  0|  2|  0|reg|
+-+---+---+---+---+---+
OperationMnemonicOpcodeDescription
return from subroutineRTS00020Move the register to PC, and pop the register from the stack.

The remaining instructions are for low level machine operations and program traps. These are typically used by operating systems, and are rarely used by most programs. They are included here for completeness.

OperationMnemonicInstructionDescription
haltHALT000000Halts the computer, requiring an operator to physically press the "continue" button
wait for interruptWAIT000001Pauses the computer until an interrupt is raised.
return from interruptRTI000002Return control from an interrupt service routine.
I/O trapIOT000004Calls the interrupt routine at vector address 14. Typically used for debugging.
Reset busRESET000005Assert a bus reset.
emulator trapEMT104xxxCalls the interrupt routine with a vector address of 30. "xxx" in the range of 000-377. Often used to implement calls to the operating system.
software trapTRAP104xxxCalls the interrupt routing with a vector address of 34. "xxx" in the range of 400-777.

These are the instructions available to the PDP-11/20 KA11 processor. DEC also sold additional options that include multiply and divide instructions, as well as instrutions to support floating point arithmetic. Additional instructions were also added to later PDP-11 machines as the architecture expanded to support multitasking with task isolation.

A simple program to add two to three would use the instructions

012700     (MOV immediate in next word to register R0)
000002
062700     (ADD immediate in next word to register R0)
000003
010037     (MOV register R0 to the absolute address in the next word)
002000
000000     (HALT the processor)

Using the programmer's console, the programmer can load address 1000, then deposit these five instructions into the next five words of memory. The programmer could then use the console to verify these words are in address 1000 through 1014. They can then set the switches back to 1000, press the "load address" switch, and finally press the start button. Upon setting the switches to 2000, pressing the "load address" then "exam" switches, the programmer should see the result 5 stored at address 2000.