- UID
- 665513
- 积分
- 0
- 金币
- 0
- 精华
- 0
- 威望
- 0
- 贡献
- 0
- 阅读权限
- 220
- 注册时间
- 2009-8-15
- 最后登录
- 2026-5-6
- 在线时间
- 0 小时
热心网友
- 金币
- 0
- 阅读权限
- 220
- 精华
- 0
- 威望
- 0
- 贡献
- 0
- 在线时间
- 0 小时
- 注册时间
- 2009-8-15
|
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 实现思路
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 实现思路
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 运行结果
【数据段】
【运行结果】
来源:https://www.cnblogs.com/chrischen98/p/10808861.html |
|