汇编语言8086笔记
學到的知識,很大的一部分會被忘卻,而被忘記的知識的影子卻保護你避免陷入很多的錯覺。——伊頓公學校長威廉·考利
為什么要學習匯編語言?
匯編語言是很多相關課程的重要基礎,比如:操作系統、接口技術等。它是底層編程語言,是計算機系統提供給用戶最快最有效的語言,也是能對硬件直接編程的語言。因此,對空間和時間要求很高的程序,或需要直接控制硬件的程序,必須使用匯編語言進行程序設計。
- 程序員是個大江湖之我來自編譯門
匯編語言的特點
匯編語言的主要應用場合
學習資源
BCD碼
BCD碼(Binary Coded Decimal)是一種二-十進制的編碼,它使用4位二進制數表示一位十進制數。最常用的BCD碼是8421碼,又叫NBCD碼(Natural Binary Coded Decimal Code),由于4位二進制數可表示16種狀態,只取前10種狀態0000-1001來表示十進制數碼0-9,從左到右每位二進制數的權分別是8、4、2、1,因此又叫8421碼。
例如:十進制數1258對應的BCD碼是0001001001011000;
反之,BCD碼1001 1000 0111 0010對應的十進制數是9872。
壓縮BCD碼:用一個字節表達兩位BCD碼,比如 0100 1001表示49。
非壓縮BCD碼:將8位二進制的高4位設置為0,僅用低4位表達一位BCD碼。比如00000100 00001001表示49。
80x86微處理器
80X86是美國Intel公司生產的微處理器系列。
微處理器:把運算器和控制器集成在一個芯片上,構成的中央處理器(CPU)。
微機:即微計算機系統,由微處理器配上存儲器、輸入輸出設備和系統軟件等構成。各硬件用系統總線連接在一起。
系統總線包括數據總線(DB)、地址總線(AB)和控制總線(CB)三組。
基于微處理器的計算機系統構成
數據總線寬度
數據總線寬度16位:決定了數據的傳輸速率。
機器字長16位
機器字長為16位:可表示數的范圍為0000~FFFFH(2^16 = 64K)
地址總線寬度
地址總線寬度為20位:尋址空間為2^20 = 1024KB = 1MB。
8086總線的工作周期
計算機是在時鐘控制下進行工作的,若干個時鐘完成一個基本操作,一個基本操作就是一個總線周期,CPU有若干種典型操作,構成相應的總線周期。如存儲器的讀寫總線周期,I/O讀寫總線周期等。
執行一條指令的時間稱為指令周期,指令周期是由若干總線周期構成。
8086/8088的基本總線周期是由4個時鐘周期組成,在執行WAIT指令或READY引腳輸入的狀態為低電平時,都需要在T3和T4之間插入1個或若干個等待時鐘周期Tw。
T1狀態
CPU向多路復用總線上發送地址信息,指出要尋址的內存單元地址或I/O端口地址。這期間CPU還要ALE(正向脈沖),在ALE下降沿將內存單元地址或I/O端口地址打入地址鎖存器。
T2狀態
CPU從總線上撤銷有效地址,使地址總線低16位呈高阻狀態,為數據傳輸做準備。總線的高4位(A19~A16)輸出總線周期的狀態信息,用于表示中斷允許狀態及正在使用的段寄存器名。
T3狀態
A19~A16上的狀態信息不變,地址總線低16位上出現CPU要寫出的或準備讀入的數據。若外設與內存來不及與總線交換數據,則應通過CPU的READY信號,在T3前沿(下降沿)之前向CPU申請插入等待狀態TW,在T3及TW前沿查詢READY信號,查到高電平則結束等待狀態,進入下一狀態。否則繼續插入等待。
T4狀態之總線周期結束
在一個總線周期之后,若不立即進入下一個總線周期,即CPU不與內存或外設交換數據或者指令隊列已滿,系統總線處于空閑狀態,CPU執行總線空閑周期,總線空閑周期一般由一個或多個時鐘周期組成。
中央處理器
CPU包含了三個部分:算術邏輯、控制邏輯、寄存器組。
總線接口部件BIU (Bus Interface Unit)
內部設有四個段地址寄存器,一個指令指針寄存器IP,一個6字節指令隊列緩沖器,20位地址加法器和總線控制電路。
主要功能:根據執行部件EU的請求,負責從內存單元中預取指令,并將它們送到指令隊列緩沖器暫存。即負責完成CPU與存儲器或I/O設備之間的數據傳送。
執行部件EU(Execution Unit)
執行部件中包含算術邏輯單元(ALU)、通用寄存器、狀態標志寄存器、數據暫存寄存器和執行部件的控制電路。
主要功能:從BIU的指令隊列中取出指令代碼,經指令譯碼器譯碼后執行指令所規定的全部功能。執行指令所得結果或執行指令所需的數據,都由EU向BIU發出命令,對存儲器或I/O接口進行讀/寫操作。
寄存器組
數據寄存器
| AX | Accumulator | 累加寄存器,常用于運算;在乘除等指令中指定用來存放操作數,另外,所有的I/O指令都使用這一寄存器與外界設備傳送數據。 |
| BX | Base | 基址寄存器,常用于存放存儲單元地址。 計算地址的時候用作基地址寄存器,用于擴展尋址,起變址的作用。 |
| CX | Count | 計數寄存器,一般作為循環或串操作等指令中的隱含計數器;常用于保存計算值,如在移位指令,循環(loop)和串處理指令中用作隱含的計數器。 |
| DX | Count | 數據寄存器,常用來存放雙字數據的高16位,或存放外設端口地址,比如雙字的乘除法。 存放操作數和列表數據,在某些I/O操作期間,用來,在乘除運算中有專用。 |
共同特點:
這四個十六位的寄存器可以分為:
- 高8位:AH,BH, CH,DH;
- 低8位:AL,BL,CL,DL。
- 這兩組八位寄存器可以分別尋址,并單獨使用。
指針寄存器
| SP | Stack Pointer | 堆棧指針寄存器。與SS(堆棧段寄存器)配合使用來確定堆棧段棧頂的位置,也就是說SP用于存放棧頂的偏移地址。 |
| BP | Base Pointer | 基址指針寄存器,可用作SS的一個相對基址位置(用于存放堆棧段中某一存儲單元的偏移地址。) |
說明:指針寄存器和變址寄存器只能按16位進行存取操作,主要用來形成操作數的地址,用于堆棧操作和變址運算中計算操作數的有效地址。
變址寄存器
| SI | Source Index | 源變址寄存器可用來存放相對于DS段之源變址指針,也就是說,源操作數偏址存放在SI中。 |
| DI | Destination Index | 目的變址寄存器,可用來存放相對于 ES段的目的變址指針,也就是說,目的操作數偏址存放在DI中。 |
說明:
SI和DI一般與數據段寄存器DS聯合使用,用來確定數據段中某一存儲單元的地址。這兩個變址寄存器有自動增量和自動減量的功能,所以用來變址是十分方便的。
在串處理中,SI和DI作為隱含的源變址和目的變址寄存器,此時SI和DI聯用,DI和附加段寄存器ES聯用,分別達到了在數據段和附加段尋址的目的。
指針寄存器和變址寄存器只能按16位進行存取操作,主要用來形成操作數的地址,用于堆棧操作和變址運算中計算操作數的有效地址。
指令指針IP(Instruction Pointer)
指令指針IP是一個16位專用寄存器(指令指針寄存器),它指向當前需要取出的指令字節,當BIU從內存中取出一個指令字節后,IP就自動加上指令長度的值,指向下一個指令字節。
注意:IP指向的是指令地址的段內地址偏移量,又稱偏移地址(Offset Address)或有效地址(EA,Effective Address),程序不能直接訪問IP。
任意時刻,CPU將CS:IP指向的內存單元中的內容看作指令。
標志寄存器FR(Flag Register)
8086有一個18位的標志寄存器FR,在FR中有意義的有9位,其中6位是狀態位,3位是控制位。
標志寄存器FR:
條件標志
狀態信息是由中央處理機根據計算結果自動設置的。
| OF | 溢出標志位OF(overflow flag)用于反映有符號數加減運算所得結果是否溢出。 如果運算結果超過當前運算位數所能表示的范圍,則稱為溢出。溢出時OF=1,否則OF= 0。 注意,這里所講的溢出,只是對有符號數運算而言,對無符號數沒有意義。 |
| SF | 符號標志SF(sign flag)用來反映運算結果的符號位,它與運算結果的最高位相同。在微機系統中,有符號數采用補碼表示法,所以,SF也就反映運算結果的正負號。正數SF=0,負數SF為1。 注意:SF是對有符號數運算有意義的標志位。 |
| ZF | 零標志ZF(zero flag)用來反映運算結果是否為0。如果運算結果為0,則其值為1,否則其值為0。在判斷運算結果是否為0時,可使用此標志位。 |
| AF | 輔助進位標志AF(auxiliary flag)記錄運算時第3位(字節運算)或第7位(字運算)產生的進位或借位值。例如,執行加法指令時第3位有進位時AF=1,否則AF=0。 |
| PF | 奇偶標志PF(parity flag)用于反映運算結果中“1”的個數的奇偶性。當結果操作數中1的個數為偶數時PF=l,否則PF=0。 |
| CF | 進位標志CF(carry flag)主要用來反映運算是否產生進位或借位。當最高有效位有進位或借位時CF=1,否則置CF=0。 注意:CF是對無符號數運算有意義的標志位 |
無符號數比較示例:
控制標志
控制標志是系統程序或用戶程序根據需要用指令設置的。
| DF | 方向標志DF(direction flag)位用來決定在串操作指令執行時有關指針寄存器發生調整的方向。當DF位為1時,每次操作后使變址寄存器SI和DI減量;當DF為0時,則使SI和DI增量。 |
| IF | 中斷允許標志IF(interrupt flag)位用來決定CPU是否響應CPU外部的可屏蔽中斷發出的中斷請求。但不管該標志為何值,CPU都必須響應CPU外部的不可屏蔽中斷所發出的中斷請求,以及CPU內部產生的中斷請求。具體規定如下: (1)當IF=1時,CPU可以響應CPU外部的可屏蔽中斷發出的中斷請求; (2)當IF=0時,CPU不響應CPU外部的可屏蔽中斷發出的中斷請求。 總的來說:IF為1時,開中斷,否則關中斷。 |
| TF | 跟蹤標志TF(trap flag), 也叫做陷阱標志。該標志可用于程序調試。TF標志沒有專門的指令來設置或清楚。 (1)如果TF=1,則CPU處于單步執行指令的工作方式,此時每執行完一條指令,就顯示CPU內各個寄存器的當前值及CPU將要執行的下一條指令。 (2)如果TF=0,則處于連續工作模式。 總的來說:TF=1時,每條指令執行完后產生陷井,TF=0時,CPU正常工作不產生陷井。 |
Debug中標志位的符號表示
| OF | OV | NV |
| DF | DN | UP |
| IF | EI | DI |
| SF | NG | PL |
| ZF | ZR | NZ |
| AF | AC | NA |
| PF | PE | PO |
| CF | CY | NC |
標志寄存器FLAGS
這是一個存放條件碼標志、控制標志和系統標志的寄存器。
段寄存器(Segment Register)
為了運用所有的內存空間,8086設定了四個段寄存器,專門用來保存段地址:
| CS(Code Segment) | 代碼段寄存器 | 代碼段存放當前正在運行的程序 |
| DS(Data Segment) | 數據段寄存器 | 數據段存放當前正在運行程序所有的數據,如果程序使用了串處理指令,則其操作數也會存放在數據段中。 處理串的時候,DS默認為源串。 |
| SS(Stack Segment) | 堆棧段寄存器 | 定義了堆棧所在的區域。 |
| ES(Extra Segment) | 附加段寄存器 | 這是一個輔助的數據區,也是串處理指令的目的操作數存放區。 處理串的時候,ES默認為目的串。 |
附錄
- 寄存器介紹
存儲器
存儲器是計算機的記憶部件,用來存放程序和數據。按所在的位置,存儲器可以分成主存儲器和輔助存儲器。
主存儲器存放當前正在執行的程序和使用的數據,CPU可以直接存取,它由半導體存儲器芯片構成,其成本高,容量小,但速度快。
輔助存儲器可用來長期保存大量程序和數據,CPU需要通過I/O接口訪問,它由磁盤或光盤構成,其成本低,容量大,但速度較慢。
存儲單元
存儲器被劃分為若干個存儲單元,每個存儲單元有一個惟一的存儲器地址,從0開始順序編號,存儲單元的地址是無符號數, n位二進制數共能表示2^n個存儲單元的地址。
類型
| DB | define byte | 定義字節類型變量,一個字節數據占1個字節單元,讀完一個,偏移量加1 |
| BW | define word | 定義字類型變量,一個字數據占2個字節單元,讀完一個,偏移量加2 |
| DD | define double(word) | 定義雙字類型變量,一個雙字數據占4個字節單元,讀完一個,偏移量加4 |
字地址
字地址:一個字存放到存儲器要占用連續的兩個字節單元。字的低字節(低8位)存放在低地址中,高字節(高8位)存放在高地址中,字單元的地址用低地址表示。
例如:34560H的字單元的內容是1234H,而地址為78780H時字單元的內容是3332H。記作:
- (34560H)=1234H
(78780H)= 3332H
同一個地址既可以看成字節單元,也可以看作是字單元、雙字單元、或者是4字單元的地址,這要根據使用情況而定。字單元地址可以是偶數,也可以是奇數。但是,在8086和80286中,訪問存儲器(要求取數或者存數)都是以字為單元進行的,也就是說,機器是以偶地址訪問存儲器的。這樣,對于奇地址的字單元,要取一個字需要訪問二次存儲器,當然,這樣做需要花費較多的時間。
存儲器有這樣一個特性:它的內容取之不盡的。也就是說,從某個單元取出其內容后,該單元仍然保持著原來的內容不變,可以重復取出,只有存入新的信息后,原來保存的內容就自動丟失了。
各類存儲器芯片
從讀寫屬性上看分為兩類:
- 隨機存儲器(RAM)和只讀存儲器(ROM)
- 從功能和連接上分類:
- 隨機存儲器RAM
- 裝有BIOS的ROM
- 接口卡上的RAM
所有的物理存儲器被看作一個由若干存儲單元組成的邏輯存儲器;每個物理存儲器在這個邏輯存儲器中占有一個地址段,即一段地址空間;CPU在這段地址空間中讀寫數據,實際上就是在相對應的物理存儲器中讀寫數據。
寄存器與存儲器的比較
實模式存儲器尋址
8086CPU的地址線是20位的,這樣最大可尋址空間應為220=1MB,其物理地址范圍從00000H~FFFFFH。
而8086CPU寄存器都是16位的,僅能表示地址范圍 0000H ~ FFFFH(64KB) 。那么,這1MB(2^20)空間如何用16位寄存器表達呢?
根據要求可把1M字節地址空間劃成若干邏輯段。每個邏輯段必須滿足兩個條件:
(1)邏輯段的起始地址(簡稱段首址)必須是16的倍數;
(2)邏輯段的最大長度為64K(2^6)。
按照這兩個條件,1M字節地址空間最多可劃分成64K個邏輯段,最少也要劃分成16個邏輯段。邏輯段與邏輯段可以相連,也可以不連,還可以重疊。
邏輯地址(LA)和物理地址(PA)
物理地址
就是存儲器的實際地址,它是指CPU和存儲器進行數據交換時所使用的地址(20位)。
邏輯地址
是在程序中使用的地址(16位) ,它由段地址和偏移地址兩部分組成。邏輯地址的表示形式為“段地址∶偏移地址”。
- 段地址:是指邏輯段在主存中的起始位置。
- 段內偏移地址:是指主存單元距離段首址的偏移量,簡稱偏移地址,用EA來表示,由于限定每段不超過64KB,所以偏移地址可以用16位數據表示。
- 物理地址形成:物理地址用PA表示, 將邏輯地址中的段地址左移4位,加上偏移地址就得到20位物理地址。
- 物理地址 = 段地址 x 16 + 偏移地址
段的概念
錯誤認識:
內存被劃分成了一個一個的段,每一個段有一個段地址。
正確認識:
內存并沒有分段,段的劃分來自于CPU,由于8086CPU用“(段地址×16)+偏移地址=物理地址”的方式給出內存單元的物理地址,使得我們可以用分段的方式來管理內存。
8086PC工作過程的簡要描述
- 1)從CS:IP指向內存單元讀取指令,讀取的指令進入指令緩沖器;
- 2)IP = IP + 所讀取指令的長度,從而指向下一條指令;
- 3)執行指令。 轉到步驟 (1),重復這個過程。
- 在 8086CPU 加電啟動或復位后( 即 CPU剛開始工作時)CS和IP被設置為CS=FFFFH,IP=0000H,即在8086PC機剛啟動時,CPU從內存FFFF0H單元中讀取指令執行,FFFF0H單元中的指令是8086PC機開機后執行的第一條指令。
匯編指令
指令格式
- ASMARM的專欄 - 淺談ARM 匯編中的標號(Labels)
1、雙操作數指令(二地址指令)
- 格式:[標號:] 操作符 DST,SRC [;注釋]
- 操作規定:
- 1)DST與SRC應為同種操作類型且類型明確,即同為字節類型或字類型。
- 2)DST不能是立即數。
- 3)SRC和DST不能同時為存儲器操作數(mem) 。
- 4)操作結束后,運算結果存入DST中,SRC內容不變。
- 例如:
2、單操作數指令(一地址指令)
- 格式:[標號:] 操作符 DST [;注釋]
- 操作規定:
- 1)DST類型必須明確即為字節類型或字類型,不能是模糊類型
- 2)操作對象為目的操作數,操作結束后結果存入DST中
- 3)DST不能是立即數,只能是寄存器操作數(reg) 或存儲器操作數(mem)。
3、無操作數指令(零地址指令)
- 格式:[標號:] 操作符 [;注釋]
- 操作規定:指令中只有操作碼,不含操作數,這種指令有兩種可能:
- 1)無需任何操作數。如停機指令(HLT)、空操作指令(NOP)等。
- 2)所需操作數是隱含指定的,操作時取固定操作數進行操作。如進位位置0(CLC)、方向標志置0(CLD)。
尋址方式
定義:尋址方式是指尋找指令中操作數所在地址的方法。
常用的尋址方式有:立即尋址、直接尋址、寄存器尋址、寄存器間接尋址、變址尋址、基址加變址、隱含尋址等。
尋找指令中所需要操作數存放地址的方式或者程序轉移時尋找轉移地址的方式稱為尋址方式,因而尋址方式有兩大類:一類是數據尋址方式,另一類是轉移地址尋址方式。
由于80x86指令涉及四種操作數:立即操作數(data)、寄存器操作數(reg)、存儲器操作數(mem)和隱含操作數,因此,數據尋址方式又可對應四種尋址方式,即:立即尋址、寄存器尋址、存儲器尋址和固定尋址。
數據尋址方式-立即尋址
操作數直接包含在指令中,它緊跟在指令操作碼后面,它作為指令存放在存儲器代碼段中,這種操作數稱為立即數。立即數可以是8位,也可以是16位。
MOV AX,1234H ;(AX)=1234H立即尋址方式用來表示常數,它常用于給寄存器或內存單元賦初值。需要強調的是,立即尋址只能用于源操作數,不能用于目的操作數,且源操作數的長度應該與目的操作數的長度一致。
立即數可以為8位,也可以是16位;規定立即數只能是整數,不能是小數、變量或者其他類型的數據。
數據尋址方式-寄存器尋址
操作數直接存放在由指令指明的寄存器中。在匯編指令中直接書寫寄存器名,16位寄存器操作數可以是AX、BX、CX、DX、SI、DI、BP、SP、DS、ES、SS、CS等;8位寄存器操作數只能是AH、AL、BH、BL、CH、CL、DH、DL。
指令指針寄存器IP和標志寄存器FLAGS一般不直接出現在程序中。
此尋址方式由于存取操作數直接從CPU內部寄存器中獲得,不需訪問存儲器,因而指令執行的速度快。
寄存器尋址既可用于源操作數,又可用于目的操作數,應用頻率最高。
注意:CS不能作為目的操作數,因為CS:IP控制著程序指令序列的執行順序,不能在程序中由指令隨意改變。
數據尋址方式-存儲器尋址
存儲器尋址方式的操作數都是存放在除代碼段以外的存儲區中,一般是數據段、附加段、堆棧段中的存儲單元。指令中給出的是存儲單元的地址或產生存儲單元地址的表達式。
在匯編語言源程序中,存儲單元地址是采用邏輯地址的形式表示的,即:段首址:段內偏移地址。段首址存放在某個段寄存器中,段內偏移地址即有效地址EA是由3個地址分量的某種組合求得,這3個地址分量是:位移量、基址(BX,BP)、變址(SI,DI) 。
這3個地址分量的不同組合,使形成有效地址EA的方法不同,相應有以下5種不同的存儲器操作數尋址方式:直接尋址、寄存器間接尋址、寄存器相對尋址、基址變址尋址、相對基址變址尋址。
直接尋址方式
直接尋址是最簡單的存儲器尋址,操作數的有效地址EA由指令直接給出,只包含位移量。它主要用于存取簡單變量。
MOV AX, [2000H] EA=2000H, 假設(DS)=3000H, 那么PA=32000H
對使用直接尋址方式需說明以下幾點:
- 操作數默認隱含的段為數據段 DS。
- 若操作數在代碼段、堆棧段或附加段中,可使用段跨越前綴 。MOV AX, ES: [2000H]
- 指令中操作數的EA既可以是一個數字地址,也可以是一個符號地址。當EA是一個數字時,一定要注意立即尋址方式與直接尋址方式的區別,直接尋址必須有[ ]符號。
- 操作數地址可由變量(符號地址)表示, 但要注意變量的屬性。
直接尋址方式適合于處理存儲器的單個存儲單元。例如,要處理某個存放在存儲器里面的變量,可以使用直接尋址方式把變量先取到一個寄存器中,然后在進一步處理。
80x86中,為了使指令字不要太長,規定雙操作數指令的兩個操作數中,只能有一個使用存儲器尋址方式,這就是一個變量常常先要送到寄存器的原因了。
寄存器間接尋址
操作數的有效地址只包含基址寄存器或者變址寄存器內容的一種成分。因此,有效地址就在某個寄存器中,而操作數則在存儲器中。
操作數的有效地址EA存放在SI、DI、BX或BP之一中,而操作數在存儲器中。若用BX、SI或DI間接尋址時,則操作數默認在數據段中,用DS的內容作為段首址,操作數的物理地址為:
若指令中使用BP間接尋址時,則用堆棧段SS的內容作為段首址,操作數的物理地址為: PA=(SS)×16 +(BP)。
注意:
- 8086不允許使用AX、CX、DX 來存放 EA,比如:MOV AX, [CX]就是錯誤的。
- SRC 和 DST 的字長一致,自動適應寄存器的長度。
- 適用于數組、字符串、表格的處理(可以看成一個數組來使用),執行完一條指令后,只需修改寄存器內容就可以取出下一項處理。
寄存器相對尋址方式
寄存器相對尋址方式也可以稱為直接變址尋址方式。
操作數的有效地址EA是指令中指定的基址寄存器或變址寄存器的內容與指令中給出的位移量之和,即
操作數的物理地址為:
若操作數不在默認段中,則應使用段跨越前綴明確指定。
- 適于數組、字符串、表格的處理。
基址變址尋址
操作數的有效地址EA是指令中的基址寄存器的內容+變址寄存器的內容。
指令格式:
- 適于數組、字符串、表格的處理
- 必須是一個基址寄存器和一個變址寄存器的組合
相對基址變址尋址
操作數的有效地址EA是指令中的基址寄存器的內容、變址寄存器的內容、位移量三個地址分量之和,即:
相對基址加變址尋址方式有多種等價的書寫方式,書寫格式:[BX+SI+1000H]、1000H[BX+SI]、1000H[BX][SI]和1000H[SI][BX]等格式都是正確的,并且其尋址含義也是一致的,但格式:BX[1000H+SI]、SI[1000H+BX]等是錯誤的,即所用寄存器不能在”[“,”]”之外,該限制對寄存器相對尋址方式的書寫也同樣起作用。
這種尋址方式通常用于對二維數組的尋址。例如,存儲器中存放著由多個記錄組成的文件,則位移量可指向文件之首,基址寄存器指向某個記錄,變址寄存器則指向該記錄中的一個元素。這種尋址方式也為堆棧處理提供了方便,一般(BP)可指向棧頂,從棧頂到數組的首地址可以使用位移量來表示,變址寄存器可以用來訪問數組中的某個元素。
附錄
可以出現在[]中的內容是有限制的,他們必須是下列中的一個(其中idata表示一個立即數):
idata BX BP SI DI BX+SI BX+DI BP+SI BP+DI BX+idata BP+idata SI+idata DI+idata BX+SI+idata BX+DI+idata BP+SI+idata BP+DI+idata其中,使用到BP的默認段寄存器為SS,其他未DS
段寄存器的使用規定
匯編語言源程序
- 匯編源程序:由三部分組成:匯編指令、偽指令、其他標號與符號。
- 指令是能被計算機識別并執行的二進制代碼,它規定了計算機能完成的某一操作。
- 偽指令是對匯編起某種控制作用的特殊命令,其格式與通常的操作指令一樣,并加在匯編程序的任何地方,但是他們并不產生機器指令。
- 一個匯編源程序是由多個段組成的,這些段被用來存放代碼、數據或當作棧空間來使用。
- 注意:一個有意義的匯編程序中至少要有一個段,這個段用來存放代碼。
處理器選擇偽操作
| .8086 | 選擇8086指令系統 |
| .286 | 選擇80286指令系統 |
| .286 P | 選擇保護模式下的80286指令系統 |
| .386 | 選擇80386指令系統 |
| .386 P | 選擇保護模式下的80386指令系統 |
| .486 | 選擇80486指令系統 |
| .486 P | 選擇保護模式下的80486指令系統 |
| .586 | 選擇80586指令系統 |
| .586 P | 選擇保護模式下的80586指令系統 |
這類偽操作一般都是放在整個程序的最前面。如果不給出,則匯編程序一般認為其默認值為.8086。
段定義偽操作
在段定義時,如果定位類型用戶未選擇,就表示是隱含類型,其隱含類型是PARA。
PARA屬定位類型,是對該段起始地址定位。一般,各個邏輯段的首地址在‘節’的整數邊界上(每16個存儲單元叫做一節),即每個邏輯段的起始地址是16的整數倍。對于PARA—指定定位段的起始地址必須在節的整數邊界。
存儲器的物理地址是由段地址和偏移地址組合而成的,匯編程序在把源程序轉換為目標程序的時候,必須確定標號和變量(代碼段和數據段的符號地址)的偏移地址,并且需要把有關信息通過目標模塊傳送給連接程序,以便連接程序把不同的段和模塊連接起來,形成一個可執行的程序。
明確段與寄存器的關系:assume cs:code, ds:data, es:extra
說明:
- 1)data為段名稱,也是段首地址,可自己定義
- 功能:定義一個段,segment說明一個段開始,ends 說明一個段結束。一個段必須有一個名稱來標識,使用格式為:
- 段名 segment
- 段名 ends
- 功能:定義一個段,segment說明一個段開始,ends 說明一個段結束。一個段必須有一個名稱來標識,使用格式為:
- 2)明確段和段寄存器的關系。assume只是說明關聯關系,并沒有對段寄存器賦值,除了CS(裝入程序時由CPU給出),其他段寄存器要在程序中設置。
- 3)mov ax,4c00h和int 21h最后兩條指令所實現的功能是程序返回。
- 4)程序結束標志,格式:END [label],標號label 指示程序開始執行的起始地址。
偽操作(偽指令)
偽操作是匯編程序對源程序進行匯編時處理的操作,完成處理器選擇、存儲模式定義、數據定義、存儲器分配、指示程序開始結束等功能。
- 處理器選擇偽操作;
- 功能:告訴匯編程序應該選擇哪一種指令系統。
位置:一般放在整個程序的最前面,也可放在程序中所用指令的上一行。
如不給出,則默認為.8086。
- 功能:告訴匯編程序應該選擇哪一種指令系統。
- 段定義偽操作;
- 程序開始和結束偽操作;
- Start
- end Start
- 數據定義及存儲器分配偽操作;
- 表達式賦值偽操作;
- 地址計數器與對準偽操作;
- 基數控制偽操作。
數據定義和存儲器分配偽操作
格式:[變量] 助記符 操作數 [ , 操作數 , … ] [ ; 注釋]
- 助記符:DB DW DD DF DQ DT
- 例:操作數可以是常數或表達式
- DATA_BYTE DB 10,4,10H,?
- DATA_WORD DW 100,100H,-5,?
- 匯編程序在匯編期間在存儲器中存入數據
- 操作數也可以是字符串
- 操作數也可以是地址
- 操作數字段還可以使用復制操作符DUP來復制操作數
- repeat_count DUP(operand,……,operand)
- repeat_count可以是一個表達式,它的值應該是一個正整數,用來指定括號中的操作數的重復次數。
- 操作數?可以保留存儲空間,但是不存入數據。(僅僅申請一個存儲空間)
- (AX)=3402H (AL)=34H
- 可以看出:同一個變量可以具有不同的類型屬性。
LABEL 偽操作: name LABEL type
- LABEL可以使同一個變量具有不同的類型屬性。
其中變量的數據類型可以是BYTE,WORD,DWORD,標號的代碼類型可以是NEAR或FAR。
數據定義及存儲器分配偽指令格式中的 “變量 “是操作數的符號地址,它是可有可無 的,它的作用與指令語句前的標號相同,區別是變量后面不加冒號。如果語句中有變量,那么匯編程序將操作數的第一個字節的偏移地址賦于這個變量。
表達式賦值偽操作
說明:有時程序中多次出現同一個表達式,為了方便起見,可以使用賦值偽操作給表達式賦予一個名字。、
EQU是賦值偽指令。賦值語句僅在匯編源程序時,作為替代符號用。不產生目標代碼,也不占有存儲單元。
此后,程序中凡是用到該表達式之處,都可以用表達式名來代替了。可見,EQU的引入提高了程序的可讀性,也使其更加易于修改。
格式:表達式名 EQU 表達式
功能:給表達式賦予一個名字,在程序中用表達式名代替該表達式。
“ = ” 偽操作 (允許重復定義)
…… EMP = 7 …… EMP = EMP+1 ……說明:EQU指令類似于C語言的#define宏,在編譯前被轉化。
地址計數器與ORG偽操作
地址計數器$
地址計數器 $:保存當前正在匯編的指令的偏移地址
- $ 用在指令中時,表示本條指令的第一個字節的地址;
- $ 用在偽操作的參數字段時表示地址計數器的當前值。
- 例如:ARRAY DW 1, 2 , $+4 , 3 , 4 , $+4
-
- 通過圖片可以看出,地址0078保存的數據為當前地址加上4;
- 地址007E是當前地址加上4。
ORG
ORG 偽操作:用來設置當前地址計數器的值。即:指定一個地址,后面的程序或數據從這個地址值開始分配。
SEG1 SEGMENTORG 10VAR1 DW 1234HORG 20VAR2 DW 5678HORG $+8; 當前地址加8,VAR3 DW 1357H SEG1 ENDSEVEN
EVEN偽操作使下一個變量或者指令開始于偶數字節地址。一個字的地址最好從偶地址開始,所以對于字數組為了保證其從偶地址開始,可以在其前面使用EVEN偽操作來達到這一目的。
EVEN ;使下一地址從偶地址開始
A DB ‘morning’ EVEN ;使下一地址從偶地址開始 B DW 2 DUP (?)ALIGN
ALIGN偽操作為保證雙字數組邊界從4的倍數開始創造了條件:
ALIGN 4 ; 保證下一個地址是4的倍數 ALIGN 2 ; 與EVEN等價說明:
- ALIGN boundary(boundary的值必須是2的冪)。
基數控制偽操作
匯編程序默認的數是十進制數,因而除非專門指定,匯編程序把程序中出現的數均看作為十進制數。為此,當使用其他基數的時候,需要專門給以標記:
| 二進制 | B |
| 十進制 | D |
| 十六進制 | H |
| 八進制 | O 或者 Q |
匯編語言程序格式
匯編語言源程序中每個語句可以由四項組成,格式如下:
其中
- 名字(name)項是一個符號。
- 操作(operation)項是一個操作碼的助記符,它可以是指令、偽操作或者宏指令名。
- 操作數(operand)項是一個或多個表達式組成,它提供為執行所要求的操作而需要的信息。
- 注釋(comment)項是用來說明程序或者語句的功能。;為識別注釋項的開始。;也可以從一行的第一個字符開始,此時整行都是注釋,常用來說明下面一段程序的功能。
名字項
操作項
操作數項由一個或者多個表達式組成,多個操作數項之間一般用逗號分開。
操作數項可以是常數,寄存器,標號,變量或者由表達式組成。表達式是常數,寄存器,標號,變量與一些操作符相結合的序列,可以有數字表達式和地址表達式兩種。
算術操作符
算術操作符有+,-,*,/、MOD。其中MOD是指除法運算后得到的余數。算術操作符可以用于數字或者地址表達式中,但當它用于地址表達式的時候,只有其結果有明確的物理意義的時候才是有效的結果。例如:兩個地址相乘和相除是沒有意義的。在地址表達式中,可以使用+或者-,但也必須注意其物理意義,例如把兩個不同段的地址相加也是沒有意義的。經常使用的是地址±數字量,它是有意義的,例如,SUM+1是指SUM字節單元的下一個字節單元的地址(注意:不是指SUM單元的內容加1),而SUM-1則是指SUM字節單元的前一個字節單元的地址。
邏輯與移位操作符
邏輯操作符有AND,OR,XOR和NOT;移位操作符有SHL和SHR。他們都是按位操作的,只能用于數字表達式中。
關系操作符
| EQ | Equal | 相等 |
| NE | Not equal | 不想等 |
| LT | Less than | 小于 |
| GT | Great than | 大于 |
| LE | Less and equal | 小于或等于 |
| GE | Great and equal | 大于或等于 |
說明:
- 關系操作符的兩個操作數必須都是數字,或者同一段內的兩個存儲器地址。
- 計算結果應為邏輯值,結果為真,表示為0FFFFH;結果為假的時候,則表示為0。
數值回送操作符
它主要有的TYPE,LENGTH,SIZE,OFFEST,SEG等等。這些操作符把一些特征或者存儲器地址的一部分作為數值回送。
TYPE
格式為:TYPE expression
如果該表達式是變量,則匯編程序將回送變量的以字節數表示的類型:DB=1,DW=2,DD=4,DF=6,DQ=8,DT=10。如果表達式是標號,則匯編程序將回送代表該標號類型的數值:NEAR=-1,FAR=-2。如果表達式是常數,則應回送0。
操作數項(表達式)
- (1) 算術操作符: +、- 、*、 /、Mod
- (2) 邏輯操作符: AND、OR、XOR、NOT,移位操作符: SHL、SHR
- (3) 關系操作符: EQ、NE、LT、LE、GT、GE
- (4) 數值回送操作符: TYPE、 LENGTH、 SIZE 、OFFSET、SEG、
- TYPE 變量 / 標號 / 常數
- DB、DW、DD、DF、DQ、DT、NEAR、FAR、常數
- 1、2、4、6、8、10、-1、-2、0(與上面的變量對應)
- (5) 屬性操作符: PTR、段操作符、SHORT、THIS、HIGH、LOW、HIGHWORD、LOWWORD
- PTR
- 格式:<類型> PTR <符號名>
- 功能是定義符號名為新類型。如,設內存變量D1是字節屬性,把它的兩個字節內容送到AX中。
- PTR
LENGTH
- LENGTH 變量
- 功能:回送由DUP定義的變量的單元數,其它情況回送1
- DR2 DW 10H DUP(0,2 DUP(2)),MOV CL,LENGTH DR2CL的值為10H
- DR1 DB 10H,30H,MOV BL,LENGTH DR1BL的值為1。
SIZE
- SIZE
- 變量功能:LENGTH * TYPE
OFFSET和SEG
- OFFSET / SEG 變量 / 標號
- 功能:回送變量或標號的偏址 / 段址
Debug
匯編調試程序Debug使用
使用DEBUG調試和運行可執行文件
- 在初次使用DEBUG時,可參照下列步驟進行:
- 1、調用DEBUG,裝入用戶程序
- 2、U命令反匯編程序,記錄代碼段與數據段首地址
- 3、R命令觀察寄存器初始狀態
- 4、以單步工作方式T開始運行程序 ,設置段寄存器的值。
- 5、D觀察用戶程序數據段初始內容
- 6、繼續以單步工作方式運行程序
- 7.G連續工作方式運行程序
- 8.E或A修改程序和數據
- 9.運用斷點調試程序 G
80x86指令系統
空操作指令指令格式:NOP
說明:NOP是英語“No Operation”的縮寫。NOP無操作數,所以稱為“空操作”。
執行NOP指令只使程序計數器PC加1,所以占用一個機器周期。
通用數據傳送指令
數據傳送指令:MOV、XCHG、LEA、LDS、LES、PUSH、POP、PUSHF、POPF、CBW、CWD、CWDE。
棧的基本操作
8086CPU的入棧和出棧操作都是以字為單位進行的。
push與pop指令的執行過程
PUSH指令每次只能壓入一個字(16位)。
push ax (1)SP = SP–2; (2)將AX中的內容送入SS:SP指向的內存單元處。 pop ax (1)將SS:SP指向的內存單元處的數據送入ax中; (2)SP = SP+2。說明:
- 每執行一次PUSH,SP指針都會減1。
MOV指令
指令寫法:MOV DST,SRC
執行操作:(DST)<-(SRC)
其中DST表示目的操作數,SRC表示源操作數。可以在CPU內部或者在存儲器之間傳送數據。
說明:
- SRC和DST的操作類型必須明確而且一致。
- DST不能是立即數,而且也不能是CS。
- 數據傳送指令是不能把數據傳送給CS的,因為CS是代碼段寄存器,CS如果被修改程序就無法執行。
- DST、SRC也不能同時是存儲器尋址。
- DST、SRC也不能同時是段寄存器。
- 立即數不能直接送段寄存器,必須通過寄存器(比如AX)送達段寄存器。
- 指令的執行不影響標志位。
- 立即數可以直接送到存儲器,但應指定存儲器的類型。如:
- mov byte ptr[di], 3
- mov word ptr[si], 3000
- mov dword ptr[bx], 0FFFFFFh
XCHG指令
指令格式: XCHG OPR1, OPR2
功能: 將操作數地址中的內容互換。
執行操作: (OPR1) <-> (OPR2)
注意:
- 指令的執行并不影響標志位。
- 不允許使用段寄存器,不允許使用立即數,不支持兩個存儲單元之間的數據交換。
- 允許字或者字節操作。
累加器專用傳送指令
XLAT
功能:將表格中的一個字節內容送到AL累加器中。常用于將一種代碼轉換為另一種代碼。
這條指令根據AL寄存器提供的位移量,將BX指使的字節表格中的代碼換存在AL中。(AL)<-((DS)*16+(BX)+(AL))
說明:本指令并不影響標志位。
地址傳送指令
| LEA | load effective address | 有效地址送寄存器 |
| LDS | load DS with pointer | 指針送寄存器和DS |
| LES | load ES with pointer | 指針送寄存器和ES |
| LFS | load FS with pointer | 指針送寄存器和FS |
| LGS | load GS with pointer | 指針送寄存器和GS |
| LSS | load SS with pointer | 指針送寄存器和SS |
LEA
功能:有效地址送寄存器。
全稱:load effective address
MOV BX, OFFSET [BX+SI] ;——× 注意:OFFSET只能與簡單的符號地址相連。
算術指令
算術運算指令會根據運算結果影響狀態標志,主要影響6個標志位:ZF、CF、AF、SF、OF和PF。
加法指令
| ADD | ADD DST,SRC | 功能:加法,將SRC和DST的值相加之后存放在DST中。 執行操作: (DST) <- (SRC) + (DST) |
| ADC | ADC DST, SRC | 功能:帶進位的加法指令,將SRC和DST的值和進位標志位(CF)相加之后存放在DST中。 執行操作:(DST) <- (SRC) + (DST) + CF |
| INC | INC OPR | 功能:加一指令。 執行操作:(OPR) <- (OPR) + 1 |
說明:加法指令都會影響條件標志位,但INC指令不影響CF標志。
INC影響的條件標志位包括:SF,ZF,OF,AF,PF。
ADC指令的作用
在執行 adc 指令的時候加上的 CF 的值是由 adc指令前面的指令決定的,也就是說,關鍵在于所加上的CF值是被什么指令設置的。
下面的指令和add ax , bx具有相同的結果:
看來CPU提供 ADC指令的目的,就是來進行加法的第二步運算的。ADC指令和ADD指令相配合就可以對更大的數據進行加法運算。
注意:有符號的雙精度數的溢出,應根據ADC指令的OF位判斷,而作低位加法用的ADD指令的溢出是無意義的。
用16位寄存器編寫程序:
說明:
OF位可以用來表示帶符號數的溢出,CF位可以用來表示無符號數的溢出。
條件標志(或者稱呼為)位中最主要的是CF,ZF,SF,OF四位,分別表示了進位、結果為零,符號和溢出的情況。
執行加法指令的時候,CF位是根據最高有效位是否向最高位的進位來設置的。有進位的時候CF=1,無進位的時候CF=0。
OF位則根據操作數的符號及其變化情況來設置的:若兩個操作數的符號相同,而結果的符號與之相反則OF=1,否則OF=0。
- 注意區分OF是根據原先的符號位來判斷的。
溢出位OF既然試試根據數的符號及其變化來設置的,當然它是用來表示帶符號數的溢出的,從其設置條件來看結論也是明顯的。
CF位可以用來表示無符號數的溢出。一方面,由于無符號數的最高有效位只有數值意義而無符號意義,所以該位產生的進位應該是結果的實際的進位值,但是在有限數位的范圍內就說明了結果的溢出情況;另一方面,它所保存的進位值有時候還是有用的。例如。雙字長數運算的時候,可以利用進位值把低位字的進位計入高位字中。
加法的ASCII碼調整指令AAA
- 功能:
- 把AL中的和調整到非壓縮BCD碼格式 → AL
- AH + 調整產生的進位值 → AH
- 影響標志位:
- CF=1,AF
- 用途:
- 適用于數字ASCⅡ碼的調整;
- 也適用于一般的非壓縮BCD碼的十進制調整
- 調整原則:
- ①若AL的低4位是在0~9之間,且AF=0,則無需調整
- ②若AL的低4位是在0AH~0FH之間,或AF=1,則AL
- 加6調整,AH+1→ AH,且使CF=1
加法的十進制調整指令DAA
- 功能:
- 把AL中的和調整到壓縮BCD碼格式→ AL
- 影響標志位:
- CF、PF、AF、ZF、SF
- 注:
- ① DAA通常跟在ADD和ADC指令之后使用
- ② 只能對AL中的運算結果進行BCD碼調整
減法指令
| SUB | SUB DST,SRC | 功能:減法,將SRC和DST的值相減之后存放在DST中。 執行操作: (DST) <- (SRC) + (DST) |
| SBB | SUB DST, SRC | 功能:帶借位減法指令,將SRC和DST的值和進位標志位(CF)相加之后存放在DST中。 執行操作:(DST) <- (SRC) + (DST) - CF CF為進位位的值。 |
| DEC | DEC OPR | 功能:加一指令。 執行操作:(OPR) <- (OPR) + 1 |
| NEG | NEG OPR | 功能:加一指令。 執行操作:(OPR) <- (OPR) + 1 把操作數按位求反后末尾加1。 |
| CMP | CMP OPR1, OPR2 | 功能:加一指令。 執行操作: (OPR1) - (OPR2) 執行減法操作,不保存結果。往往根據比較發生轉移。 |
說明:
減法運算的條件碼情況和加法類似。CF位說明無符號數相減的溢出,同時它又確實是被減數的最高有效位向高位的借位值。OF位則說明帶符號數的溢出。
減法的CF值反映了無符號數運算中的借位情況,因此當作為無符號運算時,若減數>被減數,此時有借位,則CF=1;否則CF=0。或者,也可以簡單地用二進制減法的運算中的最高有效位向高位的進位的情況來判別:有進位的時候CF=0,沒有進位的時候CF=1。
減法的OF位的設置方法為:若兩個數的符號相反,而結果的符號與減數相同則相同,則OF=1;除了上述情況外OF=0。OF=1說明帶符號數的減法溢出,結果是錯誤的。
NEG指令的條件碼按照求補后的結果設置,只有當操作數為0的時候,求補運算的結果使得CF=0,其他情況都為CF=1。所以,只有當字運算時對-128求補的時候,以及字運算的時候對-32768求補以及雙字運算的時候對-2^31求補的情況下OF=1,其他則均為OF=0。
減法的ASCII碼調整指令AAS
- 功能:
- 把AL中的差調整到非壓縮BCD碼格式 → AL
- AH - 調整產生的進位值 → AH
- 影響標志位:
- CF=1,AF
- 用途:同AAA
- 調整原則:
- ①若AL的低4位是在0~9之間,且AF=0,則無需調整
- ②若AL的低4位是在0AH~0FH之間,或AF=1,則減6調整,AH+1→ AH,且使CF=1
減法的十進制調整指令DAS
- 功能:
- 把AL中的差調整到壓縮BCD碼格式→ AL
- 影響標志位:
- CF、PF、AF、ZF、SF
- 注:
- ① DAA通常跟在SUB和SBB指令之后使用
- ② 只能對AL中的運算結果進行BCD碼調整
乘法指令
| MUL | 無符號數乘法指令 | MUL SRC |
| IMUL | 帶符號數乘法指令 | IMUL SRC |
| 字節操作數 | (AX) <- (AL) * (SRC) |
| 字操作數 | (DX, AX) <- (AX) * (SRC) |
注意:
- AL (AX) 為隱含的乘數寄存器。
- AX (DX,AX) 為隱含的乘積寄存器。 CPU會根據乘數是8位、16位,還是32位操作數,來自動選用被乘數:AL、AX或EAX。
- SRC不能為立即數。
- 除CF和OF外,對條件標志位無定義。
- 當乘積的高半部分不為0時,CF=1,OF=1。
乘法的ASCII碼調整指令AAM
- 功能:
- 把AL中的內容調整到非壓縮BCD碼格式 → AX
- 影響標志位:
- SF,ZF,PF
- 調整原則:
- 將AL中的內容除以0AH,高八位放在AH中,低八位保存在AL中
- 注:
- 執行AAM前必須執行MUL指令把兩個非壓縮的BCD碼相乘(要求高4位為0),結果放在AL中
除法指令
| DIV | 無符號數除法指令 | DIV SRC |
| IDIV | 帶符號數除法指令 | IDIV SRC |
| 字節操作數 | (AL) <- (AX) / (SRC) 的商 (AH) <- (AX) / (SRC) 的余數 |
| 字操作數 | (AX) <- (DX, AX) / (SRC) 的商 (DX) <- (DX, AX) / (SRC) 的余數 |
注意:
- AX (DX,AX) 為隱含的被除數寄存器。
- AL (AX) 為隱含的商寄存器。
- AH (DX) 為隱含的余數寄存器。
- SRC不能為立即數。
- 對所有條件標志位均無定義。但是可能產生溢出。
- 執行除法指令后,對AF,CF,OF,PF,SF,ZF標志位的影響都不確定。
除法的ASCII碼調整指令AAD
- 功能:
- 把AX中的被除數調整為二進制 → AL
- 影響標志位:
- SF,ZF,PF
- 調整原則:
- 若被除數是存放在AX中的兩位非壓縮BCD碼,調整為二進制數
- 若除數是一位非壓縮BCD碼,調整為二進制數
- 注:
- 指令AAD放在DIV指令前
標志寄存器傳送指令
| LAHF | load AH with flags | 將標志寄存器中的低八位送到AH中。 |
| SAHF | store AH into flags | 將AH寄存器的相應位傳送到標志寄存器的低8位。 |
| PUSHF/PUSHFD | push the flags or eflags | 標志進棧 |
| PUPF/POPFD | pop the flags or eflags | 標志出棧 |
注意:
這組指令中的LAHF和PUSHF/PUSHFD都不影響標志位。SAHF和POPF/POPFD則由裝入的值來確定標志位的值,但是POPFD指令不影響VM,RF,IOPF,VIF和VIP的值。
STC----是置進位標志指令,執行的結果是將進位標志CF置1
CLC----是清進位標志指令,其執行結果是置CF標志為0
讀取標志指令LAHF:
類型轉換指令
| CBW | 字節轉換為字 | AL符號擴展到AH中,形成AX中的字。即如果(AL)中的最高有效位是0,則(AH)=0;如(AL)的最高有效位為1,則(AH)=0FFH。 執行操作: 若(AL)的最高有效位為0,則(AH)= 00H 若(AL)的最高有效位為1,則(AH)= FFH |
| CWD/CWDE | 字轉換為雙字 | AX符號擴展 -> (DX,AX)雙字 執行操作: 若(AX)的最高有效位為0,則(DX)= 0000H 若(AX)的最高有效位為1,則(DX)= FFFFH |
| CWQ | 雙字轉換為4字 | EAX的內容符號擴展到EDX,形成EDX:EAX中的4字。 |
| BSWAP | 字節交換指令 | 格式:BASWAP r32。 該指令只能用于486及其后繼機型。r32指32位寄存器。 執行的操作:使指令指定的32位寄存器的字節次序變反。具體的操作為:1、4字節互換,2 |
注意:
- 隱含對AL 或AX 進行符號擴展
- 本組指令都不影響條件標志位
邏輯指令
邏輯運算指令
| AND | and | 邏輯與 格式:AND DST,SRC |
| OR | or | 邏輯或 格式:OR DST,SRC |
| NOT | not | 邏輯非 格式:NOT DST,SRC |
| XOR | exclusive or | 異或 格式:XOR DST,SRC |
| TEST | test | 作用:測試 格式:TEST OPR1,OPR2 執行的操作:(OPR1)∧(OPR2) 說明:兩個操作數相與的結果不保存,只是根據其特征置條件碼。 |
注意:
- 在以上的五種指令中,NOT不允許存放立即數。
- 其他4條指令除非源操作數是立即數,至少有一個操作數必須存放在寄存器中,另一個操作數則可以使用任意尋址方式。
- NOT不影響標志為。
- 其他4種指令將使CF位和OF位為0,AF位無定義,而SF位、ZF位和PF位則根據運算結果設置。
- 這些指令對處理操作數的某些位很有用,例如可以屏蔽某些位(將這些位置0),或者使某些位置1或者測試某些位等等。
AND
要求屏蔽0、1兩位,可以用AND指令并設置常數0FCH。
這兩條指令的運行結果使得(AL)=0BCH。因此,使用AND指令可以使得操作數的某些位被屏蔽。只需要把AND指令的源操作數設置成一個立即數,并把需要屏蔽的位設置為0,這樣指令執行的結果就可以把操作數的相應位置0,其他位保持不變。
OR
要求第5位置1,可以使用OR指令。
MOV AL,43H MOV AL,20H這兩條指令執行了之后,(AL)=63 H。因此,用OR指令可以使得操作數的某些位置1,其他位保持不變。只需要把OR指令的源操作數設置為一個立即數,并把需要置1的位設置為1,就可以達到目的了。
TEST
要測試操作數的某些位是否為0,可以使用TEST指令,同樣把TEST指令的源操作數設置成一個立即數,其中需要測試的位應該設置為1。
這里要求測試第0,1,2,3,5,7位是否為0,根據測試的結果設置條件碼為CF=OF=0,SF=0,ZF=1,說明了所需要測試的位均為0。如果在這兩條指令之后跟一條件轉移指JNZ,結果如果不是0就轉移,結果如果是0就順序往下執行,這樣就可以根據測試的情況產生不同的程序分支,轉向不同的處理方案了。
NOT
要測試操作數的某位是否為1,可以先把該操作數求反,然后使用TEST指令測試。如要測試AL寄存器中第2位是否為1,若為1則轉移到EXIT中去執行,可以用下列指令序列:
如AL寄存器的內容為0FH,為了避免破壞操作數的原始內容,把它復制到了DL中去測試,執行完TEST指令之后,結果為全0而有ZF=1,說明操作數的第2位為1引起的轉移到EXIT去執行。
XOR
要是操作數的某些位變反,可以使用XOR指令,只要把源操作數的立即數字段的相應位置設置為1就可以達到目的。如果求第0,1位變反,可以使用下面的指令:
則指令執行后,(AL)=12H,達到了第0,1位變反而其他位不變的目的。
XOR指令還可以用來測試某一個操作數是否與另外一個確定的操作數相等。這種操作在檢查地址是否匹配的時候是經常使用的。
這兩條指令是用來檢查AX的內容是否等于042EH,若相等則轉移到MATCH去執行匹配的情況需要做的工作,否則執行JZ下面的程序。
位測試并修改指令
386及其后繼機型增加了本組指令。
位掃描指令
386及其后繼機型增加了本組指令。
移位指令-這里編輯未完成
| SHL | shift logical left | 邏輯左移 |
| SAL | shift arithmetic left | 算術左移 |
| SHR | shift logical right | 邏輯右移 |
| SAR | shift arithmetic right | 算術右移 |
| ROL | rotat left | 循環左移 |
| ROR | rotat right | 循環右移 |
| RCL | rotate left through carry | 帶進位循環左移 |
| RCR | rotate right through carry | 帶進位循環右移 |
| SHLD | shift left double | 雙精度左移 |
| SHRD | shift right double | 雙精度右移 |
注意:
- OPR可用除立即數以外的任何尋址方式
- 移位次數CNT=1,SHL OPR, 1
- CNT>1,MOV CL, CNT,SHL OPR, CL
- 條件標志位:
- CF = 移入的數值
- OF = 1 CNT=1時,最高有效位的值發生變化
- OF = 0 CNT=1時,最高有效位的值不變
- 循環移位指令:
- 不影響 SF、ZF、PF、AF
- 移位指令:
- 常用來作乘以2或除以2 的操作。
- SAL:有符號數乘以2,SAR有符號數除以2;
- SHL:無符號數乘以2,SHR: 無符號數除以2。
| 邏輯左移 | 右邊統一添0,移出來的那一位放進CF |
| 算術左移 | 右邊統一添0,移出來的那一位放進CF |
| 邏輯右移 | 左邊統一添0,移出來的那一位放進CF |
| 算術右移 | 左邊添加符號位上的數,移出來的那一位放進CF |
示例:1010101010,其中[]是添加的位
邏輯左移一位:010101010[0]
算術左移一位:010101010[0]
邏輯右移一位:[0]101010101
算術右移一位:[1]101010101
控制轉移指令
一般情況下指令都是順序逐條執行的,但是實際上程序不可能全部順序執行而經常需要改變程序的執行流程。
無條件轉移指令JMP
- 段內直接短轉移:
- JMP SHORT OPR
- 執行操作:(IP) ← (IP) + 8位位移量
- short:表示實現的是段內直接短轉移,即位移量為8位數據,它對IP的修改范圍為 -128~127,也就是說,它向前轉移時可以最多越過128個字節,向后轉移可以最多越過127個字節。
- 段內直接近轉移:(轉向的符號地址)
- JMP NEAR PTR OPR
- 執行操作:(IP) ← (IP) + 16位位移量
- 段內間接轉移:(除立即數以外的尋址方式)
- JMP WORD PTR OPR
- 執行操作: (IP) ← (EA)
- 段間直接遠轉移:(轉向的符號地址)
- JMP FAR PTR OPR
- 執行操作:
- (IP) ← OPR 的段內偏移地址
- (CS) ← OPR 所在段的段地址
- 段間間接轉移:(存儲器尋址方式)
- JMP DWORD PTR OPR
- 執行操作:
- (IP) ← (EA)
- (CS) ← (EA+2)
注意:
- 執行JMP指令的時候,IP改變兩次。
- 第一次是在讀取指令的時候,JMP指令被讀取放到了指令緩沖期中,這個時候IP改變了一次;之后,JMP指令執行的時候進行了跳轉,IP又改變了一次。所以IP總共改變了兩次。
條件轉移指令
注意:只能使用段內直接尋址的8 位位移量。
1)根據單個條件標志的設置情況轉移:
| JZ(JE) OPR | jump if zero,or equal | ZF = 1 | 結果為0(相等)則轉移 |
| JNZ(JNE) OPR | jump if not zero, or not equal | ZF = 0 | 不為0(不相等)轉移 |
| JS OPR | jump if sign | SF = 1 | 為負轉移 |
| JNS OPR | jump if not sign | SF = 0 | 為正轉移 |
| JO OPR | jump if overflow | OF = 1 | 溢出轉移 |
| JNO OPR | jump if not overflow | OF = 0 | 不溢出轉移 |
| JP OPR | jump if parity, or parity even | PF = 1 | 有偶數個1則轉移 |
| JNP OPR | jump if not parity, or parity odd | PF = 0 | 有奇數個1 則轉移 |
| JC OPR | jump if carry | CF = 1 | 有進位轉移 |
| JNC OPR | jump if not cary | CF = 0 | 無進位轉移 |
2)比較兩個無符號數,并根據比較結果轉移的指令(與比較指令CMP 聯用)
| < | JB (JNAE,JC) OPR | jump if below | CF = 1 | 有借位,被減數小于減數則轉移 |
| ≥ | JNB (JAE,JNC) OPR | jump if not below | CF = 0 | 沒有借位, 被減數大于或等于減數則轉移 |
| ≤ | JNA (JBE) OPR | jump if not above, jump if below of equal | CF∨ZF = 1 | 被減數小于或等于減數則轉移 |
| > | JA (JNBE) OPR | jump if above, jump if not below or not equal | CF ∨ ZF = 0 | 被減數大于減數則轉移 |
說明:
- 適用于地址或雙精度數低位字的比較。
無符號數比較示例:cmp ax,bx
cmp指令對有符號數的比較:cmp ah, bh
- 1、如果SF=1,而OF=0, (ah)<(bh)
- OF=0,說明沒有溢出,邏輯上真正結果的正負=實際結果的正負;SF=1表示實際結果為負,所以邏輯上真正的結果為負,所以(ah)<(bh)。
- 2、如果SF=0,而OF=1 ,(ah)<(bh)
- OF=1 ,說明有溢出,邏輯上真正結果的正負≠實際結果的正負;SF=0表示由于溢出導致了實際結果非負,那么邏輯上真正的結果必然為負。這樣,SF=0,OF = 1 ,說明了(ah)<(bh)。
- 3、如果SF=0,而OF=0, (ah)≥(bh)
- OF=0,說明沒有溢出,邏輯上真正結果的正負=實際結果的正負;SF=0表示實際結果非負,所以邏輯上真正的結果必然非負。所以(ah)≥(bh)。
- 4、如果SF=1,而OF=1, (ah)>(bh)
- OF=1 ,說明有溢出,邏輯上真正結果的正負≠實際結果的正負; SF=1 表示由于溢出導致了實際結果為負,那么邏輯上真正的結果必然為正。這樣,SF=1,OF = 1 ,說明了(ah)>(bh)。
3)比較兩個帶符號數,并根據比較結果轉移的指令
| < | JL (JNGE) OPR | jump if less | SF?OF = 1 | 小于,或者不大于或等于則轉移。 |
| ≥ | JNL (JGE) OPR | jump if not less | SF?OF = 0 | 不小于,或者大于或等于則轉移。 |
| ≤ | JNG (JLE ) OPR | jump if not greater | (SF?OF)∨ZF = 1 | 不大于或者小于或等于則轉移。 |
| > | JG (JNLE) OPR | jump if greater | (SF?OF)∨ZF = 0 | 大于或者不小于或等于則轉移。 |
說明:
適用于帶符號數的比較
4)測試 CX 的值為 0 則轉移的指令
| JCXZ OPR | jump if CX register is zero | (CX)=0 | CX寄存器的內容為零則轉移。 |
循環指令
Loop
// 計算0+1+2+3+4+5+6+7+8+9的值 int sum=0; for(int i=0;i<10;i++)sum=sum+i;相當于:
// 計算0+1+2+3+4+5+6+7+8+9的值mov ax,0; ax相當于summov bx,0; bx相當于imov cx,10;標號s代表一個地址 s: add ax,bxinc bxloop s; 判斷 指令的格式是:loop 標號
CPU 執行loop指令的時候,要進行兩步操作:
① (cx)=(cx)-1;
② 判斷cx中的值,不為零則轉至標號處執行程序,如果為零則向下執行,退出循環。
可見,cx中的值影響著loop指令的執行結果。用loop指令來實現循環功能時,cx 中要存放循環次數。
一條循環指令LOOP AGAIN可以用修改循環計數和判斷轉移條件的兩條指令替代DEC CX,JNZ AGAIN。JNZ(或JNE)結果不為零(或不相等)則轉移,測試條件為ZF=0。
我們可以總結出用cx和loop 指令相配合實現循環功能的三個要點:
- 1)在cx中要存放循環次數;
- 2)loop 指令中的標號所標識地址要在前面;
- 3)要循環執行的程序段,要寫在標號和 loop 指令的中間。
- 4)loop指令的執行不影響條件碼標志位。
LOOP示例
MOV CX,0 S: ADD AX,BX LOOP S 以上指令序列執行后ADD AX,BX指令被執行了多少次?
答案是:65536次。
循環 LOOP (loop)
指令的匯編格式:LOOP label
指令的基本功能:
①(CX)←(CX)-1
② 若(CX)≠0,則(IP)←(IP)當前+位移量,否則循環結束。
指令的特殊要求:循環指令都是短轉移格式的指令,也就是說,位移量是用8位帶符號數來表示的,轉向地址在相對于當前IP值的-128 ~ +127字節范圍之內。
解析:LOOP指令是先執行CX自減的功能,之后才進行循環的。只要CX不為0,循環就不會終止。因此在上面中,第一次執行的時候,CX自減為0FFFFH,這時CX就不為0的,循環不會被終止。由此,我們可以算出總共運行了65536次。
可提前結束的循環指令
| 當為0或相等時循環 | LOOPZ / LOOPE 標號 | ZF=1且(CX)≠0 |
| 不為0或不相等循環 | LOOPNZ / LOOPNE 標號 | ZF=0且(CX)≠ 0 |
執行步驟:
- 1) (CX) ← (CX) - 1;
- 2) 檢查是否滿足測試條件,如滿足則實現循環;不滿足則退出循環。
注意:
- CX 中存放循環次數;
- 與比較指令CMP聯合使用可提前退出循環。
串處理指令
| 串傳送 | MOVSB / MOVSW | REP | CLD |
| 存入串 | STOSB / STOSW | REPE / REPZ | STD |
| 從串取 | LODSB / LODSW | REPNE / REPNZ | |
| 串比較 | CMPSB / CMPSW | ||
| 串掃描 | SCASB / SCASW | ||
| 串輸入 | INSB / INSW | ||
| 串輸出 | OUTSB / OUTSW |
說明:
字符串操作指令的實質是對一片連續存儲單元進行處理,這片存儲單元是由隱含指針DS:SI或ES:DI來指定的。
REP
與 REP 配合工作的MOVS / STOS / LODS / INS / OUTS。
REP 重復串操作直到計數寄存器CX的內容為0為止。
執行操作:
- 1) 如 (CX)=0 則退出 REP,否則轉(2)
- 2) (CX) <- (CX) -1
- 3) 執行 MOVS / STOS / LODS / INS / OUTS
- 4) 重復 (1) ~ (3)
MOVS串傳送指令
MOVS 串傳送指令:
MOVS DST, SRC MOVSB ;(字節) MOVSW ;(字) MOVSD ;(雙字,計數器為ECX,386及后繼機型) 例:MOVS ES: BYTE PTR [DI], DS: [SI]
執行操作:
- 1) ((DI)) ← ((SI))
- 2) 字節操作:(SI)←(SI)±1, (DI)←(DI)±1,字操作: (SI)←(SI)±2, (DI)←(DI)±2
- (方向標志 DF=0 時用 + ,DF=1 時用 - 。)
REP MOVS:將數據段中的整串數據傳送到附加段中。源串(數據段)→ 目的串(附加段)
執行 REP MOVS 之前,應先做好:
- 1) 源串首地址(末地址)→ SI
- 2) 目的串首地址(末地址)→ DI
- 3) 串長度 → CX
- 4) 建立方向標志CLD ( STD )
- CLD 使 DF=0,從前往后處理,地址自動增量;
- STD 使 DF=1 ,由后向前處理,地址自動減量)
代碼實例
; -------- 定義數據段 data segmentmess1 db ‘personal_computer’ data ends ; -------- 定義附加段 extra segmentmess2 db 17 dup (?) extra endscode segment assume cs:code,ds:data.es:extra mov ax, datamov ds,axmov ax, extramov es, ax; ------ 總共5條指令。lea si, mess1lea di, mess2mov cx, 17cldrep movsb … code ends反向的指令:
lea si, mess1+16 lea di, mess2+16 mov cx, 17 std rep movsb為了在同一段內處理數據,可以在DS和ES中設置同樣的地址。
data segmentmess1 db ‘personal_computer’mess2 db 17 dup (?) data endscode segmentmov ax, datamov ds, axmov es, axlea si, mess1lea di, mess2mov cx, 17cldrep movsb… code endsSTOS存入指令
STOS DST STOSB (字節) STOSW (字) 執行操作:
字節操作:((DI))←(AL), (DI)←(DI)±1
字操作:((DI))←(AX), (DI)←(DI)±2
例:把 附加段 中mess2開始的 10 個字節緩沖區全部置為 20H
或者:
lea di, mess2 mov ax, 2020H mov cx, 5 cld rep stoswLODS從串取指令
LODS SRC LODSB ;(字節) LODSW ;(字)執行操作:
- 字節操作:(AL)←((SI)), (SI)←(SI)±1
- 字操作:(AX)←((SI)), (SI)←(SI)±2
注意:
- LODS 指令一般不與 REP 聯用;
- 有時緩沖區中的一串字符需要逐次取出來測試,可使用本指令。
INS 串輸入指令:
INS DST, DX INSB ; (字節) INSW ;(字)執行操作:
字節操作:((DI))←((DX)), (DI)←(DI)±1
字操作:((DI))←((DX)), (DI)←(DI)±2功能:把端口號在DX的I/O空間的字節、字或雙字傳送到附加段中的由DI所指向的存儲單元中,并根據DF的值和數據類型修改DI的內容。
OUTS 串輸出指令:
OUTS DX , SRC OUTSB ;(字節) OUTSW ;(字)執行操作:
字節操作:((DX))←((SI)), (SI)←(SI)±1
字操作:((DX))←((SI)), (SI)←(SI)±2
功能:把由SI所指向的存儲單元中的字節、字或雙字傳送到端口號在DX的I/O端口中去,并根據DF的值和數據類型修改SI的內容。
與 REPE / REPZ(REPNE / REPNZ)配合工作的
CMPS 和 SCAS
| REPE / REPZ | 當相等 /為零時重復串操作 |
| REPNE / REPNZ | 當不相等 /不為零時重復串操作 |
執行操作:
- 1) 如 (CX)=0 或 ZF=0 (ZF=1) 則退出串操作,否則轉2)
- 2) (CX)←(CX) -1
- 3) 執行 CMPS / SCAS
- 4) 重復 (1) ~ (3)
CMPS 串比較指令:
CMPS SRC, DST CMPSB ;(字節) CMPSW ;(字)執行操作:
- 1) ((SI)) - ((DI))根據比較結果置條件標志位:相等 ZF=1,不等 ZF=0
2) 字節操作:(SI)←(SI)±1, (DI)←(DI)±1,字操作: (SI)←(SI)±2, (DI)←(DI)±2
匯編語言中,CMP和CMPS都是比較指令,不同主要有:
1、CMP比較指令是執行兩個數的相減操作,包括有符號數。CMPS比較指令是執行兩個字符串的相減操作,所有數據認為是無符號數。
2、CMP比較指令必須有兩個顯式操作數。CMPS比較指令可以有兩個顯式操作數,也可以使用指令CMPSB或CMPSW分別表示字節串比較或字串比較而隱含操作數。
3、使用CMP比較指令比較連續的數據時,必須由程序改變其中一個操作數。使用CMPS比較指令比較連續的字符時,對由SI尋址的源串中數據與由DI尋址的目的串中數據進行比較,執行完一條比較指令,SI,DI將自動調整.
REPE
例:比較兩個字符串,找出它們不相匹配的位置。
lea si, mess1 lea di, mess2 mov cx, 5 cld repe cmpsb
反向比較:
SCAS 串掃描指令
SCAS DST SCASB (字節) SCASW (字)執行操作:
字節操作:(AL) - ((DI)), (DI)←(DI)±1
字操作:(AX) - ((DI)), (DI)←(DI)±2
例:從一個字符串中查找一個指定的字符
mess db ‘COMPUTER’lea di, mess ; -------- (ES:DI)保留串地址mov al, ‘T’ ; -------- 搜索字符mov cx, 8 ; -------- 字符長度cldrepne scasb ; -------- 這里在循環的判斷是否(CX)=0或ZF=1。條件成立即中止執行完后
- (di):相匹配字符的下一個地址
- (cx):剩下還未比較的字符個數
子程序設計
int i=0; int main(){func();return 0; } void func(){i++; }等價于:
ASSUME CS:CODE CODE SEGMENT START:MOV AX,0 ;AX相當于iCALL FUNC ;調用FUNC子程序MOV AH,4CHINT 21H FUNC PROC NEARINC AX ;i++RET FUNC ENDP CODE ENDS END START 子程序:在許多應用程序中,常常需要多次使用某功能的指令序列。這時,為了減少重復編寫程序,節省內存空間,把這一功能的指令序列組成一個相對獨立的程序段。在程序運行時,如果需要使用這個給定的功能,就轉移到這個獨立的程序段,待這個獨立的程序段指令序列執行完后,又返回到原來位置繼續運行程序。我們把這個相對獨立的程序段就叫子程序或過程。
調用程序:編制程序時,按需要轉向子程序,稱為子程序調用,或稱為過程調用。調用子程序的程序稱為調用程序或主程序。主、子程序是相對而言的。但子程序一定是受調用程序或主程序調用的。子程序定義的位置可以放在主程序的前面或后面。
過程定義偽操作
過程名 PROC NEAR or FAR……RET 過程名 ENDP說明:
- 過程名是子程序入口的符號地址;
- RET是子程序返回的命令;
- NEAR屬性:調用程序和子程序在同一代碼段中,可省略。(段內調用)
- FAR屬性:調用程序和子程序不在同一代碼段中。(段間調用)
- 同一個子程序可以被段內調用,也可以段間調用。
子程序的調用與返回指令
1)CALL 子程序調用指令:隱含使用堆棧保存返回地址
指令格式:CAL DST ;其中DST為過程的目標地址(即過程名)。
指令功能:
- 把CALL指令的下一條指令地址(稱為返回點或斷點) 推入堆棧保存,然后轉到目標地址(DST)。
- CALL指令可以在段內、段間調用,尋址方式分為直接和間接兩種。
段內直接近調用:CALL DST
DST給出子程序的入口地址(子程序為near屬性),比如:CALL subp
執行操作:
- (SP) ← (SP) - 2
- ( (SP)+1,(SP) ) ← (IP)
- 上面兩句相當于PUSH IP
- (IP) ← DST
- 實際操作是把從指令中得到的距目標過程相對偏移量加到指令指針IP上(得到子程序的入口地址),實現過程調用。
段內間接近調用:CALL DST
執行操作:
- PUSH IP
- (IP) ← (EA)
- DST給出寄存器或存儲單元的內容(轉向地址)
- 比如: CALL word ptr [bx]
段間直接遠調用:CALL FAR PTR DST
DST給出子程序的入口地址(子程序為far屬性),比如:CALL far ptr subp
執行操作:
- PUSH CS
- PUSH IP
- (IP) ← DST偏移地址
- (CS) ← DST段地址
段間間接遠調用:CALL FAR PTR DST
DST給出存儲單元的內容(轉向地址),比如: CALL dword ptr [bx]
執行操作:
- PUSH CS
- PUSH IP
- (IP) ← (EA)
- (CS) ← (EA+2)
2)RET 返回主程序指令
說明:屬于無條件轉移指令。可以在段內或段間返回。
段內近返回:RET
執行操作:
- POP IP
段內帶立即數近返回:RET EXP
執行操作:
- POP IP
- (SP)←(SP)+EXP
段間遠返回:RET(F)
執行操作:
- POP IP
- POP CS
現場保護和恢復
要保護的寄存器:應該是在子程序中將被使用,返回調用程序后仍然需要使用其原有內容的那些寄存器。即保護調用程序和子程序兩者在使用上發生沖突的那些寄存器。但在編程時,一時很難弄清哪些是有沖突的寄存器,一種較為簡單的方法是把所有的寄存器均加以保護。
一般在子程序中進行寄存器保護較好。即在子程序的開始部分,先進行相關寄存器(主要是在子程序中使用的各寄存器)的保護。然后再進行子程序的處理操作。在執行完子程序后,返回前,先恢復各寄存器內容后,再返回調用程序。
子程序的參數傳送
入口參數:子程序需要從主程序獲取的參數。使子程序可以對不同數據進行相同功能的處理。
出口參數:是子程序返回給主程序的參數。使子程序可以將不同的結果送至主程序
實現的方法是把子程序所需要的入口參數,由調用程序預先放入指定的寄存器中。在進入子程序后,子程序就可直接對這些寄存器內容進行操作了。同樣子程序的運行結果,也可置入寄存器中,把它們作為子程序的出口參數寄存器使用。由于寄存器數目有限,適用于參數較少的情況。
- 1) 通過寄存器傳送參數
- 2) 通過存儲器傳送參數
- 3) 通過地址表傳送參數地址
- 4) 通過堆棧傳送參數或參數地址
參數的傳遞方法并不是固定不變的,即它們是可以綜合使用的。依實現的需要和情況的不同,可以靈活使用其中一種方式,也可以同時使用幾種方式的混合。有的時候還可能并不需要參數傳遞。
; 十六進制到十進制的轉換(通過寄存器傳送參數) ; hexidec:接收鍵盤輸入的十六進制數,在屏幕上輸出相應的十進制數 hexidec segment ; 16?10assume cs: hexidec main proc far start: push dssub ax, axpush axcall hexibin ; 16轉2call crlfcall binidec ; 2轉10call crlfret main endp……………… hexidec ends; 按位取數 binidec proc near mov cx, 10000dcall dec_divmov cx, 1000dcall dec_divmov cx, 100dcall dec_divmov cx, 10dcall dec_divmov cx, 1dcall dec_divretbinidec endpdec_div proc nearmov ax, bxmov dx, 0div cxmov bx, dxmov dl, aladd dl, 30hmov ah, 2int 21hret dec_div endp; hexibin:接收4位十六進制數的輸入 ; binidec:輸出5位的十進制數值 ; 入口參數為BX。 ; 出口參數為BX。 hexibin proc near mov bx, 0mov cx,4newchar:mov ah, 1int 21hcmp al, 30h ; 0~9的16進制表示為30~39jl exitcmp al, 3ah ; 和10比較,如果小于10的就跳轉到add_tojl add_tocmp al, 41h ; 和A比較jl exitcmp al, 47h ; 和G比較jge exitcmp al, 61h ; 和a比較jl exitcmp al, 67h ; 和g比較jge exit add_to:push cxmov cl, 4shl bx, clmov ah, 0add bx, axpop cxloop newchar exit: ret hexibin endp; 回車換行 Crlf proc nearpush axpush dxmov dl, 0dhmov ah,2int 21hmov dl, 0ahmov ah,2int 21hpop dxpop axret Crlf endpend start宏
宏:源程序中一段有獨立功能的程序代碼。在使用之前先定義一次,以后就可以多次調用。
宏指令:用戶自定義的指令。在編程時,將多次使用的功能用一條宏指令來代替。
匯編語言源程序包含:
- 指令:程序運行時執行的語句
- 偽指令(偽操作):匯編時執行
- 宏指令:匯編時展開
高級語言的宏
C語言中以#define作為標志的編譯預處理命令稱為宏定義命令。其不帶參數的格式為:
其中的標識符叫宏名,字符串叫宏體。帶參的宏一般形式為:
如:
#define PI 3.1415926 #define area(r) (3.1415926*(r)*(r))系統對宏的處理是這樣的:當遇到宏名時,就用宏體替換,即所謂的宏替換。這一過程是由預編譯程序完成的(不必用戶自己操作),而后才將宏替換后的程序交編譯程序進行編譯。
宏定義、宏調用和宏展開
; 宏定義: macro_name MACRO [啞元表] ; 形參/虛參…… …… ; 宏定義體 ENDM 宏調用: (必須先定義后調用)macro_name [實元表] ; 實參
宏展開: 宏定義體->復制到宏指令位置,實參代虛參。
宏和子程序的對比
| 子程序 | 模塊化,省內存 | 開銷大 |
| 宏 | 參數傳送簡單,執行效率高 | 占用內存空間大 |
宏定義中的參數
- 1、宏定義可以無變元(無參數);
- 2、變元可以是常數、寄存器、存儲單元名以及用尋址方式能找到的地址或表達式;
- 3、變元可以是操作碼或操作碼的一部分(必須用&作為分隔符);
- 4、變元可以是ASCII串(字符串)。
- 注意:宏調用時若實參個數少于形參個數會出現編譯錯誤,若實參個數多于形參,則按形參的順序填入實參,多余部分被忽略。
宏匯編操作符
| & | 符號1 & 符號2 | 宏展開時,合并前后兩個符號形成一個符號。 &可以作為啞元的前綴。 |
| ;; | 宏展開的時候,;;后面的注釋不予展開。 | |
| % | % 表達式 | 匯編程序將%后面的表達式轉換為當前基數下的數字,并在展開期間用這個數取代啞元。 |
LOCAL偽操作
LOCAL偽操作為每個標號建立唯一的符號(??0000~??FFFF),必須緊跟在MACRO語句之后,中間不允許有任何操作包括注釋。
; 宏定義 absol MACRO operLOCAL nextcmp oper,0jge nextneg oper next: ENDM ; 宏調用 …… absol var …… absol bx …… 說明:
反匯編出來內容如下:
從上面可以看出使用了LOCAL偽操作之后,每次調用absol后,next的地址都是不同的。即 LOCAL偽操作為每個標號建立唯一的符號。
列表偽操作
| .LALL | 在LST清單中列出宏展開后的全部語句(包括注釋)。 |
| .SALL | 在LST清單中不列出任何宏展開后的語句。 |
| .XALL | 缺省的列表方式,只列出宏體中產生目標代碼的語句。 |
宏庫的建立和調用
建立宏庫
>EDIT MACRO . MAC
調用宏庫
>EDIT EXP.ASM
include MACRO.MAC …… macro1 [實元表] …… macro2 [實元表] …… ; 刪除不用的宏定義`purge macroN` macroN [實元表] ……輸入輸出程序設計
不同外設具有的端口數各不相同,計算機中為每一個端口都賦予一個惟一編號——稱為端口地址(或端口號PORT)。 8086CPU采用I/O端口獨立編址的方式,采用16位地址最多能管理64K個端口,即端口占64KB地址空間,端口號為0~65535。必須使用專門的I/O指令訪問端口。
CPU與I/O接口進行通信是通過接口電路內部的一組寄存器實現的,這些寄存器稱為端口,包括:數據端口、狀態端口和命令端口。
累加器專用傳送指令IN/OUT
(只能用AX或AL與端口傳送信息)
輸入指令
- IN (I/O -> CPU)
長格式:(PORT是端口地址(00~FFH))
- IN AL, PORT (字節)
- IN AX, PORT (字)
執行操作:
- (AL) <- ( PORT ) (字節)
- (AX) <- ( PORT+1, PORT )(字)
短格式:
- MOV DX, PORT
- IN AL, DX (字節)
- IN AX, DX (字)
執行操作:(端口號>255時,先送到DX)
- (AL) <- ( (DX) ) (字節)
- (AX) <- ( (DX)+1, (DX) )(字)
輸出指令
- OUT (CPU -> I/O)
長格式:
- OUT PORT, AL (字節)
- OUT PORT, AX (字)
功能:將寄存器中內容輸出到指定端口。
短格式:
- MOV DX , PORT
- OUT DX, AL (字節)
- OUT DX, AX (字)
訪問端口
in al,60h;從60h號端口讀入一個字節。
執行時與總線相關的操作:
- ① CPU通過地址線將地址信息60h發出;
- ② CPU通過控制線發出端口讀命令,選中端口所在的芯片,并通知它,將要從中讀取數據;
- ③ 端口所在的芯片將60h端口中的數據通過數據線送入CPU。
- 注意:在in和out 指令中,只能使用 ax 或al來存放從端口中讀入的數據或要發送到端口中的數據。
I/O 設備的數據傳送方式
- 1、查詢方式(程序控制方式)
- 2 、中斷方式
- 3 、DMA方式(直接存儲器存取方式/成組傳送方式)
查詢方式
CPU和內存通過端口與外部設備進行通信。CPU在執行主程序過程中,當需要進行I/O操作時,很難保證輸入設備已經準備好了數據,或者是輸出設備已經處在可以接收數據的狀態。因此,一般要在外部設備準備就緒并且I/O接口已經做好數據傳送的情況下,才能進行數據傳送,這種傳送方式稱為查詢傳送方式。
查詢過程使CPU很容易與不同速度的外設實現速度配合,使接口電路十分簡單,適用于較少數據傳輸情況下使用。但要占用CPU大量時間去查詢I/O設備的狀態。
中斷傳輸方式
采用中斷方式, CPU執行主程序,等待中斷的發生。I/O設備與CPU并行操作,進行數據傳輸的準備工作。當輸入設備將數據準備好,或者輸出設備空閑時,便通過I/O接口向CPU發申請中斷。CPU在每執行完一條指令之后都會檢查是否有中斷請求,只要滿足中斷響應條件,CPU就暫停執行當前的程序,轉向執行中斷處理程序,進行數據傳送,等傳送完成后,CPU返回到被中斷的主程序,繼續進行原來的工作。
中斷方式:需要保護現場和恢復現場,數據傳輸由CPU完成。
DMA方式——成組數據傳送方式
主要由硬件DMA控制器實現其傳送功能,用于一些高速的I/O設備(比如磁盤),能使I/O設備直接與存儲器進行成批數據的快速傳送。
DMA方式:用DMA控制器來控制存儲器和I/O設備之間的數據傳送時,并不經過CPU,傳輸過程中CPU不占用總線,CPU處于原地等待。這樣,傳輸時就不需要保存斷點等額外操作了。另外,整個控制數據塊傳送的過程,包括地址增量和計數器減量的操作,都是由硬件控制完成的,因而大大縮短了數據傳送的控制時間,提高了整個系統的處理效率。
程序直接控制 I/O 方式
I/O 指令是主機與外設進行通信的最基本途徑。DOS 功能調用和BIOS例行程序中的輸入/輸出功能也是由IN和OUT指令完成的。
例:循環測試某狀態寄存器的2位是否為1
輪流查詢幾種I/O設備
輪流查詢幾種I/O設備: DEV1: IN AL, STAT1TEST AL, STAT1_BITJZ DEV2CALL FAR PTR PROC1 DEV2: IN AL, STAT2TEST AL, STAT2_BITJZ DEV3CALL FAR PTR PROC2 DEV3: IN AL, STAT3TEST AL, STAT3_BITJZ DEV1CALL FAR PTR PROC3- 優:由程序安排或修改 設備的優先次序
- 缺:查詢等待浪費CPU大量有效時間
- 使用I/O指令直接控制輸入輸出比調用DOS功能或BIOS例行程序效率更高,但其對硬件的依賴性很大,所以一般的程序設計還是盡可能使用DOS或BIOS功能調用。
I/O程序舉例: CMOS RAM 芯片
PC機中有一個CMOS RAM芯片,其有如下特征:
- 1)包含一個實時鐘和一個有128個存儲單元的RAM存儲器。
- 2)該芯片靠電池供電。所以,關機后其內部的實時鐘仍可正常工作, RAM 中的信息不丟失。
- 3)128 個字節的 RAM 中,內部實時鐘占用0~0DH單元來保存時間信息,其余大部分單元用于保存系統配置信息,供系統啟動時BIOS程序的讀取。
- BIOS也提供了相關的程序,使我們可以在開機的時候配置CMOS RAM 中的系統信息。
- 4)該芯片內部有兩個端口,端口地址為70h和71h。CPU 通過這兩個端口讀寫CMOS RAM。
- 5)70h為地址端口,存放要訪問的CMOS RAM單元的地址; 71h為數據端口,存放從選定的CMOS RAM 單元中讀取的數據,或要寫入到其中的數據。可見,CPU對CMOS RAM的讀寫分兩步進行。
- 比如:讀CMOS RAM的2號單元:
- 1、將2送入端口70h mov al, 2,out 70h, al
- 2、從71h讀出2號單元的內容in al, 71h
- 比如:讀CMOS RAM的2號單元:
CMOS RAM中存儲的時間信息
在CMOS RAM中,存放著當前時間:
| 秒 | 00H |
| 分 | 02H |
| 時 | 04H |
| 星期 | 06H |
| 日 | 07H |
| 月 | 08H |
| 年 | 09H |
這6個信息的長度都為1個字節,存儲了用兩個 BCD碼表示的兩位十進制數,高 4 位的BCD碼表示十位,低4 位的BCD 碼表示個位。
比如:00010100b表示14。
讀取CMOS RAM的信息
要讀取 CMOS RAM的信息,我們首先要向地址端口70h寫入要訪問的單元的地址:
mov al,8 out 70h,al然后從數據端口71h中取得指定單元中的數據:in al,71h
中斷傳送方式
中斷:使cpu中止正在執行的程序而轉去處理特殊事件的操作。
中斷源:引起中斷的事件。8086/8088CPU最多有256個中斷源,這些中斷源根據來自CPU的內部還是外部分為兩大類:內部中斷源和外部中斷源。
外中斷(硬中斷):
- 外設的 I/O 請求 —— 可屏蔽中斷INTR
- 電源掉電 / 奇偶錯 —— 非屏蔽中斷NMI
所謂不可屏蔽中斷是指該中斷請求不能通過軟件的方式對其屏蔽,一旦出現NMI中斷請求,CPU必須立即響應。
內中斷(軟中斷10H):
- INT 指令 / CPU 錯(除法錯、溢出)
- 為調試程序設置的中斷(t、g命令)
80x86 中斷源
(圖中引線端標示的數字為分配的終端類型號N(0-255))
8259A外部有28個引腳。有9片8259A可構成64級中斷源。
中斷向量表
各類型(0~0FFH)中斷處理程序的入口地址表
中斷的條件
(從外設發出中斷請求到CPU響應中斷,有兩個控制條件起決定性作用):
- 設置CPU中斷允許位:
- FLAGS 中的IF = 1 的時候允許中斷 ( STI 開中斷)IF = 0的時候禁止中斷 ( CLI 關中斷)
- 設置中斷屏蔽位:
- 中斷屏蔽寄存器的中斷屏蔽位 = 0的時候允許I/O設備請求中斷 ,= 1 的時候禁止I/O設備請求中斷。
修改中斷處理程序
DATAS SEGMENTDATAS ENDSSTACKS SEGMENT;此處輸入堆棧段代碼 STACKS ENDSCODES SEGMENTASSUME CS:CODES,DS:DATAS,SS:STACKS START:MOV AX,DATASMOV DS,AXMOV AL,0MOV AH,35HINT 21HPUSH ESPUSH BX ;保存原向量PUSH DSMOV AX,SEG FUNCTIONMOV DS,AXMOV DX,OFFSET FUNCTIONMOV AL,0MOV AH,25HINT 21H ;設置新的向量POP DS ;--------------------主程序部分 …… ;------------------POP DXPOP DSMOV AL,0MOV AH,25HINT 21H ;恢復原向量 MOV AH,4CHINT 21H;--------------中斷處理程序 FUNCTION PROC NEAR…… FUNCTION ENDP CODES ENDSEND STARTCPU中斷過程
- 1)取中斷類型:CPU ← type N
- 2)保護現場:FLAGS、CS、IP入棧
- 3)IF=0 (關中斷), TF=0(禁止單步中斷)
- 4)計算中斷向量地址,取中斷向量:(4×N)→ IP,(4×N+2)→ CS
- 5)轉中斷處理程序
- 以上步驟都由硬件完成。采用向量中斷的方法,大大加快了中斷處理的速度。因為計算機可直接通過中斷向量表轉向相應的處理程序,而不需要CPU去逐個檢測和確定中斷原因。
INT
格式: int n ; n為中斷類型碼。
功能:是引發n號中斷過程。
CPU 執行int n過程如下:
- 1)取中斷類型碼n;
- 2)標志寄存器入棧,IF = 0,TF = 0;
- 3)CS、IP入棧;
- 4)(IP) = (n*4),(CS) = (n*4+2)。
- 從此處轉去執行n號中斷的中斷處理程序。
或者這么理解:
- 系統功能號送到寄存器AH中
- 入口參數送到指定的寄存器中
- 用INT 21H指令執行功能調用
- 根據出口參數分析功能調用執行情況
| 4CH | 返回DOS | 無 | 無 |
| 1 | 鍵盤輸入一個字符到AL中 | 無 | AL=字符 |
| 2 | 輸出DL寄存器的字符到顯示器 | DL(存放一個字符) | 無 |
| 9 | 輸出一個以“$”結尾的字符串到顯示器 | DS:字符串所在的段地址 DX:字符串首地址 | 無 |
| 0AH | 從鍵盤輸入一個字符串到指定緩沖區 | DS:緩沖區所在的段地址 DX:緩沖區首地址 | 緩沖區相應位置 |
- 更多請見: 匯編常用的INT 21H系統調用
IRET
可見,INT 指令的最終功能和call指令相似,都是調用一段程序。一般情況下,系統將一些具有一定功能的子程序,以中斷處理程序的方式提供給應用程序調用。
我們在編程的時候,可以用int指令調用這些子程序,而在子程序中安排iret指令返回。我們將這樣的中斷處理子程序簡稱為中斷例程。
IRET指令的執行過程相當于:
中斷程序的編寫步驟
中斷處理程序的編寫與子程序類似,先保護現場,再完成功能,然后恢復現場,最后用IRET指令返回,返回地址是中斷發生時緊接著的下一條指令。
中斷處理子程序:
保存寄存器內容,如允許中斷嵌套,則開中斷 ( STI )
中斷處理功能
關中斷(CLI)
送中斷結束命令( EOI )給中斷命令寄存器
恢復寄存器內容
IRET中斷返回
主程序:
1、設置中斷向量
2、設置 CPU 的中斷允許位IF
3、設置設備的中斷屏蔽位
注意:程序員在編程的時候可以調用系統設置好的中斷例程,也可以自己編寫中斷處理程序。中斷類型號0、1、3、4是固定的內部中斷,向量2是非屏蔽中斷,向量5~31是保留給系統使用的中斷,向量32~255則是用戶可用的中斷。
示例
編寫一個中斷處理程序,要求在主程序運行期間,每隔 10秒顯示一次字符串‘ bell ’。
.model small .stack .datacnt dw 182 mes db 'bell',0ah,0dh,'$'.codestart:mov ax, @datamov ds, ax mov al, 1ch mov ah, 35hint 21h ;取向量1chpush espush bx ;保存原向量push ds mov dx, offset ringmov ax, seg ringmov ds, axmov al, 1chmov ah, 25hint 21h ;設置新向量pop dsin al, 21h;中斷屏蔽寄存器and al, 11111110bout 21h, al ;增加定時器中斷sti ;開中斷DOS系統功能調用
執行過程
- 系統功能調用的格式:
- ① 傳送入口參數到指定寄存器中(可選項)
- ② 功能號送入AH寄存器
- ③ INT 21H
- INT 21H是一個具有近90個子功能的中斷服務程序,這些子功能的編號稱為功能號。
- INT 21H的功能大致可以分為四個方面,即設備管理、目錄管理、文件管理和其他
BIOS和DOS中斷
BIOS是固化在PC機內存地址0FE000開始的8KBROM中的基本輸入輸出系統的例行程序,它為PC系列的不同微處理器提供了兼容的系統加電自檢、引導裝入、主要I/O設備的處理程序以及接口控制等功能模塊,一般以中斷處理程序的形式存在。BIOS可以處理所有的系統中斷,如鍵盤、顯示器、磁盤、打印、日期與時間等。BIOS是模塊化的結構形式,每個功能模塊的入口地址都在中斷向量表中,對這些中斷調用是通過軟中斷指令INT來實現的。
DOS是IBM PC機的磁盤操作系統,由軟盤或硬盤提供。它的兩個DOS模塊IBMBIO.COM和IBMDOS。COM使BIOS使用起來更方便。其中模塊IBMBIO.COM是一個輸入輸出設備處理程序,提供DOS到BIOS的低級接口,模塊IBMDOS。COM包括一個文件管理程序和一些處理程序,把信息傳送給IBMBIO.COM,形成BIOS調用。因為DOS模塊提供了更多更必要的測試,使DOS操作比使用相應功能的BIOS操作更簡易,而且DOS對硬件的依賴性更少些。
用戶編程原則
- ①盡可能使用DOS的系統功能調用,提高程序可移植性。
- ②在DOS功能不能實現的情況下,考慮用BIOS功能調用。(比如讀打印機狀態:BIOS中斷17H的功能2)
- ③在DOS和BIOS的中斷子程序不能解決問題時, 才使用IN/OUT指令直接控制硬件。(比如聲音控制)
中斷例程調用方法
一般來說,中斷例程中包含多個子程序,內部用AH傳遞子程序的編號來決定執行哪個子程序。
鍵盤I/O
- 大多數有用的程序都需要處理用戶的輸入。
- 鍵盤輸入寄存器的端口地址為60H,控制寄存器的端口地址為61H。
- 鍵盤上的每個鍵都有一個掃描碼(01~83)。
- ◢ 據掃描碼可確定操作的是哪個鍵、是按下鍵還是釋放鍵;
- 掃描碼用一個字節表示。低7位是掃描碼的數字編碼(即在鍵盤上的位置), 最高位D7位表示鍵的操作狀態:
- 當按下鍵時, D7=0 ,取得通碼;
- 當釋放鍵時, D7=1,取得斷碼。
- 鍵盤通過鍵盤接口電路與計算機連接。 當在鍵盤上“按下”或“放開”一個鍵時,如果鍵盤中斷是允許的(21H端口的1位等于0),就會產生一個類型9的中斷,并轉入到BIOS的鍵盤中斷處理程序。
BIOS鍵盤中斷處理程序功能
- ◢ 從鍵盤接口(60H)讀取操作鍵的掃描碼;
- ◢ 將掃描碼轉換成字符碼(大部分鍵的字符碼即相應字符的ASCII碼,沒有相應ASCII碼的鍵字符碼為0。 );
- ◢ 將鍵的掃描碼、字符碼存放在ROM BIOS數據區的鍵盤緩沖區KB_BUFFER( 0040:001A ), 供其它有關鍵盤的中斷子程應用。
BIOS鍵盤中斷(INT 16H)
| 0 | 從鍵盤讀一字符 | AL=字符碼,AH=掃描碼 |
| 1 | 讀鍵盤狀態并檢查是否有字符輸入 | 如按下ZF=0,AL=字符碼,AH=掃描碼,否則ZF=1,緩沖區空 |
| 2 | 取鍵盤狀態字節 | AH=00,AL=鍵盤狀態字節(KB_FLAG) |
比如指令序列:
MOV AH, 0 INT 16H ;等待按鍵輸入然后取得掃描碼和字符碼 MOV BX,AX ;用BX傳遞參數 CALL BINIHEX ;調用子程序將BX轉16進制并顯示使用int 16h中斷例程讀取鍵盤緩沖區
- int 16h 中斷例程的 0 號功能,進行如下的工作:
- 1)檢測鍵盤緩沖區中是否有數據;
- 2)沒有則繼續做第1 步;
- 3)讀取緩沖區第一個字單元中的鍵盤輸入;
- 4)將讀取的掃描碼送入ah,ASCII 碼送入al;
- 5)將己讀取的鍵盤輸入從緩沖區中刪除。
- 可見,B1OS 的int 9 中斷例程和int 16h 中斷例程是一對相互配合的程序,int 9 中斷例程向鍵盤緩沖區中寫入,int 16h 中斷例程從緩沖區中讀出。
DOS鍵盤中斷(INT 21H)
| 1 | 從鍵盤輸入一個字符并回顯在屏幕上 | AL =字符 | |
| 6 | 讀鍵盤字符,不回顯 | DL = 0FFH | 若有字符可取,AL=字符,ZF=0 若無字符可取,AL=0,ZF=1 |
| 7 | 從鍵盤輸入一個字符,不回顯 | AL =字符 | |
| A | 輸入字符到緩沖區 | DS:DX = 緩沖區首址 | (DX+1)=實際輸入字符數 |
| B | 檢驗鍵盤狀態 | AL=0表示有輸入 ,AL=FF表示無輸入 |
0AH功能的注意事項
- ◢ 輸入的字符均帶回顯,且光標隨字符移動。當輸入回車符結束時,也回顯回車符。表現為功能調用結束后,光標回到了行首。
- ◢ 回車符0DH作為一個輸入的字符存放在字符串尾,但計算輸入個數時,不包括回車鍵。實際最多能輸入的字符數 = 限制的最多數-1 (回車符占一個)
- ◢ 執行完0AH功能后,DS和DX的值不變, DS:DX仍指向緩沖區的首地址。
- ◢ 整個緩沖區的大小應為:限制的最多數 +2,max DB 11, ? , 11 dup (?),緩沖區必須定義為字節類型,不能定義為字類型。
顯示器I/O
- 1、顯示器通過顯卡(顯示適配器)連接到計算機上。
- 2、單色顯示適配器只能顯示黑白兩色。只能顯示ASCII碼字符和一些簡單字符圖形。
- 3、彩色顯示適配器能以紅、綠、藍彩色顯示以點繪制的圖形以及ASCII字符。
- 4、顯示器有兩種顯示方式:
- 文本方式:
- 指以字符為單位顯示的方式,字符通常是指字母、數字、普通符號和一些特殊符號(如矩形塊等)。
- 圖形方式:
- 指以點為單位顯示的方式。一個點就是一個像素。
- 文本方式:
- 5、屏幕上各象素的顯示信息,存放在顯示緩沖區(顯存)中。
文本方式
將屏幕劃分為 m列和n行 (m × n),在每個網格位置上顯示像素,一個字符是一個像素。在這種顯示方式下,顯示緩沖存儲區中存放的是字符的ASCII碼和對應的顯示屬性,每個字符占用兩個字節的空間。
圖形方式:
將屏幕劃分為 m×n的點陣,在每個點的位置顯示像素,一個點是一個像素。顯示緩沖存儲區中存放的是“像素”點的信息,它的值為“0”或者“1”,為“0”就不在屏幕上打點,為“1”則在屏幕上打點。
文本方式屬性字節的含義
DOS顯示功能調用中斷(INT 21H)
| 2 | 顯示一個字符(檢驗Ctrl-Break) | DL = 字符 光標跟隨字符移動 |
| 6 | 顯示一個字符(不檢驗Ctrl-Break) | DL = 字符 光標跟隨字符移動 |
| 9 | 顯示字符串 | DS:DX=串地址 串必須以$結束,光標跟隨串移動 |
顯示存儲器
MDA顯存的起始地址為B000:0000,CGA、EGA、VGA的是B800:0000。 1屏幕的字符數據稱為1頁數據。據顯存大小,可存儲若干頁的字符象素。
例: 16KB 顯存能存儲:(1000B=1KB)
25×80方式,4頁( 0 ~ 3 ), 80×25×2×4 =16000
25×40方式,8頁( 0 ~ 7 ), 40×25×2×8 =16000
對CGA、EGA、VGA的80列顯示方式,0頁顯存中的起始地址是B800:0000, 1頁B800:1000, 2頁B800:2000,3頁B800:3000。屏幕上某一字符位置在顯存中的偏移地址計算公式:
Char_offset=Page_offset+((row*width)+column)*byte
字符偏移地址=頁偏移地址+((行號 * 行寬)+列號)* 2
BIOS顯示中斷(INT 10H)
功能號 AH=0,1, 2, 3, 5, 6, 7, 8, 9, 0AH, 0EH 13H
設置顯示方式
- 1.入口參數 AL = 顯示方式值
| 00 | 40×25 | 黑白文本方式 |
| 01 | 40×25 | 彩色文本方式 |
| 02 | 80×25 | 黑白文本方式 |
| 03 | 80×25 | 彩色文本方式 |
| 04 | 320×320 | 彩色圖形方式 |
- 2.功能號 AH = 00H
- 3.類型號 10H
- 4.出口參數 無
5.實現功能 將顯示方式設置為指定的形式
例: 將顯示方式設置為 25×80彩色文本方式
控制光標
隱藏光標
;隱藏光標 mov ch,20h mov cl,00h mov ah,1 int 10h光標定位
Int 10h的功能02:。
DH和DL寄存器中為光標位置的行列號,BH中為頁號(單色顯示器頁號為0 )。
例:
讀光標位置
功能03 :BH中指定頁號。把光標位置的行號回送給DH,列號回送給DL,光標大小的參數填入CH和CL 。
mov ah,3 mov bh,0 int 10h ;返回參數dh:dl=行:列清屏和卷屏
功能06(07):使屏幕內容上卷(或下卷)指定的行。需要7個參數。
例:清除屏幕
顯示字符及字符串
功能9(0a): 把一個字符送到顯示屏幕,可直接在cx中設定顯示次數,調用結束后光標返回它的初始位置。(0a以正常屬性顯示)
功能8:讀取當前光標位置的字符和屬性,入口參數bh=顯示頁號,返回ah/al=字符/屬性。
例: 在品紅背景下,顯示5個淺綠色閃爍的星號。(閃爍很快幾乎覺察不到)
顯示字符串
- 功能13H:顯示字符串。
- 參數:
- ES:BP=字符串地址
- AL=寫方式(0~3)
- CX=字符串長度
- DH/DL=起始行/列
- BH/BL=頁號/屬性
- 子功能號
- AL=0,1,要指定整個顯示字符串的屬性。
- AL=2,3,必須指定每個字符的屬性。
顯示系統時間
DATAS SEGMENTCLOCK DB 0,0,':',0,0,':',0,0,'$' DATAS ENDS CODES SEGMENTASSUME CS:CODES,DS:DATAS START:MOV AX,DATASMOV DS,AX RESTART:LEA BX,CLOCKMOV AL,4CALL GETTIMEMOV AL,2CALL GETTIMEMOV AL,0CALL GETTIME;------ 設置光標位置MOV DH,10 ;行MOV DL,30 ;列MOV BH,0MOV AH,2INT 10H;------ 隱藏光標MOV CH,20HMOV CL,00HMOV AH,1INT 10H;------ 輸出時間LEA DX,CLOCKMOV AH,9INT 21H ; ------ 檢測鍵盤輸入。IN AL,60HCMP AL,1JNZ RESTART ;按下ESC退出,不斷更新時間MOV AH,4CHINT 21H ; ------ 獲取時間 入口參數:AL GETTIME PROC; ------ 讀取數據OUT 70H,AL ; 設定讀數地址IN AL,71H ; 取數; ------ 左移四位得到十位數值MOV AH,0 ;清零MOV CL,4SHL AX,CLSHR AL,CL ; 得到個位數值 ADD AH,30H ; 轉換成ASCIIADD AL,30H ; 轉換成ASCIIMOV CLOCK[BX],AHMOV CLOCK[BX+1],ALADD BX,3 ; 跳過三個字符->':'RET GETTIME ENDP CODES ENDSEND START總結
以上是生活随笔為你收集整理的汇编语言8086笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java集合基础练习题
- 下一篇: Panasonic: FP-X0 L30