无意中找到一篇十分好用,而且篇幅也不是很大的入门教程,通篇阅后,再把“栗子”敲一遍,基本可以有一个比较理性的认识,从而方便更好地进一步深入学习。

废话不多说,上干货(英语好的直接跳过本人的渣翻译了哈——!纯本人手打原创,有错请指教,要转载请声明出处,谢~~):

MIPS Architecture and Assembly Language Overview

MIPS架构及其汇编初步

(开始之前稍微再提下,整体分为4个结构:)

  1: 寄存器种类;

  2: 算术及寻址指令

  3: 程序结构

  4: 系统调用 

Data Types and Literals

数据类型

  • 所有MIPS指令都是32位长的
  • 各单位:1字节=8位,半字长=2个字节,1字长=4个字节
  • 一个字符空间=1个字节
  • 一个整型=一个字长=4个字节
  • 单个字符用单引号,例如:'b'
  • 字符串用双引号,例如:"A string"

Registers

寄存器

      • MIPS下一共有32个通用寄存器
      • 在汇编中,寄存器标志由$符开头
      • 寄存器表示可以有两种方式
      • 直接使用该寄存器对应的编号,例如:从$0到$31
      • 使用对应的寄存器名称,例如:$t1, $sp(详细含义,下文有表格
      对于乘法和除法分别有对应的两个寄存器$lo, $hi

      • 对于以上二者,不存在直接寻址;必须要通过mfhi("move from hi")以及mflo("move from lo")分别来进行访问对应的内容
      • 栈的走向是从高地址到低地址
MIPS下各个寄存器编号及描述:

Register
Number

寄存器编号

Alternative 
Name

寄存器名

Description

寄存器用途

0
zero

the value 0

永远返回零

$at

(assembler temporary) reserved by the assembler

汇编保留寄存器(不可做其他用途)

2-3
$v0 - $v1

(values) from expression evaluation and function results

Value简写)存储表达式或者是函数的返回值

4-7
$a0 - $a3

(arguments) First four parameters for subroutine.
Not preserved across procedure calls

Argument简写)存储子程序的前4个参数,在子程序调用过程中释放

8-15
$t0 - $t7

(temporaries) Caller saved if needed. Subroutines can use w/out saving.
Not preserved across procedure calls

Temp简写)临时变量,同上调用时不保存

16-23
$s0 - $s7

(saved values) - Callee saved. 
A subroutine using one of these must save original and restore it before exiting.
Preserved across procedure calls

Saved or Static简写?)静态变量?调用时保存

24-25
$t8 - $t9

(temporaries) Caller saved if needed. Subroutines can use w/out saving.
These are in addition to $t0 - $t7 above.
Not preserved across procedure calls.

Temp简写)算是前面$0~$7的一个继续,属性同$t0~$t7

26-27
$k0 - $k1

reserved for use by the interrupt/trap handler

(breaK off简写?)中断函数返回值,不可做其他用途

28
$gp

global pointer. 
Points to the middle of the 64K block of memory in the static data segment.

Global Pointer简写)指向64k(2^16)大小的静态数据块的中间地址(字面上好像就是这个意思,块的中间)

29
$sp

stack pointer 
Points to last location on the stack.

(Stack Pointer简写)栈指针,指向的是栈顶

30
$s8/$fp

saved value / frame pointer
Preserved across procedure calls

(Saved/Frame Pointer简写)帧指针

31
$ra

return address

返回地址,目测也是不可做其他用途

Program Structure

程序结构

  • 本质其实就只是数据声明+普通文本+程序编码(文件后缀为.s,或者.asm也行)
  • 数据声明在代码段之后(其实在其之前也没啥问题,也更符合高级程序设计的习惯)

Data Declarations

数据声明

  • 数据段以 .data为开始标志
  • 声明变量后,即在主存中分配空间。

Code

代码

  • 代码段以 .text为开始标志
  • 其实就是各项指令操作
  • 程序入口为main:标志(这个都一样啦)
  • 程序结束标志(详见下文)

Comments

注释

  • 同C系语言
      • MIPS程序的基本模板如下:

        1. # Comment giving name of program and description of function
          # 说明下程序的目的和作用(其实和高级语言都差不多了)
        2. # Template.s
        3. #Bare-bones outline of MIPS assembly language program
        4.  
        5. .data # variable declarations follow this line
               # 数据变量声明
        6. # ...
        7.  
        8. .text # instructions follow this line
        9. # 代码段部分
        10. main: # indicates start of code (first instruction to execute)
          # 主程序
        11. # ...
        12.  
        13. # End of program, leave a blank line afterwards to make SPIM happy
          # 必须多给你一行,你才欢?

  

