x86汇编语言实践(2)
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段预置字典如下图所示
编译、链接并执行程序,得到结果如下,第一行为元单词表,第二行为排序后的单词表(按字典序升序)
显然,运行结果符合预期。