用32位汇编语言写的一个简单的表达式计算器

本文给出一个简单的表达式计算器32位汇编源程序,并给出对应的C语言代码,有助于理解汇编和C语言编程的异同。

#include <stdio.h>
#include <stdlib.h>

//定义状态
#define ST_NIL  0  //初始状态
#define ST_NUM  1  //操作数状态
#define ST_OP   2  //操作符状态
#define ST_END  3  //结束状态

int calc(int n1,int n2,char sign)
{
    switch (sign)
    {
        case '+': return n1+n2;
        case '-': return n1-n2;
        case '*': return n1*n2;
        case '/': return n1/n2;
        default:
            return 0;
    }
}

int num_arr[2]; //操作数数组
int op_arr[2];  //运算符数组
int num_count;  //操作数个数
int op_count;   //运算符个数

// mode==0,遇到运算符,计算部分表达是
// mode==1,遇到表达是结束符,计算整个表达式
// 使用全局变量num_arr,和op_arr
void do_calc(int mode)
{
    switch ( op_count)
    {
        case 0:
            break;      //不需要计算

        case 1:         //此时,应该有2个操作数,1个操作, 表达式可能是"1+2"这样的形式
            if (mode==1)
            {
                num_arr[0]=calc(num_arr[0],num_arr[1],op_arr[0]);
                num_count=1;
                op_count=0;
            }
            break;

        case 2: //此时,应该有2个操作数,2个操作符号, 表达式可能是"1+2*"这样的形式
            num_arr[0]=calc(num_arr[0],num_arr[1],op_arr[0]);
            op_arr[0]=op_arr[1];
            num_count--;
            op_count--;
            break;
    
        default:
            printf("Error!!!\n");   //应该不会到这儿
            break;
    }
}

int calc_expression(char *szString)
{
    int curr_num;       //正在输入的数,当遇到运算符或字符串结束,将curr_num复制到num_arr
    int pre_status,     //上次状态
        curr_status,    //当前状态
        st_comb;        //上次状态和当前状态的组合
    char *p=szString;
    
    pre_status=ST_NIL;
    num_count=0;    //已经得到的操作数的个数
    op_count=0;     //已经得到的运算符的个数
    curr_num=0;     
    while (1)
    {
        char ch=*p++;

        if (ch>= '0' && ch<='9')
            curr_status=ST_NUM;
        else if (  ch == '+' || ch=='-' || ch=='*' || ch=='/')
            curr_status=ST_OP;
        else
            curr_status=ST_END;

        //前次状态有4种可能的取值,当前状态也有4种可能的取值,他们的组合有16种,其中只有5种状态是有效的
        st_comb= pre_status * 4 + curr_status;
        
        switch ( st_comb)
        {
            case ST_NIL*4+ST_NUM:       //上次状态是ST_NIL,当前状态是ST_NUM
            case ST_OP*4+ST_NUM:        //上次状态是ST_OP,当前状态是ST_NUM            
            case ST_NUM*4+ST_NUM:       //上次状态是ST_NUM,当前状态是ST_NUM
                curr_num=curr_num*10+ch-'0';
                break;

            case ST_NUM*4+ST_OP:        //上次状态是ST_NUM,当前状态是ST_OP
                num_arr[num_count++]=curr_num; //操作数存入数组 
                curr_num=0;             //为下一次输入做准备
                op_arr[op_count++]=ch;  //操作符存入数组
                do_calc(0);
                break;

            case ST_NUM*4+ST_END:       //上次状态是ST_NUM,当前状态是ST_END
                num_arr[num_count++]=curr_num;  //
                curr_num=0;             //为下一次输入做准备
                do_calc(1);
                return num_arr[0];

            default:
                printf("The %dth char is invalid\n",p-szString);
                return 0;
        }
        pre_status=curr_status;
    }
}

int main()
{
    //char *string="1234+5678-1111*9876/4321";
    char buff[256];
    printf("Please input an expression:");
    gets(buff);
    int r=calc_expression(buff);
    printf("%s=%d\n",buff,r);
    return 0;
}

 

再贴出汇编语言代码

; 一个简单的表达式计算器汇编程序,使用masm32语法编程,生成windows下运行的32位控制台程序
; 功能和限制
;   仅仅支持输入10进制整数和四则运算符,不支持输入负数
;   使用整数运算,运算过程的中间结果和最终结果可为负数
;   从左向右顺序计算,不支持运算符优先级
;   计算过程中的中间结果和最终结果不超过32位带符号整数的范围
;   例如,输入"1234+5678-1111*9876/4321",输出"13258"
 