Data Declarations

数据声明

format for declarations:

声明的格式:

  1. name: storage_type value(s)
    变量名:(冒号别少了) 数据类型 变量值
    • create storage for variable of specified type with given name and specified value
    • value(s) usually gives initial value(s); for storage type .space, gives number of spaces to be allocated
    • 通常给变量赋一个初始值;对于.space,需要指明需要多少大小空间(bytes)

Note: labels always followed by colon ( : )

  1. example
  2.  
  3. var1: .word 3 # create a single integer variable with initial value 3
                       # 声明一个 word 类型的变量 var1, 同时给其赋值为 3
  4. array1: .byte 'a','b' # create a 2-element character array with elements initialized
  5. # to a and b
                       # 声明一个存储2个字符的数组 array1,并赋值 'a', 'b'
  6. array2: .space 40 # allocate 40 consecutive bytes, with storage uninitialized
  7. # could be used as a 40-element character array, or a
  8. # 10-element integer array; a comment should indicate which!
                       # 为变量 array2 分配 40字节(bytes)未使用的连续空间,当然,对于这个变量
                       # 到底要存放什么类型的值, 最好事先声明注释下!

Load / Store Instructions

加载/保存(也许这里写成读取/写入 可能更易理解一点) 指令集

  • 如果要访问内存,不好意思,你只能用 load 或者 store 指令
  • 其他的只能都一律是寄存器操作

load:

  1. lw register_destination, RAM_source

#copy word (4 bytes) at source RAM location to destination register.

从内存中 复制 RAM_source 的内容到 对应的寄存器中

(lw中的'w'意为'word',即该数据大小为4个字节)

  1. lb register_destination, RAM_source

#copy byte at source RAM location to low-order byte of destination register,
# and sign-e.g.tend to higher-order bytes

同上, lb 意为 load byte

store word:

  1. sw register_source, RAM_destination

#store word in source register into RAM destination

#将指定寄存器中的数据 写入 到指定的内存中

  1. sb register_source, RAM_destination

#store byte (low-order) in source register into RAM destination

load immediate:

  1. li register_destination, value

#load immediate value into destination register

顾名思义,这里的 li 意为 load immediate

  1. example:
  2. .data
  3. var1: .word 23 # declare storage for var1; initial value is 23
  4.                    # 先声明一个 word 型的变量 var1 = 3;
  5. .text
  6. __start:
  7. lw $t0, var1 # load contents of RAM location into register $t0: $t0 = var1
                       # 令寄存器 $t0 = var1 = 3;
  8. li $t1, 5 # $t1 = 5 ("load immediate")
                       # 令寄存器 $t1 = 5;
  9. sw $t1, var1 # store contents of register $t1 into RAM: var1 = $t1
                       # 将var1的值修改为$t1中的值: var1 = $t1 = 5;
  10. done

Indirect and Based Addressing

立即与间接寻址

load address:

直接给地址

  1. la $t0, var1
  • copy RAM address of var1 (presumably a label defined in the program) into register $t0

indirect addressing:

地址是寄存器的内容(可以理解为指针)

  1. lw $t2, ($t0)
  • load word at RAM address contained in $t0 into $t2
  1. sw $t2, ($t0)
  • store word in register $t2 into RAM at address contained in $t0

based or indexed addressing:

+偏移量

  1. lw $t2, 4($t0)
  • load word at RAM address ($t0+4) into register $t2
  • "4" gives offset from address in register $t0
  1. sw $t2, -12($t0)
  • store word in register $t2 into RAM at address ($t0 - 12)
  • negative offsets are fine

Note: based addressing is especially useful for:

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

  • arrays; access elements as offset from base address
  • stacks; easy to access elements at offset from stack pointer or frame pointer
  1. example
    栗子:
  2.  
  3. .data
  4. array1: .space 12 # declare 12 bytes of storage to hold array of 3 integers
                            # 定义一个 12字节 长度的数组 array1, 容纳 3个整型
  5. .text
  6. __start: la $t0, array1 # load base address of array into register $t0
                            # 让 $t0 = 数组首地址
  7. li $t1, 5 # $t1 = 5 ("load immediate")
  8. sw $t1, ($t0) # first array element set to 5; indirect addressing
                            # 对于 数组第一个元素赋值 array[0] = $1 = 5
  9. li $t1, 13 # $t1 = 13
  10. sw $t1, 4($t0) # second array element set to 13
                            # 对于 数组第二个元素赋值 array[1] = $1 = 13
                            # (该数组中每个元素地址相距长度就是自身数据类型长度,即4字节, 所以对于array+4就是array[1])
  11. li $t1, -7 # $t1 = -7
  12. sw $t1, 8($t0) # third array element set to -7
                            # 同上, array+8 = (address[array[0])+4)+ 4 = address(array[1]) + 4 = address(array[2])
  13. done

