x86匯編語言實踐(4)


0 寫在前面

  為了更深入的了解程序的實現原理,近期我學習了IBM-PC相關原理,並手工編寫了一些x86匯編程序。

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

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

  我將我編寫的一些匯編代碼放到了github上,感興趣的朋友可以點擊屏幕左上角的小貓咪進入我的github,或請點擊這里下載源代碼。

1 程序設計復習1

1-1 練習要點

  • 字符串中查找指定字符

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

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

1-2 實現思路

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

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

  • 調用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 運行結果

【數據段】

 

【運行結果】

 

                                  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM