Подпрограммы в 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 является 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