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