一、实验内容
编写计算N!的程序。数值N由键盘输入,结果在屏幕上输出,N的范围为0~65535,即刚好能被一个16位寄存器容纳。
二、实验说明
编制附乘程序的难点在于随着N的增大,其结果远不是寄存器所能容纳。这就必须把结果放在一个内存缓冲区中。然而乘法运算只能限制于两个字相乘,因此要确定好算法,依次从缓冲区中取数,进行两字相乘,并将DX中的高16位积作为产生的进位。
程序根据阶乘的定义:N!=N×(N-1)×(N-2)×……×2×1,从左入右依次计算,结果先将BP初始化为存放N值,然后使BP-1,以后BP依次减1,直至变化为1.每次让BP与BUF中的字单元按由低到高的次序相乘,低位结果AX仍保存在相应的BUF字单元中,最高位结果DX则进到进位单元CY中,以作为高字单元相乘时从低字来的进位,初始化CY=0,计算结果的长度随着乘积运算而不断增长,由字单元LEN指示。当最高字单元与BP相乘时,若DX不为0,则结果长度要扩展。
三、实验流程图
四、实验程序
CRLF MACRO
MOV AH,02H
MOV DL,0DH
INT 21H
MOV AH,02H
MOV DL,0AH
INT 21H
ENDM
DATAS SEGMENT
MES1 DB 'Please input number N','$'
MES2 DB 'The result is: $'
BUF DW 256 DUP (0)
LEN DW 1
CY DW ?
DATAS ENDS
STACKS SEGMENT
DW 32 DUP(?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
MOV AH,9 ;显示提示
LEA DX,MES1
INT 21H
CRLF
CALL GETNUM ;DX中存放读到的键盘输入值
MOV BP,DX ;N值送BP
CMP BP,0
JZ L4 ;BP=0跳转
CMP BP,1
JZ L4
LEA SI,BUF ;SI指向BUF首址
MOV [SI],DX ;缓冲区初始化值为键盘输入N
D1: DEC BP ;BP-1=1跳转
CMP BP,1
JZ L5
XOR BX,BX ;BX清0,每次相乘从最低位开始
MOV WORD PTR CY,0 ;同时CY每次要清零
MOV CX,LEN ;CX送循环,判断占了多少个子单元,循环多少次
D2: MOV AX,[SI+BX]
MUL BP
ADD AX,CY ;加低位进位
JNC D3 ;结果无进位跳转
INC DX ;有进位,积高位加进位
D3: MOV [SI+BX],AX ;存低位
MOV CY,DX ;高位保存在CY,乘高位单元时加上
INC BX
INC BX ;一个字长度
LOOP D2
CMP DX,0 ;判断DX两次运算后是否为0
JZ D1 ;DX高位为0跳D1
INC WORD PTR LEN ;DX高位不为0则长度加1,DX送下一个单元
MOV [SI+BX],DX
JMP D1
L4: MOV SI,OFFSET BUF ;BUF存1
MOV WORD PTR [SI],1
L5: MOV AH,09H ;显示MES2单元内容
MOV DX,OFFSET MES2
INT 21H
MOV CX,LEN
MOV BX,CX ;BX=BUF长度
DEC BX ;BX-1
SHL BX,1
L6: MOV AX,[SI+BX]
CALL DISPLAY1 ;从高位显示结果
DEC BX
DEC BX
LOOP L6
MOV AH,4CH
INT 21H
GETNUM PROC NEAR
XOR DX,DX
L1: MOV AH,1
INT 21H
CMP AL,0DH
JZ L2
CMP AL,40H
JL L3 ;小于跳转
SUB AL,07H
L3: SUB AL,30H
MOV CL,04H
SHL DX,CL
XOR AH,AH
ADD DX,AX
JMP L1
L2: PUSH DX
CRLF
POP DX
RET
GETNUM ENDP
DISPLAY1 PROC NEAR
PUSH BX
PUSH CX
PUSH DX
PUSH AX
MOV AL,AH
CALL DISPLAY2
POP AX
CALL DISPLAY2
POP DX
POP CX
POP BX
RET
DISPLAY1 ENDP
DISPLAY2 PROC NEAR ;显示字符(AL)
MOV BL,AL
MOV DL,BL ;执行MOV AH,02,AX=0200
MOV CL,04 ;执行CALL调用,AL=30H
SHR DL,CL
CALL DISPLAY3 ;显示高位
MOV DL,BL
AND DL,0FH
CALL DISPLAY3 ;显示低位
RET
DISPLAY2 ENDP
DISPLAY3 PROC NEAR ;显示一位(DL=0XH)
ADD DL,30H
CMP DL,3AH
JB A1
ADD DL,07H
A1: MOV AH,02H
INT 21H
RET
DISPLAY3 ENDP
CODES ENDS
END START
五、实验现象
六、实验小结
①、在使用CALL调用子程序时,系统会给AL赋30H,所以在DISPLAY2中第一句MOV BL,AL
不可或缺。
②、D1中BX,CY每次要清0,不可以省略。
③、输入要是4位,0000~FFFF,输出是16进制。
④、一开始读键盘输入的值的想法是,用十进制数读入,高位键值乘10,在加上低位键值,按这方法依次读低位。程序用的是读入十六进制数,先读高位,左移四位,在读低位。这两种方法都可以使用,且效果一样,计算机保存的内容都是一样的,都是某个数的二进制(PS:用十进制数读入的话,读入的是BCD码,但是保存的话是以二进制保存的),但是显示的话用十六进制显示会很方便。