Been learning about LC-3 lately and was wondering how do I go about printing a number that's bigger then 9? In this program I made it asks for width and length and multiplies the two to get the area of the shape. My problem is any output bigger then 9 it starts printing a letter or a number not close to what I wanted. How should I go about doing this? my code:
.ORIG x3000
; Reset Registers
AND R0, R0, #0
AND R1, R1, #0
AND R2, R2, #0
AND R3, R3, #0
AND R4, R4, #0
AND R5, R5, #0
AND R6, R6, #0
AND R7, R7, #0
LEA R0, numberone
PUTS
GETC
OUT
LD R3, HEXN30
ADD R0, R0, R3
ADD R1, R0, #0
LEA R0, numbertwo
PUTS
GETC
OUT
ADD R0, R0, R3
ADD R6, R0, #0
LOOP
ADD R2, R2, R1
ADD R6, R6, #-1
BRp LOOP
LEA R0, MESG
PUTS
ADD R0, R2, x0
LD R2, NEG_TEN
ADD R2, R2, R0
BRn JUMP
AND R4, R4, #0
ADD R4, R4, R2
LD R0, ASCII_1
OUT
AND R0, R0, #0
ADD R0, R0, R4
JUMP
LD R3, HEX30 ;add 30 to integer to get integer character
ADD R0, R0, R3
OUT
HALT ;{TRAP 25}
numberone .stringz "\nPlease enter the length: "
numbertwo .stringz "\nPlease enter the width: "
MESG .STRINGZ "\n\nThe Area of the Rectangle is: "
HEXN30 .FILL xFFD0 ; -30 HEX
HEX30 .FILL x0030 ; 30 HEX
NEG_TEN .FILL #-10
ASCII_1 .FILL x0031 ; ASCII char '1'
.END
Example Output:
Please enter the length: 4
Please enter the width: 5
The area of the object is: 20
This code will always print 1:
LD R0, ASCII_1
OUT
There's no chance for it to print a 2 like you'd want from 4 times 5.
The next character prints as : because you have 10 in R4, as you haven't subtracted 10 enough times in the division by repetitive subtraction. You've only subtracted 10 once and 20 needs to have 10 subtracted twice (to get 0 as the remainder).
You should be able to see the first problem by simply reading the code there. How could loading an ascii 1 and printing it do anything other than print a 1? So there is some missing code there.
The latter problem is a form of off-by-one error (looping one to few iterations), which is something you should be looking for during single-step debugging. You want your division by repetitive subtraction to leave you with a remainder between 0 and 9 — definitely not 10!
Boundary conditions are very error prone. Using < when it should be <= (or vice vesa), for example, can result in an off by one error. What we do is try to use idioms to avoid these problems (like for(int i = 0; i < n; i++)), but when an idiom isn't applicable, then you want to be suspicious of and test those boundary conditions: at the boundary, at one less, at one more.
Related
Having trouble printing any number past 10 in my LC3 program to multiply 2 input numbers to get a area. It works for any numbers below 10 so I know my multiplication is right. The problem is it produces weird symbols or nonsense anywhere higher then 10. So confused why. Here is my code:
.ORIG x3000
AND R3, R3, #0
AND R4, R4, #0
LD R5, INVERSE_ASCII_OFFSET
LD R6, DECIMAL_OFFSET
;---------------------
;receiving length
LEA R0, PROMPT1 ;load the address of the 'PROMPT1' message string
PUTS ;Prints PROMPT1
GETC ;get length
ADD R1, R0, #0
ADD R1, R1, R5
;receving width
LEA R0, PROMPT2 ;load the address of the 'PROMPT2' message string
PUTS ;Prints PROMPT2
GETC ;get width
ADD R2, R0, #0
ADD R2, R2, R5
;----------------------
;MULTIPLICATION to find AREA
ADD R4, R2, #0
FINDAREA:
ADD R3, R3, R1
ADD R4, R4, #-1
BRp FINDAREA
LEA R0, PROMPT3
PUTS
ADD R0, R3, R6
OUT
HALT
PROMPT1 .STRINGZ "ENTER LENGTH OF THE RECTANGLE:"
PROMPT2 .STRINGZ "ENTER WIDTH OF THE RECTANGLE:"
PROMPT3 .STRINGZ "AREA OF THE RECTANGLE:"
INVERSE_ASCII_OFFSET .fill xFFD0
DECIMAL_OFFSET .fill #48
.END```
this works, but I have to do it using auto-indexing and I can not figure out that part.
writeloop:
cmp r0, #10
beq writedone
ldr r1, =array1
lsl r2, r0, #2
add r2, r1, r2
str r2, [r2]
add r0, r0, #1
b writeloop
and for data I have
.balign 4
array1: skip 40
What I had tried was this, and yes I know it is probably a poor attempt but I am new to this and do not understand
ldr r1, =array1
writeloop:
cmp r0, #10
beq writedone
ldr r2, [r1], #4
str r2, [r2]
add r0, r0, #1
b writeloop
It says segmentation fault when I try this. What is wrong? What I am thinking should happen is every time it loops through, it sets the element r2 it at = to the address of itself, and then increments to the next element and does the same thing
The ARM architechures gives several different address modes.
From ARM946E-S product overview and many other sources:
Load and store instructions have three primary addressing modes
- offset
- pre-indexed
- post-indexed.
They are formed by adding or subtracting an immediate or register-based offset to or from a base register. Register-based offsets can also be scaled with shift operations. Pre-indexed and post-indexed addressing modes update the base register with the base plus offset calculation. As the PC is a general purpose register, a 32‑bit value can be loaded directly into the PC to perform a jump to any address in the 4GB memory space.
As well, they support write back or updating of the register, hence the reason for pre-indexed and post-indexed. Post-index doesn't make much sense without write back.
Now to your issue, I believe that you want to write the values 0-9 to an array of ten words (length four bytes). Assuming this, you can use indexing and update the value via add. This leads to,
mov r0, #0 ; start value
ldr r1, =array1 ; array pointer
writeloop:
cmp r0, #10
beq writedone
str r0, [r1, r0, lsl #2] ; index with r1 base by r0 scaled by *4
add r0, r0, #1
b writeloop
writedone:
; code to jump somewhere else and not execute data.
.balign 4
array1: skip 40
For interest a more efficient loop can be done by counting and writing down,
mov r0, #9 ; start value
ldr r1, =array1 ; array pointer
writeloop:
str r0, [r1, r0, lsl #2] ; index with r1 base by r0 scaled by *4
subs r0, r0, #1
bne writeloop
Your original example was writing the pointer to the array; often referred to as 'value equals address'. If this is what you want,
ldr r0, =array_end ; finished?
ldr r1, =array1 ; array pointer
write_loop:
str r1, [r1], #4 ; add four and update after storing
cmp r0, r1
bne write_loop
; code to jump somewhere else and not execute data.
.balign 4
array1: skip 40
array_end:
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
Enter grid size (1-9):3
*
**
***
Here's the code i have so far:
.ORIG x3000
LEA R0, PRINT
LEA R1, MEMSPAC
PUTS
GETC
PUTC
M STR R0, R1, #0
N .FILL M
LD R1, N
NOT R1, R1
ADD R1, R1, #1 ; R1 = -N
AND R2, R2, #0 ; R2 = holds number of *'s to be printed
LOOPA LEA R0, NEWLN
PUTS
ADD R3, R2, R1 ; while (R2 < N)
BRzp LOOPB
ADD R5, R5, #1 ;
ADD R4, R4, #1
LOOPC LD R0, STAR ; R0 = *
OUT ; Write *
ADD R5, R5, #-1
BRp LOOPC
ADD R5, R4, #0
ADD R2, R2, #1 ;
BRnzp LOOPA
LOOPB
LEA R0, NEWLN
PUTS
STOP HALT
STAR .FILL x2A
RIP TRAP x25
PRINT .STRINGZ "Enter grid size (1-9):"
NEWLN .STRINGZ "\n"
MEMSPAC .blkw 100
.END
This code works and it prints the triangle. However it goes into an infinite loop rather than printing grid size of 3. If i change the code "N .FILL M" to "N .FILL 3" it will print out grid size of 3. Any help would be greatly appreciated. Thanks
I have an array of size 10 that takes character input from a user. Now I just need to loop through the array and print out each character followed by a new line but I don't know where to start. LC-3 assembly is not my forte...
Here is my code so far:
LD R2, COUNTER
LEA R1, ARRAY
LD R4, COUNTER2
DO_WHILE_LOOP
GETC
STR R0, R1, #0
ADD R1, R1, #1
ADD R2, R2, #-1
BRp DO_WHILE_LOOP
END_DO_WHILE_LOOP
LEA R3, ARRAY
OUT_LOOP
END_OUT_LOOP
HALT
;Local Data
ARRAY .BLKW #10
COUNTER .FILL #10
NEWLINE .STRINGZ "\n"
COUNTER2 .FILL #10
.END
My question is basically what do i put inside the OUT_LOOP?
Well there are a number of different ways to do this. When you're printing the characters back to the user do you have to do it after you've received all of the user's input?
You could just print back to the user while they are typing:
DO_WHILE_LOOP
GETC
OUT
STR R0, R1, #0
ADD R1, R1, #1
ADD R2, R2, #-1
BRp DO_WHILE_LOOP
END_DO_WHILE_LOOP
If you have to print after collecting all of the user's input then you can just load the char array's memory location into R0 and then use the PUTs command.
Example:
LEA R0, ARRAY ; Load the ARRAY memory location into R0
PUTs ; Display all of the characters until it runs into null