x86汇编语言实践(3)

时间:2021-05-03 03:41:24

0 写在前面

  为了更深入的了解程序的实现原理,近期我学习了IBM-PC相关原理,并手工编写了一些x86汇编程序

  在2017年的计算机组成原理中,曾对MIPS体系结构及其汇编语言有过一定的了解,考虑到x86体系结构在目前的广泛应用,我通过两个月左右的时间对x86的相关内容进行了学习。

  在《x86汇编语言实践》系列中(包括本篇、x86汇编语言实践(1)以及x86汇编语言实践(2)),我通过几个具体案例对x86汇编语言进行实践操作,并记录了自己再编写汇编代码中遇到的困难和心得体会,与各位学习x86汇编的朋友共同分享。

  我将我编写的一些汇编代码放到了github上,感兴趣的朋友可以点击屏幕左上角的小猫咪进入我的github,或请点击这里下载源代码。

1 递归调用计算N!

1-1 练习要点

  • 递归调用

  • 栈指针的维护

  • 子程序编写与调用

1-2 实现思路

  • 在数据段存储好待计算的N,和用以存储计算结果的RESULT

  • 主程序中首先将N和RESULT压栈

  • 调用CALCULATE进行阶乘的递归计算

  • 结果返回至RESULT

  • 调用DISP_VALUE打印输出阶乘计算结果

1-3 重点难点

  • 参数传递:使用堆栈进行参数传递,需要将参数压栈,注意子程序返回时,必须增加一个常数偏移量RET X。这里的X为压入参数所占的字节数,通常为2的倍数,以保证堆栈平衡

  • 子程序保存现场:在子程序中,往往要用到很多寄存器,但我们希望在子程序返回时,调用子程序位置处周围的变量仍能恢复,这就需要在调用的子程序中保存现场,即子程序中所用到或修改的所有寄存器,都必须压栈处理

  • 子程序中的堆栈寻址:使用BP寄存器寻址,这是为了不修改SP指针,避免弄乱堆栈栈顶指针SP

  • 中间一直困扰我的就是在子程序中获取参数N的方式MOV BX,[BP+6]为什么是BP+6呢?我们来看,BP保存的是子程序中的SP指针,但是距离我们将N压栈之间,我们经历了:将RESULT压栈、调用时将调用处的IP+2压栈以及将BP压栈,三个过程。因此当前的BP和N之间相差了6个字节的距离,故采用[BP+6]的方式进行参数N的寻址

  • 输出上的改进:仍是除10显示,但这次保存余数。为了得到正序输出,将每次的余数压栈,这样在显示的时候就是从高位向低位显示了。此外,在输出时对前导0进行了过滤处理,需要注意的是当遇到第一个非0数字后,需要将标志位置1,这样以后的数字0就可以正常显示。

1-4 代码实现

 1 STACK     SEGMENT    PARA    STACK
 2         DW        100H DUP(?)
 3 STACK    ENDS
 4 
 5 DATA    SEGMENT    PARA
 6     N     DW    7
 7     RESULT     DW     ?         
 8 DATA     ENDS
 9 
