BTN11 - A PDP-11 Cross Assembler

1 Introduction
2 Command Line Arguments
3 Input File Format
3.1 Statements
3.2 Operand Formats
4 Assembler Directives
5 Instruction Set
5.1 Notation
5.2 Availability
5.3 Addressing Modes
5.4 Addressing through PC
5.5 Instructions, A-M
5.6 Instructions, N-Z
6 Memory Locations
6.1 CPU Registers (R0-R7)
6.2 177776 - Processor Status Word (PSW)
6.3 177766 - CPU Error Register
6.4 177772 - Program Interrupt Request (PIRQ) Register
6.5 177546 - Line Time Clock (LTC) Register
6.6 177750 - Maintenance Register
6.7 Interrupt Vectors

1 Introduction

BTN11 - A PDP-11 Cross Assembler
By Nils M Holm, 2001;  in the public domain

BTN11 is a portable cross assembler which generates code for various models of the PDP-11. It accepts all instructions of the basic instruction set plus the EIS and many instructions which are available only on specific CPUs. It does neither support CIS nor FIS, though.

The input grammar is mostly compatible to the syntax described in the PDP-11 Architecture Handbook.

Many directives have been taken from PAL11, some have been added.

The output of BTN11 is an absolute binary, by default loadable at location 0. Therefore it is suitable for writing standalone programs like bootstrap loaders, etc.

By default BTN11 reads an input program from stdin and writes an absolute binary to stdout. Since it is a two-pass assembler, it stores its input in a temporary file for the second pass.

The assembler itself is written in plain, K&R-style C. It should compile without any problems on a variety of different systems.

2 Command Line Arguments

-a Absolute mode

Generate absolute mode for references to symbols. By default the DEC convention of generating PC-relative code is used. For example, the code

HERE: .WORD 7777
      MOV   HERE,R0

would generate a PC-relative reference:

007777         data
016700 177772  mov -6(PC),R0

When -a is used, an absolute reference to HERE would be emitted:

007777  data
013700  mov @(PC)+,R0
000000  address of data

This option is pretty useful for debugging stand-alone code using ODT.

-b Generate boot code

This option makes BTN11 generate output in a form which is suitable for passing it to ODT. To upload the resulting boostrap to a machine, enter ODT and then use the ASCII upload feature of your terminal emulator.

-k Keep temporary file

Do not delete the temporary file holding a copy of the input program for the second pass. Normally this file is deleted after the second pass. Useful only for debugging.

-l Generate list file

Generate a list file containing both the source code and an octal dump of the generated code. The list file will be named name.lst where 'name' is the input file name with its last suffix removed. This option does not work when reading the source program from the standard input.

-n Generate line numbers

Prefix each line in the list file with its input line number. The default is to generate code addresses instead of line numbers. This option is ignored without -l .

-v Print version

Print the usual version banner, etc.

-o file Output file

Write the resulting binary to the given file rather than to the standard output.

-t file Temp file name

Used to specify an alternative name for the temporary file. By default the file 'btn11.tmp' in the current working directory is used. Using -t, a different path may be used.

-D symbol=value Predefine symbol

Using this option, a given symbol can be assigned a value before any input is processed. Attempts to redefine the given symbol in the source code will be ignored, so this option can be used to override default values in the input program. -D is primarily intended to control conditional compilation.

file Input file

When a file name is specified in the command line, input is read from that file instead of the standard input. The input file name argument must be last.

3 Input File Format

3.1 Statements

Each BTN11 input file consists of a series of statements. Each statement has three optional parts:

[ label : ] [ instruction ] [ ; comment ]
[ label : ] [ directive ] [ ; comment ]

The most common form of the statement consists just of an instruction. Each instruction has a mnemonic and zero, one, or two operands. The number of operands to an instruction can be looked up in section (5). Two-operand instruction have opcodes of the forms

xxSSDD, xxxRSS, xxxRDD

The assembler notation for such instructions is

INSTRUCTION SOURCE, DESTINATION

