1. 判断的实现
看这条C语句的汇编实现:
if(i == j)
f = g + h;
else
f = g - h;
------------------------------------------------------------
bne $15, $16, Else #i!=j,则跳转到Else,注意:$15.$16表示寄存器
add $17, $18, $19 # f=g+h
j Exit #无条件跳转
Else:
sub $17, $18, $19 # f=g-h
Exit:
2. 小于判断的实现
if(i >= j)
f = g + h;
else
f = g - h;
--------------------------------------------------
slt $9, $15, $16 # i<j则$9=1,否则$9=0
bne $9, $0, Else # $9!=0,则跳转到Else
# 上面两条指令实现了“小于则转移”
add $17, $18, $19 # f=g+h
j Exit #无条件跳转
Else:
sub $17, $18, $19 # f=g-h
Exit:
另一种实现为:
sub $9, $15, $16 # $9=i-j
bltz $9, Else # $9小于0,则跳转到Else
#上面两条指令实现了“小于则转移”
add $17, $18, $19 #f=g+h
j Exit #无条件跳转
Else:
sub $17, $18, $19 #f=g-h
Exit:
3. 更多判断的实现
sub t0, s0, s1 # t0 = s0 - s1 -------- [1]
bgez t0, Else #t0大于等于0,则跳转到Else
#上面两条指令实现了大于等于则转移
bgtz t0, Else #t0大于0,则跳转到Else;与[1]联用,实现大于则跳转
blez t0, Else #t0小于等于0,则跳转到Else;与[1]联用,实现小于等于则跳转
bltz t0, Else #t0小于0,则跳转到Else;与[1]联用,实现小于则跳转
Else:
......
加上前面的 bne, beq相等和不等的跳转指令,可以实现所有的逻辑判断。
4. 循环的实现
while(s==k)
f = g + h;
----------------------------------
loop:
add t1, s3, s3 # t1 = 2*i
add t1, t1, t1 # t1 = 4*i
add t1, t1, s6 # s的地址置于t1
lw t0, 0(t1) # t0 = s
bne t0, s5, Exit # s !=k,则停止循环
add s3, s3, s4 # i = i+j
j loop # 跳至loop
Exit:
5. 完整的例子
.text
.globl main
.ent main
main:
.frame $fp, 32, $31 #帧指针为$fp,帧大小32,返回寄存器$31
.mask 0xc0000000,-4 # $31,$30保存于28($sp),0xc0000000为需保存之寄存器位图
.set noreorder #关闭汇编器自动填充延迟槽
.cpload $25 #汇编伪操作,告诉汇编器根据$25寄存器正确设置gp
.set nomacro
addiu $sp, -32 # 栈空间分配32字节
sw $ra, 24($sp) # 保存返回地址
sw $fp, 20($sp) # 保存帧指针
move $fp, $sp # 帧指针就绪
.cprestore 8 #汇编器伪操作,保存gp于8($fp)
#预留传参数的栈空间;printf会直接对栈上预留给参数的空间直接操作
#会覆盖掉8($fp)处的gp,造成调用失败。
addiu $sp, $sp, -20
li $18, 0x3 # 循环次数为3
lui $8, %hi(msg2) # %hi() 求地址的高16位, %lo()求低16位
addiu $17, $8, %lo(msg2) # $17中为字符串首地址
loop:
move $4, $17 # printf之第一个参数
lw $25, %got(printf)($28) #获取printf函数地址
jalr $25 # 调用printf
nop
lw $28, 8($fp) # 从栈回复gp,因为进入printf后gp被改写
addiu $18, $18, -1
bne $18, $0, loop #不等于0,跳转到前面的loop
nop
move $sp, $fp # 栈指针复位
lw $ra, 24($sp) #返回地址恢复
lw $fp, 20($sp) #原帧指针恢复
addu $sp, 32 # 回收栈空间
move $2,$0 # 返回值为0
jr $31 # 返回
nop
.end main
.rdata
msg2:
1. Hello World
从经典的Hello World开始:
.text #表示将后面的代码编译后置于目标文件的.text段
.globl main #声明main为全局变量。该变量名会置于符号表中
.ent main #告诉汇编器main函数在此开始,调试用
main: #标号,表示main函数的始地址,有实际意义
lui $4,%hi(cmd) #Load Upper Immediate
addiu $4,$4, %lo(cmd) #execve的第一个参数置于a0, 为字符串/bin/echo的首地址
lui $5, %hi(argv) #%hi,%lo为汇编器定义的宏,分别求地址的高16和低16位
addiu $5,$5, %lo(argv) #execve的第二个参数置于a1, 为字符指针数组的首地址
move $6,$0 #第三个参数a2为0 (NULL)
li $2, 4011 #将execve系统调用号置入v0寄存器
syscall #系统调用
move $2,$0 #main的返回值0, 置于v0
jr $31 #main返回
.end main #告诉汇编器main在此结束,调试用
.data #以下内容位于目标文件的数据段
cmd:
.asciiz "/bin/echo"
msg:
.asciiz "Hello world!"
argv:
.word cmd # /bin/echo的首地址
.word msg # Hello world!的首地址
.word 0x0 # NULL
存为helloworld.s,gcc helloworld.s -o hw 编译之,执行结果:
Hello world!
2. 带立即数的指令
汇编或机器语言中,称指令中的常数为立即数(Immediate)
上面的例子我们看到这些指令:lui, li, addiu
lui: Load Upper Immediate, 加载立即数到寄存器的高16位
lui t0, 0xaa55 # t0 = 0xaa55 0000
addiu t0, t1, 64 # t0 = t1 +64,为无符号相加
带立即数的指令,指令编码格式属于I-型,回忆下其格式: 6, 5, 5, 16预留给立即数的空间是16位,因此指令中的立即数范围,无符号为0~2^16-1,有符号为-2^15~2^15-1
如果超过范围,可以先用 li 将立即数置入寄存器,然后使用add运算
3. li (Load Immediate)
li (Load Immediate)没有16位的限制,因为它不是单一的一条指令,而是由汇编器定义的一个“复合”指令,一般称其为宏指令。
常见情况下,li的展开:
li t0, -5 -----> addiu t0,zero, -5
li t0, 0x8000 -----> ori t0, t0, 0x8000
li t0, 0x12345 -----> lui t0, 0x1
ori t0, t0, 0x2345
4. 龙芯下,系统调用的约定
v0: 用于置系统调用号
a0~a3: 置前四个参数,后面的参数用栈传
syscall 系统调用触发指令
返回值依旧使用v0接收
龙芯编译架构 龙芯汇编
转载本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
龙芯LS2K0300久久派上手体验
龙芯开发板初次体验
龙芯 LS2K0300 LoongArch 久久派 -
【驱动篇】龙芯LS2K0300之LED驱动
LED驱动及测试用例
龙芯 LS2K0300 LED 驱动 LoongArch -
【驱动篇】龙芯LS2K0300之驱动示例
Linux最小驱动
龙芯 LoongArch LS2K0300