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.
.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
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
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
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
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 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
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
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