MIPS 中的子例程和其他初学者的愚蠢行为

发布于 2024-09-11 04:56:28 字数 2357 浏览 7 评论 0原文

我正在使用 Project Euler 来学习 MIPS,特别是使用问题 6 来学习如何使用子例程。不幸的是,我做了一些非常错误的事情,因为我得到的答案太大了。我的问题是我如何使用子例程,还是完全是别的问题?

## p6.asm
##
## Andrew Levenson, 2010
## Project Euler, Problem 6
##
## Calculate the difference
## between the sum of the squares
## and the square of the sum
## of all natural numbers n
## such that n < 1000
        .text
        .globl  main

main:
init:   
    ## Registers
        ori     $t0, $0, 0x0        # $t0 will be used for scratch
        ori     $t1, $0, 0x0        # $t1 is the loop counter and n
        ori     $t2, $0, 0x0        # $t2 will be the sum of the squares
        ori     $t3, $0, 0x0        # $t3 will be the square of the sum
        ori     $t4, $0, 0x3E8      # $t4 = 1000, the limit

loop:
    ## Main loop
        addiu   $t1, $t1, 0x1       # Increment n

        sltu    $t0, $t1, $t4       # Is n less than 1000?
        beq     $t0, $0, diff       # if $t0 == 0 then jump to diff
        sll     $0, $0, $0          # no op

        addu    $t3, $t3, $t1       # sum = sum + n

        move    $a0, $t1            # put n in $a0
        jal     square              # jump to square and save position to $ra
        sll     $0, $0, $0          # no op

        addu    $t2, $t2, $a0       # The sum of the squares =
                                    # sum + n **2

        j       loop                # jump to loop
        sll     $0, $0, $0          # no op




square:
    ## Subroutine that squares a given number
        mul     $a0, $a0, $a0       # Argument = Argument ** 2
        jr      $ra                 # jump to $ra
                                    # $ra = instruction where jal was called
        sll     $0, $0, $0          # no op


diff:
    ## Finds the difference of $t2 and $t3
    ## But first, we have to square $t3
        move    $a0, $t3            # puts $t3 in $a0
        jal     square              # jump to square and save position to $ra
        sll     $0, $0, $0          # no op

        move    $t3, $a0            # $t3 = $a0 = $t3 ** 2

        subu    $t0, $t2, $t3       # $t0 = $t2 - $t3


print:
        li      $v0, 0x1            # system call #1 - print int
        move    $a0, $t0
        syscall                     # execute

        li      $v0, 0xA            # system call #10 - exit
        syscall

## End of Program

I'm using Project Euler to learn MIPS, specifically using Problem 6 to learn how to use a subroutine. Unfortunately, I am doing something very wrong because my answer that I'm getting is far too large in magnitude. Is my problem here with how I am using the subroutine, or is it something else entirely?

## p6.asm
##
## Andrew Levenson, 2010
## Project Euler, Problem 6
##
## Calculate the difference
## between the sum of the squares
## and the square of the sum
## of all natural numbers n
## such that n < 1000
        .text
        .globl  main

main:
init:   
    ## Registers
        ori     $t0, $0, 0x0        # $t0 will be used for scratch
        ori     $t1, $0, 0x0        # $t1 is the loop counter and n
        ori     $t2, $0, 0x0        # $t2 will be the sum of the squares
        ori     $t3, $0, 0x0        # $t3 will be the square of the sum
        ori     $t4, $0, 0x3E8      # $t4 = 1000, the limit

loop:
    ## Main loop
        addiu   $t1, $t1, 0x1       # Increment n

        sltu    $t0, $t1, $t4       # Is n less than 1000?
        beq     $t0, $0, diff       # if $t0 == 0 then jump to diff
        sll     $0, $0, $0          # no op

        addu    $t3, $t3, $t1       # sum = sum + n

        move    $a0, $t1            # put n in $a0
        jal     square              # jump to square and save position to $ra
        sll     $0, $0, $0          # no op

        addu    $t2, $t2, $a0       # The sum of the squares =
                                    # sum + n **2

        j       loop                # jump to loop
        sll     $0, $0, $0          # no op




