C语言B表示的十进制数字的h = A[B[i]+j] 怎么转换成mips指令

1【专注:Python+人工智能|Java大数据|HTML5培训】 2【免费提供名师直播课堂、公开课及视频教程】 3【地址:北京市昌平区三旗百汇物美大卖场2层微信公众号:yuzhitc】

把C语言B表示的十进制数字玳码转换为MIPS:


以下为转换为mips后的代码:

代码如下:(绿色为注释)


· TA获得超过3.2万个赞

1、硬件平台是什么?pc上还是51上或者armmips?不同硬件平台彙编不同编译器对程序的处理也就不同。用汇编一般都是考虑到可以直接访问寄存器速度有优势,但最大的缺点是不能跨平台可移植性很差。

Second的相关语)是一种采取精简指令集(RISC)的处理器架构,1981年出现由MIPS科技公司开发并授权,广泛被使用在许多电子产品、网络設备、个人娱乐装置与商业装置上最早的MIPS架构是32位,最新的版本已经变成64位

应该是前者吧,就是有li、la、sw、$t0、$v0什么的那个能在Mars里运行嘚
 
Sry,我注释写的太多了

下载百度知道APP抢鲜体验

使用百度知道APP,立即抢鲜体验你的手机镜头里或许有别人想知道的答案。

}

关于伪指令与mips指令的区别:

MIPS标准萣义cpu指令集的同时也定义了伪指令伪指令可以使汇编语言可读性更好,更容易维护每条伪指令都有对应的mips指令。汇编器负责将伪指令翻译成正式的mips指令

第一个程序: li add 指令的使用

第二个程序:li la syscall指令的使用 程序的分段

第三个程序 bgt分支语句

用户输入两个数字a,b


第四个程序 ble循环語句

}

       计算机指令就是指挥机器工作的指示和命令程序就是一系列按一定顺序排列的指令,执行程序的过程就是计算机的工作过程通常一条指令包括两方面的内容: 和,操莋码决定要完成的操作操作数指参加运算的数据及其所在的。MIPS所有指令都是32位的操作码占用高6位(bit31-bit26)表示,低26位按格式划分为R型、I型囷J型但是按mips指令的功能划分,分别介绍运算指令、访存指令、分支和跳转指令、原子指令等

同时在学习MIPS指令时,需要了解64位处理器中CPU一次可以从主存加载的操作数有1字节、2字节、4字节和8字节。在C语言B表示的十进制数字和汇编中的对应关系如下表:

表1 C语言B表示的十进制數字数据类型大小和汇编助记符

理解了表1中在接下来的汇编语言学习中,当加载数据时就能清楚的知道该使用那种指令

运算型指令完荿寄存器值的算术、逻辑、移位、乘法和除法等操作。运算型指令包含了寄存器指令格式(R型)和立即数指令格式(I型)运算方式有三目运算和兩目运算。 下面我们详细介绍:

mips中和加法运算相关的指令如下:

无溢出检测的带立即数的加法

64位无溢出检测立即数加法

从上表可以看出MIPS加法指令可以完成两个寄存器的加法运算、寄存器和立即数的运算。运算可以是32位的也可以是64位的(带D标识的)其中rd、rs、rt是通用寄存器嘚任意一个。这里有“U”的代表“无溢出检测”否则就是有溢出检测。比如下面例子:

这两句都实现的是a0+a1,结果存入t0但是add 指令对加结果囿溢出检测(仅支持有符号运算),如果a0+a1结果有溢出的话那么t0不会被改变,程序会报“整型溢出异常”而addu指令表示“无溢出检测”,a0+a1嘚结果直接存入t0

c和c++语言不支持“整型溢出异常”,所以总是使用带”U”的指令

这里的“I”代表立即数。立即数就是嵌入在指令中的常數加法运算中的立即数都占用16位,所以都需要有符号扩展到32位或者64位然后再参与加法运算,比如下面的指令:

同样是实现寄存器a0和立即数0x3的加法运算但是addiu首先把立即数0x3进行有符号扩展(sign_extend)到32位,然后和寄存器a0进行加法操作正确结果被存入t0。而daddiu实现的是64位加法运算所以需要先把立即数0x3进行有符号扩展(sign_extend)到64位,然后和寄存器a0进行加法操作正确结果被存入t0。

这里解释一下为何要对立即数进行有符号扩展下圖是addiu指令的具体格式:

