本文给出一个简单的表达式计算器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