Sorry you are right I totally forgot. Okay so this program is done in assembly language it’s supposed to output the sum of three numbers but for example input: “1, 2, 3” I get the result of “ you entered 1, 2, 3 and the sum is 1993959032” I’m not sure where the problem isLdB wrote: ↑Wed Nov 21, 2018 3:55 amHow can we help you haven't told us what it does wrong.
It looks like it assumes it has access to the standard C libraries in some manner, I assume a linker command about which you said nothing.
I can see it is supposed to print a display message, take 3 numbers from a user and add them up and display result all using the standard C calls.
None of which will work in baremetal so you must be in a linux command window.
So what does it do?
Code: Select all
ldr r0, =response /*r0 contains pointer to response message*/
mov r1, r4 /*r1 contains pointer to value_read1*/ <<< check mov goes right to left
ldr r1, [r1] /*r1 contains value dereferenced from r1 in previous instruction*/
mov r2, r5 <<< check mov goes right to left
ldr r2, [r2]
mov r3, r6 <<<< check mov goes right to left
ldr r3, [r3]So just flip them Would it be the way to go? I’m sorry I feel like I been looking at it for so long I don’t understand what you saidLdB wrote: ↑Wed Nov 21, 2018 5:03 amI think you got 3 mov instructions wrong, pretty sure what is on right goes to left and I think you want the other way around.Code: Select all
ldr r0, =response /*r0 contains pointer to response message*/ mov r1, r4 /*r1 contains pointer to value_read1*/ <<< check mov goes right to left ldr r1, [r1] /*r1 contains value dereferenced from r1 in previous instruction*/ mov r2, r5 <<< check mov goes right to left ldr r2, [r2] mov r3, r6 <<<< check mov goes right to left ldr r3, [r3]
Code: Select all
mov r1, r4 /*r1 contains pointer to value_read1Code: Select all
mov r4, r1Code: Select all
mov r1, r4
ldr r1, [r1]
mov r2, r5
ldr r2, [r2]
mov r3, r6
ldr r3, [r3]
ldr r7, =sum
mov r8, r7
add r7, r1, r2
add r7, r7, r3
str r7, [r8]
Code: Select all
ldr r1, [r4]
ldr r2, [r5]
ldr r3, [r6]
ldr r8, =sum
add r7, r1, r2
add r7, r7, r3
str r7, [r8]
Push and pop do exist in the instruction set - they are real 16-bit instructions in T32 (thumb2).DavidS wrote: ↑Fri Nov 23, 2018 3:58 pmOk I saw you saved the lr on the stack. Do stack ops using the actual instructions (usually LDMFD and STMFD), it will make your code more readable than using a faked op like POP that does not actually exist in the instruction set (and I would bet is implemented as LDMFD).
LOLjahboater wrote: ↑Fri Nov 23, 2018 7:29 pmPush and pop do exist in the instruction set - they are real 16-bit instructions in T32 (thumb2).DavidS wrote: ↑Fri Nov 23, 2018 3:58 pmOk I saw you saved the lr on the stack. Do stack ops using the actual instructions (usually LDMFD and STMFD), it will make your code more readable than using a faked op like POP that does not actually exist in the instruction set (and I would bet is implemented as LDMFD).
Please see page F5.1.138 in the ARMv8 ARM.
They are also, as you say, aliases in A32, as are VPUSH/VPOP.
The idea behind this is that the same assembler code works for thumb2 and for normal ARM.
If you use LDMFD/STMFD it will not work in thumb2 mode.
If you use push and pop, your code will work in both modes.
The compiler does things like this all the time:
push {r4, r5, r6, r7, r8, r9, lr}
......
pop {r4, r5, r6, r7, r8, r9, pc}
which does the mov r15,r14 (or more readably "mov pc,lr") implicitly (pretty cool IMHO!)
Quite a lot of ARM instructions are aliases.
There is, surprisingly, no multiply instruction for aarch64. There is instead a multiply-add combined instruction (same as "mla" in A32). So MUL is an alias for MADD rd,rn,rm,xzr (or wzr. Adding from the zero register has no effect of course).
Code: Select all
R0 = a1 ;Normal use registers are a.
R1 = a2
R2 = a3
R3 = a4
R4 = v1 ;v regs must be preserved.
R5 = v2
R6 = v3
R7 = v4
R8 = v5
R9 = v6
R10 = sl ;Stack limit.
R11 = fp ;Frame Pointer.
R12 = ip
R13 = sp
R14 = lr
R15 = pc
That's very interesting.DavidS wrote: ↑Fri Nov 23, 2018 8:17 pmSo you are a fan of the APCS convention for register naming? I never liked it because it does not highlight that all the registers are accessable, including R15/PC and R14/LR. Though I also was using ARM before that really caught on. For you if you use APCS reg names, they are (traditional name on let, APCS name on right):
Yes, the compiler probably doesn't care (much) how readable the assembler is, it just emits standard ARM assembler regardless.
As jahboater says, PUSH and POP are instructions. Ever since ARM brought in UAL (Unified Assember Language) to replace the individual ARM and THUMB assembler languages back in 2012-ish they have been the recommended instructions to use.jahboater wrote: ↑Fri Nov 23, 2018 7:29 pmPush and pop do exist in the instruction set - they are real 16-bit instructions in T32 (thumb2).DavidS wrote: ↑Fri Nov 23, 2018 3:58 pmOk I saw you saved the lr on the stack. Do stack ops using the actual instructions (usually LDMFD and STMFD), it will make your code more readable than using a faked op like POP that does not actually exist in the instruction set (and I would bet is implemented as LDMFD).
Please see page F5.1.138 in the ARMv8 ARM.
They are also, as you say, aliases in A32, as are VPUSH/VPOP.
The listing for POP (multiple registers) says :-The PUSH and POP instructions assume a full descending stack. They are the preferred synonyms for STMDB and LDM with writeback.
Code: Select all
A1 variant
POP{<c>}{<q>} <registers>
is equivalent to
LDM{<c>}{<q>} SP!, <registers>
and is the preferred disassembly when BitCount(register_list) > 1
Code: Select all
LDR{<c>}{<q>} <Rd>, [SP], #4
I did not realize that restricting yourself to calling only R13 the SP is more readable. What if I have code that uses R9 for the stack (which sometimes I do to avoid using the stack pointed to by R13 without having to save the value of R13). In ARM R13 has no special meaning over R0-R12 inclusive. Ok I will admit that I can see the reasoning for giving special names to LR and PC, that is commonly done even pre-APCS.jahboater wrote: ↑Fri Nov 23, 2018 8:36 pmThat's very interesting.DavidS wrote: ↑Fri Nov 23, 2018 8:17 pmSo you are a fan of the APCS convention for register naming? I never liked it because it does not highlight that all the registers are accessable, including R15/PC and R14/LR. Though I also was using ARM before that really caught on. For you if you use APCS reg names, they are (traditional name on let, APCS name on right):
UAL seems to be a mixture: r1 r2 r3 etc, then it uses ip, sp, lr and pc.
I prefer r1, r2, r3 etc for the general purpose registers (much nicer than aarch64 with its x1, x2, x3 (or w1, w2, s3)).
But I like pc, lr, sp because the reader can easily see what they do.
I don't like "ip" which I thought should mean "Instruction Pointer" same as the PC
Compilers don't use frame pointers any more - it free's up a register.
Misleading though is it not?Yes, the compiler probably doesn't care (much) how readable the assembler is, it just emits standard ARM assembler regardless.
If you have an instruction in one mode and not in another, one of then has to be an alias, there is no choice if you want your code to work in both modes unchanged - which is the whole point of UAL.
You can still do that of course. You can implement you own stack with any register you like.
Assembler and command line not specified. I know that they are needed for gas without any extra command line options, they are needed for AsAsm and ObjAsm always, they are needed for Asm, and these are the most commonly used ARM Assemblers (excepting the BASIC assembler, though that is not linkable output).
gas syntax isn't hard to read, maybe if you aren't used to it but horses for courses, saying it's ugly and barely readable is a bit of an overstatement.gas was primarily meant for assembling the output of gcc rather than for use as a standalone assembler anyway.
Code: Select all
AREA |main|, CODE, READONLY
ENTRY main
IMPORT scanf, printf
main
STMFD R13!, {R14}
ADR R4, value_read1 ;No reason to use higher registers.
ADR R5, value_read2 ; APCS only garenties R1-R5 be preserved by called.
ADR R6, value_read3
ADR R0, prompt ;printf (prompt);
BL printf ;Using c lib puts fits better.
ADR R0, pattern ;scanf(pattern, value_read1, value_read2, value_read3
MOV R1,R4
MOV R2,R5
MOV R3,R6
BL scanf
next ;unused label.
ADR R0, responce
MOV R1, R4 ;Redundant instruction.
LDR R1, [R1]
MOV R2,R5 ;Redundant instruction.
LDR R2, [R2]
MOV R3, R6 ;Redundant instruction.
LDR R3, [R3]
ADR R3, sum
ADD R7, R1, R2
ADD R7, R7, R3
STR R7, [R8] ;R8 has not been loaded with any address.
;Did you mean to say STR R7, sum?
BL printf
MOV R0, #0
LDMFD R13!, {R15} ;Return with exit code 0.
value_read1 DCW 0
value_read2 DCW 0
value_read3 DCW 0
sum DCW 0
prompt
DCB "Enter three integers (seperated by a space)", 0
response
DCB "You entered %d, %d, %d and the sum is %d\n"
pattern
DCB "%d %d %d"
Code: Select all
AREA |main|, CODE, READONLY
ENTRY main
IMPORT scanf, printf, puts
main
STMFD R13!, {R14}
ADR R1, val1 ;Load poitners to value storage
ADR R2, val2
ADR R3, val3
ADR R0, prmpt ;puts(prmpt);
BL puts
ADR R0, patt ;scanf("The sum of %d + %d + %d is: %d\n", &val1, &val2, &val3);
BL scanf
LDR R1,[R1] ;R4 = val1 + val2 + val3
LDR R2,[R2]
ADD R4,R1,R2
LDR R3,[R3]
ADD R4,R4,R3
ADR R0, reply
BL printf ;printf("The sum of %d + %d + %d is: %d\n", val1, val2, val3, val1+val2+val3);
MOV R0,#0
LDMFD R13!, {R15}
val1 DCW 0 ;Storage space for our integers.
val2 DCW 0
val3 DCW 0
;Strings for scanf and printf.
prmpt DCB "Enter 3 space seperated values"
patt DCB "%d %d %d"
reply DCB "The sum of %d + %d + %d is: %d\n"
Erm, R8 was set correctly...
Code: Select all
ldr r7, =sum
mov r8, r7
Your "corrected" version is likely to fail, AAPCS says that R0 to R4 are not preserved across a function call (they might be but you cannot expect it) so using the previous values of R1, R2 and R3 after the branches to either puts or scanf is a segfault waiting to happen.DavidS wrote: ↑Fri Nov 23, 2018 10:44 pmThe OP's version cleaned up, translated to ObjAsm (for easier reading), and with my comments added:A corrected version, without as many redundant instructions:Code: Select all
AREA |main|, CODE, READONLY ENTRY main IMPORT scanf, printf main STMFD R13!, {R14} ADR R4, value_read1 ;No reason to use higher registers. ADR R5, value_read2 ; APCS only garenties R1-R5 be preserved by called. ADR R6, value_read3 ADR R0, prompt ;printf (prompt); BL printf ;Using c lib puts fits better. ADR R0, pattern ;scanf(pattern, value_read1, value_read2, value_read3 MOV R1,R4 MOV R2,R5 MOV R3,R6 BL scanf next ;unused label. ADR R0, responce MOV R1, R4 ;Redundant instruction. LDR R1, [R1] MOV R2,R5 ;Redundant instruction. LDR R2, [R2] MOV R3, R6 ;Redundant instruction. LDR R3, [R3] ADR R3, sum ADD R7, R1, R2 ADD R7, R7, R3 STR R7, [R8] ;R8 has not been loaded with any address. ;Did you mean to say STR R7, sum? BL printf MOV R0, #0 LDMFD R13!, {R15} ;Return with exit code 0. value_read1 DCW 0 value_read2 DCW 0 value_read3 DCW 0 sum DCW 0 prompt DCB "Enter three integers (seperated by a space)", 0 response DCB "You entered %d, %d, %d and the sum is %d\n" pattern DCB "%d %d %d"
This assembles with AsAsm on Linux (or RISC OS) and can be linked with ld.Code: Select all
AREA |main|, CODE, READONLY ENTRY main IMPORT scanf, printf, puts main STMFD R13!, {R14} ADR R1, val1 ;Load poitners to value storage ADR R2, val2 ADR R3, val3 ADR R0, prmpt ;puts(prmpt); BL puts ADR R0, patt ;scanf("The sum of %d + %d + %d is: %d\n", &val1, &val2, &val3); BL scanf LDR R1,[R1] ;R4 = val1 + val2 + val3 LDR R2,[R2] ADD R4,R1,R2 LDR R3,[R3] ADD R4,R4,R3 ADR R0, reply BL printf ;printf("The sum of %d + %d + %d is: %d\n", val1, val2, val3, val1+val2+val3); MOV R0,#0 LDMFD R13!, {R15} val1 DCW 0 ;Storage space for our integers. val2 DCW 0 val3 DCW 0 ;Strings for scanf and printf. prmpt DCB "Enter 3 space seperated values" patt DCB "%d %d %d" reply DCB "The sum of %d + %d + %d is: %d\n"
Far from optimal, though in keeping with doing everything the same effective way as the OP, it is pretty decent.
Code: Select all
bl printf /*call printf to output response message*/
Code: Select all
push {r7}
bl printf /*call printf to output response message*/
pop {r7}
Code: Select all
pi@rpi3:~/Programming/asm $ gcc sum.s -o sum
pi@rpi3:~/Programming/asm $ ./sum
Enter three integers (separated by a space): 1 2 3
You entered 1, 2, 3, and the sum is 6
pi@rpi3:~/Programming/asm $ gcc sum2.s -o sum2
pi@rpi3:~/Programming/asm $ ./sum2
Enter three integers (separated by a space): 1 2 3
Segmentation fault
That is the opisite of what APCS says, I am looking at the document as I type this. APCS (which is what is used in RISC OS, and was used in ARM Linux 19 years ago [ok ARM Linux was brand new at the time], and was the standard) says that R1-R3 must be preserved across, while R4-R9 are subject to being overwritten by called procedures. And generally you are safe to use R4 in 99% of cases (experience not the standard).Paeryn wrote: ↑Fri Nov 23, 2018 11:08 pm
Your "corrected" version is likely to fail, AAPCS says that R0 to R4 are not preserved across a function call (they might be but you cannot expect it) so using the previous values of R1, R2 and R3 after the branches to either puts or scanf is a segfault waiting to happen.
Interesting change in the standard, making note of it. I will definitely have to look up the newer standard so I can get it correct when talking to Linux/BSD users. I admit I am accustomed to RISC OS, and the way it does things.The reason for the OP seeing the wrong sum when printed is because printf was never passed it, sum is the fifth parameter to printf so must be pushed onto the stack. Printf will use whatever it found there, if the caller never pushed it on then whatever value happened to be there will be printed. Your version doesn't push the value on either.
As I posted on the 21st, just changingtoCode: Select all
bl printf /*call printf to output response message*/makes it work.Code: Select all
push {r7} bl printf /*call printf to output response message*/ pop {r7}
Looking at the APCS info on Arm's website lists r0-r3 as argument / scratch / result and r4-r8 as preserved, that is from 1997/8 so 20 years ago at least. This is what that 1997/8 document saidThe first four registers r0-r3 (a1-a4) are used to pass argument values into a subroutine and to return a result value from a function. They may also be used to hold intermediate values within a routine (but, in general, only between subroutine calls).
A subroutine must preserve the contents of the registers r4-r8, r10, r11 and SP (and r9 in PCS variants that designate r9 as v6).
a1-a4, [f0-f3]
These are used to pass arguments to functions. a1 is also used to return integer results, and f0 to return FP results. These registers can be corrupted by a called function.
v1-v8, [f4-f7]
These are used as register variables. They must be preserved by called functions.