Single-instruction commands have only one DD, SS, R, or NN in their opcodes. Their assembler notation is

INSTRUCTION OPERAND

Each operand may have one out of 11 addressing modes. See sections (5.3) and (3.2) for their notations.

Directives are similar to instructions, but do not belong to the PDP-11 instruction set. They are used to compile data, set up internal variables, etc. They are described in detail in section (4).

Multiple labels may precede one instruction or directive. The labels may be placed in the same line as the following instruction or directive.

White space is (almost) never required. The commands

TESTRDY: BIT     #200, @#177560

and

TESTRDY:BIT#200,@#177560

are considered the same. Newline characters are required to separate commands, though.

Comments are started with a semicolon and extend up to the end of the line. All characters are allowed inside of comments.

Empty input lines are ignored.

3.2 Operand Formats

Operands to instructions may have the following formats. For a description of the addressing modes, see section (5.2).

Mode Examples    
Register R0 SP PC
Register Deferred (R0) (SP)  
Autoincrement (R0)+ (SP)+ (PC)+
Autoincrement Deferred @(R0)+ @(SP)+ @(PC)+
Autodecrement -(R0) -(SP)  
Autodecrement Deferred @-(R0) @-(SP)  
Index addr(R0) addr(SP) addr(PC)
Index Deferred @addr(R0) @addr(SP) @addr(PC)
Immediate #addr   (PC)+
Absolute @#addr   @(PC)+
Relative addr   addr(PC)
Relative Deferred @addr   @addr(PC)

Each operand to an instruction may have any of the above formats.

An address (addr) may be either a symbol or a literal number. For example, the programs

CSR:    .EQU    176500
        MOV     CSR,R0

and

        MOV     176500,R0

would have the same effect.

Numeric operands are interpreted with respect to the radix which is currently in effect. The default radix is 8 (octal).

Each numeric operand may be prefixed with a modifier, which changes the radix of the operands, complements it, or negates it:

Mod. Examples Description
^b ^b111100 change radix to binary
^d ^d789 change radix to decimal
^o ^o173000 change radix to octal
^c ^c777 emit the one's complement of the operand
^n ^n12345 emit the two's complement of the operand
    (negate the operand)

Modifiers cannot be combined.

4 Assembler Directives

.ASCII "string"

Emit the bytes of the given string.

.ASCIZ "string"

Emit the bytes of the given string and append a NUL byte.

.BLKB size

Create a block of 'size' bytes by emitting zeroes.

.BLKW size

Create a block of 'size' words by emitting zeroes.

.BYTE value [ , value ... ]

Emit the given byte values literally

.EQU value
.EQU symbol

Assign the given value (or the value of the given symbol) to the most recently defined symbol. For example,

HERE: .EQU THERE

would assign the symbol HERE the value of the symbol THERE.

.EVEN

Emit a zero byte, if the current code address is not even.

.IF equation, ... 
.ENDIF

Compile the statements between .IF and .ENDIF only if the given equation holds. Each equation has the form

A = B

where both A and B may be numeric values or symbols. Multiple equations may be specified in a comma-separated list. In this case, all equations must hold for the following code to be compiled (the comma may be considered a logical AND). If a .ELSE directive oocurs between .IF and .ENDIF, the current state will be reversed (output will be generated, if the equations did not hold, and no output will be generated, if they did hold).

Error checking is NOT turned off between .IF and .ENDIF, so using these directives to embed comments does not work.

.ODD

Emit a zero byte, if the current code address is even.

.ORG offset

Set the origin (start address) of the program to 'offset'. Should precede all instructions in the program.

.RADIX base

Interpret numbers in the input program according to the gven base. The default base is octal (8). Any base from 2 to 10 may be specified.

.WORD value [ , ... ]
.WORD symbol [ , ... ]

Emit the given word values literally. When a symbols is included in a list, the value of the symbol will be emitted.

5 Instruction Set

5.1 Notation

