具有4个以上参数的MIPS程序

时间:2014-11-07 00:08:01

标签: function assembly stack mips subroutine

以下是我的代码和最终测试用例。 main和printStats都给出了,我不得不qrite平均值(位于底部),所以看看有问题。这种情况会改变$ f,$ t,$ a和$ s中所有寄存器的值,并在其堆栈顶部添加2个项目,以确保平均方法正确保留运行其所需的值。 printStats中$ jr之后的计算。

我遇到的问题是在一些有3个击球手的测试用例中反复出现。击球手3使用任意大整数。

# printStats puts two values at the top two spots in its stack.  This is where
#    the number of homeruns and outs were placed when printStats
#    is called.  A function is allowed to change its own stack.

# Changes the $s registers used by main. The average function should be
#    getting arguments from the $a registers and the stack, and not relying
#    on what is in the $s registers.

# From test12:
# Tests if $t registers are preserved (if needed) by average.
# printStats puts values into every $t register.

# From test13:
# Tests if $a registers used correctly when average calls printStats.
# printStats changes the contents of $a1, $a2, and $a3 before returning

# test with three batters, both average and slugging percentage
# First batter has no hits, but does have outs
# Second batter has hits and outs, with realistic values
# Third hitter has large values for some of the hits and for the
#    outs. This means the hits and outs *have* to be converted from int's
#    to float's in order to get the right answer. 

.data

mainNumBatters:
   .word 3

mainBatter1:
   .word  27      # walks
   .word   0      # singles
   .word   0      # doubles
   .word   0      # triples
   .word   0      # home runs
   .word 423      # outs
mainBatter2:
   .word  27      # walks
   .word 101      # singles
   .word  22      # doubles
   .word   4      # triples
   .word  10      # home runs
   .word 423      # outs

mainBatter3:
   .word   102322 # walks
   .word  8000000 # singles
   .word       22 # doubles
   .word   500000 # triples
   .word       10 # home runs
   .word 23000000 # outs


.data
mainNewline:
         .asciiz  "\n"
mainBatterNumber:
         .asciiz  "Batter number: "
mainBattingAverage:
         .asciiz  "Batting average: "
mainSluggingPercentage:
         .asciiz  "Slugging percentage: "
mainOnbasePercentage:
         .asciiz  "On-base percentage: "

         .text

main:
         # Function prologue -- even main has one
         addiu $sp, $sp, -24     # allocate stack space -- default of 24 here
         sw    $fp, 0($sp)       # save frame pointer of caller
         sw    $ra, 4($sp)       # save return address
         addiu $fp, $sp, 20      # setup frame pointer of main

         # for (i = 0; i < mainNumBatters; i++)
         #    compute batting average
         #    compute slugging average

         la    $s1, mainNumBatters
         lw    $s6, 0($s1)       # $s6 = number of batters
         addi  $s0, $zero, 0     # $s0 = i = 0
         la    $s1, mainBatter1  # $s1 = addr of current batter's stats

