MIPS 体系结构和汇编语言快速入门
译者:Sonic Fu, Northeastern University, Boston, MA, USA
译者按:有修改,无删减,初学必读。学习笔记,抛砖引玉!网上有一个老版本,不如此版全面。
英文原版: http://logos.cs.uic.edu/366/notes/mips%20quick%20tutorial.htm#IOSystemCalls
本文分 3部分: 1、寄存器 2、程序结构框架 3、编写汇编程序
概要:数据类型和文法
数据类型: 字节,byte占用( 8bit ), halfword占 2 byte= 16bit), word占用(4byte = 32bit)
一个字符需要一个 Byte的空间;
一个整数需要 1个Word(4 Byte)的空间;
MIPS结构的每条指令长度都是 32bit
寄存器
MIPS体系架构有 32个通用寄存器。在汇编程序中,可以用编号 $0 到 $31来
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示;
也可以用寄存器的名字来进行表示, 例如: $sp, $t1, $ra….
有两个特殊的寄存器 Lo, Hi, 用来保存乘法/除法的运算结果;此 2寄存器不能直接寻址,
只能用特殊的指令:mfhi和 mflo来 aceess 其中的内容。
(含义:mfhi = move from Hi, mflo = Move from Low.)
堆栈(Stack)的增长方向是: 从内存的高地址方向, 向低地址方向;
表格
关于规范使用各类表格的通知入职表格免费下载关于主播时间做一个表格详细英语字母大小写表格下载简历表格模板下载
1:寄存器的编号名称及分类
编号 寄存器名称 寄存器描述
0 Zero 第 0号寄存器,其值始终为 0
1 $at (Assembler Temporary) 是 Assembler保留的寄存器
2 ~ 3 $v0 ~ $v1 (values)保存表达式或函数返回的结果
4-7 $a0 - $a3
(arguments) 作为函数的前四个入参。在子函数调用的过程中不
会被保留。
8-15 $t0 - $t7
(temporaries) Caller saved if needed. Subroutines can use without
saving.供汇编程序使用的临时寄存器。在子函数调用的过程中不
会被保留。
16-23 $s0 - $s7
(saved values) - Callee saved.
A subroutine using one of these must save original and restore it
before exiting. 在子函数调用的过程中会被保留。
24-25 $t8 - $t9
(temporaries) Caller saved if needed. Subroutines can use without
saving.供汇编程序使用的临时寄存器。在子函数调用的过程中不
会被保留。这是对 $t0 - $t7 的补充。
26-27 $k0 - $k1 保留,仅供中断(interrupt/trap)处理函数使用.
28 $gp
global pointer. 全局指针。Points to the middle of the 64K block of
memory in the static data segment.指向固态数据块内存的 64K的
块的中间。
29 $sp stack pointer 堆栈指针, 指向堆栈的栈顶。
30 $s8/$fp
saved value / frame pointer保存的值/帧指针
其中的值在函数调用的过程中会被保留
31 $ra return address返回地址
汇编程序结构框架
汇编源程序代码本质上是文本文件。由 数据声明、代码段 两部分组成。汇编程序文件
应该以.s为后缀,以在 Spim软件中进行模拟。(实际上 ASM也行。)
数据声明部分
在源代码中,数据声明部分以 .data 开始。声明了在代码中使用的变量的名字。同时,
也在主存(RAM)中创建了对应的空间。
程序代码部分
在源代码中,程序代码部分以 .text开始。这部分包含了由指令构成的程序功能代码。
代码以 main: 函数开始。main 的结束点应该调用 exit system call,参见后文有关 system
call 的介绍。
程序的注释部分
使用#符号进行注释。每行以#引导的部分都被视作注释。
一个MIPS 汇编程序框架:
# 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
编写 MIPS 汇编程序:
Content:
PartI: 数据的声明
Part II: 数据的装载和保存(Load/Store 指令)
Part III: 寻址
Part IV: 算术运算指令:Arithmetic Instructions
Part V 程序控制指令:Control Instructions
Part VI: 系统调用和 I/O操作(SPIM仿真)
PartI:数据的声明
格式:
name: storage_type value(s)
创建一个以 name为变量名称,values通常为初始值,storage_type代表存储类型。
注意:变量名后要跟一个:冒号
example
var1: .word 3 # create a single integer:
#variable with initial value 3
array1: .byte 'a','b' # create a 2-element character
# array with elements initialized:
# to a and 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 it.
string1 .asciiz "Print this.\n" #declare a string
Part II:数据的装载和保存(Load/Store 指令)
主存(RAM)的存取 access只能用 load / store 指令来完成。
所有其他的指令都使用的是寄存器作为操作数。
i. load指令:
lw register_destination, RAM_source
# copy word (4 bytes) at
# source_RAM location
# to destination register.
# load word -> lw
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
# load byte -> lb
li register_destination, value
#load immediate value into
#destination register
#load immediate --> li
ii. 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
举个例子:
.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
done
Part III:寻址 :
MIPS 系统结构只能用 load/store 相关指令来实现寻址操作,包含 3中寻址方式:
装载地址:load address,相当于直接寻址,把数据地址直接载入寄存器。
间接寻址:indirect addressing,间接寻址,把寄存器内容作为地址
基线寻址/索引寻址:based or indexed addressing,相对寻址,利用补偿值(offset)寻址。
直接寻址/装载地址:load address:
la $t0, var1
把 var1在主存(RAM)中的地址拷贝到寄存器 t0 中。var1也可以是程序中定义的一个
子程序标签的地址。
间接寻址:indirect addressing:
lw $t2, ($t0)
主存中有一个字的地址存在 t0 中,按这个地址找到那个字,把字拷贝到寄存器 t2 中。
sw $t2, ($t0)
把 t2 中的字存入 t0中的地址指向的主存位置。
基线寻址/索引寻址:based or indexed addressing:
lw $t2, 4($t0)
把 t0 中地址+4所得的地址所对应的主存中的字载入寄存器 t2中,4为包含在 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
此次是负的偏移量。
注意:基线寻址在一下场合特别有用:
1、数组:从基址出发寻找数组元素,通过使用偏移量。
2、堆栈:利用从堆栈指针到框架指针之间的偏移量来读写元素。
举个例子:
#example
.data
array1: .space 12 # declare 12 bytes of storage
# to hold array of 3 integers
.text
__start:
la $t0, array1 # load base address of array
# into register $t0
li $t1, 5 # $t1 = 5 ("load immediate")
sw $t1, ($t0) # first array element set to 5;
# indirect addressing
li $t1, 13 # $t1 = 13
sw $t1, 4($t0) # second array element set to 13
li $t1, -7 # $t1 = -7
sw $t1, 8($t0) # third array element set to -7
done
Part IV 算术运算指令:Arithmetic Instructions
算数运算指令的所有操作数都是寄存器,不能直接使用 RAM地址或间接寻址。
操作数的大小都为 Word (4-Byte)
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
Part V 程序控制指令:Control Instructions
1. 分支指令(Branches)
条件分支的比较
机制
综治信访维稳工作机制反恐怖工作机制企业员工晋升机制公司员工晋升机制员工晋升机制图
已经内建在指令中
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
2. 跳转指令(Jumps)
j target # unconditional jump to program label target
jr $t3 #jump to address contained in $t3 ("jump register")
3. 子程序调用指令
子程序调用指令的实质是跳转并链接(Jump and Link), 它把当前程序计数器的值保留
到$ra中,以备跳回):
跳转到子程序:
jal sub_label # "jump and link", preserve pc to $ra
sub_label为子程序的标签,如 LOOP, SUB_ROUTINE
从子程序返回:
jr $ra # "jump register" jump as the value of $ra
返回到$ra中储存的的返回地址对应的位置, $ra 中的返回地址由 jal指令保存。
注意,返回地址存放在$ra寄存器中。如果子程序调用了下一级子程序,或者是递归调
用,此时需要将返回地址保存在堆栈中,因为每执行一次 jal指令就会覆盖$ra中的返回
地址。
Part VI: 系统调用和 I/O 操作(SPIM仿真)
系统调用是指调用操作系统的特定子程序。
系统调用用来在仿真器的窗口中打印或者读入字符串 string, 并可显示程序是否结束。
用 syscall指令进行对系统子程序的调用。
本操作首先支持$v0 and $a0-$a1中的相对值
调用以后的返回值(如果存在)会保存在$v0中。
表二:系统调用的功能:
Service Codein $v0 Arguments Results
print_int 1
print_float 2
print_double 3
print_string 4
read_int 5
integer returned in
$v0
read_float 6 float returned in $v0
read_double 7
double returned in
$v0
read_string 8
$a0 = memory address of
string input buffer
$a1 = length of string
buffer (n)
sbrk 9 $a0 = amount address in $v0
exit 10
The print_string service expects the address to start a null-terminated character string. The
directive .asciiz creates a null-terminated character string.
打印字符串的功能认为起始地址为一个空终止符串。声明字符串使用的.asciiz指示符会
建立一个空终止符串。
The read_int, read_float and read_double services read an entire line of input up to and
including the newline character.
读入整形,读入浮点型和读入双精度的功能会读取一整行,包含换行符。
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.
读入字符串的功能和 UNIX 库中 fgets函数的语法相同。他会读入 n-1个字符到缓存,
然后以空字符结尾。如果少于 n-1的字符,它会读到结尾并包含换行符,并以空字符结
尾。
The sbrk service returns the address to a block of memory containing n additional bytes. This
would be used for dynamic memory allocation.
sbrk功能返回一个包含有 n个附加字节的存储区的地址,这回被用于动态内存分配。
exit功能用于停止程序运行。
e.g. Print out integer value contained in register $t2
例:打印在$t2中的整数的值
li $v0, 1 # load appropriate system call
# code into register $v0;
#code for printing integer is 1
move $a0, $t2 # move integer to be printed
# into $a0: $a0 = $t2
syscall # call operating system to
# perform operation
#e.g. 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
e.g. Print out string (useful for prompts)
.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
e.g. To indicate end of program, use exit system call; thus last lines of program should be:
li $v0, 10 # system call code for exit = 10
syscall # call operating sys