10 CODE     SEGMENT PARA
11         ASSUME    CS:CODE,DS:DATA,SS:STACK
12 CALCULATE PROC NEAR
13     CAL_PART:
14         PUSH    BP
15         MOV     BP,SP
16         PUSH     DX
17         PUSH     BX
18 
19         MOV     BX,[BP+6]
20         CMP     BX,0
21         JNZ     CAL1
22         MOV     AX,1
23         JMP     SHORT CAL2
24     CAL1:
25         PUSH     BX
26         DEC     BX
27         PUSH     BX
28         PUSH     RESULT
29         CALL     CALCULATE
30         POP     BX
31         MUL        BX        
32     CAL2:
33         MOV     RESULT,AX
34         POP         BX
35         POP     DX
36         POP        BP
37         RET     4
38 CALCULATE ENDP
39 
40 DISP_VALUE PROC
41     DISPLAY:
42         PUSH     DX
43         PUSH     CX
44         PUSH    BX
45         PUSH     AX
46 
47         MOV     CX,5
48         MOV     BX,10
49 
50     DLP1:
51         XOR     DX,DX
52         DIV     BX
53         PUSH     DX
54         LOOP     DLP1
55 
56         MOV     BX,0
57         MOV     CX,5
58     DLP2:
59         POP     DX
60         CMP     DL,0
61         JNZ     DLP2_1
62         CMP     BX,0
63         JZ         DLP2_2
64     DLP2_1:
65         MOV     BX,1
66         OR         DL,30H
67         MOV     AH,2
68         INT      21H
69     DLP2_2:
70         LOOP     DLP2
71 
72         POP     AX
73         POP     BX
74         POP     CX
75         POP     DX
76         RET
77 DISP_VALUE ENDP
78 
79 
80 MAIN    PROC     FAR
81     MAINPROC:
82         MOV     AX,DATA
83         MOV     DS,AX
84 
85         MOV     AX,N
86         PUSH     AX
87         PUSH     RESULT
88         CALL     CALCULATE
89         MOV     AX,RESULT
90         CALL     DISP_VALUE    
91 
92     EXIT:    
93         MOV     AX,4C00H
94         INT     21H
95 MAIN     ENDP
96 CODE     ENDS
97         END     MAIN

1-5 实现效果截图

1-5-1 计算N=7时的阶乘计算结果

   x86汇编语言实践(3)

  经验证,发现输出结果符合预期。

1-5-2 查看递归调用到N=4时的堆栈信息

   x86汇编语言实践(3)

  从上面单步执行的寄存器结果中可以看出,BX=4即此时已经执行到N=4,此时堆栈指针SP位于01d2。我们来分析一下,当前堆栈中的内容

  • ss:1d2 压入RESULT作为参数向递归函数中传递,值为0

  • ss:1d4 压入BX(这里也就是N=4)作为参数向递归函数中传递,值为4

  • ss:1d6 保存的减一之前的N,这是为了在子程序返回时能计算N*AX返回结果

  • ss:1d8 子程序开始是压入的BX保存的值,值为5

  • ss:1da 子程序开始是压入的DX保存的值,值为0

  • ss:1dc 子程序开始是压入的BP保存的值,值为1ea

  • ss:1de CALL子程序会保存调用处下一条指令的IP并压栈,值为1c,即该子程序返回后会跳转至1c(+偏移值)

2 练习子程序参数传递的两种方法

2-1 练习要点

  • 子程序的编写

  • 使用寄存器向子程序传递参数

  • 使用堆栈向子程序传递参数

  • 复习乘法计算子程序,字符串拷贝子程序,字符串比较子程序,查找子程序

  • 选做部分我练习的是将字符串中全部的大写字母替换成小写字母

2-2 重点难点

  • 寄存器传参比较简单,将用到参数的寄存器保存为相应的参数值即可完成参数传递

  • 堆栈传参需要注意以下几点

    • 压栈顺序一定要注意,在压入多个参数时,需要记住其相对于SP的相对位置,从而避免取出参数时的混乱

    • 子程序中对参数的索引采用BP指针代替SP指针进行寻址,从而避免改变栈顶SP指针引发的紊乱现象发生

    • 返回时需要加上一个常数偏移量,将压入栈中的参数位置地址恢复,从而维持堆栈平衡

2-3 实现思路

  • 首先为输入和输出单独编写子程序,程序主体采用跳转表实现

  • 为每一个条件单独编写一个子程序,有10中条件(A-E为堆栈传参子程序,a-e为寄存器传参子程序),因此共需编写10个子程序分别对应着实现响应功能

  • 在最外层设置循环结构,使得程序能够处理多组输入

  • 字符串、数据、参数等初始化设置在数据段完成即可