mainLoopBegin:
         slt   $t0, $s0, $s6     # $t0 = i < number of batters
         beq   $t0, $zero, mainDone

         la    $a0, mainBatterNumber
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $s0, 1
         addi  $v0, $zero, 1
         syscall
         la    $a0, mainNewline
         addi  $v0, $zero, 4
         syscall

         # Compute the batting average
         addi  $a0, $zero, 1      # $a0 = 1 = compute batting average
         lw    $a1,   0($s1)      # $a1 = walks
         lw    $a2,   4($s1)      # $a2 = singles
         lw    $a3,   8($s1)      # $a3 = doubles
         lw    $s2,  12($s1)      # $s2 = triples
         lw    $s3,  16($s1)      # $s3 = home runs
         lw    $s4,  20($s1)      # $s4 = outs

         sw    $s4,  -4($sp)      # put outs at top of average's stack
         sw    $s3,  -8($sp)      # put homeruns 2nd fm top of average's stack
         sw    $s2, -12($sp)      # put triples 3rd fm top of average's stack
         jal   average

         # Print the batting average
         mtc1  $v0, $f12          # get result fm $v0 before we print string
         la    $a0, mainBattingAverage
         addi  $v0, $zero, 4
         syscall
         addi  $v0, $zero, 2      # print the average
         syscall
         la    $a0, mainNewline
         addi  $v0, $zero, 4
         syscall
         syscall

         # do it for the slugging percentage
         addi  $a0, $zero, 2      # $a0 = 2 = compute slugging average
         lw    $a1,   0($s1)      # $a1 = walks
         lw    $a2,   4($s1)      # $a2 = singles
         lw    $a3,   8($s1)      # $a3 = doubles
         lw    $s2,  12($s1)      # $s2 = triples
         lw    $s3,  16($s1)      # $s3 = home runs
         lw    $s4,  20($s1)      # $s4 = outs

         sw    $s4,  -4($sp)      # put outs at top of average's stack
         sw    $s3,  -8($sp)      # put homeruns 2nd fm top of average's stack
         sw    $s2, -12($sp)      # put triples 3rd fm top of average's stack
         jal   average

         # Print the slugging percentage
         mtc1  $v0, $f12          # get result fm $v0 before we print string
         la    $a0, mainSluggingPercentage
         addi  $v0, $zero, 4
         syscall
         addi  $v0, $zero, 2      # print the percentage
         syscall
         la    $a0, mainNewline
                 addi  $v0, $zero, 4
         syscall
         syscall

         # do it again for the on-base percentage
         addi  $a0, $zero, 3      # $a0 = 3 = compute slugging average
         lw    $a1,   0($s1)      # $a1 = walks
         lw    $a2,   4($s1)      # $a2 = singles
         lw    $a3,   8($s1)      # $a3 = doubles
         lw    $s2,  12($s1)      # $s2 = triples
         lw    $s3,  16($s1)      # $s3 = home runs
         lw    $s4,  20($s1)      # $s4 = outs

         sw    $s4,  -4($sp)      # put outs at top of average's stack
         sw    $s3,  -8($sp)      # put homeruns 2nd fm top of average's stack
         sw    $s2, -12($sp)      # put triples 3rd fm top of average's stack

         jal   average

         # Print the slugging percentage
         mtc1  $v0, $f12          # get result fm $v0 before we print string
         la    $a0, mainOnbasePercentage
         addi  $v0, $zero, 4
         syscall
         addi  $v0, $zero, 2      # print the percentage
         syscall
         la    $a0, mainNewline
         addi  $v0, $zero, 4
         syscall
         syscall

         addi  $s0, $s0, 1       # i++
         addi  $s1, $s1, 24      # $s1 = addr of next batter's stats
         j     mainLoopBegin

mainDone:
         # Epilogue for main -- restore stack & frame pointers and return
         lw    $ra, 4($sp)       # get return address from stack
         lw    $fp, 0($sp)       # restore frame pointer for caller
         addiu $sp, $sp, 24      # restore frame pointer for caller
                  jr    $ra               # return to caller

.data
printStatsOuts:
         .asciiz "Outs:      "
printStatsWalks:
         .asciiz "Walks:     "
printStatsSingles:
         .asciiz "Singles:   "
printStatsDoubles:
         .asciiz "Doubles:   "
printStatsTriples:
         .asciiz "Triples:   "
printStatsHomeruns:
         .asciiz "Home runs: "
printStatsNewline:
         .asciiz "\n"

