前言
微机原理的学习基本大部分内容都和汇编语言相关,以下是笔者第二次实验的相关实验内容。
一、实验内容
编写程序,将键盘接收到的四位十六进制数转换为等值的二进制数,再显示在屏幕上。若输入的不是0-F间的数字,则显示错误信息,并要求重新输入。
二、实验原理
在进行代码书写前我们首先需要知道,在汇编语言中INT 21H的调用是一个非常重要的命令,课程或者实验中经常需要用到其中的一些指令
而对于本门课程来说,编号01H(单字输入),02H(单字输出),09H(字符串输出),0AH(字符串输入)和4CH(程序结束)是几条比较常用的指令。在该程序中,调用的是0AH号功能进行输入。当然,题目要求的是接收四位,因此采用01H号功能是常规的选择,笔者这里是进行了一些拓展。
MOV AH,0AH
INT 21H
LEA SI,BUFFER1 ;输入字符串
INC SI
MOV CL,[SI] ;BUFFER1内第二个数为长度,控制循环
INC SI ;此时SI指向了字符串的首地址
接下来我们需要做的便是进行数值判断,如果是0-9之间的数字,无需处理,如果是大写A-F或者小写的a-f,进行统一的大小写转换,如果是其他的数值则抛出错误。
L1:
MOV BL,20H
OR [SI],BL ;将字符变成小写字母(设置第六位为1)
MOV AL,[SI]
CMP AL,'9' ;判断字符是否大于9
JA OVER_OCT
CMP AL,'0' ;判断字符是否小于0
JB INPUT_ERROR
JMP NORMAL ;在0~9之间,不进行其他操作
OVER_OCT:
CMP AL,'a' ;判断字符是否小于a
JB INPUT_ERROR
CMP AL,'f' ;判断字符是否大于f
JA INPUT_ERROR
在完成输入之后我们则需要进行数值处理,这是本次实验中比较关键的一个部分。有些代码可能采用移位的操作将单个数字转换成二进制数。笔者这里采用的是连除法,直接用进制转换的公式进行推导。
每次处理一个数将它除以二,所得的余数存于一个数组内,商继续转到下一次的除法中,直到四次除完。
譬如十六进制中的E,转换为十进制数字是14。14÷2=7余0,7÷2=3余1,3÷2=1余1,1÷2=0余1,我们只需要将这些余数存入数组,最后倒着输出即可得到E对应的二进制数1110。将数组显示出来后,对接下来的数字进行相同的操作即可。
SHOW PROC
MOV BL,[SI]
CMP BL,60H
JB NORMAL_OCT
MOV BL,7H
SUB [SI],BL ;根据所用的转换方法推导可得a~f需要减7H再减去60H
NORMAL_OCT:
MOV AL,[SI]
MOV SHU,AL ;保护AL
SUB AL,30H
MOV DI,OFFSET EMP
MOV CX,4 ;循环4次,除以4次2
D1:
MOV AH,0
MOV AL,SHU ;被除数放AX里
DIV TWO ;余数在AH,商在AL
MOV SHU,AL ;商留着放下一次除法循环里
ADD AH,30H
MOV DS:[DI],AH ;余数加30H放在EMP里面
INC DI
LOOP D1
MOV CX,4 ;单个数的BCD码是四位
D2:
DEC DI ;倒着输出EMP里的数
MOV DL,DS:[DI]
MOV AH,02H
INT 21H
LOOP D2
RET
SHOW ENDP
最后我们将所有代码片段结合起来。
…
三、实验代码
DATAS SEGMENT
BUFFER1 DB 101,?,101 DUP (?) ;第一个101表示最大长度为101个字符,?占用的那个字节在调用结束后会被放入实际输入的字符个数
;101 DUP(?)表示开辟101字节单元,存放输入的字符的ASCII码。
MES1 DB 0AH,0DH,'The code is: $' ;0A ASCII码表示换行,0D ASCII码表示回车
MES2 DB 0AH,0DH,'$' ;换行,清晰程序
MES3 DB 'Input your number: $'
MES4 DB 0AH,0DH,'Input error,please try again!',0AH,0DH,'$'
SHU DB ?
EMP DB 4 DUP (?) ;在此处理BCD码
TWO DB 2 ;定义TWO=2
DATAS ENDS
STACKS SEGMENT
DW 256 DUP (?) ;涉及到进入子程序的断点保护,需要入栈出栈
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
;初始化
MOV AX,DATAS
MOV DS,AX
MOV AX,STACKS
MOV SS,AX
MOV DX,OFFSET MES3
MOV AH,09H
INT 21H
;输入到缓冲区
LEA DX,BUFFER1
MOV AH,0AH
INT 21H
LEA SI,BUFFER1 ;输入字符串
INC SI
MOV CL,[SI] ;BUFFER1内第二个数为长度,控制循环
INC SI ;此时SI指向了字符串的首地址
MOV DX,OFFSET MES1
MOV AH,09H
INT 21H
;开始逐个处理输入的字符串
L1:
MOV BL,20H
OR [SI],BL ;将字符变成小写字母(设置第六位为1)
MOV AL,[SI]
CMP AL,'9' ;判断字符是否大于9
JA OVER_OCT
CMP AL,'0' ;判断字符是否小于0
JB INPUT_ERROR
JMP NORMAL ;在0~9之间,不进行其他操作
OVER_OCT:
CMP AL,'a' ;判断字符是否小于a
JB INPUT_ERROR
CMP AL,'f' ;判断字符是否大于f
JA INPUT_ERROR
NORMAL:
PUSH CX ;进入子程序前保护CX
CALL SHOW
POP CX
MOV DL,20H ;输出空格
MOV AH,02H
INT 21H
INC SI
LOOP L1
MOV DX,OFFSET MES2
MOV AH,09H
INT 21H
JMP DONE
DONE:
MOV AH,4CH ;程序在此结束
INT 21H
INPUT_ERROR:
MOV DX,OFFSET MES4 ;错误字符,重新跳转到开头输入
MOV AH,09H
INT 21H
JMP START
SHOW PROC
MOV BL,[SI]
CMP BL,60H
JB NORMAL_OCT
MOV BL,7H
SUB [SI],BL ;根据所用的转换方法推导可得a~f需要减7H再减去60H
NORMAL_OCT:
MOV AL,[SI]
MOV SHU,AL ;保护AL
SUB AL,30H
MOV DI,OFFSET EMP
MOV CX,4 ;循环4次,除以4次2
D1:
MOV AH,0
MOV AL,SHU ;被除数放AX里
DIV TWO ;余数在AH,商在AL
MOV SHU,AL ;商留着放下一次除法循环里
ADD AH,30H
MOV DS:[DI],AH ;余数加30H放在EMP里面
INC DI
LOOP D1
MOV CX,4 ;单个数的BCD码是四位
D2:
DEC DI ;倒着输出EMP里的数
MOV DL,DS:[DI]
MOV AH,02H
INT 21H
LOOP D2
RET
SHOW ENDP
CODES ENDS
END START
四、实验结果
五、注意事项
在ASCII中,39H后是3AH,而不是40H。因此如果需要将A~F转换成对应的数字,需要减去7。笔者当时在这卡了一会,后来突然想到十六进制中是逢十六进一的,希望以上能够帮助到你的实验。