目录
1.大纲要求
1.1.指令格式
1.一条指令由操作码和若干操作数组成:
①操作码:这条指令的动作(加/减/乘/除/移位等等)
②操作数:该动作的参数(寻址方式位 + 形式地址)
2.扩展操作码:短操作码不能是长操作码的前缀(类似DS的哈夫曼树,CN的变长子网划分)
1.2.寻址方式
1.有效地址:即最终要访问的虚拟地址(PC→虚拟地址,MAR→物理地址)
2.①操作数在指令中以形式地址的方式给出
②CPU根据寻址方式将形式地址转换为有效地址(虚拟地址,存放在PC)
③通过TLB/页表将虚拟地址转换为物理地址(存放在MAR)
④通过Cache/内存访问物理地址
3.①隐含寻址:操作数默认在某个寄存器中或在栈顶,即无需指明操作数在何处(POP指令)
②立即寻址:指令中给出立即数,例如OP 10086,即对10086进行某种操作
③直接寻址:操作数给出的地址就是最终的有效地址(例:OP 指针,对某数据进行某种操作,并给出该数据的指针)
④一次间接寻址:OP 指针的指针,根据指针A找到该操作数的指针B,再通过该指针B找到操作数
⑤寄存器寻址:
⑥寄存器间接寻址:相当于指针存在寄存器中
⑦相对寻址:转移指令,EA = (PC)+ A
1.PC的内容加上指令中形式地址A形成操作数的有效地址,即EA = (PC)+ A(A为相对于PC的偏移量,正负都可,用补码表示)
2.PC在取出使用相对寻址的指令时,就会进行+ “1”操作,即相对寻址是相对于下一条指令的存放地址的偏移量("1"的大小取决于当前正在执行指令的指令字长)
3.优点:无论代码被放在什么位置,都不用更改跳转指令的地址码(若使用直接寻址的方式,则每次都需要更改)便于程序浮动(一段代码在程序内部浮动),广泛被用于转移类指令
⑧基址寻址:
1.基址寄存器中存放的是当前程序的首地址(基地址,运行中基址寄存器中的内容不变,形式地址作为偏移量可变)
2.基址寄存器可以是专用,也可以是指定一个通用寄存器(需要在指令中指明是哪个通用寄存器)
3.便于程序浮动(整段程序在内存中的浮动),多道程序并发运行(仅需修改基址寄存器的内容实现不同程序的正确寻址,相较于直接寻址,直接寻址需要修改每条指令的地址;OS动态分配中,每个程序占有连续的内存空间,可以通过基址寄存器记录每个程序在内存中的起始地址,通过偏移量的改变得到正确的地址);可以增加寻址范围(基址寄存器的位数大于形式地址位数,但由于形式地址决定偏移量,因此偏移量有限)
4.基址寄存器面向操作系统(用户可以决定使用哪个基址寄存器,但内容由操作系统决定)
⑨变址寻址:
1.变址寄存器中的内容(可变)作为偏移量,形式地址A(不变)作为基地址(与基址寻址刚好相反):可以通过不断的改变变址寄存器中的内容,访问数组中的元素,因此适合循环程序
2.变址寄存器可以是专用,也可以是指定一个通用寄存器(需要在指令中指明是哪个通用寄存器)
3.变址寄存器面向用户(用户可以修改变址寄存器的内容)
⑩堆栈寻址:
1.操作数存放在堆栈中,隐含使用堆栈指针(SP:Stack Pointer)作为操作数地址
2.堆栈指针存在专门的寄存器中,其指向栈顶元素
①专用寄存器:不同情况下对SP的操作顺序和操作方式不同(参考数据结构栈),取指令需访存,执行指令无需访存
②主存中开辟堆栈空间:每次操作都需要进行访存
1.3.数据对齐和大小端存放方式
1.4.CISC和RISC
判断是CISC/RISC:①指令字长是定长→RISC ②有除LOAD/STORE外指令访存→CISC
1.5.高级语言程序与机器级代码间对应
1.高级语言(C)经过编译器的编译后形成汇编语言
2.汇编语言和机器语言指令一一对应:一条汇编语言对应一条机器语言指令
3.汇编语言经过汇编器的编译后形成机器语言(010101)
4.链接器:把各个机器语言指令的模块(主函数和库函数)连接起来形成一个完整的地址空间
5.机器级代码:
①if - else、switch(条件转移指令):cmp a,b; jxxx(je、jle、jg、jge) xxxx
②goto(无条件转移指令):jump xxxx
2.x86
2.1.运算、逻辑类指令
有符号数和无符号数在形式上可能有差别,但是都会带有每个指令的主体部分
2.2.分支结构
1.CMP交由ALU运行,并由ALU产生标志位,再通过标志位进行判断和相应操作
2.偏移量的单位可能是字节,也可能是指令字长(往前/往后跳多少条指令,RISC):
jmp -10; PC = AE86
①以字节为单位:PC = AE86 - 10
②以指令字长为单位(RISC,定长指令4B):PC = AE86 - 10 * 4
2.3.循环结构
1.条件转移指令:先实现循环结构主体相对应的机器指令,再在这些指令后,添加cmp和jxx指令,即满足条件的话,则回到循环主体指令对应的开始处
2.loop:循环到label位置
2.4.函数调用/返回
1.(1)call(调用一个函数):
①压入调用函数的所使用的参数(程序员通过push指令手动实现)
②执行Call指令
③压入返回地址,即当前的PC值(CALL指令实现)(存放返回地址的地址是a栈的栈顶)
④压入a的栈底指针,即ebp的值(程序员通过push指令手动实现)(存放a的栈底地址的地址是b栈的栈底)(每个函数刚开始执行的时都要执行push ebp,即将上一层的栈底指针压入栈中)
(该函数刚开始便执行push ebp,将上一层的栈底地址压入函数调用栈中)
⑤修改ESP寄存器的值为b函数的栈顶,即ESP指向④;修改EBP为b函数的栈底,即EBP指向③(CALL指令实现)(esp寄存器和ebp寄存器分别只有一个,故执行b函数时保存的是b函数的相关值)
(存放返回地址的地址属于a栈,存放a的栈底地址的地址属于b栈)
(2)ret(这次调用返回):CALL的逆向操作
①通过逐一弹出函调用栈数的栈顶元素的方式(pop)回收该函数的函数调用栈空间,每次esp弹出后都会 - 1
②当弹出到ebp所指向的位置是该函数的栈底(该处存放上一层函数的栈底地址)时(ebp和esp此时都指向该栈的栈底)
A.弹出栈顶元素,并修改ebp寄存器为上一层函数的栈底地址(ebp指向上一层函数的栈底),esp弹出自动 - 1,即esp指向上一层函数的栈顶元素(该地址存放的是上一层函数的返回地址,即调用函数结束后,返回上一层函数继续执行的下一条指令的地址)
B.继续弹出栈顶元素,并将PC的值修改为上层函数的返回地址
2.设main函数调用a函数,a函数调用b函数:则函数调用栈中
①最底层为main函数
②a函数在main函数的上一层:存放a函数中的变量、返回地址(b函数执行结束,返回a函数后执行的下一条指令的地址;执行结束后,将PC的值修改为该地址)
③b函数在a函数的上一层:存放b函数中的变量、a函数的栈底地址的地址(函数b执行结束后
,ebp的值修改为该地址,作用是确定每个函数的地址范围)
即每个被调用函数需要记录:
①其函数结束,返回上一层函数后应该继续执行上一层函数的哪个指令,即返回地址
②上一层函数的栈底地址(存放该地址的地址作为自身函数的栈底,EBP指向该处)
3.①EBP(栈底指针)寄存器和ESP(栈顶指针)寄存器分别只有一个(函数调用和函数返回时ebp和esp都要进行修改)
②b函数在执行过程中,EBP指向b函数的栈底,ESP指向b函数的栈顶
③b函数执行结束,通过逐一弹出b函数的栈顶元素的方式(pop)回收b函数的函数调用栈空间(b函数的ESP在执行弹出栈顶元素操作时不断减1),直到b函数的ESP和EBP指向同一位置时,即到了b函数的栈底,且该位置存放a函数的栈底地址(a函数调用的b函数,即a函数是b函数的上一层函数);然后,EBP改为指向a栈底(将EBP寄存器的值从b的栈底地址改为a的栈底地址),ESP则通过继续弹出栈顶元素的方式,就会从指向b的栈底 - 1,即ESP指向a的栈顶(a的栈顶此时存放的是返回地址,即b函数结束,返回a函数时应该继续执行的下条指令),将a的栈顶元素弹出并放入PC,即将下一条要执行的a函数的指令(即调用b函数指令的下一条指令)放入PC中
4.函数中声明一个局部变量的机器级指令实现是通过一个push指令将该变量压入栈中
5.函数使用当前函数的局部变量和上一层函数调用时使用的参数是通过对EBP进行偏移实现的:
设每个地址的长度是4B,且高地址部分存放的是a栈,低地址部分存放的是b栈,函数a调用函数b,则在函数b的执行期间
①ebp为b的栈底,存放的是a的栈底的地址
②ebp + 4为返回地址,存放的是函数b执行结束返回函数a时,应该执行函数a的下一条指令的地址
③ebp + 8开始为函数a调用函数b时传入的参数,属于a栈(+8即a2,+12即a1)
④ebp - 4开始为函数b的局部变量,属于b栈(-4即b1,-8即b2)
3.真题
①第1行(push):每次循环调用该函数时,第一件事先将ebp寄存器的值(上一层函数的栈底地址)压入栈中
②第11行(cmp):将存放在ebp + 8地址的值(上一层函数调用该函数时所使用的的参数)和1对比
③第12行(jle):当第11行的cmp结果为真时,跳转到虚拟地址为0040 1035处执行,否则按顺序执行下一条指令
④第13行(mov):将存放在ebp + 8地址的值(上一层函数调用该函数时所使用的的参数)放入eax寄存器中
⑤第14行(sub):对eax寄存器的值减1(实现n-1)
⑥第15行(push):将eax寄存器的值压入函数调用栈中(调用函数时,传入该调用函数的参数要压入本函数的函数调用栈中)
⑦第16行(call):调用CALL函数,跳转虚拟地址为0040 1000H处执行指令(实现循环)
(1)f(10)将调用10次函数f1;执行第16行call指令时将会递归调用f1
(2)第12行jle是条件转移指令;第16行call、第20行jmp、第30行ret一定会使程序跳转执行
(3)第16行的CALL指令占用5个字节(E8 D6 FF FF FF),且该指令的地址为0040 1025H,则下一条指令的地址为0040 1025 + 5 = 0040 102AH
相对寻址方式是以PC寄存器中的值为基准进行偏移,PC中存放的是下一条指令的地址,则执行CALL指令时,PC中的内容是0040 102AH,要将下一条执行的指令回到f1处,则需将PC修改为0040 1000H,即偏移量0040 1000H - 0040 102A = -2AH
偏移量-2AH用补码表示为FF FF FF D6,而CALL指令的后4B为D6 FF FF FF,故采用小端存储
(4)f(13)的真值超过了32bit的int类型所能表示的范围,需要将返回值类型改为double
(5)当高n + 1位不全为0或者不全为1时,乘法就发生溢出;发生溢出时转为异常处理,则需要添加陷入指令