I'm trying to print a binary number to the console using LC-3 assembly.
What I've tried so far includes (but isn't limited to):
binary .fill b10000110
lea r0, binary
puts ; prints garbage
ld r0, binary
out ; prints 0 (I know it only prints one character but I don't know why it chooses to print 0)
lea r1, binary
and r2, r2, #0
loop ldr r0, r1, r2
out
add r2, r2, #1
and r3, r3, #0
not r3, r2
add r3, r3, #1
add r3, r3, #8 ; I know all the binary numbers will be exactly 8 bits long
brz end
add r3, r3, #0 ; to be safe
brnzp loop
end
; more code...
None of this works particularly well. I'm pulling my hair out trying to figure out the proper way to do this, but everything I'm thinking of relies on binary
being a string, which I can't do.
In LC-3, the OUT
trap subroutine takes the value currently stored in register R0
, finds the corresponding ASCII value and outputs it to the console, while the PUT
trap subroutine takes the value stored in R0
as a memory, and iterates through all the data stored at that address, outputting each byte as ASCII to the console, until it finds a NULL character.
In the example you gave, PUTS
will print out the ASCII representation of b10000110
, followed by garbage until it happens to hit a NULL character whereas OUT
will simply print the ASCII representation of b10000110
.
Subsequently, to actually print a 0 or a 1, we must print the ASCII representation of those numbers, not the numbers themselves. So, we define two words, one for the ASCII character 0
, and the other for 1
.
ascii0 .fill x30
ascii1 .fill x31
So, for any 1-bit number, we can print it to the console with a simple if-else branch and the OUT
subroutine.
binary .fill b1
LD R1, binary
AND R0, R1, #1
BRnz else
LD R0, ascii1
BRnzp done
else LD R0, ascii0
done OUT
Now, we must extend this to n-bits. That is to say that we must break our n-bit number into a series of 1-bit numbers that we can easily print. To achieve this, all we need is a simple AND
and a mask for the ith bit (e.g. given the 8-bit number b10000110
, to determine the 3rd least significant bit, we would use the mask b00000100
). So, for an 8-bit number, we will need the sequence of masks b10000000
, b01000000
, ..., b00000001
. There are several ways to do this, such as starting with b10000000
and left-shifting/multiplying by 2 for each bit, however for the sake of simplicity we will use a lookup table.
masks .fill b10000000
.fill b01000000
.fill b00100000
.fill b00010000
.fill b00001000
.fill b00000100
.fill b00000010
.fill b00000001
To choose the mask before we print out each bit, we can use a simple for-loop branch.
AND R4, R4, #0 ;clears the register we will count with
LD R1, binary
LEA R2, masks ;finds the address in memory of the first mask
loop LDR R3, R2, #0 ;load the mask from the address stored in R2
ADD R2, R2, #1 ;next mask address
AND R0, R1, R3
;print out 1-bit number
ADD R4, R4, #1
ADD R0, R4, #-8 ;sets condition bit zero when R4 = 8
BRn loop ;loops if R4 < 8
Finally, we have our completed program.
.ORIG x3000
AND R4, R4, #0 ;clears the register we will count with
LD R1, binary
LEA R2, masks ;finds the address in memory of the first mask
loop LDR R3, R2, #0 ;load the mask from the address stored in R2
ADD R2, R2, #1 ;next mask address
AND R0, R1, R3
BRnz else
LD R0, ascii1
BRnzp done
else LD R0, ascii0
done OUT
ADD R4, R4, #1
ADD R0, R4, #-8 ;sets condition bit zero when R4 = 8
BRn loop ;loops if R4 < 8
HALT
masks .fill b10000000
.fill b01000000
.fill b00100000
.fill b00010000
.fill b00001000
.fill b00000100
.fill b00000010
.fill b00000001
ascii0 .fill x30
ascii1 .fill x31
binary .fill b10000110
.END