Gabgorozco
Posts: 14
Joined: Wed Oct 10, 2018 6:08 pm

Help with this assembly code

Wed Nov 21, 2018 3:20 am

Hello can anyone help me with my code here. I’m not sure what I’m doing wrong! Thanks!


.section .data
/*Prompt message*/
prompt: .asciz "Enter three integers (separated by a space): "

/*Response message*/
response: .asciz "You entered %d, %d, %d, and the sum is %d\n"

/*Format pattern for scanf*/
pattern: .asciz "%d %d %d"

/*the numbers read and the sum*/
value_read1: .word 0
value_read2: .word 0
value_read3: .word 0
sum: .word 0

.section .text
.global main
main:
push {lr} /*save return address*/

//r4 holds address of value_read1, r5 holds that of 2, r6 holds 3
ldr r4, =value_read1
ldr r5, =value_read2
ldr r6, =value_read3

ldr r0, =prompt /*r0 contains pointer to the prompt message*/
bl printf /*call printf to output the prompt message*/

ldr r0, =pattern /*r0 contains pointer to format string for the scan pattern*/
mov r1, r4 /*r1 contains pointer to variable label where the first number is stored*/
mov r2, r5 /*r2 ... second ...*/
mov r3, r6 /*r3 ...third...*/
bl scanf /*call to scanf*/
next:
ldr r0, =response /*r0 contains pointer to response message*/
mov r1, r4 /*r1 contains pointer to value_read1*/
ldr r1, [r1] /*r1 contains value dereferenced from r1 in previous instruction*/
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]

bl printf /*call printf to output response message*/

mov r0, #0 /*exit code 0 = program terminates normally*/
pop {pc} /*exit the main function*/

LdB
Posts: 1099
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help with this assembly code

Wed Nov 21, 2018 3:55 am

How 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?

Gabgorozco
Posts: 14
Joined: Wed Oct 10, 2018 6:08 pm

Re: Help with this assembly code

Wed Nov 21, 2018 4:22 am

LdB wrote:
Wed Nov 21, 2018 3:55 am
How 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?
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 is

LdB
Posts: 1099
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help with this assembly code

Wed Nov 21, 2018 5:03 am

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

Gabgorozco
Posts: 14
Joined: Wed Oct 10, 2018 6:08 pm

Re: Help with this assembly code

Wed Nov 21, 2018 5:13 am

LdB wrote:
Wed Nov 21, 2018 5:03 am

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]
I 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.
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 said

LdB
Posts: 1099
Joined: Wed Dec 07, 2016 2:29 pm

Re: Help with this assembly code

Wed Nov 21, 2018 5:50 am

http://www.keil.com/support/man/docs/ar ... 878994.htm
Its
MOV Destination register, Source Register

So

Code: Select all