2-4 代码实现

  1 STACK     SEGMENT    PARA    STACK
  2         DW        100H DUP(?)
  3 STACK    ENDS
  4 
  5 DATA    SEGMENT    PARA
  6     LEN     EQU 7
  7     N        EQU 10    ;TIMES OF LOOP
  8     X        DW     7
  9     Y        DW  8
 10     Z        DW     ?
 11     STRING1 DB    'QIQI',20H,0,'$'
 12     STRING2 DB    'CHEN',20H,0,'$'    
 13     CHAR     DB    'C'
 14     OP         DB     ?
 15     NL        DB     13,10,'$'
 16     MSGEQ     DB     'STRING1=STRING2',13,10,'$'
 17     MSGGT     DB     'STRING1>STRING2',13,10,'$'
 18     MSGLT   DB     'STRING1<STRING2',13,10,'$'
 19     DOFOUND DB    'CHAR FOUND IN STRING2',13,10,'$'
 20     NOTFOUND DB 'CHAR NOT FOUND IN STRING2',13,10,'$'
 21 DATA     ENDS
 22 
 23 CODE     SEGMENT PARA
 24         ASSUME    CS:CODE,DS:DATA,SS:STACK
 25 ;PRINT A NEWLINE
 26 NEWLINE MACRO
 27     PUSH      DX
 28     PUSH     AX
 29     MOV        DX,OFFSET NL
 30     MOV     AH,9
 31     INT     21H
 32     POP     AX
 33     POP         DX
 34     ENDM
 35 ;GET OPERATION TO OP
 36 GETOP     MACRO
 37     GETOPM:
 38         MOV     AH,1
 39         INT     21H
 40         MOV     OP,AL
 41         ENDM
 42 ;OUTPUT MSG
 43 OUTPUT     MACRO MSG
 44     PUSH     DX
 45     PUSH     AX
 46     MOV     DX,OFFSET MSG
 47     MOV     AH,9
 48     INT     21H
 49     POP     AX
 50     POP     DX
 51     ENDM
 52 ;DISPLAY VALUE IN AX
 53 DISP_VALUE PROC
 54     DISPLAY:
 55         PUSH     DX
 56         PUSH     CX
 57         PUSH    BX
 58         PUSH     AX
 59 
 60         MOV     CX,5
 61         MOV     BX,10
 62 
 63     DLP1:
 64         XOR     DX,DX
 65         DIV     BX
 66         PUSH     DX
 67         LOOP     DLP1
 68 
 69         MOV     BX,0
 70         MOV     CX,5
 71     DLP2:
 72         POP     DX
 73         CMP     DL,0
 74         JNZ     DLP2_1
 75         CMP     BX,0
 76         JZ         DLP2_2
 77     DLP2_1:
 78         MOV     BX,1
 79         OR         DL,30H
 80         MOV     AH,2
 81         INT      21H
 82     DLP2_2:
 83         LOOP     DLP2
 84 
 85         NEWLINE
 86         POP     AX
 87         POP     BX
 88         POP     CX
 89         POP     DX
 90         RET
 91 DISP_VALUE ENDP
 92 
 93 DISP_STR2 PROC
 94     PRINTSTR2:
 95         PUSH     DX
 96         MOV     DX,OFFSET STRING2
 97         MOV     AH,9
 98         INT     21H
 99         NEWLINE