.text
printStats:
         # Function prologue
         addiu $sp, $sp, -32     # allocate stack space
         sw    $a3, 20($sp)      # save $a0 thru $a3
         sw    $a2, 16($sp)
         sw    $a1, 12($sp)
         sw    $a0, 8($sp)
         sw    $ra, 4($sp)       # save return address
         sw    $fp, 0($sp)       # save frame pointer of caller
         addiu $fp, $sp, 28      # setup frame pointer of average

         # printStats expects to find the following:
         # $a0 = walks
         # $a1 = singles
         # $a2 = doubles
         # $a3 = triples
         # 5th argument = homeruns
         # 6th argument = outs

         # print the outs
         la    $a0, printStatsOuts
                  syscall
         lw    $a0, 0($fp)       # the outs are at the top of our stack
         addi  $v0, $zero, 1
         syscall
         la    $a0, printStatsNewline
         addi  $v0, $zero, 4
         syscall

         # print the walks
         la    $a0, printStatsWalks
         addi  $v0, $zero, 4
         syscall
         lw    $a0, 8($sp)       # the walks were passed in $a0
         addi  $v0, $zero, 1
         syscall
         la    $a0, printStatsNewline
         addi  $v0, $zero, 4
         syscall

         # print the singles
         la    $a0, printStatsSingles
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $a1, 0        # the singles were passed in $a1
         addi  $v0, $zero, 1
         syscall
         la    $a0, printStatsNewline
         addi  $v0, $zero, 4
         syscall

         # print the doubles
         la    $a0, printStatsDoubles
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $a2, 0        # the doubles were passed in $a2
         addi  $v0, $zero, 1
         syscall
         la    $a0, printStatsNewline
         addi  $v0, $zero, 4
         syscall

         # print the triples
         la    $a0, printStatsTriples
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $a3, 0        # the doubles were passed in $a3
         addi  $v0, $zero, 1
         syscall
         la    $a0, printStatsNewline
         addi  $v0, $zero, 4
         syscall

         # print the homeruns
         la    $a0, printStatsHomeruns
         addi  $v0, $zero, 4
         syscall
         lw    $a0, -4($fp)       # the homeruns are 4 bytes below the top of our stack
         addi  $v0, $zero, 1
         syscall
         la    $a0, printStatsNewline
         addi  $v0, $zero, 4
         syscall

         # Put -1 in $t0, then copy that value to each of the $f registers
         addi  $t0, $zero, -1
         mtc1  $t0, $f0
         mtc1  $t0, $f1
         mtc1  $t0, $f2
         mtc1  $t0, $f3
         mtc1  $t0, $f4
         mtc1  $t0, $f5
         mtc1  $t0, $f6
         mtc1  $t0, $f7
         mtc1  $t0, $f8
         mtc1  $t0, $f9
         mtc1  $t0, $f10
         mtc1  $t0, $f11
         mtc1  $t0, $f12
         mtc1  $t0, $f13
                 mtc1  $t0, $f14
         mtc1  $t0, $f15
         mtc1  $t0, $f16
         mtc1  $t0, $f17
         mtc1  $t0, $f18
         mtc1  $t0, $f19
         mtc1  $t0, $f20
         mtc1  $t0, $f21
         mtc1  $t0, $f22
         mtc1  $t0, $f23
         mtc1  $t0, $f24
         mtc1  $t0, $f25
         mtc1  $t0, $f26
         mtc1  $t0, $f27
         mtc1  $t0, $f28
         mtc1  $t0, $f29
         mtc1  $t0, $f30
         mtc1  $t0, $f31

         # Put various values in the $t registers
         addi  $t0, $zero, -1111
         addi  $t1, $zero, -2222
         addi  $t2, $zero, -3333
         addi  $t3, $zero, -4444
         addi  $t4, $zero, -5555
         addi  $t5, $zero, -6666
         addi  $t6, $zero, -7777
         addi  $t7, $zero, -8888
         addi  $t8, $zero, -9999
         addi  $t9, $zero, -1111

         # Put various values in the $a registers.
         addi  $a0, $zero, -1111
         addi  $a1, $zero, -1111
         addi  $a2, $zero, -2222
         addi  $a3, $zero, -3333
                  # Put two values at the top two spots in our stack.  This is where
         #    the number of homeruns and outs were placed when printStats
         #    is called.  A function is allowed to change its own stack.
         addi  $t7, $zero, -1234
         sw    $t7,  0($fp)
         sw    $t7, -4($fp)

printStatsDone:
         # Epilogue for printStats -- restore stack & frame pointers and return
         lw    $ra, 4($sp)       # get return address from stack
         lw    $fp, 0($sp)       # restore frame pointer for caller
         addiu $sp, $sp, 32      # restore frame pointer for caller
         jr    $ra               # return to caller

# Your code goes below this line

average: 
    #takes 7 arguments
    #Arguments $a0-$a3 are put on the Caller's stack
    #arguments 5-7 are put on the Callee's stack and called

