【MIPS】--- 教你看懂MIPS架构的基本汇编程序(全是干货)

使用龙芯Ls2k1000系列处理器,遇到内存的问题,真的是百般刁难。要梳理内存初始化流程,然后最麻烦的是龙芯的内存乃代码全是汇编源码,看起来着实有点头疼,被汇编按在地上摩擦了无数回后,也算是对汇编有个基本的认识。接下来用一篇文档教你如何快速入门看懂龙芯MIPS的汇编源码!

按照国际惯例,先抛问题
1. 汇编常用指令
2. MIPS架构处理器的寄存器简析
3. 汇编语法总结
4. 基本的编程思想

问题一:汇编常用指令

这里我就不一一介绍了,龙芯的GS265_user_Guid中对大多数指令已有了简单的介绍,直接查阅即可。

   


有人说,汇编早已淘汰,属于计算机历史上侏罗纪时代的古董产物;还有人说,汇编是让你深刻理解计算机处理机制的神兵利器。

个人感觉,争执这种问题都是瞎扯。对于汇编这种语言,要求自己能看懂即可。简单概括,一次看懂,终身受益。不同处理器无非指令不一样,但是汇编处理的逻辑和方式都一样。因此,各位看官要好好理解本文哦。

问题二:MIPS架构处理器的寄存器简析

1. 通用寄存器

MIPS32架构定义了32个通用寄存器,使用$0、$1……$31表示,都是32位。其中$0一般用做常量0,具体的用法可以参考下图。
这其中你最常用的应该就是a0-a3, t0-t9, s0-s7,编程中的变量运算等等,都离不开这些寄存器。

 

2. 特殊寄存器
MIPS32架构中定义的特殊寄存器有三个:PC(Program Counter程序计数器)、HI(乘除结果高位寄存器)、LO(乘除结果低位寄存器)。进行乘法运算时,HI和LO保存乘法运算的结果,其中HI存储高32位,LO存储低32位;进行除法运算时,HI和LO保存除法运算的结果,其中HI存储余数,LO存储商。

3. MIPS架构属于小端模式,具体什么是小端,参考之前的一篇文章

问题三: 汇编语法总结---直接以LS2k代码为例进行实战讲解

1. 标号1,2,3……,这些标号什么意思,如何理解?
标号的意思基本可以理解为打个标签,方便汇编代码执行跳转指令。

2. 标号用途详解

汇编不同于C,没有那么智能,仅能顺序执行,但代码逻辑必定不能线性执行。我们以循环执行nop空与语句为例进行说明:

C编程,如下:

 

汇编编程,如下:

含义解释如下:

    dli : 取双字常数装载到a3寄存器
    subu: 无符号减,等于于 a3=a3-1
    nop : 空操作,一般用于短延时
    Bgtz: 大于0,则跳转

通过上述例子,应该能理解这些一大堆标号的意思,其中跳转是有时会看到1b,1f, 1b表示向上跳转至标号为1的代码处执行;1f表示向下跳转至标号为1的代码处执行,一般都是同一个函数内由于代码执行逻辑非线性,才会用这种简单的标号表示。如果类似于高级编程语言那种函数的话,一般会重新封装一个函数来做,这是你看到的标号,就不是简单的数字,有可能就是自己起的名字了。

问题四: 汇编实战练习
想实际理解汇编代码,最简单的方式,实战来个程序即可。接下来以我自己写的一个例子进行讲解。当内存控制器中的某个寄存器值为0x68,则执行拉低GPIO的操作。

1. 问题背景,内存Training是GateLeveling过程中,需要进行8个Byte的PreambleCheck,这个preaam_check_init函数将会被循环执行8次,每次执行会改变的变量是dll_gate寄存器地址。实际测量得dll_gate2寄存器校验出错,因此在开始进行dll_gate2值校验之前,我要拉低一个GPIO作为触发信号,然后进行信号测量。

2. dll_gate寄存器的地址大致如下:

  

3.判断0x68寄存器的值,符合条件则跳转的代码如下:

 

1)  t1含义解释:t1表示得是dll_gate得地址,也就是从0x38, 0x58, 0x78……等

2)  红框内代码解释:

line1:   将t1得知放进a0(别问为啥,因为没有为啥), 此时a0表示当前正在校验得dll_gate地址

line2:   a0 = a0&0xff, 仅保留低2位。因为目标地址是0x78,放置高位垃圾值影响我得判断

line3:   If a0==0x68, 跳转至inspur_gpio_test这个函数下执行

4. inspur_gpio_test的程序如下,分为五步解释:

 

Tips: 明确我们得目的是将GPIO27信号进行拉低拉高,产生跳变,先理解GPIO相关得寄存器如何配置:

 

 接下来进行代码分析(分成了五个小块):

1)设置GPIO方向寄存器

1     li t4, 0xbfe10500      //将0xbfe10500这个地址装载到t4寄存器
2     lb t6, 0x3(t4)         //将0xbfe10500+3这个地址得单Byte值装载到t6
3     and t6, t6, 0xf7       //t6 = t6&0xf7   (这步就是设置GPIO270为输出模式的操作)
4     sb t6, 0x3(t4)         //将设置后的t6写入到寄存器中

Tips:
    问题1: 0x3(t4)这种书写表示什么意思?
    答: 简单可以理解一个地址操作,就是t4地址再便宜0x3
    问题2. and t6, t6, 0xf7为何是清0操作?
   答:根据龙芯手册表述,GPIO方向控制寄存器位阈为[63:0],也就是8个Byte,从低到高,每个bit表示一个GPIO,因此GPIO27应该是0xbfe10500这个起始位置的第27个bit,基本表示如下:

2)设置GPIO27输出低电平

1     li t4, 0xbfe10510   //将GPIO电位设置寄存器的基地址放入t4
2     lb t6, 0x3(t4)      //将0xbfe10510+3的地址的单byte数据放入t6寄存器
3     and t6, t6, 0xf7    //将GPIO27的bit清0,使得GPIO27输出低
4     sb t6, 0x3(t4)      //将t6的值保存至对应寄存器中

3)延时,前面举过例子,这里不做详解

1     dli a3,0xff         //delay
2     1:
3     subu a3, 0x1
4     nop
5     bgtz a3, 1b


4)设置GPIO27输出为高(由于前面已将GPIO电位设置寄存器地址放进t4,且没有修改,这里可直接用)

1     lb t6, 0x3(t4)     //参考问题三中以延时函数举例的说明
2     ori t6, t6, 0x08
3     sb t6, 0x3(t4)

5)函数执行完毕,跳回到之前的位置继续向下执行

1     PRINTSTR("
 start inspur GPIO end
")
2     b Inspur                                    //跳转至Inpsur标签位置继续执行

通过这样的例子,希望能更加清楚的让各位了解汇编代码的执行,不同架构处理器区别仅在于指令不一样。但是汇编的执行流程和逻辑均类似,可以对比学习

如有其他问题,请直接回复留即可,我看到后会尽快和您交流。

也欢迎您关注我的个人公众号,一起成长,一起交流。

如有需要龙芯MIPS架构指令手册文档的,关注我的公众号后,回复"MIPS指令",自动获取。 

原文地址:https://www.cnblogs.com/szhb-5251/p/13091460.html