以下”数字>…>”代表层次结构。
1>首先在数据段中存入三个字节长度的变量,初始值为0,分别用来计数找到的数字的个数、字母的个数、其它字符的个数。
1>进入代码段后,先指明段关联关系,然后根据END指明的程序入口执行程序。首先用AX寄存器过渡把段地址装入段寄存器,然后立即进入主循环。
2>>在主循环中,调用21H号中断的01H号用DOS输入并回显一个字符,将其ASCII码放在AL里。然后判断这个字符是不是回车(0DH),如果遇到输入了回车就直接跳转到程序的ENDR段。
3>>>在ENDR段中,每次CALL(给出子程序地址的调用方式)调用FUN_NEWLINE输出换行回车,然后调用数字/字母/其它字符对应的输出函数。
4>>>>建立这样的函数的目的是因为保存下来的计数值如果直接输出,将会输出其值对应的ASCII码字符,却不能按照十进制的方式显示。在每个这样的函数中,首先把其值给AX作为被除数,然后除以100(放入寄存器,这里用的BL),然后调用了一个FUN_OUTER函数。
5>>>>>在FUN_OUTER函数中,首先把这位值(本次商值,在AL里)加上字符’0’对应的ASCII码准备输出(因为输出时总是输出ASCII码),在输出时因为21H号中断总要用到AH寄存器指明做什么事,而我们现在的AH里又存了本次除法的余数值,要备份它,我这里备份给了寄存器DH,然后输出。输出后将余数(本来是AH里的,现在备份到了DH里)重新拼成16位数给AX作为下次除法的被除数。
4>>>>RET到FUN_ABC/NUM/OTR调用FUN_OUTER的位置,继续除以10,调用FUN_OUTER,除以1,调用FUN_OUTER…。
3>>>RET到ENDR代码块中,对于剩下的没输出的变量继续这样进行输出,最终程序将结束。
2>>对于输入值不是回车的情况,用CMP按照ASCII码表的顺序判断它是数字、字母还是其他字符,分别跳转到各自的段中去。
3>>>对于这三个段,会使用INC将变量值加1,然后JMP无条件跳转回主循环中,继续输入->判断->跳转。
DATAS SEGMENT
HINT_NUM DB 00H ;存16进制数:数字的个数
HINT_ABC DB 00H ;存16进制数:字母的个数
HINT_OTR DB 00H ;存16进制数:其它的个数
DATAS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START:
;段地址装入段寄存器,用AX过渡
MOV AX,DATAS
MOV DS,AX
;主循环:输入一个字符一个字符输入
XUN1:
MOV AH,1
INT 21H ;调用DOS输入,输入后放在AL里
CMP AL,0DH ;判断是不是回车
JE ENDR ;如果是,跳转到ENDR
CMP AL,30H ;'0'
JB OTHER ;比'0'小属其它
CMP AL,39H ;'9'
JBE NUMBER ;'0'~'9'属数字
CMP AL,41H ;'A'
JB OTHER
CMP AL,5AH ;'Z'
JBE WORD1
CMP AL,61H ;'a'
JB OTHER
CMP AL,7AH ;'z'
JBE WORD1
JMP OTHER ;剩下的都算其它字符
;调试块(执行不经过)
TIAOSHI:
;输出显示
MOV DL,AL
MOV AH,2
INT 21H
;HINT_NUM++
MOV DL,HINT_NUM
INC DL
MOV HINT_NUM,DL
JMP XUN1
;是数字
NUMBER:
INC HINT_NUM
JMP XUN1
;是字母
WORD1:
INC HINT_ABC
JMP XUN1
;是其它
OTHER:
INC HINT_OTR
JMP XUN1
;终
ENDR:
;输出换行回车
CALL FUN_NEWLINE
;输出数字数目
CALL FUN_NUM
;输出换行回车
CALL FUN_NEWLINE
;输出字母数目
CALL FUN_ABC
;输出换行回车
CALL FUN_NEWLINE
;输出其它数目
CALL FUN_OTR
;输出换行回车
CALL FUN_NEWLINE
;必须有,否则MASM FOR WIN不能正常结束
MOV AX,4C00H
INT 21H
;函数:NUM数转10进制变ASCII码输出
FUN_NUM PROC NEAR
;拼成16位给AX作为被除数
MOV AH,00H
MOV AL,HINT_NUM
;以下每次DIV(无符号除法)后,余数在AH,商在AL
MOV BL,100 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,10 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,1 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
RET ;返回调用者
FUN_NUM ENDP
;函数:ABC数转10进制变ASCII码输出
FUN_ABC PROC NEAR
;拼成16位给AX作为被除数
MOV AH,00H
MOV AL,HINT_ABC
;以下每次DIV(无符号除法)后,余数在AH,商在AL
MOV BL,100 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,10 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,1 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
RET ;返回调用者
FUN_ABC ENDP
;函数:OTR数转10进制变ASCII码输出
FUN_OTR PROC NEAR
;拼成16位给AX作为被除数
MOV AH,00H
MOV AL,HINT_OTR
;以下每次DIV(无符号除法)后,余数在AH,商在AL
MOV BL,100 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,10 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,1 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
RET ;返回调用者
FUN_OTR ENDP
;函数:输出换行
FUN_NEWLINE PROC NEAR
;输出换行
MOV DL,0AH
MOV AH,2
INT 21H
;输出回车
MOV DL,0DH
MOV AH,2
INT 21H
RET ;返回调用者
FUN_NEWLINE ENDP
;函数:输出本位,并把余数当成被除数(即拼成16位给AX)
FUN_OUTER PROC NEAR
ADD AL,30H ;商值AL变ASCII
MOV DL,AL
;关键!因为输出时要用AH,而它又存了余数
MOV DH,AH ;因此,这里用DH暂存其值
MOV AH,2
INT 21H
;余数AH(现在值在DH里)拼成16位给AX作被除数
MOV AL,DH
MOV AH,00H
RET ;返回调用者
FUN_OUTER ENDP
CODES ENDS ;代码段结束
END START ;指明程序入口
此外,如果要去掉每个输出值前面的0,可以指定一个变量作为标志,在三次输出中分别先设置标志是0,从开始判断这个商是不是0以及标志是不是0,如果两个条件都满足就直接RET不做输出,并保持这个标志是0,一旦出现商不是0的情况,就把标志设置为1,这样在以后的判断中就能保证此后的值都是有效的,要输出的。并且因为有两个判断条件,所以即便是60这样的0也会正确输出。
下面是具有去掉0功能的代码。
DATAS SEGMENT
HINT_NUM DB 00H ;存16进制数:数字的个数
HINT_ABC DB 00H ;存16进制数:字母的个数
HINT_OTR DB 00H ;存16进制数:其它的个数
FLAG DB 00H ;标志位:判断是否要输出
DATAS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START:
;段地址装入段寄存器,用AX过渡
MOV AX,DATAS
MOV DS,AX
;主循环:输入一个字符一个字符输入
XUN1:
MOV AH,1
INT 21H ;调用DOS输入,输入后放在AL里
CMP AL,0DH ;判断是不是回车
JE ENDR ;如果是,跳转到ENDR
CMP AL,30H ;'0'
JB OTHER ;比'0'小属其它
CMP AL,39H ;'9'
JBE NUMBER ;'0'~'9'属数字
CMP AL,41H ;'A'
JB OTHER
CMP AL,5AH ;'Z'
JBE WORD1
CMP AL,61H ;'a'
JB OTHER
CMP AL,7AH ;'z'
JBE WORD1
JMP OTHER ;剩下的都算其它字符
;调试块(执行不经过)
TIAOSHI:
;输出显示
MOV DL,AL
MOV AH,2
INT 21H
;HINT_NUM++
MOV DL,HINT_NUM
INC DL
MOV HINT_NUM,DL
JMP XUN1
;是数字
NUMBER:
INC HINT_NUM
JMP XUN1
;是字母
WORD1:
INC HINT_ABC
JMP XUN1
;是其它
OTHER:
INC HINT_OTR
JMP XUN1
;终
ENDR:
;输出换行回车
CALL FUN_NEWLINE
;输出数字数目
CALL FUN_NUM
;输出换行回车
CALL FUN_NEWLINE
;输出字母数目
CALL FUN_ABC
;输出换行回车
CALL FUN_NEWLINE
;输出其它数目
CALL FUN_OTR
;输出换行回车
CALL FUN_NEWLINE
;必须有,否则MASM FOR WIN不能正常结束
MOV AX,4C00H
INT 21H
;函数:NUM数转10进制变ASCII码输出
FUN_NUM PROC NEAR
;标志位清0
MOV FLAG,0
;拼成16位给AX作为被除数
MOV AH,00H
MOV AL,HINT_NUM
;以下每次DIV(无符号除法)后,余数在AH,商在AL
MOV BL,100 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,10 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,1 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
RET ;返回调用者
FUN_NUM ENDP
;函数:ABC数转10进制变ASCII码输出
FUN_ABC PROC NEAR
;标志位清0
MOV FLAG,0
;拼成16位给AX作为被除数
MOV AH,00H
MOV AL,HINT_ABC
;以下每次DIV(无符号除法)后,余数在AH,商在AL
MOV BL,100 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,10 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,1 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
RET ;返回调用者
FUN_ABC ENDP
;函数:OTR数转10进制变ASCII码输出
FUN_OTR PROC NEAR
;标志位清0
MOV FLAG,0
;拼成16位给AX作为被除数
MOV AH,00H
MOV AL,HINT_OTR
;以下每次DIV(无符号除法)后,余数在AH,商在AL
MOV BL,100 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,10 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
MOV BL,1 ;除数不妨给BL
DIV BL
CALL FUN_OUTER ;输出这次商,并把余数当成被除数(即拼成16位给AX)
RET ;返回调用者
FUN_OTR ENDP
;函数:输出换行
FUN_NEWLINE PROC NEAR
;输出换行
MOV DL,0AH
MOV AH,2
INT 21H
;输出回车
MOV DL,0DH
MOV AH,2
INT 21H
RET ;返回调用者
FUN_NEWLINE ENDP
;函数:输出本位,并把余数当成被除数(即拼成16位给AX)
FUN_OUTER PROC NEAR
CMP FLAG,0 ;判断FLAG是不是0
JNE NOZERO ;不是0才跳转到NOZERO
CMP AL,0 ;判断AL是否是0
JNE SET1 ;如果不是0,跳入SET1置1并进入NOZERO
;如果两个条件都是0,只要拼好然后返回
MOV AL,AH
MOV AH,00H
RET
SET1:
MOV FLAG,1 ;FLAG置1
NOZERO: ;不是0的话必然要输出了
ADD AL,30H ;商值AL变ASCII
MOV DL,AL
;关键!因为输出时要用AH,而它又存了余数
MOV DH,AH ;因此,这里用DH暂存其值
MOV AH,2
INT 21H
;余数AH(现在值在DH里)拼成16位给AX作被除数
MOV AL,DH
MOV AH,00H
RET ;返回调用者
FUN_OUTER ENDP
CODES ENDS ;代码段结束
END START ;指明程序入口