D destination address
S source address
I current instruction word
R register
Rv1 register pair (even, odd)
R+1 register following R
Rx specific register
PC program counter (=R7)
SP stack pointer (=R6)
PS processor status register
x<a:b> bits b through a of x
<- assignment
+ sum
- difference
* product
~ bitwise complement
v bitwise OR
/\ bitwise AND
x! = -(SP)<-x
x? = x<-(SP)+
N constant
x(PD) x in previous data space
x(PI) x in previous instruction space
shl shift left
shr shift right
rol rotate left
ror rotate right
mod modulus
N Negative flag
Z Zero flag
V Overflow flag
C Carry flag

In flags field:

N,Z,V,C affected
0 cleared
1 set
- not affected

5.2 Availability

MARK is not available on the FALCON SBC-11/21.

MARK, RESET, TRAP, WAIT, BPT, EMT, IOT, HALT are not available in VAX-11 mode.

The following instructions are *not* part of the basic instruction set (which is available on all machines). These instructions are limited to the machines listed in the following table.

2   = LSI-11/2
23  = 11/23
23+ = 11/23+
24  = 11/24
44  = 11/44
SBC = FALCON SBC-11/21
M11 = MICRO/11
J11 = MICRO/J11
T11 = MICRO/T11
VAX = VAX-11 (compatibility mode)
EIS = Extended Integer instruction Set option
         2   23 23+  24  44 SBC M11 J11 T11 VAX EIS
        --- --- --- --- --- --- --- --- --- --- ---
ASH      -   -   X   X   -   -   X   -   -   X   X
ASHC     -   -   X   X   -   -   X   -   -   X   X
BPT      -   X   X   X   X   X   X   X   X   -   X
CSM      -   -   -   -   X   -   -   X   -   -   -
DIV      -   -   X   X   -   -   X   -   -   X   X
EMT      -   X   X   X   X   X   X   X   X   -   X
HALT     -   X   X   X   X   X   X   X   X   -   X
IOT      -   X   X   X   X   X   X   X   X   -   X
MARK     -   X   X   X   X   -   X   X   X   -   X
MFPD     -   X   X   X   X   -   X   X   -   -   -
MFPI     -   X   X   X   X   -   X   X   -   -   -
MFPS     X   X   X   X   -   X   X   X   X   -   -
MFPT     -   X   X   X   X   X   X   X   X   -   -
MTPD     -   X   X   X   X   -   X   X   -   -   -
MTPI     -   X   X   X   X   -   X   X   -   -   -
MTPS     X   X   X   X   -   X   X   X   X   -   -
MUL      -   -   X   X   -   -   X   -       X   X
RESET    -   X   X   X   X   X   X   X   X   -   X
SPL      -   -   -   -   X   -   -   X   -   -   -
TRAP     -   X   X   X   X   X   X   X   X   -   X
TSTSET   -   -   -   -   -   -   -   X   -   -   -
WAIT     -   X   X   X   X   X   X   X   X   -   X
WRTLCK   -   -   -   -   -   -   -   X   -   -   -

5.3 Addressing Modes

Each operand to a 1-operand or 2-operand instruction has two fields with a length of three bits each. The first field contains the addressing mode (M) and the second one a register (R):

+------- operand -------+
|                       |
-------------------------
| M | M | M | R | R | R |
-------------------------
|           |           |
+---mode----+-register--+

The register field always specifies one of the eight registers R0 through R7. The registers have the following purposes:

R0-R5 general
R6 processor stack pointer (SP)
R7 program counter (PC)

Each register may be combined with the following eight addressing modes (Rx denotes any register):

0 Register mode Rx

The operand is direct fetched from the specified register.

1 Register Deferred mode (Rx)

The operand is fetched from the memory location, the specified register points to.

2 Autoincrement mode (Rx)+

Like Register Deferred mode, but after fetching the operand, the register is incremented. In word operations, two is added to the register, and in byte operation, one is added.

3 Autoincrement Deferred mode @(Rx)+

The operand is fetched from the address stored in the cell pointed to by the specified register. This is a double indirection. Rx is incremented by two after fetching the operand.

4 Autodecrement mode -(Rx)