square:
    ## Subroutine that squares a given number
        mul     $a0, $a0, $a0       # Argument = Argument ** 2
        jr      $ra                 # jump to $ra
                                    # $ra = instruction where jal was called
        sll     $0, $0, $0          # no op


diff:
    ## Finds the difference of $t2 and $t3
    ## But first, we have to square $t3
        move    $a0, $t3            # puts $t3 in $a0
        jal     square              # jump to square and save position to $ra
        sll     $0, $0, $0          # no op

        move    $t3, $a0            # $t3 = $a0 = $t3 ** 2

        subu    $t0, $t2, $t3       # $t0 = $t2 - $t3


print:
        li      $v0, 0x1            # system call #1 - print int
        move    $a0, $t0
        syscall                     # execute

        li      $v0, 0xA            # system call #10 - exit
        syscall

## End of Program

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

以可爱出名 2024-09-18 04:56:28

我认为主要问题是 问题 6 要求您与从 1 到 100 的数字,而您的代码适用于 1 到 999!

子程序调用看起来没问题。一些建议:

1)你有一个奇怪的基本操作与伪操作的混合。顶部的 ori 指令可以使用 li 编写(就像底部的系统调用常量一样)。或者,如果您故意想使用基本操作作为学习练习,请注意 move $a0, $t1 可以写为 addu $a0, $t1, $0 (或 或 $a0, $1, $0 也可以)。

2) 带有sltubeq的条件分支可以写成bge $t1, $t4, diff。这是一个伪操作,它扩展到 sltubeq,但很有趣,因为它自动使用 $1 作为临时寄存器 - 按照惯例,这是寄存器被保留用作“临时汇编器”并命名为$at

3) sll $0, $0, $0 实际上是一个 sllv 指令,因为移位量有一个寄存器。规范的 MIPS 无操作是 sll $0, $0, 0,它汇编为操作码 0x00000000。更好的是,只需编写 nop 即可。

4) 使用显式分支延迟槽的地方(请注意,某些汇编器会自动重新排序指令来填充它们 - 例如 gas 会执行此操作,除非您使用 .set reorder),以某种方式清楚地标记它们以使它们脱颖而出是很有帮助的。一个有用的约定是给它们一点额外的缩进:(

move    $a0, $t1            # put n in $a0
jal     square              # jump to square and save position to $ra
 nop                        # no-op

nop 无论如何都会很突出,但是如果你开始尝试用有用的指令填充它们,如果你不这样做,你很快就会发疯。不要以某种方式标记它们。)

I think the main problem is that question 6 asks you to work with the numbers from 1 to 100, whereas your code works with 1 to 999!

The subroutine call looks OK. Some suggestions:

1) You have a strange mixture of basic ops vs. pseudo-ops. The ori instructions at the top can be written using li (just as the constants for the syscalls at the bottom are). Alternatively, if you deliberately want to use basic ops as a learning exercise, note that move $a0, $t1 can be written as addu $a0, $t1, $0 (or or $a0, $1, $0 also works).

2) The conditional branch with sltu and beq can be written as bge $t1, $t4, diff. This is a pseudo-op which expands to sltu and beq, but is interesting because it automatically uses $1 as a temporary register - by convention this register is reserved for use as the "assembler temporary" and named $at.

3) sll $0, $0, $0 is really a sllv instruction, as the shift amount there is a register. The canonical MIPS no-op is sll $0, $0, 0 which assembles to an opcode of 0x00000000. Better still, just write nop.

4) Where explicit branch delay slots are being used (note that some assemblers will reorder instructions automatically to fill them - e.g. gas does this unless you tell it not to with .set reorder), it's helpful to clearly mark them in some way so that they stand out. A useful convention is to give them an extra bit of indentation:

move    $a0, $t1            # put n in $a0
jal     square              # jump to square and save position to $ra
 nop                        # no-op

(A nop tends to stand out anyway, but if you start trying to fill them with useful instructions, you will quickly go mad if you don't mark them in some way.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文