1. 给出以下概念的解释说明
-
机器语言程序
用机器语言编写的程序称为机器语言程序
-
汇编指令
用若干个助记符表示的与机器指令一一对应的指令称为汇编指令
-
汇编语言程序
用汇编语言编写的程序称为汇编语言程序
-
汇编助记符
在汇编语言中,通常用容易记忆的英文单词或缩写来表示指令操作码的含义,用标号、变量名称、寄存器名称、常数等表示操作数或地址码。这些英文单词或其缩写、标号、变量名称等都被称为汇编助记符。
-
汇编程序
用来将汇编语言源程序中的汇编指令翻译成机器指令的程序称为汇编程序.
-
反汇编程序
将机器指令反过来翻译成汇编指令的程序称为反汇编程序.
-
机器级代码
用机器指令表示的机器语言程序和用汇编指令表示的汇编语言程序统称为机器级程序
-
CISC
随着VLSI技术的迅速发展,计算机硬件成本不断下降,软件成本不断上升。为此,人们在设计指令系统时增加了越来越多功能强大的复杂指令,以使指令的功能接近高级语言语句的功能,给软件提供较好的支持。这类计算机称为复杂指令集计算机(Complex Instruction Set Coputer)
-
RISC
RISC的着眼点不是简单的放在简化指令系统上,而是通过简化指令使计算机结构更加简单合理,从而提高机器的性能。与CISC相比,RISC指令系统的主要特点如下:
- 指令数目少
- 指令格式规整,采用定长指令字方式,操作码和操作数地址等字段的长度固定
- 只有Load/Store指令中的数据需要访存,这种称为Load/Store型指令风格
- 采用大量通用寄存器
-
通用寄存器
通用寄存器是指没有专门用途的可以存放各类定点操作数的寄存器.
包含:EAX,EBX,ECX,EDX,ESP,EBP,ESI,EDI
-
变址寄存器
用来存放变址值的寄存器,包含源变址寄存器ESI,目的变址寄存器EDI
-
基址寄存器EBP
在变址寻址方式中用来提供基地址的寄存器,与变址寄存器共同形成最终地址
-
栈指针寄存器ESP
保存程序运行进的栈地址的寄存器
-
指令指针寄存器EIP
与程序计数器PC功能完成一样,用于存放将要执行的下一条指令的地址。
-
标志寄存器EFLAGS
主要用来记录机器的状态和信息
-
条件标志(条件码)
Condition Code,CC.用来存放运行的状态信息,由硬件自动设定,条件标志有时也称为条件码.
-
寻址方式
根据指令给定信息得到操作数或操作数地址的方式称为寻址方式.
-
立即寻址
立即寻址指指令中直接给出操作数
-
寄存器寻址
寄存器寻址指指令中给出操作数所存放的寄存器的编号.
-
相对寻址
以当前程序计数器pc的内容为基址,加上指令给出的一字节补码数(偏移量)形成新的pc值的寻址方式称为相对寻址
-
存储器操作数
除了立即寻址和寄存器寻址外,其他寻址方式下的操作数都在存储单元中,称为存储器操作数.
-
实地址模式
实地址模式是为与8086/8088兼容而设置的,在加电或复位时处于这一模式.此模式下的存储管理,中断控制以及应用程序运行环境都与8086/8088相同.其最大寻址空间为1MB,32条地址线中的A31~A20不起作用,存储管理采用分段方式,每段的最大地址空间为64KB,物理地址由段地址乘以16加上偏移地址构成,其中段地址位于段寄存器中,偏移地址用来指定段内一个存储单元。
-
保护模式
保护模式的引入是为了实现在多任务方式下对不同任务使用的虚拟存储空间进行完全的隔离,以保证不同任务之间不会相互破坏各自的代码和数据.保护模式是80286以上高档微处理器最常用的工作模式.系统启动后总是先进入实地址模式,对系统进行初始化,然后转入保护模式进行操作.在保护模式下,处理器釆用虚拟存储器管理方式.
-
有效地址
根据段寄存器的内容能够确定操作数据在的段在某个存储空间的起始地址,而有效地址则给出了操作数在所在段的段内偏移地址.
-
比例变址
有效地址由指令中给出的寻址方式来确定如何计算.有比例变址和非比例变址两种变址方式.比例变址时,变址值等于变址寄存器内容乘以比例系数S(也称为比例因子),S的含义为操作数的字节个数,在IA-32中,S的值可以是1、2、、4、8.
-
非比例变址
非比例变址相当于比例系数为1的比例变情况,即变址值就是变址寄存器的内容,无须乘以比例系数.
-
比例系数(比例因子)
比例变址时,变址值等于变址寄存器内容乘以比例系数S(也称为比例因子),S的含义为操作数的字节个数,在IA-32中,S的值可以是1、2、、4、8.
-
MMX指令
MMX是MultiMedia eXtensions(多媒体扩展)的缩写.MMX指令使用8个64位寄存器MM0~MM7借用了x87 FPU中8个80位浮点数据寄存器ST(0)~ST(7),它特地为视频信号、音频信号以及图像处理而设计的57条指令,提高多媒体处理能力。
-
SSE指令集
SSE指令集由MMX指令集发展而来,由于MMX指令集并没有带来3D游戏性能的显著提升,推出了SSE指令集,后续双陆续推出了SSE2,SSE3,SSE4等釆用SIMD技术的指令集,这些统称为SSE指令集。SSE指令集兼容MMX指令,并通过SIMD技术在单个时钟周期内并行处理多个浮点数来有效提高浮点运算速度。
-
SIMD
单指令多数据(Sigle Instruction Multi Data)技术,可实现单条指令同时并行处理多个数据的元素的功能,其操作数来源于专门新增的8个128位寄存器XMM0~XMM7.SSE架构中使用这一技术,其它地方也有使用这一技术,寄存器不一定使用这8个寄存器,这8个只用在SSE架构中。
-
多媒体扩展通用寄存器
因为在MMX技术中借用了x87 FPU的8个浮点寄存器,导致x87浮点运算速度降低,因而SSE指令新增了8个128位的SSE指令专用的多媒休扩展通用寄存器XMM0~XMM7。它的寄存器位数是MMX指令的两倍,因而一条SSE指令可以同时并行处理16个字节,或8个字,或4个双字或两个4字数据。
-
栈(stack)
栈是一种采用先进后出方式进行访问的一块存储区,在处理过程调用时非常有用.大多数情况下栈是从高地址向低地址增长的,在IA-32中,用ESP寄存器指向当前栈顶。
-
调用者保存寄存器
i386 System V ABI规范规定,寄存器EAX,ECX,EDX是调用者保存寄存器(Caller Saved Register).当过程P调用过程Q时,Q可以直接使用这三个寄存器,不用将它们的值保存到栈中、这也意味着,如果P在从Q返回后还要用这三个寄存器中的值,P应在转到Q之前先保存它们的值,并在从Q返回后先恢复它们的值再使用。
-
被调用者保存寄存器
寄存器EBX,ESI,EDI是被调用者保存寄存器(Callee Saved Registor),Q必须先将它们的值保存到栈中再使用它们,并在返回P之前先恢复它们的值。
-
帧指针寄存器
每个过程都有自己的栈区,称为栈帧(Stack Frame),因此,一个栈由若干栈帧组成,每个栈帧用专门的帧指针寄存器EBP指定起始位置。
-
当前栈帧
当前栈帧指当前运行过程的栈帧,它的范围在帧指帧EBP和栈指针ESP指向区域之间。过程执行时,由于不断有数据入栈,所以栈指针会动态移动,而帧指针则固定不变。
-
按值传递参数
当形参是基本类型变量名时,采用按值传递方式.
-
按地址传递参数
当形参是指针类型变量名或构造类型变量名时,采用按地址传递方式.
-
嵌套调用
嵌套调用指的是在一个函数的执行过程中,又调用了其他函数,而这些被调用的函数中可能还包含了更多的函数调用,形成了多层嵌套的调用关系。简单来说,就是在一个函数内部调用其他函数,而这些被调用的函数可能也会调用其他函数,以此类推。
-
递归调用
递归调用是指一个函数在其定义中直接或间接地调用自身的过程。在计算机科学中,递归是一种解决问题的方法,其中问题被分解成更小的相似子问题,直到达到某个基本条件,然后逐层返回结果,最终解决原始问题。
2. 简单回答下列问题
-
一条机器指令通常由哪些字段组成?各字段的含义分别是什么?
前缀类型 指令前缀 段前缀 操作数长度 地址长度 字节数 0或1 0或1 0或1 0或1 指令段 OP ModR/M SIB 位移 立即数 字节数 1、2、3 0或1 0或1 0、1、2、4 0、1、2、4 包含前缀和指令本身的代码部分。
前缀部分最多占4B,有4种前缀类型,每个前缀占1B.
- 指令前缀包括加锁(LOCK)和重复执行(REP/REPE/REPZ/REPNE/REPNZ)
- 段前缀用于指定指令所使用的非默认段寄存器
- 操作数长度前缀,指定非默认的操作数长度
- 地址长度前缀,指定非默认的地址长度
指令本身最多有5个字段:
- 主操作码(OP):指令的操作码,长度1-3B
- ModR/M :可再分成Mod、Reg/OP和R/M三个字段。Reg/OP可能是3位扩展操作码,也可能是寄存器编号,用来表示某一个操作数地址;Mod和R/M共5位,表示另一个操作数的寻址方式。
- SIB:是否有此字段由Mod和R/M确定,存在时寻址方式由SIB确定。SIB字节有比例因子SS、变址寄存器Index和基址寄存器Base构成。
- 位移:如果寻址方式中有位移量,由位移量字段给出
- 立即数:用于给出指令中的一个源操作数
-
将一个高级语言程序转换成计算机能直接执行的机器代码通常需要哪几个步骤?
这个转换过程分为以下4个步骤:
- 预处理,如头文件的插入或宏替换
- 编译,将预处理后的源程序文件编译生成相应的汇编语言程序.
- 汇编,由汇编程序将汇编语言源程序文件转换为可重定位的机器语言目标代码文件.
- 链接,由链接器将多个可重定位的机器语言目标文件以及库例程链接起来,生成最终可执行文件.
-
IA-32中的逻辑运算指令如何生成条件标志?移位指令可能会改变哪些条件标志?
5类逻辑运算指令:NOT,AND,OR,XOR,TEST. OF=CF=0,而ZF和SF根据运算结果来设置:若结果全为0,则ZF=1;若最高位为1,则SF=1.
移位过程中,把CF看作扩展位,用它接收从操作数最左或最右移出的一个二进制位。执行SAL(算术左移)时,如果移位前后符号位发生变化,则OF=1,表示左移后结果溢出.移位指令只影响这两个标志位
-
执行条件跳转指令时所用到的条件标志信息从何而来?请举例说明.
每条加/减指令和比较指令执行以后,会根据运算结果产生相应的进/借位标志CF、符号标志SF、溢出标志OF、零标志ZF等,并保存到标志寄存器(FLAGS/EFLAGS)中.
对于无符号整数的情况,判断大小时使用的是CF和ZF标志。ZF=1,说明两数相等,CF=1说明有借位,是小于关系
-
无条件跳转指令和调用指令的相同点和不同点是什么?
无条件跳转指令JMP的执行结果就是直接跳转到目标地址处执行。调用指令CALL是一种无条件跳转指令,跳转方式与JMP指令类似。但它包含入栈操作与JMP不同,1.将返回地址入栈,2.跳转到指定地址处执行.因些CALL指令会修改栈指针ESP。
-
按值传递参数和按地址传递参数两种方式有哪些不同点?
传递内容不同,一个传递数值参数,一个传递地址参数。所占空间不同,按值传递参数较多时将使用较多寄存器,按地址传递则使用原来存存储空间不用再占用其它寄存器存储参数。影响范转不同,按值传递只会影响被调用者函数内数据,调用者数据不会受影响,按参数传递则数据变化会影响原来内容。
-
为什么在递归较深时递归调用的时间开销和空间开销都会较大?
每次递归调用过程中都会形成栈帧,递归调用越深,形成的栈帧就越多,栈帧所占空间越大。同时每个过程调用都包含准备阶段和结束阶段,每增加一次过程调用,就要增加许多与准备阶段和结束阶段的额外指令,这些额外的指令执行时间开销很大。
-
为什么数据在存储器中最好按地址对齐方式存放?
若一条指令要访问的数据不在地址对齐的范围内时,则需要多次访存,因而延长了指令的执行时间。因此在对齐方式下程序的执行效率更高。
3. 对于以下AT&T格式汇编指令,根据操作数的长度确定对应指令助记符中的长度后缀,并说明每个操作数的寻址方式。
-
mov 8(%ebp, %ebx, 4),%ax
指令应为
movw
,源操作数寻址方式为:基址加比例变址加位移,LA=(SR)+(B)+(I)*S+A,目的操作数寻址方式为:寄存器寻址 -
mov %al, 12(%ebp)
指令应为
movb
,源操作数寻址方式为:寄存器寻址,目的操作数寻址方式为:基址加位移 -
add ( , %ebx, 4), %ebx
指令应为
addl
,源操作数寻址方式为:比例变址加位移,目的操作数寻址方式为寄存器寻址 -
or (%ebx), %dh
指令应为
orb
,源操作数寻址方式为:基址寻址,目的操作数为寄存器寻址 -
push $0xF8
指令可为
pushw
或pushl
,根据想要使用位数据决定,寻址方式为立即数寻址 -
mov $0XFFF0, %eax
指令为
movl
,源操作数寻址方式为:立即数寻址,目的操作数寻址方式为:寄存器寻址 -
test %cx, %cx
指令为
testw
,操作数寻址方式为寄存器寻址 -
lea 8(%ebx, %esi), %eax
指令为
leal
,源操作数寻址方式为:基址加变址加位移寻址,目的操作数为寄存器寻址
4. 使用汇编器处理以下各行AT&T格式时都会产生错误,请说明每一行存在什么错误
-
movl 0xFF, (%eax)
mov指令操作数必须有一个是寄存器,0xFF表示内存地址,M(0xFF)→Reg
-
movb %ax, 12(%ebp)
指令后缀与操作数位数不一致,应为
movw
,操作数为16位宽度,或操作数改为%al
-
addl %ecx, $0xF0
目的操作数不能为立即数
-
orw $0xFFFF0, (%ebx)
源操作数宽度为32位,指令后缀应为l,
orl
,位运算指令最多只能有一个存储器操作数 -
addb $0xF8, (%dl)
目的操作数内存地址为32位,应使用
%edx
-
movl %bx, %eax
源操作数宽度与命令不匹配,应改为
%ebx
-
andl %esi, %esx
不存在
%esx
寄存器 -
movw 8(%ebp, , 4), %ax
不存在变址寄存器,不应有变址因子
5. 假设变量x和ptr的类型声明如下:
src_type x;
dst_type *ptr;
这里,src_type和dst_type是用typedef声明的数据类型。有以下一个C语言赋值语句:
*prt=(dst_type)x;
若x存储在寄存器EAX或AX或AL中,ptr
存储在寄存器EDX中,则对于表中给出的src_type
和dst_type的类型组合,写出实现上述赋值语句的机器级代码。要求用AT&T格式表示机器级代码。
src_type | dst_type | 机器级表示 |
---|---|---|
char | int | movsbl %al, (%edx) |
int | char | movb %eax, (%edx) |
int | unsigned | movl %eax, (%edx) |
short | int | movswl %ax, (%edx) |
unsigned char | unsigned | movzbl %al, (%edx) |
char | unsigned | movzbl %al, (%edx) |
int | int | movl %eax, (%edx) |
6. 假设某个C语言函数func的原型声明如下:
void func(int *xptr, int yptr, int *zptr);
函数func的过程体对应的机器级代码用AT&T汇编形式表示如下:
movl 8(%ebp), %eax
movl 12(%ebp), %ebx
movl 16(%ebp), %ecx
movl (%ebx), %edx
movl (%ecx), %esi
movl (%eax), %edi
movl %edi, (%ebx)
movl %edx, (%ecx)
movl %esi, (%eax)
回答下列问题或完成下列任务。
-
在过程体开始时三个入口参数对应实参所存放的存储单元地址是什么?(当前栈帧底部由帧指针寄存器EBP指示)
xptr:R[ebp]+16; yptr:R[ebp]+12, zptr: R[ebp]+8
-
根据上述机器级代码写出函数func的C语言代码。
void func(int *xptr, int yptr, int *zptr) { int temp1=*xptr,temp2=*yptr; //z->y->x->z *yptr = *zprt; *xptr = temp2; *zptr = temp1; }
7. 假设变量x和y分别存放在寄存器EAX和ECX中,给出以下每条指令执行后寄存器EDX中的结果
-
leal (%eax), %edx
R[edx] : x的值
-
leal 4(%eax, %ecx), %edx
R[edx] : x+y+4的值
-
leal (%eax, %ecx, 8), %edx
R[edx] : x+y*8的值
-
leal 0xc(%ecx, %eax, 2), %edx
R[edx] : y+x*2+12的值
-
leal ( , %eax, 2), %edx
R[edx] : x*2的值
-
leal ( %eax, %ecx), %edx
R[edx] : x+y的值
8. 假设以下地址以及寄存器中存放的机器数如下表所示。
地址 | 机器数 | 寄存器 | 机器数 |
---|---|---|---|
0x0804 9300 | 0xffff fff0 | EAX | 0x0804 9300 |
0x0804 9400 | 0x8000 0008 | EBX | 0x0000 0100 |
0x0804 9384 | 0x80f7 ff00 | ECX | 0x0000 0010 |
0x0804 9380 | 0x908f 12a8 | EDX | 0x0000 0080 |
分别说明执行以下指令后,哪些地址或寄存器中的内容会发生改变?改变后的内容是什么?条件标志OF、SF、ZF和CF会发生什么改变?
-
addl (%eax), %edx
edx寄存器内容会发生改变,内容变为:0x0000 0070,OF=1,SF=0,ZF=0,CF=1
-
subl (%eax, %ebx), %ecx
ecx寄存器内容会发生改变,内容变为:0x7FFF FFF8,OF=0,SF=0,ZF=0,CF=0
-
orw 4(%eax, %ecx, 8), %bx
EBX寄存器内容发生改变,内容变为:0x0000 0100,OF=0,SF=0,ZF=0,CF=0
-
testb $0x80, %dl
寄存器或存储器内容都不发生变化,OF=0,SF=1,ZF=0,CF=0
-
imull $32, (%eax, %edx), %ecx
ECX寄存器的内发生了变化,内容变为:0x11E2 5500,OF=CF=1,SF=0,ZF=0
-
mulw %bx
AX,DX寄存器内容发生了变化,AX内容为0x0000,DX内容为:0x0093,CF=OF=1,ZF=0,SF=0
-
decw %cx
CX寄存器内容发生变化,CX内容为:0x0F,CF=OV=ZF=SF=0
9. 假设函数operate的部分C代码如下:
int operate(int x, int y, int z, int k){
int v = ;
return v;
}
以下汇编代码用来实现第2行语句的功能,请写出每条汇编指令的注释,并根据以下汇编代码,填写operate函数缺失的部分。
movl 12(%ebp), %ecx #R[ecx]<- z,z的值放入ECX
sall $8, %ecx #R[ecx]<-(z<<8)
movl 8(%ebp), %eax #R[eax]<- k,k的值放入EAX
movl 20(%ebp), %edx #R[edx]<-x,x的值放入EDX
imull %edx, %eax #R[eax]<-x*k
movl 16(%ebp), %edx #R[edx]<-y,y的值放入EDX
andl $65520, %edx #R[edx]<-y&0xFFF0,y的值低4位清零
addl %ecx, %edx #R[edx]<-R[edx]+R[ecx],(y&0xFFF0)+(z<<8)
subl %edx, %eax #R[eax]<-R[edx]-R[eax],(y&0xFFF0)+(z<<8)-x*k
缺失部分代码为:(y&0xFFFF)+(z<<8)-x*k
10. 假设函数product的C语言代码如下,其中num_type是用typedef声明的数据类型.
void product(num_type *d, unsigned x, num_type y){
*d = x*y;
}
函数product的过程体对应的主要汇编代码如下:
movl 12(%ebp), %eax #R[eax]<-x,x的值放入EAX寄存器
movl 20(%ebp), %ecx #R[ecx]<-y的高32位,y的高32位放入ECX寄存器
imull %eax, %ecx #R[ecx]<-x*y的高32位
mull 16(%ebp) #R[eax}<-x*y的低32位
leal (%ecx, %edx), %edx #R[edx]<-R[ecx]+R[edx],ECX的值放入EDX
movl 8(%ebp), %ecx #R[ecx]<-d,d的值放入ECX
movl %eax, (%ecx) #M[R[ecx]]<-R[eax],x与y的低32位乘积放入d指向地址的低32位
movl %edx, 4(%ecx) #M[R[edx]]<-R[edx],x与y的高32位乘积放入d指向地址的高32位
请给出上述每条汇编指令的注释,并说明num_type是什么类型.
根据栈分配大小和和使用的乘积命令得到num_type是long long int型数据类型.