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

本文介绍MIPS架构的基础知识,包括数据类型、寄存器、指令集等内容,并提供程序结构示例,帮助读者快速掌握MIPS汇编语言的基本概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载:http://www.cnblogs.com/thoupin/p/4018455.html?utm_source=tuicool

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

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

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个通用寄存器
  • 在汇编中,寄存器标志由$符开头
  • 寄存器表示可以有两种方式
        1. 直接使用该寄存器对应的编号,例如:从$0到$31
        2. 使用对应的寄存器名称,例如:$t1, $sp(详细含义,下文有表格
        • 对于乘法和除法分别有对应的两个寄存器$lo, $hi 对于二者,不存在直接寻址;必须要通过mfhi("move from hi")以及mflo("move from lo")分别来进行访问对应的内容
        • 栈的走向是从高地址到低地址
          MIPS下各个寄存器编号及描述:

          Register

          Number

          寄存器编号

          Alternative 

          Name

          寄存器名

          Description

          寄存器用途

          0
          zero

          the value 0

          永远返回零

          1
          $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

          函数调用时必须保存,调用完成后需要恢复

          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

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

          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系语言
              基本模板

          # 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
          # 必须多给你一行,你才欢?

          Data Declarations 数据声明

          format for declarations:
          
          声明的格式:
          
          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,指明保留空间大小(字节)

           

          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)未使用的连续空间,当然,对于这个变量
                             # 到底要存放什么类型的值, 最好事先声明注释下!


           

           

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

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

          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-e.g.tend to higher-order bytes 同上, lb 意为 load byte
          
          store:
          
          	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 顾名思义,这里的 li 意为 load immediate

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



           

          Indirect and Based Addressing 立即与间接寻址

          load address 直接给地址:

          	la	$t0, var1 #var1一般为定义的Label,将标记var1的地址存入t0寄存器中

          indirect addressing: 间接寻址

                 寻址格式为 lw/sw      寄存器        偏移(基址寄存器)

          	lw	$t2, 4($t0)
          • load word at RAM address ($t0+4) into register $t2
          • "4" gives offset from address in register $t0
          	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

           

          		.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])
          		done

           

          Arithmetic Instructions 算术指令集

          • 最多3个操作数
          • 再说一遍,在这里,操作数只能是寄存器,绝对不允许出现地址
          • 所有指令统一是32位 = 4 * 8 bit = 4bytes = 1 word
                          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
                                   运算结果存储在hi,lo(hi高位数据, lo地位数据)
          		div	$t5,$t6		#  Lo = $t5 / $t6   (integer quotient)
          					#  Hi = $t5 mod $t6   (remainder)
                                   商数存放在 lo, 余数存放在 hi
          		mfhi	$t0		#  move quantity in special register Hi to $t0:   $t0 = Hi
                                    不能直接获取 hi 或 lo中的值, 需要mfhi, mflo指令传值给寄存器
          		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
           
          

          Control Structures 控制流Branches

          分支(if else系列)

            b	target	        #PC=target
            bal   target          #RA=PC+8; PC=target
            beq	$t0,$t1,target	#if t0==t1 then PC=target
            beqz  $t0, target     #if t0==0  then PC=target
            bgez  $t0, target     #if t0>=0  then PC=target
            bgezal $t0, target    #RA=PC+8; if t0>=0  then  PC=target
            bgtz   $t0, target    #if t0>0 then PC=target
            blez   $t0, target    #if t0<=0 then PC=target
            bltz   $t0, target    #if t0<0 then PC=target
            bltzal $t0, target    #RA=PC+8 if t0<0 then PC=target
            bne	$t0,$t1,target	#if t0!=t1 then PC=target


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

            j	target	             # PC=PC[31:28]::target[27:0]
            jr	$t3                     # PC=t3


          Subroutine Calls子程序调用
           jal	sub_label                  # RA=PC+8 ; PC=PC[31:28]::sub_label[27:0]
           jalr   $t3                        # RA=PC+8;  PC=t3

          注意上述跳转指令,之遥带有AL(And Link)都会更新RA。

          如果说调用的子程序中有调用了其他子程序,嵌套调用, 则需要将$ra保存到栈内,用于以后恢复$ra, 毕竟 $ra 只有一个



           

          System Calls and I/O (SPIM Simulator)系统调用 与 输入/输出(主要针对SPIM模拟器)

          • 通过系统调用实现终端的输入输出,以及声明程序结束
          • 学会使用 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 类型即可。
            • .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.
            • 你懂得。。。
          打印整数

           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

          读取整数

          		li	$v0, 5			# load appropriate system call code into register $v0;
          						# code for reading integer is 5 声明需要调用的操作代码为 5 (read_int) 并赋值给 $v0 
          		syscall				# call operating system to perform operation、 经过读取操作后, $v0 的值已经变成了 输入的 5
          		sw	$v0, int_value		# value read from keyboard returned in register $v0;
          						# store this in desired location 通过写入(store_word)指令 将 $v0的值(5) 存入 内存中  

          读取字符串

             .data
          dataarray: .space 40
              .text
              li $v0, 8
              syscall
              mov $t0,  $a0
              la      $t1,  dataarray
          1:  lw  $t2,  $t0
              sw $t2, 0($t2)
              sub $a1, $a1, 4
              bnez $a1 , 1b
              nop

          打印字符串

          		.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 打印字符串, 赋值对应的操作代码 $v0 = 4
          		la	$a0, string1		# load address of string to be printed into $a0 将要打印的字符串地址赋值  $a0 = address(string1)
          		syscall				# call operating system to perform print operation
          

          结束程序


          结束程序

          		li	$v0, 10		     # system call code for exit = 10
          		syscall				# call operating sys


          1.Mars4.4
          2.PCSpim Simulator
          3.《MIPS Qucik Tutorial》

          除了上述基础,还需要注意编译相关的伪指令。例如:.set mips32  .set noreorder



          评论 1
          添加红包

          请填写红包祝福语或标题

          红包个数最小为10个

          红包金额最低5元

          当前余额3.43前往充值 >
          需支付:10.00
          成就一亿技术人!
          领取后你会自动成为博主和红包主的粉丝 规则
          hope_wisdom
          发出的红包
          实付
          使用余额支付
          点击重新获取
          扫码支付
          钱包余额 0

          抵扣说明:

          1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
          2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

          余额充值