之前已经完成了X86的基本编程与串操作编程,此次作业是完成完成X86函数的初步调用,并完成一个函数指针数组,使用函数表进行相关的函数调用。
一、进制转换与乘除运算
此次完成一个集合程序,将十六进制转为十进制,十进制转十六进制,二进制转十进制和乘除运算作为函数可供主程序循环调用,并将这些子程序集合在一个函数指针数组内。
这些函数都不难完成,现在只需要在数据段中进行设置即可将他们映射到函数表里
首先需要在数据段中生命相关空间,5各函数指针使用5个32位DW存储即可
STACK SEGMENT PARA STACK
STACK_AREA DW 100H DUP(?)
STACK_BTM EQU $ - STACK_AREA
STACK ENDS
DATA SEGMENT PARA
FUNCTAB DW 5 DUP(0)
TABLEN DB 0
SELECT_MSG DB 'SELECT FUNCTION: 0 EXIT 1 HEC2DEC 2 DEC2HEC 3 BIN2DEC 4 MUL 5 DIV$'
NEWLINE DB 0DH,0AH,'$'
DATA ENDS
之后需要考虑如何将这些子函数与数组对应,只需要将这些函数的有效地址存到数组即可
MOV FUNCTAB:OFFSET,OFFSET FUNCION
按照这样的思路,就可以存储函数指针和循环调用函数指针的设计:
MAIN PROC FAR
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV AX,STACK
MOV SS,AX
MOV SP,STACK_BTM
MOV SI,OFFSET FUNCTAB ;初始化函数表
MOV BX,OFFSET FUNCTION_HEX2DEC
MOV [SI],BX
INC TABLEN
MOV BX,OFFSET FUNCTION_DEC2HEX
MOV [SI + 2],BX
INC TABLEN
MOV BX,OFFSET FUNCTION_BIN2DEC
MOV [SI + 4],BX
INC TABLEN
MOV BX,OFFSET FUNCTION_MULT
MOV [SI + 6],BX
INC TABLEN
MOV BX,OFFSET FUNCTION_DIV
MOV [SI + 8],BX
INC TABLEN
PUSH SI
MAIN_LOOP:
LEA DX,SELECT_MSG
MOV AH,09H
INT 21H
PRINT_LINE
MOV AH,1
INT 21H
SUB AL,'0'
XOR AH,AH
CMP AX,0
JZ EXIT
DEC AX
MOV BX,AX
SHL BX,1 ;BX X 2
PRINT_LINE
CALL [SI + BX]
PRINT_LINE
JMP MAIN_LOOP
EXIT: MOV AX,4C00H
INT 21H
MAIN ENDP
那么现在只需要补充我们想要的相关函数即可,由于前几次已经完成了类似的设计,这一部分还是比较轻松的
首先是读取一个16进制数到AX中,因为对于16进制,读取高位数字仅仅左移操作即可完成,仅需要做一些ASCII码到数值的转换即可。ANS = (ANS << 4) + C
READ_HEX PROC ;读16进制数到AX
PUSH SI
MOV SI,0
READ_HEX_LP1:
MOV AH,1
INT 21H
CMP AL,0DH ;判断是否位回车
JE READ_HEX_RETURN
CMP AL,'9'
JA READ_HEX_L2
READ_HEX_L1:
XOR AH,AH
SUB AX,30H
SAL SI,4
ADD SI,AX
JMP READ_HEX_LP1
READ_HEX_L2:
XOR AH,AH
SUB AX,'A' - 10
SAL SI,4
ADD SI,AX
JMP READ_HEX_LP1
READ_HEX_RETURN:
MOV AX,SI
POP SI
RET
READ_HEX ENDP
类比于读取16进制,完成读取二进制,读取一位后将ANS左移一位再加刚刚读取的数值即可
读取十进制,不能用移位来实现,使用乘法也不难完成
READ_BIN PROC
PUSH SI
MOV SI,0
READ_BIN_LP1:
MOV AH,1
INT 21H
CMP AL,0DH
JE READ_BIN_RETURN
SHL SI,1
CMP AL,'0'
JZ READ_BIN_LP1 ;读数为0直接下一次循环
INC SI
JMP READ_BIN_LP1
READ_BIN_RETURN:
MOV AX,SI
POP SI
RET
READ_BIN ENDP
READ_DEC PROC ;读十进制数到AX
PUSH SI
PUSH DX
PUSH BX
MOV SI,0
MOV BX,10
READ_DEC_LP1:
MOV AH,1
INT 21H
CMP AL,0DH
JE READ_DEC_RETURN
CMP AL,30H
JB READ_DEC_LP1
CMP AL,39H
JA READ_DEC_LP1
SUB AL,30H
XOR AH,AH
PUSH AX
MOV AX,SI
MUL BX
MOV SI,AX
POP AX
ADD SI,AX
LOOP READ_DEC_LP1
READ_DEC_RETURN:
MOV AX,SI
POP BX
POP DX
POP SI
RET
READ_DEC ENDP
接下来考虑如何将16进制数AX转换成各种进制输出。
对于十进制,可以考虑整除10^N来得出商和余数来完成,对于而十六进制,只需要每次读取最高4位显示即可
根据以上思路得出相关代码
DISP_NUM MACRO ;宏定义,输出十进制ASCII码
PUSH DX
MOV AH,2
MOV DL,AL
ADD DL,30H
INT 21H
POP DX
DISP_NUM ENDM
DIVIDE MACRO WNUM1,WNUM2
MOV AX,WNUM1
MOV BX,WNUM2
XOR DX,DX
DIV BX
DIVIDE ENDM
DISP_DEX PROC ;显示AX十进制数
PUSH DX
PUSH AX
XOR DX,DX
DIVIDE AX,10000
DISP_NUM ; 商在AX中,余数在DX中
DIVIDE DX,1000
DISP_NUM
DIVIDE DX,100
DISP_NUM
DIVIDE DX,10
DISP_NUM
MOV AL,DL
DISP_NUM
POP AX
POP DX
RET
DISP_DEX ENDP
DISP_HEX PROC ;显示16进制形式输出AX,32位二进制数同理
MOV BX,AX
MOV CX,4
DISP_HEX_LP1:
PUSH CX
MOV CL,4
ROL BX,CL
MOV AL,BL
AND AL,0FH
ADD AL,30H
CMP AL,39H
JBE DISP_HEX_DIS
ADD AL,'A' - '9' - 1
DISP_HEX_DIS: MOV DL,AL
MOV AH,2
INT 21H
POP CX
LOOP DISP_HEX_LP1
RET
DISP_HEX ENDP
最后补上乘除操作和集合的代码块,得到我们想要的结果
FUNCTION_DEC2HEX PROC
CALL READ_DEC
PRINT_LINE
CALL DISP_HEX
RET
FUNCTION_DEC2HEX ENDP
FUNCTION_HEX2DEC PROC
CALL READ_HEX
PRINT_LINE
CALL DISP_DEX
RET
FUNCTION_HEX2DEC ENDP
FUNCTION_BIN2DEC PROC
CALL READ_BIN
PRINT_LINE
CALL DISP_DEX
RET
FUNCTION_BIN2DEC ENDP
FUNCTION_MULT PROC
CALL READ_HEX
MOV DX,AX
CALL READ_HEX
MUL DX
XCHG DX,AX
CALL DISP_HEX
XCHG DX,AX
CALL DISP_HEX
RET
FUNCTION_MULT ENDP
FUNCTION_DIV PROC
CALL READ_HEX
MOV DX,AX
CALL READ_HEX
XCHG DX,AX
DIV DX
XCHG DX,AX
CALL DISP_HEX
XCHG DX,AX
CALL DISP_HEX
RET
FUNCTION_DIV ENDP
进行测试
通过
二、串相关操作集合程序
既然已经将一些数值运算集合到了一个程序中,不妨将P3部分中完成的串操作做一次集合
运行结果如下
则将字符串查找与替换、字符串的排序集合到一个程序中,之前已经完成了大部分工作,细数现在需要做的工作
1、完成函数映射的框架
2、因为需要循环调用,需要完成一个字符串的输入函数
首先完成框架设计
STACK SEGMENT PARA STACK
STACK_AREA DW 100H DUP(?)
STACK_BTM EQU $ - STACK_AREA
STACK ENDS
DATA SEGMENT PARA
FUNCTAB DW 5 DUP(0)
TABLEN DB 0
SELECT_MSG DB 'SELECT FUNCTION: 0 EXIT 1 SORT 2 REPLACE$'
NEWLINE DB 0DH,0AH,'$'
TR_BUFF DB 200H DUP(0) ;缓冲区
STR_NUM DW 0
STR_LEN Dw 0
STR_CPY DB 20H DUP(0) ;备份区域
CMP_FLAG DW 0
STR_ARR DW 0FFH DUP(0)
DST DB 100 DUP(0)
DST_LEN DW 0
SEL DB 100 DUP(0)
DST DW 0
DATA ENDS
CODE SEGMENT PARA
ASSUME DS:DATA,CS:DATA,SS:STACK,ES:DATA
MAIN PROC FAR
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV AX,STACK
MOV SS,AX
MOV SP,STACK_BTM
MOV SI,OFFSET FUNCTAB ;初始化函数表
MOV BX,OFFSET FUNCTION_SORT
MOV [SI],BX
INC TABLEN
MOV BX,OFFSET FUNCTION_REPLACE
MOV [SI + 2],BX
INC TABLEN
MOV BX,OFFSET FUNCTION_UPPER
MOV [SI + 4],BX
INC TABLEN
PUSH SI
MAIN_LOOP:
LEA DX,SELECT_MSG
MOV AH,09H
INT 21H
PRINT_LINE
MOV AH,1
INT 21H
SUB AL,'0'
XOR AH,AH
CMP AX,0
JZ EXIT
DEC AX
MOV BX,AX
SHL BX,1 ;BX X 2
PRINT_LINE
CALL [SI + BX]
PRINT_LINE
JMP MAIN_LOOP
EXIT: MOV AX,4C00H
INT 21H
MAIN ENDP
CODE ENDS
END MAIN
再补充输入函数,功能为将字符串输入到SI 所指的空间中
SORT_RETURN:
RET
STR_SORT ENDP
READ_STR PROC
PUSH DX
PUSH CX
MOV SI,DI
MOV AH,0AH
INT 21H
XOR CX,CX
XOR BX,BX
CLD
READ_STR_LEN_LP1: ;统计字符串长度
LODSB
CMP AL,00H
JZ READ_STR_LEN_END
INC CX ;统计字符串长度
CMP AL,' '
JNZ SHORT READ_STR_LEN_LP1 ;统计空格的次数,即单词数量
INC BX
PUSH BX
SHL BX,1
ADD BX,OFFSET STR_ARR ;将每处的索引位置存入到STR_ARR中
MOV DS:[BX],SI
POP BX
JMP SHORT READ_STR_LEN_LP1
READ_STR_LEN_END:
PUSH SI
MOV SI,OFFSET STR_LEN
MOV DS:[SI],CX ;已经统计出输入字符串的长度
MOV SI,OFFSET STR_NUM
MOV DS:[SI],BX ;已经统计出输入字符串的数量
POP SI
POP CX
POP DX
RET
READ_STR ENDP
将之前做的串处理函数引入其中,完成
STR_SORT PROC
SORT_LP1:
MOV SI, OFFSET STR_BUFF
MOV CX, STR_NUM ;单词个数
DEC CX ;使用CX计数
XOR BX,BX ;BX为当前单词索引
MOV CMP_FLAG,0
SORT_LP2:
MOV AX,BX
SHL AX,1
MOV SI,AX
ADD SI,OFFSET STR_ARR
MOV DI,SI
MOV DX,[SI]
MOV SI,DX
MOV DX,[DI + 2]
MOV DI,DX ;DI SI为当前索引
CALL STRCMP ;比较两字符串
JNZ SORT_CONTINUE
CALL CHG_STR
MOV CMP_FLAG,1 ;设立FLAG表示已交换
SORT_CONTINUE:
INC BX
LOOP SORT_LP2
MOV AX,CMP_FLAG
CMP AX, 1
JZ SORT_RETURN
JMP SORT_LP1
SORT_RETURN:
RET
STR_SORT ENDP
FIND PROC ;查找字符串
PUSH SI
PUSH DI
MOV DI,OFFSET SEL
FIND_LP1:
CLD
CMPSB
JNZ FIND_END ;找出不同
MOV BX,SEL_LEN ;匹配完成dst
MOV AX,OFFSET SEL
ADD BX,AX
CMP BX,DI
MOV AX,1
JZ FIND_RET
MOV BX,SRC_LEN ;SRC的末尾
CMP SI,BX
JA FIND_END
JMP FIND_LP1
FIND_END:
MOV AX,0 ;用AX做返回值
FIND_RET:
POP DI
POP SI
RET
FIND ENDP
TRANSLATE PROC
PUSH SI
PUSH DI
MOV AX,SEL_LEN
MOV BX,DST_LEN
CMP AX,BX ;AX > BX 向前平移
JA TRAN_CLD
TRAN_STD:
STD ;向后平移,向前复制
ADD AX,SI ;AX为目的
DEC AX
MOV BX,AX
MOV SI,(OFFSET SRC) + SRC_LEN - 1
MOV DI,SI
SUB DI,SEL_LEN
ADD DI,DST_LEN
TRAN_LOOP1:
LODSB
STOSB
CMP SI,BX
JA TRAN_LOOP1
JMP TRAN_END
TRAN_CLD: ;向前平移,向后复制
MOV DI,SI
ADD DI,DST_LEN
ADD SI,SEL_LEN
MOV BX,(OFFSET SRC) + SRC_LEN
CLD
TRAN_LOOP2:
LODSB
STOSB
CMP SI,BX
JB TRAN_LOOP2
TRAN_END:
POP DI
POP SI
RET
TRANSLATE ENDP
STRCPY PROC
PUSH SI
MOV DI,SI
MOV SI,OFFSET DST
MOV BX,(OFFSET DST) + DST_LEN
CLD
CPY_LOOP: LODSB
STOSB
CMP SI,BX
JB CPY_LOOP
POP SI
RET
STRCPY ENDP
REPLACE PROC
MOV SI,OFFSET SRC
MOV DI,OFFSET SEL
REPLACE_LP1:
XOR AX,AX
CALL FIND
INC SI
CMP AX,0
JZ REPLACE_LP1
DEC SI
CALL TRANSLATE
CALL STRCPY
REPLACE ENDP
NEXT:递归调用与堆栈传参