从上图可以看出,addiu是I型指令立即数占用16位。而addiu实现的是32位加法操作所以需要把立即数进行符号扩展到32位,然后囷rs(a0)进行加法操作结果存入rt。

再介绍一下符号扩展符号扩展是指计算机对于小字节转换成大字节的规则。比如要16个字节的数转换成32字节多出来的16个字节到底怎么填充?mips指令集中会经常需要将其中的立即数进行符号扩展符号扩展分无符号扩展和有符号扩展,区别如下:

從上表可以看出有符号扩展就是高位填充的值取决于这个数扩展前的最高位。扩展前的数高位为1,那么扩展出来的高位就都是1扩展前的數高位为0,那么扩展出来的高位就都是0而无符号扩展就是无论扩展前的数是多少,扩展出来的高位都是0

减法指令中没有带立即数的操莋,只区别是32位和64位减法减法是否有溢出。比如:

都是实现a0减a1结果存入t0操作。dsubu支持64位的减法而且没有整数溢出异常。

MIPS中乘法和除法指令如下表:

乘法运算结果低32位存入LO,高32位存入HI

无符号乘法运算结果低32位存入LO,高32位存入HI

64位乘法运算结果低64位存入LO,高64位存入HI

64位无苻号乘法运算结果低64位存入LO,高64位存入HI

除法运算商存入LO,余数存入HI

64位除法运算商存入LO,余数存入HI

无符号除法运算商存入LO,余数存叺HI

64位无符号除法运算商存入LO,余数存入HI

上表中完成了和乘法/除法相关的算术运算运算过程中,操作数(rs,rt)默认按有符号计算如需完荿无符号数的乘法/除法操作,指令里面要带“U”所以这张表里面的“U”意为”无符号”。”T”代表”结果临时存储“也就是需要特殊寄存器HI/LO存储运算结果。下面举例说明:

mul 实现了2个32位有符号数乘法运算得出可以是64位的结果并存入t0。

mult 也实现两个32位有符号数乘法运算得絀可以是64位的结果,但是结果的低32位有符号扩展到64位后存入寄存器LO高32位有符号扩展到64位后存入寄存器HI。multu 同mult指令区别是a0和a1按32位无符号数運算。

dmult 实现的是两个64位有符号数乘法运算可以得出一个128位的结果。结果的低64位存入LO高64位存入HI。

接下来在看另一组运算:

a0和a1均为无符号數

madd实现了两个32位寄存器的有符号乘法运算后,结果的低32位累加到LO寄存器高32位累加到HI寄存器。maddu功能同madd区别是操作数a0和a1为无符号数。

msub实现了兩个32位寄存器的有符号乘法运算后,结果的低32位累减到LO寄存器高32位累减到HI寄存器。msubu功能同msub区别是操作数a0和a1为无符号数。

,a0和a1均为无符号数

div實现两个有符号32位寄存器的除法运算商有符号扩展到64位存放在LO寄存器,余数有符号扩展到64位后存放在HI寄存器divu功能同div,区别是操作数a0和a1為无符号数

ddiv实现两个有符号64位寄存器的除法运算,商存放在LO寄存器余数存放在HI寄存器。ddivu功能同ddiv区别是操作数a0和a1为无符号数。

上面介紹的和乘法/除法相关的运算时涉及到两个寄存器HI、LO这两个寄存器不是通用寄存器。所以需要把里面的结果复制到通用寄存器才可以使用下面介绍和HI、LO寄存器相关的指令:

上面4条指令完成了HI、LO和通用寄存器的互拷贝操作。指令中“f”意为from“t”意为to 。mfhi实现把HI寄存器值拷贝箌t0;mflo 实现把LO寄存器值拷贝到t0mthi实现把t0拷贝到HI寄存器,mtlo实现把t0拷贝到LO寄存器

MIPS中逻辑运算指令包括与、或、非、条件设置等,具体如下表所示:

双字中的半字中字节交换

这张表中的U代表无符号数具体举例如下:

slt指令比较两个操作数寄存器t0和t1,结果为真则目标寄存器a3置1,结果为假則目标寄存器a3置0slti功能同slt,就是操作数之一是个立即数sltu功能同slt,就是要求两个操作数必须是无符号数sltiu功能也同slt,就是要求操作数之一昰立即数且两个操作数都为无符号数。

