简体   繁体   中英

I would like to calculate the sum of the first n integers in assembly language

I have just started to learn assembly language at school, and as an exercise I have to make a program that calculate the sum of the first n integers (1+2+3+4+5+...+n). I managed to build this program but during the comparison (line.9) I only compare the even numbers in register R1, so I would have to do another comparison for the odd numbers in R0.

    MOV R0,#1       ; I put a register at 1 to start the sequence
    INP R2,2        ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
    B myloop        ; I define a loop 
myloop:
    ADD R1,R0,#1    ; I calculate n+1 and put it in register 1
    ADD R3,R1,R0    ; I add R0 and R1, and I put the result in the register R3
    ADD R0,R1,#1    ; I calculate n+2 and I put in the register R0, and so on...
    ADD R4,R4,R3    ; R4 is the total result of all additions between R0 and R1
    CMP R1,R2       ; I compare R1 and the maximum number to calculate
    BNE myloop      ; I only exit the loop if R1 and R2 are equal
    STR R4,100      ; I store the final result in memory 100
    OUT R4,4        ; I output the final result of the sequence
    HALT            ; I stop the execution of the program

I've tried several methods but I can't manage to perform this double comparison... (a bit like an "elif" in python) Basically I would like to add this piece of code to also compare odd numbers:

CMP R0,R2
BNE myloop

But adding this like this directly after comparing even numbers doesn't work no matter if I put "BNE" or not.

You're trying to do a conjunction, in context, something like this:

do {
   ...
} while ( odd != n && even != n );
...

Eventually, one of those counters should reach the value n and stop the loop. So, both tests must pass in order to continue the loop. However, if either test fails, then the loop should stop.

First, we'll convert this loop into the if-goto-label form of assembly (while still using the C language:):

loop1:
    ...
    if ( odd != n && even != n ) goto loop1;
    ...

Next, let's break down the conjunction to get rid of it. The intent of the conjunction is that if the first component fails, to stop the loop, without even checking the second component. However, if the first component succeeds, then go on to check the second component. And if the second also succeeds, then, and only then return to the top of the loop (knowing both have succeeded), and otherwise fall off the bottom. Either way, whether the first component fails or the second component fails, the loop stops.

This intent is fairly easy to accomplish in if-goto-label:

loop1:
    ...
    if ( odd == n ) goto endLoop1; 
    if ( even != n ) goto loop1;
endLoop1:
    ...

Can you figure out how to follow this logic in assembly?


Another analysis might look like this:

loop1:
    ...
    if ( odd == n || even == n ) goto endLoop1; 
    goto loop1;
endLoop1:
    ...

This is the same logic, but stated as how to exit the loop rather than how to continue the loop. The condition is inverted ( De Morgan ) but the intended target of the if-goto statement is also changed — it is effectively doubly-negated, so holds the same.

From that we would strive to remove the disjunction, again by making two statements instead of one with disjunction, also relatively straightforward using if-goto:

loop1:
    ...
    if ( odd == n ) goto endLoop1; 
    if ( even == n ) goto endLoop1;
    goto loop1;
endLoop1:
    ...

And with an optimization sometimes known as branch over unconditional branch (the unconditional branch is goto loop1; ), we perform a pattern substitution, namely: (1) reversing the condition of the conditional branch, (2) changing the target of the conditional branch to the target of the unconditional branch, and (3) removing the unconditional branch.

loop1:
    ...
    if ( odd == n ) goto endLoop1; 
    if ( even != n ) goto loop1;
endLoop1:
    ...

In summary, one takeaway is to understand how powerful the primitive if-goto is, and that it can be composed into conditionals of any complexity.

Another takeaway is that logic can be transformed by pattern matching and substitution into something logically equivalent but more desirable for some purpose like writing assembly. Here we work toward increasing use of simple if-goto's and lessor use of compound conditions.


Also, as @vorrade says, programs generally should not make assumptions about register values — so suggest to load 0 into the registers that need it at the beginning of the program to ensure their initialization. We generally don't clear registers after their use but rather set them before use. So, in another larger program, your code might run with other values from some other code left over in those registers.

First of all your code assumes that R4 is 0 at the beginning. This might not be true.