mov r1, r4 /*r1 contains pointer to value_read1
That writes R4 into R1 .. so if R1 contains your answer or a pointer to it you just killed the answer

Code: Select all

mov r4, r1
That writes R1 into R4 .. which is what i assume you want given r1 has the result pointer

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Help with this assembly code

Wed Nov 21, 2018 8:54 pm

They were the right way around to start with. Initially R4 holds the address of the first variable but it needs to be copied to R1 for the scanf call, same for the R5 and R6.

What looks to be wrong is the result (sum) which is in R7 at the end needs to be pushed onto the stack otherwise the printf call will use whatever random value is on the top of the stack as the sum. (Assuming normal AAPCS rules, R0 to R3 for the first four parameters then any more have to be pushed on to the stack).

The code has unnecessary instructions where you load a value into a register just to move it into another,

Code: 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]
Can be condensed to

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]
She who travels light — forgot something.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 3:55 pm

I do not see a declaration for scanf or printf as externs, both of which your code calls.

Also please avoid fake names like pop no such instruction in 32-bit ARM. Also what are you poping off of the stack to start with? To return from your function you jsut need to move lr (R14) into pc (r15) so you end up using a MOV R15, R14 instruction to return.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 3:58 pm

Ok 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).
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

jahboater
Posts: 4173
Joined: Wed Feb 04, 2015 6:38 pm

Re: Help with this assembly code

Fri Nov 23, 2018 7:29 pm

DavidS wrote:
Fri Nov 23, 2018 3:58 pm
Ok 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).
Push and pop do exist in the instruction set - they are real 16-bit instructions in T32 (thumb2).
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. Instead the multiply-add combined instruction (same as "mla" in A32) is used.
So MUL is an alias for MADD rd,rn,rm,xzr (or wzr. Adding from the zero register has no effect of course).

Finally, I think it is a matter of personal opinion which is the most readable. I bet a poll would show the official ARM push/pop is preferred by more people.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 8:17 pm

jahboater wrote:
Fri Nov 23, 2018 7:29 pm
DavidS wrote:
Fri Nov 23, 2018 3:58 pm
Ok 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).
Push and pop do exist in the instruction set - they are real 16-bit instructions in T32 (thumb2).
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).
LOL
The combined add and multiply has been there ever since the ARMv2 way back in 1985, and is still there in 32-bit ARM (what some now call AARCH32). It is called MLA.

I did not realize that it was supposed to be thumb code, I thought it was ARM code (yes they run on the same CPU, still a distenction). For ARM code aliases make code less readable, the output of a compiler is not supposed to be easily human readable.

So 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):

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
And this also promotes using ONLY R13 for the stack (usefull for a calling convention, though limiting otherwise). There is no good reason not to have multiple stacks (in fact there are good reasons to have multiple stacks). And stack ops in ARM mode work equally well with any base register.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

jahboater
Posts: 4173
Joined: Wed Feb 04, 2015 6:38 pm

Re: Help with this assembly code

Fri Nov 23, 2018 8:36 pm

DavidS wrote:
Fri Nov 23, 2018 8:17 pm
So 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):
That's very interesting.
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.
DavidS wrote:
Fri Nov 23, 2018 8:17 pm
I did not realize that it was supposed to be thumb code, I thought it was ARM code (yes they run on the same CPU, still a distenction). For ARM code aliases make code less readable, the output of a compiler is not supposed to be easily human readable.
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.
Last edited by jahboater on Fri Nov 23, 2018 8:48 pm, edited 1 time in total.

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Help with this assembly code

Fri Nov 23, 2018 8:48 pm

jahboater wrote:
Fri Nov 23, 2018 7:29 pm
DavidS wrote:
Fri Nov 23, 2018 3:58 pm
Ok 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).
Push and pop do exist in the instruction set - they are real 16-bit instructions in T32 (thumb2).
Please see page F5.1.138 in the ARMv8 ARM.
They are also, as you say, aliases in A32, as are VPUSH/VPOP.
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.

To quote directly from Arm's documentation (2010-2013) :-
The PUSH and POP instructions assume a full descending stack. They are the preferred synonyms for STMDB and LDM with writeback.
The listing for POP (multiple registers) says :-

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
Similarly for single a register where it is equivalent to

Code: Select all

LDR{<c>}{<q>} <Rd>, [SP], #4
She who travels light — forgot something.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 9:03 pm

jahboater wrote:
Fri Nov 23, 2018 8:36 pm
DavidS wrote:
Fri Nov 23, 2018 8:17 pm
So 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):
That's very interesting.
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.
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.
DavidS wrote:
Fri Nov 23, 2018 8:17 pm
I did not realize that it was supposed to be thumb code, I thought it was ARM code (yes they run on the same CPU, still a distenction). For ARM code aliases make code less readable, the output of a compiler is not supposed to be easily human readable.
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.
Misleading though is it not?

If I am using a call+parameter stack in R9 for example, which is perfectly allowable on ARM, and I attempt to use the PUSH/POP aliases (which do not specify the base register) then I am going to be using the wrong stack.

Just because some things can not be done in thumb16 does not mean we should limit how we do it in ARM.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 9:13 pm

Though back on topic.

I think that at very least the OP neads to add the extern directives for printf/scanf as they use both.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

jahboater
Posts: 4173
Joined: Wed Feb 04, 2015 6:38 pm

Re: Help with this assembly code

Fri Nov 23, 2018 9:18 pm

DavidS wrote:
Fri Nov 23, 2018 9:03 pm
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).
You can still do that of course. You can implement you own stack with any register you like.

The point is that 99.999% of human programmers and all compilers will use r13 for the stack.
Surely "sp" reads better as "stack pointer" than "r13" does? It does to me anyway.

You wont like aarch64, the stack is accessed as register 31 for some instructions only and is very restricted :(

Did you know that IBM mainframes had 16 registers and R14 was the link register? I strongly suspect that's where ARM got the idea from!

jahboater
Posts: 4173
Joined: Wed Feb 04, 2015 6:38 pm

Re: Help with this assembly code

Fri Nov 23, 2018 9:21 pm

DavidS wrote:
Fri Nov 23, 2018 9:13 pm
Though back on topic.

I think that at very least the OP neads to add the extern directives for printf/scanf as they use both.
Yes, you probably do for some assemblers.
But UAL doesn't appear to need them as far as I can see.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 9:27 pm

jahboater wrote:
Fri Nov 23, 2018 9:21 pm
DavidS wrote:
Fri Nov 23, 2018 9:13 pm
Though back on topic.

I think that at very least the OP neads to add the extern directives for printf/scanf as they use both.
Yes, you probably do for some assemblers.
But UAL doesn't appear to need them as far as I can see.
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).

Thus my responce. I have found that if omited with gas the link symbols are not produced, and the the identifiers are NULL value, which causes a failure. And that ugly barely human readable syntax looks like gas to me.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Help with this assembly code

Fri Nov 23, 2018 10:12 pm

DavidS wrote:
Fri Nov 23, 2018 9:27 pm
I have found that if omited with gas the link symbols are not produced, and the the identifiers are NULL value, which causes a failure. And that ugly barely human readable syntax looks like gas to me.
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.
She who travels light — forgot something.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 10:44 pm

I found the OP's mistake, he was attempting to store a value to the address in R8, and never set R8.

The OP's version cleaned up, translated to ObjAsm (for easier reading), and with my comments added:

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"

A corrected version, without as many redundant instructions:

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"
This assembles with AsAsm on Linux (or RISC OS) and can be linked with ld.

Far from optimal, though in keeping with doing everything the same effective way as the OP, it is pretty decent.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Help with this assembly code

Fri Nov 23, 2018 11:08 pm

DavidS wrote:
Fri Nov 23, 2018 10:44 pm
I found the OP's mistake, he was attempting to store a value to the address in R8, and never set R8.
Erm, R8 was set correctly...

Code: Select all

ldr r7, =sum
mov r8, r7
R7 is set to the address of sum which is then copied to R8.
DavidS wrote:
Fri Nov 23, 2018 10:44 pm
The OP's version cleaned up, translated to ObjAsm (for easier reading), and with my comments added:

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"

A corrected version, without as many redundant instructions:

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"
This assembles with AsAsm on Linux (or RISC OS) and can be linked with ld.

Far from optimal, though in keeping with doing everything the same effective way as the OP, it is pretty decent.
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.

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 changing

Code: Select all

    bl printf /*call printf to output response message*/