Like Register Deferred mode, but before fetching the operand, the register is decremented. In word operations, two is subtracted from the register, and in byte operation, one is subtracted.

5 Autodecrement Deferred mode @-(Rx)

The operand is fetched from the address stored in the cell pointed to by the specified register after decrementing the register by two. This is a double indirection.

6 Index mode X(Rn)

The address of the actual operand is built by adding the constant X to the address contained in the specified register. The operand is fetched from the computed address.

7 Index Deferred mode @X(Rn)

First, the address of a cell A is computed by adding the constant X to the address contained in the specified register. The operand is fetched from the address stored in the cell A. This is a double indirection.

5.4 Addressing through PC

Some modes, like immediate and absolute mode, are implememted by indirection through the program counter PC.

27 PC Immediate mode (R7)+ #N

When using Autoincrement mode (2) with R7, the operand will be taken from the address following the instruction. Since PC is incremented after fetching the operand, the operand will be skipped in the flow of operation. PC is always incremented by two, even in byte operations.

37 PC Absolute mode @(R7)+ @#A

When using Autoincrement Deferred mode (3) with R7, the operand is taken from the address contained in the cell following the instruction word. This is a double indirection. Like in PC Immediate mode, R7 is always incremented by two.

67 PC Relative mode X(R7) A

In PC Relative mode, the address of the operand is formed by adding the constant X to the PC. When multiple operand are addressed using PC, the PC value used to compute a relative address is the value after fetching all operands. The advantage of PC Relative addressing is that the code may be relocated, as long as the distance between the instruction and the operand keeps the same.

77 PC Relative Deferred mode @X(R7) @A

This mode is similar to PC Relative mode, but the operand is taken from the address pointed to by X(R7). This is a double indirection.

5.5 Instructions, A-M

Symbol Opcode Description Flags
------ ------ ----------- -----
ADC 00055DD D<-D+C NZVC
ADCB 01055DD D<-D+C NZVC
ADD 006SSDD D<-S+D NZVC
ASH 0072RSS R<-R shl x, x=SS<5:0>, -x=shr NZVC
ASHC 0073RSS Rv1<-Rv1 shl x, x=SS<5:0>, -x=shr NZVC
ASL 00063DD D<-D shl 1 NZVC
ASLB 01063DD D<-D shl 1 NZVC
ASR 00062DD D<-D shr 1 NZVC
ASRB 01062DD D<-D shr 1 NZVC
BCC 0103000 PC<-PC + I<0:7>*2, if C=0 ----
BCS 0103400 PC<-PC + I<0:7>*2, if C=1 ----
BEQ 0001400 PC<-PC + I<0:7>*2, if Z=1 ----
BGE 0002000 PC<-PC + I<0:7>*2, if N xor V = 0 ----
BGT 0003000 PC<-PC + I<0:7>*2, if Zv(NvV) = 0 ----
BHI 0101000 PC<-PC + I<0:7>*2, if C=0, Z=0 ----
BHIS 0103000 PC<-PC + I<0:7>*2, if C=0 NZVC
BIC 004SSDD D<-~S/\D NZ0C
BICB 014SSDD D<-~S/\D NZ0C
BIS 005SSDD D<-SvD NZ0C
BISB 015SSDD D<-SvD NZ0C
BIT 003SSDD D/\S NZ0C
BITB 013SSDD D/\S NZ0C
BLE 0003400 PC<-PC + I<0:7>*2, if Zv(NvV) = 1 ----
BLO 0103400 PC<-PC + I<0:7>*2, if C=1 ----
BLOS 0101400 PC<-PC + I<0:7>*2, if CvZ = 1 ----
BLT 0002400 PC<-PC + I<0:7>*2, if N xor V = 1 ----
BMI 0100400 PC<-PC + I<0:7>*2, if N = 1 ----
BNE 0001000 PC<-PC + I<0:7>*2, if Z = 0 ----
BPL 0100000 PC<-PC + I<0:7>*2, if N = 0 ----
BPT 0000003 PS!, PC!, PC<-(14), PS<-(16) TRAP
BR 0000400 PC<-PC + I<0:7>*2 ----
BVC 0102000 PC<-PC + I<0:7>*2, if V=0 ----
BVS 0102400 PC<-PC + I<0:7>*2, if V=1 ----
CCC 0000257 N<-0, Z<-0, V<-0, C<-0 0000
CLC 0000241 C<-0 ---0
CLN 0000250 N<-0A 0---
CLR 00050DD DD<-0 -1--
CLRB 01050DD DD<-0 -1--
CLV 0000242 V<-0 --0-
CLZ 0000244 Z<-0 -0--
CMP 002SSDD S-D NZVC
CMPB 012SSDD S-D NZVC
COM 00051DD D<-~D NZ01
COMB 01051DD D<-~D NZ01
CSM 00070DD if MMR3<3> = 1 and in kernel mode, ----
    then SP(supervisor) <- SP, x<-PS,  
    x<3:0> <- 0, PS<13:12> <- PS<15:14>,i  
    PS<15:14> <- 01, PS<4> <- 0,  
    x!, PC!, D!, PC<-(10)  
    else TRAP 10 in kernel mode  
