||||
实验9程序与实验10实验课题
实验10 利用子程序进行串处理及有关日期的程序设计
一、实验内容:利用子程序结构设计从串中查找一个字符的所有位置、查找一个字符串。宏汇编程序设计。
二、实验目的:1. 学习结构化程序设计方法。
2. 如何在一个串中查找到一个字符的所有位置。
3. 如何在一个串中查找到一个另一个字符串。
4. 认识宏的概念,学习宏汇编程序设计方法
三、实验题目
1. 要求自行设定二个字类型的串STRING1与STRING2的内容,用程序对这二个串进行比较,如果相等,0送BX,否则,0FFFFH送BX。设串长为COUNT。最后分别以16进制形式显示BX、STRING1与STRING2的内容。
思考:本题如果不借助串比较指令来做,需要怎样设计?要考虑一个串和另一个串不相同但前面有部分字符相同的情况。
以下两题为实验8第1题利用子程序结构求解的设计参考。
2. 已知程序段如下,将它补充完整,使能将结果(BX的值)以16进制形式在屏幕上显示,根据显示情况分析说明其功能是什么?要求将16进制显示程序段利用子程序进行设计。
DATA SEGMENT
BUF DB ‘AABAAAAABBCD‘
COUNT EQU $-BUF
DATA ENDS
……
MOV AL, ‘A‘
MOV DI,OFFSET BUF
MOV CX,COUNT
DEC DI
NEXT: INC DI
CMP AL,[DI]
LOOPE NEXT
MOV BX,DI
JNZ EXIT
MOV BX,0FFFFH ;当ZF=1,CX=0时退出循环
EXIT:…….
3. 对在STRING中搜索A的程序段进行补充设计,要求对设计加详细注释。要求自行设定一个长度COUNT的字节类型的串STRING的内容,用循环程序对这个串进行搜索,如果搜索到字节类型数据A,显示当前位置(字长)。STRING、COUNT、A的内容自行设计。
LEA DI,STRING
MOV AL,A
MOV CX,COUNT
CLD
REPNE SCASB
JZ OK
……
OK: ……
4. 在一个串中查找一个字符,显示查到该字符的次数。
5 用串指令在一个串中查找一个字符,显示所有查到位置的地址。如果没有欲查字符,显示“-1”。
6.在一个串中查找另一串,如果找到,将查到串的首地址存到DI中。
7.求编写显示系统的当前日期和时间的程序。要求程序运行时显示的信息格式为:
Current date is 10-30-2008. Current time is 10:23:26.12
相关的DOS功能调用:
【获取系统当前日期】
调用指令:INT 21H
功 能 号:2AH
入口参数:无
出口参数:CX = 年(范围1980~2099)
DH = 月(1~12)
DL = 日
【获取系统当前时间】
调用指令:INT 21H
功 能 号:2CH
入口参数:无
出口参数: CH = 小时(0~23)
CL = 分(0~59)
DH = 秒(0~59)
DL = 百分之一秒(0~99)
8. 设计一个万年历程序。已知2008年1月1日为星期二,求2008年之后21世纪的某年某月某日是星期几?不考虑输入错误。
分析:假设通过键盘分别输入公元年份(输入末二位)、月份(二位)、日期(二位),可以利用前面的子程序分别变成二进制数。再计算输入日期与2008年1月1日之间相差日数X,将X除以7的余数加2就代表星期几,7代表星期日,1代表星期1,……。而要计算相差日数X,必须考虑闰年问题:4年一闰、百年不闰、400年闰。非闰年365天,2月份28天;闰年366天, 2月份29天。可以先分析年份之差,对每一年判断是否闰年,再加366或365;再分析月份之差,求出所求年内距离1月1日的天数。为了能用一个程序计算各个月的情况,设计一个表:ruler=31,28,31,30,31,30,31,31,30,31,30,31,每一个值代表一个月是大月还是小月,1表示31天,0表示30天,利用间址查表求天数。2月单独计算。为求程序清晰,分别设计了分析闰年、月份二个子程序,在求解星期几的程序中调用。
实验10参考程序段
1. 用循环指令在一个串中查找一个字符,第一次查到该字符时将地址放在DI中。
MOV AX,DATA
MOV DS,AX
MOV AL, 'A'
MOV DI,OFFSET BUF
MOV DX,0
MOV CX,COUNT
DEC DI
NEXT: INC DI
CMP AL,[DI]
JZ NEXT1
LOOP NEXT
NEXT1:
2. 用串指令在一个串中查找一个字符,查到第一个时将位置放在DI中。
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV AL, 'A'
MOV DI,OFFSET BUF
MOV CX,COUNT
REPNE SCASB
JNZ EXIT
DEC DI ;找到的存放地址放在DI中
3.在DAT1串中查找ADD2串的第1个字符(串长COUNT2),如果找到,将剩余串的首地址存到ADD1中,剩余串长度存到ADD2中。
MOV AX,DATA
MOV DS,AX
MOV ES,AX
MOV ADD1,OFFSET DAT1 ;立即数传存储器
MOV AL,X ;取欲查之字符串第1个字符
MOV ADD2,COUNT2
CLD
BACK1: MOV DI,ADD1 ;取欲查字串首地址
MOV CX,ADD2 ;取欲查字串长度
REPNE SCASB ;查找
MOV ADD1,DI ;保存剩余串首地址
MOV ADD2,CX ;保存剩余串长度
4.求系统的当前日期和时间分别放在DX与CX中的程序。假使在数据区FNAME保存一个文件的名字。其中DX 中存年月日的格式: 0-4位:日,5-8位:月,9-15位:年份-1980。CX中所存时间的格式: 0-4位:秒/2,5-10位:分,11-15位:时
MOV DX, OFFSET FNAME
MOV AL, 2 ; 读写方式
MOV AH, 3DH ;打开文件
INT 21H
MOV BX, AX
MOV AH, 57H ; 读改日期
MOV AL,0 ;0 表示读出时间,1表示改写时间
INT 21H
MOV DI,CX
5.求以十进制方式显示DX(日期)内容的宏。
AXTOBCD1 MACRO
PUSH SI
PUSH DI
PUSH CX
MOV SI,DX
;DX 中存年月日 0-4位:日,5-8位:月,9-15位:年份-1980
;保存CX中所存时间 0-4位:秒/2,5-10位:分,11-15位:时
MOV CL,7 ;年
ROL SI,CL
MOV DX,SI
AND DX,7FH
ADD DX,1980
AXTOBCD DX
MOV DL,'.'
MOV AH,2
INT 21H
MOV CL,4 ;月
ROL SI,CL
MOV DX,SI
AND DX,0FH
AXTOBCD DX
MOV DL,'.'
MOV AH,2
INT 21H
MOV CL,5 ;日
ROL SI,CL
MOV DX,SI
AND DX,1FH
AXTOBCD DX
MOV DL,'.'
MOV AH,2
INT 21H
POP CX
POP DI
POP SI
ENDM
6.求以十进制方式显示DI(时间)内容的宏。
AXTOBCD2 MACRO
PUSH SI
PUSH DI
PUSH CX
MOV CL,5 ;时
ROL DI,CL
MOV DX,DI
AND DX,1FH
AXTOBCD DX
MOV DL,'.'
MOV AH,2
INT 21H
MOV CL,6 ;分
ROL DI,CL
MOV DX,DI
AND DX,3FH
AXTOBCD DX
MOV DL,'.'
MOV AH,2
INT 21H
MOV CL,5 ;秒
ROL DI,CL
MOV DX,DI
AND DX,1FH
SHL DX,1
AXTOBCD DX
MOV DL,'.'
MOV AH,2
INT 21H
POP CX
POP DI
POP SI
ENDM
7. 求从键盘输入二个十进制数字变成二进制数子程序
;子程序名:IMPOR
;程序功能:从键盘输入二个十进制数字变成二进制数
;入口参数:
;出口参数: 二进制数:BX
;使用寄存器:AX
IMPOR PROC
MOV AH,1H
INT 21H ;键盘输入1个字符,不考虑输入错
SUB AL,30H
MOV BL,10
MUL BL
MOV BL,AL
MOV AH,01H
INT 21H ;键盘输入第二个数字
SUB AL,30H
ADD AL,BL
EXIT: MOV AH,0
MOV BX,AX
RET
IMPOR ENDP
8. 求AX中数表示的某一年份的日数的子程序
;子程序名:LEAPY
;程序功能:求AX中数表示的年份的日数
;入口参数:年份:CX
;出口参数:该年日数:AX
;使用寄存器:DX、BX
LEAPY PROC
MOV AX,CX ;保存DI到AX
MOV DX,0
MOV BX,4
DIV BX
CMP DX,0
JNZ DISP3 ;不能整除4,不是闰年
MOV AX, CX
MOV BX,100
DIV BX
CMP DX,0
JNZ DISP4 ;不能整除100,是闰年
MOV AX, CX
MOV BX,400
DIV BX
CMP DX,0
JZ DISP4 ;能整除400,是闰年
DISP3: MOV AX,365
MOV BYTE PTR Y,0 ;置非闰年标志
JMP EXIT1
DISP4: MOV AX,366
MOV BYTE PTR Y,1 ;置闰年标志
EXIT1: RET
LEAPY ENDP
实验9部分参考程序
1、在数据区中有如下定义,要求首先将有关常量存放到数据区BUF中,再编一个循环程序,使依次以10进制形式显示各变量的值(不要求显示变量名)。
NUM1 EQU 25*4-50
NUM2 EQU NUM1/7
VAR1 DB 1,2,3,4,5
VAR2 DB '12345'
NUM4 EQU VAR2-VAR1
NUM5 EQU 0FH
BUF DB 7 DUP(?)
;程序1解:以十进制形式显示各数(循环方式)
DATA SEGMENT
NUM1 EQU 25*4-50
NUM2 EQU NUM1/7
VAR1 DB 1,2,3,4,5
VAR2 DB '12345'
NUM4 EQU VAR2-VAR1
NUM5 EQU 0FH
BUF DB 7 DUP(?)
D1 DB ?,?,?,?,?,? ;根据子程序要求建立显示数据区
DATA ENDS
STACK SEGMENT STACK
DB 200 DUP(0)
STACK ENDS
CODE SEGMENT
ASSUME DS:DATA,SS:STACK,CS:CODE
START:MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET BUF ;将有关数据放BUF数据区使方便循环求解
MOV [SI],BYTE PTR NUM1
INC SI
MOV [SI],BYTE PTR NUM2
INC SI
MOV AL,VAR1
MOV [SI],AL
INC SI
MOV AL,VAR2
MOV [SI],AL
INC SI
MOV [SI],BYTE PTR NUM4
INC SI
MOV [SI],BYTE PTR NUM5
MOV SI,OFFSET BUF ;准备从BUF数据区依次取数显示
MOV DI,OFFSET D1+5 ;根据子程序要求将DI指向显示数据区尾部
MOV BP,6 ;共计显示6个数据
NEXT: MOV AH,0 ;根据子程序要求将欲显示数据取到AX中
MOV AL,[SI] ;显示数据为字节类型,而AX为字类型
CALL P1 ;调用显示子程序
INC SI
DEC BP ;共显示6个数据,由BP决定循环次数
JNZ NEXT
MOV AH,4CH
INT 21H
;子程序名:p1
;功能: 以十进制形式显示任意字
;输入参数: 显示字在AX中。要求在数据区建5字节的显示数据区,
;要求用DI指向其尾部。
;输出参数:
;* * * * * * *
P1 PROC
MOV BX,10 ;准备除以10
CHANGE10: MOV DX,0 ;将除数的高16位(在DX中)清0,DX存放余数
DIV BX ;DX,AX除以BX (BX=10)
ADD DL,30H ;余数变为ASCII码值
MOV BYTE PTR[DI],DL ;存储十进制数
DEC DI ;到上一单元
CMP AX,0 ;是否为0?
JNZ CHANGE10 ;不为0转CHANGE10
MOV CX,5 ;准备连续显示5个数
CHANGE11:MOV DL,BYTE PTR[DI] ;取出存放在BUF中的十进制数到DL供显示
MOV AH,2 ;显示1个字符
INT 21H
INC DI
LOOP CHANGE11
RET
P1 ENDP
CODE ENDS
END START
2. 利用子程序结构将八位无符号二进制数转换为十进制数的ASCII码并显示。
DATA SEGMENT
BIN DB 11111111B
ASC DB 3 DUP(?),"$"
DATA ENDS
STACK SEGMENT STACK
DB 200 DUP(0)
STACK ENDS
CODE SEGMENT
ASSUME DS:DATA,SS:STACK,CS:CODE
START:MOV AX,DATA ;给数据段寄存器DS赋值
MOV DS,AX
MOV AL,BIN
MOV BX,OFFSET ASC
ADD BX,2 ;指向存放ASCII码值结果的存贮单元末地址
CALL TRANSFORM ;转子程序实现转换
LEA DX,ASC
MOV AH,9
INT 21H
MOV AH,4CH ;中断返回DOS状态
INT 21H
;* * * * * * *
;子程序名:TRANSFORM
;功能:将八位无符号二进制数转换为十进制数ASCII码
;入口参数:八位二进制数=>AL
; 存放ASCII码值末地址=>BX
;出口参数:在ASC开始的存贮单元中
;* * * * * * *
TRANSFORM PROC NEAR
PUSH CX
PUSH DX
MOV CX,3
MOV DL,10
AGAIN:MOV AH,0
DIV DL
ADD AH,30H
MOV [BX],AH
DEC BX
LOOP AGAIN
POP DX
POP CX
RET
TRANSFORM ENDP
CODE ENDS
END START
说明:转换子程序中用到了AX(AH,AL),BX,CX,DX(DH,DL)四个16位的寄存器。为什么进入子程序后,只对CX,DX寄存器作了压栈保护,而对AX,BX 寄存器没有呢?原因在于:AX,BX是用于参数传递目的的两个寄存器,而CX,DX却不是。进入子程序中压栈保护所用的寄存器一般指不用于参数传递目的的寄存器,所以对CX,DX作了压栈保护,而对AX,BX却没有。
另外,此题也可不用除法指令,而采用一连串的相减来实现,思路为:八位二进制数能够减去100的次数,就是转换的十进制数百位;同理,从余下的二进制数中再减去10的次数,便是十进制的十位;最后余下的就是十进制数个位。有兴趣的同学可自己试着实现。
结合上题与本题可以总结子程序的格式,子程序所放的位置,子程序的调用方法。
调用子程序命令为:
CALL
子程序以 <子程序名> PROC 开始。
例如:
TRANSFORM PROC
后面的 NEAR 表示在同一段内,可以省略,否则要用FAR。子程序结尾为:
RET
<子程序名> ENDP
例如,本子程序结尾:
RET
TRANSFORM ENDP
每个子程序开始应当给出说明:子程序名、功能、入口参数、出口参数、使用寄存器。既便于自己理解,也方便之后其他程序调用。使用寄存器只列举在本程序中使用了但未在子程序中保护且不是入口参数和出口参数中声明了的寄存器。
子程序用CALL命令调用,格式: CALL <子程序名>
子程序不能在代码中随意放,初学建议放在MOV AH,4CH 和 INT 21H 这二句之后。
3. 用子程序形式求解实验7第3题:在内存的源数据区SOURCE处有若干个ASCII码字符,要求将其中的英文大写字母变换为小写字母,其它字符不变换,并存放到结果数据区DEST处。
分析:英文大写字母‘A’—‘Z’,ASCII码值为41H—5AH。相应的小写字母为‘a’—‘z’,ASCII码值为61H—7AH。英文大写字母要转换为相应的小写字母只要ASCII码值加20H即可。
例如:‘B’─‘b’ 42H+20H=62H
我们将转换这一过程用子程序TRANBL来实现,参数传递借助存贮单元,其算法流程图如右图所示。
完整程序如下所示:
DATA SEGMENT
SOURCE DB 'BEIjin2008OLYMPIC'
COUNT EQU $-SOURCE ;字符个数
DEST DB COUNT DUP(?)
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:MOV AX,DATA
MOV DS,AX
CALL TRANBL
MOV AH,4CH
INT 21H
;* * * * * * *
;子程序名:TRANBL
;功能:大写字母转换为小写字母
;输入参数:数据区SOURCE处,字符个数由COUNT指明
;输出参数: 输出数在数据区DEST处
;* * * * * * *
TRANBL PROC NEAR
PUSH AX
PUSH CX
PUSH SI
PUSH DI
MOV SI, OFFSET SOURCE
MOV DI, OFFSET DEST
MOV CX,COUNT
AGAIN:MOV AL,[SI]
CMP AL,41H ;与大写字母‘A’比较
JB NEXT
CMP AL,5AH ;与大写字母‘Z’比较
JA NEXT
ADD AL,20H ;转换
NEXT:MOV [DI],AL
INC SI
INC DI
LOOP AGAIN
POP DI
POP SI
POP CX
POP AX
RET
TRANBL ENDP
CODE ENDS
END START
6. 求将一个16位二进制数转换为任意P(0到19)进制数,P从键盘输入并显示(超过9的数字按:10用A,11用B,……,19用I表示)。
分析:在前面例子中已经给出了16位二进制数转换为十进制、十六进制的例子,方法都是依次除进制数、取余、倒写。转换为任意P进制数的问题,只是将这些例题中的除数要更换为P的具体值。可以将这些转换程序改写成子程序,子程序中要用到寄存器DI、AX,使用寄存器不多,故设计P从主程序经BX带入、二进制数从主程序经SI带入。为具有实用价值,本题所涉及的P值及二进制数均从键盘输入,输入的都是ASCII码,需要变成二进制数,该工作分别设计子程序完成。
程序设计如下:
DATA SEGMENT
CHAR_BUF DB 6 ;缓冲区最大长度
DB ? ;实际输入字符个数
DB 6 DUP(0) ;输入缓冲区
BUF DB 17 DUP(0) ;输出缓冲区
CLEW1 DB 'PLEASE INPUT ALGORI X(0....65535) :', '$' ;提示信息1
CLEW2 DB 0DH,0AH,'PLEASE INPUT CARRY P(0....19) :', '$' ;提示信息2
ENTER1 DB 0DH,0AH, '$' ;回车换行
DATA ENDS
STACK SEGMENT STACK
DB 200 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME DS:DATA,CS:CODE,SS:STACK
START:MOV AX,DATA
MOV DS,AX
MOV DX,OFFSET CLEW1 ;提示信息1
MOV AH,9
INT 21H
CALL IMPOR1 ;输入二进制数子程序
MOV DX,OFFSET CLEW2 ;提示信息2
MOV AH,9
INT 21H
CALL IMPOR2 ;输入进制数P子程序
MOV DX,OFFSET ENTER1 ;回车换行
MOV AH,9
INT 21H
MOV DI, OFFSET BUF ;根据VERSIO入口要求使指针DI指向BUF缓冲区
ADD DI,17 ;假定BUF从0起准备17个单元,DI指向BUF尾
MOV BYTE PTR [DI],"$" ; 所有余数的最后加“$”
CALL VERSIO ;转换并显示子程序
MOV AH,4CH
INT 21H
;子程序名:IMPOR1
;程序功能:从键盘输入至多5个十进制数字变成二进制数(0到65535),回车表示输完
;入口参数:缓冲区首址:CHAR_BUF
;出口参数: 从键盘输入的数:SI
;使用寄存器:DI,DX,BX,CX,AX
IMPOR1 PROC NEAR
INPUT: MOV DX,OFFSET CHAR_BUF
MOV AH,0AH
INT 21H ;键盘输入字符串
MOV DI,OFFSET CHAR_BUF
MOV CL,[DI+1] ;取实际输入串长度
MOV CH,0
MOV AX,0 ;准备存放结果
MOV BX,10 ;乘10的乘数
MOV DH,0 ;清DX高8位
CHANGE: MUL BX ;(AX)乘10
MOV DL,[DI+2] ;取一个字符
SUB DL,30H ;变成数字
ADD AX,DX
INC DI
LOOP CHANGE
MOV SI,AX ;带出结果
RET
IMPOR1 ENDP
;子程序名:IMPOR2
;程序功能:从键盘输入至多二个十进制数字变成进制数P(2……19)
;入口参数:
;出口参数: 进制数P:BX
;使用寄存器:AX
IMPOR2 PROC NEAR
MOV AH,01H
INT 21H ;键盘输入1个字符,不考虑输入错
SUB AL,30H
CMP AL,1 ;如果第1个字符输入大于1,表示只1位
JA EXIT
MOV BL,10
MUL BL
MOV BL,AL
MOV AH,01H
INT 21H ;键盘输入第二个数字
SUB AL,30H
ADD AL,BL
EXIT: MOV AH,0
MOV BX,AX
RET
IMPOR2 ENDP
;子程序名:VERSIO
;程序功能:将16 位二进制数转换为P进制数并显示
;入口参数:数组BUF首址: DI,进制数P:BX,存放二进制数:SI
;出口参数:
;使用寄存器:AX
VERSIO PROC NEAR
MOV AX,SI
CHANGE1: DEC DI ;指针向前移
DIV BL ;(AX)除以P余数在AH中
ADD AH,30H ;余数变为对应的ASCII码值
CMP AH,39H ;是否是‘A’-‘F’
JBE NEXT
ADD AH,7 ;如果是‘A’-‘F’,再加7
NEXT: MOV BYTE PTR[DI],AH ;保存转换后的十六进制数结果
MOV AH,0
CMP AL,0 ;判断商是否为0?
JNZ CHANGE1 ;不为0转CHANGE1
MOV DX,DI
MOV AH,9
INT 21H
RET
VERSIO ENDP
CODE ENDS
END START
7. 求一个小于65535的整数N的因数分解,即将一个整数分解成质数的乘积。
分析:因数分解的方法是从I=2起,看能否整除N,如果能,则I为一个质数,记下整除后的数代换N并继续整除到不能整除为止。再将I加1后继续,……,直到I大于被除数为止。
;设数据区如下,求分解为质数放在PRIMEN中并显示。
DATA SEGMENT
M1 DW 10920
M2 DW 0
PRIMEN DW 10 DUP(0)
M DB 0
d db 5 dup(0)
DATA ENDS
STACK SEGMENT STACK
DB 200 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME DS:DATA,CS:CODE,SS:STACK
START:MOV AX,DATA
MOV DS,AX
CALL PRIME ;转分解质数子程序
LEA DI,D+5 ;指向显示数据区尾地址
LEA SI,PRIMEN ;指向质数数据区首地址
MOV CL,M ;欲显示的质数个数
MOV CH,0
LOOP1:MOV AX,[SI] ;取显示数据
CALL P1
ADD SI,2
LOOP LOOP1
MOV AH,4CH
INT 21H
;子程序名:PRIME
;程序功能:将字数据分解为质数
;入口参数:字数据:变量M1
;出口参数: 质数数组:PRIMEN
; 数组元素个数:M
;使用寄存器:AX
PRIME PROC
MOV DI,OFFSET PRIMEN
MOV AX,M1
MOV M2,AX
MOV CL,0
DISP1: MOV BX,2 ;从2开始分析质数
DISP2: MOV AX,M2
MOV DX,0
DIV BX
CMP DX,0 ;余数是否为0?
JNE DISP4
MOV [DI],BX ;保存一个质数
ADD DI,2 ;准备存放下一个质数
MOV M2,AX
INC CL
DISP3: MOV DX,0
DIV BX
CMP DX,0 ;继续取出同一个质数不再保存
JNE DISP4
MOV M2,AX
JMP DISP3
DISP4: CMP BX,M2
JA EXIT
INC BX
JMP DISP2
EXIT: MOV M,CL
RET
PRIME ENDP
;子程序名:p1
;功能: 以十进制形式显示任意字
;输入参数: 显示字在AX中。要求在数据区建5字节的显示数据区,
;要求用DI指向其尾部。
;输出参数:
;* * * * * * *
P1 PROC
PUSH BX
PUSH DX
PUSH DI
PUSH CX
MOV BX,10 ;准备除以10
CHANGE10: MOV DX,0 ;将除数的高16位(在DX中)清0,DX存放余数
DIV BX ;DX,AX除以BX (BX=10)
ADD DL,30H ;余数变为ASCII码值
MOV BYTE PTR[DI],DL ;存储十进制数
DEC DI ;到上一单元
CMP AX,0 ;是否为0?
JNZ CHANGE10 ;不为0转CHANGE10
MOV CX,5 ;准备连续显示5个数
CHANGE11:MOV DL,BYTE PTR[DI] ;取出存放在BUF中的十进制数到DL供显示
MOV AH,2 ;显示1个字符
INT 21H
INC DI
LOOP CHANGE11
POP CX
POP DI
POP DX
POP BX
RET
P1 ENDP
CODE ENDS
END START
8. 计算:X的平方根与有的平方根的和
其中x、y为整型数据,分别存放在XBUF和YBUF单元中,所得结果f存放在FBUF中。要求将FBUF中数据在屏幕上显示。
分析:求一个整数平方根可以通过将该整数依次减去1、3、5、7…(n-1)的奇数,所够减的次数即为平方根值。
例如求16的平方根时可这样进行:
16-1=15 够减1次
15-3=12 够减2次
12-5=7 够减3次
7-7=0 够减4次
所以16=4
求平方根的过程我们用子程序SQROOT来实现,参数的传递借助堆栈。求平方根子程序算法流程图如图所示。
DATA SEGMENT
XBUF DW 81
YBUF DW 100
FBUF DW ?
DATA ENDS
STACK SEGMENT
TOP DW 20 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START:MOV AX,DATA ;给DS赋值
MOV DS,AX
MOV SI,XBUF
CALL SQROOT
MOV AX,SI
MOV SI,YBUF
CALL SQROOT
ADD SI,AX
CALL DISPLAY16
MOV AH,4CH
INT 21H
;* * * * * * *
;子程序名:SQROOT
;功能:求平方根运算
;输入参数:SI
;输出参数:SI
;* * * * * * *
SQROOT PROC NEAR
PUSH AX
PUSH BX
PUSH CX
MOV AX,SI ;取被开方数
SUB CX,CX ;CX置0
AGAIN:MOV BX,CX ;下面三条指令使BX=2*CX+1
ADD BX,BX
INC BX
SUB AX,BX
JC OVER ;不够减转OVER
INC CX
JMP AGAIN
OVER:MOV SI,CX ;平方根放回原堆栈区
POP CX
POP BX
POP AX
RET
SQROOT ENDP
;子程序名:DISPLAY16
;功能:以移位方式显示16进制数
;占用寄存器:BX,CL,AX,DX
;输入参数:要显示数据在SI中
;输出参数:无
DISPLAY16 PROC
MOV BX,4 ;每个字显示4个16进制字符
CHANGE10: MOV CL,4 ;准备左移4位(除以16)
ROL SI,CL ;SI循环移4位
MOV DX,SI ;转存到DX
AND DX,0FH ;清高12位
ADD DL,30H ;一个16进制数变为ASCII码值
CMP DL,3AH ;是否小于10?
JB CHANGE12 ;如果小于10转显示
ADD DL,7 ;否则为A到F之间数,加7
CHANGE12: MOV AH,2 ;显示1个字符
INT 21H
DEC BX ;处理下一个字符
CMP BX,0 ;是否为0?
JNZ CHANGE10 ;不为0转CHANGE10
RET
DISPLAY16 ENDP
CODE ENDS
END START
本题如果采用堆栈传递参数的程序如下所示,请注意堆栈的变化。
DATA SEGMENT
XBUF DW 81
YBUF DW 100
FBUF DW ?
DATA ENDS
STACK SEGMENT
TOP DW 20 DUP(?)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START:MOV AX,DATA ;给DS赋值
MOV DS,AX
MOV AX,STACK ;给SS赋值
MOV SS,AX
MOV SP,SIZE TOP ;确定SP的指向位置
MOV AX,XBUF ;取被开方数X
PUSH AX ;压入堆栈
CALL SQROOT
POP AX ;取X平反根暂存AX中
MOV DX,YBUF ;取被开方数Y
PUSH DX ;压入堆栈
CALL SQROOT
POP DX ;取Y平反根存入DX中
ADD AX ,DX ; +
MOV FBUF,AX ;送入FBUF单元
MOV SI,FBUF
CALL DISPLAY16
MOV AH,4CH
INT 21H
;* * * * * * *
;子程序名:SQROOT
;功能:求平方根运算
;输入参数:压入堆栈区
;输出参数:位于堆栈区
;* * * * * * *
SQROOT PROC NEAR
PUSH BP
MOV BP,SP ;定位
PUSH AX
PUSH BX
PUSH CX
MOV AX,[BP+4] ;取被开方数
SUB CX,CX ;CX置0
AGAIN:MOV BX,CX ;下面三条指令使BX=2*CX+1
ADD BX,BX
INC BX
SUB AX,BX
JC OVER ;不够减转OVER
INC CX
JMP AGAIN
OVER:MOV [BP+4],CX ;平方根放回原堆栈区
POP CX
POP BX
POP AX
POP BP
RET
SQROOT ENDP
;子程序名:DISPLAY16
;功能:以移位方式显示16进制数
;占用寄存器:BX,CL,AX,DX
;输入参数:要显示数据在SI中
;输出参数:无
DISPLAY16 PROC
MOV BX,4 ;每个字显示4个16进制字符
CHANGE10: MOV CL,4 ;准备左移4位(除以16)
ROL SI,CL ;SI循环移4位
MOV DX,SI ;转存到DX
AND DX,0FH ;清高12位
ADD DL,30H ;一个16进制数变为ASCII码值
CMP DL,3AH ;是否小于10?
JB CHANGE12 ;如果小于10转显示
ADD DL,7 ;否则为A到F之间数,加7
CHANGE12: MOV AH,2 ;显示1个字符
INT 21H
DEC BX ;处理下一个字符
CMP BX,0 ;是否为0?
JNZ CHANGE10 ;不为0转CHANGE10
RET
DISPLAY16 ENDP
CODE ENDS
END START
思考:请编写求解一元二次方程AX2+BX+C=0的程序。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-4 04:06
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社