数据的分类统计程序
数据的分类和统计也是一类非数值计算,数据的分类统计方法在例6.10中已介绍,下面通过一个例子介绍数据的分类存储问题。
例10.5 统计从地址0040H:0000H开始的100个字中,把正数和负数按照它们先后出现的次序分别存储在缓冲区Data1和Data2,并把每类的个数存入相应缓冲区的第一个字单元中。
解:由于在指定地址之后的100个字中,可能存在全是正数或负数的情况,所以,缓冲区Data1和Data2的容量都应是100个字。 |
|||
|
.MODEL SMALL |
||
.DATA |
|||
Num = 100 |
|||
Data1 |
DW ?, Num dup(?) |
||
Data2 |
DW ?, Num dup(?) |
||
.CODE |
|||
.STARTUP |
|||
MOV |
AX, 40H |
||
MOV |
ES, AX |
||
LEA |
SI, Data1 2 |
;指向存储正数的缓冲区 |
|
LEA |
DI, Data2 2 |
;指向存储负数的缓冲区 |
|
XOR |
BX, BX |
;BX用于扫描存储单元 |
|
MOV |
CX, 100 |
;字符个数 |
|
.REPEAT |
|||
MOV |
AX, ES:[BX] |
||
ADD |
BX, 2 |
||
CMP |
AX, 0 |
||
.CONTINUE .IF ZERO? |
|||
JL |
next1 |
||
MOV |
[SI], AX |
;向正数缓冲区内存储数据 |
|
ADD |
SI, 2 |
||
.CONTINUE |
|||
next1: |
|||
|
MOV |
[DI], AX |
;向负数缓冲区内存储数据 |
ADD |
DI, 2 |
||
.UNTILCXZ |
|||
SUB |
SI, OFFSET Data1 2 |
||
SUB |
DI, OFFSET Data2 2 |
||
SHR |
SI, 1 |
||
SHR |
DI, 1 |
||
MOV |
Data1, SI |
||
MOV |
Data2, DI |
;把每类的统计个数存入缓冲区的第一个字单元 |
|
.EXIT |
0 |
||
END |
例10.6 用键盘输入任意一字符串,分类统计该字符串中每个数字和字母的出现次数。
解: |
|||
|
.MODEL SMALL |
||
.DATA |
|||
N = 80 |
|||
Buff |
DB N, ?, N DUP(?) |
||
Num |
DW 36 DUP(0) |
;每个字用于存放'0'~'9','A'~'Z'出现的个数 |
|
.CODE |
|||
.STARTUP |
|||
LEA |
DX, Buff |
||
MOV |
AH, 0AH |
||
INT |
21H |
;输入一个字符串 |
|
XOR |
CH, CH |
||
MOV |
CL, Buff 1 |
;CX=输入字符串的个数 |
|
LEA |
SI, Buff 2 |
||
XOR |
BX, BX |
||
.REPEAT |
|||
MOV |
BL, [SI] |
;考虑下面的思考题 |
|
INC |
SI |
||
.IF BL>='0' && BL<='9' |
;分类统计'0'~'9'中的每个数字的个数 |
||
SUB |
BL, '0' |
||
ADD |
BX, BX |
||
INC |
Num[BX] |
||
.CONTINUE |
|||
.ENDIF |
|||
.IF BL>='a' && BL<='z' |
|||
SUB |
BL, 20H |
;小写变大写 |
|
.ENDIF |
|||
.IF BL>='A' && BL<='Z' |
;分类统计'A'~'Z'中的每个字母的个数 |
||
SUB |
BL, 'A'-10 |
||
ADD |
BX, BX |
||
INC |
Num[BX] |
||
.ENDIF |
|||
.UNTILCXZ |
|||
.EXIT 0 |
|||
END |
思考题:在本例中,用指令“MOV BL, [SI]”来把当前检测的字符存入BL,当然,我们也可以用AL来代替BL,有关指令要作相应的改动,但这样做,会更方便吗?希望读者能知道:为什么要用BL,而不用AL?
10.3 数据转换程序
数据类型转换是输入输出过程中经常遇到的问题。输入时,计算机系统要把用户从键盘上输入的字符串转变成相应的数值,并存储在内存中;输出时,要把计算机内部的二进制数据形式转换成相应的十进制字符串,然后再输出。
在高级语言编程环 境中,程序员能用各种输入输出语句,按一定的格式进行交互式操作,很少或根本不关心输入输出是如何实现的。有的程序员甚至认为其输入的就是十进制数值,输 出数据也就是把内存中存储的数据直接输出出来。其实,输入输出过程并不是如此简单,计算机系统要进行复杂而又细致的数据类型转换和格式化等工作。
本节试图通过用汇编语言实现数据类型的转换来反映输入输出的本来面目,使程序员在用高级语言编程时,对其输入输出语句的实现过程有所了解,也知道有别人(或编译程序)帮他完成了输入输出的准备工作。
例10.7 编写一个程序,它能把字类型变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号"-",否则,不输出符号。
例10.7是用“用16位除10”的方法从低向高依次得到每位的数值,但若待输出的数据是32位,用10除之后,其商很可能会超过16位,所以,不能简单地引用例10.7的方法来输出32位二进制。
假设:32位二进制数Z为A×216+B,其中:A和B都是16位二进制数。 用10去除A,得:A=A1×10+A2,于是,
(1)
假设A2×216+B被10除后所得的商和余数分别为B1和C1(B1≥0,C1≥0)。
利用式(1)和“A2<10”,我们不难看出:Z的个位就是C1和B1<216。
令Z1=A1×216+B1,显然,Z1就是Z/10所得到的商。
对于Z1,再利用式(1)得到商Z2和C2。……,重复上面的步骤,直到所得商为0为止。
下面的例10.9就是利用上面方法来输出32位二进制数值。
例10.9 编写一个子程序,该子程序能把32位二进制变量的数值以十进制形式输出出来。若该数值为负数,则需要输出负号"-",否则,不输出符号。
解: |
||||
|
.MODEL SMALL, C |
|||
.DATA |
||||
CR = 13 |
||||
LF = 10 |
||||
Data1 |
DD 908976789 |
|||
.CODE |
||||
;子程序Display是按十进制输出32位二进制数值SOURCE |
||||
Display |
PROC USES AX BX CX DX SI DI SOURCE:DWORD |
|||
LOCAL |
FLAG:BYTE |
;定义一个字节类型的局部变量FLAG |
||
MOV |
BX, WORD PTR [SOURCE] |
|||
MOV |
CX, WORD PTR [SOURCE 2] |
|||
MOV |
FLAG, 0 |
;FLAG=0——正数 |
||
CMP |
CX, 0 |
|||
JGE |
next |
|||
INC |
FLAG |
;FLAG=1——负数 |
||
NOT |
BX |
|||
NOT |
CX |
|||
ADD |
BX, 1 |
;能否用指令INC BX? |
||
ADC |
CX, 0 |
;上四条指令把32位数CX-BX变为正数 |
||
next: |
||||
|
XOR |
DI, DI |
;压入堆栈字符的个数 |
|
MOV |
SI,10 |
;用10来除 |
||
.REPEAT |
;本循环把32位二进制数转换成十进制 |
|||
XOR |
DX, DX |
;数的字符串存入堆栈之中 |
||
MOV |
AX, CX |
|||
DIV |
SI |
|||
MOV |
CX, AX |
|||
MOV |
AX, BX |
|||
DIV |
SI |
|||
ADD |
DL, '0' |
|||
PUSH |
DX |
|||
INC |
DI |
|||
MOV |
BX, AX |
|||
.UNTIL |
BX==0 && CX==0 |
|||
.IF FLAG==1 |
;判断前面转换的数是否为负数 |
|||
MOV |
AL, '-' |
;若是,把符号'-'压入堆栈 |
||
PUSH |
AX |
|||
INC |
DI |
|||
.ENDIF |
|
|||
MOV |
CX, DI |
|||
.REPEAT |
;本循环把堆栈中的字符串显示出来 |
|||
POP |
DX |
|||
MOV |
AH, 2 |
|||
INT |
21H |
|||
.UNTILCXZ |
||||
MOV |
DL, CR |
;下面六条指令显示回车、换行 |
||
MOV |
AH, 2 |
|||
INT |
21H |
|||
MOV |
DL, LF |
|||
MOV |
AH, 2 |
|||
INT |
21H |
|||
RET |
||||
Display |
ENDP |
|||
.STARTUP |
||||
INVOKE |
Display, Data1 |
|||
INVOKE |
Display, -123456789 |
|||
.EXIT |
0 |
|||
END |