注释符号: #
add rd, rs1, rs2 # rd = rs1 + rs2 针对寄存器操作
add rd, rs1, x0 # rd = rs1 + x0 x0代表零号寄存器 只读 代表0 这个语句就是把rs1赋给rd
sub rd, rs1, rs2 # rd = rs1 - rs2 针对寄存器减法操作
addi rd, rs1, 10 # rd = rs1 + 10 针对寄存器和立即数的加法操作
addi rd, rs1, -10 # rd = rs1 - 10 减法也是用addi
内存地址的存储方式有两种 即big endian(大端):低位字节存在高位字节地址上
和 little endian(小端):低位字节存在低位字节上
risc-v 使用的是小端
lw rd, 12(rs) #lw代表load word的意思 把rs作为基址寄存器加上偏移量12的值加载到rd
load的过程是内存到寄存器
sw rs, 40(rd) #sw代表store word 把rs寄存器的值存到rd加偏移量40的内存地址中
sw的过程是寄存器到内存
lb #load byte 将字节地址中的1字节数据符号扩展后装载到对应寄存器中
比如1000 0000 扩展就是把最高位的1作为符号位扩展 形成0xffffff80
lbu #load byte unsigned 就是把填充的都用0来填充
sb #store byte 将寄存器中最低字节位的1字节数据保存到对应字节地址中
跳转指令:
beq rs1, rs2, L1 #branch if equal 如果rs1等于rs2 那么就跳转到L1
bne #branch if not equal 不等于跳转
blt #branch if less than 小于跳转
bltu #branch if less than unsigned 小于跳转 把寄存器看作是无符号数
bge #banch if greater or equal 大于等于跳转
j L1 #jump L1 直接跳转到L1
逻辑运算指令:
and rd, rs1, rs2 #rd = rs1 & rs2
andi rd, rs1, 3 #rd = rs1 & 3
sll #shift left logical 逻辑左移 右边0填充
slli rd, rs1, 2 #shift left logical immediate 逻辑 立即数左移 把rs1逻辑左移2位 右边0填充
sra #shift right arithmetic 算术右移指令 即移动后左边用原数最高位填充
srai #立即数算术右移 移动后左边用原数最高位填充
srl #逻辑右移 移动后左边用0来填充
srli #立即数逻辑右移 移动后左边用0来填充
or #逻辑或
ori #立即数逻辑或
xor #逻辑异或
xori #立即数逻辑异或
risc-v中也有一些伪指令 比如mv rd, rs = addi rd, rs, 0
li rd, 13 = addi rd, x0, 13
它们用来简化书写的语法
通过编译得到的可执行文件 指令段和数据段分别存放在内存里的指令空间和数据空间中
PC寄存器存放着下一条待执行指令的地址 通过不停的更新PC值来让处理器不停执行
一般情况下是顺序执行 就是PC值加4 但是遇到分支指令的时候 就需要更新为跳转位置
函数的调用6个基本步骤:
1.发生函数调用时,在执行函数功能前,先将这次调用中用到的参数保存,方便取用
2. 将控制权交给这调用的功能函数
3.根据情况为函数申请一定的本地存储空间,以满足函数执行过程中需要的存储需求
4.执行该函数的操作
5.在函数执行完成后,将得到的结果数据存放好,便于主进程来获取,同时还原函数执行过程中使用到的寄存器值,释放分配给函数的本地存储空间
6.将控制权转移给原进程
zero代表x0寄存器
通常a0 - a7 (x10 - x17)寄存器是用来向调用的函数传递参数,a0和a1寄存器常用于传递返回值
ra,即x1寄存器,用来保存返回时的返回地址值
s0 - s11 对应的编号x8 - x9 和 x18 - x27的寄存器用来作为保存寄存器,保存原进程中的关键数据避免在函数调用过程中被破环
jr #函数调用可能发生在程序段多个位置,每次调用函数返回地址都不同 随时都会发生变化
需要在函数调用前记录下返回地址并保存在ra寄存器中 返回时用jr指令返回到ra寄存器中保存的地址保证多次调用的灵活性
jal #跳转并链接 可以形成指向调用点的地址或链接,从而是函数能返回正确的地址
跳转则会使PC跳转指向被调用函数的地址 并且将链接得到的下一指令的地址作为返回地址 保存在ra寄存器中