ARM Division by 10 Save remainder and quotient - arm

This is a question on homework from CS2400 MSU Denver
Hello,
I have a program that reads keys from the user until they have either entered a non HEX character or entered a max of 8 HEX characters. As keys are entered I maintain a sum of hex values being entered by the user by multiplying the sum register by 16 and adding the new hex value.
This part is all fine and dandy, no help needed. I am having trouble taking this final result, in HEX, and converting it to DEC. I know I need to divide by 10 only I don't know how I can accomplish this.
Please help me determine how to divide by 10 and save the quotient and remainder. Thanks.
AREA HW6, CODE
ENTRY
Divsor EQU 10
MAIN
MOV R1, #0 ; Clear register to be used as symbols received counter
MOV R2, #0 ; Clear register to be used as temp result
LDR R4, =DecStr ; Load address of DecStr
LDR R5, =TwosComp ; Load address of TwosComp
LDR R6, =RvsDecStr
BL READ_CHARS ; Read characters from the keyboard
BL TO_DECIMAL ; Is R2 negative ?
SWI 0x11
READ_CHARS
CMP R1, #8 ; Check if necessary to read another key
BEQ DONE_READ_CHAR ; User has entered 8 hex symbols
SWI 4 ; [R0] <--- Key from keyboard (ASCII)
CMP R0, #'0' ; Verify digit is valid
BLO DONE_READ_CHARS
CMP R0, #'9' ; Verify digit is valid
BHI CHECK_HEX
SUB R0, R0, #'0' ; Obtain Hex equivalent of ASCII char 0-9
B STORE_INPUT
CHECK_HEX
CMP R0, #'A'
BLO DONE_READ_CHARS ; Invalid Hex symbol
CMP R0, #'F'
BHI DONE_READ_CHARS ; Invalid Hex symbol
SUB R0, R0, #'A'
ADD R0, R0, #0xA ; Adding ten to receive Hex equivalent of ASCII A-F
STORE_INPUT
MOV R3, R2, LSL#4 ; *16
ADD R2, R3, R0 ; Add valid Hex symbol to temp result
ADD R1, R1, #1 ; Increase symbol's recieved counter
B READ_CHARS ; Get next key
DONE_READ_CHARS
MOV PC, LR ; Return to BL READ_CHARS ( MAIN )
TO_DECIMAL
TST R2, #2, 2
BEQ POSITIVE
STRB #'-', [R4], #1 ; Store - as first byte in DecStr
MVN R2, R2 ; [R2] <- 1's complement of R2
ADD R2, R2, #1 ; [R2] <- 2's complement of R2
POSITVE
STR R2, [R5] ; Store all entered hex values in memory at TwosComp
LDR R7, [R5] ; Initial quotient
udiv10
LDRB R7, [R5], #1 ; Load a byte of TwosComp
CMP R7, #0
BEQ DONE_TO_DECIMAL
DONE_TO_DECIMAL
MOV PC, LR
AREA data1, DATA
TwosComp
DCD 0
DecStr % 12
RvsDecStr
% 11
ALLIGN
END

You can do it by subtracting-and-shift like elementary division easily. There are also many division algorithms on this site and Google
How does one do integer (signed or unsigned) division on ARM?
Assembly mod algorithm on processor with no division operator
But if you only want to convert from hexadecimal to decimal then double dabble may fit your need. It converts number to packed BCD without any division

Related

Printing double digit numbers in LC-3

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.

How can I do this section of code, but using auto-indexing with ARM Assembly

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:

How to print the binary version of a value in an LC3(using Assembly language) using a loop and a string? [duplicate]

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

ARM Assembly error: A1898E: Target cannot be relocated. No suitable relocation exists for this instruction

