"); //-->
摊上了移植bootloader和OS的事情,没办法,得把mips工作机制弄明白了才能干好啊。今天托我家狗狗了买了本 《see mips run》。就用它为主了。争取用一个月的时间走一遍。同时把学习笔记和心得记录到这里。本篇为第一章。从mips体系结构开始。
一.mips汇编语言
1.借助c预处理器,在汇编代码中避免使用寄存器编号。尽量使用寄存器名称。
2. 在一个单词后加 ":"表示这是个标号,标号可以包含各种字符,以及定义代码入口点和指定数据段的存储地址。
/* this is a comment */
# so is this
entrypoint: #that is a label
addu $1,$2,$3 #(regisers) $1 =$2+$3
3. 许多指令是3 个操作数,目的寄存器在左边(和intel的x86相反的)。
目前知道这些就行了。
二 寄存器的特点
1. 有32个通用寄存器可供编程使用:$0 -$31.其中两个寄存器特殊:
$0 无论存入什么,总是返回0.
$31 被常规子程序调用指令(jal)用来保存返回地址。注意:尽管寄存器调用指令(jalr)可以用任何寄存器存放返回地址,但不建议使用$31以外的其他寄存器。
2. 其他方面,所有寄存器都是一样的,可以在任何指令中以相同的方式使用。
3. jal 指令返回地址是下例第三行指令,而不是气候的那条指令。
.........
jal printf
move $4,$6
xxx #return here after call
紧跟在调用指令后面立即执行的指令(move)称做调用的延迟槽。
4. hi和lo保存计算结果。它们不是通用寄存器,且只能用于乘和除的操作。但mips定义了一些指令可以往hi和lo 里存入任何值。
5. 浮点数处理和具体的实现有关。可以看看godson的相关文档。
6. 寄存器的习惯命名和用法。
7. 寄存器约定的汇编程序名称和用法。
at:这个寄存器由编译器生成的复合指令使用。没完全懂。
v0,v1:用来存放子程序(函数)的非浮点的结果或返回值。如果两个寄存器不够存放需要返回的值,编译器会通过内存来完成。
a0-a1:用来传递前4个非浮点参数给一个子程序,并非绝对。
s0-s8:习惯上,子程序必须保证其中的值在调用前后保持不变。那有两种情况,要么在子程序中不使用它,要么调用前后把他们保存在堆栈中并在函数退出时恢复。这使他们非常适合做寄存器变量或保存那些在一次子程序调用过程中需要保持不变的值。
ko-k1: 为操作系统陷入/中断处理保留使用。可以使用他们而不恢复原来的值。
gp: 如果存在一个全局指针,它将指向静态数据区中的一个运行时临时决定的地址。不明白
sp: 上下移动栈指针需要明确的指令,因此mips代码通常通常只在子程序入口或出口处调整栈指针,这应该由被调用子程序实现。在子程序入口点,sp被调整指向栈底部,编译器可以通过一个sp中的便宜地址访问栈里的变量。
fp: 帧指针,也称做 s8。若子程序想要在运行时候动态的扩展栈的大小。fp 将用以跟踪栈变换的情况。c语言中的alloca()就是利用了fp来动态的调整栈的。
ra: 在任何子程序的入口点。返回地址存放在ra寄存器中。所以子程序通常都以 “jr ra”指令结尾。当一个子程序需要调用其他子程序必须先存储 ra时,通常存放在栈中。
三 寻址方式
加载和存储。任何载入和存储机器指令都可以写成:
lw $1,offset($2)
可以使用任何寄存器作为目的操作数或源操作数。偏移量是个有符号的16位数。程序使用rd与偏移量之和载入地址。
四 存储器和寄存器中的数据类型
1. 整数类型数据
2.未对齐的加载和存储。
3,存储器中的浮点数据。
五 汇编语言的合成指令。
汇编器对汇编程序员的帮助有下:
1. 一个32位的立即数加载:可以在代码中加载任何数据,汇编器将会将其拆开为两条指令,分别是在加载数据的前半部分和后半部分。
2. 从一个内存地址加载:可以加载一个内存变量。汇编器会把这个变量地址的高位放入一个暂时的寄存器中,然后将低位作为一个加载的偏移量。
3.对内存变量的快速访问。利用gp。
4.更多类型的跳转条件:汇编器通过对两个寄存器的算术测试来合成一系列的条件跳转。
5.简单或不同形式的指令:一元操作。如 not.neg sub.还可以用两个操作数的方式表示一个三操作数的指令。
6. 隐藏跳转延迟槽。
7.隐藏加载延迟:如果汇编器检测到一条指令试图使用上一条加载指令的结果。它会转移代码。早期的mips中,采取的是加入一条nop.
8.没对齐的数据传送。
9.其他流水线的矫正。
10.其它的优化。
六 基本地址空间
32位cpu的地址空间划分:
kuseg 0x00000000-0x7fffffff(低端2G):用户模式下可用的地址。分为有MMU无MMU两种情况。如果希望代码在两者之间具有兼容性,最好不使用这片区域。
kseg0 0x80000000-0x9fffffff(512M):只需把最高位清零,这些地址就变为物理地址。然后把他们连续映射到物理内存中512M大小的低字段(0x00000000-0x1fffffff内)。通常通过快速缓存对这段内存区域访问。因此在cache被正确初始化之前,不使用这片地址。通常在无MMU的系统中,存放大多数程序和数据,有MMU的系统中,操作系统会存放在这个区域。
kseg1 0xA0000000-0xbfffffff(512M):这个区域不经过cache存取(uncached)。这块是唯一在系统重启时候能正常访问的地址空间。入口向量是0xbfc00000会在这个区域的原因了。
kseg2 0xc0000000-0xfffffff(1G):这段地址空间只能在核心态下使用并且经过MMU的转化。在MMU没有设置好之前,不能对它访问。
今天就写到这里,做为mips的一些基本知识。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。