与、或、非等相关的指令比较简单这里不展开解释。

LI、LUI都是加载立即数到寄存器立即数大小昰16位。如果超过16位将会被编译拆分成多条指令指令LUI(Load Upper Immediate),这里的U代表Upper就是加载一个16位的立即数放到目标寄存器rt的高16位上,rt低16为为0例洳我在内嵌汇编里面写了下面语句:

gcc编译后会把它转化成:

指令CLO(Count Leading Ones)对地址为rs的通用寄存器的值,从其最高位开始向最低位方向检查直箌遇到值为“0”的位,将该位之前“1”的个数保存到地址为rd的通用寄存器中如果地址为rs的通用寄存器的所有位都为1(即OxFFFFFFFF),那么将32保存箌地址为rd的通用寄存器中而指令CLZ(Count Leading Zeros)和CLO相反,对地址为rs的通用寄存器的值从其最高位开始同最低位方向检查,直到遇到值为“1”的位将该位之前“0”的个数保存到地址为rd的通用寄存器中,如果地址为rs的通用寄存器的所有位都为0(即0x)那么将32保存到地址为rd的通用寄存器中。DCLO和DCLZ分别是CLO和CLZ的64位运算形式

指令INS(Insert Bit Field)实现的是32位寄存器的域值插入,就是将操作数rs的低size位插入到目标操作数的(pos到pos+size)位置。此处rs,rt均为32位数举例说明:

上面INS指令的意思是将寄存器t0的低3位,放到目标寄存器a3的第5位到第7位(bit5-bit7)DINS指令功能同INS,都实现的是目标操作数rt的低32位的域值设置区别在于rs,rt可以是64位数。而DINSM和DINSU中的M代表Middle,U代表Upper,实现的是对目标操作数rt的高32位的域值设置通常情况我们只要使用DINS指令就可以,彙编器会根据需要选择是DINSU还是DINSM

指令EXT实现32位寄存器的域值提取,就是将操作数rs的第pos位到第(pos+size-1)位提取出来结果保存到目标寄存器rt中。例洳:

上面的ext指令的意思是将寄存器t0的第5位到第7位提取出来存放到寄存器a3相关指令还有DEXT,实现的是64位操作数的域值部分提取。而DEXTM和DEXTU分别完成64位操作数rs的高32位(bit32-bit63)域值的部分提取通常情况我们只要使用DEXT指令就可以,汇编器会根据需要选择是DEXTU还是DEXTM

指令SEB和SEH实现的是符号扩展,B代表Byte(8位)、H代表HalfWord(16位)命令比较简单,这里不做举例

指令WSBH实现一个32位操作数rt内的两个半字内部进行字节交换,结果存入rd形式类似于:

移位操作是一组经常使用的指令,属于逻辑指令中的一部分它包括逻辑左移、逻辑右移动、算数右移、循环移位。其功能为将目的的所有位按规定的方式移动n位结果送入目的地址mips中移位相关指令如下表:

 可变的双字逻辑左移

移位量加 32 的双字循环右移

逻辑左移SLL(Shift Logical Left),就昰rt向左(高位)移动sa位sa是常量,取值范围在0-31之间(SLL指令是R型sa占用5bit),空出的右边(低位)补0和SLL相关的指令运算举例如下:

这个例子Φsll就是把一个32位操作数t1左移3位,低3位添0结果符号扩展到64位后存入a3寄存器。sllv功能同sll区别在于sllv中的v意为variable,就是要移位的位数不是常量而昰寄存器。dsll实现的是一个64位操作数t1左移3位低3位添0,高位溢出后丢弃结果存入a3寄存器。dsll32功能同dsll区别在于sa需要加32。dsllv功能同sllv区别在于t1表礻64位数。

逻辑右移SRL(Shft Right Logical),就是rt向右(低位)移动sa位sa取值范围在0-31,空出的左边(高位)补0和SRL相关的指令运算举例如下:

这个例子中srl就是把┅个32位操作数t1右移3位,空出的高3位添0结果符号扩展到64位存入寄存器a3。其他几组srlv、dsrl、dsrlv、dsrl32功能相信读者可以自行推倒出来

sra实现一个32位操作數t1的右移3位,空出来的高3位填充t1的bit31值结果符号扩展后存入寄存器a3。和算数右移相关的还有指令srav、dsra、dsrav、dsra32

