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
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
oriinstructions 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, $t1can be written asaddu $a0, $t1, $0(oror $a0, $1, $0also works).2) The conditional branch with
sltuandbeqcan be written asbge $t1, $t4, diff. This is a pseudo-op which expands tosltuandbeq, but is interesting because it automatically uses$1as a temporary register – by convention this register is reserved for use as the “assembler temporary” and named$at.3)
sll $0, $0, $0is really asllvinstruction, as the shift amount there is a register. The canonical MIPS no-op issll $0, $0, 0which 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.
gasdoes 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
noptends 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.)