我想用汇编语言计算前n个整数的总和

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

我刚开始在学校学习汇编语言,作为练习我必须编写一个程序来计算前n个整数的和(1+2+3+4+5+...+ n). 我设法构建了这个程序,但是在比较过程中(第 9 行)我只比较寄存器 R1 中的偶数,所以我必须对 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

我尝试了几种方法,但我无法执行这种双重比较...(有点像 python 中的“elif”) 基本上我想添加这段代码来比较奇数:

CMP R0,R2
BNE myloop

但是无论我是否输入“BNE”,在比较偶数之后直接这样添加都不行。

首先,您的代码假定 R4 在开始时为 0。 这可能不是真的。

如果在较小的循环中添加每个数字,您的程序将变得更简单、更容易理解,如下所示:

    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

你只需要3个寄存器:R0代表电流n,R2代表限制,R4代表总和。

但是,如果你真的需要将偶数和奇数分开相加,你可以这样做:

    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

您正在尝试在上下文中进行连词,如下所示:

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

最终,这些计数器之一应该达到值 n 并停止循环。因此,两个测试都必须通过才能继续循环。但是,如果任一测试失败,则循环应该停止。

首先,我们将把这个循环转换成if-goto-label形式的汇编(同时仍然使用C语言!):

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

接下来,让我们分解连词以摆脱它。联合的目的是如果第一个组件失败,则停止循环,甚至不检查第二个组件。但是,如果第一个组件成功,则继续检查第二个组件。如果第二个也成功,那么,只有 return 到循环的顶部(知道两个都成功了),否则从底部掉下来。无论哪种方式,无论第一个组件失败还是第二个组件失败,循环都会停止。

这个意图在 if-goto-label 中很容易实现:

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

你能想出如何在汇编中遵循这个逻辑吗?


另一个分析可能是这样的:

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

这是相同的逻辑,但声明为如何退出循环而不是如何继续循环。条件被反转 (De Morgan),但 if-goto 语句的预期目标也发生了变化 — 它实际上是 doubly-negated,因此保持不变。

由此我们将努力消除析取,再次通过创建两个语句而不是一个带有析取的语句,使用 if-goto:

也相对简单
loop1:
    ...
    if ( odd == n ) goto endLoop1; 
    if ( even == n ) goto endLoop1;
    goto loop1;
endLoop1:
    ...

并且通过有时称为无条件分支上的分支的优化(无条件分支是 goto loop1;),我们执行模式替换,即:(1) 反转条件分支的条件,(2)将条件分支的目标更改为无条件分支的目标,以及 (3) 删除无条件分支。

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

总而言之,一个要点是了解原语 if-goto 有多么强大,并且它可以组合成任何复杂的条件。

另一个要点是,可以通过模式匹配和替换将逻辑转换为逻辑上等效但更适合某些目的(例如编写汇编)的东西!在这里,我们致力于增加对简单 if-goto 的使用,减少对复合条件的使用。


此外,正如@vorrade 所说,程序通常不应该对寄存器值做出假设——因此建议在程序开始时将 0 加载到需要它的寄存器中,以确保它们的初始化。我们通常不会在使用后清除寄存器,而是在使用前设置它们。因此,在另一个更大的程序中,您的代码可能 运行 与那些寄存器中遗留的其他代码的其他值。

另外,我回答了提出的关于复合条件的问题,并详细解释了它们的工作原理;尽管如其他地方所述,没有必要将偶数和奇数分开以对它们求和,并且还有一个公式可以在不迭代的情况下计算数字的总和(尽管确实需要可能无法直接使用的乘法,所以会需要更有限的迭代..).

首先非常感谢@vorrade、@Erik Eidt 和@Peter Cordes,我非常仔细地阅读了你们的评论和建议,它们对我非常有用:)

但实际上在我的问题 post 之后,我继续自己寻找问题的解决方案,并且我开始开发这个完美运行的代码!

// 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

我发表了一些评论来解释我的过程!

我现在意识到分解成几个循环确实是我问题的正确解决方案,它让我更好地实现了我最初写在纸上的不同步骤。