1
1.码制转换
十、二进制数,ASCII码之间的互相转换。
① BCD数 → 2进制数
算法,Dn-1*10n-1+……+D0*10 0
= (…(D n-1*10+ Dn-2)*10+…)*10+ D 0
= (…(( 0*10+Dn-1)*10+ Dn-2)*10+…)*10+ D 0
即,新的中间结果 = 中间结果 *10+本位数字
(中间结果初值为 0)
4.5 常见程序设计举例
2
程序 1:将 ≤65535的非压缩 BCD数转换成 2进
制数。程序如下。;数据段定义
mydata SEGMENT
decnum DB 5,3,0,1,9 ;BCD数 53019
binnum DW?
mydata ENDS
3
prog SEGMENT
ASSUME CS:prog,DS:mydata
begin,MOV AX,mydata
MOV DS,AX
MOV SI,OFFSET decnum
MOV CX,5 ;5位 BCD数
MOV BX,10
XOR AX,AX ;中间结果初始值为 0
Next,MUL BX ;中间结果 *10+本位数字
ADD AL,[SI]
ADC AH,0
INC SI ;指向下位 BCD数
LOOP next
MOV binnum,AX ;保存结果
MOV AH,4CH
INT 21H
prog ENDS
END begin
4
程序 2:把 ≤255的非压缩 BCD数转换成 2进制数
decnum DB 1,5,9 ;BCD数 159
binnum DB?
……
MOV AX,decnum
XCHG AH,AL ;百位在 AH,十位在 AL
AAD ;百位数 *10 + 十位数
MOV AH,AL ;中间结果送 AH
MOV AL,decnum+2
AAD ;中间结果 *10 + 个位数
MOV binnum,AL
……
5
例:从键盘输入两个整数,并求其和。
因键入为整数,故要进行如下转换:
ASCII→BCD→ 二进制数
ASCII→BCD 码很简单,高 4位清零即可得到非压
缩的 BCD码。
BCD→ 二进制数在本例中采用用以下方法:
((((0+千位数 )*10+百位数 )*10)+十位数 )*10+个位数
② ASCII码 → 二进制数 (用于输入 )
第一次中间结果
第二次中间结果
第三次中间结果
最终结果
6
开始
两个数分别转换
成二进制数
键入两个数
相加
结束
返回 DOS
如有溢出则提示
开始
取第一个 ASCII码
是负号吗?
数字符个数- 1,指针+ 1
指针定位
字符个数- 1
= 0?
取数字,与中间结果
相加,再乘以 10
指向下一个数字字符
加个位数
是负数
则求补
存结果
结束
N
Y
Y
N
转换子程序
7
程序如下:
DATA SEGMENT
STR1 DB 10,?,10 DUP(?) ;第 1个数的输入缓冲区
STR2 DB 10,?,10 DUP(?) ;第 2个数的输入缓冲区
NUM DW?,? ;存转换后的二进制数
SUM DW 0 ;存和
OVER DB ‘Overflow!’,13,10,’$’
DATA ENDS;
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
MAIN PROC FAR
8
START,MOV AX,DATA
MOV DS,AX
MOV AH,0AH
LEA DX,STR1
INT 21H ;输入第一个数字串 (设为 26)
MOV AH,0AH
LEA DX,STR2
INT 21H ;输入第二个数字串 (设为 33)
LEA BX,STR1 ;串 1的首地址送 BX
LEA DI,NUM ;存二进制首地址送 DI
CALL CHANGE ;将串 1 ASCII码 → 二进制
LEA BX,STR2 ;串 2的首地址送 BX
9
LEA DI,NUM+2 ;指向
CALL CHANGE ;将串 2 ASCII码 → 二进制
MOV AX,NUM ;(AX)=[NUM]=001AH
ADD AX,NUM+2 ;两数相加,(AX)=003BH
MOV SUM,AX ;存和
JNO NEXT ;无溢出,转 NEXT
LEA DX,OVER
MOV AH,9
INT 21H ;显示 ’ Overflow!’
NEXT,MOV AH,4CH
INT 21H ;返回 DOS
MAIN ENDP
10
CHANGE PROC
MOV CL,[BX+1] ;实际字符数送 CL
MOV AL,[BX+2] ;第一个字符送 AL
MOV CH,AL ;暂存在 CH
CMP AL,’-’ ;第一个字符是负号吗?
JNZ NEXT1 ;不是,转 NEXT1
DEC CL ;字符数减 1
INC BX
NEXT1,ADD BX,2 ;指向第一个数字字符
MOV AX,0 ;清零 AX,存二进制数
LP1,DEC CL
JZ NEXT2 ;若 (CL)=0,转 NEXT2
MOV DL,[BX] ;取字符
AND DL,0FH ;转换成 BCD码
ADD AL,DL ;加到中间结果上
ADC AH,0
11
MOV DX,10
MUL DX ;*10
INC BX ;指向下一个字符
JMP SHORT LP1
NEXT2,MOV DL,[BX] ;取个位数
AND DL,0FH ;个位 ASCII→ 未组合 BCD
ADD AX,DX ;加个位数,(AX)=001AH
CMP CH,’-’ ;是 ’ -’?
JNZ NEXT3 ;该数非负,转 NEXT3
NEG AX ;若为负,求补
NEXT3,MOV [DI],AX ;存二进制结果
RET
CHANGE ENDP;
CODE ENDS
END START
12
02
0A
32
36
0D

