# 基于80x86汇编实现的浮点表达式计算器
# 一、设计题目
从键盘输入一个简单的表达式,如“S=4+6\*9-1+8/5”,按回车键结束输入,则屏幕显示S=58.6,小数点保留1位。假设输入的表达式中只含个位十进制数和 “+”、“-”、“\*”、“/” 运算符,且同一运算符最多出现2次。
# 二、设计说明
## 2.1 整个程序的功能应该能完成的工作
这个程序应该能正确处理数字和数学表达式的输入。我的设想是使其进一步处理最多12位十进制小数的输入,以及带有括号、四则运算算式的正确处理,并给出可以精确到小数点后五位的正确结果。
该程序应完成工作:
- **公式的输入**:包括处理数字输入、符号输入,以及正确处理输入公式的句法
- **公式的计算**:其中包括正确处理各种符号运算的优先级和结合性、中间数的临时保存、小数的正确处理等
- **结果的正确显示**
## 2.2 多个任务(子程序)的划分、调用关系
- 十进制浮点数字的输入
- 十进制浮点数字的输出
- 符号的输入
- 对于输入的公式的综合处理
- 对于处理后的公式的运算
- 主程序
## 2.3 各子程序的功能与联系
### 2.3.1 处理十进制小数的转换:ID.ASM
包含过程:INPUTDECIMAL,负责将内存中的十进制小数字符串转换为双精度浮点数,仍然存在内存中。
输入参数包括字符串的起始位置 DS:SI ,和输出位置 ES:BX 。
### 2.3.2 处理十进制小数的输出:OD.ASM
包含过程:OUTPUTDECIMAL,负责将内存中的双精度浮点数以十进制小数形式输出。
输入参数包括字符串的起始位置 DS:SI 。
### 2.3.3 处理算式:PARSEEXP.ASM
包含过程:PARSEEXP,负责将内存中的一串算式字符串进行解析运算,并将最终结果存在内存中。
也同时输出算式的逆波兰符号形式,方便调试找出错误。
会调用ID.ASM、OD.ASM中的功能。
输入参数包括字符串的起始位置 DS:SI 和输出位置 DS:BX 。
### 2.3.4 主程序:MAIN.ASM
包含过程MAIN,是程序的入口。
功能包括显示输入提示、让用户输入算式,以及调用PARSEEXP处理算式,最后用OUTPUTDECIMAL输出计算结果。
## 2.4 程序框图
主程序的大致框图如下:
![](http://www.writebug.com/myres/static/uploads/2021/10/19/ac107ad5c14e0c8ec26fac6db9a6e094.writebug)
## 2.5 子程序说明和流程图
### 2.5.1 PARSEEXP
逐字符进行读取,并根据读取到的字符判断算式中出现的token属于什么类型。若是数字,则调用INPUTDECIMAL将其处理成双精度浮点数 ;若是运算符,则对应处理(见下)。
对于算式的处理和运算,采用了调度场算法(Shunting yard algorithm)。采用两个堆栈,一个放数字,一个放运算符。当算法执行到将运算符放置到数字上的步骤时,立即进行运算。这样,分析完算式后,数字栈顶便是结果。
运算用Intel处理器的FLD、FADD等指令完成。
流程见图,省略了对部分错误状况的处理,详见源码。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/1c0e38d63a01bd54faf5ab4e30678046.writebug)
### 2.5.2 INPUTDECIMAL
利用了英特尔处理器的FBLD指令,将十进制的小数转换为浮点数。
FBLD指令可以将一段长度为10字节空间中的前9字节中的18位BCD码从十进制整数转换为FPU寄存器中的REAL10浮点数,后一字节最高位决定符号位。
如一系列从低位到高位排列的字节:21 43 65 87 00 00 00 00 00 80可以转换为-87654321.0,存储在FPU的栈顶。
对字符串倒序、去掉小数点转BCD码后,用FBLD转换为大浮点数;再根据小数点位置,用FBLD构造一个是10的若干次幂的浮点数,除前者,得到字符串表示的浮点数。再用FSTP将其作为双精度数存储于内存,该功能即得以实现。
大致框图见图。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/dd5e2d5018f28578885d188dac5b1766.writebug)
### 2.5.3 OUTPUTDECIMAL
利用的是FBLD的逆向指令FBSTP。将双精度数用FLD载入FPU的寄存器后,乘以100000,用FBSTP转换为包含小数点后五位的BCD数码,再从右往左解析输出,在第五位加小数点即可。
## 2.6 程序清单
共四个源码文件:
- MAIN.ASM
- ID.ASM
- OD.ASM
- PARSEEXP.ASM
# 三、调试说明
## 3.1 调试情况
本项目调试工作较重,一方面因为本人经验不足,一方面因为16位汇编本身难以调试的特性。在调试时,主要遇到以下问题:
### 3.1.1 浮点数指令工作不正常
在用FADD等指令进行浮点运算时,发现加载的浮点数并无任何问题,但算出结果储存后是FFF80000...(NaN)。最后发现,将各个子程序中分散的FINIT指令集中在MAIN程序中解决了此问题。可能FINIT执行多次会带来未定义的操作。
### 3.1.2 在子程序中用地址表失效
在子程序PARSEEXP中,判断操作符类型时,采用了地址表。但无论是在CS段中定义还是在DS段中定义,地址表都无法正常工作。最后发现,地址表中的标号会在汇编时转换为偏移地址,但其对应的段地址和远调用时认定的子程序段地址并不一致,所以用地址表时会跳到不正确的地方造成NTVDM崩溃。最后,将地址表改为简单的条件跳转后问题解决。
其他问题皆是一些一眼能看出问题所在、由于粗心造成的小问题。
### 3.1.3 程序调试技巧
在调试中我积累了一些经验:
- 程序源码的组织一定要经过深思熟虑,否则会在调试时造成更大麻烦
- 善用DEBUG的一些指令以及其参数,如-P+数字可以一次执行多个指令,对于跳跃到问题代码很有帮助
## 3.2 链接的要求
对四个文件分别汇编,生成.OBJ文件后,在控制台中输入LINK MAIN+PARSEEXP+ID+OD回车,可以执行链接操作生成MAIN.EXE。
## 3.3 测试数据
![](http://www.writebug.com/myres/static/uploads/2021/10/19/2adb6ec288d52ba55d94657e814a7220.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/ec00d0ed6d2ae797145607f3c24664be.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/eec19e629b7f1699812159fdd16ac1ed.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/58b40c28243ef30d7d0696546dddde1f.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/09017cf7cc829383323205206886479b.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/4a9a51f2ff622b99a207607aa99c4f56.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/5a7ae102a7e49a811755b2b82e2ee5fb.writebug)
以下是各种输入错误的情形。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/858b401b2fc6613c35c767e777c9d583.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/296eeca81047966037e418f80811b6c7.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/a6be7cbf5d73c07f308fc35c4ccf5881.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/c81ad6388611cb69d6a0285e4c91d57f.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/348873469ade48ac919767ff3c757d37.writebug)
## 3.4 结果分析
就本人测试过的数据来看,正确输入的算式都得到了正确的结果。对于输入错误的算式而言,本程序并没有覆盖到太多情况,比如+.+6判断为0+6=6等。
综上所述,本程序基本达到了题目要求。
# 四、使用说明
## 4.1 程序环境、适用范围
**本程序应运行于**:
- Intel 的16位处理器(或模拟其工作的模拟器上),如 8086,并且应支持浮点运算功能
- MS-DOS,或Windows 7及以下x86操作系统的命令提示符中(在NTVDM中运行)。本程序的测试环境为DOSBox,