PDP11 ASSEMBLY LANGUAGE (MACRO 11) ================================== This is not intended to be a comprehensive guide to PDP 11 assembly language programming. The intention that you become able to understand and write simple MACRO-11 programs after a short period of time. Only a few of the more common instructions have been described. Comparison is often made to statements in BASIC. This is because it is assumed that you know BASIC, and also because many MACRO-11 instructions (and, for that matter, many instructions in many other assembly languages) have exact (or nearly exact) analogues in BASIC. More detailed introductions to MACRO-11 are given in texts such as INTRODUCTION TO COMPUTER SYSTEMS Using the PDP-11 and Pascal by MacEwen, and Minicomputer Systems: Organization and Programming (PDP-11) by Eckhouse. PDP-11 Memory organization The PDP-11's memory contains up to 65536 (64K) bytes, each consisting of 8 bits. Data in memory can be accessed in bytes, but is more frequently accessed in words of 16 bits (two bytes). Each word must start on an even-numbered byte. Memory addresses are 16 bits long (2 ** 16 = 65536), and so require exactly one word to store them. Integers are usually implemented so that they can be held in one word. Machine instructions also take up one word of storage (any addresses or integers that they use are held in the following words). Most machine instructions usually refer to two addresses. MACRO-11 Instructions In the examples given below, A and B are memory locations. If A represents the word starting at memory location 1000, and B represents the word starting at memory location 1002, then MOV A,B means take the contents of memory location 1000 and put it in memory location 1002. Label LABEL in the MACRO 11 statements below is replaced by sequence number 1000 in the equivalent BASIC statements. MACRO 11 Statement BASIC statement ------------------ --------------- MOV A,B ;move LET B = A ADD A,B ;add LET B = B + A SUB A,B ;subtract LET B = B - A BR LABEL ;branch GOTO 1000 JSR PC,LABEL ;jump to subroutine GOSUB 1000 RTS PC ;return from subroutine RETURN CLR A ;clear LET A = 0 NEG A ;negate LET A = -A INC A ;increment LET A = A + 1 DEC A ;decrement LET A = A - 1 The IF statements in BASIC shown below require two MACRO 11 instructions to achieve the same effect. CMP A,B ;compare BEQ LABEL ;branch if equal IF A = B THEN 1000 CMP A,B BNE A,B ;branch if not equal IF A <> B THEN 1000 Similarly, if we replace the inequality sign by <, <=, >, or >=, then the equivalent MACRO 11 instructions would contain BLT, BLE, BGT, BGE respectively. We now know enough MACRO 11 to write some simple programs. Note that there is no exact equivalent of the INPUT or PRINT statements. Until we learn how to handle I/O (Input/Output), we will have to examine memory to check whether our programs have executed correctly. Our first example program is a program to calculate numbers in the Fibonacci series. Numbers in this series are defined recursively as follows: fibo(0) = 1 fibo(1) = 1 fibo(n) = fibo(n - 1) + fibo(n - 2) We can implement this iteratively in the following structured BASIC program: 100 REM Iterative calculation of Fibonacci numbers 110 OLD = 1 120 FIBO = 1 130 WHILE N > 1 DO 'if n <= 0, then fibo = 1 140 NEW = FIBO + OLD 'calculate new fibonacci number 150 OLD = FIBO 'remember old one 160 FIBO = NEW 'current fibo 170 N = N - 1 180 WEND 190 In unstructured BASIC, the program becomes (for comparison, the MACRO 11 code is shown alongside) 110 OLD = 1 MOV #1.,OLD 120 FIBO = 1 MOV #1.,FIBO 125 ' WHILE: CMP N, #1. 130 IF N <= 1 THEN 190 BLE WEND 135 ' MOV FIBO,NEW 140 NEW = FIBO + OLD ADD OLD, NEW 150 OLD = FIBO MOV FIBO,OLD 160 FIBO = NEW MOV NEW,FIBO 170 N = N - 1 DEC N 180 GOTO 125 BR WHILE 190 WEND: There are some details of the MACRO 11 code which require further explanation. Decimal numbers which are intended as constants are preceded by a hash (#), and followed by a dot (.). If the dot were missing, the number would be assumed to be in base 8 (octal). This does not actually matter for the example program, but it would matter if the program contained any numbers greater than 7. If the hash were missing, the value used instead of 1 would be whatever number is stored at memory location 1. The MACRO 11 program would not actually assemble as it stands. We have not given N a value, nor have we told the assembler where to locate the variables OLD, NEW and FIBO. Also, the assembler does not know where the program should start executing. The corrected program is shown below (with N given the value 6) ; ************************************** ; * CALCULATE THE NTH FIBONACCI NUMBER * ; ************************************** ; OLD: .WORD 0. ;initialise OLD, NEW, and FIBO NEW: .WORD 0. FIBO: .WORD 0. N: .WORD 6. ;initialise N START: ;program starts executing here MOV #1.,OLD ;set OLD, FIBO to 1 MOV #1.,FIBO WHILE: CMP N, #1. ;WHILE N > 1 DO BLE WEND MOV FIBO,NEW ADD OLD,NEW ; NEW = FIBO + OLD MOV FIBO,OLD ; OLD = FIBO MOV NEW,FIBO ; FIBO = NEW DEC N ; N = N - 1 BR WHILE ;WEND WEND: HALT ;STOP .END START The program above will now assemble. Data can be declared as WORD (16 bits), BYTE (8 bits), or ASCII (arbitrary length character strings). For example .WORD 1997. .BYTE 65. ; ASCII code for 'A' .ASCII /Real men don't eat quiche./ Each character in a string is stored in a byte in memory. The assembler ignores anything between a semicolon (;) and the end of the line. Comments can be placed after a semicolon. Assembly language programs are difficult to read, and so comments should be frequent - one should appear on most lines. The last statement of the program (.END START) tells the assembler that the program should start executing at the instruction immediately following the label START. The HALT instruction causes the processor operation to cease if the processor is in kernel mode. (The processor is in kernel mode when certain parts of the operating system are executing.) In user mode, the instruction is regarded as illegal, and the program ceases execution at that point. REGISTERS AND ADDRESSING MODES A register is a special type of memory location. Registers are not in main memory, but inside the processor. The PDP11 has 8 registers which the programmer can use. These are R0, R1, R2, R3, R4, R5 General Purpose Registers R6 Stack Pointer (SP) R7 Program Counter (PC) Registers R0 through R5 are normally used as temporary storage locations. The PDP-11 has a wide range of addressing modes. Not all of them are included in the examples shown below. For a more comprehensive guide to addressing modes, refer to the PDP11/70 Processor Handbook. MOV R0,R1 ;take the contents of R0 and put them in R1 MOV (R0),R1 ;take the contents of the memory location ;whose address is held in R0 and put them in ;R1 MOV (R0),(R1) ;take the contents of the memory location ;whose address is held in R0 and put them in ;the memory location whose address is held in ;R1 MOV X,R0 ;take the contents of memory location X ;(which will have been defined earlier in the ;program by means of the .WORD statement) and ;put them in R0 MOV #X,R0 ;take the address of memory location X and ;put it in R0 There are also instructions for accessing array elements. The following example uses R0 as the index into an array of words starting at memory location A. MOV #5.,A(R0) ;put 5 in the location obtained by adding ;R0 to A. (As each array element is a two ;byte word, to obtain the Nth element we ;need to use N * 2 as the index.) The Special-Purpose Registers The program counter holds the location of the instruction about to be executed. The stack pointer holds the location of the top word or byte on the stack. The stack is used by the processor for storing the return addresses of subroutines, but the programmer is also free to use it as a temporary data store, though he/she should take care not to interfere with the use made of it by the processor. There are four types of instruction which make use of the stack. They are PUSH, POP, CALL and RETURN. PUSH and POP work as follows: X Y SP ! ! ! ------ ------ ---------------------------------------- ! a ! ! z ! ! w ! b ! c ! d ! e ! f ! ------ ------ ---------------------------------------- PUSH (X) SP ! ------ ------ ---------------------------------------- ! a ! ! z ! ! a ! b ! c ! d ! e ! f ! ----- ------ ---------------------------------------- POP (Y) SP ! ------ ------ ---------------------------------------- ! a ! ! a ! ! a ! b ! c ! d ! e ! f ! ------ ------ ---------------------------------------- Stack Operation MACRO 11 code --------------- ------------- PUSH X MOV X,-(SP) POP X MOV (SP)+,X CALL SUBR JSR PC,SUBR RETURN RTS PC The subroutine call instruction (JSR) pushes the address of the following instruction on to the stack pointed to by the SP register (R6), and then replaces the current contents of the program counter (PC, alias R7) by the start address of the subroutine. The subroutine return instruction (RTS) pops the top word on the stack, and puts it in PC. Another Programming Example We now know enough MACRO-11 to write more complex code. The following code pops the top two words off the stack and places them in the head and tail of the first cell of a linked list. The address of the head of this cell is then pushed on to the stack. During this process, the pointer to the list is replaced by the list's tail. A linked list is implemented as a number of cells. Each cell consists of two words, a head and a tail. The head contains some data (perhaps an integer, or else a pointer to another linked list), and the tail contains the address of the head of the first cell of the rest of the list. The last cell contains the value #NIL in its tail. CONS: MOV LIST,R0 ;Put the pointer to the list in R0. CMP R0,#NIL ;If the list is empty (LIST = #NIL) then BNE SKIP ;call the garbage collector (GARCOL). JSR PC,GARCOL ;This subroutine takes cells not ;currently in use and adds them on to ;the front of the LIST. SKIP: MOV 2(R0),LIST;Replace LIST by the tail of LIST. ** MOV (SP)+,R2 ;Pop the top word on the stack into R2. MOV (SP)+,R1 ;Pop the next word into R1. MOV R1,(R0) ;Put the contents of R1 and R2 into the MOV R2,2(R0) ;head and tail of the first cell of the ;original LIST. ** MOV R0,-(SP) ;Push a pointer to the cell on to the ;stack. ** The tail of the list (or, rather, the address of the head of the first cell of the rest of the list) is obtained by indexing.