100         POP     DX
101         RET
102 DISP_STR2 ENDP
103 
104 MULTIPLE PROC
105     MULTI:
106         PUSH     BP
107         MOV     BP,SP
108         PUSH     AX
109         PUSH     BX
110 
111         MOV     AX,[BP+4]
112         MOV     BX,[BP+6]
113         MUL     BX
114         MOV     Z,AX
115 
116         POP     BX
117         POP     AX
118         POP     BP
119 
120         RET     4
121 MULTIPLE ENDP
122 
123 MULTIPLE2 PROC
124     MULTI2:
125         MUL     BX
126         MOV     Z,AX
127         RET
128 MULTIPLE2 ENDP
129 
130 STRCPY PROC
131     STRCPYPROC:
132         PUSH     BP
133         MOV     BP,SP
134 
135         PUSH     DI 
136         PUSH     SI
137         MOV     SI,[BP+4]
138         MOV        DI,[BP+6]
139 
140         CLD
141     CPYLP:
142         LODSB
143         STOSB
144         CMP     AL,0
145         JNZ     CPYLP
146         POP     SI
147         POP     DI
148         POP     BP
149         RET     4
150 STRCPY ENDP
151 
152 STRCPY2 PROC
153     STRCPY2PROC:
154         CLD
155     CPYLP2:
156         LODSB
157         STOSB
158         CMP     AL,0
159         JNZ     CPYLP2
160         RET
161 STRCPY2 ENDP
162 
163 STRCMP     PROC
164     STRCMPROC:
165         PUSH     BP
166         MOV     BP,SP
167 
168         PUSH     DI
169         PUSH     SI
170 
171         MOV     SI,[BP+4]
172         MOV     DI,[BP+6]
173         CALL     STRCMP2
174 
175         POP     SI
176         POP     DI
177         POP     BP
178         RET     4
179 STRCMP     ENDP
180 
181 STRCMP2 PROC
182     STRCMP2PROC:
183         PUSH     CX
184         PUSH     SI
185         CLD
186         PUSH     SI
187         MOV     CX,1
188     CMPLP2:
189         LODSB
190         CMP     AL,0
191         JZ         CMPLPBEG2
192         INC     CX
193         JMP     SHORT CMPLP2
194     CMPLPBEG2:
195         POP     SI
196         REPE     CMPSB
197         JA         L2_1
198         JB         L2_2
199         OUTPUT     MSGEQ
200         JMP     SHORT CMPRET2
201     L2_1:
202         OUTPUT     MSGGT
203         JMP     SHORT CMPRET2
204     L2_2:
205         OUTPUT     MSGLT
206     CMPRET2:
207         POP      SI
208         POP     CX
209         RET
210 STRCMP2 ENDP
211 
212 FIND PROC
213     FINDCHAR:
214         PUSH     BP
215         MOV         BP,SP
216         PUSH     CX
217 
218         MOV     DI,[BP+6]
219         MOV     CX,LEN
220         DEC     CX
221         MOV     AX,[BP+4]
222         CLD
223         REPNZ     SCASB
224         JZ     FOUND
225         OUTPUT     NOTFOUND
226         JMP     SHORT FIND_RETURN
227     FOUND:
228         OUTPUT     DOFOUND
229     FIND_RETURN:
230         POP     CX
231         POP     BP
232         RET        4
233 FIND ENDP
234 
235 FIND2 PROC
236     FIND2PROC:
237         PUSH     CX
238         PUSH     DI
239         MOV     CX,LEN
240         DEC     CX
241         CLD
242         REPNZ     SCASB
243         JZ         FOUND2
244         OUTPUT     NOTFOUND
245         JMP     SHORT FIND2RETURN
246     FOUND2:
247         OUTPUT     DOFOUND
248     FIND2RETURN:
249         POP     DI
250         POP     CX
251         RET
252 FIND2 ENDP
253 
254 TOLOWER PROC
255     TOLOW:
256         PUSH     BP
257         MOV     BP,SP
258         PUSH     SI
259         PUSH     DI
260         PUSH     CX
261         PUSH     AX
262 
263         MOV     SI,[BP + 4]
264         MOV     DI,SI
265         MOV     CX,LEN
266         CLD
267     TOLOW_LP:
268         LODSB
269         CMP     AL,'A'
270         JB         TOLOW_CONTINUE
271         CMP     AL,'Z'
272         JA         TOLOW_CONTINUE
273         ADD     AL,20H
274     TOLOW_CONTINUE:
275         STOSB
276         LOOP     TOLOW_LP
277         
278         POP     AX
279         POP     CX
280         POP     DI
281         POP     SI
282         POP     BP
283         RET     2
284 TOLOWER ENDP
285 
286 TOLOWER2 PROC
287     TOLOW2:
288         PUSH     SI
289         PUSH     DI
290         PUSH     CX
291         PUSH     AX
292         MOV     DI,SI
293         MOV     CX,LEN
294         DEC     CX
295         CLD
296     TOLOW_LP2:
297         LODSB
298         CMP     AL,'A'
299         JB         TOLOW_CONTINUE2
300         CMP     AL,'Z'
301         JA         TOLOW_CONTINUE2
302         ADD     AL,20H
303     TOLOW_CONTINUE2:
304         STOSB
305         LOOP     TOLOW_LP2
306         POP     AX
307         POP     CX
308         POP     DI
309         POP     SI
310         RET
311 TOLOWER2 ENDP
312 
313 SWITCH     PROC
314     SWITCHPROC:
315         PUSH     CX
316     S0:
317         CMP     OP,'A'
318         JNE        S1
319         PUSH     X
320         PUSH     Y
321         CALL     MULTIPLE
322         MOV     AX,Z
323         CALL     DISP_VALUE
324         JMP     CONTINUE
325     S1:
326         CMP     OP,'B'
327         JNE        S2
328         MOV     DX,OFFSET STRING2
329         PUSH     DX
330         MOV     DX,OFFSET STRING1
331         PUSH     DX
332         CALL     STRCPY
333         OUTPUT     STRING2
334         NEWLINE
335         JMP     CONTINUE
336     S2:
337         CMP     OP,'C'
338         JNE        S3
339         MOV     DX,OFFSET STRING2
340         PUSH     DX
341         MOV     DX,OFFSET STRING1
342         PUSH     DX
343         CALL     STRCMP
344         JMP     CONTINUE
345     S3:
346         CMP     OP,'D'
347         JNE        S4
348         MOV     DX,OFFSET STRING2
349         PUSH     DX
350         MOV     DL,CHAR
351         XOR     DH,DH
352         PUSH     DX
353         CALL     FIND
354         JMP     CONTINUE
355     S4:
356         CMP     OP,'E'
357         JNE        S5
358         MOV     DX,OFFSET STRING1
359         PUSH     DX
360         CALL     TOLOWER
361         OUTPUT     STRING1
362         NEWLINE
363         JMP     CONTINUE
364     S5:
365         CMP     OP,'a'
366         JNE        S6
367         MOV     AX,X
368         MOV     BX,Y
369         CALL     MULTIPLE2
370         MOV        AX,Z
371         CALL     DISP_VALUE
372         JMP     CONTINUE
373     S6:
374         CMP     OP,'b'
375         JNE        S7
376         MOV         SI,OFFSET STRING1
377         MOV     DI,OFFSET STRING2
378         CALL     STRCPY2
379         OUTPUT     STRING2
380         NEWLINE
381         JMP     CONTINUE
382     S7:
383         CMP     OP,'c'
384         JNE        S8
385         MOV     SI,OFFSET STRING1
386         MOV     DI,OFFSET STRING2
387         CALL     STRCMP2
388         JMP     CONTINUE
389     S8:
390         CMP     OP,'d'
391         JNE        S9
392         MOV         DI,OFFSET STRING2
393         MOV     AL,CHAR
394         CALL    FIND2
395         JMP     CONTINUE
396     S9:
397         CMP     OP,'e'
398         JNE        CONTINUE
399         MOV     SI,OFFSET STRING2
400         CALL     TOLOWER2
401         OUTPUT     STRING2
402         NEWLINE
403     CONTINUE:
404         POP     CX    
405         RET
406 SWITCH     ENDP
407 
408 MAIN    PROC     FAR
409     MAINPROC:
410         MOV     AX,DATA
411         MOV     DS,AX
412         MOV     ES,AX
413 
414         MOV     CX,N
415     MAINLOOP:
416         GETOP
417         NEWLINE
418         CALL     SWITCH
419         LOOP     MAINLOOP
420 
421     EXIT:    
422         MOV     AX,4C00H
423         INT     21H
424 MAIN     ENDP
425 
426 CODE     ENDS
427         END     MAIN

2-5 运行结果

    为了验证程序符合预期,需要设计以下样例进行测试。设置循环次数为10次

    设置数据区如下:

   x86汇编语言实践(3)

 数据分别表示

  • LEN 字符串长

  • N 外循环次数

  • X,Y,Z  执行A/a操作时的乘数和结果

  • STRING1,STRING2 待操作的两个字符串

  • CHAR 待寻找的字符串

  • OP 读入的操作指令符

  • NL 回车换行标志

  • MSGEQ,MSGGT,MSGLT,DOFOUND,NOTFOUND 输出提示信息

  运行程序,得到如下结果

   x86汇编语言实践(3)

  显然,运行结果符合预期。