x86彙編語言實踐(4)

0 寫在前面

  爲了更深刻的瞭解程序的實現原理,近期我學習了IBM-PC相關原理,並手工編寫了一些x86彙編程序。html

  在2017年的計算機組成原理中,曾對MIPS體系結構及其彙編語言有過必定的瞭解,考慮到x86體系結構在目前的普遍應用,我經過兩個月左右的時間對x86的相關內容進行了學習。git

  在《x86彙編語言實踐》系列中(包括本篇、x86彙編語言實踐(1)x86彙編語言實踐(2)x86彙編語言實踐(3)以及x86彙編語言複習筆記),我經過幾個具體案例對x86彙編語言進行實踐操做,並記錄了本身再編寫彙編代碼中遇到的困難和心得體會,與各位學習x86彙編的朋友共同分享。github

  我將我編寫的一些彙編代碼放到了github上,感興趣的朋友能夠點擊屏幕左上角的小貓咪進入個人github,或請點擊這裏下載源代碼。學習

1 程序設計複習1

1-1 練習要點

  • 字符串中查找指定字符url

  • 字符串中刪除指定字符(使用快慢指針spa

  • 子程序調用的堆棧參數傳遞.net

1-2 實現思路

  • 在數據段存儲好待查找的CHAR,和目標字符串STR1,並將兩者初始化設計

  • 主程序中首先將CHAR和STR1壓棧3d

  • 調用FIND_CH子程序查找是否有CHAR指針

  • 若找到CHAR則調用DELX刪除STR1中的X

  • 爲了在STR1的原內存空間上操做字符串的修改動做,採用快慢指針的方式進行刪除。

1-3 重點難點

  • 參數傳遞:使用堆棧進行參數傳遞,須要將參數壓棧,注意子程序返回時,必須增長一個常數偏移量RET X。這裏的X爲壓入參數所佔的字節數,一般爲2的倍數,以保證堆棧平衡

  • 子程序保存現場:在子程序中,每每要用到不少寄存器,但咱們但願在子程序返回時,調用子程序位置處周圍的變量仍能恢復,這就須要在調用的子程序中保存現場,即子程序中所用到或修改的全部寄存器,都必須壓棧處理

  • 子程序中的堆棧尋址:使用BP寄存器尋址,這是爲了避免修改SP指針,避免弄亂堆棧棧頂指針SP

  • 快慢指針:與高級語言程序設計中的思路相似,首先將快慢指針指向STR1的頭部,以後循環STR1的長度LEN次,若快指針SI指向的位置的字符不爲CHAR,則將SI複製到慢指針DI,不然只將SI++。這裏用到的技巧是可使用LODSB和STOSB自動實現快指針SI與慢指針DI的自增操做

1-4 代碼實現

 1 STACK SEGMENT PARA STACK  2  DW 100H DUP(?)  3 STACK ENDS  4 
 5 DATA SEGMENT PARA  6     LEN        EQU 12
 7     CHAR     DB     'X'
 8     STR1     DB     'CHENQIXIAN',13,10,'$'
 9     MSG1     DB     'X IN CHENQIXIAN',13,10,'$'
 10     MSG2    DB     'NOT FOUND X IN CHENQIXIAN',13,10,'$'
 11  STR2 DB LEN DUP(?)  12 DATA ENDS  13 
 14 CODE SEGMENT PARA  15         ASSUME    CS:CODE,DS:DATA,SS:STACK  16 OUTPUT MACRO MSG  17         PUSH AX  18         PUSH DX  19         MOV DX,OFFSET MSG  20         MOV     AH,9
 21         INT 21H  22         POP DX  23         POP AX  24  ENDM  25 
 26 DELX PROC  27     DELETEX:
 28         PUSH BP  29         MOV BP,SP  30         PUSH SI  31         PUSH DI  32         PUSH CX  33         PUSH AX  34 
 35         MOV     SI,[BP+4]  36         MOV     DI,[BP+6]  37         MOV CX,LEN  38 
 39     DELX_LP:
 40         LODSB
 41         CMP     AL,'X'
 42         JE DELX_CONTINUE  43         STOSB
 44     DELX_CONTINUE:    
 45  LOOP DELX_LP  46 
 47     DELETEXRET:
 48         MOV     AL,'$'
 49         STOSB
 50         POP AX  51         POP CX  52         POP DI  53         POP SI  54         POP BP  55         RET     4
 56 DELX ENDP  57 
 58 FIND_CH PROC  59     FINDCHAR:
 60         PUSH BP  61         MOV BP,SP  62         PUSH AX  63         PUSH DI  64         PUSH CX  65 
 66         MOV         AX,[BP+4]  67         MOV     DI,[BP+6]  68         MOV CX,LEN  69 
 70         CLD
 71         REPNZ     SCASB
 72         JZ FOUND  73  OUTPUT MSG2  74         JMP SHORT FINDCHARRET  75     FOUND:
 76  OUTPUT MSG1  77         MOV DX,OFFSET STR1  78         PUSH DX  79         PUSH DX  80         CALL DELX  81     FINDCHARRET:
 82         POP CX  83         POP DI  84         POP AX  85         POP BP  86         RET     4
 87 FIND_CH ENDP  88 
 89 MAIN PROC FAR  90     MAINPROC:
 91         MOV AX,DATA  92         MOV DS,AX  93         MOV ES,AX  94 
 95         MOV DX,OFFSET STR1  96         PUSH DX  97         MOV DL,CHAR  98         XOR DH,DH  99         PUSH DX 100         CALL FIND_CH 101 
102     EXIT:    
103         MOV AX,4C00H 104         INT 21H 105 MAIN ENDP 106 
107 CODE ENDS 108         END     MAIN

1-5 實現效果截圖

1-5-1 程序運行結果

 

經驗證,發現輸出結果符合預期

1-5-2 查看刪除後內存中新的字符串

 

經驗證,發現內存中的結果符合預期

2 程序設計複習2

2-1 練習要點

  • 字符的輸入輸出

  • 數字讀入存儲邏輯

  • 數字的最優輸出方式

2-2 實現思路

  • 首先爲讀入字符和輸出數字分別單獨編寫子程序

  • 主程序中循環調用讀入字符,因爲題目固定讀入兩位十進制數,所以讀入的第一個數乘10加上第二個讀入的數,即爲讀入的數字

  • 在輸出上的改進:還是除10顯示,但此次保存餘數。爲了獲得正序輸出,將每次的餘數壓棧,這樣在顯示的時候就是從高位向低位顯示了。此外,在輸出時對前導0進行了過濾處理,須要注意的是當遇到第一個非0數字後,須要將標誌位置1,這樣之後的數字0就能夠正常顯示

2-3 代碼實現

 1 STACK SEGMENT PARA STACK  2  DW 100H DUP(?)  3 STACK ENDS  4 
 5 DATA SEGMENT PARA  6     LEN EQU 5
 7     X     DB     0
 8     Y     DB     0
 9  Z DB ?  10     NL     DB    13,10,'$'
 11 DATA ENDS  12 
 13 CODE SEGMENT PARA  14         ASSUME    CS:CODE,DS:DATA,SS:STACK  15 NEWLINE MACRO  16         PUSH AX  17         PUSH DX  18         MOV DX,OFFSET NL  19         MOV     AH,9
 20         INT 21H  21         POP DX  22         POP AX  23  ENDM  24 GETNUM PROC  25     INPUT:
 26         MOV     AH,1
 27         INT 21H  28         SUB AL,30H  29         XOR AH,AH  30         RET
 31 GETNUM ENDP  32 
 33 OUTPUT PROC  34     PRINT:
 35         PUSH DX  36         PUSH CX  37         PUSH BX  38         PUSH AX  39  NEWLINE  40         MOV CX,LEN  41         MOV     BX,10
 42     PRINT_LP1:
 43         XOR DX,DX  44         DIV BX  45         PUSH DX  46  LOOP PRINT_LP1  47 
 48         MOV CX,LEN  49         MOV     BX,0
 50     PRINT_LP2:
 51         POP DX  52         CMP     DL,0
 53         JNE PRINT_LP2_1  54         CMP        BX,0
 55         JZ PRINT_LP2_2  56     PRINT_LP2_1:
 57         MOV     BX,1
 58         MOV     AH,2
 59         OR DL,30H  60         INT 21H  61         
 62     PRINT_LP2_2:
 63  LOOP PRINT_LP2  64         POP AX  65         POP BX  66         POP CX  67         POP DX  68         RET
 69 OUTPUT ENDP  70 
 71 
 72 MAIN PROC FAR  73     MAINPROC:
 74         MOV AX,DATA  75         MOV DS,AX  76         MOV ES,AX  77 
 78         CALL GETNUM  79         MOV     BL,10
 80         MUL BL  81         MOV X,AL  82         CALL GETNUM  83         ADD X,AL  84 
 85         CALL GETNUM  86         MOV        BL,10
 87         MUL BL  88         MOV Y,AL  89         CALL GETNUM  90         ADD Y,AL  91 
 92         MOV AL,X  93         MOV BL,Y  94         MUL BL  95 
 96         CALL OUTPUT  97 
 98     EXIT:    
 99         MOV AX,4C00H 100         INT 21H 101 MAIN ENDP 102 
103 CODE ENDS 104         END     MAIN

2-4 運行結果

   

顯然,運行結果符合預期。

3 程序設計複習3

3-1 練習要點

  • 字符串讀取:0AH號中斷調用

  • 字符串拷貝

  • 子程序調用參數的傳遞與保持

3-2 實現思路

  • 首先爲讀入字符串和輸出字符串分別單獨編寫子程序

  • 輸入待插入字符串後,首先調用第一次拷貝字符串子程序,判斷條件爲讀取到空格即中止拷貝。注意邊界條件的判斷,以及最後一次拷貝後SI與DI的保持

  • 緊接着在主程序中將SI壓棧保存,將SI指向待插入字符串首地址,調用插入子程序。將待插入字符串拼接到目標串尾部

  • 最後將SI彈出棧恢復,即又指向原列表空格後的第一個字符的位置處,調用第二次拷貝字符串子程序。此時邊界判斷條件爲’$’符號

  • 輸出目標串

3-3 代碼實現

 1 STACK SEGMENT PARA STACK  2  DW 100H DUP(?)  3 STACK ENDS  4 
 5 DATA SEGMENT PARA  6     LEN     EQU    32
 7     LIST    DB    'ABOVE ZEBRA$'
 8  TEMP DB LEN DUP(?)  9     NL         DB     13,10,'$'
 10     STR1    DB    LEN-1
 11  DB ?  12  DB LEN DUP(?)  13 DATA ENDS  14 
 15 CODE SEGMENT PARA  16         ASSUME    CS:CODE,DS:DATA,SS:STACK  17 NEWLINE MACRO  18         PUSH DX  19         PUSH AX  20         MOV DX,OFFSET NL  21         MOV     AH,9
 22         INT 21H  23         POP AX  24         POP DX  25  ENDM  26 
 27 OUTPUT MACRO MSG  28         PUSH DX  29         PUSH AX  30  NEWLINE  31         MOV DX,OFFSET MSG  32         MOV     AH,9
 33         INT 21H  34         POP AX  35         POP DX  36  ENDM  37 
 38 INPUT PROC  39     INPUTSTR1:
 40         PUSH DX  41         PUSH AX  42         PUSH SI  43 
 44         MOV DX,OFFSET STR1  45         MOV AH,0AH  46         INT 21H  47         MOV     SI,OFFSET STR1+2
 48         MOV     AL,STR1+1
 49         XOR AH,AH  50         ADD SI,AX  51         MOV     BYTE PTR [SI],'$'
 52 
 53         POP SI  54         POP AX  55         POP DX  56         RET
 57 INPUT ENDP  58 
 59 COPY PROC  60     STRCPY:
 61         LODSB
 62         CMP AL,20H  63         JE COPYRET  64         STOSB
 65         JMP STRCPY  66     COPYRET:
 67         STOSB
 68         RET
 69 COPY ENDP  70 
 71 INSERT PROC  72     INSERT_STR1:
 73         MOV     CL,STR1+1
 74         XOR CH,CH  75     INSERT_LP:
 76         LODSB
 77         STOSB
 78  LOOP INSERT_LP  79         MOV AL,20H  80         STOSB
 81         RET
 82 INSERT ENDP  83 
 84 COPY2 PROC  85     STRCPY2:
 86         LODSB
 87         CMP     AL,'$'
 88         JE COPYRET2  89         STOSB
 90         JMP STRCPY2  91     COPYRET2:
 92         STOSB
 93         RET
 94 COPY2 ENDP  95 
 96 MAIN PROC FAR  97     MAINPROC:
 98         MOV AX,DATA  99         MOV DS,AX 100         MOV ES,AX 101  OUTPUT LIST 102         CALL INPUT 103         MOV SI,OFFSET LIST 104         MOV DI,OFFSET TEMP 105         CALL COPY 106         PUSH SI 107         MOV     SI,OFFSET STR1+2
108         CALL INSERT 109         POP SI 110         CALL COPY2 111  OUTPUT TEMP 112 
113     EXIT:    
114         MOV AX,4C00H 115         INT 21H 116 MAIN ENDP 117 
118 CODE ENDS 119         END     MAIN

3-4 運行結果

 

4 程序設計複習4

4-1 練習要點

  • 16進制輸出方式

  • 從10向A的轉化

  • 2號中斷調用輸出單個字符

4-2 實現思路

  • 首先在數據段初始化一個64位數字

  • 注意因爲一個字是2個字節16位,所以在輸出時,要依次在基地址的基礎上+2

  • 因爲是循環訪問數據段中的除數,所以用SI寄存器記錄數據段中除數的位置,每次循環都要使用兩次INC指令,保證訪問到下一個字中的內容。

  • 訪問除數必須用WORD PTR [SI],不然會提示 ’must have size’

  • 判斷16進制輸出的數字是否大於10,若不大於則直接輸出,不然須要加7(在ASCII數值上‘9’和‘A’之間差8),注意從數字轉換爲ASCII碼此處必須用ADD 30H 來代替 OR 30H,不然會出現錯誤。

4-3 代碼實現

 1 STACK SEGMENT PARA STACK  2  DW 100H DUP(?)  3 STACK ENDS  4 
 5 DATA SEGMENT PARA  6  NUM DW 1606H,1160H,1234H,0FFFFH  7  DIVISOR DW 1000H,100H,10H,1H  8 DATA ENDS  9 
10 CODE SEGMENT PARA 11         ASSUME    CS:CODE,DS:DATA,SS:STACK 12 
13 OUTPUT PROC 14     PRINT:
15         MOV SI,OFFSET DIVISOR 16         MOV     CX,4
17     OUTPUT_LP:
18         XOR DX,DX 19         DIV WORD PTR [SI] 20         PUSH DX 21         CMP     AL,10
22         JB OUTPUT_CONTINUE 23         ADD     AL,7
24     OUTPUT_CONTINUE:
25         ADD AL,30H 26         MOV DL,AL 27         MOV     AH,2
28         INT 21H 29         INC SI 30         INC SI 31         POP AX 32  LOOP OUTPUT_LP 33         RET
34 OUTPUT ENDP 35 
36 MAIN PROC FAR 37     MAINPROC:
38         MOV AX,DATA 39         MOV DS,AX 40         MOV ES,AX 41 
42         MOV AX,NUM 43         CALL OUTPUT 44 
45         MOV     AX,NUM+2
46         CALL OUTPUT 47 
48         MOV     AX,NUM+4
49         CALL OUTPUT 50 
51         MOV     AX,NUM+6
52         CALL OUTPUT 53 
54     EXIT:    
55         MOV AX,4C00H 56         INT 21H 57 MAIN ENDP 58 
59 CODE ENDS 60         END     MAIN

4-4 運行結果

【數據段】

 

【運行結果】

 

相關文章
相關標籤/搜索