彙編語言(Assembly Language)是面向機器的程序設計語言。在彙編語言中,用助記符(Memoni)代替機器指令的操作碼,用地址符號(Symbol)或標號(Label)代替指令或操作數的地址,如此就增強了程序的可讀性並且降低了編寫難度,象這樣符號化的程序設計語言就是彙編語言,因此亦稱為符號語言。使用匯編語言編寫的程序,機器不能直接識別,還要由彙編程序或者叫彙編語言編譯器轉換成機器指令。彙編程序將符號化的操作代碼組裝成處理器可以識別的機器指令,這個組裝的過程稱為組合或者彙編。因此,有時候人們也把彙編語言稱為組合語言。
EMU8086是學習彙編必不可少的工具,它結合了一個先進的原始編輯器、組譯器、反組譯器、具除錯功能的軟件模擬工具(虛擬PC),還有一個循序漸進的指導工具。該軟件包含了學習彙編語言的全部內容。Emu8086集源代碼編輯器,彙編/反彙編工具以及可以運行debug的模擬器(虛擬機器)於一身,此外,還有循序漸進的教程。
工具/原料
Assembly Language emu8086(8086彙編模擬工具)
方法/步驟
下載安裝emu8086(8086彙編模擬工具),打開並運行軟件,新建文件命名為xiaobo calculator.asm
輸入如下代碼
;==給DD數x賦值的宏==將DD型的兩個數X,Y其中Y的值賦給X
give macro x,y
mov ax,y
mov x,ax
mov ax,y[2]
mov x[2],ax
endm
;**給DD數x賦值的宏**將DD型的兩個數X,Y其中Y的值賦給X
;==判斷正負宏== 判斷DD型x,y的正負利用fhx、fhy兩個常數記錄下並將
; 他們都化為正數方便運算
judge macro x,y
local judge1,judge2,judge3,judge4;;標號註釋,用於保證宏的重複調用
;;定位的標號不錯亂
mov ax,x
cmp ax,0
jge judge1
mov fhx,1;;記錄x的正負
neg x;;轉換為正數
judge1:
mov ax,x[2]
cmp ax,0
jge judge2
mov fhx,1
neg x[2]
judge2:
mov ax,y
cmp ax,0
jge judge3
mov fhy,1
neg y
judge3:
mov ax,y[2]
cmp ax,0
jge judge4
mov fhy,1
neg y[2]
judge4:
endm
;**判斷正負宏結束**
;==根據介入y值的0,1將x結果轉為正或負==
change macro x,y
local change1
cmp y,0
je change1
neg x
neg x[2]
change1:
endm
;**根據介入y值的0,1將x結果轉為正或負**
;==進位宏開始== 對小數部分除以100,將商進位,餘數補回小數
carry macro x
push cx;;免除對宏外面的cx,dx值造成干擾
push dx
mov ax,x[2]
mov cx,100
mov dx,0
div cx
mov x[2],dx
add x,ax
pop dx
pop cx
endm
;**進位宏結束**
;==新的加法宏開始== 算法,同號相加,異號相減
newadd macro x,y
local subsub,endnewadd,returnadd1,xbig,endadd1,endadd2,endadd3,endadda,endaddc
judge number3,number4;;不能直接代用x,y.
;;x其實既number3,y既number4。原因,下面一條註釋
mov ax,fhx
cmp fhy,ax
jne subsub
;;------兩個數符號相等則,直接兩部分相加
mov ax,y
add x,ax
mov ax,y[2]
add x[2],ax
carry number3
change number3,fhx
jmp endnewadd
;;---如果符號相反則,大數減去小數
subsub:
mov ax,y
cmp x,ax
ja xbig
jne endadda
mov ax,y[2]
cmp x[2],ax
ja xbig
endadda:
mov ax,x
sub y,ax
add y[2],100
mov ax,x[2]
sub y[2],ax
cmp y[2],100
jnb endadd1
sub y,1
jmp endaddc
endadd1:
sub y[2],100
endaddc:
give number3,number4
change number3,fhy
jmp endnewadd
xbig:
mov ax,y
sub x,ax
add x[2],100
mov ax,y[2]
sub x[2],ax
cmp x[2],100
jnb endadd2
sub x,1
jmp endadd3
endadd2:
sub x[2],100
endadd3:
change number3,fhx
jmp endnewadd
endnewadd:
mov fhx,0
mov fhy,0
endm
;**新的加法宏結束**
;==新的減法宏==
newsub macro x,y
mov fhx,1
change number4,fhx
mov fhx,0
newadd number3,number4
endm
;**新的減法宏**
;==新的乘法宏== 算法 (a1+b1)*(a2+b2)=a1*a2+a1*b2+a2*1+b1*b2
newmul macro x,y
judge number3,number4
push bx
push dx
mov bx,y
mov ax,x
mul bx
push ax;;壓棧用於後面的加法
mov ax,x[2]
mul bx
push ax;;正數部分乘小數部分的結果可以直接加到小數部分
mov bx,y[2]
mov ax,x
mul bx
push ax
mov ax,x[2]
mul bx
mov dx,0
mov bx,100;;小數部分乘小數部分的結果必需再縮小100倍,才
;;能再加回小數位
div bx
mov x[2],ax
pop ax
add x[2],ax
pop ax
add x[2],ax
pop ax
mov x,ax
carry number3
mov ax,fhy
xor fhx,ax
change number3,fhx
mov fhx,0
mov fhy,0
pop dx
pop bx
endm
;**新的乘法宏**
;==新的除法宏== 利用減法完成除法運送,但是為了減少逐減次數,所以
;先用a1/(a2+1)得要一個商,這個商一定不會大於逐減
;次數,所以就可以從(a1+b1)-商*(a2+b2)開始逐減,
;直到減出負數後,回加一個(a2+b2)得(a3+b3)。這時
;候的次數,就是結果的整數部分。最後將(a3+b3)*100
;按前面的方法,就可以得到,結果的兩位小數部分。
newdiv macro x,y
local endnewdiv
push bx
push dx
push cx
judge number3,number4
give fhdx,fhx
mov fhx,0
mov fhy,0
give divn4,number4
intdiv number3,number4;;求結果的整數部分
mov bx,100;;將減完的剩餘數擴大100倍
mov ax,number3
mul bx
mov number3,ax
mov ax,number3[2];;小數位擴大100倍,就等於直接進入整數位
add number3,ax
mov number3[2],0
give number4,divn4
intdiv number3,number4;;求結果的小數部分
pop number3[2]
pop number3
endnewdiv:
mov ax,fhdy
xor fhdx,ax
change number3,fhdx
mov fhdx,0
mov fhdy,0
pop cx
pop dx
pop bx
endm
;**新的除法宏**
;==除法的逐減宏==
intdiv macro x,y
local intdiv1,intdiv2,endintdiv;;內嵌宏,用於逐減
mov ax,x
mov bx,y
add bx,1
mov dx,0
div bx
push ax
mov bx,y
mul bx
mov y,ax
pop ax
push ax
mov bx,y[2]
mul bx
mov y[2],ax
carry number4
pop cx
newsub number3,number4
give number4,divn4;防止減法運算後改變的了number4的值
intdiv1:
newsub number3,number4
give number4,divn4
inc cx
cmp number3,0
jl endintdiv
je intdiv2
jmp intdiv1
intdiv2:
cmp number3[2],0
jl endintdiv
jmp intdiv1
endintdiv:
newadd number3,number4
dec cx
push cx
endm
;**除法的逐減宏**
;==設置光標宏==
curse macro cury,curx
mov ah,2
mov dh,cury
mov dl,curx
mov bh,0
int 10h
endm
;**設置光標宏**
;==定位字符串顯示宏==
menu macro op1,op2,op3 ;菜單顯示宏定義 將op3的內容顯示到op1,op2的位置
mov ah,02h
mov bh,00h
mov dh,op1
mov dl,op2
int 10h
mov ah,09h
lea dx,op3
int 21h
endm
;**定位字符串顯示宏**
;==清屏加色宏==
scroll macro n,ulr,ulc,lrr,lrc,att
mov ah,06
mov al,n;n=上卷行數;n=0時,整個窗口空白
mov ch,ulr;左上角行號
mov cl,ulc;左上角列號
mov dh,lrr;右下角行號
mov dl,lrc;右下角列號
mov bh,att;捲入行屬性
int 10h
endm
;**清屏加色宏**
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;程序開始
data segment
fhx dw 0;記錄x值的正負
fhy dw 0;記錄y值的正負
fhdx dw 0;記錄除法運算中的x值正負
fhdy dw 0;記錄除法運算中的y值正負
divn4 dd 0;除法中幫助暫存number4
number0 db 100;計入鍵盤輸入字符串
db 0
db 100 dup(0);
number dw 200 dup(0);存入輸入公式處
number2 dw 200 dup(0);讀取去括號後的公式暫存處
number3 dd 0;存儲用於計算的x數
number4 dd 0;存儲用於計算的y數
crx db 20;記錄光標列
cry db 10;記錄光標行
crx2 db 2;記錄光標列2
cry2 db 2;記錄光標行2
memb dw 0;記錄bx中的值是否儲存過
memx db 0;記錄是否應該進入小數存儲部分
memcl db 0;記錄已經存儲到小數的第幾位
rsi dw 0
begain db 'Xiaobo Welcome you to use the calculator!','$'
begain1 db 'data:2014/4/23','$'
begain2 db '****Designed by [email protected]!****','$'
begain3 db ' ;,'$'
begain4 db ' ^Henan University of Economics and Law^$','$'
begain5 db 'press "E" key to exit','$'
begain6 db 'press any key to contiune','$'
begain7 db 'Press "Enter" key to introudction','$'
help db 'Confine:32512.99~(-32768.00)','$'
help1 db 'Format: 1.32*99+(98.43/(-34))= ','$'
help2 db 'Notice 1:negative must like (-x)','$'
help3 db ' 2:only can abet double decimal fraction as 567.33','$'
help4 db 'press any key go on!','$'
error1 db ' Error!',13,10,'$'
DBUFFER DB 8 DUP (':'),12 DUP (' ');時間的底
data ends
code segment
assume cs:code,ds:data,es:data
start:
mov ax,data
mov ds,ax
mov ax,data
mov es,ax
;----歡迎界面-----
scroll 0,0,0,24,79,0;清屏
scroll 25,0,0,24,79,50h;開外窗口,品紅底
scroll 21,2,2,22,77,0fh;開內窗口,黑底白字
menu 4,20,begain
menu 6,20,begain1
menu 8,20,begain2
menu 10,20,begain3
menu 12,20,begain4
menu 14,20,begain5
menu 16,20,begain6
menu 18,20,begain7
mov ah,01
int 21h
cmp al,0dh;'Enter'
jne helpo
scroll 21,2,2,22,77,0fh;清屏,內窗
menu 6,20,help
menu 8,20,help1
menu 10,20,help2
menu 12,20,help3
menu 14,20,help4
mov ah,01
int 21h
scroll 21,2,2,22,77,0fh;清屏,內窗
jmp start1
helpo:
cmp al,45h;'E'
je exit
scroll 21,2,2,22,77,0fh;清屏,內窗
;----清零處理-----
start1:
call time
start3:
curse 10,25;光標定位中間
mov cx,200
mov si,0
sclear:
mov number[si],0
add si,2
loop sclear
mov cx,0
mov si,0
mov bx,0
mov crx,25
scroll 1,10,25,10,77,0fh;清除原來等式
;---開始----
call write
call loopcount
curse cry,crx
call output
mov ah,01
int 21h
scroll 1,cry2,2,cry2,77,0fh;清除原來等式
menu cry2,crx2,number0[2]
inc cry2
cmp cry2,10
jl start2
mov cry2,2
start2:
jmp start1
exit:
mov ah,4ch
int 21h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;程序結束
;====寫入函數======
;數據格式:定義一個DD雙字數組,雙字的第一個字存數的整數部分,第二個字
; 存數的小數部分。但是為了最後的輸出方便,以及乘法運算的小數
; 部分運算後向整數部分進位,小數部分採用100進一制
;存數範圍:十進制32512.99~(-32767.00),十六進制7F00.63~8001.00(補碼)
;存數算法:整數部分 正數ai=ai*10+a(i+1),負數對正數求補碼
; 小數部分 正數bi=b1*10+b2,負數對正數求補碼
;存數說明:採用單個字符一個一個輸入的方式,字符為0~9時進入存數算法,當輸
; 入是運算符號時結束存數算法,並且開始存數及存運算符.但是這裡要
; 考慮一個問題不能每次有運算符號進入時都存數,比方A+((X)),如果每
; 次都存,A就會被存3次.所以這裡我們定義了一個常數remb記錄每個數存
; 入情況,方便存數時判斷
;負數處理:負數的介入要求格式為(-X),因為在讀取部分,會依次去括號,這樣就
; 變為了-X,所以只需要判斷一串公式開頭位'-'號,就知道介入的為負
; 數,再直接對X求補。(注:這步處理放在支持混合運算的si_ze宏部分)
;小數處理: 通過介入'.'來判斷是否開始存入小數部分
;特別說明: 1:由於運算符號的ASCLL較小,會與介入的數字相等,所以把運算符的
; ASCLL碼加上7F00H在存入,這樣也就要把7F00H~7FFFH預留給運算
; 符號,同時這也會導致存數範圍的縮小,不過個人覺得是值的.
;輸入格式: 按照平時正常等式的輸入方式輸入,以等號結尾.
; 例如:11*(1-3)/(-3)=
write proc near
;--寫入函數主部分----
startw:
lea dx,number0
mov ah,0ah
int 21h
xor cx,cx
mov cl,number0[1]
mov si,cx
add si,2
mov rsi,si
mov si,0
mov di,2
mov bx,0
startw2:
mov al,number0[di]
inc di
inc crx
mov ah,0
cmp al,45h;'E' 用於程序退出
jz exit
cmp al,2ah;'*'
jz memory;轉入存數及存運算符
cmp al,2fh;'/'
jz memory
cmp al,2bh;'/'
jz memory
cmp al,2dh;'-'
jz memory
cmp al,29h;')'
jz memory
cmp al,28h;'('
jz memory
cmp al,2eh;'.'
jz memoryx;轉入存入小數程序
cmp al,3dh;'='
jz memory
cmp al,0dh;'CR'
jz endwrite
sub al,30h;為存數算法做準備,讓'1'真正變成二進制碼的1
cmp al,0
jl error;報錯程序
cmp al,9
ja error
cmp memx,2
je error
cmp memx,1
je arithmeticx;小數存數算法程序
jmp arithmetic;整數存數算法程序
;---memory存入部分---
memory:
cmp memb,0;remb位0時表示數已經數存儲過;注:開始為remb賦值時
;也一定要為0,因為這是為了防止'(1+3)*4'這樣直接以運
;算符開始的等式
je memory1
mov number[si],bx;存入數
cmp memx,1;
je memory2;存小數部分程序
add si,4
gomemory1:
add ax,7f00h;將運算符轉換到7F00~7FFF之間
mov number[si],ax;存入運算符
mov memb,0;說明數已經存儲過
mov memx,0
mov memcl,0
mov bx,0
add si,4
loop startw2
jmp endwrite
memory1:;只存運算符號
add ax,7f00h
mov number[si],ax
add si,4
loop startw2
jmp endwrite
memory2:
add si,2
jmp gomemory1
;-memoryx小數存入部分-
memoryx:
mov memx,1;當remx為1時表示開始準備小數存入
mov number[si],bx
mov bx,0
add si,2;沒有加四,應為這下是要轉入存小數的下個字節
loop startw2
jmp endwrite
;-arithmetic存數算法--
arithmetic:
;算法bx=bx*10+ax
push ax
mov ax,bx
push cx
mov cx,10
mul cx
pop cx
pop bx
add ax,bx
mov bx,ax
mov memb,1;說明開始存數
cmp bx,7f00h;保證數在合適範圍內
jae error
loop startw2
jmp endwrite
;-arithmeticx存數算法--
arithmeticx:
cmp memcl,1
je arithmetix1
push cx
mov cx,10
mul cx
add bx,ax
pop cx
mov memb,1
inc memcl
loop startw2
jmp endwrite
arithmetix1:
add bx,ax
inc memcl
loop startw2
jmp endwrite
;---error報錯部分----
error:
curse cry,crx
lea dx,error1
mov ah,9
int 21h
jmp start1;重新運行程序
endwrite:
mov di,0
ret
write endp
;***寫入函數結束***
;===去括號函數=====
;程序說明:比方'11+(3+99)='這個等式,先從number的首位開始每隔兩個字取出數
; 與')'的現ASCLL碼7f29h比較從而找到第一個')'的位置,再到回去找到
; 最近的'('的位置,從而將中間沒括號的等式讀出到number3中,接著利
; 用si_ze函數算出這個等式的值,並且賦值回number中,以替代原來的括
; 號及括號能的等式
loopcount proc near
startlp:
mov bp,0
mov di,0
mov bx,0
mov si,0
startl:
mov ax,number[si]
cmp ax,7f29h
je rsee
add si,4
cmp ax,7f3dh
je lastl
jmp startl
rsee:
sub si,4
mov ax,number[si]
cmp ax,7f28h
je rwrite
jmp rsee
rwrite:
push si
; mov lct2,si
mov di,0
add si,2
rwrite1:
add si,2
mov ax,number[si]
mov number2[di],ax
cmp ax,7f29h
je rcount
add di,2
jmp rwrite1
rcount:
push si
; mov lct1,si
call si_ze
pop ax
pop di
push ax
; mov di,lct2
mov ax,number3
mov number[di],ax
mov number3,0
mov ax,number3[2]
add di,2
mov number[di],ax
mov number3[2],0
pop si
; mov si,lct1
add si,2
rcount1:
add si,2
add di,2
mov ax,number[si]
mov number[di],ax
cmp ax,7f3dh
je startlp
jmp rcount1
lastl:
mov si,0
lastll:
mov ax,number[si]
mov number2[si],ax
add si,2
cmp ax,7f3dh
je endl
jmp lastll
endl:
call si_ze
ret
loopcount endp
;**去括號函數結束**
;==混合四則運算函數==
;說明: 通過先不處理加減,先把乘除運算進行,將算出的結果帶回原來
; 的地方取代。最終得到一個只有加減的公式。來方便運算
;特別說明: 本運算還承貸著將(-1)轉為ffffh存入number中,既負數輸入
si_ze proc near
mov si,4
mov ax,number2
cmp ax,7f2dh;'-'
jne sstart
give number3,number2[4]
mov fhx,1
change number3,fhx
mov fhx,0
jmp end4ze
sstart:
mov ax,number2[si]
cmp ax,7f2ah;'*'
je mull;計算乘法
cmp ax,7f2fh;'/'
je divv
cmp ax,7f3dh;'='
je sznext;去完乘除後跳轉
cmp ax,7f29h;')'
je sznext
add si,4
jmp sstart
mull:
sub si,4
give number3,number2[si]
add si,8
give number4,number2[si]
newmul number3,number4
sub si,8
give number2[si],number3
call sloop
jmp sstart
divv:
sub si,4
give number3,number2[si]
add si,8
give number4,number2[si]
newdiv number3,number4
sub si,8
give number2[si],number3
call sloop
jmp sstart
sznext:
give number3,number2
mov si,0
sznext1:
add si,4
mov ax,number2[si]
cmp ax,7f2bh
je addd
cmp ax,7f2dh
je subb
cmp ax,7f3dh
je end4ze
cmp ax,7f29h
je end4ze
jmp sznext1
addd:
add si,4
give number4,number2[si]
newadd number3,number4
jmp sznext1
subb:
add si,4
give number4,number2[si]
newsub number3,number4
jmp sznext1
end4ze:
RET
si_ze endp
sloop proc near;用於循環賦值
mov di,si
add si,8
sloop1:
add di,4
add si,4
mov ax,number2[si]
push ax
give number2[di],number2[si]
pop ax
cmp ax,7f3dh;等號
je endsloop
cmp ax,7f29h;括號
je endsloop
jmp sloop1
endsloop:
ret
sloop endp
;**混合四則運算結束**
;==輸出函數==;通過除十取餘的方法,將數從低位到高位逐一取出再加30h轉為
; ascll輸出。注:先判斷數的正負來考慮是否輸出符號,接著輸出
; 整數部分再輸出一個“.”小數點最後輸出小數部分
output proc near
mov si,rsi
judge number3,number4
cmp fhx,1
jne output1
mov number0[si],2dh
inc si
mov dx,2dh
mov ah,02h
int 21h
output1:
mov ax,number3
mov bx,10
mov cx,0
call intoutput
mov number0[si],2eh
inc si
mov dx,2eh;輸出小數點
mov ah,02h
int 21h
mov ax,number3[2]
mov bx,10
mov cx,0
call intoutput
mov number0[si],'$'
ret
output endp
intoutput proc near
output2:
mov dx,0
div bx
push dx
inc cx
cmp ax,0
je output3
jmp output2
output3:
pop ax
add ax,30h
mov number0[si],al
inc si
mov dx,ax
mov ah,02
int 21h
loop output3
ret
intoutput endp
;**輸出函數**
CRLF PROC NEAR ;回車、顯示功能過程定義,屬性為NEAR
MOV DL,0DH ;把回車的ASCII碼0DH傳給DL
MOV AH,02H ;送DOS 的中斷調用功能號
INT 21H ; DOS 的中斷調用
MOV DL,0AH ; 把換行的ASCII碼0AH傳給DL
MOV AH,02H ; 送DOS 的中斷調用功能號
INT 21H ; DOS 的中斷調用
RET ; 返回
CRLF ENDP ;完成過程定義
TIME PROC NEAR ;顯示時間子程序
DISPLAY1:MOV SI,0
MOV BX,100
DIV BL
MOV AH,2CH ;取時間
INT 21H
MOV AL,CH
CALL BCDASC ;將時間數值轉換成ASCII碼字符
INC SI
MOV AL,CL
CALL BCDASC
INC SI
MOV AL,DH
CALL BCDASC
MOV BP,OFFSET DBUFFER
MOV DX,161dH
MOV CX,8
MOV BX,004EH
MOV AX,1301H
INT 10H
MOV AH,02H
MOV DX,0300H
MOV BH,0
INT 10H
MOV BX,0018H
RE: MOV CX,0FFFFH
REA: LOOP REA
DEC BX
JNZ RE
MOV AH,01H
INT 16H
JE DISPLAY1
JMP start3
RET
TIME ENDP
BCDASC PROC NEAR ;時間數值轉換成ASCII碼字符子程序
PUSH BX
CBW
MOV BL,10
DIV BL
ADD AL,'0'
MOV DBUFFER[SI],AL
INC SI
ADD AH,'0'
MOV DBUFFER[SI],AH
INC SI
POP BX
RET
BCDASC ENDP
code ends
end start
在彙編器選項中選擇編譯文件,編譯分為五個階段,生成一系列文件 debug 文件 list文件 symbol文件 和執行文件exe文件
執行編譯生成的xiaobocalculator.exe,可以看到如下界面 顯示的作者的個人信息和執行方法 press "E" key to exit press any key to contiune Press "Enter" key to introudction
Xiaobo Welcome you to use the calculator!
data:2014/4/23
****Designed by [email protected]!****
^Henan University of Economics and Law^
press "E" key to exit
press any key to contiune
Press "Enter" key to introudction
按enter繼續操作出現如下第一個界面,再enter出現操作界面,下角有時間顯示與系統時間同步
在這個界面中輸入 4344/2= 按enter就會出現答案如下
例43.43*44= 3*(4*(13-9))= 78.432/(4.53*(14-9+4-4-4))=