# Prologue: set up stack and frame pointers for average
        addiu   $sp, $sp, -36    # allocate stack space -- 36 needed here
        sw      $fp, 0($sp)      # save caller's frame pointer
        sw      $ra, 4($sp)      # save return address
        # save parameter values $a0-$a3 on the stack
        sw      $a0, 8($sp) # designates which calculation to do
        sw      $a1, 12($sp)    # walks
        sw      $a2, 16($sp)    # singles
        sw      $a3, 20($sp)    # doubles
        addiu   $fp, $sp, 32    # setup average frame pointer

    #Allocate proper locations of arguments for printStats
    #arguments 5 and 6 (home runs and outs will be placed on stack)
    addi    $t5, $a0, 0 #$t5 designates which calculation to run
    addi    $a0, $a1, 0 #walks of printStats
    addi    $a1, $a2, 0 #singles of printStats
    addi    $a2, $a3, 0 #doubles of printStats
    lw  $a3, 24($sp)    #triples of printStats
    lw  $t1, 28($sp)    #home runs of printStat
    sw  $t1, -8($sp)    #home runs now 2nd from top of stack
    lw  $t2, 32($sp)    #outs of printStats
    sw  $t2, -4($sp)    #out now top of stack
    jal printStats

    #restore registers used in average
    lw  $t2, 32($sp)    #outs
    lw  $t1, 28($sp)    #homeruns
    lw  $a3, 24($sp)    #triples
    lw  $a2, 20($sp)    #doubles
    lw  $a1, 16($sp)    #singles
    lw  $a0, 12($sp)    #walks
    lw  $t5, 8($sp) #compare which calculation to run   

    #convert outs to float
    mtc1    $t2, $f4    #outs = $f4

    #hits
        #hits = singles + doubles + triples + home runs
        add     $t3, $a1, $a2   # hits = singles + doubles
        add     $t3, $t3, $a3   # hits = $t3 + triples
        add     $t3, $t3, $t1   # hits = $t3 + home runs
    mtc1    $t3, $f1        #convert hits to float $f1

        #atBats
        #atBats = hits + outs
        add.s   $f2, $f1, $f4   # atBats = hits + outs
    mfc1    $t4, $f2        #convert atBats to int for compare

    #Compare $t5
    #if $t5 == 1, computer batting average
    #if $t5 == 2, compute slugging percentage
    #if $t5 == 3, compute on-base percentage

    addi    $t0, $zero, 1   #$t0 = 1
    beq $t5, $t0, averageBattingAverage

    addi    $t0, $zero, 2   #$t0 = 2
    beq $t5, $t0, averageSluggingPercentage

    addi    $t0, $zero, 3   #$t0 = 3
    beq $t5, $t0, averageOnBasePercentage

averageBattingAverage:
    #batting average
    #batting average = hits/atBats
    #if atBats == 0, average = 0

    #compare atBats
    beq     $t4, $zero, averageBattingSluggingZero

    #batting
    div.s   $f3, $f1, $f2   #batting average = hits/atBats

    #Set to print
    mfc1    $v0, $f3    #return $v0 the batting average
    j   averageDone #done

averageSluggingPercentage:
    #Slugging percentage
    #slugging percentage = (singles + doubles*2 + triples*3
    #           + home runs*4)/atBats 
    #if atBats == 0, percentage = 0

    #compare atBats
    beq     $t4, $zero, averageBattingSluggingZero

    #slugging
    add $t5, $a2, $a2   #doubles*2
    add $t6, $a3, $a3   #$t6 = triples * 2
    add $t6, $t6, $a3   #$t6 = triples * 3
    add $t7, $t1, $t1   #$t7 = home runs * 2
    add $t7, $t7, $t7   #$t7 = home runs * 4
    add $t8, $a1, $t5   #$t8 = singles + doubles*2
    add $t8, $t8, $t6   #$t8 = $t8 + triples*3
    add $t8, $t8, $t7   #$t8 = $t8 + home runs*4
    mtc1    $t8, $f1    #convert $t8 to float $f1
    div.s   $f3, $f1, $f2   #slugging percentage = $f1/atBats

    #set to print
    mfc1    $v0, $f3        #return $v0 the slugging percentage
    j   averageDone #done   

