前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >十分钟教会你MIPS编程入门

十分钟教会你MIPS编程入门

作者头像
物联网安全小编
发布2021-01-21 10:07:07
2.1K0
发布2021-01-21 10:07:07
举报
文章被收录于专栏:物联网IOT安全
本文从四个方面来介绍MIPS架构及其汇编:

1. 寄存器种类

2. 算术及寻址指令

3. 程序结构

4. 系统调用

需要使用的工具是:Mars4.4

下载地址:

代码语言:javascript
复制
http://courses.missouristate.edu/KenVollmar/mars/download.htm

1 数据类型

1. 所有MIPS指令都是32位长

2. 1字节 = 8位,半字长 = 2个字节,1字长 = 4个字节

3. 一个字符空间 = 1个字节

4. 一个整型 = 一个字长 = 4个字节

5. 单个字符用单引号,例如:'b'

6. 字符串用双引号,例如:"A string"

2 寄存器

1. MIPS下一共有32个通用寄存器

2. 在汇编中,寄存器标志由$符开头

3. 寄存器表示可以有两种方式

直接使用该寄存器对应的编号,例如:从0到31

使用对应的寄存器名称,例如:t1,sp(详细名称见下表)

4. Lo和Hi寄存器专门用来存储乘法和除法的结果

对于以上两者,不存在直接寻址;必须要通过特殊指令mfhi ("move from Hi") 和 mflo ("move from Lo")来访问内容

5. 栈的走向是高地址到低地址

3 程序结构

1. 本质其实就是数据声明 + 普通文本 + 程序编码(文件扩展名为 .s或者 .asm都可以)

2. 数据声明在代码段之后(其实在之前也没啥问题,也更符合高级程序的设计习惯)

数据声明:

1. 数据段以.data为开始标志

2. 声明变量后,即在主存中分配空间

代码:

1. 代码段以 .text为开始的标志

2. 其实就是各项指令操作

3. 程序入口为main:标志(这个都一样)

4. 程序结束标志

注释:

1. 是注释符

2. MIPS程序的基本模版如下:

代码语言:javascript
复制
# Comment giving name of program and description of function
# 说明下程序的目的和作用(其实和高级语言都差不多了)
# Template.s
# Bare-bones outline of MIPS assembly language program

    .data       # variable declarations follow this line
                # 数据变量声明
                # ...

    .text       # instructions follow this line  
                # 代码段部分  

main:      # indicates start of code (first instruction to execute)
           # 主程序
           # ...

# End of program, leave a blank line afterwards to make SPIM happy
# 必须多给你一行,你才欢?

4 数据声明

声明的格式:

代码语言:javascript
复制
name:    storage_type     value(s)

通常给变量赋一个初始值;对于.space,需要指明需要多少大小空间(bytes)

注意:name后面始终要跟着冒号

代码语言:javascript
复制
example
var1:        .word       3  
# create a single integer variable with initial value 3
# 声明一个 word 类型的变量 var1, 同时给其赋值为 3

array1:     .byte       'a','b'
# create a 2-element character array with elements initialized
# to  a  and  b
# 声明一个存储2个字符的数组 array1,并赋值 'a', 'b'

array2:     .space      40  
# allocate 40 consecutive bytes, with storage uninitialized
# could be used as a 40-element character array, or a
# 10-element integer array; a comment should indicate which!  
# 为变量 array2 分配 40字节(bytes)未使用的连续空间,当然,对于这个变量
# 到底要存放什么类型的值, 最好事先声明注释下!

5 加载保存【读取写入】

1. 如果要访问内存,不好意思,你只能用 load 或者 store 指令

2. 其他的只能都一律是寄存器操作

代码语言:javascript
复制
load:
        lw  register_destination, RAM_source
        # copy word (4 bytes) at source RAM location to destination register.
        # 从内存中 复制 RAM_source 的内容到 对应的寄存器中(lw中的'w'意为'word',即该数据大小为4个字节)
        lb  register_destination, RAM_source
        # copy byte at source RAM location to low-order byte of destination register,and sign-extend to higher-order bytes
        # 同上, lb 意为 load byte

