欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

汇编课程设计(二)--连续平方差

程序员文章站 2022-04-18 20:57:41
...

汇编课程设计(二)–连续平方差


基本要求

1、从键盘上输入一串表达式,程序能够解析表达式并求出对应的值
2、表达式的操作数为小于100的十进制正整数(个数不限)
3、运算符为^(求平方)和-(求差),前者的优先级高于后者


系统模块设计


汇编课程设计(二)--连续平方差
系统模块设计


流程图


汇编课程设计(二)--连续平方差
模块流程图


汇编课程设计(二)--连续平方差
解析函数流程图


源代码

data segment
    Exp db 20,?,20 dup(?)
    oper db 0;用于存放双目运算符
    sign db '+'
    InMsg db 'Please input the expression for square error:',13,10,'$'
    OutMsg db 'The result is:',13,10,'$'
    Ctrl db 13,10,'$'
    InsPot db ?
data ends

code segment
    assume cs:Code,ds:Data
start:
    mov ax,Data
    mov ds,ax
    lea dx,InMsg
    mov ah,9
    int 21h
    mov ax,'#'
    push ax;将'#'压入堆栈,用于指示栈底
;输入表达式
ExpIn:
    lea dx,Exp
    mov ah,10
    int 21h

    ;回车换行
    lea dx,Ctrl
    mov ah,9
    int 21h

    ;设置循环次数和起始指针
    mov cl,Exp[1]
    mov si,2

;逐个字符解析表达式
LoopParse:
    mov dl,Exp[si]
    call Parse
    inc si
    dec cl
    cmp cl,0
    jnz LoopParse

    lea dx,OutMsg
    mov ah,9
    int 21h

    mov dl,sign
    mov ah,2
    int 21h

    pop ax;将'#'弹出堆栈
    pop ax
    call Output
    MOV AH,4CH
    INT 21H

;解析表达式的子函数
Parse proc near
    pop ax
    mov InsPot,al
    cmp dl,'^'
    jz Squ
    cmp dl,'-'
    jz Subtra
    ;经过前面的比较之后,后面进行数据的处理
    ;判断oper单元中是否存放了双目运算符,如果存放了,则先将数据给压入栈中
    ;此时栈中会有两个元素,代表双目运算的操作数
    ;根据运算符来选择调用相关的子函数
    ;mov bl,oper
    ;cmp bl,0
    ;jnz HavOpr;如果不等于0,则说明已经有运算符
    pop bx
    cmp bx,'#'
    jnz IsDec;将栈顶元素出栈,如果和栈底元素不相等,说明输入的是两位数
    push bx;如果弹出来的是'#',则将'#'重新压入堆栈
    sub dl,30h
    push dx;否则将个位数压入堆栈
    mov al,InsPot
    xor ah,ah
    push ax;将IP指针压入堆栈
    ret

IsDec:
    ;bl中存放的是之前压入的个位数,这里应该让其乘以10,再加上
    ;新输入的数,组成两位数,将两位数压入堆栈
    sub dl,30h
    mov al,bl
    mov bl,10
    mul bl
    add ax,dx
    push ax
    mov al,InsPot
    push ax
    ret

Subtra:
    mov oper,dl
    mov al,InsPot
    push ax
    ret

;计算平方
Squ:
    pop ax;如果是单目运算符的话,就把操作数弹出堆栈
    mul al;这里相当于求平方,然后将其结果保存到ax中
    push ax;将结果压入堆栈中
    cmp oper,'-';判断前面的字符是否含有减号
    jz SubIt;如果含有则开始做减法
    mov bx,'#'
    push bx
    mov al,InsPot
    xor ah,ah
    push ax
    ret

;做减法
SubIt:
    mov oper,0;将操作符重置
    pop bx;减数
    pop ax;将#弹出堆栈
    pop ax;被减数
    cmp sign,'-';如果符号位为负,则将被减数取反
    jz Negation
Normal:
    cmp ax,bx;比较被减数和减数的大小
    jl Exc
Work:
    sub ax,bx
    push ax
    mov bx,'#'
    push bx
    mov al,InsPot
    xor ah,ah
    push ax
    ret
Exc:
    xchg ax,bx
    mov sign,'-'
    jmp work
Negation:
    neg ax
    jmp Normal
Parse endp

Output proc near
    mov bx,10;每次除以10,将10放进bx中
    mov cx,0;计数,相当于有几位数
next:
    mov dx,0
    div bx
    add dl,'0'
    push dx;余数在dx中,将其放进栈中
    inc cx;计数加一,相当于有多少位数
    cmp ax,0;与0比较如果还不为0就继续执行操作
    jnz next
    mov ah,2
loop1:
    pop dx;//输出
    int 21h
    loop loop1
    ret
Output endp

code ends
end start

运行结果


汇编课程设计(二)--连续平方差
运行结果


心得

利用数据结构中的算法思想,我将表达式中的运算符分为单目运算符和双目运算符,将栈中的数据用#隔开方便后续的算法处理。每计算一次就将结果压入栈中,这样循环往复就可以完成计算n位数平方差的过程。计算差的时机是在数据段中保存着之前解析的表达式中含有减号且这次解析的符号是平方。将算法列出流程图以后,编写程序起来就变得简单了。
在这个程序中遇到的问题比之前多得多,但我都通过DEBUG将其解决。最关键的一点是,我没有意识到ret语句其实也隐含着出栈的操作,如果没有正确处理子过程返回的代码段地址,那么便会将正确的数据给误弹出栈,于是我想到将调用处的IP值保存在数据段中,需要使用ret语句则将IP重新压入栈中。后面出现的一些计算结果不正确的错误,都是由于算法没有涉及周到导致的,我通过单步跟踪寄存器的值,发现了这些问题的所在之处,并完善了相应的算法。

相关标签: 连续平方差