averageOnBasePercentage:
    #On-base percentages
    #on-base percentages = (hits + walks)/(atBats + walks)
    #if atBats + walks is zero, percentage is zero

    add $t6, $t3, $a0   #$t6 = hits + walks
    add $t7, $t4, $a0   #$t7 = atBats + walks

    #compare atBats + walks
    beq $t7, $zero, averageOnBaseZero

    #on-base
    mtc1    $t6, $f1    #convert $t6 to float $f6
    mtc1    $t7, $f2    #convert $t7 to float $f7
    div.s   $f3, $f1, $f2   #on-base percentage = $f6/$f7

    #set to print
    mfc1    $v0, $f3        #return $v0 the on-base percentage
    j   averageDone #done   

averageBattingSluggingZero:
    #if atBats is zero, batting average and slugging
    #percentage are both zero

    addi    $t0, $zero, 0   #$t0 = 0
    mtc1    $t0, $f0    #convert 0 to float $f0

    #set to print
    mfc1    $v0, $f0        #return $v0 = 0 
    j       averageDone     #done

averageOnBaseZero:
    #if atBats + walks is zero, on-base percentage
    # is also zero

    addi    $t0, $zero, 0   #$t0 = 0
    mtc1    $t0, $f0    #convert 0 to float $f0

    #set to print
    mfc1    $v0, $f0        #return $v0 = 0
    j       averageDone     #done

averageDone:  # Epilogue: restore stack and frame pointers and return
        lw      $ra, 4($sp)      # get return address from stack
        lw      $fp, 0($sp)      # restore the caller's frame pointer
        addiu   $sp, $sp, 36     # restore the caller's stack pointer
        jr      $ra              # return to caller's code

我的输出没有排队,我不知道为什么。 以下是击球手3的正确输出。

Batter number: 3
Outs:      23000000
Walks:     102322
Singles:   8000000
Doubles:   22
Triples:   500000
Home runs: 10
Batting average: 0.26984200

Outs:      23000000
Walks:     102322
Singles:   8000000
Doubles:   22
Triples:   500000
Home runs: 10
Slugging percentage: 0.30158967

Outs:      23000000
Walks:     102322
Singles:   8000000
Doubles:   22
Triples:   500000
Home runs: 10
On-base percentage: 0.27220613

这是我的输出,有一个!标记不一致

Batter number: 3
Outs:      23000000
Walks:     102322
Singles:   8000000
Doubles:   22
Triples:   500000
Home runs: 10
!Batting average: 0.22532867

Outs:      23000000
Walks:     102322
Singles:   8000000
Doubles:   22
Triples:   500000
Home runs: 10
!Slugging percentage: 0.25183919

Outs:      23000000
Walks:     102322
Singles:   8000000
Doubles:   22
Triples:   500000
Home runs: 10
!On-base percentage: 0.22559348

我对这一个击球手的计算结果如何?我该怎么做才能解决它?

1 个答案:

答案 0 :(得分:1)

有多种调用约定,你应该弄清楚你应该遵循哪一种。如果没有文档,可以查看编译器生成的汇编代码,或者使用调试器。

也就是说,从堆栈上只传递4个参数的事实来看,你似乎正在使用O32调用约定。这意味着,进入函数时的堆栈包含4个参数槽,用于保存在寄存器$a0-$a3中传递的前4个参数。接下来是剩下的所有论点。因此,在进入您的函数时,第5个参数(第一个未通过寄存器传递的参数)将位于地址16($sp),第{6}位于20($sp),第7位位于24($sp)。 (这假设您的参数各为1个字。)您从36中减去$sp以为您的本地人腾出空间,因此在此之后所有偏移量都会增加该数量,即{{1 }} 等等。您可以使用这些地址访问它们。

然后,您将52($sp)设置为$fp。我不认为调用约定要求$sp + 32的任何特定值,所以这应该没问题。如果您随后希望访问与$fp相关的参数,则需要再次调整偏移以进行补偿,这次是$fp。因此,第五个参数应该是-32,其次是其余的。