设计思想

  1. 发现斐波那契数列的排列规律,有1,1,2,3,5,8……,即有函数规律:f[n]=f[n-1]+f[n-2]。

  2. 先构成main函数,进行数据的初始化并编好函数框架。

  3. 包含FIB函数,依次按照规律将数据存储到buff缓冲区,并判断是否溢出。

  4. 编写print函数进行打印,根据要求先打印下标,然后是十进制数,再通过hex分支打印对应的十六进制数据。并且每行结束后打印空格。

  5. 有机地联系起来,还用到了堆栈保护特定的数据或地址。

流程图

流程图

开发环境

Windows平台+Mars环境

程序源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
################################################################

#程序功能描述:

#输入整数N,将对应的前N个斐波那契数存入数组

#分别输出前N个元素的十进制数和十六进制数

#若数值错误,输出错误信息

################################################################

#寄存器的使用:

#$a0:存储数值N,用于在程序中传递N

#$a1:传递数组的地址

################################################################

.data

start: .asciiz "\n Input N = "

last1: .asciiz "\n The result is: \n"

last2: .asciiz "index Dec Hex\n"

out1: .asciiz "\n N is not legal\n" #N输入非法

out2: .asciiz "\n overflow\n" #溢出

empty: .asciiz " "

Hex: .asciiz " 0Xxxxxxxxx\n"

buf: .word 1,1

.space 4096 #设定数组大小

.text

main: la $a0,start

​ li $v0,4

​ syscall #输出start字符串

​ la $a0,buf #数组首地址

​ move $a1,$a0 #$a1是地址

​ li $v0,5

​ syscall #输入N

​ addi $v0,$v0,-1

​ move $a0,$v0 #$a0作为计数器,等于N-1

​ bltz $a0,out_1 #N非法输入

​ move $t5,$a1

​ move $t6,$a0

​ li $s1,1

​ jal FIB #按要求存储数据

​ addi $a3,$a0,0

​ li $a2,0

​ la $a0,last1

​ li $v0,4

​ syscall

​ la $a0,last2

​ li $v0,4

​ syscall

​ jal print #按要求打印数据

​ li $v0,10 #退出

​ syscall

#################### FIB函数 #####################

#函数名称:FIB

#函数功能:将斐波那契数列存到数组中,并判断溢出

#寄存器功能:

#$t1,$t2:传递数组地址

#$a3:计算相应的斐波那契数

################################################################

FIB: ble $t6,$s1,ret #$t6<=1就返回

​ lw $t1,($t5)

​ lw $t2,4($t5)

​ addu $a3,$t1,$t2

​ addi $t4,$0,-1

​ subu $t3,$t4,$t1 #f3=f1+f2

​ bltu $t3,$t2,out_2 #判断溢出

​ addi $t5,$t5,4

​ sw $a3,4($t5)

​ addi $t6,$t6,-1

​ b FIB

#################### print函数 #####################

#函数名称:print

#函数功能:分别输出斐波那契数列的前N项的下标、十进制表示、十六进制表示

################################################################

print: bgt $a2,$a3,ret

​ move $a0,$a2

​ li $v0,1

​ syscall #打印下标

​ la $a0,empty

​ li $v0,4

​ syscall #空格

​ lw $a0,($a1)

​ li $v0,1

​ syscall #打印十进制数值

​ la $a0,empty

​ li $v0,4

​ syscall #空格

​ lw $a0,($a1)

​ addi $sp,$sp,-16

​ sw $ra,12($sp)

​ sw $a0,8($sp)

​ sw $a2,4($sp)

​ sw $a1,($sp) #用堆栈存储来保护寄存器中的值

​ jal hex #调用hex函数转换成十六进制

​ lw $ra,12($sp)

​ lw $a0,8($sp)

​ lw $a2,4($sp)

​ lw $a1,($sp) #恢复相应寄存器的值

​ addi $sp,$sp,16

​ la $a0,Hex

​ li $v0,4

​ syscall #输出转换好的的十六进制字符串

​ addi $a1,$a1,4

​ addi $a2,$a2,1

​ b print

hex: la $a0, Hex

​ lw $a1,8($sp)

​ li $a2, 7 #循环次数

​ addi $t1, $a0, 10 #从位置buf+10处开始存放16进制数

loop: andi $t0, $a1, 0x0f #取a1的低4位

​ srl $a1, $a1, 4 #a1右移4位

​ bge $t0, 10, char #t0大于等于10跳转到为A-F处理

​ addi $t0, $t0, 0x30 #0的ASCII码为0x30,在原先基础上加0x30

​ b put

char: addi $t0, $t0, 0x37 #A的ASCII码为65,在原先基础上加(65-10)

put: sb $t0, ($t1) #放置字符

​ addi $t1, $t1, -1 #放置位置前移一个字符

​ addi $a2, $a2, -1 #将循环次数减1

​ bgez $a2, loop #判断循环是否结束

​ jr $ra

out_1: la $a0,out1 #N非法

​ li $v0,4

​ syscall

​ b out

out_2: la $a0,out2 #数值出现溢出

​ li $v0,4

​ syscall

​ b out

ret: jr $ra #返回地址

out: li $v0,10 #退出

syscall

测试过程

​ 刚开始测试出来,十六进制值与预想结果不符合,发现调用函数不注意保护寄存器的值,经过个别修改后,正常运行且结果正确。

结果

2.png

3.png

4.png

5.png

6.png

写在最后

​ 编写汇编语言需要有严谨的思路和函数框架,寄存器的使用也要符合规则,否则后期很难调试出来。设计层面也要注重可行性,尽可能地减小编写过程的困难程度,综合考虑汇编语言的编写难度和调试难度。编写的同时应该注意注释的标注,以便后期的查看和修改,养成好习惯。

​ 调试过程中应该注意寄存器的数值是否与预期相符合,以及程序运行的跳转顺序是否正确,这样可以快速确定错误点,从而进行修改。