to

Code: Select all

    push {r7}
    bl printf /*call printf to output response message*/
    pop {r7}
makes it work.

My simple modification is sum.s, your code is sum2.s ...

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
Checking with gdb for your code, on returning from puts() R1 has the value 0 (using standard gcc on Raspbian) so the segfault happens the moment scanf() tries writing the first number to address 0.
She who travels light — forgot something.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Fri Nov 23, 2018 11:30 pm

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

I will also double check the errata on APCS as there may have been an error in the original doc, AND going only from memory after my stroke is not always working out.
As I posted on the 21st, just changing

Code: Select all

    bl printf /*call printf to output response message*/
to

Code: Select all

    push {r7}
    bl printf /*call printf to output response message*/
    pop {r7}
makes it work.
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Help with this assembly code

Fri Nov 23, 2018 11:56 pm

The AAPCS (yes there are two As, APCS was replaced by ATPCS which was then replaced by AAPCS) has this to say
The 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).
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 said
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.
She who travels light — forgot something.

User avatar
DavidS
Posts: 3800
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

Re: Help with this assembly code

Sat Nov 24, 2018 12:02 am

I just looked it up in the APCS erata that I have, and the original doc I have had it reversed. Arguments may be changed by the called procedure (a1-a4 or R0-R3), while variable registers (v1-v6 or R4-R9) must be preserved across calls.

This makes me wonder why the code I posted above assembled linked and ran as expected. Well not so much as none of the three functions called have a good reason to use R0-R3 for internal purposes.

So it works as I demonstrated, though it is by luck of the way the library is coded. As I said memory has not been that great since my medical issue. Sorry for getting that part wrong.

Though if only registers R0 though R3 are used for arguments how is it correctly using the value in R4 for the fifth argument?
RPi = Way for me to have fun and save power.
100% Off Grid.
Household TTL Electricity Usage = 1.4KW/h per day.
500W Solar System, produces 2.8KW/h per day average.

User avatar
Paeryn
Posts: 2512
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: Help with this assembly code

Sat Nov 24, 2018 12:26 am

The oldest APCS I could find is from Acorn RISC Machines with a copyright of 1994 (so slightly earlier than I could find on Arm's website), no idea what revision it is though (it doesn't say and it's a plain text document), that has the same usage as now.

No idea on how printf is picking up R4 for you without knowing how it was compiled. Have you tried compiling a C program against it and looking at how it is passing parameters?
She who travels light — forgot something.

Return to “Bare metal, Assembly language”