中科院云平台架构师,专注于数字化、智能化,技术方向:云、Linux内核、AI、MES/ERP/CRM/OA、物联网、传感器、大数据、ML、微服务。
分类: 服务器与存储
2020-02-26 14:30:18
MIPS Architecture and Assembly Language Overview MIPS架构及其汇编初步
1: 寄存器种类;2: 算术及寻址指令
3: 程序结构
4: 系统调用
Data types:数据类型
Instructions are all 32 bits 所有MIPS指令都是32位长的 byte(8 bits), halfword (2 bytes), word (4 bytes) 各单位:1字节=8位,半字长=2个字节,1字长=4个字节 a character requires 1 byte of storage 一个字符空间=1个字节 an integer requires 1 word (4 bytes) of storage 一个整型=一个字长=4个字节Literals:字符
numbers entered as is. e.g. 4 数字就是数字,例如:4 characters enclosed in single quotes. e.g. 'b' 单个字符用单引号,例如:'b' strings enclosed in double quotes. e.g. "A string" 字符串用双引号,例如:"A string"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. (Argument简写)存储子程序的前4个参数,在子程序调用过程中释放 |
8-15
|
$t0 - $t7
|
(temporaries) Caller saved if needed. Subroutines can use w/out saving. (Temp简写)临时变量,同上调用时不保存 |
16-23
|
$s0 - $s7
|
(saved values) - Callee saved. |
24-25
|
$t8 - $t9
|
(temporaries) Caller saved if needed. Subroutines can use w/out saving. |
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 返回地址,目测也是不可做其他用途 |
-
# 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# 必须多给你一行,你才欢?
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,需要指明需要多少大小空间(bytes)
Note: labels always followed by colon ( : )
-
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:
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 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
顾名思义,这里的 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
-
-
load address: 直接给地址
la $t0, var1
copy RAM address of var1 (presumably a label defined in the program) into register $t0
indirect addressing: 地址是寄存器的内容(可以理解为指针)
lw $t2, ($t0)
load word at RAM address contained in $t0 into $t2
sw $t2, ($t0)
store word in register $t2 into RAM at address contained in $t0
based or indexed addressing:+偏移量
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
-
example:栗子:
-
-
.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
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 运算结果存储在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
-
-
Branches 分支(if else系列)
comparison for conditional branches is built into instruction
-
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系列)
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
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
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 只有一个 。
The following table lists the possible syscall services.
下表给出了系统调用中对应功能,代码,参数机返回值
Code in $v0 对应功能的调用码 Arguments 所需参数 Results 返回值 Service
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 |
|
sbrk 应该同C中的sbrk()函数 动态分配内存 |
$v0 = 9
|
$a0 = amount 需要分配的空间大小(单位目测是字节 bytes) |
address in $v0 将分配好的空间首地址给 $v0 |
exit 退出 |
$v0 =10
|
你懂得 |
1. 2. 3.《MIPS Qucik Tutorial》
https://blog.csdn.net/ben_chong/article/details/51794392