8086匯編語言實現簡易計算器
本周看了一個很有意思的知識。
C語言的編譯器最初是怎么來的?
最初應該是由匯編語言實現一個簡化版C語言,然后使用簡化版的C語言進行多次迭代,功能更新,從而出現了如今強大的C語言。
本人找到了一個古老的課程設計,當時學匯編時候的一個小demo分享出來。
1.概述
為了更深入地理解計算機工作原理以及CPU的功能和結構,掌握匯編語言的使用。本文以簡易計算器程序的匯編語言實現為主要任務,進行對程序的一些算法和匯編語言語法的論述。計算器是最簡單的計算工具,簡單計算器具有加、減、乘、除四項運算功能。想要用匯編語言實現簡單的計算器,就必須通過對數據存儲,寄存器的使用,加減乘除相關指令以及模塊的調用等匯編語言知識進行運用,以實現一個基本功能完善,界面友好,操作簡便易行的計算器。用匯編語言實現簡單計算器還涉及到輸入輸出模塊的設計,加減乘除運算的判斷以及退出程序的判斷的設計。通過對各種指令的合理使用,設計各個功能模塊。當實現各個程序模塊后,通過程序的調用最終實現一個簡單的計算器。本文以用8086匯編語言實現簡易計算器為目標,對程序的算法以及結構進行分析和解釋。
匯編語言的語句類型有指令語句、偽指令語句和宏指令語句。
在實現簡易計算器的過程中暫不需要宏指令語句,故對此語句不進行介紹。
計算器的實現需要使用輸入輸出,DOS系統的01H,02H,09H號調用可以完成所需功能。
由於簡易計算器對結果沒有很高的范圍要求,故對四則運算只需考慮ADD,SUB,MUL,DIV等指令。在計算器中,輸入的是10進制數字,而在匯編語言中讀入字符只能一位一位的讀取,故需要使用MUL設置一個循環算法,將輸入的數字以16進制形式放入寄存器中,而輸出則是使用MOD設置一個循環算法,將16進制轉化為10進制后處理為ASCII碼進行輸出。
2.程序算法結構
簡易計算器程序的任務主要有處理輸入數據,選擇運算方式進行計算,顯示算式結果。程序中為這三個任務做了5個子程序:do_before,do_add,do_sub,do_mul,do_div,show。
簡易計算機結構
在進行簡易計算器程序的制作之前,需要設計程序的結構。從簡易計算器設計的任務要求可知,需要計算機先進行判定需要進行的運算方式,對輸入數據進行計算,最后輸出輸入的算式及計算結果。如:
系統輸出:please input the operator(from + - * /):
用戶輸入運算符選擇運算方式
系統輸出:please input the x and y:
用戶輸入兩個運算數
系統輸出:x+y=z (進行加法運算)
程序流程圖如下:(很簡陋,本科階段畫的)
算法介紹
主要需要用到的子程序主要有輸入轉化子程序do_before:DOS功能01H號調用中的輸入,是將數字一位一位地存儲在AL寄存器中的(如123,讀入寄存器的為1,2,3),而且輸入的數字是以ASCII碼存儲在寄存器中的,需要將其轉化為ASCII碼代表的數字,並將一位一位的數字合並轉化為一個具有數學意義的多位數字。四則運算子程序do_add,do_sub,do_mul,do_div:主要使用add,sub,mul,div指令,下文中只對do_add子程序進行介紹,其余三個子程序類似。輸出結果子程序show:DOS功能02H號調用中的輸出,是輸出一位數字,不能輸出整數型,所以需要對運算結果進行處理,將16進制的數據,轉化為10進制,並一位位地進行輸出。
1.輸入轉化子程序
do_before:
mov ah,1h
int 21h ;調用DOS功能1H號,讀入一位數字
mov temp,0
sub al,'0' ;將數字轉化為ASCII碼代表的數字
cmp al,0 ;判斷輸入的是否是數字
jl done1 ;不是數字的話結束輸入
cmp al,9 ;判斷輸入的是否是數字
jg done1 ;不是數字的話結束輸入
xor ah,ah ;高位清零
mov temp,ax
mov ax,x
mul ten ;ten 中存儲的是10
add ax,temp
mov x,ax
jmp do_before ; 輸入未結束,循環調用
done1:
ret
該子程序流程圖如下:
2.加法子程序
do_add:
call crlf ;調用輸出回車子程序
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message4 ;輸出’please input the x and y:’
int 21h
call do_before1
call do_before2 ;輸入兩個運算數
call crlf
mov ax,temp1
mov x,ax
call show1
mov dl,'+'
mov ah,2h
int 21h
mov ax,temp2
mov x,ax
call show1
mov ax,temp1
mov x,ax
mov dl,'=' ;輸出算式
mov ah,2h
int 21h
mov ax,x
add ax,y ;進行加法
mov x,ax
call show ;輸出結果
ret
由於該子程序是順序結構,不對其流程圖進行描述。
do_add子程序直接對輸入算式進行了輸出,對兩個運算數進行加法后調用了show子程序對結果進行了輸出,兩部分合在一起后便湊成了完整的算式。
3.輸出結果子程序
show: ;顯示結果並結束
mov ax,x ;x中此時存儲的是運算結果
mov bx,0ah ;給bx寄存器中賦10,方便結果輸出
mov cx,0 ;cx寄存器作計數器
step1: mov dx,0
div bx ;執行div指令后商在ax寄存器中,余數在dx寄存器中
cmp ax,0ah ;判斷商是否小於10
jl n1
push dx ;將余數壓入棧
inc cx ;計數器加1,cx代表棧中數字的數量
jmp step1 ;若商不小於10,則一直進行壓棧、判斷的循環
n1: push dx ;將最后一位余數壓入棧
inc cx
push ax ;最后一位小於10的商入棧
inc cx
jmp step2
step2: pop dx ;將棧中數字依次輸出
add dl,'0'
mov ah,2h
int 21h
loop step2
mov ah,4ch ;返回DOS
int 21h ;系統調用
ret
該子程序的流程圖如下:
該子程序算法是將16進制數除以10,將余數壓入棧,若商大於10,將商除以10,余數再次壓入棧,循環這個步驟直到商小於10,將商壓入棧,壓入的順序即是10進制數字由后到前的順序,依次出棧得到的結果就是10進制的運算結果。
此外,如do_add子程序中出現的show1子程序,是為了顯示兩個運算數,調用完之后不返回DOS。程序中,將第一個運算數存進了x,第二個運算數存進了y。由於程序中不需要考慮負數作為運算數,故寄存器中存儲的都是無符號數,需要考慮減法和除法中,x小於y的情況。在do_sub中,當判斷x小於y后,將x和y內的數據調換,調換過程中需要借用一個寄存器,在源代碼中借用的ax寄存器,調換之后按正常情況計算,在運算結果前添加負號即得到正確結果。在do_div中,判斷x小於y后,可直接在等號后輸出x。
3.實現效果
4.源碼
data segment
x dw 0 ;第一個數
y dw 0 ;第二個數
z dw 0
temp1 dw 0
temp2 dw 0
temp dw 0
ten dw 0ah
op db 0 ;操作符
message db 'please choose the operator : (from + - * /)',0ah,0dh,'$'
message2 db 'input error',0ah,0dh,'$'
message3 db '......','$'
message4 db 'please input the x and y:',0ah,0dh,'$'
data ends
assume cs:code,ds:data
code segment
start:
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message
int 21h
call input
input:
mov ah,1h
int 21h
cmp al,'+'
jz nx_1
cmp al,'-'
jz nx_2
cmp al,'*'
jz nx_3
cmp al,'/'
jz nx_4
jnz er_1
er_1:
call crlf
call show_error
call done
nx_1:
call do_add
nx_2:
call do_sub
nx_3:
call do_mul
nx_4:
call do_div
show_error:
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message2
int 21h
do_before1:
mov ah,1h
int 21h
mov temp,0
sub al,'0'
cmp al,0
jl done1
cmp al,9
jg done1
xor ah,ah ;高位清零
mov temp,ax
mov ax,x
mul ten
add ax,temp
mov x,ax
mov temp1,ax
jmp do_before1
done1:
;add x,'0'
ret
do_before2:
mov ah,1h
int 21h
mov temp,0
sub al,'0'
cmp al,0
jl done1
cmp al,9
jg done1
xor ah,ah ;高位清零
mov temp,ax
mov ax,y
mul ten
add ax,temp
mov y,ax
mov temp2,ax
jmp do_before2
done:
mov ax,4c00h
int 21h
do_add:
call crlf
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message4
int 21h
call do_before1
call do_before2
call crlf
mov ax,temp1
mov x,ax
call show1
mov dl,'+'
mov ah,2h
int 21h
mov ax,temp2
mov x,ax
call show1
mov ax,temp1
mov x,ax
mov dl,'='
mov ah,2h
int 21h
mov ax,x
add ax,y
mov x,ax
call show
ret
do_sub:
call crlf
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message4
int 21h
call do_before1
call do_before2
call crlf
call show1
mov dl,'-'
mov ah,2h
int 21h
mov ax,temp2
mov x,ax
call show1
mov ax,temp1
mov x,ax
mov dl,'='
mov ah,2h
int 21h
mov ax,x
cmp ax,y
jl exchange
mov ax,x
sub ax,y
mov x,ax
call show
ret
exchange:
;小x減大y
mov dl,'-'
mov ah,2h
int 21h
mov ax,y
mov bx,x
sub ax,bx
mov x,ax
call show
do_mul:
call crlf
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message4
int 21h
call do_before1
call do_before2
call crlf
call show1
mov dl,'*'
mov ah,2h
int 21h
mov ax,temp2
mov x,ax
call show1
mov ax,temp1
mov x,ax
mov dl,'='
mov ah,2h
int 21h
mov ax,x
mov bx,y
mul bx
mov x,ax
call show
do_div:
call crlf
mov ax,data
mov ds,ax
mov ah,09h
lea dx,message4
int 21h
call do_before1
call do_before2
call crlf
mov ax,temp1
mov x,ax
call show1
mov dl,'/'
mov ah,2h
int 21h
mov ax,temp2
mov x,ax
call show1
mov ax,temp1
mov x,ax
mov dl,'='
mov ah,2h
int 21h
mov ax,x
mov bx,y
cmp ax,bx
jl do_div2
mov dx,0
div bx
mov x,ax
mov temp,dx
cmp dx,0
jz div_0
call show1
mov ax,data ;求余
mov ds,ax
lea dx,message3
mov ah,9h
int 21h
mov ax,temp
mov x,ax
call show1
mov ah,4ch
int 21h
ret
div_0:
call show
do_div2: ;x比y小
mov dl,'0'
mov ah,2h
int 21h
mov ax,data ;求余
mov ds,ax
lea dx,message3
mov ah,9h
int 21h
mov ax,x
call show
crlf: ;回車換行
mov ah,2
mov dl,0DH
int 21H
mov dl,0AH
int 21H
ret
show: ;顯示結果並結束
mov ax,x
mov bx,0ah
mov cx,0
step1: mov dx,0
div bx
cmp ax,0ah
jl n1
push dx
inc cx
jmp step1
n1: push dx
inc cx
push ax
inc cx
jmp step2
step2: pop dx
add dl,'0'
mov ah,2h
int 21h
loop step2
mov ah,4ch
int 21h
ret
show1: ;顯示結果不結束
mov ax,x
mov bx,0ah
mov cx,0
step_1: mov dx,0
div bx
cmp ax,0ah
jl n_1
push dx
inc cx
jmp step_1
n_1: push dx
inc cx
push ax
inc cx
jmp step_2
step_2: pop dx
add dl,'0'
mov ah,2h
int 21h
loop step_2
ret
code ends
end start