2019 操作系统 汇编代码指令寻址方式标志寄存器 解答题
第 45 题

已知f(n)=n!=n×(n−1)×(n−2)×⋯×2×1,计算f(n)的 C 语言函数 f1 的源程序(阴影部分)及其在 32 位计算机 M 上的部分机器级代码如下:

int f1(int n) {
1   00401000   55             push ebp
      ...           ...            ...
   if(n>1)
11  00401018  83 7D 08 01    cmp   dword ptr [ebp+8],1
12  0040101C  7E 17          jle   f1+35h (00401035)
   return n*f1(n-1);
13  0040101E  8B 45 08       mov   eax, dword ptr [ebp+8]
14  00401021  83 E8 01       sub   eax, 1
15  00401024  50             push  eax
16  00401025  E8 D6 FF FF FF call  f1 ( 00401000)
      ...           ...            ...
19  00401030  0F AF C1       imul  eax, ecx
20  00401033  EB 05          jmp   f1+3Ah (0040103a)
   else return 1;
}
21  00401035  B8 01 00 00 00 mov   eax,1
      ...           ...            ...
26  00401040  3B EC          cmp   ebp, esp
      ...           ...            ...
30  0040104A  C3             ret

其中,机器级代码行包括行号、虚拟地址、机器指令和汇编指令,计算机 M 按字节编址,int 型数据占 32 位。请回答下列问题:

(1) 计算 f(10) 需要调用函数 f1 多少次?执行哪条指令会递归调用 f1?

(2) 上述代码中,哪条指令是条件转移指令?哪几条指令一定会使程序跳转执行?

(3) 根据第 16 行的 call 指令,第 17 行指令的虚拟地址应是多少?已知第 16 行的 call 指令采用相对寻址方式,该指令中的偏移量应是多少(给出计算过程)?已知第 16 行的 call 指令的后 4 字节为偏移量,M 是采用大端方式还是采用小端方式?

(4) f(13)=6227020800,但 f1(13) 的返回值为 1932053504,为什么两者不相等?要使 f1(13) 能返回正确的结果,应如何修改 f1 的源程序?

(5) 第 19 行的 imul 指令(带符号整数乘)的功能是 R[eax]←R[eax]×R[ecx],当乘法器输出的高、低 32 位乘积之间满足什么条件时,溢出标志 OF=1?要使 CPU 在发生溢出时转异常处理,编译器应在 imul 指令后应加一条什么指令?

汇编代码 指令寻址方式 标志寄存器

[tag_link]

1)计算 f(10) 需要调用函数 f1 共 10 次,执行第 16 行的 call 指令会递归调用 f1。

2)第 12 行的 jle 指令是条件转移指令,其含义为小于等于时转移,本行代码的意义为:当 n≤1 时,跳转至地址 0040 1035H。第 16 行的 call 指令为函数调用指令,第 20 行的 jmp 指令为无条件转移指令,第 30 行的 ret 指令为子程序的返回指令,这三条指令一定会使程序跳转执行。

3)其长度计算机 M 上按字节编址,第 16 行的 call 指令的虚拟地址为 0040 1025H,长度为 5 字节,故第 17 行的指令的虚拟地址为 0040 1025H + 5 = 0040 102AH。第 16 行的 call 指令采用相对寻址方式,即目标地址 = (PC) +偏移量,call 指令的目标地址为 0040 1000H,所以偏移量 = 目标地址 - (PC) = 0040 1000H - 0040 102AH = FFFF FFD6H。根据第 16 行的 call 指令的偏移量字段为 D6 FF FF FF,可以确定 M 采用小端方式。

4)因为 f(13) = 6227020800,其结果超出了 32 位 int 型数据可表示的最大范围,因此 f(13) 的返回值是一个发生了溢出的错误结果。为使 f1(13) 能返回正确结果,可将函数 f1 的返回值类型改为 double(或 long long,或 long double,或 float)类型。

5)若乘积的高 33 位为非全 0 或非全 1,则 OF=1。编译器应在 imul 指令后加一条“溢出自陷指令”,使得 CPU 自动查询溢出标志 OF,当 OF=1 时调出“溢出异常处理程序”。