Arithmetic Instructions

算术指令集

  • 最多3个操作数
  • 再说一遍,在这里,操作数只能是寄存器,绝对不允许出现地址
  • 所有指令统一是32位 = 4 * 8 bit = 4bytes = 1 word

add $t0,$t1,$t2 # $t0 = $t1 + $t2; add as signed (2's complement) integers

  1. sub $t2,$t3,$t4 # $t2 = $t3 Ð $t4
  2. addi $t2,$t3, 5 # $t2 = $t3 + 5; "add immediate" (no sub immediate)
  3. addu $t1,$t6,$t7 # $t1 = $t6 + $t7; add as unsigned integers
  4. subu $t1,$t6,$t7 # $t1 = $t6 + $t7; subtract as unsigned integers
  5.  
  6. mult $t3,$t4 # multiply 32-bit quantities in $t3 and $t4, and store 64-bit
  7. # result in special registers Lo and Hi: (Hi,Lo) = $t3 * $t4
                             运算结果存储在hi,lohi高位数据, lo地位数据)
  8. div $t5,$t6 # Lo = $t5 / $t6 (integer quotient)
  9. # Hi = $t5 mod $t6 (remainder)
                             商数存放在 lo, 余数存放在 hi
  10. mfhi $t0 # move quantity in special register Hi to $t0: $t0 = Hi
                             不能直接获取 hi lo中的值, 需要mfhi, mflo指令传值给寄存器
  11. mflo $t1 # move quantity in special register Lo to $t1: $t1 = Lo
  12. # used to get at result of product or quotient
  13. move $t2,$t3 # $t2 = $t3

Control Structures

控制流

Branches

分支(if else系列)

  • comparison for conditional branches is built into instruction
  1. b target # unconditional branch to program label target
  2. beq $t0,$t1,target # branch to target if $t0 = $t1
  3. blt $t0,$t1,target # branch to target if $t0 < $t1
  4. ble $t0,$t1,target # branch to target if $t0 <= $t1
  5. bgt $t0,$t1,target # branch to target if $t0 > $t1
  6. bge $t0,$t1,target # branch to target if $t0 >= $t1
  7. bne $t0,$t1,target # branch to target if $t0 <> $t1

Jumps

跳转(while, for, goto系列)

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

Subroutine Calls

子程序调用

subroutine call: "jump and link" instruction

  1. jal sub_label # "jump and link"
  • copy program counter (return address) to register $ra (return address register)
  • 将当前的程序计数器保存到 $ra 中
  • jump to program statement at sub_label

subroutine return: "jump register" instruction

  1. jr $ra # "jump register"
  • jump to return address in $ra (stored by jal instruction)
  • 通过上面保存在  $ra 中的计数器返回调用前

Note: return address stored in register $ra; if subroutine will call other subroutines, or is recursive, return address should be copied from $ra onto stack to preserve it, since jal always places return address in this register and hence will overwrite previous value

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


System Calls and I/O (SPIM Simulator)

系统调用 与 输入/输出(主要针对SPIM模拟器)

(本人使用的是Mars 4.4,也通用--!)

  • 通过系统调用实现终端的输入输出,以及声明程序结束
  • 学会使用 syscall
  • 参数所使用的寄存器:$v0, $a0,  $a1
  • 返回值使用: $v0

下表给出了系统调用中对应功能,代码,参数机返回值

Service

Code
in $v0

对应功能的调用码

Arguments

所需参数

Results

返回值

print_int

打印一个整型

$v0 = 1

$a0 = integer to be printed

将要打印的整型赋值给 $a0

 

print_float

打印一个浮点

$v0 = 2

$f12 = float to be printed

将要打印的浮点赋值给 $f12

 

print_double

打印双精度

$v0 = 3

$f12 = double to be printed

将要打印的双精度赋值给 $f12

 
print_string
$v0 = 4

$a0 = address of string in memory

将要打印的字符串的地址赋值给 $a0

 
read_int
$v0 = 5
 

integer returned in $v0

将读取的整型赋值给 $v0

read_float

读取浮点

$v0 = 6
 

float returned in $v0

将读取的浮点赋值给 $v0

read_double

读取双精度

$v0 = 7
 

double returned in $v0

将读取的双精度赋值给 $v0

read_string

读取字符串

$v0 = 8

$a0 = memory address of string input buffer

