好的,我的任务是修改此代码以计算大小写元音和小写元音。该程序的目的是演示使用堆栈来保存函数调用之间的数据:
##
## vowel.a - prints out number of vowels in
## - the string str
##
## a0 - points to the string
##
#################################################
# #
# text segment #
# #
#################################################
.text
.globl __start
__start: # execution starts here
la $a0,str
jal vcount # call vcount
move $a0,$v0
li $v0,1
syscall # print answer
la $a0,endl
li $v0,4
syscall # print newline
li $v0,10
syscall # au revoir...
#------------------------------------------------
# vowelp - takes a single character as a
# parameter and returns 1 if the character
# is a (lower case) vowel otherwise return 0.
# a0 - holds character
# v0 - returns 0 or 1
#------------------------------------------------
vowelp: li $v0,0
beq $a0,'a',yes
beq $a0,'e',yes
beq $a0,'i',yes
beq $a0,'o',yes
beq $a0,'u',yes
jr $ra
yes: li $v0,1
jr $ra
#------------------------------------------------
# vcount - use vowelp to count the vowels in a
# string.
# a0 - holds string address
# s0 - holds number of vowels
# v0 - returns number of vowels
#------------------------------------------------
vcount:
sub $sp,$sp,16 # save registers on stack
sw $a0,0($sp)
sw $s0,4($sp)
sw $s1,8($sp)
sw $ra,12($sp)
li $s0,0 # count of vowels
move $s1,$a0 # address of string
nextc: lb $a0,($s1) # get each character
beqz $a0,done # zero marks end
jal vowelp # call vowelp
add $s0,$s0,$v0 # add 0 or 1 to count
add $s1,$s1,1 # move along string
b nextc
done: move $v0,$s0 # use $v0 for result
lw $a0,0($sp) # restore registers
lw $s0,4($sp)
lw $s1,8($sp)
lw $ra,12($sp)
add $sp,$sp,16
jr $ra
#################################################
# #
# data segment #
# #
#################################################
.data
str: .asciiz "long time ago in a galaxy far away"
endl: .asciiz "\n"
##
## end of file vowel.a
我的修改后的代码有效:
##
## vowel.a - prints out number of vowels in
## - the string str
##
## a0 - points to the string
##
#################################################
# #
# text segment #
# #
#################################################
.text
.globl __start
__start: # execution starts here
la $a0,str
jal vcount # call vcount
move $a0,$v0
li $v0,1
syscall # print answer
la $a0,endl
li $v0,4
syscall # print newline
move $a0,$t0
li $v0,1
syscall
la $a0,endl
li $v0,4
syscall
li $v0,10
syscall # au revoir...
vowell: li $v0,0
beq $a0,'a',yes
beq $a0,'e',yes
beq $a0,'i',yes
beq $a0,'o',yes
beq $a0,'u',yes
jr $ra
yes: li $v0,1
jr $ra
vowelu:
li $v0,0
beq $a0,'A',yes
beq $a0,'E',yes
beq $a0,'I',yes
beq $a0,'O',yes
beq $a0,'U',yes
jr $ra
vcount:
sub $sp,$sp,20
sw $a0,0($sp)
sw $s0,4($sp)
sw $s1,8($sp)
sw $ra,12($sp)
sw $s2,16($sp)
li $s0,0
li $s2,0
move $s1,$a0
nextc:
lb $a0,($s1)
beqz $a0,done
jal vowell
add $s0,$s0,$v0
jal vowelu
add $s2,$s2,$v0
add $s1,$s1,1
b nextc
done:
move $v0,$s0
move $t0,$s2
lw $a0,0($sp)
lw $s0,4($sp)
lw $s1,8($sp)
lw $ra,12($sp)
lw $s2,16($sp)
add $sp,$sp,20
jr $ra
.data
str: .asciiz "Long Time Ago in a Galaxy Far Far Away"
endl: .asciiz "\n"
我不明白最后的lw块是什么。程序将计数分别存储在s0和t0中,那么这一点是什么?它看起来好像只是在最后恢复原始值。哎呀,那只是为了证明它可能吗?
答案 0 :(得分:1)
我对MIPS知之甚少,但我认为这个想法类似于x86。
正如你所说,最后一个LW正在恢复原始值。发生这种情况的原因是你可以在另一个函数内部调用一个函数(初始样式),而不用担心丢失放在堆栈上的未分配给内存的变量和值(比如迭代器等)。
例如,假设您正在迭代整页文本,一次一行。您将外部循环的迭代器(页面行)存储在寄存器中。现在,当您输入计算元音数量的函数时,您不必担心会丢失该值,因此会将其推入堆栈。你的元音计数器将运行它应该做的事情,使用它想要的任何寄存器,然后当它完成时它将(根据你的方法)将值从栈中恢复到它们原来的位置。这样内部函数就不会破坏外部函数,并且你的寄存器没有被被调用方法的函数粉碎。
答案 1 :(得分:0)
我不熟悉MIPS程序集,但通常每个平台都有关于子程序应该如何表现的约定。其中一个约定通常是CPU注册子程序必须保留的。这些惯例合在一起构成了ABI。
以这种方式思考:当你有一个只有几个子程序的程序时,它很容易跟踪“是的,这个例程会在你每次调用它时都会破坏寄存器X”。但随着你的计划的发展,这变得非常困难。想象一下,更改函数以使用新寄存器的难度 - 您必须检查调用此例程的每个子例程,以确保它不依赖于调用中的寄存器。并且每个例程都会调用这些例程等。更改常用的实用程序功能,最后必须验证整个程序;这种方式就是疯狂。
有两种可维护的解决方案:调用者保存它正在使用的所有寄存器,或者被调用者保存它更改的所有寄存器。通常,您希望代码在您获得的调用链中越来越复杂(并使用更少的寄存器),因此被调用者可能有一个较小的存储集。此外,函数调用的数量通常超过函数的数量,因此被调用者保存也会产生更少的代码。看起来MIPS遵循这个逻辑,并要求被调用者保存寄存器。有时,在具有大量寄存器的架构(例如,PowerPC)上,有一些被认为是“临时的”,因此被调用者不必保存它们;这是两种方法的结合。
答案 2 :(得分:0)
最常见的MIPS调用约定
$t0,$t1,...,$t9
可以被销毁(即更改但不会恢复)。$s0,$s1,...,$s7
(被调用者已保存)寄存器必须保持不变。如果需要使用这些寄存器,您调用的函数通常会将被调用者保存的寄存器的值存储在堆栈中。被调用者保存的寄存器将在函数返回之前从堆栈中恢复。