《深入理解计算机系统》 练习题3.2-3.5 汇编相关

汇编命令总结

1)%eax,%dx就是寄存器的名字
2)(%rax)。只要是有括号的了,那就是内存引用。注意x86-64中的内存引用总是用四字长寄存器给出的,即寄存器名字开头都是r开头的。(%rax)意思是取寄存器%rax中的存的地址中的存的值,相当于解引用两次,先解引用寄存器,再解引用这地址(我用“解引用”这个词只是为了方便理解)。
3)mov命令中,两个操作数只允许有一个内存引用,即只能有一个带括号的。
4)关于mov命令的后缀,肯定与内存引用中的寄存器的长度无关,而是与另一个寄存器的长度有关
5)b代表1个字节;w代表1个字,2个字节;l代表2个字,4个字节;q代表4个字,8个字节。
《深入理解计算机系统》 练习题3.2-3.5 汇编相关_第1张图片

3.2

补充以下mov命令的后缀:

原命令 讲解
mov %eax, (%rsp) %eax是2个字的,所以应该是movl
mov (%rax), %dx %dx是1个字的,所以应该是movw
mov $0xFF, %bl %bl是1个字节的,所以应该是movb
mov (%rsp,%rdx,4), %dl %dl是1个字节的,所以应该是movb
mov (%rdx), %rax %rax是4个字的,所以应该是movq
mov %dx, (%rax) %dx是1个字的,所以应该是movw

3.3

下面的每行汇编命令都是错的,讲出原因

原命令 讲解
movb $0xF, (%ebx) 内存引用的寄存器必须是四个字的,改成movb $0xF, (%rbx)
movl %rax, (%rsp) %rax是四个字而l代表两个字,改成movl %eax, (%rsp) 或者 movq %rax, (%rsp)
movw (%rax), 4(%rsp) 两个操作数不能都是内存引用
movb %al, %sl 没有寄存器名字叫%sl
movq %rax,$0x123 dest不能作为des操作数
movl %eax, %rdx 原答案为destination operand incorrect size,改成movl %eax, %edx
movb %si, 8(%rbp) %si是一个字而b代表一个字节,改成movb %sil, 8(%rbp) 或者 movw %si, 8(%rbp)

3.4

有以下的c语言程序:

src_t *sp;
dest_t *dp;
*dp = (dest_t) *sp;

假设转换成汇编语言后, sp 的值存在寄存 %rdi, dp 的值存在 %rsi。而第一个指令都是将sp的值先导入到 %rax寄存器的相应字节中的。
注意第一条指令就得选择好是零拓展还是符号拓展(当从小的到大的时,关心的是源操作数的有无符号),当从大到小时,就不需要选择了,毕竟直接截断就好了。
第一条指令,也得先拓展到目标类型的大小。
第二条指令,只需要关心目的类型的大小以此来决定后缀。

src_t dest_t 指令 讲解
long long movq (%rdi), %rax
movq %rax, (%rsi)
long认为8字节,所以用q;因为目标为long,所以第二条指令为q
char int movsbl (%rdi), %eax
movl %eax, (%rsi)
从小到大,因为源是有符号的,所以s;因为从char到int,所以是bl;
拓展好了直接第二个指令移过去就好了
char unsigned movsbl (%rdi),%eax
movl %eax,(%rsi)
和上面一样,但目标符号不同,但这里并不关心
unsigned char long movzbl (%rdi),%eax
movq %rax,(%rsi)
因为源无符号所以用z。但第一条指令的目的是2个字的
使用l和%eax。之所以不直接复制到4个字;
因为如果更新寄存器的低4字节,那么高4字节会自动置为0
int char movl (%rdi),%eax
movb %al,(%rsi)
从大到小,先直接全部读,然后再按照目标类型大小截断即可
insigned unsigned char movl (%rdi),%eax
movb %al,(%rsi)
和上面一样,源有无符号根本无所谓
char short movsbw (%rdi),%ax
movw %ax,(%rsi)
有符号;第一条命令先从b到w;

总结:从小到大或一样大,两条指令的寄存器名字相同(这里指不带括号的,因为带括号的叫内存引用),但除了上表第4行,原因不明。猜想原因可能是:“常规的movq指令只能以表示为32补码数字的立即数作为操作数,然后把这个值符号拓展得到64位的值,放到目的位置”。

3.5

将下面的汇编代码转成 C 语言,假设函数函数为 void decode1( long *xp, long *yp, long *zp);汇编代码为:

void decode1(1ong *xp, long *yp, long *zp);
xp in %rdi, yp in %rsi, zp in %rdx
decode1:
    movq (%rdi), %r8    --> %r8=*xp;
    movq (%rsi) , %rcx  --> %rcx=*yp
    movq (%rdx), %rax   --> %rax=*zp
    movq %r8, (%rsi)    --> *yp = *xp;
    movq %rcx, (%rdx)   --> *zp = *yp;
    movq %rax, (%rdi)   --> *xp = *zp;
    ret

简单来说就是,xp的值到yp里,yp的值到zp里,zp的值到xp里。书中标准答案如下:

void decode1(1ong *xp, long *yp, long *zp)
{
	long x = *xp;
	long y = *yp;
	long z = *zp;

	*yp = x;
	*zp = y;
	*xp = z;
}

简化后可写成:

long temp = *zp;
*zp = *yp;
*yp = *xp;
*xp = temp;
return temp;

3.8

原题不写了,其中第三条需要手算10进制转16进制。

def solve(num):
    dict = [str(i) for i in range(10)]
    dict += ['A','B','C','D','E','F']
    #虽然是个list,却是起dict的作用
    li = []
    result = num//16
    while result >=16:
        li.append(num%16)
        
        num = result
        result = num//16
    li.append(num%16)
    li.append(result)


    while li:
        print(dict[li.pop()],end='')

简单地说就是:
1)num除以16,有商和余数。余数是0-15的。
2)如果一个数除以16的商,直接是0-15的,那么先入栈余数,再入栈商。
3)如果一个数除以16的商,是>=16的,那么先入栈余数,然后将商除以16再来判断,如果是3的情况那么重复3,一直到2情况为止。

你可能感兴趣的:(CSAPP.3e)