單片機匯編指令入門學習和查看
6.輸入 D100
1975:0100 BA 09 01 B4 09 CD 21 CD-20 75 6E 64 65 72 73 74 ......!. underst
1975:0110 61 6E 64 24 8B 46 F8 89-45 04 8B 46 34 00 64 19 and$.F..E..F4.d.
1975:0120 89 45 02 33 C0 5E 5F C9-C3 00 C8 04 00 00 57 56 .E.3.^_.......WV
1975:0130 6B F8 0E 81 C7 FE 53 8B-DF 8B C2 E8 32 FE 0B C0 k.....S.....2...
1975:0140 74 05 33 C0 99 EB 17 8B-45 0C E8 D4 97 8B F0 89 t.3.....E.......
1975:0150 56 FE 0B D0 74 EC 8B 45-08 03 C6 8B 56 FE 5E 5F V...t..E....V.^_
1975:0160 C9 C3 C8 02 00 00 6B D8-0E 81 C3 FE 53 89 5E FE ......k.....S.^.
1975:0170 8B C2 E8 FB FD 0B C0 75-09 8B 5E FE 8B 47 0C E8 .......u..^..G..
現(xiàn)在,我們來剖析另一個程序:由鍵盤輸入任意字符串,然后顯示出來。db 20指示DEBUG保留20h個未用的內(nèi)存空間供緩沖區(qū)使用。
輸入A100
MOV DX,0116 ;DS:DX = 緩沖區(qū)地址,由DB偽指令確定緩沖區(qū)地址
MOV AH,0A ;0Ah 號功能調(diào)用
INT 21 ;鍵盤輸入緩沖區(qū)
MOV DL,0A ;由于功能Ah在每個字符串最后加一個歸位碼(0Dh由 Enter
MOV AH,02 ;產(chǎn)生),使光標自動回到輸入行的最前端,為了使新輸出的
INT 21 ;字符串不會蓋掉原來輸入的字符串,所以利用功能2h加一
;個換行碼(OAh),使得光標移到下一行的的最前端。
MOV DX,0118 ;裝入字符串的起始位置
MOV AH,09 ;9h功能遇到$符號才會停止輸出,故字符串最后必須加上
INT 21 ;$,否則9h功能會繼續(xù)將內(nèi)存中的無用數(shù)據(jù)胡亂顯示出來
INT 20
DB 20 ;定義緩沖區(qū)
送你一句話:學匯編切忌心浮氣燥。
客套話就不講了。工欲善其事,必先利其器。與其說DEBUG 是編譯器,倒不如說它是“直譯器”,DEBUG的A命令只可將一行匯編指令轉(zhuǎn)成機器語言,且立刻執(zhí)行。真正編譯器(MASM)的運作是利用文本編輯器(EDIT等)將匯編指令建成一個獨立且附加名為.ASM的文本文件,稱源程序。它是MASM 程序的輸入部分。MASM將輸入的ASM文件,編譯成.OBJ文件,稱為目標程序。OBJ文件僅包含有關程序各部份要載入何處及如何與其他程序合并的信息,無法直接載入內(nèi)存執(zhí)行。鏈結(jié)程序LINK則可將OBJ文件轉(zhuǎn)換成可載入內(nèi)存執(zhí)行(EXEcute)的EXE文件。還可以用EXE2BIN,將符合條件的EXE文件轉(zhuǎn)成COM文件(COM 文件不但占用的內(nèi)存最少,而且運行速度最快)。
下面我們用MASM寫一個與用DEBUG寫的第一個程序功能一樣的程序。
用EDIT編輯一個SMILE.ASM的源程序文件。
源程序 DEBUG 程序
prognam segment
assume cs:prognam
org 100h A100
mov dl,1 mov dl,1
mov ah,2 mov ah,2
int 21h int 21
int 20h int 20
prognam ends
end
比較一下:1.因為MASM會將所有的數(shù)值假設為十進制,而DEBUG則只使用十六進制,所以在源程序中,我們必須在有關數(shù)字后加上代表進制的字母,如H代表十六進制,D代表十進制。若是以字母開頭的十六進制數(shù)字,還必須在字母前加個0,以表示它是數(shù),如0AH。2.源程序增加五行敘述:prognam segment 與 prognam ends 是成對的,用來告訴 MASM 及LINK,此程序?qū)⒎旁谝粋€稱為PROGNAM(PROGram NAMe)的程序段內(nèi),其中段名(PROGNAM)可以任取,但其位置必須固定。assume cs:prognam 必須在程序的開頭,用來告訴編譯器此程序所在段的位置放在CS寄存器中。end用來告訴MASM,程序到此結(jié)束, ORG 100H作用相當于DEBUG的A100,從偏移量100開始匯編。COM 文件的所有源程序都必須包含這五行,且必須依相同的次序及位置出現(xiàn),這點東西記下就行,千篇一律。接著,我們用MASM編譯SMILE.ASM。
輸入 MASM SMILE ←不用打入附加名.ASM。
Microsoft (R) Macro Assembler Version 5.10
Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
Object filename [SMILE.OBJ]: ←是否改動輸出OBJ文件名,如不改就ENTER
Source listing [NUL.LST]: ← 是否需要列表文件(LST),不需要就ENTER
Cross-reference [NUL.CRF]: ←是否需要對照文件(CRF),不需要則ENTER
50162 + 403867 Bytes symbol space free
0 Warning Errors ←警告錯誤,表示編譯器對某些語句不理解,通常是輸入錯誤。
0 Severe Errors ←嚴重錯誤,會造成程序無法執(zhí)行,通常是語法結(jié)構(gòu)錯誤。
如果沒有一個錯誤存在,即可生成OBJ文件。OBJ中包含的是編譯后的二進制結(jié)果,它還無法被 DOS載入內(nèi)存中加以執(zhí)行,必須加以鏈結(jié)(Linking)。以LINK將OBJ文件(SMILE.OBJ)鏈結(jié)成 EXE 文件(SMILE.EXE)時,。
1.輸入 LINK SMILE ←不用附加名OBJ
Microsoft (R) Overlay Linker Version 3.64
Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.
Run File [SMILE.EXE]: ← 是否改動輸出EXE文件名,如不改就ENTER
List File [NUL.MAP]: ← 是否需要列表文件(MAP),不需要則ENTER
Libraries [.LIB]: ←是否需要庫文件,要就鍵入文件名,不要則ENTER
LINK : warning L4021: no stack segment← 由于COM文件不使用堆棧段,所以錯誤信息
←"no stack segment"并不影響程序正常執(zhí)行
至此已經(jīng)生成EXE文件,我們還須使用EXE2BIN 將EXE文件(SMILE.EXE),轉(zhuǎn)換成COM文件(SMILE.COM)。輸入EXE2BIN SMILE產(chǎn)生 BIN 文件(SMILE.BIN)。其實 BIN 文件與 COM 文件是完全相同的,但由于DOS只認COM、EXE及BAT文件,所以BIN文件無法被正確執(zhí)行,改名或直接輸入 EXE2BIN SMILE SMILE.COM即可?,F(xiàn)在,磁盤上應該有 SMILE.COM 文件了,你只要在提示符號C:>下,直接輸入文件名稱 SMILE ,就可以執(zhí)行這個程序了。
你是否覺得用編譯器產(chǎn)生程序的方法,比 DEBUG 麻煩多了!以小程序而言,的確是如此,但對于較大的程序,你就會發(fā)現(xiàn)其優(yōu)點了。我們再將ASCII程序以編譯器方式再做一次,看看有無差異。首先,用EDIT.COM建立 ASCII.ASM 文件。
prognam segment ;定義段
assume cs:prognam ;把上面定義段的段基址放入 CS
mov cx,100h ; 裝入循環(huán)次數(shù)
mov dl,0 ; 裝入第一個ASCII碼,隨后每次循環(huán)裝入新碼
next: mov ah,2
int 21h
inc dl ;INC:遞增指令,每次將數(shù)據(jù)寄存器 DL 內(nèi)的數(shù)值加 1
loop next ; 循環(huán)指令,執(zhí)行一次,CX減1,直到CX為0,循環(huán)停止
int 20h
prognam ends ;段終止
end ;匯編終止
在匯編語言的源程序中,每一個程序行都包含三項元素:
start: mov dl,1 ;裝入第一個ASCII碼,隨后每次循環(huán)裝入新碼
標識符 表達式 注解
在原始文件中加上注解可使程序更易理解,便于以后參考。每行注解以“;”與程序行分離。編譯器對注解不予理會,注解的數(shù)據(jù)不會出現(xiàn)在OBJ、EXE或COM文件中。由于我們在寫源程序時,并不知道每一程序行的地址,所以必須以符號名稱來代表相對地址,稱為“標識符”。我們通常在適當行的適當位置上,鍵入標識符。標識符(label)最長可達31 個字節(jié),因此我們在程序中,盡量以簡潔的文字做為標識符?,F(xiàn)在,你可以將此ASCII.ASM 文件編譯成 ASCII.COM 了。1.MASM ASCII,2.LINK ASCII,3.EXE2BIN ASCII ASCII.COM。
注意:當你以編譯器匯編你設計的程序時,常會發(fā)生打字錯誤、標識符名稱拼錯、十六進制數(shù)少了h、邏輯錯誤等。匯編老手常給新人的忠告是:最好料到自己所寫的程序一定會有些錯誤(別人告訴我的);如果第一次執(zhí)行程序后,就得到期望的結(jié)果,你最好還是在檢查一遍,因為它可能是錯的。原則上,只要大體的邏輯架構(gòu)正確,查找程序中錯誤的過程,與寫程序本身相比甚至更有意思。寫大程序時,最好能分成許多模塊,如此可使程序本身的目的較單純,易于撰寫與查錯,另外也可讓程序中不同部份之間的界限較清楚,節(jié)省編譯的時間。如果讀程序有讀不懂的地方最好用紙筆記下有關寄存器、內(nèi)存等內(nèi)容,在紙上慢慢比劃,就豁然開朗了?! ∠旅嫖覀儗懸粋€能從鍵盤取得一個十進制的數(shù)值,并將其轉(zhuǎn)換成十六進制數(shù)值而顯示于屏幕上的“大程序”。前言:要讓8086執(zhí)行這樣的功能,我們必須先將此問題分解成一連串的步驟,稱為程序規(guī)劃。首先,以流程圖的方式,來確保整個程序在邏輯上沒有問題(不用說了吧!什么語言都要有此步驟)。這種模塊化的規(guī)劃方式,稱之為“由上而下的程序規(guī)劃”。而在真正寫程序時,卻是從最小的單位模塊(子程序)開始,當每個模塊都完成之后,再合并成大程序;這種大處著眼,小處著手的方式稱為“由下而上的程序設計”。
我們的第一個模塊是BINIHEX,其主要用途是從8086的BX寄存器中取出二進制數(shù),并以十六進制方式顯示在屏幕上。注意:子程序如不能獨立運行,實屬正常。
binihex segment
assume cs:binihex
mov ch,4 ;記錄轉(zhuǎn)換后的十六進制位數(shù)(四位)
rotate: mov cl,4 ;利用CL當計數(shù)器,記錄寄存器數(shù)位移動次數(shù)
rol bx,cl ;循環(huán)寄存器BX的內(nèi)容,以便依序處理4個十六進制數(shù)
mov al,bl ;把bx低八位bl內(nèi)數(shù)據(jù)轉(zhuǎn)移至al
and al,0fh ;把無用位清零
add al,30h ;把AL內(nèi)數(shù)據(jù)加30H,并存入al
cmp al,3ah ;與3ah比較
jl printit ;小于3ah則轉(zhuǎn)移
add al,7h ;把AL內(nèi)數(shù)據(jù)加30H,并存入al
printit:mov dl,al ;把ASCII碼裝入DL
mov ah,2
int 21h
dec ch ;ch減一,減到零時,零標志置1
jnz rotate ;JNZ:當零標志未置1,則跳到指定地址。即:不等,則轉(zhuǎn)移
int 20h ;從子程序退回主程序
binihex ends
end
利用循環(huán)左移指令ROL循環(huán)寄存器BX(BX內(nèi)容將由第二個子程序提供)的內(nèi)容,以便依序處理4個十六進制數(shù):1. 利用CL當計數(shù)器,記錄寄存器移位的次數(shù)。2.將BX的第一個十六進制值移到最右邊。利用 AND (邏輯“與”運算:對應位都為1時,其結(jié)果為1,其余情況為零)把不要的部份清零,得到結(jié)果:先將BL值存入AL中,再利用AND以0Fh(00001111)將AL的左邊四位清零。由于0到9的ASCII碼為30h到39h,而A到F之ASCII碼為41h到46h,間斷了7h,所以得到結(jié)果:若AL之內(nèi)容小于3Ah,則AL值只加30h,否則AL再加7h。ADD指令會將兩個表達式相加,其結(jié)果存于左邊表達式內(nèi)。標志寄存器(Flag Register)是一個單獨的十六位寄存器,有9個標志位,某些匯編指令(大部份是涉及比較、算術(shù)或邏輯運算的指令)執(zhí)行時,會將相關標志位置1或清0,常碰到的標志位有零標志(ZF)、符號標志(SF)、溢出標志(OF)和進位標志(CF)。標志位保存了某個指令執(zhí)行后對它的影響,可用其他相關指令,查出標志的狀態(tài),根據(jù)狀態(tài)產(chǎn)生動作。CMP指令很像減法,是將兩個表達式的值相減,但寄存器或內(nèi)存的內(nèi)容并未改變,只是相對的標志位發(fā)生改變而已:若 AL 值小于 3Ah,則正負號標志位會置0,反之則置1。 JL指令可解釋為:小于就轉(zhuǎn)移到指定位置,大于、等于則向下執(zhí)行。CMP和JG 、JL等條件轉(zhuǎn)移指令一起使用,可以形成程序的分支結(jié)構(gòu),是寫匯編程序常用技巧。
第二個模塊DECIBIN 用來接收鍵盤打入的十進制數(shù),并將它轉(zhuǎn)換成二進制數(shù)放于BX 寄存器中,供模塊1 BINIHEX使用。
decibin segment
assume cs:decibin
mov bx,0 ;BX清零
newchar:mov ah,1 ;
int 21h ;讀一個鍵盤輸入符號入al,并顯示
sub al,30h ;al減去30H,結(jié)果存于al中,完成ASCII碼轉(zhuǎn)二進制碼
jl exit ;小于零則轉(zhuǎn)移
cmp al,9d
jg exit ;左>右則轉(zhuǎn)移
cbw ;8位al轉(zhuǎn)換成16位ax
xchg ax,bx ;互換ax和bx內(nèi)數(shù)據(jù)
mov cx,10d ;十進制數(shù)10入cx
mul cx ;表達式的值與ax內(nèi)容相乘,并將結(jié)果存于ax
xchg ax,bx
add bx,ax
jmp newchar ;無條件轉(zhuǎn)移
exit: int 20 ;回主程序
decibin ends
end
CBW 實際結(jié)果是:若AL中的值為正,則AH填入00h;反之,則AH填入FFh。XCHG常用于需要暫時保留某個寄存器中的內(nèi)容時。
當然,還得一個子程序(CRLF)使后顯示的十六進制數(shù)不會蓋掉先輸入的十進制數(shù)。
crlf segment
assume cs:crlf
mov dl,0dh ;回車的ASCII碼0DH入DL
mov ah,2
int 21h
mov dl,0ah ;換行的ASSII碼0AH入AH
mov ah,2
int 21h
int 20 ;回主程序
crlf ends
end
現(xiàn)在我們就可以將BINIHEX、DECIBIN及CRLF等模塊合并成一個大程序了。首先,我們要將這三個模塊子程序略加改動。然后,再寫一段程序來調(diào)用每一個子程序。
crlf proc near;
mov dl,0dh
mov ah,2
int 21h
mov dl,0ah
mov ah,2
int 21h
ret
crlf endp
類似SEGMENT與ENDS的偽指令,PROC與ENDP也是成對出現(xiàn),用來識別并定義一個程序。其實,PROC 真正的作用只是告訴編譯器:所調(diào)用的程序是屬于近程(NEAR)或遠程(FAR)。 一般的程序是由 DEBUG 直接調(diào)用的,所以用 INT 20 返回,用 CALL 指令所調(diào)用的程序則改用返回指令RET,RET會把控制權(quán)轉(zhuǎn)移到棧頂所指的地址,而該地址是由調(diào)用此程序的 CALL指令所放入的。
各模塊都搞定了,然后我們把子程序組合起來就大功告成
decihex segment ;主程序
assume cs:decihex
org 100h
mov cx,4 ;循環(huán)次數(shù)入cx;由于子程序要用到cx,故子程序要將cx入棧
repeat: call decibin;調(diào)用十進制轉(zhuǎn)二進制子程序
call crlf ;調(diào)用添加回、換行符子程序
call binihex ;調(diào)用二進制轉(zhuǎn)十六進制并顯示子程序
call crlf
loop repeat ;循環(huán)4次,可連續(xù)運算4次
mov ah,4ch ; 調(diào)用DOS21號中斷4c號功能,退出程序,作用跟INT 20H
int 21H ; 一樣,但適用面更廣,INT20H退不出時,試一下它
decibin proc near push cx ;將cx壓入堆棧,;
┇ exit: pop cx ;將cx還原; retdecibin endp binihex proc near push cx
┇ pop cx retbinihex endp crlf proc near
push cx
┇ pop cx retcrlf endpdecihex ends end
CALL指令用來調(diào)用子程序,并將控制權(quán)轉(zhuǎn)移到子程序地址,同時將CALL的下行一指令地址定為返回地址,并壓入堆棧中。CALL 可分為近程(NEAR)及遠程(FAR)兩種:1.NEAR:IP的內(nèi)容被壓入堆棧中,用于程序與程序在同一段中。2.FAR:CS 、IP寄存器的內(nèi)容依次壓入堆棧中,用于程序與程序在不同段中。PUSH、POP又是一對指令用于將寄存器內(nèi)容壓入、彈出,用來保護寄存器數(shù)據(jù),子程序調(diào)用中運用較多。堆棧指針有個“后進先出”原則,像PUSH AX,PUSH BX…POP BX,POP AX這樣才能作到保護數(shù)據(jù)絲毫不差。
評論