将读取的字符串地址赋值给 $a0
$a1 = length of string buffer (n)

将读取的字符串长度赋值给 $a1

 

sbrk

应该同C中的sbrk()函数

动态分配内存

$v0 = 9

$a0 = amount

需要分配的空间大小(单位目测是字节 bytes)

address in $v0

将分配好的空间首地址给 $v0

exit

退出

$v0 =10
 你懂得  
    • 大概意思是要打印的字符串应该有一个终止符,估计类似C中的'\0', 在这里我们只要声明字符串为 .asciiz 类型即可。下面给个我用Mars4.4的提示:
    • .ascii 与 .asciiz唯一区别就是 后者会在字符串最后自动加上一个终止符, 仅此而已
    • The read_int, read_float and read_double services read an entire line of input up to and including the newline character.
    • 对于读取整型, 浮点型,双精度的数据操作, 系统会读取一整行,(也就是说以换行符为标志 '\n')
    • The read_string service has the same semantices as the UNIX library routine fgets.
      • It reads up to n-1 characters into a buffer and terminates the string with a null character.
      • If fewer than n-1 characters are in the current line, it reads up to and including the newline and terminates the string with a null character.
      • 这个不多说了,反正就是输入过长就截取,过短就这样,最后都要加一个终止符。
    • The sbrk service returns the address to a block of memory containing n additional bytes. This would be used for dynamic memory allocation.
    • 上边的表里已经说得很清楚了。
    • The exit service stops a program from running.
    • 你懂得。。。
  1. e.g. Print out integer value contained in register $t2
    栗子: 打印一个存储在寄存器 $2 里的整型
  2. li $v0, 1 # load appropriate system call code into register $v0;
                                 
    声明需要调用的操作代码为 1 print_int) 并赋值给 $v0
  3. # code for printing integer is 1
  4. move $a0, $t2 # move integer to be printed into $a0: $a0 = $t2
                                 
    将要打印的整型赋值给 $a0
  5. syscall # call operating system to perform operation
  6. e.g. Read integer value, store in RAM location with label int_value (presumably declared in data section)
    栗子: 读取一个数,并且存储到内存中的 int_value 变量中
  7. li $v0, 5 # load appropriate system call code into register $v0;
  8. # code for reading integer is 5
                                 
    声明需要调用的操作代码为 5 read_int) 并赋值给 $v0 
  9. syscall # call operating system to perform operation、
                                 
    经过读取操作后, $v0 的值已经变成了 输入的 5
  10. sw $v0, int_value # value read from keyboard returned in register $v0;
  11. # store this in desired location
                                 
    通过写入(store_word)指令 $v0的值(5 存入 内存中         
  12. e.g. Print out string (useful for prompts)
    栗子: 打印一个字符串(这是完整的,其实上面栗子都可以直接替换main: 部分,都能直接运行)
  13. .data
  14. string1 .asciiz "Print this.\n" # declaration for string variable,
  15. # .asciiz directive makes string null terminated
  16. .text
  17. main: li $v0, 4 # load appropriate system call code into register $v0;
  18. # code for printing string is 4
                                
    打印字符串, 赋值对应的操作代码 $v0 = 4
  19. la $a0, string1 # load address of string to be printed into $a0
                                 
    将要打印的字符串地址赋值 $a0 = address(string1)
  20. syscall # call operating system to perform print operation

  21. e.g. To indicate end of program, use exit system call; thus last lines of program should be:
    执行到这里, 程序结束, 立马走人, 管他后边洪水滔天~~
  22. li $v0, 10      # system call code for exit = 10
  23. syscall # call operating sys
  24. -------------------------------------------------我是那个分呀分呀分割线--------------------------------------------------------------------------

  1. OK, 十分轻松又愉快的MIPS入门之旅到此告一段落, 下面我把用到的一些软件和这篇文章的原文链接贴到下边,有需要的, 各位客官自取哈~~~
  2.  
  3. 1.Mars4.4
    2.PCSpim Simulator
    3.MIPS Qucik Tutorial
  1. 你都看到这里了, 难道还怕点那么一个赞么~~~~~
  1.  

【十分钟教会你汇编】MIPS编程入门(妈妈说标题要高大上,才会有人看>_<!)的更多相关文章

  1. 08.十分钟学会JSP传统标签编程

    一.认识标签 1,说明:传统标签编程在开发中基本用不到,学习标签编程主要还是为了完善知识体系. 2,标签的主要作用:移除或减少jsp中的java代码 3,标签的主要组成部分及运行原理 4,简单标签示例 ...

  2. 十分钟学会Scratch图形化编程

    一.概要 Scratch是麻省理工学院开发的供儿童或者初学者学习编程的开发平台.其通过点击并拖拽的方式,完成编程,可以使儿童或者成人编程初学者学习编程基础概念等.Scratch是一款积木式图形编程软件 ...

  3. 十分钟教会你使用安卓热修复框架AndFix

    腾讯最近开发出一个Tinker,阿里也有一个Dexposed框架,当然还有一个就是今天的主角热修复框架AndFix.接下来,我会从它的概念.原理.使用方法等为你详细介绍. 1.什么是AndFix? A ...

  4. 十分钟教会你如何使用VitePress搭建及部署个人博客站点

    使用VitePress可以让我们快速搭建一个静态博客网站,这篇文章将带领大家搭建一个基于VitePress的静态博客网站并且部署到GitHub Pages(github提供的静态网页服务) 快速上手 ...

  5. [转]Teach Yourself Programming in Ten Years——用十年教会自己编程

    作者:Peter Norvig 译者:刘海粟 本文原文为:http://norvig.com/21-days.html 该翻译文档的PDF版可以在这里获得:http://download.csdn.n ...

  6. 快速入门:十分钟学会Python

    初试牛刀 假设你希望学习Python这门语言,却苦于找不到一个简短而全面的入门教程.那么本教程将花费十分钟的时间带你走入Python的大门.本文的内容介于教程(Toturial)和速查手册(Cheat ...

  7. 十分钟入门less(翻译自:Learn lESS in 10 Minutes(or less))

    十分钟入门less(翻译自:Learn lESS in 10 Minutes(or less)) 注:本文为翻译文章,因翻译水平有限,难免有缺漏不足之处,可查看原文. 我们知道写css代码是非常枯燥的 ...

  8. 转:Teach Yourself Programming in Ten Years——用十年教会自己编程

    转自:http://blog.csdn.net/UndeadWraith/article/details/6140455 作者:Peter Norvig 译者:刘海粟 本文原文为:http://nor ...

  9. 快速入门:十分钟学会Python(转)

    初试牛刀 假设你希望学习Python这门语言,却苦于找不到一个简短而全面的入门教程.那么本教程将花费十分钟的时间带你走入Python的大门.本文的内容介于教程(Toturial)和速查手册(Cheat ...

随机推荐

  1. Navicat 导入导出

    当我们对mysql数据库进行了误操作,造成某个数据表中的部分数据丢失时,肯定就要利用备份的数据库,对丢失部分的数据进行导出.导入操作了.Navicat工具正好给我们提供了一个数据表的导入导出功能. 1 ...

  2. C#中的ODBC、OLEDB连接

      using System;using System.Collections.Generic;using System.Text;using System.Data.Odbc;using Syste ...

  3. Good vs Evil

    Good vs Evil Description Middle Earth is about to go to war. The forces of good will have many battl ...

  4. mysql导出csv/excel文件的几种方法,mysql的load导入csv数据

    方法一 php教程用mysql的命令和shell select * into outfile './bestlovesky.xls' from bestlovesky where 1 order by ...

  5. poj3254Corn Fields(状压)

    http://poj.org/problem?id=3254 第一个状压题 思路挺好想 用二进制表示每行的状态 然后递推 用左移 右移来判断是不是01间隔出现 c大神教的 我的代码WA在这个地方了.. ...

  6. mac 修改密码后 频繁输入钥匙串问题修复方法

    就一句话就是 清空钥匙串缓存 下面是具体方法 进入硬盘目录-->资源库-->Keychains 删除里面的文件夹(这个文件夹里面有 keychain-2.db keychain-2.db- ...

  7. .NET(C#)调用webService获取客户端IP地址所属区域(非异步)

    功能描述: 此接口用于获取客户端访问的IP的地址所属的区域(国家,城市等).通过输入IP地址查询国家.城市.所有者等信息.没有注明国家的为中国输入参数:IP地址(自动替换 " ." ...

  8. Apache ‘mod_pagespeed’模块跨站脚本漏洞

    漏洞名称: Apache ‘mod_pagespeed’模块跨站脚本漏洞 CNNVD编号: CNNVD-201310-677 发布时间: 2013-11-05 更新时间: 2013-11-05 危害等 ...

  9. 树莓PI安装web服务器

    参考:http://www.eeboard.com/bbs/forum.php?mod=viewthread&tid=27383 http://www.eeboard.com/bbs/thre ...

  10. 分享form表单提交问题

    前段时间做了一个form表单传值问题  当时觉得form表单的submit不就是提交form表单name的value值吗 ? 其实是对的  但是我做的是一个打印页面  需要把当前页面的元素传入下一个u ...