store word:
        sw  register_source, RAM_destination
        # store word in source register into RAM destination
        # 将指定寄存器中的数据 写入到指定的内存中
        sb  register_source, RAM_destination
        # store byte (low-order) in source register into RAM destination

load immediate:
        li  register_destination, value
        # load immediate value into destination register

举例

代码语言:javascript
复制
  .data
        var1:  .word  23    # declare storage for var1; initial value is 23

  .text
    __start:
         lw  $t0, var1    # load contents of RAM location into register $t0:  $t0 = var1
         li  $t1, 5       #  $t1 = 5   ("load immediate")
         sw  $t1, var1    # store contents of register $t1 into RAM:  var1 = $t1
         done

6 立即与间接寻址

代码语言:javascript
复制
load address:
    la  $t0, var1
    # 将var1的RAM地址复制到寄存器$t0中
indirect addressing:
    lw  $t2, ($t0)
    #将$t0中包含的RAM地址的值加载到$t2中
    sw  $t2, ($t0)
    # 将寄存器$ t2中的值存储到$ t0中包含的地址的RAM中
based or indexed addressing:
    lw  $t2, 4($t0)
    # 将RAM地址($t0+4)的值存到$t2寄存器
    sw  $t2, -12($t0)
    # 将$t2寄存器的值存到地址($t0-12)中

不必多说,要用到偏移量的寻址,基本上使用最多的场景无非两种:数组,栈。

代码语言:javascript
复制
    .data
        array1:    .space  12
    #  declare 12 bytes of storage to hold array of 3 integers
    #  定义一个 12字节 长度的数组 array1, 容纳 3个整型
    .text
        __start:  
          la  $t0, array1    
          #  load base address of array into register $t0
          #  让 $t0 = 数组首地址
          li  $t1, 5    
          #  $t1 = 5   ("load immediate")
          sw $t1, ($t0)    
          #  first array element set to 5; indirect addressing
          # 对于 数组第一个元素赋值 array[0] = $1 = 5
          li $t1, 13    
          #   $t1 = 13
          sw $t1, 4($t0)    
          #  second array element set to 13
          # 对于 数组第二个元素赋值 array[1] = $1 = 13 
          # (该数组中每个元素地址相距长度就是自身数据类型长度,即4字节, 所以对于array+4就是array[1])
          li $t1, -7    
          #   $t1 = -7
          sw $t1, 8($t0)    
          #  third array element set to -7
          # 同上, array+8 = (address[array[0])+4)+ 4 = address(array[1]) + 4 = address(array[2])

7 算术指令集

1. 最多3个操作数

2. 在这里,操作数只能是寄存器,绝对不允许出现地址

3. 所有的指令统一是32位 = 4 * 8 bit = 4 bytes = 1 word

代码语言:javascript
复制
    add  $t0,$t1,$t2  
    #  $t0 = $t1 + $t2;   add as signed (2's complement) integers
    sub  $t2,$t3,$t4  
    #  $t2 = $t3, $t4
    addi  $t2,$t3, 5  
    # $t2 = $t3 + 5;   "add immediate" (no sub immediate)
    addu  $t1,$t6,$t7  
    # $t1 = $t6 + $t7;   add as unsigned integers
    subu  $t1,$t6,$t7  
    # $t1 = $t6 + $t7;   subtract as unsigned integers
    mult  $t3,$t4    
    # multiply 32-bit quantities in $t3 and $t4, and store 64-bit
    # result in special registers Lo and Hi:  (Hi,Lo) = $t3 * $t4
    div  $t5,$t6    
    # Lo = $t5 / $t6   (integer quotient)
    # Hi = $t5 mod $t6   (remainder)
    mfhi  $t0    
    # move quantity in special register Hi to $t0:   $t0 = Hi
    mflo  $t1    
    # move quantity in special register Lo to $t1:   $t1 = Lo
    # used to get at result of product or quotient
    move  $t2,$t3  #  $t2 = $t3

