懒得整理了,将就看
# 简单的引例
# a = b + c
add a, b, c
# a = b - c
sub a, b, c
# f = (g + h) - (i + j)
add t0, g, h
add t1, i, j
sub f, t0, t1
# MIPS 中寄存器为 32 位(5 位编码),MIPS 字的大小与寄存器大小相同
# MIPS 一般为 32 个寄存器
# $ 外加序号 0 ~ 31 表示一个寄存器,如:$0、$1、$3 ...。当然也有别名
# $1 = $2 + $3
add $1, $2, $3
# 数据传送指令,访问存储器中的一个字,指令必须给出地址
# g = h + A[8],假设 s3 寄存器存放了数组 A 起始地址(基址)
# 在存储器中取出 A[8] 的指放入寄存器
lw $0, 8($s3)
# 数组 A 基址存放在寄存器 $1 中,将 $t0 寄存器的数据写入 A[8]
sw $t0, 48($s1)
# 编译器会将常用的变量尽量保持在寄存器中
# 将不常用的变量存回到存储器的过程称寄存器溢出
# 立即数,避免使用取数指令,MIPS 将寄存器 $zero 恒置为 0
addi $s3, $s3, 1 # $s3 = $s3 + 1
# MIPS 支持负立即数,无需设置立即数减法操作
lui $s0, 61 # $s3 = 0,存储无符号立即数的高 16 位
# MIPS 按字节编址,且对齐限制(强制要求起始地址为 4 的整数倍)
# MIPS 采用大端编址
# 大端编址,高位低字节;小端编址,高位高地址。
# -15041 二进制表示,负数已补码形式存储在计算机中
# 原码:1 011 1010 1100 0001
# 反码:1 100 0101 0011 1110
# 补码:1 100 0101 0011 1111
# 最左边(最上面)最高有效位,最右边(最下面)最低有效位
# 符号扩充:1 111 1111 1111 1111 1100 0101 0011 1111
# 指令的布局称为指令格式,指令的比特序列叫做机器码(定长 32 位)
# R 型(用于寄存器)机器码:00000 0000 0000 0000 0000 00000
# 1. 第一段指明操作码
# 2. 第二段到第四段用来指明寄存器(源、源、目标)
# 3. 第五段表示位移量,用于移位指令
# 4. 最后一段指明功能码
# 第一段和最后一段表明了操作类型
# I 型(用于立即数)机器码:00000 0000 0000 0000 000000000
# I 型指令将 R 型指令最后两个字段合成为一个字段指明地址
# J 型指令,前 6 位指明跳转操作类型,后 26 位为跳转地址
# 逻辑操作 C 操作符 Java 操作符 MIPS 指令
# 左移 << << sll
# 右移 >> >> srl
# 位与 & & and, andi
# 位或 | | or, ori
# 按位取反 ~ ~ 无
# 或非 无 无 nor
# 异或 ^ ^ xor
# MIPS 为了保持格式,没有直接按位取反,引入或非变相解决
# $t1 为 0101 0101 0101 0101 0101 0101 0101 0101
# $t2 全为 0
# 或非结果:1010 1010 1010 1010 1010 1010 1010 1010
nor $t3, $t1, $t2 # 与 0 或非就是按位取反
# 决策指令,利用决策指令可以实现分支与循环
beq $t0, $t1, target # if $t0 == $t1 then target
bne $t0, $t1, target # if $t0 != $t1 then target
j target # jump to target
# 无 < <= > >= 解决方案,统一使用 slt。小于置 0,大于置 1
slt $t0, $t1, $t2 # if $t1 < $t2 then $t0 = 0
slti $t0, $t1, num # if $t1 < num then $t0 = 0
# 过程(c 中的函数,Java 中的方法)
# 过程调用寄存器规约
# 1. $a0 ~ $a3:用于传递参数
# 2. $v0 ~ $v1:用于返回值
# 3. $ra:用于返回地址。实际 $ra = PC + 4
# 4. $t0 ~ $t9:临时寄存器,过程调用中无需保存
# 5. $s0 ~ $s7:保留寄存器,过程调用中必须保存
jal target # jump to target and save position to $ra
jr $ra # jump to $ra
# 栈。历史惯例栈增长按照从高到低顺序进行,压栈值减小,出栈值变大
# $sp:栈指针寄存器,指向栈顶元素。但 MIPS 硬件不支持栈
# 需要使用栈时,程序员要遵守栈的特点去使用数组
sw $t0, 8($sp) # 使用数组模拟压栈
sw $t1, 4($sp)
sw $t2, 0($sp)
lw $t0, 0($sp) # 出栈
lw $t0, 4($sp)
lw $t0, 8($sp)
# 过程嵌套的返回地址将其压入栈
# $gp:全局指针寄存器,指向静态数据区的保留寄存器
# $pf:帧指针寄存器,用来指定栈基指寄存器
# ASCAII 字符,需要字节操作
lb $t0, 0($s1) # 将 s1[0] 低 8 位放入 $t0 低 8 位
sb $t0, 0($s1) # 将 $t0 低 8 位放入 s1[0] 低 8 位
# Unicode,需要半字操作
lh $t0, 0($s1) # 将 s1[0] 低 16 位放入 $t0 低 16 位
sh $t0, 0($s1) # 将 $t0 低 16 位放入 s1[0] 低 16 位
lhu $t0, 0($s1) # 无符号
# MIPS 强制边界对齐,所以会将字节与半字压缩成一个字
# 字符串表示方法
# 1. 第一个元素给出字符串长度(Java)
# 2. 最后一个元素为 '\0'(C)
# 3. 附加一个带有字符串长度的变量
# MIPS 寻址方式
# 1. 立即数寻址(立即数)
# 2. 寄存器寻址(寄存器)
# 3. 基址寻址(寄存器 + 立即数)
# 4. PC 相对寻址(beq、bne)
# 5. 伪直接寻址(J 指令)
# 3、4、5 寻找的数据都存在内存中
# C 语言程序翻译到执行
# 1. C 语言经过编译器翻译为汇编语言
# 2. 汇编语言经过汇编翻译为由机器语言构成的目标模块
# 3. 生成的目标模块再与库程序经过链接器解析引用生成可执行文件
# 4. 可执行文件通过加载器加载到内存合适位置,等待 CPU 执行
# .c c 文件,.s 汇编文件,.o 目标文件,.a 静态链接库,.so 动态链接库,.out 可执行文件
# 汇编指令:可以翻译为机器码的指令
# 伪指令:给编译器看的指令,不可被翻译为机器码
# MIPS 中 move 不是汇编指令,是伪指令
move $t0, $t1 # $t0 = $t1
# move 伪指令编译器看到会将其转换类似于以下的汇编指令
add $t0, $t1, $zero # $t0 = $t1 + 0
# 同样 MIPS 中 ble、blt、bge、bgt 也是伪指令,最终会被 slt 和 beq 替代
ble $t0, $t1, target # if $t0 <= $t1 then target
blt $t0, $t1, target # if $t0 < $t1 then target
bge $t0, $t1, target # if $t0 >= $t1 then target
bgt $t0, $t1, target # if $t0 > $t1 then target
# 伪指令使 MIPS 拥有硬件所实现的更为丰富的指令集
# 每条指令最终都会翻译为二进制序列,编译器必须处理所有标号对应的地址。这些标号都放入符号表中
# UNIX 目标文件:
# 1. 目标文件头,描述目标文件其他部分的大小和位置
# 2. 代码段,包含机器码
# 3. 静态数据段,包含程序生命周期内分配的数据
# 4. 重定位信息,标记程序加载进内存时依赖绝对地址的指令和数据
# 5. 符号表,包含未定义的剩余标记,如外部引用
# 6. 调试信息,包含说明目标模块如何编译的描述
# 链接出现的原因
# 对于源程序的任意一处修改都需要重新编译代码
# 但是库程序,所有程序都会被引用,且也几乎不会被更改
# 如果对于库程序也要重新编译,这将会存在大量的浪费
# 静态链接库:在程序运行前就链接
# 动态链接库:在程序执行过程中才链接库程序