以下是我的代码和最终测试用例。 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
我对这一个击球手的计算结果如何?我该怎么做才能解决它?
答案 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
,其次是其余的。