LC/3 Programming I

Arithmetic

This example illustrates the execute and load/store operations of the LC-3. It also shows how to subtract and OR values in the LC-3.

The program

          .ORIG   x3000
          LD      R1,X
          LD      R2,Y
;; Compute X + Y
          ADD     R3,R1,R2
          ST      R3,XPLUSY
;; Compute X & Y
          AND     R3,R1,R3
          ST      R3,XANDY
;; Compute X | Y
          NOT     R3,R1
          NOT     R4,R2
          AND     R3,R3,R4
          NOT     R3,R3
          ST      R3,XORY
;; Compute X - Y
          NOT     R3,R2
          ADD     R3,R3,#+1
          ADD     R3,R3,R1
          ST      R3,XMINUSY
          HALT
;; Parameters and results stored here
X         .FILL   #255
Y         .FILL   #2009
XANDY     .BLKW   1
XORY      .BLKW   1
XPLUSY    .BLKW   1
XMINUSY   .BLKW   1
          .END

Symbol table

One of the first jobs of the LC-3 assembler is to determine the address of the data values. Here is the symbol table file produced by the assembler.

//Symbol Name		Page Address
//----------------	------------
//	X                       3010
//	XANDY                   3012
//	XMINUSY                 3015
//	XORY                    3013
//	XPLUSY                  3014
//	Y                       3011

1's and 0's

Here is the binary file produced by the assembler. Try to match it with the assembler language program. The first binary word is the origin for the program.

Notice the that LD and ST instructions store the target addresses as offset from the PC. For example, the first instruction is LD  R1,X. This instruction is located at address x3000. When the instruction is being executed, the PC is x3001. Since X is stored at address x3010, the offset for the PC is x3010-x3001, or x0F. This is why the first instruction is 220F.

0011000000000000
0010001000001111
0010010000001111
0001011001000010
0011011000010000
0101011001000011
0011011000001100
1001011001111111
1001100010111111
0101011011000100
1001011011111111
0011011000001000
1001011010111111
0001011011100001
0001011011000001
0011011000000110
1111000000100101
0000000011111111
0000011111011001
0000000000000000
0000000000000000
0000000000000000
0000000000000000

The listing

Most assembler produce listings. In ancient times these were often used for debugging.

(0000) 3000  0011000000000000 (   1)                 .ORIG x3000
(3000) 220F  0010001000001111 (   2)                 LD    R1 X
(3001) 240F  0010010000001111 (   3)                 LD    R2 Y
(3002) 1642  0001011001000010 (   5)                 ADD   R3 R1 R2
(3003) 3610  0011011000010000 (   6)                 ST    R3 XPLUSY
(3004) 5643  0101011001000011 (   8)                 AND   R3 R1 R3
(3005) 360C  0011011000001100 (   9)                 ST    R3 XANDY
(3006) 967F  1001011001111111 (  11)                 NOT   R3 R1
(3007) 98BF  1001100010111111 (  12)                 NOT   R4 R2
(3008) 56C4  0101011011000100 (  13)                 AND   R3 R3 R4
(3009) 96FF  1001011011111111 (  14)                 NOT   R3 R3
(300A) 3608  0011011000001000 (  15)                 ST    R3 XORY
(300B) 96BF  1001011010111111 (  17)                 NOT   R3 R2
(300C) 16E1  0001011011100001 (  18)                 ADD   R3 R3 #1
(300D) 16C1  0001011011000001 (  19)                 ADD   R3 R3 R1
(300E) 3606  0011011000000110 (  20)                 ST    R3 XMINUSY
(300F) F025  1111000000100101 (  21)                 TRAP  x25
(3010) 00FF  0000000011111111 (  23) X               .FILL x00FF
(3011) 07D9  0000011111011001 (  24) Y               .FILL x07D9
(3012) 0000  0000000000000000 (  26) XANDY           .FILL x0000
(3013) 0000  0000000000000000 (  27) XORY            .FILL x0000
(3014) 0000  0000000000000000 (  28) XPLUSY          .FILL x0000
(3015) 0000  0000000000000000 (  29) XMINUSY         .FILL x0000

Array processing

This next program sets elements of an array VABS to the absolute values of the corresponding elements of VECT. It is similar to the following C/Java for loop.

for (i=0; i<SIZE; ++i) {
  int nextV = VECT[i] ;
  if (nextV < 0) {
    nextV = -nextV ;
  }
  VABS[i] = nextV ;
}

However, due to its use of addresses stored in registers (which are similar to C pointers), it is much more similar to this obscurely written C loop.