I am trying to write simple program to sum up data in array, but I get this error
error: A1898E: Target cannot be relocated. No suitable relocation exists for this instruction
Here is my code
AREA SUMARRAY, CODE, READONLY
EXPORT __main
ENTRY
__main
LDR r2, LENGTH
SUB r2, r2, #1 ; r2 contains (LENGTH-1)
MOV r6, #0 ; r6 sum set to 0
FOR_INIT MOV r1, #0 ; r1 index I set to 0
ADR r3, ARRAY ; start r3 with address of A[0]
FOR_CMP CMP r1, r2 ; compare I and (LENGTH-1)
BGT END_FOR ; drop out of loop if I < (LENGTH-1)
LDR r4, [r3],#4 ; load r4 with A[I] then walk r3 down ARRAY
ADD r6, r6, r4 ; update sum with A[I]
ADD r1, r1, #1 ; increment I
B FOR_CMP ; loop back to for-loop check
END_FOR
STR r6, SUM ; store result in SUM
STOP B STOP
AREA my_data, DATA, READWRITE
ALIGN
SUM DCD 0XFFFFFFFF
ARRAY DCD 1,5,20,32,13,66,3,5,23,64,112,66,22
LENGTH DCD 13
Compiler says that problem is in this line
ADR r3, ARRAY ; start r3 with address of A[0]
What is wrong with this code ? Could someone explain why this error appears ?
And how can I fix it ?
Thanks.
UPDATE
AREA Sorting, CODE, READONLY
EXPORT __main
ENTRY
__main
LDR r2, LENGTH
SUB r2, r2, #1 ; r2 contains (LENGTH-1)
MOV r6, #0 ; r6 sum set to 0
FOR_INIT MOV r1, #0 ; r1 index I set to 0
LDR r3, =ARRAY ; start r3 with address of A[0]
FOR_CMP CMP r1, r2 ; compare I and (LENGTH-1)
BGT END_FOR ; drop out of loop if I < (LENGTH-1)
LDR r4, [r3],#4 ; load r4 with A[I] then walk r3 down ARRAY
ADD r6, r6, r4 ; update sum with A[I]
ADD r1, r1, #1 ; increment I
B FOR_CMP ; loop back to for-loop check
END_FOR
STR r6, SUM ; store result in SUM
STOP B STOP
LTORG
AREA my_data, DATA, READWRITE
ALIGN
SUM DCD 0XFFFFFFFF
ARRAY DCD 1,5,20,32,13,66,3,5,23,64,112,66,22
LENGTH DCD 13
But in this case I get errors
Error: L6286E: Relocation #REL:0 in sorting.o(SUMARRAY) with respect to LENGTH. Value(0x3fffff18) out of range(0 - 0xfff) for (R_ARM_LDR_PC_G0)
Error: L6286E: Relocation #REL:1 in sorting.o(SUMARRAY) with respect to SUM. Value(0x3ffffeb4) out of range(0 - 0xfff) for (R_ARM_LDR_PC_G0)
From the documentation for ADR:
If expression is program-relative, it must evaluate to an address in the same code area as the ADR pseudo-instruction. Otherwise the address may be out of range after linking.
So one way of fixing the problem could be to move ARRAY to the same code AREA as the ADR instruction that references ARRAY.
Another possible solution would be to use the pseudo-version of LDR to load the address. That is, instead of ADR r3,ARRAY, use LDR r3,=ARRAY (note the = sign).
This way you should be able to keep ARRAY in the data AREA. Note that you may have to place an LTORG directive after the end of your main function.

LC-3 Decimal to Binary Converter

I have to create a program that converts the given user input (decimal) into its binary match when the user presses enter or exit when they press X. Can anyone give a pointer on how to start this assignment. We are required to use masks and loops, but I do not know where to start with this.
I wrote this because there aren't a whole lot of examples of binary masks.
A binary mask is useful in assembly because it gives us the power to examine a single bit in a decimal or hex value.
Example:
If we look at the decimal number 5 we know its binary value is 0101, when we AND R4, R4, R5 on our first loop we compare 1000 with 0101. Since bit[3] is a zero in 0101 we then tell the LC3 simulator to print out an ASCII char "0". We then repeat the process with the next binary mask 0100.
This can be a tricky concept to get at first so I would first look at a few examples of loops and bit masks.
LC3 Bit Counter
How do I write a program that prints out “Hello World”, 5 times using a loop in LC3?
.ORIG x3000
LEA R0, PROMPT
PUTs ; TRAP x22
LD R0, ENTER
OUT ; TRAP x21
IN ; TRAP x23
AND R5, R5, #0 ; clear R5
ADD R5, R5, R0 ; Store the user input into R5
AND R1, R1, #0 ; clear R1, R1 is our loop count
LD R2, MASK_COUNT ; load our mask limit into R2
NOT R2, R2 ; Invert the bits in R2
ADD R2, R2, #1 ; because of 2's compliment we have
; to add 1 to R2 to get -4
WHILE_LOOP
ADD R3, R1, R2 ; Adding R1, and R2 to see if they'll
; will equal zero
BRz LOOP_END ; If R1+R2=0 then we've looped 4
; times and need to exit
LEA R3, BINARY ; load the first memory location
; in our binary mask array
ADD R3, R3, R1 ; use R1 as our array index and
; add that to the first array location
LDR R4, R3, #0 ; load the next binary mask into R4
AND R4, R4, R5 ; AND the user input with the
; binary mask
BRz NO_BIT
LD R0, ASCII_ONE
OUT ; TRAP x21
ADD R1, R1, #1 ; add one to our loop counter
BRnzp WHILE_LOOP ; loop again
NO_BIT
LD R0, ASCII_ZERO
OUT ; TRAP x21
ADD R1, R1, #1 ; add one to our loop counter
BRnzp WHILE_LOOP ; loop again
LOOP_END
LD R0, ENTER
OUT ; TRAP x21
HALT ; TRAP x25
; Binary Maps
BINARY .FILL b0000000000001000
.FILL b0000000000000100
.FILL b0000000000000010
.FILL b0000000000000001
.FILL b0000000000000000
; Stored Values
ENTER .FILL x000A
ASCII_ZERO .FILL x0030
ASCII_ONE .FILL x0031
MASK_COUNT .FILL x04 ; loop limit = 4
PROMPT .STRINGZ "Enter a number from 0-9"
.END

Resources