8 控制流

branches分支(if else系列)

指令内置了分支条件的比较:

代码语言:javascript
复制
b  target    #  unconditional branch to program label target
beq  $t0,$t1,target  #  branch to target if  $t0 = $t1
blt  $t0,$t1,target  #  branch to target if  $t0 < $t1
ble  $t0,$t1,target  #  branch to target if  $t0 <= $t1
bgt  $t0,$t1,target  #  branch to target if  $t0 > $t1
bge  $t0,$t1,target  #  branch to target if  $t0 >= $t1
bne  $t0,$t1,target  #  branch to target if  $t0 <> $t1

Jumps跳转(while,for,goto系列)

代码语言:javascript
复制
j  target  #  unconditional jump to program label target
           # 看到就跳转,不用考虑任何条件  
jr  $t3    # jump to address contained in $t3 ("jump register")
           # 类似相对寻址,跳到该寄存器给出的地址处

子程序调用 subroutine return: "jump register" instruction

代码语言:javascript
复制
jr  $ra  #  "jump register"

跳转到寄存器$ra中保存的返回地址(由jal指令存储)

如果说调用的子程序中有调用了其他子程序,如此往复, 则返回地址的标记就用栈(stack)来存储, 毕竟 $ra 只有一个,(哥哥我分身乏术啊)

9 系统调用和输入/输出

1. 通过系统调用实现终端的输入输出,以及声明程序结束

2. 学会使用 syscall

3. 参数所使用的寄存器:v0, a0,

4. 返回值使用:$v0

大概意思是要打印的字符串应该有一个终止符,估计类似C中的'\0', 在这里我们只要声明字符串为 .asciiz 类型即可。下面给个我用Mars4.4的提示:

1. 对于读取整型, 浮点型,双精度的数据操作, 系统会读取一整行,(也就是说以换行符为标志 '\n')

2. read_stringfgets类似

举例 打印一个存储在寄存器$2里的整型:

代码语言:javascript
复制
Print out integer value contained in register $t2

li  $v0, 1         # load appropriate system call code into register $v0;
                   # 声明需要调用的操作代码为 1 (print_int) 并赋值给 $v0
                   # code for printing integer is 1
move    $a0, $t2   # move integer to be printed into $a0:  $a0 = $t2
                   # 将要打印的整型赋值给 $a0
syscall            # call operating system to perform operation

举例 读取一个数,并且存储到内存中的 int_value 变量中:

代码语言:javascript
复制
Read integer value, store in RAM location with label int_value (presumably declared in data section)
li  $v0, 5           # load appropriate system call code into register $v0;
                     # code for reading integer is 5
syscall              # call operating system to perform operation
sw  $v0, int_value   # value read from keyboard returned in register $v0;
                     # store this in desired location

举例 打印一个字符串(这是完整的,其实上面栗子都可以直接替换main: 部分,都能直接运行

代码语言:javascript
复制
    .data
string1    .asciiz  "Print this.\n"
# declaration for string variable, 
# .asciiz directive makes string null terminated

    .text
        main:    li  $v0, 4  
# load appropriate system call code into register $v0;
# code for printing string is 4
                 la  $a0, string1
# load address of string to be printed into $a0
                  syscall  
# call operating system to perform print operation

举例 执行到这里,程序结束,立马走人,管他后边洪水滔天~~

代码语言:javascript
复制
li  $v0, 10      # system call code for exit = 10
syscall           # call operating sys

参考引用:

代码语言:javascript
复制
本文属于转载非原创,略有修改: https://www.cnblogs.com/thoupin/p/4018455.html
这篇文章的英文原版地址在这里: https://minnie.tuhs.org/CompArch/Resources/mips_quick_tutorial.html
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 物联网IOT安全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档