目录
实验前置知识
- 无论是字符还是数字,在计算机中都是以位向量(即0\1串)储存的。每个位向量唯一代表一个字符或数字。因此,字符‘0’和数字0的位向量是不同的,‘0’的ASCII码是30H(或者说是48),该ASCII码对应一个8位的位向量(具体多少不用关心);而数字0的位向量是0000 0000(其实数字的位向量可以通过把数字转化为2进制数字来得到)。所以请记住,寄存器中n位的位向量其实代表一个具体的(十进制)数字。
- 数字和字符之间相互转化的关系
数字0~9 加上30H ——>‘0’~‘9’
数字A~Z 加上37H ——>‘A’~‘Z’
1、将BX中的无符号数以二进制形式输出
算法分析:
代码:
DATAS SEGMENT
;此处输入数据段代码
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP;此题用不到数据段和堆栈段,也可以不用初始化
XOR BX,BX
MOV BL,7;BX随便什么数字都行
MOV CL,16
;算法核心部分;
L1: MOV DL,30H ;输入输出都是以字符形式来实现的,
;所以必须把数字转化为形式相同的字符,
;这样才能把数字显示在屏幕上
SHL BX,1
ADC DL,0
;算法核心部分
MOV AH,2 ;前面的DL存好数据了
INT 21H
LOOP L1
MOV AH,4CH
INT 21H
CODES ENDS
END START
2、将BX中的无符号数以八进制形式输出
算法分析:
代码:
DATAS SEGMENT
;此处输入数据段代码
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
XOR BX,BX
MOV BX,70 ;BX随便什么数字都行
;先把最高位处理了,剩下的刚好是3位一组,共5组
MOV DL,30H ;重置DL顺带方便待会数字转换成数字字符
SHL BX,1
ADC DL,0
MOV AH,2
INT 21H
MOV CX,5;剩下的数字可分为5组(5次循环)
L1: PUSH CX ;因为下面的指令需要用到CX,这里进栈保存一下循环的次数
;算法核心部分;
MOV CL,3
ROL BX,CL
MOV DX,BX ;保留BX内数据
AND DL,7 ;获取BX最后三位位向量(注意:这三位位向量
;其实就代表一个真实的数字,不管我们把它
;写成十进制还是八进制)
ADD DL,30H ;数字转化成与之形式相同的字符
;算法核心部分
MOV AH,2
INT 21H
POP CX ;恢复之前保护的循环次数
LOOP L1
MOV AH,4CH
INT 21H
CODES ENDS
END START
Tips:最后屏幕上显示的就是八进制数字
3、将BX中的无符号数以十六进制形式输出
算法分析:
代码:
DATAS SEGMENT
;此处输入数据段代码
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
XOR BX,BX
MOV BX,6C3BH ;BX随便什么数字都行
MOV CX,4;剩下的数字可分为4组(4次循环)
L1: PUSH CX ;因为下面的指令需要用到CX,这里进栈保存一下循环的次数
;算法核心部分;
MOV CL,4
ROL BX,CL
MOV DX,BX ;保留BX内数据
AND DL,15 ;获取BX最后四位位向量
;(但是!!!单个十六进制数字
;有数字形式还有字母形式,所以需要判别,
;是转化为数字字符还是字母字符)
CMP DL,0AH;判断是否大于等于10
JGE L2
ADD DL,30H;小于10说明应转化为数字字符
JMP L3
L2: ADD DL,37H;大于等于10说明应转化为字母字符
;算法核心部分
L3: MOV AH,2
INT 21H
POP CX ;恢复循环次数
LOOP L1
MOV AH,4CH
INT 21H
CODES ENDS
END START
4、将BX中的无符号数以十进制形式输出
算法分析:
文章开头前置知识里提过,位向量就代表一个数字。相信大家C语言都做过一道拆分整数的每个数字显示每个数字的算法题。这第4小题其实就是拆分数字再显示出来。**算法步骤见下图。**被除数的位向量除以10,得到的就是商和余数的位向量,分别存在寄存器中,这时把数字转换为字符就可以用DOS功能调用把数字字符显示出来。
代码:
DATAS SEGMENT
COUNT DB 0 ;压入栈内的数字总数
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
XOR BX,BX
MOV BX,6C3BH ;BX随便什么数字都行(6C3BH = 27707)
MOV AX,BX
;算法核心部分;
L1: XOR DX,DX ;重置准备工作
MOV SI,10
DIV SI ;除法指令不接受立即数!!
PUSH DX
INC COUNT
CMP AX,0 ;商为0则数字拆分结束
JNE L1
;算法核心部分
XOR CX,CX
MOV CL,COUNT ;栈内有几个数字,就应循环弹出几次
L2: POP DX
ADD DL,30H ;转化成字符
MOV AH,2
INT 21H
LOOP L2
MOV AH,4CH
INT 21H
CODES ENDS
END START
5、将BX中的有符号数以十进制形式输出
**算法分析:**简直跟第四小题一模一样,只需要判断一下是正数还是负数,正数直接按无符号数处理;负数进行求补运算,变成负数的相反数(正数),这时就可以按照无符号数处理。
代码:
DATAS SEGMENT
COUNT DB 0 ;压入栈内的数字总数
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
MOV BX,0FFFFH ;特地放入一个16位全为1的位向量(有符号形式代表-1)
;判断符号位是否为1; 1、JL指令 2、TEST BX,8000H,JC
CMP BX,0
JL L1 ;是负数
JMP L2 ;不是负数,跳过负数的处理部分
;算法核心部分;
L1: NEG BX ;把负数求补,变成其相反数(想想负数的补码与其相反数的补码的转换关系)
MOV DL,'-' ;先把符号显示出来
MOV AH,2
INT 21H
;拆分整数获得各位上的数字
MOV AX,BX
L2: XOR DX,DX
MOV SI,10
DIV SI
PUSH DX
INC COUNT
CMP AX,0
JNE L2 ;商不为0继续拆分数字
;算法核心部分;
XOR CX,CX
MOV CL,COUNT ;栈内有几个数字,就应循环弹出几次
L3: POP DX
ADD DL,30H ;转化成字符
MOV AH,2
INT 21H
LOOP L3
MOV AH,4CH
INT 21H
CODES ENDS
END START
6、第五章习题5.1-5.3
5.1 把键盘输入的小写字母转换成大写字母并显示出来
代码:
DATAS SEGMENT
;输入数据段代码
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
L1: MOV AH,7
INT 21H
;合法范围:'a'<=输入的字符<='z'
CMP AL,0DH
JE L2 ;回车结束输入
CMP AL,'a'
JB L1
CMP AL,'z'
JA L1
;转换大写并显示
MOV DL,AL
SUB DL,20H ;小写转大写
MOV AH,2
INT 21H
JMP L1
L2: MOV AH,4CH
INT 21H
CODES ENDS
END START
5.2 输入一个小写字母,找出它的前导字符和后续字符,按顺序显示这三个字符
代码:
DATAS SEGMENT
;输入数据段代码
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
L1: XOR AX,AX
MOV AH,1
INT 21H
;可省略的代码(美化输入的文字排版,相当于\n)
PUSH AX ;DOS2号功能会改变AL的值(书上没写)!需要进栈保存
MOV AH,2
MOV DL,0DH
INT 21H
MOV AH,2
MOV DL,0AH
INT 21H
POP AX ;恢复原来AL的值
;可省略的代码
;检测是否合法
CMP AL,'a'
JB L1
CMP AL,'z'
JA L1
XOR DX,DX
MOV DL,AL
SUB DL,1 ;变成前导字符
MOV CX,3
L2: MOV AH,2
INT 21H
INC DL
LOOP L2
MOV AH,4CH
INT 21H
CODES ENDS
END START
值得注意的一些冷知识:DOS 的2号功能会改变AL中的值!!书上没写!!!
本来想美化一下输入界面的,每输入一个字符程序自动换行,输入合法程序在下一行显示结果;不合法则换行重新输入,但不显示结果。结果程序就出了问题。debug过程中就看到调用2号功能后AL的值发生了改变。点击这个链接可以看到为什么会这样。
5.3 把AX中的16位数按顺序均分为4组,然后把这四组数分别放入AL、BL、CL和DL中
算法分析:就是获取AX中的长度为4的位向量嘛,用屏蔽指令AND可以轻松搞定。唯一麻烦的是,题目要求保存到四个通用寄存器里面,而好多操作都要修改这几个寄存器的值,所以问题是:如何把切割出来的位向量送入四个通用寄存器,而不改变他们的值呢?我的想法是借助堆栈临时保存切割出的位向量,完成所有切割后,再把堆栈中保存的位向量弹出到这四个通用寄存器中。
代码:
DATAS SEGMENT
COUNT DB 4 ;需要处理4个数字
DATAS ENDS
STACKS SEGMENT
DB 30H DUP(0)
TOP LABEL WORD
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 SP,TOP
MOV AX,6C3BH ;随便什么数都行
;算法核心部分;
L1: MOV DX,AX
AND DX,0FH
PUSH DX ;取AX最后四位进栈保存
MOV CL,4
SHR AX,CL
DEC COUNT
CMP COUNT,0 ;这里CX和堆栈都被占用了,不好用LOOP了,所以要用最原始的循环了
JNE L1
; 算法核心部分;
POP AX
POP BX
POP CX
POP DX
MOV AH,4CH
INT 21H
CODES ENDS
END START
结果: