Подпрограми в 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

person Oso    schedule 22.07.2010    source източник


Отговори (1)


Мисля, че основният проблем е, че въпрос 6 ви кара да работите с числата от 1 до 100, докато вашият код работи с 1 до 999!

Извикването на подпрограмата изглежда добре. Някои предложения:

1) Имате странна смес от основни операции срещу псевдо-операции. Инструкциите ori в горната част могат да бъдат написани с помощта на li (точно както са константите за системните извиквания в долната част). Като алтернатива, ако съзнателно искате да използвате основни операции като учебно упражнение, имайте предвид, че move $a0, $t1 може да се запише като addu $a0, $t1, $0 (или or $a0, $1, $0 също работи).

2) Условното разклонение с sltu и beq може да бъде записано като bge $t1, $t4, diff. Това е псевдооперация, която се разширява до sltu и beq, но е интересна, защото автоматично използва $1 като временен регистър - по конвенция този регистър е запазен за използване като "временен асемблер" и се нарича $at.

3) sll $0, $0, $0 наистина е инструкция sllv, тъй като количеството на смяната има регистър. Каноничният MIPS no-op е 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 има тенденция да се откроява така или иначе, но ако започнете да се опитвате да ги напълните с полезни инструкции, бързо ще полудеете, ако не ги маркирате по някакъв начин.)

person Matthew Slattery    schedule 22.07.2010
comment
Благодаря за задълбочения отговор! За съжаление, програмата все още дава неправилен отговор. Виждате ли нещо, което може да правя неправилно алгоритмично? - person Oso; 23.07.2010
comment
Няма значение, всъщност току-що го разбрах! Използвах числата 1..99, вместо 1..100. Промяната на $t4 на 0x65 го поправи. Благодаря ви отново! - person Oso; 23.07.2010