题目描述:
计算器设计。在PC机上实现从键盘读入数据,并完成加、减、乘、除的计算。要求:1)屏幕上显示一个主菜单,提示用户输入相应的数字键,分别执行加、减、乘、除4种计算功能和结束程序的功能。若按其他键,则显示提示输入出错并要求重新输入,并继续显示主菜单。分别按数字键“1”、“2”、“3”,则执行相应子模块1、2、3,进行两个字节与两个字节的加法、减法和乘法运算,并在屏幕上显示运算结果。按数字键“4”,执行模块4,进行两字节除一个字节的除法运算。按数字键“5”,程序退出,返回DOS;2)要使用到子程序。
代码:
enterline macro ;定义回车换行的宏指令
mov dl,13
mov ah,2
int 21h
mov dl,10
mov ah,2
int 21h
endm
DATAS SEGMENT
menus db' MENU$'
input db' Please select a function!$'
number db' 1-add, 2-sub, 3-mult, 4-div, 5-exit$'
a db'You are adding. Please enter two numbers separated by a space$'
s db'You are subtracting. Please enter two numbers separated by a space$'
m db'You are in the process of multiplication. Please enter two numbers separated by a space$'
d db'You are in the process of division.Please enter two numbers separated by a space$'
e db'Exiting program$'
err db 'Illegal input! Please Try Again$'
again db'Invalid input, try again.$'
overout db'The number overflowed, try again$'
err1 db'The result is overflowed, try again$'
command db ?
flag db ?
temp dw 0
buf db 20,?,20 dup(0) ;定义键盘接收字符缓冲区,最多接收19个字符
ff db ? ;输出的判断前导0的标志
op1 dw ? ;定义两个操作数(16位)
op2 dw ?
hex_buf db 4 dup(30h),'H'
crlf db '$' ;这是一个字符串的结尾符号,紧跟在缓冲区后可形成字符串,丢失后会产生
DATAS ENDS
STACKS SEGMENT
;此处输入堆栈段代码
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
;此处输入代码段代码
;注意每次做完了计算都要回到主菜单
menu: ;菜单部分
lea dx,menus
mov ah,9
int 21h
enterline
lea dx,input
mov ah,9
int 21h
enterline
lea dx,number
mov ah,9
int 21h
enterline
mov ah,1 ;字符存在AL里面
int 21h
judge: ;判断执行哪个功能
cmp al,'1'
je a1
cmp al,'2'
je a2
cmp al,'3'
je a3
cmp al,'4'
je a4
cmp al,'5'
je stop
;上面匹配都不成功,则把输入当成无效的输入,要求重新输入
enterline
lea dx,again
mov ah,9
int 21h
enterline
jmp menu
a1: ;加法模块 ;设计完加法子程序后把下面这部分也封装进去
enterline ;这部分是输出提示性语句
lea dx,a
mov ah,9
int 21h
enterline
call inputi ;调用输入的子程序
cmp flag,1
je a1 ;由于错误输入跳回a1重新进行加法操作
cmp flag,2
je a1 ;由于溢出跳回a1重新输入
call addi ;调用加法子程序
cmp flag,2
je over
call outi
jmp menu ;执行完后跳回主菜单
over:
mov ax,op1
call to16str;将十进制转化为十六进制
mov dx,offset hex_buf
mov ah,9
int 21h
mov ax,op2
call to16str;将十进制转化为十六进制
mov dx,offset hex_buf
mov ah,9
int 21h
enterline
jmp menu ;执行完后跳回主菜单
a2: ;减法模块
enterline
lea dx,s
mov ah,9
int 21h
enterline
call inputi ;调用输入的子程序
cmp flag,1
je a2 ;由于错误输入跳回a1重新进行加法操作
cmp flag,2
je a2 ;由于溢出跳回a1重新输入
call subi
call outi
jmp menu ;执行完后跳回主菜单
a3: ;乘法模块
enterline
lea dx,m
mov ah,9
int 21h
enterline
call inputi ;调用输入的子程序
cmp flag,1
je a3 ;由于错误输入跳回a1重新进行加法操作
cmp flag,2
je a3 ;由于溢出跳回a1重新输入
call multi
mov ax,op1
call to16str;将十进制转化为十六进制
mov dx,offset hex_buf
mov ah,9
int 21h
mov ax,op2
call to16str;将十进制转化为十六进制
mov dx,offset hex_buf
mov ah,9
int 21h
enterline
jmp menu ;执行完后跳回主菜单
a4: ;除法模块
enterline
lea dx,d
mov ah,9
int 21h
enterline
call inputi ;调用输入的子程序
cmp flag,1
je a4 ;由于错误输入跳回a1重新进行加法操作
cmp flag,2
je a4 ;由于溢出跳回a1重新输入
call divi
cmp flag,1
je a4 ;由于除数可能输0导致重新输入
call outi
jmp menu ;执行完后跳回主菜单
stop: ;程序的结束
enterline
lea dx,e
mov ah,9
int 21h
MOV AH,4CH
INT 21H
inputi proc ;输入子程序如下
mov flag,0 ;初始化flag
lea dx,buf ;从键盘接收输入数值放入buf缓冲区(输入操作)
mov ah,10
int 21h
enterline ;回车换行
mov cl,buf+1 ;获取实际键入字符数,置于CX寄存器中
xor ch,ch ;ch清0
xor di,di ;累加器清0
xor dx,dx ;dX寄存器清0
mov bx,1 ;由于从个位数开始算起,因而将所乘权值设为1
lea si,buf+2 ;将si指向接收到的第1个字符位置
add si,cx ;因为从个位算起,所以将si指向最后1个接收到的个位数
dec si ;往回减1使其指向字串最后一个元素
;cov是检测并生成第一个数字的步骤
cov:mov al,[si] ;取出个位数给al
cmp al,' '
jz next1 ;遇见空格则跳转
cmp al,'0' ;边界检查:如果输入不是0-9的数字,就报错
jb wrong
cmp al,'9'
ja wrong
sub al,30h ;将al中的ascii码转为数字
xor ah,ah
mul bx ;乘以所处数位的权值
cmp dx,0 ;判断结果是否超出16位数范围,如超出则报错
jne yichu
add di,ax ;将形成的数值叠加放在累加器di中
jc yichu ;CF是进位标志
mov ax,bx ;将BX中的数位权值扩大10倍,此处需要借助ax来实现
mov bx,10
mul bx
mov bx,ax
dec si ;si指针减1,指向前一数位
loop cov ;按CX中的字符个数计数循环
;跳到次处表明第一个数字已经生成,接着去检测第二个数字
next1:
mov op1,di ;将结果储存在op1中4
xor ax,ax
xor di,di ;累加器清0
xor bx,bx
mov bx,1 ;由于从个位数开始算起,因而将所乘权值设为1
dec si ;向前移动一格位置
dec cx ;遇到空格cx相应的减少1
;cov2是检测并生成第2个数字
cov2:
mov al,[si] ;取出个位数给al
cmp al,'0' ;边界检查:如果输入不是0-9的数字,就报错
jb wrong
cmp al,'9'
ja wrong
sub al,30h ;将al中的ascii码转为数字
xor ah,ah
mul bx ;乘以所处数位的权值
cmp dx,0 ;判断结果是否超出16位数范围,如超出则报错
jne yichu
add di,ax ;将形成的数值放在累加器di中
jc yichu ;CF是进位标志
mov ax,bx ;将BX中的数位权值扩大10倍,此处需要借助ax来实现
mov bx,10
mul bx
mov bx,ax
dec si ;si指针减1,指向前一数位
loop cov2 ;按CX中的字符个数计数循环
next2:
mov op2,di ;将结果储存在op2中
jmp return ;结束后跳到return部分
wrong:
lea dx,err
mov ah,9
int 21h
mov flag,1
jmp return
yichu:
mov flag,2
lea dx,overout
mov ah,9
int 21h
return:
ret
inputi endp
addi proc ;加法子程序(16位数相加)
xor bx,bx
xor cx,cx
mov bx,op2
mov cx,op1
add bx,cx
cmp bx,op1
jb ex
cmp bx,op2
jb ex
jmp addret
ex: ;表示结果高于16位的加法操作
mov flag,2
mov op2,bx
mov op1,1 ;表示进位
addret:
ret
addi endp
subi proc ;减法子程序(16位数相减)
xor bx,bx
xor cx,cx
mov bx,op2
mov cx,op1
cmp bx,cx ;比较大小
jb fuhao
sub bx,cx ;结果储存在bx中
jmp subret
fuhao:
mov dx,'-'
mov ah,2
int 21h
sub cx,bx
mov bx,cx
subret:
ret
subi endp
multi proc ;乘法子程序(16位数相乘)
xor ax,ax
xor cx,cx
mov ax,op2
mov cx,op1
mul cx ;结果存在dx:ax里面
mov op1,dx
mov op2,ax ;暂存在op1和op2
ret
multi endp
divi proc ;除法子程序(16位数除以8位数)
xor bx,bx ;注意 该程序的除数不能超过255 并且商也不能超过255 他们的承载能力只有8位
xor cx,cx
xor ax,ax
mov cx,op1 ;实际上存在cl中
cmp cx,255 ;让cx的值处于0~255之间(因为寄存器是8位的)
ja divwrong
cmp cl,0
je divwrong
mov al,255 ;让255和op1相乘,与op2比较,若小于op2则会发生divide error因此判断非法
mul cl
cmp ax,op2
jb overflow
mov ax,op2
div cl ;字除以1字节型除法,商存在al中
mov ah,0 ;清除ah中的内容
mov bx,ax
jmp divret
divwrong:
lea dx,err
mov ah,9
int 21h
mov flag,1
overflow:
lea dx,err1
mov ah,9
int 21h
mov flag,1
divret:
ret
divi endp
outi proc
mov ax,bx ;待输出的数先存在bx里面,在给ax
mov bx,10000 ;初始数位权值为10000
mov ff,0 ;每次都赋初值0
cov1:xor dx,dx ;将dx:ax中的数值除以权值
div bx
mov cx,dx ;余数备份到CX寄存器中
cmp ff,0 ;检测是否曾遇到非0商值
jne nor1 ;如遇到过,则不管商是否为0都输出显示
cmp ax,0 ;如未遇到过,则检测商是否为0
je cont ;为0则不输出显示
nor1:
mov dl,al ;将商转换为ascii码输出显示
add dl,30h
mov ah,2
int 21h
mov ff,1 ;曾遇到非0商,则将标志置1
cont:
cmp bx,10 ;检测权值是否已经修改到十位了
je outer ;如果相等,则完成最后的个位数输出显示
xor dx,dx ;将数位权值除以10
mov ax,bx
mov bx,10
div bx
mov bx,ax
mov ax,cx ;将备份的余数送入AX
jmp cov1 ;继续循环
outer:
mov dl,cl ;最后的个位数变为ascii码输出显示
add dl,30h
mov ah,2
int 21h
enterline
ret
outi endp
to16str proc;功能:将十进制转化为十六进制
mov bx,ax;将带转换的十进制数赋值给bx
mov si,offset hex_buf ;将字符串的首地址赋值给si
mov ch,4 ;将10进制转为4位16进制数,每次操作1位,ch为当前还需要转换的位数
loop_trans:
mov cl,4
rol bx,cl;此处cl的值为4,代表将BX中的值循环左移4位,bx中做该4位移动到最低4位
mov al,bl;从高到低提取四位二进制数送入al,和0fh进行与操作得bl中低4位
and al,0fh
add al,30h;al=0~9,加30h转化为ascii码
cmp al,3ah
jl next_trans
add al,7 ;al>9,加37h转化为ascii码,转换为字母A~F
next_trans:
mov [si],al ;将转换好的ascii码赋值给字符串的si位置处
inc si ;si向后移动一位
dec ch ;代表还需转换的位数减1
jnz loop_trans;注意,这儿只能用dec运算对标志位的设置来判断循环与否
;因为cl被用来存放位移数了
ret
to16str endp
CODES ENDS
END START
结果示例: