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 运行结果

【数据段】

 

【运行结果】

 

                                  

posted @ 2019-05-04 16:34  Chris_Chen98  阅读(1183)  评论(0编辑  收藏  举报