; 如何编译链接该程序
; 本程序需使用masm32来编译,若你的masm32安装在c盘,请设置如下环境变量
; set include=c:\masm32\include
; set lib=c:\masm32\lib
; set PATH=c:\masm32\bin;%PATH%

; 若本程序名为test.asm,则运行如下命令编译和链接和运行
; ml /c /coff calc.asm
; link /subsystem:console test.calc
; calc.exe


.386
.model flat,stdcall ; 32 bit memory model
option casemap :none  ; case sensitive

include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib

.data?
        inBuffer      db    256 dup(?)  ;输入缓冲区
        outBuffer     db    32  dup(?)  ;输出缓冲区
        hOutput       dd    ?
        hInput        dd    ?
        
        num_arr       dd    2 dup (?)   ;操作数数组
        op_arr        dd    2 dup (?)   ;运算符号数组
        num_count     dd    ?           ;操作数个数
        op_count      dd    ?           ;运算符个数
        curr_num      dd    ?
        
        pre_status    dd    ?
        curr_status   dd    ?
        st_comb       dd    ?
        tLen        dd    ?           ;仅用于API函数调用
        
         
.const
       szFmtout1        db    '%d',0
       szOutString      db    'Please input a expression:'
       
.code

ST_NIL  equ     0       ;初始状态
ST_NUM  equ     1       ;操作数状态
ST_OP   equ     2       ;操作符状态
ST_END  equ     3       ;结束状态


;var_N1: input parameter, the first number
;var_N2: input paramenter, the second number
;var_sign: op sign
calc  proc far C uses ebx ecx, var_N1,var_N2,var_sign
    mov eax,var_N1
    mov ebx,var_N2
    mov ecx,var_sign

check_01:
    cmp  cl, '+'
    jnz  check_02
    add  eax,ebx
    jmp  this_exit

check_02:
    cmp cl, '-'
    jnz check_03
    sub eax,ebx
    jmp this_exit

check_03:
    cmp cl, '*'
    jnz check_04
    imul  ebx
    jmp  this_exit

check_04:
    cmp cl, '/'
    jnz err
    xor edx,edx
    idiv ebx
    jmp this_exit

err:
    xor eax,eax 
this_exit:
    ret
calc endp   


;var_mode: input parameter,
;         mode=1,表达式分析已经完成,0:当前遇到四则运算符号
;return value: none
;This procedure work on global array num_arr,op_arr,num_count,op_count

do_calc  proc far C uses ebx ecx esi,var_mode
    mov esi,op_count
    
check_0:                ; op_count ==0? 
    cmp esi,0
    jz this_exit        ; do nothing

check_1:                ; op_count ==1? 
    cmp esi,1
    jnz check_2
    
    mov edx,var_mode    ; if mode==0, do nothing
    or  edx,edx
    jz  this_exit   
    
    mov ecx,    op_arr[0]
    invoke calc,num_arr[0],num_arr[1*4],op_arr[0]
    mov num_arr[0],eax
    
    mov  num_count,1
    mov  op_count,0
    jmp  this_exit
    
check_2:                ; op_count ==2 ? 
    cmp esi,2
    jnz err_input
    
    invoke calc,num_arr[0],num_arr[1*4],op_arr[0]
    mov num_arr[0],eax  
    
    mov eax,op_arr[1*4] ; op_arr[0]=op_arr[1]
    mov op_arr[0],eax
    
    dec num_count       ; num_count--
    dec op_count        ; op_count--
    jmp  this_exit
                        ; op_count ==1? 
err_input:
                        ; do nothing,only return
this_exit:
    ret
do_calc endp
    

;var_string: input parameter,the expression address
;eax: output parameter,  the result
calc_exp  proc far C USES esi edi ebx ecx ,var_string
    mov esi,var_string
 
init_vars:  
    xor ecx,ecx
    xor eax,eax
    mov num_count,eax       ;num_cont=0
    mov op_count, eax       ;op_count=0
    mov curr_num, eax       ;curr_num=0
    mov pre_status,ST_NIL   ;pre_status=ST_NIL
    
loop_start:                 ;the loop entry
check_0to9:
    mov cl,[esi]
    inc esi
    cmp cl,'0'
    jl  check_add_sign
    cmp cl,'9'
    jg  check_add_sign

do_ch_is_dig:
    mov curr_status,ST_NUM
    jmp next_02

check_add_sign:   
    cmp cl,'+'
    jz do_ch_is_op
    
check_sub_sign:
    cmp cl,'-'
    jz do_ch_is_op
    
check_mul_sign:  
    cmp cl,'*'
    jz do_ch_is_op
    
check_div_sign: 
    cmp cl,'/'
    jz  do_ch_is_op
    jmp  do_op_is_others
    
do_ch_is_op:
    mov curr_status,ST_OP
    jmp next_02 

do_op_is_others:
    mov curr_status,ST_END
    jmp next_02
 
next_02:
    mov eax,pre_status
    shl eax,2
    add eax,curr_status
    mov st_comb,eax             ;st_comb=pre_status*4+curr_status

check_01:   
    cmp st_comb,ST_NIL*4+ST_NUM ;last status is ST_NUL,curr status is ST_NUM,
    jz  case_01                     

check_02:
    cmp st_comb,ST_OP*4+ST_NUM  ;last status is ST_OP,curr status is ST_NUM
    jz  case_01

check_03:
    cmp st_comb,ST_NUM*4+ST_NUM ;last status is ST_NUM,curr status is ST_NUM
    jz  case_01

check_04:
    cmp st_comb,ST_NUM*4+ST_OP  ;last status is ST_NUM,curr status is ST_OP
    jz  case_02
    
check_05:
    cmp st_comb,ST_NUM*4+ST_END ;last status is ST_NUM,curr status is ST_END
    jz  case_03
    
    jmp case_error              ;invalid status, jump to case_error

case_01:    ;st_comb is ST_NIL*4+ST_NUM,ST_OP*4+ST_NUM or ST_NUM*4+ST_NUM
    mov edx,curr_num
    shl edx,1                   ;edx =curr_num *2
    lea eax,[edx+edx*4]         ;eax= curr_num *10
    sub cl,'0'
    movzx edx,cl
    add eax,edx
    mov curr_num,eax            ;curr_num=curr_num + cl-'0'
    jmp next_loop

case_02:                        ;st_comb is ST_NUM*4+ST_OP
    mov ebx,num_count
    mov eax,curr_num
    mov num_arr[ebx*4],eax      ;num_arr[num_count++]=curr_num
    inc ebx                     ;num_count++        
    mov num_count,ebx
    mov curr_num,0          
    
    mov ebx,op_count        
    movzx eax, cl    ;op_arr[op_count++]= curr char
    mov op_arr[ebx*4],eax
    inc ebx                     ;op_count++
    mov op_count,ebx
    
    invoke do_calc,0
    jmp next_loop
    
case_03:                        ;st_comb is ST_NUM*4+ST_END
    mov eax,curr_num
    mov ebx,num_count
    mov num_arr[ebx*4],eax      ;num_arr[num_count++]=curr_num
    inc ebx                     ;num_count++        
    mov num_count,ebx
    ;mov curr_num,0         
    
    invoke do_calc,1
    jmp this_exit               ;return num_arr[0]

case_error:
    xor eax,eax
    mov num_arr[0],eax
    jmp this_exit               ;invliad case, return 0
    
next_loop:
    mov edx,curr_status         ;pre_status=curr_status
    mov pre_status,edx
    jmp loop_start

this_exit:
    mov eax,num_arr[0]
    ret
calc_exp endp   

main proc far
start:
    invoke GetStdHandle,STD_OUTPUT_HANDLE   ; 获取控制台输出设备的句柄
    mov    hOutput,eax
    invoke WriteConsole,hOutput,addr szOutString,sizeof szOutString,addr tLen,0
    
    invoke GetStdHandle,STD_INPUT_HANDLE    ; 获取控制台输入设备的句柄
    mov hInput, eax
    invoke ReadFile,hInput,addr inBuffer,sizeof inBuffer,addr tLen,NULL

    invoke calc_exp, addr inBuffer          ;计算表达式inBuff,
    mov    num_arr[0], eax                  ;将这个数存入number
    
    invoke wsprintf, addr outBuffer , addr szFmtout1,num_arr[0]
    invoke WriteConsole,hOutput,addr outBuffer,eax,addr tLen,0
    
    xor eax, eax
    invoke ExitProcess,eax                  ;退出进程
    ret 
main endp

end main


 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值