简单介绍龙芯汇编

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:
    .asciiz "Hello Baby!\n"




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接收


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值