美文网首页
学堂在线MOOC《汇编语言程序设计》题解

学堂在线MOOC《汇编语言程序设计》题解

作者: 张慕晖 | 来源:发表于2021-11-13 17:00 被阅读0次

    MOOC地址:

    汇编语言程序设计 - 清华大学 - 学堂在线

    1 浮点数的机器表示

    1.1 填空题 浮点数

    单精度浮点数的exp域的位宽是()位?

    答案:8

    1.2 填空题 浮点数

    单精度浮点数的frac域位宽是()位?

    答案:23

    1.3 判断题

    已知

    int x = ...;
    float f = ...;
    double d = ...;
    

    df都不是NaN,判断以下关系式是否成立?

    x == (int)(float) x
    

    答案:×

    解析:float的frac域宽度不够,可能会有精度损失。

    1.4 判断题

    已知

    int x = ...;
    

    判断以下关系式是否成立?

    x == (int)(double) x
    

    答案:√

    解析:double的frac域足够放下int,不会有精度损失。

    1.5 判断题

    已知

    float f = ...;
    

    f不是NaN,判断以下关系式是否成立?

    f == (float)(double) f
    

    答案:√

    解析:float转换成double不会有精度损失。

    1.6 判断题

    已知

    double d = ...;
    

    d不是NaN,判断以下关系式是否成立?

    d == (float) d
    

    答案:×

    解析:double转换成float可能会有精度损失。

    1.7 判断题

    已知

    float f = ...;
    

    f不是NaN,判断以下关系式是否成立?

    f == -(-f)
    

    答案:√

    解析:浮点数取负只改变符号位,不会溢出。

    1.8 判断题

    判断以下关系式是否成立?

    2/3 == 2/3.0
    

    答案:×

    解析:2/3的结果是整数,但2/3.0的结果是浮点数。

    1.9 判断题

    已知

    double d = ...;
    

    d不是NaN,判断以下关系式是否成立?

    (d < 0.0) == ((d*2) < 0.0)
    

    答案:√

    解析:浮点数的下溢是逐步下溢,不像整数会从负数变成正数。

    1.10 判断题

    已知

    float f = ...;
    double d = ...;
    

    df都不是NaN,判断以下关系式是否成立?

    (d > f) == (-f > -d)
    

    答案:√

    解析:浮点数取负不会溢出。

    1.11 判断题

    已知

    float f = ...;
    double d = ...;
    

    df都不是NaN,判断以下关系式是否成立?

    (d + f) - d == f
    

    答案:×

    解析:d+f可能会溢出,损失精度。

    2 80x80汇编与C语言-2

    2.1 填空题

    已知寄存器edx内的数值为0x8000ecx内的则为0x10;请给出地址表达式0x4(%edx, %ecx, 4)所表示的地址值。

    答案:0x8044

    解析:D(Rb, Ri, S) = Rb + S*Ri + D

    2.2 填空题

    x86-64体系结构具有()个通用寄存器,而x86-32只有()个。

    答案:

    • 16
    • 8

    2.3 判断题

    leal (%edx, %eax), %ecx这条指令在执行过程中需要访问内存数据。

    答案:×

    解析:leal指令只计算地址,不访存。因此leal指令也可以用于整数运算。

    2.4 单选题

    请问哪个条件码可用于检测无符号整数运算的溢出?

    • CF
    • SF
    • ZF
    • OF

    答案:CF

    解析:

    • CF(Carry Flag),无符号整数运算溢出时置位
    • SF(Sign Flag),计算结果<0时置位
    • ZF(Zero Flag),计算结果=0时置位
    • OF(Overflow Flag),带符号整数运算溢出时置位。

    2.5 单选题

    setasetb指令适用于无符号还是带符号整数的条件码读取?

    • 无符号
    • 带符号

    答案:无符号

    解析:setasetb适用于无符号整数;其中a、b分别指above和below。带符号整数对应的是setgsetl(g和l分别指greater和less)。

    2.6 填空题

    请补充与下图中C语言对应的汇编代码中的遗漏部分(x86-32结构下编译生成)。

    /* C */
    int absdiff(int x, int y)
    {
        int result;
        if (x > y) {
            result = x - y;
        } else {
            result = y - x;
        }
        return result;
    }
    
    # asm
    pushl %ebp
    movl %esp, %ebp
    pushl %ebx
    movl 8(%ebp), %ecx
    movl 12(%ebp), %edx
    movl %ecx, %ebx
    subl %edx, %ebx
    movl %edx, %eax
    subl %ecx, %eax
    cmpl %edx, %ecx
    ____ %ebx, %eax
    popl %ebx
    popl %ebp
    ret
    

    答案:cmovg

    解析:将汇编代码“翻译”如下:

    ecx := x
    edx := y
    ebx := x
    ebx := x - y
    eax := y
    eax := y - x
    Compare x to y
        if (?) (eax := x - y)
    return eax
    

    由分析可知,此处应该填写条件移动指令cmovg(若x>y则传送)。

    3 80x80汇编与C语言-3

    3.1 填空题

    下图给出了一个C函数,并由gcc编译成相应的汇编代码(AT&T语法格式),请补全这段代码里头被省去的部分。(X86-32结构)

    /* C */
    int arith(int x, int y, int z)
    {
        int t1 = x * y;
        int t2 = z - t1;
        int t3 = x + 40;
        int t4 = y * 2;
        int t5 = t3 >> t4;
        int rval = t2 * t5;
        return rval;
    }
    
    # asm
    pushl %ebp
    movl ____, %ebp
    movl ____(%ebp), %edx
    movl ____(%ebp), %ecx
    pushl %ebx
    movl 16(%ebp), %eax
    movl %edx, %ebx
    addl $40, %edx
    imull ____, %ebx
    addl %ecx, ____
    sarl %cl, %edx
    subl %ebx, %eax
    imull ____, %eax
    popl ____
    popl %ebp
    ret
    

    答案:

    • %esp
    • 8
    • 12
    • %ecx
    • %ecx
    • %edx
    • %ebx

    解析:

    以下是填空完成后的完整汇编代码。推理过程略。

    asm explanation
    pushl %ebp setup, save old ebp
    movl **%esp**, %ebp setup
    movl **8**(%ebp), %edx edx = x
    movl **12**(%ebp), %ecx ecx = y
    pushl %ebx save old ebx
    movl 16(%ebp), %eax eax = z
    movl %edx, %ebx ebx = edx = x
    addl $40, %edx edx += 40 (edx = x + 40 = t3)
    imull **%ecx**, %ebx ebx *= y (ebx = x * y = t1)
    addl %ecx, **%ecx** ecx += y (ecx = 2*y = t4)
    sarl %cl, %edx edx >>= cl (cl = t4, edx = t5)
    subl %ebx, %eax eax -= ebx (eax = z - t1 = t2)
    imull **%edx**, %eax eax *= edx (eax = t2 * t5 = rval)
    popl **%ebx** reset ebx
    popl %ebp reset ebp
    ret return eax (rval)

    3.2 填空题

    在X86-32位体系结构中,当前运行函数的帧(Frame)基址寄存器所指向的栈地址的“上方”(高地址)由低到高存放的是函数返回地址、( );“下方”存放的是( )、( )(此处无需考虑顺序)。

    答案:

    • 输入参数
    • 局部变量
    • 临时存储(存在其他正确填法,如“保存的寄存器值”,此处只列出标准答案)

    4 80x80汇编与C语言-4

    4.1 填空题

    请按顺序填写图左侧汇编代码对应的C代码(e.g. 右3)

    # asm
    foo1:
        pushl %ebp
        movl %esp, %ebp
        movl 8(%ebp), %eax
        sall %$4, %eax
        subl 8(%ebp), %eax
        movl %ebp, %esp
        popl %ebp
        ret
    
    foo2:
        pushl %ebp
        movl %esp, %ebp
        movl 8(%ebp), %eax
        testl %eax, %eax
        jge .L4
        addl $15, %eax
    .L4:
        shrl $4, %eax
        movl %ebp, %esp
        popl %ebp
        ret
    
    foo3:
        pushl %ebp
        movl %esp, %ebp
        movl 8(%ebp), %eax
        shrl $31, %eax
        movl %ebp, %esp
        popl %ebp
        ret
    
    /* C */
    int choice1(int x)
    {
        return (x < 0);
    }
    
    int choice2(int x)
    {
        return (x << 31) & 1;
    }
    
    int choice3(int x)
    {
        return 15 * x;
    }
    
    int choice4(int x)
    {
        return (x + 15) / 4;
    }
    
    int choice5(int x)
    {
        return x / 16;
    }
    
    int choice6(int x)
    {
        return (x >> 31);
    }
    
    

    答案:

    • 右3
    • 右5
    • 右1

    解析:值得注意的是右5和对应的汇编代码。由于算术右移指令对应的除法的取整模式是向下取整,而C语言要求的除法的取整模式是向0取整,因此负数除法的取整会出问题,不能直接右移。正确的计算公式是:

    • a ≥ 0时,a / 2^b = a >> b
    • a < 0时,a / 2^b = (a + 2^b − 1) >> b(此处除16相当于b=4,因此2^b - 1 = 15

    这就解释了汇编代码中testl %eax, %eaxjge .L4的意义。testl的功能是,取后面两个操作数的与,根据运算结果置条件码。当%eax >= 0时,运算结果仍为%eax,SF为0,OF也为0;当%eax < 0时,SF为1,OF为0。当条件码满足SF=OF时,jge .L4会跳转到.L4直接右移(不是负数,不用加上15);否则先加上15再右移。

    4.2 填空题

    已知三个二维矩阵的定义如下,并已初始化。

    /* C */
    #define n 10
    int a[n][n] ;
    int b[n][n] ;
    int c[n][n] ;
    

    需要进行矩阵乘,即矩阵a x b结果置于c。下面这段C代码是一个矩阵乘函数。

    /* C */
    void column()
    {
        int j, k, i;
        int r;
        for (j=0; j<n; j++)
        {
            for (k=0; k<n; k++) {
                r = b[k][j];
                for (i=0; i<n; i++)
                    c[i][j] += a[i][k] * r;
             }
         }
    }
    

    下面是相应的汇编代码。请填空。

    L11:
        movl -16(%ebp), %edx
        leal (%ecx, %edx), %eax
        leal (%ecx, %edi), %edx
        movl a_start_addr(, ____, 4), %eax
        addl $10, %ecx
        imull ____, %edx
        addl %edx, c_start_addr(, %eax, 4)
        decl %ebx
        jns L11
        addl $40, -20(%ebp)
        incl %edi
        cmpl ____, %edi
        jle L12
        incl -16(%ebp)
        cmpl ____, -16(%ebp)
        jle L13
        addl ____, %esp
        popl %ebx
        popl %esi
        popl %edi
        popl %ebp
        ret
    
    _matrix:
        pushl %ebp
        movl ____, %ebp
        pushl %edi
        pushl %esi
        pushl %ebx
        subl $8, %esp
        movl $0, -16(____)
    
    L13:
        movl -16(%ebp), %eax
        xorl %edi, ____
        leal b_start_addr(, ____, 4), %eax
        movl ____, -20(%ebp)
    
    L12:
        movl ____(%ebp), %edx
        xorl %ecx, %ecx
        movl $9, %ebx
        movl (%edx), %esi
    

    答案:

    • %esp
    • %ebp
    • %edi
    • %eax
    • %eax
    • -20
    • %edx
    • %esi
    • $9
    • $9
    • $8

    解析:

    以下是填空完成后的完整汇编代码。推理过程略。

    asm explanation
    _matrix:
    pushl %ebp setup, save old ebp
    movl %esp, %ebp setup
    pushl %edi save edi
    pushl %esi save esi
    pushl %ebx save ebx
    subl $8, %esp esp -= 8
    movl $0, -16(%ebp) j = 0 (-16(%ebp) = j)
    L13: the start of j loop
    movl -16(%ebp), %eax eax = j
    xorl %edi, %edi edi = 0 (edi = k)
    leal b_start_addr(, %eax, 4), %eax eax = &b[4*j]
    movl %eax, -20(%ebp) -20(%ebp) = &b[4*j + (40*k)]
    L12: the start of k loop
    movl -20(%ebp), %edx edx = &b[4*j + (40*k)]
    xorl %ecx, %ecx ecx = 0 (ecx = i * 10)
    movl $9, %ebx ebx = 9 // as "i"
    movl (%edx), %esi esi = b[k][j], or r = b[k][j] (esi = r)
    L11: the start of i loop
    movl -16(%ebp), %edx edx = j
    leal (%ecx, %edx), %eax eax = ecx + edx = 10 * i + j
    leal (%ecx, %edi), %edx edx = ecx + edi = 10 * i + k
    movl a_start_addr(,%edx, 4), %edx edx = a[4*edx] (edx = a[i][k])
    addl $10, %ecx ecx += 10 (ecx = i * 10)
    imull %esi, %edx edx *= r (edx = a[i][k] * r)
    addl %edx, c_start_addr(, %eax, 4) c[4*eax] += edx (c[i][j] += a[i][k] * r)
    decl %ebx ebx-- // as "i"
    jns L11 if (ebx > 0) goto L11 (the end of i loop)
    addl $40, -20(%ebp) -20(%ebp) += 40 (-20(%ebp) = 40\\*k+4*j)
    incl %edi edi++ (k++)
    cmpl $9, %edi compare k : 9
    jle L12 if (k <= 9) goto L12 (the end of k loop)
    incl -16(%ebp) -16(%ebp)++ (-16(%ebp)=j) (j++)
    cmpl $9, -16(%ebp) compare j : 9
    jle L13 if (j <= 9) goto L13 (the end of j loop)
    addl $8, %esp esp += 8
    popl %ebx finish, reset ebx
    popl %esi finish, reset esi
    popl %edi finish, reset edi
    popl %ebp finish, reset ebp
    ret return

    5 80x80汇编编程-1

    5.1 填空题

    在X86-32位编程中有一种简单的获得所运行的指令地址的方法(X86-32位结构下eip寄存器是无法直接访问的)。比如说我们要获得下面程序中XXXX这条指令的地址并置于eax寄存器中,那么可以采用如下代码段。请补充完函数GetAddress的第一条语句(AT&T语法)。

    Getaddress:
        movl ____, ____
        ret
    
    call GetAddress
    xxxx
    

    答案:

    • (%esp)
    • %eax

    解析:在call之后,GetAddress的返回地址,也就是xxxx的地址被压栈。此时,只需将栈顶所指向位置的内容((%esp),注意括号代表访存)存入eax中再返回。

    平台上第二空给的答案是$eax,是错的。我18年上这个课的时候就错了,当时向老师反映来着。现在还没改。

    5.2 填空题

    已知一个C语言结构类型的定义如下图所示,请问在X86 32位Linux系统下变量p所占的空间大小是____字节,对齐的要求为____字节对齐?

    struct S1 {
        char c;
        int i[2];
        my_struct v;
    } p;
    
    typedef TagStruct {
        int k[2];
        char c2;
    } my_struct;
    

    答案:

    • 24
    • 4

    解析:

    TagStruct的内存分布:

    | k[0] (4 bytes) | k[1] (4 bytes) | c2 (1 byte) | padding (3 bytes) |
    | -------------- | -------------- | ----------- | ----------------- |
    
    

    大小为12字节,4对齐。

    S1的内存分布:

    | c (1 byte) | padding(3 bytes) | i[0] (4 bytes) | i[1] (4 bytes) | v (12 bytes) |
    | ---------- | ---------------- | -------------- | -------------- | ------------ |
    
    

    大小为24字节,4对齐。

    6 80x80汇编编程-1

    6.1 填空题

    有如下的C代码以及对应的反汇编出来的汇编代码(x86-32体系结构):

    /* copy string x to buf */
    void foo(char* x) {
        int buf[1];
        strcpy((char *)buf, x);
    }
    
    void callfoo() {
        foo("abcdefghi");
    }
    
    080484f4 <foo>:
    080484f4: 55                     pushl %ebp
    080484f5: 89 e5                  movl %esp, %ebp
    080484f7: 83 ec 18               subl $0x18, $esp
    080484fa: 8b 45 08               movl 0x8(%ebp), %eax
    080484fd: 83 c4 f8               addl $0xfffffff8, %esp
    08048500: 50                     pushl %eax
    08048501: 8d 45 fc               leal 0xfffffffc(%ebp), %eax
    08048504: 50                     pushl %eax
    08048505: e8 ba fe ff ff         call 80483c4 <strcpy>
    0804850a: 89 ec                  movl %ebp, %esp
    0804850c: 5d                     popl %ebp
    0804850d: c3                     ret
    08048510 <callfoo>:
    08048510: 55                     pushl %ebp
    08048511: 89 e5                  movl %esp, %ebp
    08048513: 83 ec 08               subl $0x8, %esp
    08048516: 83 c4 f4               addl $0xfffffff4, %esp
    08048519: 68 9c 85 04 08         pushl $0x804859c  # push string address
    0804851e: e8 d1 ff ff ff         call 80484f4 <foo>
    08048523: 89 ec                  movl %ebp, %esp
    08048525: 5d                     popl %ebp
    08048526: c3                     ret
    

    strcpy调用完成返回到foo过程时,buf[0]buf[1]buf[2]的值分别是多少?

    在执行0x0804850dret指令前(popl后),ebp的值是多少?上述ret指令执行后,eip的值是多少?用32位16进制表示,注意大小端。

    0x00000000 字符的十六进制转换表如下:

    Character Hex Value
    'a' 0x61
    'b' 0x62
    'c' 0x63
    'd' 0x64
    'e' 0x65
    'f' 0x66
    'g' 0x67
    'h' 0x68
    'i' 0x69
    '\0' 0x00

    答案:

    • 0x64636261
    • 0x68676665
    • 0x08040069
    • 0x68676665
    • 0x08040069

    解析:

    通过分析可以画出调用strcpy之前完整的栈(具体分析过程略)如下表(每个格子代表4个字节,address向下递减):

    内容 指向该位置的指针
    callfoo过程保存的%ebp
    empty
    empty
    empty
    empty
    empty
    string address (0x0804859c)
    foo过程的返回地址 (0x08048523)
    foo过程保存的%ebp %ebp
    buf
    empty
    empty
    empty
    empty
    empty
    empty
    empty
    string address (strcpy的第二个参数)
    %ebp - 4 (buf)

    可以看出,传递给strcpybuf指针就是%ebp-4,在复制了字符串"abcdefghi"之后,会发生溢出,破坏栈中保存的%ebp和返回地址。但现在的问题是,在考虑大小端之后,栈中的实际内容到底应该是什么样子的呢?

    由于X86的字节序为小端(“低对低,高对高”),可以画出从buf指针开始向上到string address位置中实际的保存内容(一个格子代表一个字节,地址从上往下递减):

    描述 内容
    string address (高) 08
    04
    85
    string address (低) 9c
    foo的返回地址 (高) 08
    04
    85
    foo的返回地址 (低) 23
    保存的%ebp (高) ??
    ??
    ??
    保存的%ebp (低) ??
    buf[0] (高) ??
    ??
    ??
    buf[0] (低) ??

    在向以buf开头的地址中写入字符串"abcdefghi\\0"(转换成16进制,就是0x61626364656667686900)时,由于char类型的大小只有一个字节,大小端对它来说是无所谓的,只要从低地址向高地址覆写就可以。于是,我们得到了修改过的栈帧:

    描述 内容
    string address (高) 08
    04
    85
    string address (低) 9c
    foo的返回地址 (buf[2]) (高) 08
    04
    85 00
    foo的返回地址 (buf[2]) (低) 23 69
    保存的%ebp (buf[1]) (高) ?? 68
    ?? 67
    ?? 66
    保存的%ebp (buf[1]) (低) ?? 65
    buf[0] (高) ?? 64
    ?? 63
    ?? 62
    buf[0] (低) ?? 61

    而寄存器会以小端模式来解释内存中的内容,因此可得,buf[0] = 0x64636261buf[1] = 0x68676665buf[2] = 0x08040069popl后得到的%ebp0x68676665,执行ret%eip的值(也就是要返回到什么地址)为0x08040069

    7 MIPS32指令集与编程

    7.1 填空题

    异常(exception)可以分类为 ____ 和____两类,其中系统调用属于____异常、时钟中断属于____异常、Page Fault是 ____异常、机器cold reset是 ____异常。

    答案:

    • 同步
    • 异步
    • 同步
    • 异步
    • 同步
    • 异步

    解析:同步异常一般是指令引起的,异步异常一般是硬件引起的。

    7.2 填空题

    位于某个跳转指令的Branch Delay Slot中的指令(这一slot中的指令地址为A)发生了异常,那么异常处理完成后,恢复执行的指令地址是 ____;如果该跳转指令是JAL,那么该跳转指令执行完成后31号寄存器的内容是 ____。

    答案:

    • A-4
    • A+4

    解析:精确异常处理要求,延迟槽中的指令如果发生异常,恢复执行的指令是跳转指令;函数返回到下一条指令。

    8 期末考试

    我当年学的时候还有一个期末考试,现在已经没有了,不过姑且还是把当时的题整理在这里。

    8.1 填空题

    X、Y的数据宽度均为16位,计算结果也用16进制表示)已知[X]补=0019H,[Y]补=FE6AH,则[X+Y]补=____,[X-Y]补=____。

    答案:

    • FE83H
    • 00AFH

    解析:显然,X是正数,Y是负数。

    [X+Y]补 = X补+Y补 = 0x0019 + 0xFE6A = 0xFE83
    −Y原 = (Y补 − 1)反 = 0x0096
    [X−Y]补 = X补 − Y原 = 0x0019 + 0x0096 = 0x00AF
    

    8.2 文字填空题

    在X86-32位体系结构中,C语言过程调用的默认传参规则是将过程参数从____至____压入栈,过程返回值(32位)通过____寄存器传出。

    答案:

    • %eax

    8.3 填空题

    给出13/8这一数字的32位浮点数(符合IEEE 754标准)表示,即exp= ____;frac= ____

    答案:

    • 01111111
    • 10100000000000000000000

    解析:

    13/8 = (1101)2/2^3 = (1.101)_2

    因此E=0exp = E+bias = 0+(2^(E_length−1)−1) = 0+(01111111)_2 = (01111111)_2

    省略掉有效数字开头的1,frac=10100000000000000000000。(后面一共20个0)

    8.4 填空题

    寄存器EAX,EBX内存储的为带符号32位整数,若%EAX >%EBX,则指令cmpl %EAX,%EBX执行后 SF=____,OF= ____。(若不确定,可以填“不确定”)

    答案:

    • 不确定
    • 不确定

    解析:cmpl指令根据%ebx - %eax的值设置条件码。因为补码加法可能溢出也可能不溢出,带符号数的减法结果可能溢出到正数也可能不溢出,所以两空均为不确定。

    8.5 填空题

    X86 32位linux系统下的float类型的数据对齐要求是____字节对齐,double类型的是____字节对齐;X86 32位Windows系统下的double类型数据是____字节对齐。

    答案

    • 4
    • 4
    • 8

    8.6 填空题

    lw $t6, 65536($sp)经过MIPS 32汇编器处理后,产生的代码如下,请补全。

    lui $1,____ 
    addu $1, $1,____ 
    lw $t6, 0($1)
    
    

    答案:

    • 1
    • $sp

    解析:lui将立即数装载到寄存器的高16位,低16位清零,而65536=(10000000000000000)_2,因此,执行lui指令后,$1=65536

    8.7 填空题

    li $6, 0x345678经过MIPS 32汇编器处理后,产生的代码如下,请补全

    lui $1, ____
    ____ $6, $1, ____
    
    

    答案:

    • 0x34
    • oriaddiu
    • 0x5678

    解析:lui的作用同上题;ori作用的是寄存器的低16位。

    相关文章

      网友评论

          本文标题:学堂在线MOOC《汇编语言程序设计》题解

          本文链接:https://www.haomeiwen.com/subject/aglzzltx.html