0 写在前面
为了更深入的了解程序的实现原理,近期我学习了IBM-PC相关原理,并手工编写了一些x86汇编程序。
在2017年的计算机组成原理中,曾对MIPS体系结构及其汇编语言有过一定的了解,考虑到x86体系结构在目前的广泛应用,我通过两个月左右的时间对x86的相关内容进行了学习。
在《x86汇编语言实践》系列中(包括本篇、x86汇编语言实践(1)、x86汇编语言实践(3)、x86汇编语言实践(4)以及x86汇编语言复习笔记),我通过几个具体案例对x86汇编语言进行实践操作,并记录了自己再编写汇编代码中遇到的困难和心得体会,与各位学习x86汇编的朋友共同分享。
我将我编写的一些汇编代码放到了github上,感兴趣的朋友可以点击屏幕左上角的小猫咪进入我的github,或请点击这里下载源代码。
1 十进制输入输出的乘法练习
1-1 练习要点
- 输入输出中断调用练习
- 宏练习
- 子程序编写与调用
1-2 实现思路
- 数据区使用byte类型存放两个十进制乘数NUM1和NUM2
- 输入采用宏GETNUM实现,从百位读到个位,调用乘法宏MULTI计算出NUM1和NUM2的值
- 调用乘法宏MULTI计算出结果,并保存到RESULT,以便debug调试
- 调用OUTPUT子程序进行输出,输出从RESULT中保存的结果
- 其他对于输入输出的控制对输出结果进行改进
1-3 代码实现
1 STACK SEGMENT PARA STACK
2 DW 100H DUP(?)
3 STACK ENDS
4
5 DATA SEGMENT PARA
6 NUM1 DB ?
7 NUM2 DB ?
8 RESULT DW ?
9 DATA ENDS
10
11 CODE SEGMENT PARA
12 ASSUME CS:CODE,DS:DATA,SS:STACK
13
14 GETNUM MACRO
15 MOV AH,1
16 INT 21H
17 SUB AL,30H
18 ENDM
19
20 MULTI MACRO N1,N2
21 MOV AL,N1
22 MOV BL,N2
23 MUL BL
24 ENDM
25
26 DIVIDE MACRO N1,N2
27 MOV AX,N1
28 MOV BX,N2
29 XOR DX,DX
30 DIV BX
31 ENDM
32
33 DISPNUM MACRO
34 PUSH DX
35 MOV AH,2
36 MOV DL,AL
37 ADD DL,30H
38 INT 21H
39 POP DX
40 ENDM
41
42 INPUT MACRO NUM
43 GETNUM
44 MULTI AL,100
45 MOV NUM,AL
46 GETNUM
47 MULTI AL,10
48 ADD NUM,AL
49 GETNUM
50 ADD NUM,AL
51 ENDM
52
53 NEWLINE MACRO
54 MOV AH,2
55 MOV DL,0DH
56 INT 21H
57 MOV AH,2
58 MOV DL,0AH
59 INT 21H
60 ENDM
61
62 OUTPUT PROC
63 DIVIDE AX,10000
64 DISPNUM
65 DIVIDE DX,1000
66 DISPNUM
67 DIVIDE DX,100
68 DISPNUM
69 DIVIDE DX,10
70 DISPNUM
71 MOV AL,DL
72 DISPNUM
73 RET
74 OUTPUT ENDP
75
76 MAIN PROC FAR
77 MOV AX,DATA
78 MOV DS,AX
79 ;get num1
80 INPUT NUM1
81 ;getchar
82 GETNUM
83 XOR AX,AX
84 ;get num2
85 INPUT NUM2
86 ;num1 * num2
87 MULTI NUM1,NUM2
88 MOV RESULT,AX
89 ;newline
90 NEWLINE
91 ;output result
92 MOV AX,RESULT
93 CALL OUTPUT
94
95
96 EXIT: MOV AX,4C00H
97 INT 21H
98 MAIN ENDP
99
100 CODE ENDS
101 END MAIN
1-4 实现效果截图
经验证,发现输出结果符合预期。
2 字符串操作与跳转表练习
2-1 练习要点
- 字符串的操作,包括:字符串的拼接、比较、查找
- 子程序调用与宏
- 跳转表的使用
2-2 重点难点
- 子程序调用需要对子程序中使用到的变量进行压栈处理,以免变量污染
- 字符串操作通常都需要对ES:DI和DS:SI进行初始化
- 宏的内容不能有标签
2-3 实现思路
- 首先为输入和输出单独编写子程序,程序主体采用跳转表实现
- 为每一个条件单独编写一个子程序,有4个条件,因此共需编写4个子程序
2-4 代码实现
1 STACK SEGMENT PARA STACK
2 DW 100H DUP(?)
3 STACK ENDS
4
5 DATA SEGMENT PARA
6 LEN EQU 128
7 MSG1 DB 'INPUT OPRAND:',13,10,'$' ;THE MSG TO OUTPUT
8 MSG2 DB 'INPUT STRING 1:',13,10,'$'
9 MSG3 DB 'INPUT STRING 2:',13,10,'$'
10 MSG4 DB '<','$'
11 MSG5 DB '=','$'
12 MSG6 DB '>','$'
13 MSG7 DB 'S3=','$'
14 MSG8 DB 'INPUT A CHAR:',13,10,'$'
15 MSG9 DB 'CHAR FOUND IN STR1!',13,10,'$'
16 MSG10 DB 'CHAR NOT FOUND IN STR1!',13,10,'$'
17 STR1 DB LEN-1
18 DB ?
19 DB LEN DUP(?)
20 STR2 DB LEN-1
21 DB ?
22 DB LEN DUP(?)
23 STR3 DB LEN-1
24 DB ?
25 DB LEN DUP(?)
26 CHAR DB ?
27 OP DB ? ;THE OPRAND USER INPUT
28 DATA ENDS
29
30 CODE SEGMENT PARA
31 ASSUME CS:CODE,DS:DATA,SS:STACK
32
33 HINT MACRO MSG
34 LEA DX,MSG ;OUTPUT MSG1
35 MOV AH,09H
36 INT 21H
37 ENDM
38
39 GETOP MACRO
40 MOV AH,1
41 INT 21H
42 SUB AL,30H
43 MOV OP,AL
44 ENDM
45
46 NEWLINE MACRO
47 PUSH AX
48 PUSH DX
49 MOV AH,2
50 MOV DL,0DH
51 INT 21H
52 MOV AH,2
53 MOV DL,0AH
54 INT 21H
55 POP DX
56 POP AX
57 ENDM
58
59 GETCHAR MACRO
60 MOV AH,1
61 INT 21H
62 MOV CHAR,AL
63 ENDM
64
65 GETSTR1 PROC
66 MOV DX,OFFSET STR1
67 MOV AH,0AH
68 INT 21H
69 MOV CL,STR1+1
70 XOR CH,CH
71 MOV SI,OFFSET STR1+2
72 LP1: INC SI
73 LOOP LP1
74 MOV BYTE PTR [SI],'$'
75 RET
76 GETSTR1 ENDP
77
78 GETSTR2 PROC
79 MOV DX,OFFSET STR2
80 MOV AH,0AH
81 INT 21H
82 MOV CL,STR2+1
83 XOR CH,CH
84 MOV SI,OFFSET STR2+2
85 LP2:INC SI
86 LOOP LP2
87 MOV BYTE PTR [SI],'$'
88 RET
89 GETSTR2 ENDP
90
91 STRCAT PROC
92 PUSH AX
93 CLD
94 CAT_LP1:LODSB
95 STOSB
96 CMP AL,0
97 JNZ CAT_LP1
98 POP AX
99 RET
100 STRCAT ENDP
101
102 P2_PUTEND PROC
103 MOV CL,STR1+1 ;SET CX TO LEN FOR LOOP
104 ADD CL,STR2+1
105 XOR CH,CH
106 MOV SI,OFFSET STR3+2 ;GET ACTUAL STRING
107 PLP2: INC SI
108 LOOP PLP2
109 MOV BYTE PTR [SI],'$' ;PUT END TO STRING
110 RET
111 P2_PUTEND ENDP
112 ;STRCMP
113 P1 PROC
114 PUSH CX
115 CLD
116 PUSH SI
117 MOV CX,1
118 SLP1: LODSB
119 CMP AL,00H
120 JZ SLP1_1
121 INC CX
122 JMP SHORT SLP1
123 SLP1_1: POP SI
124 REPE CMPSB
125 HINT STR1+2
126 JA SL1
127 JB SL2
128 HINT MSG5
129 MOV AL,0
130 JMP SHORT RETURN
131 SL1: HINT MSG6
132 MOV AL,1
133 JMP SHORT RETURN
134 SL2: HINT MSG4
135 MOV AL,2
136 RETURN: HINT STR2+2
137 POP CX
138 RET
139 P1 ENDP
140 ;STRCAT(S1,S2)+STRCPY(S3,S1)
141 P2 PROC
142 ;STRCAT(S1,S2)
143 PUSH AX
144 MOV SI,OFFSET STR2+2
145 MOV CL,STR1+1
146 XOR CH,CH
147 MOV DI,OFFSET STR1+2
148 CATLP1: INC DI
149 LOOP CATLP1
150 CALL STRCAT
151 ;STRCPY(S3,S1)
152 MOV SI,OFFSET STR1+2
153 MOV DI,OFFSET STR3+2
154 CALL STRCAT
155 CALL P2_PUTEND
156 HINT MSG7
157 HINT STR3+2
158 POP AX
159 RET
160 P2 ENDP
161 ;STRCAT(S2,S1)+STRCPY(S3,S2)
162 P2_2 PROC
163 ;STRCAT(S2,S1)
164 PUSH AX
165 MOV SI,OFFSET STR1+2
166 MOV CL,STR2+1
167 XOR CH,CH
168 MOV DI,OFFSET STR2+2
169 CATLP2: INC DI
170 LOOP CATLP2
171 CALL STRCAT
172 ;STRCPY(S3,S1)
173 MOV SI,OFFSET STR2+2
174 MOV DI,OFFSET STR3+2
175 CALL STRCAT
176 CALL P2_PUTEND
177 HINT MSG7
178 HINT STR3+2
179 POP AX
180 RET
181 P2_2 ENDP
182 ;STRFIND
183 P3 PROC
184 HINT MSG8
185 GETCHAR
186 NEWLINE
187 MOV DI,OFFSET STR1+2
188 MOV CL,STR1+1
189 XOR CH,CH
190 MOV AL,CHAR
191 CLD
192 REPNZ SCASB
193 JZ FOUND
194 HINT MSG10
195 JMP P3_RETURN
196 FOUND: HINT MSG9
197 P3_RETURN:RET
198 P3 ENDP
199 ;SRTCMP+STRCAT+STRCPY
200 P4 PROC
201 MOV SI,OFFSET STR1+2
202 MOV DI,OFFSET STR2+2
203 CALL P1
204 NEWLINE
205 CMP AL,0
206 JNE LA41
207 CALL P2
208 JMP CONTINUE4
209 LA41: CMP AL,1
210 JNE LA42
211 CALL P2
212 JMP CONTINUE4
213 LA42: CMP AL,2
214 JNE CONTINUE4
215 CALL P2_2
216 CONTINUE4:
217 RET
218 P4 ENDP
219
220 SWITCH PROC
221 CMP OP,1
222 JNE LA1
223 MOV SI,OFFSET STR1+2
224 MOV DI,OFFSET STR2+2
225 CALL P1
226 JMP CONTINUE
227 LA1: CMP OP,2
228 JNE LA2
229 CALL P2
230 JMP CONTINUE
231 LA2: CMP OP,3
232 JNE LA3
233 CALL P3
234 JMP CONTINUE
235 LA3: CMP OP,4
236 JNE LAN
237 CALL P4
238 JMP CONTINUE
239 LAN: HINT MSG3
240 CONTINUE:RET
241 SWITCH ENDP
242
243 MAIN PROC FAR
244 MOV AX,DATA
245 MOV DS,AX
246 MOV ES,AX
247
248 ;input str1
249 HINT MSG2
250 CALL GETSTR1
251 NEWLINE
252 ;input str2
253 HINT MSG3
254 CALL GETSTR2
255 NEWLINE
256 ;input op
257 HINT MSG1
258 GETOP
259 NEWLINE
260 ;SWITCH OP
261 CALL SWITCH
262
263 EXIT: MOV AX,4C00H
264 INT 21H
265 MAIN ENDP
266
267 CODE ENDS
268 END MAIN
2-5 运行结果
2-5-1 操作类型为1,即比较str1和str2的字典序
2-5-2 操作类型为2,即将str2拼接到str1后,并将整个串拷贝至str3
2-5-3 操作类型为3,即再输入一个字符char,判断char是否属于str1
2-5-4 操作类型为4,即比较str1和str2的字典序按降序进行拼接,并拷贝到str3
显然,运行结果符合预期。
3 字符串按字典序排序
3-1 练习要点
- 字符串的操作,LODSB,STOSB,CMPSB,REPE等等
- 各个字符串操作指令对PSW和SI,DI的影响
- 子程序调用与宏
- 冒泡排序算法
- 复杂程序的调试
3-2 重点难点
- 冒泡排序
- 宏和子程序的编写时必须注意堆栈的维护,用到的变量必须压栈处理
- 字符串交换的逻辑
- 操作字符串的子程序必须对SI,DI压栈保存,因为会隐式修改SI,DI
3-3 实现思路
- 调用输出子程序输出原单词表
- 调用排序子程序
- 调用输出子程序输出排序后的单词表
3-4 代码实现
- 输出子程序中每输出一个单词,就将SI增加STR_LEN,循环输出TABLE_LEN次
- 排序子程序采用冒泡,在相邻两单词比较时,将当前单词置于DS:SI,相邻下一单词置于ES:DI,并比较其字典序,若当前单词字典序较大,则调用交换子程序,并将标志位BX置0,表示这一趟外层循环排序有调整。当一趟外层循环排序无调整,则表示当前单词表有序,即可退出排序子程序。
- 比较子程序要注意对SI的维护,以及REPE CMPSB的含义:当CX≠0且ZF=1时执行CMPSB,CMPSB返回SI和DI的比较结果,并将二者分别+1,CX为提前计算好的两字符串的长度。含义是,当两字符串的前若干位字符相同时,就继续向后比较,直到比较到长度的结尾,或出现不同时结束。后接JA,JB指令,根据比较结果进行跳转与执行相关逻辑。
- 交换S1,S2的逻辑:S1->TMP , S2->S1 , TMP->S2。
1 STACK SEGMENT PARA STACK
2 DW 100H DUP(?)
3 STACK ENDS
4
5 DATA SEGMENT PARA
6 TABLE_LEN EQU 9
7 STR_LEN EQU 7
8 TABLE DB 'QIQI',20H,0,'$'
9 DB 'CHEN',20H,0,'$'
10 DB 'XIAN',20H,0,'$'
11 DB 'QIQJ',20H,0,'$'
12 DB 'XHAN',20H,0,'$'
13 DB 'XIBN',20H,0,'$'
14 DB 'XHQI',20H,0,'$'
15 DB 'LOVE',20H,0,'$'
16 DB 'SURE',20H,0,'$'
17 TEMP DB STR_LEN DUP(?)
18 NEW_LINE DB 0DH,0AH,'$'
19 DATA ENDS
20
21 CODE SEGMENT PARA
22 ASSUME CS:CODE,DS:DATA,SS:STACK
23
24 NEWLINE MACRO
25 PUSH DX
26 PUSH AX
27 MOV DX,OFFSET NEW_LINE
28 MOV AH,9
29 INT 21H
30 POP AX
31 POP DX
32 ENDM
33
34 OUTPUT PROC
35 PART1:
36 MOV CX,TABLE_LEN
37 MOV SI,OFFSET TABLE
38 LP1:
39 MOV DX,SI
40 MOV AH,9
41 INT 21H
42 ADD SI,STR_LEN
43 LOOP LP1
44 NEWLINE
45 RET
46 OUTPUT ENDP
47
48 COMP PROC
49 COMPARE:
50 PUSH SI
51 PUSH CX
52 CLD
53 PUSH SI
54 MOV CX,1
55 SLP1:
56 LODSB
57 CMP AL,00H
58 JZ SLP1_1
59 INC CX
60 JMP SHORT SLP1
61 SLP1_1:
62 POP SI
63 REPE CMPSB
64 JA SL1
65 JB SL2
66 MOV AL,1 ;SI=DI
67 JMP SHORT RETURN
68 SL1:
69 MOV AL,2 ;SI>DI
70 JMP SHORT RETURN
71 SL2:
72 MOV AL,0 ;SI<DI
73 RETURN:
74 POP CX
75 POP SI
76 RET
77 COMP ENDP
78
79 STRCPY PROC
80 STRING_COPY:
81 PUSH SI
82 PUSH DI
83 PUSH AX
84 CLD
85 CPY_LP1:
86 LODSB
87 STOSB
88 CMP AL,0
89 JNZ CPY_LP1
90 POP AX
91 POP DI
92 POP SI
93 RET
94 STRCPY ENDP
95
96 EXCHG PROC
97 EXCHANGE:
98 PUSH SI
99 PUSH DI
100
101 MOV DI,OFFSET TEMP
102 CALL STRCPY
103
104 MOV DI,SI
105 ADD SI,STR_LEN
106 CALL STRCPY
107
108 MOV DI,SI
109 MOV SI,OFFSET TEMP
110 CALL STRCPY
111 POP DI
112 POP SI
113 RET
114 EXCHG ENDP
115
116 SORT PROC
117 PART2:
118 MOV CX,TABLE_LEN
119 DEC CX
120 LP2:
121 MOV BX,1
122 MOV SI,OFFSET TABLE
123 PUSH CX
124
125 LP2_1:
126 MOV AX,SI
127 ADD AX,STR_LEN
128 MOV DI,AX
129 CALL COMP
130 CMP AL,1
131 JBE CONTINUE
132 CALL EXCHG
133 MOV BX,0
134 CONTINUE:
135 ADD SI,STR_LEN
136 LOOP LP2_1
137
138 POP CX
139 DEC CX
140 CMP BX,1
141 JZ SORT_RETURN
142 JMP SHORT LP2
143 SORT_RETURN:
144 RET
145 SORT ENDP
146
147 MAIN PROC FAR
148 MAIN_PART:
149 MOV AX,DATA
150 MOV DS,AX
151 MOV ES,AX
152 ;DISPLAY ORIGIN TABLE
153 CALL OUTPUT
154 ;SORT
155 CALL SORT
156 ;DISPLAY ORDERED TABLE
157 CALL OUTPUT
158
159 EXIT:
160 MOV AX,4C00H
161 INT 21H
162 MAIN ENDP
163
164 CODE ENDS
165 END MAIN
3-5 运行结果
在DATA段预置字典如下图所示
编译、链接并执行程序,得到结果如下,第一行为元单词表,第二行为排序后的单词表(按字典序升序)
显然,运行结果符合预期。
来源:oschina
链接:https://my.oschina.net/u/4377611/blog/3564550