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 代码实现
STACK SEGMENT PARA STACK
DW 100H DUP(?)
STACK ENDS DATA SEGMENT PARA
NUM1 DB ?
NUM2 DB ?
RESULT DW ?
DATA ENDS CODE SEGMENT PARA
ASSUME CS:CODE,DS:DATA,SS:STACK GETNUM MACRO
MOV AH,
INT 21H
SUB AL,30H
ENDM MULTI MACRO N1,N2
MOV AL,N1
MOV BL,N2
MUL BL
ENDM DIVIDE MACRO N1,N2
MOV AX,N1
MOV BX,N2
XOR DX,DX
DIV BX
ENDM DISPNUM MACRO
PUSH DX
MOV AH,
MOV DL,AL
ADD DL,30H
INT 21H
POP DX
ENDM INPUT MACRO NUM
GETNUM
MULTI AL,
MOV NUM,AL
GETNUM
MULTI AL,
ADD NUM,AL
GETNUM
ADD NUM,AL
ENDM NEWLINE MACRO
MOV AH,
MOV DL,0DH
INT 21H
MOV AH,
MOV DL,0AH
INT 21H
ENDM OUTPUT PROC
DIVIDE AX,
DISPNUM
DIVIDE DX,
DISPNUM
DIVIDE DX,
DISPNUM
DIVIDE DX,
DISPNUM
MOV AL,DL
DISPNUM
RET
OUTPUT ENDP MAIN PROC FAR
MOV AX,DATA
MOV DS,AX
;get num1
INPUT NUM1
;getchar
GETNUM
XOR AX,AX
;get num2
INPUT NUM2
;num1 * num2
MULTI NUM1,NUM2
MOV RESULT,AX
;newline
NEWLINE
;output result
MOV AX,RESULT
CALL OUTPUT EXIT: MOV AX,4C00H
INT 21H
MAIN ENDP CODE ENDS
END MAIN
1-4 实现效果截图
经验证,发现输出结果符合预期。
2 字符串操作与跳转表练习
2-1 练习要点
- 字符串的操作,包括:字符串的拼接、比较、查找
- 子程序调用与宏
- 跳转表的使用
2-2 重点难点
- 子程序调用需要对子程序中使用到的变量进行压栈处理,以免变量污染
- 字符串操作通常都需要对ES:DI和DS:SI进行初始化
- 宏的内容不能有标签
2-3 实现思路
- 首先为输入和输出单独编写子程序,程序主体采用跳转表实现
- 为每一个条件单独编写一个子程序,有4个条件,因此共需编写4个子程序
2-4 代码实现
STACK SEGMENT PARA STACK
DW 100H DUP(?)
STACK ENDS DATA SEGMENT PARA
LEN EQU
MSG1 DB 'INPUT OPRAND:',,,'$' ;THE MSG TO OUTPUT
MSG2 DB 'INPUT STRING 1:',,,'$'
MSG3 DB 'INPUT STRING 2:',,,'$'
MSG4 DB '<','$'
MSG5 DB '=','$'
MSG6 DB '>','$'
MSG7 DB 'S3=','$'
MSG8 DB 'INPUT A CHAR:',,,'$'
MSG9 DB 'CHAR FOUND IN STR1!',,,'$'
MSG10 DB 'CHAR NOT FOUND IN STR1!',,,'$'
STR1 DB LEN-
DB ?
DB LEN DUP(?)
STR2 DB LEN-
DB ?
DB LEN DUP(?)
STR3 DB LEN-
DB ?
DB LEN DUP(?)
CHAR DB ?
OP DB ? ;THE OPRAND USER INPUT
DATA ENDS CODE SEGMENT PARA
ASSUME CS:CODE,DS:DATA,SS:STACK HINT MACRO MSG
LEA DX,MSG ;OUTPUT MSG1
MOV AH,09H
INT 21H
ENDM GETOP MACRO
MOV AH,
INT 21H
SUB AL,30H
MOV OP,AL
ENDM NEWLINE MACRO
PUSH AX
PUSH DX
MOV AH,
MOV DL,0DH
INT 21H
MOV AH,
MOV DL,0AH
INT 21H
POP DX
POP AX
ENDM GETCHAR MACRO
MOV AH,
INT 21H
MOV CHAR,AL
ENDM GETSTR1 PROC
MOV DX,OFFSET STR1
MOV AH,0AH
INT 21H
MOV CL,STR1+
XOR CH,CH
MOV SI,OFFSET STR1+
LP1: INC SI
LOOP LP1
MOV BYTE PTR [SI],'$'
RET
GETSTR1 ENDP GETSTR2 PROC
MOV DX,OFFSET STR2
MOV AH,0AH
INT 21H
MOV CL,STR2+
XOR CH,CH
MOV SI,OFFSET STR2+
LP2:INC SI
LOOP LP2
MOV BYTE PTR [SI],'$'
RET
GETSTR2 ENDP STRCAT PROC
PUSH AX
CLD
CAT_LP1:LODSB
STOSB
CMP AL,
JNZ CAT_LP1
POP AX
RET
STRCAT ENDP P2_PUTEND PROC
MOV CL,STR1+ ;SET CX TO LEN FOR LOOP
ADD CL,STR2+
XOR CH,CH
MOV SI,OFFSET STR3+ ;GET ACTUAL STRING
PLP2: INC SI
LOOP PLP2
MOV BYTE PTR [SI],'$' ;PUT END TO STRING
RET
P2_PUTEND ENDP
;STRCMP
P1 PROC
PUSH CX
CLD
PUSH SI
MOV CX,
SLP1: LODSB
CMP AL,00H
JZ SLP1_1
INC CX
JMP SHORT SLP1
SLP1_1: POP SI
REPE CMPSB
HINT STR1+
JA SL1
JB SL2
HINT MSG5
MOV AL,
JMP SHORT RETURN
SL1: HINT MSG6
MOV AL,
JMP SHORT RETURN
SL2: HINT MSG4
MOV AL,
RETURN: HINT STR2+
POP CX
RET
P1 ENDP
;STRCAT(S1,S2)+STRCPY(S3,S1)
P2 PROC
;STRCAT(S1,S2)
PUSH AX
MOV SI,OFFSET STR2+
MOV CL,STR1+
XOR CH,CH
MOV DI,OFFSET STR1+
CATLP1: INC DI
LOOP CATLP1
CALL STRCAT
;STRCPY(S3,S1)
MOV SI,OFFSET STR1+
MOV DI,OFFSET STR3+
CALL STRCAT
CALL P2_PUTEND
HINT MSG7
HINT STR3+
POP AX
RET
P2 ENDP
;STRCAT(S2,S1)+STRCPY(S3,S2)
P2_2 PROC
;STRCAT(S2,S1)
PUSH AX
MOV SI,OFFSET STR1+
MOV CL,STR2+
XOR CH,CH
MOV DI,OFFSET STR2+
CATLP2: INC DI
LOOP CATLP2
CALL STRCAT
;STRCPY(S3,S1)
MOV SI,OFFSET STR2+
MOV DI,OFFSET STR3+
CALL STRCAT
CALL P2_PUTEND
HINT MSG7
HINT STR3+
POP AX
RET
P2_2 ENDP
;STRFIND
P3 PROC
HINT MSG8
GETCHAR
NEWLINE
MOV DI,OFFSET STR1+
MOV CL,STR1+
XOR CH,CH
MOV AL,CHAR
CLD
REPNZ SCASB
JZ FOUND
HINT MSG10
JMP P3_RETURN
FOUND: HINT MSG9
P3_RETURN:RET
P3 ENDP
;SRTCMP+STRCAT+STRCPY
P4 PROC
MOV SI,OFFSET STR1+
MOV DI,OFFSET STR2+
CALL P1
NEWLINE
CMP AL,
JNE LA41
CALL P2
JMP CONTINUE4
LA41: CMP AL,
JNE LA42
CALL P2
JMP CONTINUE4
LA42: CMP AL,
JNE CONTINUE4
CALL P2_2
CONTINUE4:
RET
P4 ENDP SWITCH PROC
CMP OP,
JNE LA1
MOV SI,OFFSET STR1+
MOV DI,OFFSET STR2+
CALL P1
JMP CONTINUE
LA1: CMP OP,
JNE LA2
CALL P2
JMP CONTINUE
LA2: CMP OP,
JNE LA3
CALL P3
JMP CONTINUE
LA3: CMP OP,
JNE LAN
CALL P4
JMP CONTINUE
LAN: HINT MSG3
CONTINUE:RET
SWITCH ENDP MAIN PROC FAR
MOV AX,DATA
MOV DS,AX
MOV ES,AX ;input str1
HINT MSG2
CALL GETSTR1
NEWLINE
;input str2
HINT MSG3
CALL GETSTR2
NEWLINE
;input op
HINT MSG1
GETOP
NEWLINE
;SWITCH OP
CALL SWITCH EXIT: MOV AX,4C00H
INT 21H
MAIN ENDP CODE ENDS
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。
STACK SEGMENT PARA STACK
DW 100H DUP(?)
STACK ENDS DATA SEGMENT PARA
TABLE_LEN EQU
STR_LEN EQU
TABLE DB 'QIQI',20H,,'$'
DB 'CHEN',20H,,'$'
DB 'XIAN',20H,,'$'
DB 'QIQJ',20H,,'$'
DB 'XHAN',20H,,'$'
DB 'XIBN',20H,,'$'
DB 'XHQI',20H,,'$'
DB 'LOVE',20H,,'$'
DB 'SURE',20H,,'$'
TEMP DB STR_LEN DUP(?)
NEW_LINE DB 0DH,0AH,'$'
DATA ENDS CODE SEGMENT PARA
ASSUME CS:CODE,DS:DATA,SS:STACK NEWLINE MACRO
PUSH DX
PUSH AX
MOV DX,OFFSET NEW_LINE
MOV AH,
INT 21H
POP AX
POP DX
ENDM OUTPUT PROC
PART1:
MOV CX,TABLE_LEN
MOV SI,OFFSET TABLE
LP1:
MOV DX,SI
MOV AH,
INT 21H
ADD SI,STR_LEN
LOOP LP1
NEWLINE
RET
OUTPUT ENDP COMP PROC
COMPARE:
PUSH SI
PUSH CX
CLD
PUSH SI
MOV CX,
SLP1:
LODSB
CMP AL,00H
JZ SLP1_1
INC CX
JMP SHORT SLP1
SLP1_1:
POP SI
REPE CMPSB
JA SL1
JB SL2
MOV AL, ;SI=DI
JMP SHORT RETURN
SL1:
MOV AL, ;SI>DI
JMP SHORT RETURN
SL2:
MOV AL, ;SI<DI
RETURN:
POP CX
POP SI
RET
COMP ENDP STRCPY PROC
STRING_COPY:
PUSH SI
PUSH DI
PUSH AX
CLD
CPY_LP1:
LODSB
STOSB
CMP AL,
JNZ CPY_LP1
POP AX
POP DI
POP SI
RET
STRCPY ENDP EXCHG PROC
EXCHANGE:
PUSH SI
PUSH DI MOV DI,OFFSET TEMP
CALL STRCPY MOV DI,SI
ADD SI,STR_LEN
CALL STRCPY MOV DI,SI
MOV SI,OFFSET TEMP
CALL STRCPY
POP DI
POP SI
RET
EXCHG ENDP SORT PROC
PART2:
MOV CX,TABLE_LEN
DEC CX
LP2:
MOV BX,
MOV SI,OFFSET TABLE
PUSH CX LP2_1:
MOV AX,SI
ADD AX,STR_LEN
MOV DI,AX
CALL COMP
CMP AL,
JBE CONTINUE
CALL EXCHG
MOV BX,
CONTINUE:
ADD SI,STR_LEN
LOOP LP2_1 POP CX
DEC CX
CMP BX,
JZ SORT_RETURN
JMP SHORT LP2
SORT_RETURN:
RET
SORT ENDP MAIN PROC FAR
MAIN_PART:
MOV AX,DATA
MOV DS,AX
MOV ES,AX
;DISPLAY ORIGIN TABLE
CALL OUTPUT
;SORT
CALL SORT
;DISPLAY ORDERED TABLE
CALL OUTPUT EXIT:
MOV AX,4C00H
INT 21H
MAIN ENDP CODE ENDS
END MAIN
3-5 运行结果
在DATA段预置字典如下图所示
编译、链接并执行程序,得到结果如下,第一行为元单词表,第二行为排序后的单词表(按字典序升序)
显然,运行结果符合预期。