note-RISC-V

note-RISC-V

SYuan03 Lv4

伪指令与基本指令

伪指令就是并不是硬件实现的直接指令,只是给解释器看的,比如li

就是说向li这种指令,其实并不直接对硬件进行操作

实际上他就是一个addi指令

加法、减法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
li t0, 20	# li: load immediate 加载立即数,现在这种写法是10进制
li t1, 22
add t2, t0, t1

li t0, 32
addi t0, t0, 32 # addi: add immediate
addi t0, t0, -16 # 没有subi指令,所以只能这样,但是sub还是有的

# f = (g + h) - (i + j)
# t6 = (t0 + t1) - (t3 + t4)
li t0, 0
li t1, 10
add t2, t0, t1
li t3, 30
li t4, 40
add t5, t3, t4
sub t6, t2, t5

系统调用

一共4步

1
2
3
4
5
6
7
8
9
10
11
12
# Step 1. Load the service number in register a7. 
# Step 2. Load argument values, if any, in a0, a1, a2, a3, fa0, ... as specified.
# Step 3. Issue the ECALL instruction.
# Step 4. Retrieve return values, if any, from result registers as specified.

li t0, 16
li t1, 32
add t2, t0, t1

li a7, 1 # a7存放系统调用号,此处为输出
mv a0, t2 # argument1,伪指令,实际上是add a0, zero, t2
ecall

mv其实是cp并不是会把原来的地方的数据就清空了的

.data .text

.text就是代码指令开始的地方

.的就是写给汇编器看的,比如.data编译器就会放到.data的块的地方

.text就会放到存放代码的地方(如0x0040 0000)

.word = 4byte

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
.data 
g: .word 16
h: .word 48
i: .word 0
j: .word 0
result: .word 0
msg: .string "Hello world!:" # .string会在末尾加'\0'而.ascii不会

.text
la t0, g # load address to t0
lw t0, 0(t0) # load word t0中的地址+偏移量0
la t1, h # load address to t0
lw t1, 0(t1) # load word t0中的地址+偏移量0

add t5, t0, t1

la t0, result
sw t5, 0(t0) # t5的结果保存到t0的地址中

# la t1, msg
# li a7, 4
# mv a0, t1
# ecall

li a7, 4 # 打印字符串
la a0, msg # 其实可以直接la到a0里面就行,不需要再经过寄存器中转了,本身就是地址
ecall

li a7, 1 # 打印数字
mv a0, t5
ecall

数组

1
2
3
4
5
6
7
8
.data
nums: .word -30, -40, -50, -60, 60, 100

.text
la t0, nums
lw t1, 8(t0)
addi t1, t1, 50
sw t1, 8(t0) # 把-50变成了0

branch-max.asm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 实现c = max(a,b)

.data
a: .word 100
b: .word 200
c: .word 0

.text
# la t0, a
# lw t0, 0(t0)
lw t0, a # 该RARS支持的一个语法糖,并不是RISC-V的语法,考试应该不能用
lw t1, b

bge t0, t1, greater_equal # t0 >= t1 就跳转到greater_equal
mv t2, t1
j end # 伪指令, 防止再执行到greater_equal

greater_equal:
mv t2, t0

end:
la t3, c
sw t2, 0(t3) # 存到c的地方,同样也有简便写法 sw t2, c, t3

array-index-bit.asm求一个数组正数的和与负数的和

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
.data
numbers: .word -30, 30, -20, 20, -10, 10, 0
size: .word 7
positive_sum: .word 0
negative_sum: .word 0

.text
la t0, numbers # t0: the address of the current array
lw t1, size
mv t2, zero # counter, initially 0
li t3, 0 # t3: sum of positive numbers <- 0
li t4, 0 # t4: sum of negative numbers <- 0

loop:
bge t2, t1, end_loop #计数器大于等于size就跳转到end_loop
# number[t2]
# mul t5, t2, 4
slli t5, t2, 2 # 逻辑左移,即t5*4
add t5, t0, t5
lw t5, 0(t5)
addi t2, t2, 1
bltz t5, negative # bltz: branch if less than zero
add t3, t3, t5
j loop
negative:
add t4, t4, t5
j loop
end_loop:
sw, t3, positive_sum, t5
sw, t3, positive_sum, t5

proc-max.asm函数调用

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
.data
max_result: .word 0

.text
.global main

max:
# a0 argument 0
# a1 argument 1
# 同时a0-a7的前两个寄存器也可以用来返回值的保存
blt a0, a1, smaller
# 没跳转说明a0 >= a1
# mv a0, a0 正好省略不用写
j end_max

smaller:
mv a0, a1

end_max:
ret # 等同
# jr ra # 等同
# jalr zero 0(ra) # jalr: jump and link register
# zero的作用:jalr会将下一条指令的地址存入

### main ###
.data
a: .word 100
b: .word 200

.text
main:
lw a0, a # 并非risc-v的指令,而是汇编器的伪指令,将a的地址存入a0
lw a1, b

# jal ra, max # jal: jump and link ra: Return Address register
# ra保存了返回地址,jal会将下一条指令的地址存入ra
# jal max
call max # 三种写法都一样
sw a0, max_result, t0

调用函数

参数多了可以考虑用栈来传递

1
2
jalr zero 0(ra) # jalr: jump and link register
# zero的作用:jalr会将下一条指令的地址存入

zero的作用:

jalr会做两件事情

  • 一是按照后面给的0(ra)进行跳转

  • 二是会把目前所在位置的下一条指令的地址存入给出的寄存器中,如果把zero换成t0就会保存到t0,写zero的话就表示不想存这个下一条地址(用不到),因为写到zero的东西都会被清零

proc-fact.asm递归

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
.text
.global main

factorial:
beqz a0, base_case # 为0跳转,f(0) = 1

# 先改变栈指针,再保存寄存器的值,先保存参数寄存器的值,再保存ra的值
addi sp, sp, -8 # 栈空间高地址往低地址增长,所以先减8
sw a0, 4(sp) # 先保存a0的值,即n
sw ra, 0(sp) # 还需要再保存ra的值

# n > 0: n * f(n-1)
addi a0, a0, -1 # 此时a0为n-1
call factorial # a0为f(n-1)
mv t0, a0 # t0:f(n-1)

lw ra, 0(sp) # 恢复ra的值
lw a0, 4(sp) # 恢复a0的值
addi sp, sp, 8 # 恢复栈指针

mul a0, a0, t0 # a0 = n * f(n-1)
j end # 返回, a0还要用来保存返回值

base_case:
li a0, 1

end:
ret

### main ###
.data
n: .word 5

.text
main:
lw a0, n
call factorial

注意ra的值会变

  • 标题: note-RISC-V
  • 作者: SYuan03
  • 创建于 : 2023-06-23 21:13:08
  • 更新于 : 2024-03-10 19:54:17
  • 链接: https://bblog.031105.xyz/posts/2023-Spring-Courses-编译原理/note-risc-v.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论