MIPS 中的子例程和其他初学者的愚蠢行为
我正在使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我认为主要问题是 问题 6 要求您与从 1 到 100 的数字,而您的代码适用于 1 到 999!
子程序调用看起来没问题。一些建议:
1)你有一个奇怪的基本操作与伪操作的混合。顶部的 ori 指令可以使用 li 编写(就像底部的系统调用常量一样)。或者,如果您故意想使用基本操作作为学习练习,请注意
move $a0, $t1
可以写为addu $a0, $t1, $0
(或或 $a0, $1, $0
也可以)。2) 带有
sltu
和beq
的条件分支可以写成bge $t1, $t4, diff
。这是一个伪操作,它扩展到sltu
和beq
,但很有趣,因为它自动使用$1
作为临时寄存器 - 按照惯例,这是寄存器被保留用作“临时汇编器”并命名为$at
。3)
sll $0, $0, $0
实际上是一个sllv
指令,因为移位量有一个寄存器。规范的 MIPS 无操作是sll $0, $0, 0
,它汇编为操作码0x00000000
。更好的是,只需编写nop
即可。4) 使用显式分支延迟槽的地方(请注意,某些汇编器会自动重新排序指令来填充它们 - 例如
gas
会执行此操作,除非您使用.set reorder
),以某种方式清楚地标记它们以使它们脱颖而出是很有帮助的。一个有用的约定是给它们一点额外的缩进:(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 usingli
(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 thatmove $a0, $t1
can be written asaddu $a0, $t1, $0
(oror $a0, $1, $0
also works).2) The conditional branch with
sltu
andbeq
can be written asbge $t1, $t4, diff
. This is a pseudo-op which expands tosltu
andbeq
, 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 asllv
instruction, as the shift amount there is a register. The canonical MIPS no-op issll $0, $0, 0
which assembles to an opcode of0x00000000
. Better still, just writenop
.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:(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.)