CSAPP深入理解計算機系統(第二版)第三章家庭作業答案
阿新 • • 發佈:2018-11-27
《深入理解計算機系統(第二版)》CSAPP 第三章 家庭作業
這一章介紹了AT&T的彙編指令 比較重要 本人完成了《深入理解計算機系統(第二版)》(以下簡稱CSAPP)第三章的家庭作業,並與網上的一些答案進行了對比修正。
感謝博主summerhust的整理,以下貼出AT&T常用匯編指令
AT&T常用匯編指令
資料傳送指令
指令 | 效果 | 描述 |
---|---|---|
movl S,D | D <-- S | 傳雙字 |
movw S,D | D <-- S | 傳字 |
movb S,D | D <-- S | 傳位元組 |
movsbl S,D | D <-- 符號擴充套件S | 符號位填充(位元組->雙字) |
movzbl S,D | D <-- 零擴充套件S | 零填充(位元組->雙字) |
pushl S | R[%esp] <-- R[%esp] – 4;M[R[%esp]] <-- S | 壓棧 |
popl D | D <-- M[R[%esp]];R[%esp] <-- R[%esp] + 4; | 出棧 |
算數和邏輯操作地址:
指令 | 效果 | 描述 |
---|---|---|
leal S,D | D = &S | movl地版,S地址入D,D僅能是暫存器 |
incl D | D++ | 加1 |
decl D | D-- | 減1 |
negl D | D = -D | 取負 |
notl D | D = ~D | 取反 |
addl S,D | D = D + S | 加 |
subl S,D | D = D – S | 減 |
imull S,D | D = D*S | 乘 |
xorl S,D | D = D ^ S | 異或 |
orl S,D | D = D | S | 或 |
andl S,D | D = D & S | 與 |
sall k,D | D = D << k | 左移 |
shll k,D | D = D << k | 左移(同sall) |
sarl k,D | D = D >> k | 算數右移 |
shrl k,D | D = D >> k | 邏輯右移 |
特殊算術操作:
指令 | 效果 | 描述 |
---|---|---|
imull S | R[%edx]:R[%eax] = S * R[%eax] | 有符號64位乘 |
mull S | R[%edx]:R[%eax] = S * R[%eax] | 無符號64位乘 |
cltd S | R[%edx]:R[%eax] = 符號位擴充套件R[%eax] | 轉換為4位元組 |
idivl S | R[%edx] = R[%edx]:R[%eax] % S;R[%eax] = R[%edx]:R[%eax] / S; | 有符號除法,儲存餘數和商 |
divl S | R[%edx] = R[%edx]:R[%eax] % S;R[%eax] = R[%edx]:R[%eax] / S; | 無符號除法,儲存餘數和商 |
注:64位數通常儲存為,高32位放在edx,低32位放在eax。
條件碼:
條件碼暫存器描述了最近的算數或邏輯操作的屬性。
CF:進位標誌,最高位產生了進位,可用於檢查無符號數溢位。
OF:溢位標誌,二進位制補碼溢位——正溢位或負溢位。
ZF:零標誌,結果為0。
SF:符號標誌,操作結果為負。
比較指令:
指令 | 基於 | 描述 |
---|---|---|
cmpb S2,S1 | S1 – S2 | 比較位元組,差關係 |
testb S2,S1 | S1 & S2 | 測試位元組,與關係 |
cmpw S2,S1 | S1 – S2 | 比較字,差關係 |
testw S2,S1 | S1 & S2 | 測試字,與關係 |
cmpl S2,S1 | S1 – S2 | 比較雙字,差關係 |
testl S2,S1 | S1 & S2 | 測試雙字,與關係 |
訪問條件碼指令:
指令 | 同義名 | 效果 | 設定條件 |
---|---|---|---|
sete D | setz | D = ZF | 相等/零 |
setne D | setnz | D = ~ZF | 不等/非零 |
sets D | D = SF | 負數 | |
setns D | D = ~SF | 非負數 | |
setg D | setnle | D = ~(SF ^OF) & ZF | 大於(有符號>) |
setge D | setnl | D = ~(SF ^OF) | 小於等於(有符號>=) |
setl D | setnge | D = SF ^ OF | 小於(有符號<) |
setle D | setng | D = (SF ^ OF) | ZF | 小於等於(有符號<=) |
seta D | setnbe | D = ~CF & ~ZF | 超過(無符號>) |
setae D | setnb | D = ~CF | 超過或等於(無符號>=) |
setb D | setnae | D = CF | 低於(無符號<) |
setbe D | setna | D = CF | ZF | 低於或等於(無符號<=) |
跳轉指令:
指令 | 同義名 | 跳轉條件 | 描述 |
---|---|---|---|
jmp Label | 1 | 直接跳轉 | |
jmp *Operand | 1 | 間接跳轉 | |
je Label | jz | ZF | 等於/零 |
jne Label | jnz | ~ZF | 不等/非零 |
js Label | SF | 負數 | |
jnz Label | ~SF | 非負數 | |
jg Label | jnle | ~(SF^OF) & ~ZF | 大於(有符號>) |
jge Label | jnl | ~(SF ^ OF) | 大於等於(有符號>=) |
jl Label | jnge | SF ^ OF | 小於(有符號<) |
jle Label | jng | (SF ^ OF) | ZF | 小於等於(有符號<=) |
ja Label | jnbe | ~CF & ~ZF | 超過(無符號>) |
jae Label | jnb | ~CF | 超過或等於(無符號>=) |
jb Label | jnae | CF | 低於(無符號<) |
jbe Label | jna | CF | ZF | 低於或等於(無符號<=) |
轉移控制指令:(函式呼叫):
指令 | 描述 |
---|---|
call Label | 過程呼叫,返回地址入棧,跳轉到呼叫過程起始處,返回地址是call後面那條指令的地址 |
call *Operand | |
leave | 為返回準備好棧,為ret準備好棧,主要是彈出函式內的棧使用及%ebp |
家庭作業參考答案
3.54
x at %ebp+8, y at %ebp+12, z at %ebp+16, return value at %eax
int decode2(int x,int y,int z){
int temp1 = z-y;
int temp2 = temp1<<15;
temp2 = temp2>>15;
return (x^temp1)*temp2;
}
3.55
ll_t is defined as long long
void store_prod(ll_t *dest, ll_t x, int y){
*dest = x*y;
}
dest at %ebp+8, x at %ebp+12, y at %ebp+20
movl 12(%ebp),%esi ;get x(long long)的低位
movl 20(%ebp),%eax ;get y(int)
movl %eax,%edx ;備份y
sarl $31,%edx ;獲取y的符號位
movl %edx,%ecx ;備份y的符號位
imull %esi,%ecx ;x的低位無符號乘法乘以y的符號位(0或-1 即 全0或全1)
movl 16(%ebp),%ebx ;get x(long long)的高位
imull %eax,%ebx ;x的高位乘以y
addl %ebx,%ecx ;%ecx=x高位*y+x低位*y的符號位(補全下面無符號的缺少部分)
mull %esi ;%edx=(x的低位*y)的高位 %eax=(x的低位*y)的低位(這一步無符號)
leal (%ecx,%edx),%edx;%edx = %ecx+%edx
movl 8(%ebp),%ecx ;get dest
movl %eax,(%ecx) ;dest[0] = (x的低位*y)的低位
movl %edx,4(%ecx) ;dest[1] = (x的低位*y)的高位+x的高位*y+x的低位*y的符號位(0或者-1)
32位機器模擬64位機器的有符號乘法
x表示 long long x 的值
x = xh*2^32 + xl
x*y = y*xh*2^32 + y*xl 完整形式是96位長 但僅需要64位
因此我們需要y*xl的完整64位和y*xh的低32位 並分開儲存
3.56
一個知識點:位移操作可以是一個立即數或者是存放在單位元組暫存器元素%cl中
A.
x:%esi,n:%ebx,result:%edi,mask:%edx
B.
result=0x55555555; //0101 0101 0101 0101 0101 0101 0101 0101
mask=0x80000000;
C.
mask!=0
D.
mask每次迴圈右移n位
E.
result每次迴圈和x&mask的值進行異或
int loop(int x,int n){
int result = 0x55555555;
int mask;
for(mask = 0x80000000;mask!=0;mask=(unsigned)mask>>n){//需要注意邏輯右移 將有符號數轉化為無符號
result ^= mask&x;
}
return result;
}
3.57
int cread_alt(int *xp){
int temp = 0;
int *p = xp?xp:&temp;
return *p;
}
3.58
typedef enum {MODE_A, MODE_B, MODE_C, MODE_D, MODE_E} mode_t;
int switch3(int *p1, int *p2, mode_t action){
int result = 0;
switch(action){
case MODE_A:
result = *p1;
*p1 = *p2;
break;
case MODE_B:
result = *p1+*p2;
*p2 = result;
break;
case MODE_C:
*p2 = 15;
result = *p1;
case MODE_D:
*p2 = *p1;
case MODE_E:
result = 17;
break;
default:
result = -1;
break;
}
return result;
}
選一個部分的彙編程式碼註釋解釋一下
.L14(MODE_B):
movl 12(%ebp),%edx ;get p2 reuslt = p2
movl (%edx),%eax ;temp_p2 = p2
movl %eax,%edx ;result = *p2
movl 8(%ebp),%ecx ;get p1 temp_p1 = p1
addl (%ecx),%edx ;result += *p1此時result = *p1+*p2
movl 12(%ebp),%eax ;get p2
movl %edx,(%eax) ;*p2 = result
jmp .L19 ;
3.59
int swith_prob(int x, int n){
int result = x;
switch(n){
case 40:
case 42:
result <<= 3;
break;
case 43:
result >>= 3;
break;
case 44:
result <<= 3;
result -= x;
case 45:
result *= result;
case 41:
default:
retult += 17;
break;
}
return result;
}
解釋和部分彙編程式碼註釋
從gdb打印出來的內容可以看出40的情況和42是一樣的因此40內容為空,其後緊跟42
44跳轉至後緊接著執行了45跳轉的內容 45之後也無跳轉 因此44和45沒有break
41的情況與default相同因此41置為空寫在defaul前
mov 0xc(%ebp),%eax ;%ebp+12的位置獲取第二個引數,即n
sub 0x28,%eax ;n-40(0x28為16進位制 轉為10進製為2*16+8=40)
cmp 0x5,%eax ;n-40 與 5
ja 8048435(switch_pro+0x15);n-40-5>0? 即n若大於45直接返回
3.60
A.從彙編程式碼看出
A[i][j][k] = A+(63i+9j+k)*4
B.
解決方程T=9, S*T=63, R*S*T*4=2772
T = 9
S = 7
R = 11
3.61
int var_prod_ele(int n, int A[n][n], int B[n][n], int i,int k){
int result = 0;
int *a_l = &A[i][0];
int *a_r = &A[i][n];
int *b_l = &b[0][k];
while(a_l!=a_r){
result += (*a_l)*(*b_l);
b_l += n;
a_l++;
}
return result;
}
可自行檢視彙編程式碼驗證
L3:
movl (%ebx), %ecx
imull (%edx), %ecx
addl %ecx, %eax
addl %edi, %ebx
addl $4, %edx
cmpl %edx, %esi
jne L3
3.62
A.
M = 76/4 = 19
B.
%edi 儲存 i
%ecx 儲存 j
使用指標進行優化
void transpose(int A[M][M]){
int i,j;
for(i=0;i<M;i++){
int *temp1 = &A[i][0];
int *temp2 = &A[0][i];
for(j=0;j<i;j++){
int t = *temp1;
*temp1 = *temp2;
*temp2 = t;
temp1++;
temp2 += M;
}
}
}
3.63
#define E1(n) 3*n
#define E2(n) 2*n-1
3.64
A.
8(%ebp) result
12(%ebp) s1.p
16(%ebp) s1.v
B.
------------%ebp
s2.sum
s2.prod
s1.v
s1.p
&s2
------------%esp
C.
將結構體變數的各個成員的值傳入函式
D.
將返回變數的地址傳遞出去
3.65
A=3,B=7
3.66
這個題看了好久,不得不佩服GCC
寫下詳細註釋
push %ebp
mov %esp,%ebp
push %ebx
mov 0x8(%ebp),%eax ;get i
mov 0xc(%ebp),%ecx ;get *bp
imul $0x1c,%eax,%ebx ;28*i
lea 0x0(,%eax,8),%edx;8*i
sub %eax,%edx ;7*i
add 0x4(%ecx,%ebx,1),%edx ;7i+(bp+28i+4)注意bp+4是a_struct a的首地址 +28i即是bp->a[i]即一個a_struct大小是28 同時 有*ap = (bp+28i+4)
mov 0xc8(%ecx),%eax ;bp->right = bp+200
add (%ecx),%eax ;bp->left + bp->right 即 28*CNT = 200 - 4 即 CNT = 7
mov %eax,0x8(%ecx,%edx,4);bp+8+4*(7i+(bp+28i+4))=(bp+28*i+4+4)(即ap->idx+4,apx->x[0])+*(bp+0x1c*i+4)*4
pop %ebx
pop %ebp
ret
A.
CNT = 7
B.
a_struct{
int idx;
int x[6];
}
其實分析出大小就能做這個題 並不需要完全看懂彙編程式碼
3.67
A.
e1.p:0
e1.x:4
e2.y:0
e2.next:4
B.8
C.
void proc(union ele *up)
{
up->e2.next->e1.x=*(up->e2.next->e1.p) - up->e2.y;
}
3.68
void good_echo()
{
char c;
int x = 0;
while( x=getchar(), x!='\n' && x!=EOF)
{
putchar(x);
}
}
3.69
long trace(tree_ptr tp){
long result = 0;
while(tp){
result = tp->val;
tp = tp->left;
}
return result;
}
輸出二叉樹最左邊節點的值
3.70
long traverse(tree_ptr tp){
if(!tp) return 9223372036854775807;
long v = tp->val;
long left = traverse(tp->left);
long result = traverse(tp->right);
if(left <= result) result = left;
if(v <= result) result = v;
return result;
}
或者換一種寫法
long traverse(tree_ptr tp){
long result = 9223372036854775807;
if(tp){
long lv = traverse(tp->left);
long rv = traverse(tp->right);
result = lv <= rv ? lv : rv;
result = result > tp->val ? tp->val : result;
}
return result;
}
求二叉樹節點最小值
指令 | 效果 |
---|---|
cmovle s,r | 小於或等於 s->r |
cmovg s,r | 大於 s->r |
詳見資料傳送指令
參考文獻
《深入理解計算機系統(第二版)》