for(R0=&VEXT[0], R2=&VABS[0], R4=SIZE; R4>0; ++R0, ++R2, --R4) {
  R1 = *R0 ;
  if (R1<0) {
    R1 = -R1 ;
  }
  *R2 = R1 ;
}  

The program

The commments follow the obscure C loop.

          .ORIG   x3000
          LEA     R0,VECT       ;; R0 = &VECT[0]
          LEA     R2,VABS       ;; R2 = &VABS[0]
          LD      R4,SIZE       ;; R4 = SIZE
          BRnz    NOLOOP        ;; if R4≤0 goto NOLOOP
LOOP      LDR     R1,R0,#0      ;; R1 = *R0
          BRzp    NMPOS         ;; if R1≥0 goto NMPOS
          NOT     R1,R1         ;; R1 = -R1
          ADD     R1,R1,#1
NMPOS     STR     R1,R2,#0      ;; *R2 = R1
          ADD     R0,R0,#1      ;; ++R0
          ADD     R2,R2,#1      ;; ++R2
          ADD     R4,R4,#-1     ;; --R4
          BRp     LOOP          ;; if R4>0 goto LOOP
NOLOOP    HALT
;; Parameters and results stored here
SIZE      .FILL   #7
VECT      .FILL   #-255
          .FILL   #2009
          .FILL   #201
          .FILL   x4567
          .FILL   xBEEF
          .FILL   x8000
          .FILL   #-1
VABS      .BLKW   7
          .END

Symbol table

Here's the symbol table, which now contains addresses of some instructions, in addition to data.

//Symbol Name		Page Address
//----------------	------------
//	LOOP                    3004
//	NMPOS                   3008
//	NOLOOP                  300D
//	SIZE                    300E
//	VABS                    3016
//	VECT                    300F

Symbol table

Here's the symbol table. It's rather boring; however, note the negative offset in the BRp  LOOP instruction. That instruction is located at x300C which means the PC will be x300D when it is executed. The target, LOOP, is located at x3004, so the offset will be x3004-x300D, or -9. In nine-bit two's-complement -9 is 111110111 and the instruction is encoded as 03F7.

(0000) 3000  0011000000000000 (   1)                 .ORIG x3000
(3000) E00E  1110000000001110 (   2)                 LEA   R0 VECT
(3001) E414  1110010000010100 (   3)                 LEA   R2 VABS
(3002) 280B  0010100000001011 (   4)                 LD    R4 SIZE
(3003) 0C09  0000110000001001 (   5)                 BRNZ  NOLOOP
(3004) 6200  0110001000000000 (   6) LOOP            LDR   R1 R0 #0
(3005) 0602  0000011000000010 (   7)                 BRZP  NMPOS
(3006) 927F  1001001001111111 (   8)                 NOT   R1 R1
(3007) 1261  0001001001100001 (   9)                 ADD   R1 R1 #1
(3008) 7280  0111001010000000 (  10) NMPOS           STR   R1 R2 #0
(3009) 1021  0001000000100001 (  11)                 ADD   R0 R0 #1
(300A) 14A1  0001010010100001 (  12)                 ADD   R2 R2 #1
(300B) 193F  0001100100111111 (  13)                 ADD   R4 R4 #-1
(300C) 03F7  0000001111110111 (  14)                 BRP   LOOP
(300D) F025  1111000000100101 (  15) NOLOOP          TRAP  x25
(300E) 0007  0000000000000111 (  17) SIZE            .FILL x0007
(300F) FF01  1111111100000001 (  18) VECT            .FILL xFF01
(3010) 07D9  0000011111011001 (  19)                 .FILL x07D9
(3011) 00C9  0000000011001001 (  20)                 .FILL x00C9
(3012) 4567  0100010101100111 (  21)                 .FILL x4567
(3013) BEEF  1011111011101111 (  22)                 .FILL xBEEF
(3014) 8000  1000000000000000 (  23)                 .FILL x8000
(3015) FFFF  1111111111111111 (  24)                 .FILL xFFFF
(3016) 0000  0000000000000000 (  26) VABS            .FILL x0000
(3017) 0000  0000000000000000 (  26)                 .FILL x0000
(3018) 0000  0000000000000000 (  26)                 .FILL x0000
(3019) 0000  0000000000000000 (  26)                 .FILL x0000
(301A) 0000  0000000000000000 (  26)                 .FILL x0000
(301B) 0000  0000000000000000 (  26)                 .FILL x0000
(301C) 0000  0000000000000000 (  26)                 .FILL x0000