实现函数表对于我们理解函数调用的实质以及函数调用时堆栈的应用有很大的帮助。
进制转化、乘除运算的函数表实现
功能简介:
程序实现函数表功能有十六进制转十进制,十进制转十六进制,
乘法,除法,二进制转十进制,并以交互式选项的方式一一呈现。
这里展示一下程序运行的截图:
代码演示:
;stack segment define
STACK1 SEGMENT PARA STACK
STACK_AREA DW 100H DUP(?)
STACK_BTM EQU $ - STACK_AREA
STACK1 ENDS
DATA1 SEGMENT
RES_BCD DB 10 DUP(0) ;存储乘法结果的BCD码
OPR1 DW 0 ;操作数1
RES DW 2 DUP(0) ;结果
BUF DB 6 ;最大6个字符,65536\n
DB ? ;实际输入字符个数
DB 6 DUP(?) ;存放字符
; 定义函数指针数组
FUNC DW 5 DUP(?)
TABLEN DB 0
MIAN_ERR DB 'Index out of range!', '$'
MAIN_MSG DB 'Please input your choice(0.exit;1.DEC2HEX;2.HEX2DEC;3.MULTPLY;4.DIVIDE): ', '$'
IN_DEC_MSG DB 'Please input a number in DEC: ','$'
OUT_HEX_MSG DB 'The number in HEX is: ','$'
IN_HEX_MSG DB 'Please input a number in HEX: ','$'
OUT_DEC_MSG DB 'The number in DEC is: ','$'
DIV_0_MSG DB 'Divided by ZERO!', '$'
DATA1 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1, DS:DATA1, SS:STACK1, ES:DATA1
;The first choice DEC2HEX
DECTOHEX PROC
LEA DX, IN_DEC_MSG
CALL HINTS
CALL NEWLINE
CALL READ_DEC ;读一个数到AX中
MOV BX, AX
CALL NEWLINE
LEA DX, OUT_HEX_MSG
CALL HINTS
CALL DISP_16 ;将BX中的数以16进制显示
RET
DECTOHEX ENDP
;The sceond choice HEXTODEC
HEXTODEC PROC
LEA DX, IN_HEX_MSG
CALL HINTS
CALL NEWLINE
CALL READ_HEX ;读一个数到AX中
CALL NEWLINE
LEA DX, OUT_DEC_MSG
CALL HINTS
CALL DISP_10
RET
HEXTODEC ENDP
;The third choice MULTPLY
MULTPLY PROC
CALL READ_DEC
MOV OPR1, AX
CALL NEWLINE
MOV DL, 'x'
MOV AH, 2
INT 21H
CALl NEWLINE
CALL READ_DEC
MUL OPR1
CALL NEWLINE
MOV WORD PTR RES, AX
MOV WORD PTR RES+2, DX
CALL DISP_BCDEC
RET
MULTPLY ENDP
;The fourth choice DIVIDE
DIVIDE PROC
CALL READ_DEC
MOV OPR1, AX
CALL NEWLINE
MOV DL, '/'
MOV AH, 2
INT 21H
CALl NEWLINE
CALL READ_DEC
CMP AX, 0
JZ DIV_0_ERR
CALL NEWLINE
MOV BX, AX
MOV AX, OPR1
XOR DX, DX
DIV BX
CALL DISP_10
JMP RET_DIV
DIV_0_ERR: CALL NEWLINE
LEA DX, DIV_0_MSG
CALL HINTS
;CALL NEWLINE
RET_DIV: RET
DIVIDE ENDP
;十进制显示32bit数
DISP_BCDEC PROC NEAR
PUSH DS
PUSH SI
mov ax, DATA1
mov ds, ax
mov cx, 32 ;要进行十六次移位,利用adc获得
XOR AX, AX
XOR BX, BX
XOR DX, DX
loop_wai: shl RES, 1 ;得到 var 的 Bi 位
RCL RES+2, 1
; LEA BX, RES_BCD
; ADD BX, 9
mov bx, 9
push cx
mov cx, 10
loop_nei: mov al, [bx] ;执行 buf*2 + Bi 操作
adc al, al
aaa ;非压缩格式 BCD 码调整
mov [bx], al
dec bx
loop loop_nei ;内循环为 9 次
pop cx
loop loop_wai ;外循环为 32 次
MOV CX, 10
MOV SI, OFFSET RES_BCD
PREFIX_0: MOV DL, [SI]
CMP DL, 0H
JNZ DISP_DEC
INC SI
DEC CX
JZ DISP_ZERO
JMP PREFIX_0
DISP_DEC: MOV DL, [SI]
ADD DL, 30H
MOV AH, 2
INT 21H
INC SI
LOOP DISP_DEC
JMP DISP_D
DISP_ZERO: MOV AH, 2
MOV DL, 30H
INT 21H
DISP_D: MOV AH, 2
MOV DL, 44H
INT 21H
POP SI
POP DS
RET
DISP_BCDEC ENDP
;读一个16位十进制数到AX
READ_DEC PROC NEAR
PUSH DS
PUSH SI
MOV AX, DATA1 ;缓冲区基地址
MOV DS, AX
LEA DX, BUF ;缓冲区首地址偏移
MOV AH, 0AH
INT 21H
MOV AX, 0
MOV CX, 10 ;作为固定数10
MOV SI, DX
ADD SI, 2
LP_R_D: MOV BL, [SI]
CMP BL, 0DH
JE BRK
MUL CX
AND BX, 00FFH
SUB BX, 30H
ADD AX, BX
INC SI
JMP LP_R_D
BRK: POP SI
POP DS
RET
READ_DEC ENDP
;读一个16位十六进制数到AX
READ_HEX PROC NEAR
PUSH DS
PUSH SI
MOV AX, DATA1 ;缓冲区基地址
MOV DS, AX
LEA DX, BUF ;缓冲区首地址偏移
MOV AH, 0AH
INT 21H
XOR AX, AX
MOV CX, 16 ;作为固定数10
MOV SI, DX
ADD SI, 2
LP_R_H: MOV BL, [SI]
CMP BL, 0DH
JE BRK_H
MUL CX
AND BX, 00FFH
SUB BX, 30H
ADD AX, BX
INC SI
JMP LP_R_H
BRK_H: POP SI
POP DS
RET
READ_HEX ENDP
;将AX中的数以10进制显示
DISP_10 PROC NEAR
MOV CX, 0
LP_10_real: MOV DX, 0
MOV BX, 10
DIV BX ;商在AX,余数在DX
PUSH DX ;余数入栈
INC CX
CMP AX, 0
JNZ LP_10_real
LP_10_num: POP AX
AND AL, 0FH
ADD AL, 30H
MOV AH, 2
MOV DL, AL
INT 21H
LOOP LP_10_num
MOV AH, 2 ;打印10进制末尾D
MOV DL, 44H
INT 21H
RET
DISP_10 ENDP
;将BX中的数以16进制显示
DISP_16 PROC NEAR
MOV CH, 4
LP_16_real: MOV CL, 4
ROL BX, CL ;循环左移四位将高四位存到低四位
MOV AL, BL ;
AND AL, 0FH ;取低四位
ADD AL, 30H
CMP AL, 3AH
JB LP_16_num
ADD AL, 7H
LP_16_num: MOV AH, 2
MOV DL, AL
INT 21H
DEC CH
JNZ LP_16_real
MOV AH, 2
MOV DL, 48H
INT 21H
RET
DISP_16 ENDP
HINTS PROC
PUSH AX
MOV AH, 9
INT 21H
POP AX
RET
HINTS ENDP
NEWLINE PROC
PUSH AX
PUSH DX
MOV AH, 2 ;打印回车
MOV DL, 0AH
INT 21H
POP DX
POP AX
RET
NEWLINE ENDP
MAIN PROC FAR
MOV AX, STACK1
MOV SS, AX
MOV SP, STACK_BTM
MOV AX, DATA1
MOV DS, AX
MOV ES, AX
; 初始化函数指针数组
MOV SI, OFFSET FUNC
MOV BX, OFFSET DECTOHEX
MOV [SI], BX
INC TABLEN
MOV BX, OFFSET HEXTODEC
MOV [SI+2], BX
INC TABLEN
MOV BX, OFFSET MULTPLY
MOV [SI+4], BX
INC TABLEN
MOV BX, OFFSET DIVIDE
MOV [SI+6], BX
INC TABLEN
;MOV BX, OFFSET BINTODEC
; MOV [SI+8], BX
; 根据输入数值求出目标函数在函数指针数组中的偏移量并进行跳转
;PUSH SI
MLOOP: LEA DX, MAIN_MSG
CALL HINTS
MOV AH, 1
INT 21H
CALL NEWLINE
SUB AL, 30H
CMP AL, 0
JZ EXIT
CMP AL, TABLEN
JBE REAL_MLP
LEA DX, MIAN_ERR
CALL HINTS
CALL NEWLINE
JMP MLOOP
REAL_MLP: DEC AL
SHL AL, 1
MOV BL, AL
AND BX, 00FFH
; POP SI
; PUSH SI
CALL [SI+BX]
CALL NEWLINE
JMP MLOOP
EXIT: MOV AX, 4C00H
INT 21H
MAIN ENDP
CODE1 ENDS
END MAIN
字符串处理的函数表实现
功能简介:
程序实现字符串的大小写转化、增加、查找、复制、删除、
替换、比较以及字典排序,并将这些操作以交互式集成为3个函数。
这里展示一下程序运行的截图:
代码演示:
;stack segment define
STACK1 SEGMENT PARA STACK
STACK_AREA DW 100H DUP(?)
STACK_BTM EQU $ - STACK_AREA
STACK1 ENDS
DATA0 SEGMENT
; 定义函数指针数组
FUNC DW 5 DUP(?)
TABLEN DB 0
MIAN_ERR DB 'Index out of range!', '$'
MAIN_MSG DB 'Please input your choice(0.exit;1.STRCPY;2.STRREP;3.DIC_SORT): ', '$'
IN_DEC_MSG DB 'Please input a number in DEC: ','$'
OUT_HEX_MSG DB 'The number in HEX is: ','$'
IN_HEX_MSG DB 'Please input a number in HEX: ','$'
OUT_DEC_MSG DB 'The number in DEC is: ','$'
DIV_0_MSG DB 'Divided by ZERO!', '$'
DATA0 ENDS
;data segment define
DATA1 SEGMENT
BUF DB 10H DUP(0)
SRC_CPY DB 'Hello, x86!'
DB '$'
LEN_CPY EQU $ - SRC_CPY
DST DB LEN_CPY DUP(0)
DATA1 ENDS
;data segment define
DATA2 SEGMENT
FLAG DW 0 ;默认前移
DIFF DW 0 ;子串和替换串长度一致
RPSTR_BUF DB 20H
RPLEN_REP DB ?
RPSTR DB 1EH DUP(?)
SBSTR_BUF DB 20H
SBLEN_REP DB ?
SBSTR DB 1EH DUP(?)
SRC_REP_BUF DB 0FFH
LEN_REP DB ?
SRC_REP DB 0FEH DUP(?)
BUFF_ DB 010H DUP(?)
DATA2 ENDS
;data segment define
DATA3 SEGMENT
IN_MSG DB 'Please input strings separated by SPACE and ended by ENTER:(only letters)','$'
OUT_MSG DB 'The dictionary sort result is:', '$'
TMP_BUF DB 10H DUP(0)
TMP_LEN DW 0
STR_ARR DB 0FFH
DB ?
STRS DB 0FCH DUP(0)
ARR_LEN DW 0
CMP_FLAG DW 0 ;0-不需要排序,1-需要排序
;INDEX_ARR DW 0FFH DUP(0)
DATA3 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1, DS:DATA0, SS:STACK1, ES:DATA0
;-------------------1----------------------
;The first choice of functab
STRCPY_IN PROC
PUSH DS
PUSH ES
PUSH SI
MOV AX, DATA1
MOV DS, AX
MOV SI, OFFSET SRC_CPY
MOV DI, OFFSET SRC_CPY
ADD DI, LEN_CPY
SUB DI, 3
MOV CX, LEN_CPY
CALL STRCPY
LEA DX, SRC_CPY
CALL HINTS
POP SI
POP ES
POP DS
RET
STRCPY_IN ENDP
;源地址SI,目的地址DI,拷贝长度CX
STRCPY PROC
PUSH ES ;维护ES
PUSH DS
POP ES ;ES与DS处于同一个段
MOV BX, SI ;暂存SI
CMP SI, DI
JZ BRK
JAE FORDER ;SI >= DI -> forward
ADD SI, CX
DEC SI
CMP SI, DI ;SI+LEN_CPY<=DI -> forward
JBE FORDER
BACKER: STD
ADD DI, CX
DEC DI
JMP CPY
FORDER: CLD
MOV SI, BX
JMP CPY
CPY: LODSB
STOSB
LOOP CPY
BRK: POP ES
RET
STRCPY ENDP
;-------------------1----------------------
;------------------2--------------------
;The second choice of functab
STRREP_IN PROC
PUSH DS
PUSH ES
PUSH SI
MOV AX, DATA2
MOV DS, AX
MOV ES, AX
LEA DX, SRC_REP_BUF ;SRC_REP缓冲区首地址偏移
CALL READ_STR
CALL NEWLINE
LEA DX, SBSTR_BUF ;SBSTR子串缓冲区首地址偏移
CALL READ_STR
CALL DISP_SP
LEA DX, RPSTR_BUF
CALL READ_STR
CALL NEWLINE
MOV CL, SBLEN_REP ;子串长度
AND CX, 00FFH
MOV BL, RPLEN_REP ;替换串长度
AND BX, 00FFH
CMP BX, CX
JBE FW
MOV FLAG, 1 ;1代表后移
SUB BX, CX
MOV DIFF, BX
JMP RP
FW: SUB CX, BX
MOV DIFF, CX
RP: CALL REP_STR
POP SI
POP ES
POP DS
RET
STRREP_IN ENDP
;将地址缓冲区首地址偏移存入DX
READ_STR PROC
PUSH AX
MOV AH, 0AH
INT 21H
POP AX
RET
READ_STR ENDP
REP_STR PROC
PUSH ES
PUSH DS
POP ES
MOV SI, OFFSET SRC_REP
MOV CL, LEN_REP
AND CX, 00FFH
LP_SRC_REP: PUSH CX
PUSH SI
MOV CL, SBLEN_REP ;获取子串长度
AND CX, 00FFH
MOV DI, OFFSET SBSTR
CLD
LP_CMP: CMPSB
JNZ CONTINUE
LOOP LP_CMP ;DS:[SI]==ES:[DI]->LP
POP BX ;匹配串初始位置
POP CX ;决定替换之后原串长度更新
SUB CL, SBLEN_REP ;预置CX
SBB CH, 0
INC CX
PUSH CX
PUSH BX
CMP FLAG, 0
JZ MVFW ;剩余的串前移
MVBK: ADD SI, CX
DEC SI
MOV DI, SI
ADD DI, DIFF
STD
REP MOVSB
JMP REPLACE
MVFW: MOV DI, SI
SUB DI, DIFF
CLD
REP MOVSB
REPLACE: POP DI
MOV CL, RPLEN_REP ;替换穿作为SI,原串作为DI
AND CX, 00FFH
MOV SI, OFFSET RPSTR
CLD
REP MOVSB
POP CX
MOV SI, DI
;LOOP LP_SRC_REP
JMP REAL_CNT
CONTINUE: POP SI
INC SI
POP CX
REAL_CNT: LOOP LP_SRC_REP
;显示处理结果
DEL_END: MOV BYTE PTR [SI], 24H
MOV AH, 9
MOV DX, OFFSET SRC_REP
INT 21H
POP ES
RET
REP_STR ENDP
DISP_SP PROC
PUSH AX
PUSH DX
MOV AH, 2 ;打印空格
MOV DL, 0AH
INT 21H
MOV AH, 2 ;打印箭头
MOV DL, 2DH
INT 21H
MOV AH, 2 ;
MOV DL, 3EH
INT 21H
MOV AH, 2 ;打印空格
MOV DL, 20H
INT 21H
POP DX
POP AX
RET
DISP_SP ENDP
;------------------2--------------------
;-----------------3---------------------
;将地址缓冲区首地址偏移存入DX
DICSORT_IN PROC
PUSH ES
PUSH DS
PUSH SI
MOV AX, DATA3
MOV DS, AX
MOV ES, AX
;input hints
LEA DX, IN_MSG
CALL HINTS
CALL NEWLINE
LEA DX, STR_ARR
CALL READ_CAPSTR ;读字符串并统一为大写
CALL DICSORT
MOV AH, 2 ;打印回车
MOV DL, 0AH
INT 21H
;ouput hints
LEA DX, OUT_MSG
CALL HINTS
CALL NEWLINE
;output disp
LEA DX, STRS
CALL HINTS
POP SI
POP DS
POP ES
RET
DICSORT_IN ENDP
DICSORT PROC
LP1_DIC: MOV AX, 1 ;标志位
LEA SI, STRS ;首个字符串起始index
MOV CX, ARR_LEN ;字符串的个数
DEC CX ;冒泡排序,比较n-1次
MOV DI, SI ;取两个index,DI为前字符串,SI为后字符串
LP2_DIC: ;MOV SI, [BX+2]
CALL GET_NEXT ;从SI指向的字符串开始,将下一个字符串的指针存在SI中
CALL DIC_CMP ;比较并按两个输入字符串,指针为DI,SI[不能改变DI,SI],结果存入CMP_FLAG
CMP CMP_FLAG, 0
JZ CONTINUE_DIC
CALL CHG_STR ;交换SI,DI的两个字符串
XOR AX, AX ;设立FLAG表示已交换
CONTINUE_DIC:
MOV DI, SI
LOOP LP2_DIC
CMP AX, 1
JZ EXIT_DIC
JMP LP1_DIC
EXIT_DIC: RET
DICSORT ENDP
READ_CAPSTR PROC
PUSH ES
PUSH DS
POP ES
MOV AH, 0AH
INT 21H
;LEA BX, INDEX_ARR
MOV SI, DX
INC SI ;加1获得len
MOV CL, [SI]
AND CX, 00FFH
INC SI ;位移到字符串起始地址
; MOV [BX], SI
; ADD BX, 2
MOV DI, SI
CLD
LP1_RD: LODSB
CMP AL, 'a'
JB CONTINUE_RD
CMP AL, 'z'
JA CONTINUE_RD
SUB AL, 20H
CONTINUE_RD:CMP AL, 20H
JNZ CONTINUE_R
INC ARR_LEN
;MOV [BX], SI
;ADD BX, 2
CONTINUE_R: STOSB
LOOP LP1_RD
MOV BYTE PTR [DI], 20H
; MOV BYTE PTR [DI+1], 0DH
MOV BYTE PTR [DI+1], '$'
INC ARR_LEN
POP ES
RET
READ_CAPSTR ENDP
CHG_STR PROC
PUSH ES
PUSH AX
PUSH CX
PUSH DI
PUSH SI
PUSH DS
POP ES
;前字符串拷贝到TMP中
MOV CX, SI
SUB CX, DI ;获得前字符串的长度
MOV TMP_LEN, CX
MOV SI, DI ;设定源地址
LEA DI, TMP_BUF
CALL STRCPY
;后字符串拷贝到前字符串中
POP SI ;将后字符串取出作为源地址
MOV AX, SI ;将SI暂存到AX中
POP DI ;前字符串取出作为目的地址
PUSH DI ;入栈保存
;PUSH SI
CALL GET_NEXT ;获得后字符串长度
MOV CX, SI
SUB CX, AX
MOV SI, AX ;将AX返还给SI
CALL STRCPY ;由于是++拷贝,故拷贝完之后的DI作为下一次拷贝的目的地址
PUSH DI ;DI是下一个字符串新的起始地址,即CONTINUE_DIC中应该更新的SI
;将前字符串拷贝到后字符串中
LEA SI, TMP_BUF
MOV CX, TMP_LEN
CALL STRCPY
POP SI
POP DI
POP CX
POP AX
POP ES
RET
CHG_STR ENDP
GET_NEXT PROC
PUSH ES
PUSH AX
PUSH DS
POP ES
CLD
LP_NEXT: LODSB
CMP AL, 20H ;若是单个字符串结尾,则+1记录为下一个字符串开始index
JNZ LP_NEXT
POP AX
POP ES
RET
GET_NEXT ENDP
;比较并按两个输入字符串,指针为DI,SI
DIC_CMP PROC
PUSH ES
PUSH AX
;PUSH BX
PUSH CX
PUSH SI
PUSH DI
PUSH DS
POP ES
MOV CX, SI
SUB CX, DI ;将前一个字符串的长度作为比较长度
CLD
MOV CMP_FLAG, 0 ;清空排序标志位(两个内存数不能操作)
LP_CMP_DIC: CMPSB
JZ CONTINUE_CMP
;JA CMP1
JA EXIT_CMP ;DI在前,SI在后,故采用JA
MOV CMP_FLAG, 1
JMP EXIT_CMP
CONTINUE_CMP:
LOOP LP_CMP_DIC
EXIT_CMP: POP DI
POP SI
POP CX
;POP BX
POP AX
POP ES
RET
DIC_CMP ENDP
;-----------------3---------------------
HINTS PROC
PUSH AX
MOV AH, 9
INT 21H
POP AX
RET
HINTS ENDP
NEWLINE PROC
PUSH AX
PUSH DX
MOV AH, 2 ;打印回车
MOV DL, 0AH
INT 21H
POP DX
POP AX
RET
NEWLINE ENDP
MAIN PROC FAR
MOV AX, STACK1
MOV SS, AX
MOV SP, STACK_BTM
MOV AX, DATA0
MOV DS, AX
MOV ES, AX
; 初始化函数指针数组
MOV SI, OFFSET FUNC
MOV BX, OFFSET STRCPY_IN
MOV [SI], BX
INC TABLEN
MOV BX, OFFSET STRREP_IN
MOV [SI+2], BX
INC TABLEN
MOV BX, OFFSET DICSORT_IN
MOV [SI+4], BX
INC TABLEN
; MOV BX, OFFSET DIVIDE
; MOV [SI+6], BX
; INC TABLEN
;MOV BX, OFFSET BINTODEC
; MOV [SI+8], BX
; 根据输入数值求出目标函数在函数指针数组中的偏移量并进行跳转
; PUSH SI
MLOOP: LEA DX, MAIN_MSG
CALL HINTS
MOV AH, 1
INT 21H
CALL NEWLINE
SUB AL, 30H
CMP AL, 0
JZ EXIT
CMP AL, TABLEN
JBE REAL_MLP
LEA DX, MIAN_ERR
CALL HINTS
CALL NEWLINE
JMP MLOOP
REAL_MLP: DEC AL
SHL AL, 1
MOV BL, AL
AND BX, 00FFH
; POP SI
; PUSH SI
CALL [SI+BX]
CALL NEWLINE
JMP MLOOP
EXIT: MOV AX, 4C00H
INT 21H
MAIN ENDP
CODE1 ENDS
END MAIN
以上便是关于函数表实现的x86程序演示,引用请注明出处(https://echo-ji.github.io/2018/05/14/x86-函数表/)。