简单计算机程序设计.docx
《简单计算机程序设计.docx》由会员分享,可在线阅读,更多相关《简单计算机程序设计.docx(27页珍藏版)》请在冰点文库上搜索。
简单计算机程序设计
专业综合课程设计任务书
学生姓名:
aaaaaa专业班级:
电信1102班
指导教师:
李达工作单位:
信息工程学院
题目5:
简单计算器程序设计
初始条件:
(1)提供实验室机房及PC机;
(2)微机原理与通信接口的基本理论学习。
要求完成的主要任务:
(包括课程设计工作量及其技术要求,以及说明书撰写等具体要求):
(1)编写一个程序实现十进制加减乘除运算;
(2)在命令提示后输入算式,运算符号不超过四个;
(3)回车后进行计算并以十进制显示结果;
(4)程序采用汇编语言在PC机上完成;
(5)写出系统需求分析、原理图和程序实现流程图;
(6)对结果进行分析;
(7)要求阅读相关参考文献不少于5篇;
(8)根据课程设计有关规范,按时、独立完成课程设计说明书。
时间安排:
(1)布置课程设计任务,查阅资料,确定方案四天;
(2)进行编程设计一周;
(3)完成课程设计报告书三天;
指导教师签名:
年月日
系主任(或责任教师)签名:
年月日
1.引言
1.1设计目的
本次课程设计的题目是用汇编语言实现一个简单的计算器,要求:
编写一个程序,每运行一次可执行程序,可以实现加减乘除四则运算。
计算器是最简单的计算工具,简单计算器具有加、减、乘、除四项运算功能。
通过使用汇编语言设计实现简单计算器,以此进一步了解和掌握对数据存储,寄存器的使用,加减乘除相关指令以及模块的调用等汇编语言知识的有效运用。
本次课程设计以实现一个基本功能完善,界面友好,操作简便易行的计算器为最终目的。
通过对具有加减乘除基本功能的计算器的设计实现,学会使用汇编语言实现输入输出模块的设计,模块合理调用的设计,加减乘除运算的判断以及退出程序的判断的设计。
通过对各种指令的合理使用,熟悉并加深对各种指令的用法。
学会使用汇编语言设计各个功能模块。
当实现各个程序模块后,学会通过程序的调用最终实现一个具有基本计算功能的简单计算器。
简单计算器项目设计,主要是对汇编语言运算的应用,要完成该项目必须对汇编的四则运算语法和逻辑有深入的了解。
除此之外,还必须对汇编各种指令、伪指令集和计算机系统有很好的了解。
1.2设计要求及需求分析
1.2.1设计要求
(1)编写一个程序实现十进制加减乘除运算;
(2)在命令提示后输入算式,运算符号不超过四个;
(3)回车后进行计算并以十进制显示结果
1.2.2需求分析
用8086汇编语言编写一个能实现四则混合运算计算器程序。
程序能实现键盘十进制运算表达式的输入和显示(例如输入:
“1+2*3-4”),按回车键后输出十进制表示的运算结果。
该计算器能够对输入的符号进行实时的读取和分析,它接收0-9,+、-,*,/,=符号,用来对操作数进行运算,如果输入的操作数大于9,还需要对数字进行数码转换;
如果识别用户输入ESC,那么计算器会提示退出消息,如果输入是L,它将会执行清屏操作。
程序将获取的有效运算符保存在内存中,当获取到回车键后,即刻进行运算操作,最终将运算结果显示在计算机屏幕上。
1.3软件运行环境
使用的设备及软件为8086兼容机及轻松汇编开发软件。
2.总体设计分析
2.1题目分析
根据题目要求,可以把程序的工作过程划分为运算表达式输入、计算式运算、结果输出三部分。
因此在编写程序时可以按此把程序大致划分为三个模块。
总体设计流程图如下图2.1所示
图2.1软件总体设计流程图
2.2设计概述
输入模块分为包含运算表达式合法性检查,只有输入+,-,*,/,=,0-9,ESC,L,才算合法,其中ESC是退出程序功能,L是清屏功能
表达式运算模块包含,数值正负性判断,ASCII码转换,数值计算模块。
输出模块分为两个部分,第一个部分是程序启动时的提示语句输出,第二部分是运算结果进行转换输出。
详细的流程和设计方案将在设计步骤中说明。
3.设计步骤
3.1运算表达式输入
用户通过键盘输入的运算表达式为一个ASCII码字符串,字符串的最后一个字符是“=”号。
对于这个运算表达式,“+、-、*、/、0~9、=,ESC,L”是合法的表达式内容,其他的字符则是无法进行运算的非法内容,因此需要首先进行表达式合法性检查。
另外,由于计算机能进行计算的是2进制的补码,因此还需要把以ASCII码表示的数值转换为补码的形式并加以保存。
当然,控制运算方式的符号也要进行保存。
因此,“运算表达式输入”这个模块可以细化为:
表达式合法性检查、数值的ASCII码到补码转换及保存、符号的保存三个小部分,如图3-1所示。
图3-1“运算表达式输入”的流程图
3.1.1运算表达式合法性的检查方法
观察“ASCII字符编码表”,可以发现“+、-、*、/”的ASCII码由2AH到2FH,而“0~9”的ASCII码则由30H到39H,因此只需对输入的字符一个一个地进行数值范围比较,看看是否处于28H~39H这个范围里面,即可区分输入的表达式是否合法,流程图如图5-2所示。
此流程图是采用循环输入字符的方法,每输入一个字符即进行判断。
图3-2运算表达式合法性检查
3.2数值的补码转换方法
要进行数值的ASCII码到补码的转换,首先就得判断输入的字符是数值还是符号。
根据上文所提,“+、-、*、/”的ASCII码由2AH到2FH,而“0~9”的ASCII码则由30H到39H,只需比较字符是否小于等于2FH(或小于30H)即可判断是否为符号,否则则是数值,如图3-2所示。
众所周知,要把一个ASCII码数值转换为二进制补码的形式,只需要对其减30H即可实现。
但如果输入的是多位数,例如123,那么计算机获得的是31H、32H、33H三个字节,即使分别对这三个字节进行减30H操作,也只是获得1、2、3三个数而已。
实际上可以利用加权的方法合并这几个数:
123=1×100+2×10+3×1
但另一个问题是,由于输入是随机的,即输入的运算数有多少位是未知的,因此无法使用上面的方面静态确定每一位的权重。
这里介绍的方法是,每输入运算数的一位,则把前面的合并结果(称为原值)乘以10再与这一位相加,实现动态的加权合并。
例如:
令原值为0,输入1,结果为:
0×10+1=1
输入2,结果为:
1×10+2=12
输入3,结果为:
12×10+3=123
即:
123=(((0×10+1)×10+2)×10)+3。
数值的补码转换流程如图3-3所示,当然,在获得第一个数值输入前要先把原值设置为0。
图3-3数值的补码转换流程图
3.3表达式计算
由于运算表达式有多个数值和符号,而符号有不同的优先级别,因此上文提到的数值保存和符号保存应该分开两个地方进行保存,这样有利于表达式的计算算法设计。
下面把“+、-、*、/”称为运算符,+、-的运算优先级要低于*、/,在运算时要先处理优先级高的运算。
图3.4运算表达式的存储举例
观察图3.4的三条运算表达式,再联系四则混合运算的优先原则,可以归纳出几点:
数值的数量是运算符的数量加1(优先符不算),第1个运算符代表第1、2个数值的运算操作,第N个运算符代表第N、N+1个数值的运算操作……
每进行一次运算,相应的运算符即被消除,而参与运算的两个数值合并为一个数值,仍然满足(a)。
例如图3.4(a),当完成乘法运算后,数值存储区有408、56两个数,符号存储区有“+”一个运算符。
括号(优先符)的作用是把括号内的运算符的优先级别提高到比外部高。
因此,要实现运算表达式的运算,最重要的就是确定所有运算符的优先级别。
下面讨论运算符优先级别的编程设计方法。
3.3.1运算优先级别的静态确定法
此方法是完成了把整条运算表达式全部存入数值存储区和符号存储区后才开始对运算符优先级进行判断的方法:
设置“*、/”的优先级为2、“+、-”的优先级为1;
运用优先级别静态确定法处理图3.4的三条表达式的运算符,结果如图3.5所示。
其中图3.5的“12*34+56”,其数据与优先级如图3.5所示。
由于*的优先级高于+,那么计算时先计算12*34
图3.5运算符的静态优先级别
最后,由于四则混合运算遵循从左往右计算的原则,即相同优先级别的运算符靠左的优先。
因此,只需计算出符号存储区里面的所有运算符的优先级别,然后根据优先级的大小先后执行运算符对应的运算即可实现计算(当然每进行一次运算,相应的运算符即被消除,而参与运算的两个数值合并为一个数值)。
当数值存储区里面剩下一个数值时,运算结束,这个最后的数值就是运算的最终结果。
分析运算结果的特点:
运算结果为一个2进制补码,整数,如果数据长度为16位,则运算结果范围是:
-32768~32767。
运算结果的输出要解决的主要问题是:
正负数区分、补码到ASCII码转换并输出显示。
运算结果的输出流程如图3.6所示。
图3.6结果输出流程图
3.3.2正负数区分
运算结果有三种情况:
正整数、负整数、零。
运算结果以补码形式对这三种情况进行统一的存储,但显示输出时则有所不同。
负整数前面需要显示“-”号,因此需要对运算结果的符号进行判断。
另一方面,正整数和零的补码与原码相同,而负整数的补码则不一样。
把负整数进行取补码运算,把它转换为原码,可以实现运算结果统一的ASCII码转换输出方法,而不需要分别为正整数和零、负整数分别设计两个不同的ASCII码转换程序,如图3.7所示。
图3.7正负数区分流程图
3.3.3补码到ASCII码转换
计算结果在屏幕上的输出显示实际上是ASCII码的输出显示。
假设程序采用的数据长度为16位,则运算结果范围是:
-32768~32767,即屏幕最多得显示5位ASCII码。
由于上文已经把结果统一为原码,下面介绍如何把原码转换为ASCII码。
这个转换过程实际上跟上文的“数值的补码转换方法”是相反操作。
例如要把123在屏幕上输出显示,即要把123的百位、十位、个位分离,得到1、2、3,然后转换为31H、32H、33H三个ASCII码。
众所周知,把一位数转换为ASCII码只需加30H即可,下面介绍把一个多位数的各位分离的方法。
(a)除十法
分离方法是:
对一个多位数进行除10处理,得到的余数即为个位数,而商则是删除个位后的多位数。
对商反复进行除10处理,直到商为0为止,即可把各位数分离。
例如对123进行除十法处理:
123/10,商是12,余数是3
12/10,商是1,余数是2
1/10,商是0,余数是1
可见经过三次除十计算,得到的三个余数刚好就是对123的各位的分离结果。
接着只需分别对这些余数加30H即可转换为ASCII码,实现输出转换。
除十法的优点是不需要理会要输出的数值有多少位,不断除以10直到商为0即可;缺点是得到的余数的顺序跟输出的方向相反,不方便输出。
例如上例得到的三个余数的顺序是3、2、1,加30H转换输出后屏幕显示为“321”,跟期望显示的顺序相反,要作进一步处理。
处理方法是把余数放进堆栈里面,然后再出栈显示。
由于堆栈是先进后出的,即可解决该输出的顺序问题。
图3.8除十法流程图
(b)除最高位法
分离方法是先除以10位数-1,得到的商即为最高位,余数为删除最高位后的多位数。
接着令余数除以10位数-2,得到的商为次高位,……。
例如123,其位数是3(个位、十位、百位),则计算过程为:
123/103-1,商是1,余数是23
23/103-2,商是2,余数是3
3/103-3,商是3,余数是0
可见经过三次计算,得到的三个商刚好是对123的各位的分离结果,而且顺序跟输出方向相同。
可以直接加30H转换输出,屏幕显示为“123”。
该方法的缺点是:
必须首先确定要输出的数值有多少位,编程者必须十分清楚需要输出的数值的数值范围。
本次软件设计过程中用的是(a)除十法
图3.9除最高位法流程图
3.3.4除法运算
除法运算不同于其他的几种运算,因为除法运算中可能会产生小数,在汇编中没有直接获取小数的除法运算指令,一般除法我们用DIV指令,用DIV做除法运算,根据除数占字节数不同,结果保存的方法也不一样。
被除数默认放在AX或(DX和AX)中,如果除数为8位,被除数为16位,被除数默认在AX中存放,如果除数为16位,被除数为32位,被 除数则在(DX和AX)中存放,DX存放高16位,AX存放低16位。
如果除数是8位,则AL存储除法操作的商,AH存储除法操作的余数;如果除数是16位,则AX存储除法操作的商,DX存储除法操作的余
两个整数相除得到小数时,虽然我们不能直接得到小数部分,但是我们可以通过不断的将余数除以除数的方法得到小数部分,而将小数部分整数部分分开存放,分开显示,是一种可取的方法。
如图3.10所示,是具体操作过程
例求4/3的结果
图3.10除法取小数操作
4.软件运行结果
图4.1个位数加减法运算
图4.2多位数加减法运算
图4.3乘除法运算
图4.4混合运算
图4.5ESC退出操作
5.软件改进计划
由上述设计步骤和结果展示可知,软件能实现常规的四则运算,并且经测试软件稳定性和健壮性能够达到设计要求,完成了软件设计的要求。
在后期,将对软件功能进行改进或者扩展,主要有以下两个方面:
(1)软件现在不支持()参与的运算;
(2)软件只支持2个字节数据运算。
对于括号运算功能的添加,我们将括号也当成一种运算符,也有优先级,只是它不参与运算而已。
而现在软件不能进行更高字节的数据运算,主要是因为设定时,默认将存放数据的寄存器、堆栈等空间设置为DW,如果将它们设置为更高字节,它们可以满足范围更广的运算
6.程序源代码
STACKSSEGMENTSTACK
DW20DUP(?
)
STACKSENDS
DATASEGMENT
_NUMDW0
TAB1DB"PLEASEINPUTAFORMULA:
(ENTERESCTOEXIT)",13,10,"$"
NUM1DW100DUP(?
)
NUM2DW10DUP(?
);存放除法产生的小数位
NUM4DW100DUP(?
)
C0DW0
C1DW0
C2DW0
RESULTDW0
WRONGDB13,10,"WRONGINPUT!
$"
;OVERDB13,10,"NOTOPERATIONALCHARACTER!
$"
DATAENDS
;----------------------------
;------代码段----------------
CODESEGMENT
ASSUMECS:
CODE,DS:
DATA
;------主程序---------------
START:
MOVAX,STACKS
MOVSS,AX
MOVAX,DATA
MOVDS,AX
CALLDISPMENU;显示汇编计算器菜单
STARTA:
MOVBX,-1
MOVSI,0
MOVDI,30
MOVCX,0
MOVNUM2,0
;初始化各个变量
CALLINPUT
CALLCALC
CALLOUTPUT
EXIT:
MOVAH,4CH
INT21H
;------计算器显示菜单子程序--------
DISPMENUPROCNEAR
MOVDX,OFFSETTAB1
MOVAH,09H
INT21H
RET
DISPMENUENDP
;---------------------------
;输入字符及转换程序模块
;----输入并转换子程序,结果式存入NUM1-------
INPUTPROCNEAR
STARTW:
MOVAH,1
INT21H
MOVAH,0
CMPAL,1BH;'ESC'退出
JZEXIT
CMPAL,2AH;'*'
JZMARK
CMPAL,2FH;'/'
JZMARK
CMPAL,2BH;'+'
JZMARK
CMPAL,2DH;'-'
JZMARK
CMPAL,3DH;'='
JEEQUAL
CMPAL,'L';'L'清屏
JECLS
SUBAL,30H
CMPAL,0;非数字和运算符则报错
JLERROR
CMPAL,9
JGERROR
JMPARITH
ERROR:
;显示输入错误
LEADX,WRONG
MOVAH,9
INT21H
JMPSTARTA
CLS:
MOVAX,03H
INT10H
ARITH:
CMPC1,0
JETTOM
MANA:
;数值的补码转换
MOVC0,AX
ADDC1,1
MOVAX,BX;AX->BX
MOVCX,10
MULCX
ADDAX,C0;AX=BX*10+AX
MOVBX,AX;AX->BX,下次转换使用
JMPSTARTW
TTOM:
MOVBX,0
JMPMANA
MARK:
CMPBX,-1;BX=-1表示上一次输入的是数字
JEGGON
MOVNUM1[SI],BX;将操作符和运算符都放入NUM1
ADDSI,2
MOVNUM1[SI],AX
MOVBX,-1
MOVC1,0
ADDSI,2
JMPSTARTW
GGON:
MOVNUM1[SI],AX
ADDSI,2
JMPSTARTW
EQUAL:
;输入是等号时,调到主程序执行运算
CMPBX,-1
JELLOP
MOVNUM1[SI],BX
ADDSI,2
MOVNUM1[SI],AX
JMPENDW
LLOP:
MOVNUM1[SI],AX
JMPENDW
ENDW:
RET
INPUTENDP
;---------------------------------
;------计算混合运算的子程序(不含括号的)------------------
CALCPROCNEAR
MOVAX,NUM1
MOVNUM4,AX
MOVNUM2,0
MOVDI,2
MOVCX,0
MOVSI,2
INT1:
;检测算式的运算符,并根据运算符进行
MOVBX,NUM1[SI];相关的操作
CMPBL,2AH;'*'
JEMULL
CMPBL,2FH;'/'
JEDIVV
CMPBL,3DH;'='
JEJMP_CB
CMPBL,29H;')'
JEJMP_CB
CMPBL,2BH;'+'
JEPOTT
CMPBL,2DH;'-'
JEPOTT
INT2:
MOVNUM4[DI],BX
ADDDI,2
ADDSI,2
MOVAX,NUM1[SI]
MOVNUM4[DI],AX
ADDDI,2
ADDSI,2
JMPINT1
POTT:
SUBDI,2
MOVNUM4[DI],AX;运算结果存放在NUM4
ADDDI,2
JMPINT2
MULL:
ADDSI,2
MOVBX,NUM1[SI]
MULBX;AX存放计算结果
ADDSI,2
JMPINT1
JMP_CB:
JMPCHUBU
DIVV:
ADDSI,2;是除法则直接运算
MOVBX,NUM1[SI]
XORDX,DX
DIVBX
PUSHAX;将AX值保存起来,防止取小数操作改变
MOVCX,DI
MOVDI,0
XS:
MOVAX,DX;得到小数部分
MOV_NUM,10
MUL_NUM;余数乘以10,继续除
DIVBX;AX/BX商保存在AX,余数保存在DX
MOVNUM2[DI],AX;将得到的商存入NUM2中,成为小数部分
ADDDI,2
CMPDI,4
JLXS
POPAX
MOVDI,CX
ADDSI,2
JMPINT1
ADDD:
;最终的加法运算
ADDDI,2
MOVBX,NUM4[DI]
ADDAX,BX
ADDDI,2
JMPINT3
SUBB:
;最终加法运算
ADDDI,2
MOVBX,NUM4[DI]
SUBAX,BX
ADDDI,2
JMPINT3
CHUBU:
MOVNUM4[DI],BX;对结果进行初步处理
SUBDI,2
MOVNUM4[DI],AX
MOVAX,NUM4
MOVDI,2
INT3:
MOVBX,NUM4[DI]
CMPBX,2BH;'+'
JEADDD
CMPBX,2DH;'-'
JESUBB
MOVRESULT,AX
RET
CALCENDP
;---------------------------------
;-----输出最终结果的子程序---------------
OUTPUTPROCNEAR
MOVSI,0
MOVAX,RESULT
BEGAIN:
PUSHAX
CMPAX,0;判断结果是否为负数
JLFS_
JMPZS
FS_:
MOVDL,2DH;结果为负数就先输出'-'
MOVAH,02H
INT21H
POPAX
NEGAX
ZS:
MOVBX,10
XORCX,CX;CX=0
Q0:
XORDX,DX;DX=0
DIVBX;AX/10--->商放入ax,余数放入dx
ORDX,0E30H;将负数取反变成正数处理,正数直接处理
PUSHDX
INCCX
CMPAX,0
JNZQ0
Q1:
POPAX
DECCX
INT10H;输出字符
CMPCX,0
JNZQ1
;-----判断小数部分是否是0,是0则不用输出小数---------
MOVDI,0
CMPNUM2[DI],0;判断小数第一位
JNZOT
ADDDI,2
CMPNUM2[DI],0;判断小数第二位
JNZOT
ADDDI,2
CMPNUM2[DI],0;判断小数第三位
JNZOT
CALL_ENTER
JMPSTART
;输出小数程序
OT:
MOVDL,'.';输出负号
MOVAH,02H
INT21H
MOVDI,0
OT1:
MOVDX,NUM2[DI];将小数部分逐个输出
ADDDI,2
ADDDX,30H;转换ASCII码输出
MOVAH,02H
INT21H
CMPDI,6;输出3位小数