循环右移就是操作数向右移动n位,然后把移出的n位顺序填充左边n位比如:

rota实现一个32位操作数向右循环移位,循环量是3就是把t1低3位放入t1的高3位,t1的高29位向右移动3位ROTRV功能同ROTA,只是移位量不是常数而是寄存器不过寄存器取值范围同SA(0-31)。

            访存指令就是实现对主存中的数据进行访问或存储MIPS 体系结构采用 load/store 架构。所有运算都在寄存器上进行,只有访存指令可对主存中的数据进行访问访存指令包含各种宽度数据的读写、无符号读、非对齐访存囷原子访存等。MIPS中访存指令如下:

上表中L开头的都是从主存加载数据操作S开头的都是存储数据操作。操作数据的大小有B(Byte 8bit)、H(Halfword 16bit)、W(Word 32bit)、D(Double 64bit)加载地址都是base+offset方式,offset取值范围在在-32767至32768这一点可以从任意加载指令格式可以看出。

从图4 可以看出offset域占用16bit的大小所以load相关指令嘚可寻址范围在base-32767到base+32768之间。下面举个例子说明加载指令的使用:

这里ld指令意思是从地址值为gp-32688的内存位置加载一个64位的数据赋给寄存器v0。如果地址无效程序会收到异常信号。lwu指令意思是从地址为gp+32的内存位置加载一个32位数据结果无符号扩展到64位后存入v1寄存器。

存储指令和加載指令功能上恰好相反负责把寄存器数据存储到主存。举例说明:

这里sd指令意思是把寄存器ra里的64位数写出到内存地址为(ra-352)的位置如果地址无效,程序会收到异常信号sw指令意思是把寄存器ra里的32位数写出到内存地址为s8+0的位置。

上表中有两个指令ll和sc比较特殊在本章最后┅节会单独介绍。

跳转和分支指令可改变程序的控制流mips中指令如下表:

寄存器绝对跳转,寻址空间大于256M

寄存器子程序调用寻址空间大於256M

相对PC的跳转,寻址范围小于256K

相对PC的子程序调用寻址空间小于256K

MIPS体系架构中,跳转、分支和子程序调用的命名规则如下:

  1. 和PC相关的相对寻址指令(地址计算依赖于PC值)称为“分支”(branch)助记词以b开头。绝对地址指令(地址计算不依赖PC值)称为“跳转”(Jump)助记词以j开头。
  2. 子程序调用为“跳转并链接”或“分支并链接”助记词以al结尾。

上表中的第一个指令J 表示无条件跳转到一个绝对目标地址地址可寻址范圍在256MB(原因可以参阅第5章)。使用上可以是如下形式:

如果要跳跃到大于256MB的地址可以使用JR指令(寄存器指令跳转)。JR的操作数是寄存器可以表达更大的地址空间。比如:

JAL、JALR实现了直接和间接的子程序调用这两个指令和上面的J、JR指令的区别在于,JAL、JALR在跳转到指定地址的哃时还要保存返回地址(PC+8)到寄存器ra($31)。这样在函数返回可以通过JR  ra完成常见使用如下:

这里jal test指令执行时,首先会把pc+8的地址赋值给ra那么ra就指向了“ld v0 ,32696(gp)”的位置。然后程序跳转到test程序运行test子程序最后会通过“jr ra”指令返回到“ld v0 ,32696(gp)”的位置。

如果要跳转的子程序超过了258M可以使用JALR指令完成。这里不做介绍

相对寻址(PC-relotive)的子程序调用可以用BAL。BAL指令使用16位存储offset所以可寻址范围特别小,仅为256K所以平时很少使用箌。

BEQ、BNE、BLEZ、BGTZ、BLTZ、BGEZ的功能都是带条件的分支跳转即当操作数寄存器rs和寄存器满足一定条件时,才会跳转到label以BEQ为例:

当寄存器$8和$4值相等时,跳转到标识符again位置这里again的地址计算是依赖于程序计数器PC,计算过程由汇编器帮助我们完成我们所要注意的就是again的范围必须在(PC-127K)至(PC+128K)之间 ,否则分支跳转就会失败跳转失败不会有异常。

BLTZAL、BGEZAL指令 实现的是带条件的子程序调用以BLTZAL指令为例:

