汇编语言之计算器设计

题目描述:  
  计算器设计。在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 他们的承载能力只有8xor 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进制转为416进制数,每次操作1,ch为当前还需要转换的位数
    loop_trans:
    mov cl,4
    rol bx,cl;此处cl的值为4,代表将BX中的值循环左移4位,bx中做该4位移动到最低4位
    
    mov al,bl;从高到低提取四位二进制数送入al,0fh进行与操作得bl中低4and 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

结果示例:
运行结果

猜你喜欢

转载自blog.csdn.net/qq_44174803/article/details/105458916