DEC 00053DD D<-D-1 NZV-
DECB 01053DD D<-D-1 NZV-
DIV 0071RSS R<-Rv1/S, R+1<-Rv1 mod S NZVC
EMT 0104000 PS!, PC!, PC<-(30), PS<-(32) TRAP
    I<0:7> = 000...0377  
HALT 0000000 Halt CPU ----
INC 00052DD D<-D+1 NZV-
INCB 01052DD D<-D+1 NZV-
IOT 0000004 PS!, PC!, PC<-(20), PS<-(22) TRAP
JMP 00001DD PC<-D ----
JSR 0004RDD R!, R<-PC, PC<-D ----
MARK 00064NN SP<-PC+2*NN, PC<-R5, R5<-(SP)+NN ----
MFPD 01065SS S(PD)! NZ0-
MFPI 00065SS S(PI)! NZ0-
MFPS 01067DD D<-PS<7:0> NZ0-
MFPT 0000007 R0<-processor type, subtype ----
MOV 001SSDD D<-S NZ0-
MOVB 011SSDD D<-S NZ0-
MTPD 01066DD D(PD)? NZ0-
MTPI 00066DD D(PI)? NZ0-
MTPS 01064SS PS<-S NZVC
MUL 0070RSS Rv1<-R*S NZ0C

5.6 Instructions, N-Z

Symbol Opcode Description Flags
------ ------ ----------- -----
NEG 00054DD D<- -D NZVC
NEGB 01054DD D<- -D NZVC
NOP 0000240 no operation ----
RESET 0000005 Send INIT on the bus ----
ROL 00061DD D<-D rol 1 NZVC
ROLB 01061DD D<-D rol 1 NZVC
ROR 00060DD D<-D ror 1 NZVC
RORB 01060DD D<-D ror 1 NZVC
RTI 0000002 PC?, PS? NZVC
RTS 000020R PC<-R, R? ----
RTT 0000006 PC?, PS? NZVC
SBC 00056DD D<-D-C NZVC
SBCB 01056DD D<-D-C NZVC
SCC 0000277 N<-1, Z<-1, V<-1, C<-1 1111
SEC 0000261 C<-1 ---1
SEN 0000270 N<-1 1---
SEV 0000262 V<-1 --1-
SEZ 0000264 Z<-1 -1--
SOB 0077RNN R<-R-1, PC<-PC-N*2, if R =/= 0 ----
SPL 000023N PS<7:5> <- N ----
SUB 016SSDD D<-D-S NZVC
SWAB 00003DD x<-D<7:0>, D<7:0>=D<8:15>, D<7:0>=x NZ00
SXT 00067DD D<-0, if N=0, D<-177777, if N=1 -Z0-
TRAP 0104400 PS!, PC!, PC<-(34), PS<-(36) TRAP
    I<0:7> = 000...0377  
TST 00057DD D<-D NZ00
TSTB 01057DD D<-D NZ00
TSTSET 00072DD R0<-D (lock), D<-R0v1 (unlock) NZ0C
WAIT 0000001 wait for interrupt ----
WRTLCK 00073DD D<-R0 using bus lock NZ0-
XOR 0074RDD D<- R xor D NZ0-

6 Memory Locations

6.1 CPU Registers (R0-R7)

R0 General Purpose
R1 General Purpose
R2 General Purpose
R3 General Purpose
R4 General Purpose
R5 General Purpose
R6 General Purpose, Kernel Stack Pointer
R7 General Purpose, Program Counter

6.2 177776 - Processor Status Word (PSW)

 15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|  CM   |  PM   |RS | 0 | 0 |///|    PRI    | T | N | Z | V | C |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
CM R/W Current Mode
    00 = Kernel
    01 = Supervisor
    10 = Illegal
    11 = User
PM R/W Previous Mode, see CM
RS R/W Register Select 0 = R0-R5, 1=R0'-R5'
9,10 R Not used
8 R/W Reserved
PRI R/W Priority 000=0, 111=7
T R/W Trace (can only be set using RTT/RTI)
N R/W Negative result
Z R/W Zero result
V R/W Overflow
C R/W Carry flag

6.3 177766 - CPU Error Register

 15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |HLT|ADR|NXM|I/O|YSV|RSV| 0 | 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
15-8 R Not used
HLT R HALT in supervisor or user mode
ADR R Address error (odd word address, jump to register)
NXM R Memory timeout
I/O R I/O register timeout
YSV R Yellow stack violation (KSP < 400)
RSV R Red stack violation (kernel stack push aborted)
1,0 R Not used

6.4 177772 - Program Interrupt Request (PIRQ) Register

 15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|IR7|IR6|IR5|IR4|IR3|IR2|IR1| 0 |   LEVEL   | 0 |   LEVEL   | 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
IR7 R/W Request interrupt at priority level 7
IR6 R/W Request interrupt at priority level 6
IR5 R/W Request interrupt at priority level 5
IR4 R/W Request interrupt at priority level 4
IR3 R/W Request interrupt at priority level 3
IR2 R/W Request interrupt at priority level 2
IR1 R/W Request interrupt at priority level 1
8 R Unused
LEVEL R Highest interrupt level requested (1=highest)
4 R Unused
0 R Unused

6.5 177546 - Line Time Clock (LTC) Register

 15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |CIE| 0 | 0 | 0 | 0 | 0 | 0 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
15-7 R Unused
CIE R/W Clock interrupt (BEVNT) enable. When enabled,
    BEVNT L causes a trap to 100 at priority level 6.
5-0 R Unused

6.6 177750 - Maintenance Register

 15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | H |  PWR  |POK|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
15-4 R Unused
H R Halt option: 0=enter ODT, 1=trap to location 4
PWR R Power up mode:
    00 = PC @ 24, PS @ 26
    01 = ODT
    10 = PC = 173000, PS = 340
    11 = User boot
POK R Power OK (BPOK)

6.7 Interrupt Vectors

NM   = non-maskable
HALT = HALT line
SYNC = synchronous
4 NM Red stack violation
4 NM Yellow stack violation
4 NM Address error
4 NM Memory timeout
4 NM Device timout
4 HALT Halt requested (HALT)
10 SYNC Call to supervisor mode (CSM)
14 NM Trace trap
14 SYNC Breakpoint (BPT)
20 SYNC I/O trap (IOT)
24 NM Power failed
30 SYNC Emulator trap (EMT)
34 SYNC User trap (TRAP)
100 6 Line time clock (BEVNT L)
240 7 Program interrupt request, IR7
240 6 Program interrupt request, IR6
240 5 Program interrupt request, IR5
240 4 Program interrupt request, IR4
240 3 Program interrupt request, IR3
240 2 Program interrupt request, IR2
240 1 Program interrupt request, IR1
244 SYNC Floating point exception
250 NM Memory management violation