bltzal指令意思是如果寄存器a0的徝小于0,那么PC跳转到子程序test这里要注意的是无论调用是否发生,返回地址一律保存到寄存器ra中

协处理器0指令CPU控制指令

MIPS的协处理器0指囹就是CPU控制指令,包括数据在通用寄存器和CP0寄存器间的读写、管理内存、处理异常等具体指令如下表所示:

从 CP0 寄存器取字

往 CP0 寄存器写字

從 CP0 寄存器取双字

往 CP0 寄存器写双字

异常返回,返回EPC中保存的地址

在 TLB 中搜索匹配项

MFC0、MTC0、DMFC0、DMTC0可以完成通用寄存器和CP0寄存器之间的数据传送比如峩们可以通过下面指令来获取epc寄存器的值。

取epc寄存器($14)数据存到通用寄存器t0

mfc0完成从cp0寄存器获取一个32位数据(如果cp0寄存器是64位数据则只取其低32位。)到通用寄存器如果要获取的是64位数据则需要使用dmfc0指令。

注意:和CP0相关的指令执行都需要特权模式比如上面的mfc0指令的运行需要root权限或者sudo才可以运行。所以在一般用户程序中很少见到这些CP0相关的控制指令

mips64中,除了本章列举的运算指令、控制指令、跳转指令等还有一些无法归类的指令,这里统称为其他指令具体如下表:

SYSCALL指令可以产生一个引发系统调用的异常,相当于x86的 “int 0x80”指令它的使用方式在本书后面第6章会有专门的讲解。

BREAK指令也称断点指令它可以产生一个“断点”类型的异常。在我们调试代码时break指令是很有用的调試手段了。比如我们在C语言B表示的十进制数字中插入一个break指令:

那么程序执行到这条指令时就会收到一个SIGTRAP信号,提示信息为“Trace/breakpoint trap”

TEQ、TNE都昰条件自陷指令。如果条件成立就会引发一个自陷(Trap)异常自陷指令又叫做访管指令,出现在计算机操作系统中用于实现在用户态下運行的进程调用操作系统内核程序,即当运行的用户进程或系统实用进程欲请求操作系统内核为其服务时可以安排执行一条陷入指令引起一次特殊异常。通常自陷指令是给编译器和解释器用的可以实现运行时数组边界检查之类的操作。比如我用C语言B表示的十进制数字写叻一个除法语句:

gcc编译完成后对应的汇编指令就是下面的样子:

上面的div实现两个有符号数的除法运算,商存入lo余数存入hi。下面第二句teq僦是判断v0(代表C语言B表示的十进制数字里的变量b)是否为0如果为0,那么程序自陷0x7会被保存到Cause寄存器。程序会收到信息“浮点数例外”后面的mfhi、mflo将不再执行。

SYNC是用在多核处理器的内存操作(load/store寄存器)同步指令用来保证 sync 之前对于内存的操作能够在 sync 之后的指令开始之前完成。仳如下面指令:

RDHWR指令允许用户态读取硬件寄存器(CP0寄存器)当然并不是所有硬件寄存器都可以读取的,而是受到CP0寄存器HWREna限制的HWREna决定了哪些硬件寄存器可以被用户态程序访问。当前支持可读的寄存器信息如下表:

用于获取当前进程正在运行在哪个CPU上实际是访问CP0寄存器Ebase的CPUNUM域

用于获取一级高速缓存行的有效宽度

用于获取线程本地存储(TLS)的位置

在第一章我们已经举例了用RDHWR指令获取TLS地址方法。这里再举例说明洳何获取当前进程正在运行在那个CPU核上指令如下:

这里就是从RDHWR的0号寄存器获取CPUNum信息到通用寄存器v0($2)。

MOVE、MOVZ、MOVN都实现了寄存器到寄存器的複制MOVE是无条件复制,例如:

move就是把寄存器a2的值复制到寄存器a1这条指令和下面的指令相等:

ll和sc指令具体介绍如下图:

rt,offset(base)执行时,首先会检查是否可以保证从上次执行的LL开始以来的读写操作能否原子性的完成如果能,rt值被成功存储同时rt被置1。否则rt被置0所以上面实例中sc指令完荿后,会有个对v0值的判断“beq v0,zero,atomic_inc”如果v0值为0说明原子操作失败,需要返回atomic_inc函数入口重试

}

我要回帖

更多关于 C语言B表示的十进制数字 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信