第一周
第二周
:tabe fn 在一个新的标签页中编辑文件fn gt 切换到下一个标签页 gT 切换到上一个标签页 :tabr 切换到第一个标签页 :tabl 切换到最后一个标签页 :tabm [N] 把当前tab移动到第N个tab之后 对,正如你所想象的那样,跟eclipse, ue等的标签页是一个意思! 窗口命令 ctrl+w s 水平分割窗口 ctrl+w w 切换窗口 ctrl+w q 退出当前窗口(由于同时有多个文件,此命令不会影响其他窗口) ctrl+w v 垂直分割窗口
:w 将缓冲区写入文件,即保存修改 :wq 保存修改并退出 :x 保存修改并退出 :q 退出,如果对缓冲区进行过修改,则会提示 :q! 强制退出,放弃修改 查找替换 /pattern 向后搜索字符串pattern ?pattern 向前搜索字符串pattern n 下一个匹配(如果是/搜索,则是向下的下一个,?搜索则是向上的下一个) N 上一个匹配(同上) :%s/old/new/g 搜索整个文件,将所有的old替换为new :%s/old/new/gc 搜索整个文件,将所有的old替换为new,每次都要你确认是否替换 复制粘贴 dd 删除光标所在行 dw 删除一个字(word) x 删除当前字符 X 删除前一个字符 D 删除到行末 yy 复制一行,此命令前可跟数字,标识复制多行,如6yy,表示从当前行开始复制6行 yw 复制一个字 y$ 复制到行末 p 粘贴粘贴板的内容到当前行的下面 P 粘贴粘贴板的内容到当前行的上面 ]p 有缩进的粘贴,vim会自动调节代码的缩进 "a 将内容放入/存入a寄存器,可以支持多粘贴板
h,j,k,l 上,下,左,右 ctrl-f 上翻一页 ctrl-b 下翻一页 % 跳到与当前括号匹配的括号处,如当前在{,则跳转到与之匹配的}处 w 跳到下一个字首,按标点或单词分割 W 跳到下一个字首,长跳,如end-of-line被认为是一个字 e 跳到下一个字尾 E 跳到下一个字尾,长跳 b 跳到上一个字 B 跳到上一个字,长跳 0 跳至行首,不管有无缩进,就是跳到第0个字符 ^ 跳至行首的第一个字符 $ 跳至行尾 gg 跳至文件的第一行 gd 跳至当前光标所在的变量的声明处 [N]G 跳到第N行,如0G,就等价于gg,100G就是第100行 fx 在当前行中找x字符,找到了就跳转至 ; 重复上一个f命令,而不用重复的输入fx tx 与fx类似,但是只是跳转到x的前一个字符处 Fx 跟fx的方向相反 ),( 跳转到上/下一个语句 * 查找光标所在处的单词,向下查找 # 查找光标所在处的单词,向上查找 `. 跳转至上次编辑位置 在屏幕上移动
H 移动光标到当前屏幕上最上边的一行 M 移动光标到当前屏幕上中间的一行 L 移动光标到当前屏幕上最下边的一行 书签 ma 把当前位置存成标签a `a 跳转到标签a处 编辑 r 替换一个字符 J 将下一行和当前行连接为一行 cc 删除当前行并进入编辑模式 cw 删除当前字,并进入编辑模式 c$ 擦除从当前位置至行末的内容,并进入编辑模式 s 删除当前字符并进入编辑模式 S 删除光标所在行并进入编辑模式 xp 交换当前字符和下一个字符 u 撤销 ctrl+r 重做 . 重复上一个编辑命令 ~ 切换大小写,当前字符 g~iw 切换当前字的大小写 gUiw 将当前字变成大写 guiw 将当前字变成小写 >> 将当前行右移一个单位 << 将当前行左移一个单位(一个tab符) == 自动缩进当前行
插入模式 i 从当前光标处进入插入模式 I 进入插入模式,并置光标于行首 a 追加模式,置光标于当前光标之后 A 追加模式,置光标于行末 o 在当前行之下新加一行,并进入插入模式 O 在当前行之上新加一行,并进入插入模式 Esc 退出插入模式 可视模式 标记文本 v 进入可视模式,单字符模式 V 进入可视模式,行模式 ctrl+v 进入可视模式,列模式,类似于UE的列模式 o 跳转光标到选中块的另一个端点 U 将选中块中的内容转成大写 O 跳转光标到块的另一个端点 aw 选中一个字 ab 选中括号中的所有内容,包括括号本身 aB 选中{}括号中的所有内容 ib 选中括号中的内容,不含括号 iB 选中{}中的内容,不含{}
对标记进行动作 > 块右移 < 块左移 y 复制块 d 删除块 ~ 切换块中内容的大小
2. gcc
GNU CC(简称为gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。gcc又是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译。
GCC编译代码的过程如下:
我们可以把编译过程分成四步,以编译hello.c生成可执行文件hello为例,如下图:
- 预处理:gcc –E hello.c –o hello.i;gcc –E调用cpp
- 编 译:gcc –S hello.i –o hello.s;gcc –S调用ccl
- 汇 编:gcc –c hello.s –o hello.o;gcc -c 调用as
- 链 接:gcc hello.o –o hello ;gcc -o 调用ld
编译过程比较难记,我们简化一下,前三步,GCC的参数连起来是“ESc”,相应输入的文件的后缀是“iso”,这样记忆起来就容易多了。
学习GCC的另外一个重点是:参考教材《深入理解计算机系统》 7.6,7.10节,学习静态库,动态库的制作。
info b 查看所设断点
break 行号或函数名 <条件表达式> 设置断点
tbreak 行号或函数名 <条件表达式> 设置临时断点,到达后被自动删除
delete [断点号] 删除指定断点,其断点号为”info b”中的第一栏。若缺省断点号则删除所有断点
disable [断点号]] 停止指定断点,使用”info b”仍能查看此断点。同delete一样,省断点号则停止所有断点
enable [断点号] 激活指定断点,即激活被disable停止的断点
condition [断点号] <条件表达式> 修改对应断点的条件
ignore [断点号]<num> 在程序执行中,忽略对应断点num次
step 单步恢复程序运行,且进入函数调用
next 单步恢复程序运行,但不进入函数调用
finish 运行程序,直到当前函数完成返回
c 继续执行函数,直到函数结束或遇到新的断点
由于设置断点在Gdb的调试中非常重要,所以在此再着重讲解一下Gdb中设置断点的方法。
Gdb中设置断点有多种方式:其一是按行设置断点,设置方法在3.5.1节已经指出,在此就不重复了。另外还可以设置函数断点和条件断点,在此结合上一小节的代码,具体介绍后两种设置断点的方法。
① 函数断点
(gdb) b 函数名
② 条件断点
格式为:b 行数或函数名 if 表达式
(gdb) b 8 if i==10
3. gdb
2.1预处理
可以输出test.i文件中存放着test.c经预处理之后的代码。打开test.i文件,看一看,就明白了。后面那条指令,是直接在命令行窗口中输出预处理后的代码.
gcc的-E选项,可以让编译器在预处理后停止,并输出预处理结果。在本例中,预处理结果就是将stdio.h 文件中的内容插入到test.c中了。
2.2编译为汇编代码(Compilation)
预处理之后,可直接对生成的test.i文件编译,生成汇编代码:
gcc的-S选项,表示在程序编译期间,在生成汇编代码后,停止,-o输出汇编代码文件。
2.3汇编(Assembly)
对于上一小节中生成的汇编代码文件test.s,gas汇编器负责将其编译为目标文件,如下:
2.4连接(Linking)
gcc连接器是gas提供的,负责将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。附加的目标文件包括静态连接库和动态连接库。
对于上一小节中生成的test.o,将其与C标准输入输出库进行连接,最终生成程序test
在命令行窗口中,执行./test, 让它说HelloWorld吧!
建议使用CGDB,比GDB好用,熟悉VC的调试方式,可以使用DDD。 注意使用GCC编译时要加“-g”参数。 参考gdb参考卡GDB最基本的命令有:
- gdb programm(启动GDB)
- b 设断点(要会设4种断点:行断点、函数断点、条件断点、临时断点)
- run 开始运行程序
- bt 打印函数调用堆栈
- p 查看变量值
- c 从当前断点继续运行到下一个断点
- n 单步运行
- s 单步运行
- quit 退出GDB
4.其他
display 跟踪变量值的改变
until 跳出循环
finish 跳出函数
help 帮助
第三周
一、数字表示
1、 无符号数:编码基于传统的二进制表示法表示大于或等于零的数字。
2、 补码:编码是表示有符号整数的最常见方法,可以是正或者是负的数字。
3、 浮点数:编码是表示实数的科学计数法的以二位基数的版本。
- 三种数字:无符号数、有符号数(2进制补码)、浮点数
- 溢出:计算机的表示法是用有限数量的位来对一个数字编码,当结果太大以至不能表示时,会溢出
- 整数运算:编码的数值范围较小,精确;浮点运算:数值范围较大,近似,不可结合
§1 信息存储
- 最小的可寻址的存储器单位:字节(8位)
- 虚拟存储器、地址、虚拟地址空间(p22)
进制转换
十六进制表示法
十六进制中一个字节的值域为00H~FFH
用0x或0X开头表示十六进制数字常量
快捷算法:要表示的数字常量为x=2^n,n=i+4j,且0≤i≤3时,开头的十六进制数字为1(i=0)、2(i=1)、4(i=2)、8(i=3),后面跟随着j个十六进制的0。这里的j是代表着每四位二进制位对应的十六进制位,而i的范围是因为十六进制中每一位的范围是0-F
二、字
字长:指明整数和指针数据的标称大小。决定虚拟地址空间的最大大小
字长为w,虚拟地址范围为0~2^w-1,程序最多访问2^w个字节,cpu一次处理w位数据
三、数据大小
在不同字长的计算机中,相同的数据类型所占用的字节数不同
在64位机上生成32位代码:gcc -m32
32位与64位机器中的数据大小
C声明 |
32位机器 |
64位机器 |
char |
1 |
1 |
short int |
2 |
2 |
int |
4 |
4 |
long int |
4 |
8 |
long long int |
8 |
8 |
char * |
4 |
8 |
float |
4 |
4 |
double |
8 |
8 |
四、寻址和字节顺序
多字节对象倍存储为连续的字节序列,对象的地址为所使用字节中最小的地址
字节顺序是网络编程的基础
1.两个通用规则(w为整数,位表示为[Xw-1,Xw-2,……,X1,X0],其中Xw-1是最高有效位,X0是最低有效位):
- 小端法:最低有效字节在最前面(大多数Intel兼容机)
- 大端法:最高有效字节在最前面(大多数IBM和Sun Microsystems机器)
- 一些新的微处理器使用双端法
字节内部的顺序不变
反汇编器:确定可执行程序文件所表示的指令序列的工具;将可执行程序文件转换回可读性更好的ASCII码形式的程度
2.强制类型转换
表示字符串
c语言中字符串被编码成为一个以null(值为0)字符结尾的字符数组
命令man ascii:得到ASCII字符码表
表示代码
二进制代码在不同的操作系统上有不同的编码规则。所以二进制代码是不兼容的
布尔代数
1.最简单布尔代数:与& 或| 非~ 异或^(结果为0或1)
2.扩展的布尔运算:位向量的运算(结果仍是位向量)
位向量的应用:表示有限集合,对集合编码
位级运算
1.将位向量按位进行逻辑运算,结果仍是位向量
2.掩码运算
掩码:用来选择性的屏蔽信号,是一个位模式,表示从一个字中选出的位的集合。
用位向量给集合编码,通过指定掩码来有选择的屏蔽或者不屏蔽一些信号,某一位位置上为1时,表明信号i是有效的;0表示该信号被屏蔽。这个掩码就表示有效信号的集合。
0xFF:屏蔽除最低有效字节之外的所有字节。
~0:生成全1的掩码
逻辑运算
1.逻辑运算符:与&& 或|| 非!
2.计算方法:所有非零参数都代表TRUE,0参数代表FALSE。1代表TRUE,0代表FALSE
- 只有当参数被限制为0或1时,逻辑运算才与按位运算有相同的行为。
- 如果对第一个参数求值就能确定表达式的结果,逻辑运算符就不会对后面的参数求值。
移位运算
C语言还提供了一组移位运算,以便向左或向右移动位模式。移位运算可以从左向右结合的,所以x<<j<<k等价于(x<<j)<<k。
一般而言,机器支持两种形式的右移:逻辑右移和算数右移。逻辑右移在最左端补k个0,算数右移在最左端补k各最高有效位的值。
C语言标准没有明确定义应该使用哪种类型的右移。对于无符号数据,右移必须是逻辑的。对于有符号数据,算数的或者逻辑的右移都可以。
C语言中有符号数和无符号数的转换
位向量不变,只是上下文的读取方式不同,所以根据不同的读取规则,最终的读取结果也不同。这就是所谓的信息就是位+上下文。
注:c语言中要创建一个无符号常量,必须加上后缀字符'U'或者'u'。
转换的原则是底层的位表示保持不变。
怎么让负数等于正数
将有符号负数转化为无符号正数,变的只是上下文的读取方式,但二进制位级表示是一样的。
零扩展和符号扩展
零扩展:将一个无符号数转换为一个更大的数据类型,我们只需要简单地在开头添加0。
号扩展:将一个补码数字转换为一个更大的数据类型,规则是在表示中添加最高有效位的值的副本。
无符号数与有符号数容易造成的错误
例:以下一段代码
float sum_elements(float a[],unsigned length){
int i;
float result=0;
for(i=0;i<=length-1;i++)
result+=a[i];
return result;
}
因为参数length是无符号的,计算0-1将进行无符号运算,这等价于模数加法。结果得到UMax。<=比较进行同样使用无符号数比较,而因为任何数都是小于或者等于UMax的,所以这个比较总是为真!因此,代码将试图访问数组a的非法元素。
改正方法:1.将length声明为int类型。
2.或将for循环的测试条件改为i<length。
整数溢出
整数溢出:指完整的整数结果不能放到数据类型限制的字长中去。
避免整数溢出:当两个整数进行运算时,其结果用更大的数据类型进行存储。比如两个int类型的整数相乘的结果用long long数据类型的变量来存储。
关于整数运算的思考
计算机执行的"整数"运算实际上是一种模运算形式。表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。
补码提供了一种既能表示负数也能表示正数的灵活方法,同时使用了与执行无符号算数相同的位级实现,这些运算包括加减乘除,无论是以无符号形式还是以补码形式表示的,都有完全一样或者非常类似的位级行为。
浮点数
浮点表示对形如V=x*的有理数进行编码。它对涉及非常大的数字(| V|>>0)、非常接近于0(|V|<<1)的数字,以及更普遍地作为实数运算的近似值的计算,是很有用的。
浮点数的运算及执行标准:IEEE标准754。
浮点数运算的不精确性和舍入
当一个数字不能精确地表示为IEEE标准754时,就必须向上或者向下调整,此时出现舍入。
IEEE浮点标准,float和double类型p70
单精度浮点格式(float)中,s、exp和frac字段分别为1位、k=8位和n=23位,得到一个32位的表示。
双精度浮点格式(double)中,s、exp和frac字段分别为1位、k=11位和n=52位,得到一个64位的表示。
整数与浮点数转换规则
当在int、float和double格式之间进行强制类型转换时,程序改变数值和位模式的原则如下(假设int是32位的):
-
从int转换成float,数字不会溢出,但是可能被舍入。
-
从int或float转换成double,因为double有更大的范围(也就是可表示值的范围),也有更高的精度(也就是有效位数),所以能够保留精确的数值。
-
从float或者double转换为int,值将会向零舍入。
第四周
1.P104\P105
X86 寻址方式经历三代:
-DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
-8086的分段模式
-IA32的带保护模式的平坦模式
2.P106
ISA:机器级程序的格式和行为,定义为指令集体系机构,它定义了处理器状态指令的格式,以及每条指令对状态的影响。
机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字符数组
PC:程序计数器。在IA32中,用%eip表示,指示将要执行的下一条指令在存储器中的地址。
程序存储器:包含程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的存储器块。
3.P107
64位机上想得到32位代码:gcc -m32 -S xxx.c
编译并产生汇编目标文件xxx.o:gcc -O1 -c xxx.c
获得汇编代码:gcc -S xxx.c -o xxx.s
Ubuntu中获得汇编代码:gcc -S xxx.c更接近教材
教材中获得汇编代码:gcc -O1 -S xxx.c(编译器使用的事第一级优化)
4.P108
二进制文件可用od命令查看,也可以用gdb的x查看:(gdb)x/17xb sum表示检查17个十六进制的字节;
显示代码过多或过少可用more、less结合管道查看,也可以用输出重定向:
od xxx.o | more
od xxx.o > xxx.txt
5.P109
gcc -S 产生的汇编中可以把 以”.“开始的语句都删除了再阅读
6.P110
Linux和windows的汇编格式的区别:
-Intel代码省略了指示大小的后缀,即'l'
-Intel代码省略了寄存器名字前面的‘%’符号,用的是esp,而不是%esp
-Intel代码用不同的方式来描述存储器中位置
-在带有多个操作数的指令情况下,列出操作数的顺序相反
7.P111
-db char 1
-dw short int 2
-dd int 或float 4
-dq long int 或 double 8
8.P112
-esi、edi可以用来操纵数组,esp、ebp用来操纵栈帧。
-通用寄存器中的eax,ebx,ecx,edx中,32位的eax,16位的ax,8位的ah,al都是独立的。例如:假定当前是32位x86机器,eax寄存器的值为0x8226,执行完addw $0x8266, %ax指令后eax的值是多少?
解析:0x8226+0x826=0x1044c, ax是16位寄存器,出现溢出,最高位的1会丢掉,剩下0x44c,不要以为eax是32位的不会发生溢出。
多数情况下,前6个寄存器为通用寄存器,最后两个寄存器保存着指向程序栈中重要位置的指针。
另外,字节操作指令可以独立的读或者写前4个寄存器的2个低位字节。
9.P113
操作数的三种类型:立即数(常数值)、寄存器(某个寄存器的内容)、存储器(根据计算出来的地址访问某个存储器位置)
-有效地址的计算方式 Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
10.P114
-MOV:相当于C语言的赋值“=”,将源操作数的值复制到目的操作数中;
MOVS:将一个较小的源数据复制到一个较大的数据位置,高位用位扩展;
MOVZ:将一个较小的源数据复制到一个较大的数据位置,高位用零扩展。
-push:把数据压入栈中;
pop:删除数据。
11.P115\P116
-栈顶元素的地址是所有栈中元素地址中最低的
-栈指针%esp保存栈顶元素的地址。
12.P117
-指针就是地址
-局部变量保存在寄存器中
13.P119
按目的操作数分类:
第一类:加载有效地址。实际是将有效地址写入目的操作数,目的操作数必须是寄存器。
第二类:一元操作。操作数既是源又是目的。可以是寄存器也可以是存储器。
第三类:二元操作。第二个操作数既是源又是目的。但两个操作数不能同时是存储器。
第四类:移位操作。位移量是一个立即数或放在单字节寄存器%cl中。移位操作的目的操作数可以是一个寄存器或是一个存储器位置。
14.P123\P124
有条件跳转(实现if,switch,while,for)
无条件跳转jmp(实现goto)
15.P125
SET指令根据t=a-b的结果设置条件码
16.P127
跳转指令(导致执行切换到程序中一个全新的位置,跳转的目的地通常用一个标号指明)
无条件跳转:JMP 可以是直接跳转也可以是间接跳转(写法是*后面加操作数指示符)
有条件跳转:根据条件码的某个组合,或者跳转或者继续执行下一条指令
17.P130-P145
-条件分支——if-else结构:在两个分支语句中选择执行一个,汇编实现通过goto,就是汇编器为两个分支产生各自的代码块,它会插入条件和无条件分支,以保证能执行正确的代码块。
-循环结构——do-while、while、for:用条件测试和跳转组合实现循环的效果。大多数汇编器根据do-while形式来产生循环代码,其他的循环会首先转换成do-while形式,然后再编译成机器代码。
-switch语句:根据一个整数索引值进行多重分支。通过使用跳转表这种数据结构实现更加高效。跳转表是一个数组,表项i是一个代码段的地址,这个代码段实现当开关索引值为i时程序该做的。此时跳转可以用goto/jmp
18.P149
IA32通过程序栈来实现过程调用
19.P150\P151
call指令\ret指令
函数返回值存在%eax中
20.P174
关于栈帧的gdb命令bt/frame/up/down
命令 |
描述 |
---|---|
backtrace(或bt) |
查看各级函数调用及参数 |
finish |
连续运行到当前函数返回为止,然后停下来等待命令 |
frame(或f) 帧编号 |
选择栈帧 |
info(或i) locals |
查看当前栈帧局部变量的值 |
list(或l) |
列出源代码,接着上次的位置往下列,每次列10行 |
list 行号 |
列出从第几行开始的源代码 |
list 函数名 |
列出某个函数的源代码 |
next(或n) |
执行下一行语句 |
print(或p) |
打印表达式的值,通过表达式可以修改变量的值或者调用函数 |
quit(或q) |
退出
|
set var |
修改变量的值 |
start |
开始执行程序,停在
|
step(或s) |
执行下一行语句,如果有函数调用则进入到函数中 |
break(或b) 行号 |
在某一行设置断点 |
break 函数名 |
在某个函数开头设置断点 |
break ... if ... |
设置条件断点 |
continue(或c) |
从当前位置开始连续运行程序 |
delete breakpoints |
删除断点 |
display 变量名 |
跟踪查看某个变量,每次停下来都显示它的值 |
disable breakpoints |
禁用断点 |
enable 断点号 |
启用断点 |
info(或i)breakpoints |
查看当前设置了哪些断点 |
run(或r) |
从头开始连续运行程序 |
undisplay 跟踪显示号 |
取消跟踪显示 |
watch |
设置观察点 |
info(或i) watchpoints |
查看当前设置了哪些观察点 |
x |
从某个位置开始打印存储单元的内容,全部当成字节来看,而不区分哪个字节属于哪个变量 |
第五周
4.1 Y86指令集体系结构
一、程序员可见的状态
程序员可见状态:程序中的每条指令都会读取或修改处理器状态的某些部分
--8个程序寄存器:%eax,%ecx,%edx,%ebx,%esi,%edi,%esp,%ebp.他们都可以存储一个字;
%esp被入栈、出栈、调用和返回指令作为栈指针;
其他情况时寄存器没有固定的含义或固定值
--3个一位条件码:ZF、OF、SF.保存最近的算术或逻辑指令所造成影响的有关信息
--PC(程序计数器):存放当前正在执行指令的地址。
--存储器:一个很大的字节数组,保存着程序和数据;
Y86用虚拟地址来引用存储器位置,硬件和操作系统软件联合起来将虚拟地址翻译成实际或物理地址,指明数据实际保存在存储器中哪个地方
--Stat:状态码,程序状态的最后一个部分,表明程序执行的总体状态,指示是正常运行还是出现了某种异常
二、Y86指令
--halt:这个指令将会终止指令的执行。
--nop:这是一个占位指令,它不做任何事情,后续为了实现流水线,它有一定的作用。
--xxmovl:这是一系列的数据传送指令,其中r代表寄存器,m代表存储器,i代表立即数。比如rrmovl指令,则代表将一个寄存器的值,赋给另外一个寄存器。
--opl:操作指令,比如加法,减法等等。
--jxx:条件跳转指令,根据后面的条件进行跳转。
--cmovxx:条件传送指令,后面的xx代表的是条件。特别的是,条件传送只发生在两个寄存器之间,不会将数据传送到存储器。
--call与ret:方法的调用和返回指令。一个将返回地址入栈,并跳到目标地址。一个将返回地址入PC,并跳到返回地址。
--push与pop:入栈和出栈操作。
三、指令编码
--对于opl、jxx、cmovxx指令来说,都有一个fn标识,占用4个二进制位(半个字节)。这个便是指令的功能部分,这个是由于它们的指令编码一样,但功能有所不同所造成的。比如对于opl,就有加、减、与、异或等操作,那么它们的指令编码第一个字节就分别为十六进制的60、61、62、63。
--Y86指令集图中可看见指令的字节级编码。每条指令需要1-6个字节不等。每条指令的第一个字节表明指令的类型。这个字节分为两个部分,每部分4位:高4位是代码部分,低4位是功能部分。功能值只有在一组相关指令共用一个代码时才有用。
--有的指令只有一个字节长,因为可能附加有寄存器指示符字节,指定一个或两个寄存器。这些寄存器字段为rA,rB。有则有,无则无,只有一个的则将第二个设为0xF。
四、Y86异常
--对于Y86来说,程序猿可见的状态中就有stat状态码,它标识了程序执行的状态。Y86需要有能力根据stat去做一些处理。不过为了简单起见,这里除了正常执行之外,都将停止指令的执行。真实当中,会有专门的异常处理程序。
--Y86有四种不同的状态码,AOK(正常)、HLT(执行halt指令)、ADR(非法地址)和INS(非法指令)。
五、Y86程序和Y86指令详情
--比如对于X86指令中的 addl $4,%ecx 这样的指令,由于Y86当中的addl指令中不包含立即数,所以Y86需要先将立即数存入寄存器,即使用irmovl指令,然后再使用addl来处理加法运算。
--创建Y86代码的唯一工具是汇编器。
--以“.”开头的词是汇编命令,他们告诉汇编器调整地址,以便在那儿产生代码或插入一些数据。命令.pos0告诉编译器应该从地址0处开始产生代码。这个地址是所有Y86程序的起点。
4.2 逻辑设计和硬件控制语言HCL
一、逻辑门
--逻辑门产生的输出,等于它们输入位值的某个布尔函数。
--AND &&
OR ||
NOT !
二、组合电路和布尔表达式
--逻辑门产生的输出,等于它们输入位值的某个布尔函数。
--两个或多个逻辑门的输出不能连接在一起,否则可能会使线上的信号矛盾,导致一个不合法的电压或电路故障。
--网必须无环。
三、字级的组合电路和HCL整数表达式
--所有字级的信号都声明为int,不指定字的大小
--算数/逻辑单元(ALU)是很重要的组合电路,有三个输入,标号为A、B的两个数据输入和一个控制输入。根据控制输入的设置,电路会对数据输入执行不同的算数或逻辑操作。
四、集合关系
--判断集合关系的通用格式是:iexpr in {iexpr1,iexpr2,...,iexprk}
五、存储器和时钟
--时钟寄存器(简称寄存器)存储单个位或字,时钟信号控制寄存器加载输入值
--随机访问存储器(简称存储器)存储多个字,用地址来选择该读或该写哪个字
4.3 Y86的顺序实现
一、将处理组织成阶段
--取指:取指阶段从存储器读取指令字节,地址为程序计数器PC的值
--译码:译码阶段从寄存器文件读入最多两个操作数
--执行:在执行阶段,算数/逻辑单元要么根据ifun的值执行指令指明的操作,计算机存储器引用的有效地址,要么增加或减少栈指针
--访存:访存阶段可以将数据写入存储器,或从存储器读出数据
--写回:写回阶段最多可以写两个结果到寄存器文件
--更新PC:将PC设置成下一条指令的地址
二、SEQ硬件结构和时序:看书上P258图
三、SEQ阶段的实现
--取指阶段:取指阶段包括指令存储器硬件单元。以PC作为第一个字节(字节0)的地址,这个单元一次从存储器读出6个字节,第一个字节被解释称指令字节,分为两个4位数。标号为“icode”和“ifun”的控制逻辑块计算指令和功能码等于从存储器读出值,或者当指令地址不合法时(imem_error指明),这些值对应于nop指令。
--译码和写回阶段:都要访问寄存器文件。寄存器文件有四个端口,支持同时进行两个读(端口A、B)和两个写(E、M),每个端口都有一个地址连接和一个数据连接。根据指令代码icode以及寄存器指示值rA和rB,可能还会根据执行阶段计算出的Cnd条件信号。
--执行阶段:执行阶段包括算术/逻辑单元(ALU)第一步每条指令的ALU计算,执行阶段还包括条件码寄存器。
--访存阶段:访存阶段的任务是读或者写程序数据,两个控制块产生存储器地址和存储器输入数据的值,另外两个块产生控制信号表明应该执行读操作还是写操作。当执行读操作时数据存储器产生值valM。
--更新PC阶段:SEQ中最后一个阶段会产生程序计数器的新值,依据指令的类型和是否要选择分支,新的PC可能是valC、valM、valP
第六周
三种常见存储技术:RAM/ROM/磁盘
(1)随机访问存储器RAM
-
两类:静态RAM(SRAM)和动态RAM(DRAM)
-
静态RAM(SRAM)比动态RAM(DRAM)更快,但也贵很多。
-
静态RAM
- SRAM将每个位存储在一个双稳态的存储器单元里,每个单元是用一个六晶体管电路来实现的。
-
这个电路的一个属性:它可以无限制地保持在两个不同的电压配置或状态之一。其他任何状态都是不稳定的。
- 特点:由于SRAM的双稳态特性,只要有电,它就会永远地保持它的值,即使有干扰,如电子噪音,来扰乱电压,当干扰消除,电路也能恢复到稳定值。 - 应用:SRAM用来作为高速缓存存储器,即可以在CPU芯片上,也可以在片下。
-
动态DRAM
-
DRAM将每个位存储为对电容的充电。电容约为30×10-15F。
- 特点:对干扰特别敏感,当电容的电压被扰乱之后,它就永远不会恢复了。暴露在光线下会导致电容电压改变。
- - FLASH:闪存,基于EEPROM。(固态硬盘SSD基于闪存)
-
存储在ROM设备中的程序通常称为固件。
(3)访问主存
- 读事务:从主存传送数据到CPU。 - 写事务:从CPU传送数据到主存。 - 总线:一组并行的导线,能携带地址、数据的控制信号。 数据总线、控制总线、地址总线
-
读事务语句
movl A,%eax
- CPU从总线读出字x,并将它copy到寄存器eax中。
-
写事务语句
-
movl %eax,A
(4)磁盘存储
-
磁盘构造
- 由盘片构成,每个盘片有两面或者称为表面,表面覆盖着磁性记录材料。盘片中央有一个可以旋转的主轴,使得盘片以固定的旋转速率旋转,通常是5400~15000转每分钟(RPM)
- 每个表面是由一组称为磁道的同心圆组成;每个磁道被划分成一组扇区;每个扇区包含相等数量的数据位(通常是512字节);这些数据编码在扇区上的磁性材料中。扇区之间由一些间隙分隔开,这些间隙中不存在数据位。间隙存储用来标识扇区的格式化位。
-
磁盘容量
- 一个磁盘上可以记录的最大位数称为它的最大容量/容量。
-
磁盘容量的决定因素:
- 记录密度:磁道一英寸的段可以放入的位数。 - 磁道密度:从盘片中心出发半径上一英寸的段内可以有的磁道数。 - 面密度:记录密度与磁道密度的乘积。
-
-
学习总结
老师提高我的学习能力,传授了科学的学习方法,以改变我不良的学习思维习惯为出发点,我在上课后及时消化课上知识,学习到不要就题论题,要灵活运用老师所讲知识举一反三,触类旁通,尤其是对我这种学习成绩并不是特别优秀的学生,学习后应遵循“复习为主,少做精练”的原则。古语说得好,温故而知新,我绝不能忽视旧课复习,一味地关注下半学期新课的学习,应充分利用下课将上半学期旧课知识的疑点、难点、重点进行有效地查漏补缺。