02
0A
33
33
0D

00
1A
21
00
3B
00
STR1
STR2
NUM
SUM
10个
10个
‘O’


OVER
04
0A
31
32
34

STR1
若键入 ‘ 1234’
33
0D
‘1’
‘2’
‘3’
‘4’

设键入第 1个数为 26,第 2个数为 33,则在内存各变量分配如下,
13
本例题重点掌握,
*如何从键盘输入一个字符串
* ASCII→ 未组合 BCD→ 二进制
*有符号数的运算,对负数和溢出如何处理
思考题,
若键入第一个数 26,第二个数为 -4,填写
各变量结果 。
14
? 方法 1
计算二进制数中所包含的 1000的个数、
100的个数,10的个数和 1的个数。
? 方法 2
除 10取余。
下面举例介绍第一种方法 。 流程图如下:
③ 二进制数 → BCD
15
Y
N
二进制数 ?AX
令( DL)= 0
(AX)-1000
<0?
(DL)+1
(AX)+1000?(AX)
DL存至缓冲区
令 (DL)= 0
Y
N
(AX)-10
<0?
(DL)+1
(AX)+10?(AX)
存 DL
存 AL
返回 DOS
求 100的个数,结构同上
A
16
汇编程序如下:
DATA SEGMENT
BNUM DB 270FH
DNUM DB 4 DUP(?) ;存放 BCD码的缓冲区
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
BINBCD PROC FAR
BEGIN,MOV AX,DATA
MOV DS,AX
MOV AX,BNUM ;取二进制数
LEA BX,DNUM ;BCD码缓冲区首地址送 BX
17;计算百位的个数
MOV DL,0 ;千位的个数计数器
AGAIN1,SUB AX,1000 ;(AX)-1000
JC NEXT1 ;若 ≤ 0,则退出循环
INC DL ;(DL)+1
JMP AGAIN1
NEXT1,ADD AX,1000 ;(AX)←(AX)+ 1000
MOV [BX],DL ;存千位的个数;计算百位的个数
MOV DL,0 ;百位的个数计数器
AGAIN2,SUB AX,100 ;(AX)-100
JC NEXT2
INC DL
JMP AGAIN2
NEXT2,ADD AX,100
MOV [BX+1],DL ;存百位的个数
18
MOV DL,0 ;十位的个数计数器
AGAIN3,SUB AX,10 ;(AX)-10
JC NEXT3
INC DL
JMP AGAIN3
NEXT3,ADD AX,10
MOV [BX+2],DL ;存十位的个数
MOV [BX+3],AL ;存个位的个数
MOV AH,4CH
INT 21H
BINBCD ENDP;
CODE ENDS
END BEGIN
19
④ BCD→ASCII
前面举例介绍过,略。
⑤二进制串转换为 ASCII码
一个二进制位串若要送显示或打印,需把
串中每一位 (0或 1)化为 ASCII码。
思路:先将目标串 全部预置为 30H,再把每
个二进制位逐位左移至 CF,然后判 CF=0?
若 是, 取下一位;若不是, 将 31H送此单元 。
流程图如下:
20
初始化
用’ 0’填满串
取要转换的数
左移 1位
存入‘ 1’
结束
CF=1?
转换完?
调整指针
N
N
21
汇编程序如下:
DATA SEGMENT
NUM DW 6F78H
STRING DB 16 DUP(?)
DATA ENDS;
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
BINCA PROC FAR
BEGIN,MOV AX,DATA
MOV DS,AX
MOV ES,AX
CLD
LEA DI,STRING
MOV CX,16 ;串的长度
22
MOV AL,30H
REP STOSB ;串中全部填充为 ‘ 0’
MOV CX,16
LEA DI,STRING
MOV AL,’1’
MOV BX,NUM ;(BX)=6F78H
AGAIN,RCL BX,1 ;含进位位循环左移
JNC NEXT ;若为 0,转
MOV [DI],AL ;若为 1,对应位送入 ’ 1’
NEXT,INC DI
LOOP AGAIN
23
MOV AH,4CH
INT 21H
BINCA ENDP
CODE ENDS
END BEGIN
24
2.子程序的参数传递
编写子程序时,很重要的一个工作是如何
把参数传给子程序,这个过程叫参数传送。
传送方法有:
?把参数放在 CPU内部寄存器中
?把参数放在变量中
?把参数放在地址表中
?利用堆栈传送参数
25
下面举例介绍第 4种方法,它通常在主程
序中把参数或参数地址保存在堆栈中,而在
子程序中将参数从堆栈取出来。
例:把一个用十六进制表示的字 → ASCII码,然
后送到屏幕上显示。
汇编程序如下:
DATA SEGMENT
NUM DW 25AFH ;要显示的数
STRING DB 4 DUP(?),13,10,’$’
DATA ENDS
26
STACK SEGMENT
DB 100 DUP(?)
TOP EQU $
STACK ENDS;
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA,SS:STACK
BEGIN,MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV AX,STACK
MOV SS,AX
MOV SP,TOP
LEA BX,STRING ;取变量偏址
PUSH BX ;将偏址压栈
PUSH NUM ;将变量压栈
00
02
0064H
0062H
(SP)
25
AF 0060H
堆栈
27
CALL BINHEX ;(SP)=005EH
CS:0113 LEA DX,STRING ;(DX)=0002H
MOV AH,9
INT 21H
MOV AH,4CH
INT 21H;***********************
BINHEX PROC
PUSH BP ;(SP)=005CH
MOV BP,SP ;(BP)=005CH
PUSH AX ;(SP)=005AH
PUSH DI ;(SP)=0058H
PUSH CX ;(SP)=0056H
PUSH DX ;(SP)=0054H
00
02
0064H
0062H
25
AF 0060H
005EH
01
13
005CH(SP)
返回
地址 (IP)?
?原 (BP)
28
00
02
0064H
0062H
25
AF 0060H
005EH
01
13
xx
xx 005CH(BP)
PUSHF ;(SP)=0052H
MOV AX,[BP+4] ;(AX)=25AFH
MOV DI,[BP+6] ;(DI)=0002H
ADD DI,LENGTH STRING-4;(DI)=0005H
MOV DX,AX ;(DX)=25AFH
MOV CX,4
STD ;从后往前存
AGAIN:AND AX,0FH ;第一次 (AX)=000FH
CALL HEXD ;转换为 ASCII码
STOSB
PUSH CX
MOV CL,4
SHR DX,CL ;逻辑右移 4位
MOV AX,DX ;第 1次 (AX)=025AH
POP CX
LOOP AGAIN ;(CX)-1=0?不等,转
+4
29
POPF
POP DX
POP CX
POP DI
POP AX
POP BP
RET 4
BINHEX ENDP;********************
HEXD PROC
CMP AL,0AH
JL LP
ADD AL,7
LP,ADD AL,30H
RET
HEXD ENDP
CODE ENDS
END BEGIN
0064H
0062H
0060H
005EH
005CH
00
02
25
AF
01
13
xxxx
(SP)
30
从本例可知,通过堆栈传递的两个参数分别
为:变量 NUM的内容 25AFH和变量 STRING的偏移
地址。这两个参数在调用子程序之前压入堆栈,
当 CALL指令返回时,其 (SP)=0060H,不是初值
0064H。故采用带参数返回指令 RET 4。
本例重点掌握:
*进一步了解堆栈的使用
*学会 RET n的应用
*子程序嵌套
31
其他例子
宏的使用例子
逻辑尺的例子
查表法求立方值
作业,p194
4.6(1),4.10,4.17,4.18(用逻辑尺 )