Your program becomes simpler and easier to understand if you add each number in a smaller loop, like this:

    INP R2,2        ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
    MOV R0,#0       ; I put a register at 0 to start the sequence
    MOV R4,#0       ; I put a register at 0 to start the sum
    B testdone      ; Jump straight to test if done
myloop:
    ADD R0,R0,#1    ; I calculate n+1 and keep it in register 0
    ADD R4,R4,R0    ; I add R4 and R0, and I put the result in the register R4
testdone:
    CMP R0,R2       ; I compare R0 and the maximum number to calculate
    BNE myloop      ; I only exit the loop if R0 and R2 are equal
    STR R4,100      ; I store the final result in memory 100
    OUT R4,4        ; I output the final result of the sequence
    HALT            ; I stop the execution of the program

You only need 3 registers: R0 for current n, R2 for limit and R4 for the sum.

However, if you really have to add the even and odd numbers separately, you could do this way:

    INP R2,2        ; I ask the user up to what number the program should calculate, and I put its response in the R2 register
    MOV R0,#0       ; I put a register at 0 to start the sequence
    MOV R4,#0       ; I put a register at 0 to start the sum
    B testdone      ; Jump straight to test if done
myloop:
    ADD R0,R0,#1    ; I calculate n+1 (odd numbers), still register 0
    ADD R4,R4,R0    ; I add R4 and R0, keep the result in the register R4
    CMP R0,R2       ; I compare R0 and the maximum number to calculate
    BEQ done        ; I only exit the loop if R0 and R2 are equal
    ADD R0,R0,#1    ; I calculate n+1 (even numbers) and keep it in register 0
    ADD R4,R4,R0    ; I add R4 and R0, and I put the result in the register R4
    
testdone:
    CMP R0,R2       ; I compare R0 and the maximum number to calculate
    BNE myloop      ; I only exit the loop if R0 and R2 are equal
done:
    STR R4,100      ; I store the final result in memory 100
    OUT R4,4        ; I output the final result of the sequence
    HALT            ; I stop the execution of the program

First of all I would like to thank you very much @vorrade, @Erik Eidt and @Peter Cordes, I read your comments and advice very carefully, and they are very useful to me:)

But in fact following the post of my question I continued to seek by myself a solution to my problem, and I came to develop this code which works perfectly!

// Date         : 31/01/2022                                                                    //
// Description  : A program that calculate the sum of the first n integers (1+2+3+4+5+...+n)    //

    MOV R0,#1       // I put a register at 1 to start the sequence of odd number
    INP R2,2        // I ask the user up to what number the program should calculate, and I put its response in the R2 register
    B myloop        // I define a main loop 
myloop:
    ADD R1,R0,#1    // I calculate n+1 (even) and I put in the register R1, and so on...   
    ADD R3,R1,R0    // I add R0 and R1, and I put the result in the register R3
    ADD R4,R4,R3    // R4 is the total result of all additions between R0 and R1, which is the register that temporarily stores the calculated results in order to increment them later in register R4
    ADD R0,R1,#1    // I calculate the next odd number to add to the sequence
    B test1         // The program goes to the first comparison loop
test1:
    CMP R0,R2       // I compare the odd number which is in the current addition with the requested maximum, this comparison can only be true if the maximum is also odd.
    BNE test2       // If the comparison is not equal, then I move on to the next test which does exactly the same thing but for even numbers this time.
    ADD R4,R4,R2    // If the comparison is equal, then I add a step to the final result because my main loop does the additions 2 by 2.
    B final         // The program goes to the final loop
test2:
    CMP R1,R2       // I compare the even number which is in the current addition with the requested maximum, this comparison can only be true if the maximum is also even.
    BNE myloop      // If the comparison is not equal, then the program returns to the main loop because this means that all the comparisons (even or odd) have concluded that the maximum has not yet been reached and that it is necessary to continue adding.
    B final         // The program goes to the final loop
final:
    STR R4,100      // I store the final result in memory 100
    OUT R4,4        // I output the final result of the sequence
    HALT            // I stop the execution of the program

I made some comments to explain my process!

I now realize that the decomposition into several loops was indeed the right solution to my problem, it allowed me to better realize the different steps that I had first written down on paper.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM