微機原理與系統設計筆記4 | 組合語言程式設計與其他指令

climerecho發表於2023-02-16

  • 打算整理組合語言與介面微機這方面的學習記錄。本部分介紹組合語言程式設計以及一些跟程式設計密切相關的指令類。

  • 參考資料

    • 西電《微機原理與系統設計》周佳社
    • 西交《微機原理與介面技術》
    • 課本《組合語言與介面技術》王讓定
    • 小甲魚《組合語言》

1. 彙編程式結構/框架

  • 段定義偽指令

    程式不同的資訊要定義在不同的段中,該指令就

    SEGNAME SEGMENT [定位型別][組合型別]['類別']
    	;段實體
    SEGNAME ENDS
    
    • 這裡要注意首尾一致,也就是SEGNAME要一樣。
    • 有SEGMENT就要有ENDS,像C語言大括號一樣。
    • 定義了一個段名SEGNAME,就具備了段地址屬性。

    定位型別是告訴彙編器這個邏輯段起始地址的要求,取值有:

    1

    常用是PARA 節型。

    組合型別是多模組程式設計中告訴連結器器,不同模組中同段名同組合型別的邏輯段如何連結。

    課程只要求單模組程式設計,所以組合型別不瞭解也罷。

    老師這裡提了STACK型(說明本段是堆疊段),例子:

    STACK SEGMENT STACK
          DB 256 DUP(?)
    STACK ENDS	
    

    注意這裡第一行的第二個STACK宣告為組合型別後,就不需要在程式碼段設定SS和SP了,如果不寫這個組合型別宣告,程式碼段還是需要宣告。

    另一個組合型別是NONE型別,預設型。其他的暫且不提。

    類別,如果要寫,需要加上單引號說明,只告訴程式設計師這個段是幹什麼的,類似於段前註釋,沒有其他用處。

2. 8086彙編程式完整結構

;定義堆疊
STACK SEGMENT STACK
      DB 256 DUP(?)
      ;定義堆疊段,找16可以整除的,SP = 棧底+1
TOP LABEL WORD 
;TOP為變數名;TOP 不佔用記憶體位置,獲取SP的地址;LABEL偽指令只有儲存單元地址、型別屬性但是不佔記憶體位置
STACK ENDS

;定義資料段(可能有多個)
DATA SEGMENT 
;實體

DATA ENDS
X_BYTE LABLE BYTE;這樣就可以直接使用34H這個位元組型變數X_BYTE,而不用對X_WORD進行型別轉換。
X_WORD DW 1234H
;定義程式碼段
CODE SEGMENT 
ASSUME CS:CODE,DS:DATA,ES:DATA;段定址偽指令,告訴彙編程式段名和段的關係
ASSUME SS:STACK
;這種關係是承諾關係,並沒有向這些暫存器賦值,還需要再賦值

START:MOV AX,DATA
      MOV DS,AX;資料初始化,不能直接DATA給DS
	  MOV ES,AX
  /*  MOV AX,STACK
	  MOV SS,AX 
	  MOV SP,OFFSET TOP*/
	  ;相當於STACK組合型別的作用
/*    MOV AH,4CH
      INT 21H */   
      ;DOS系統的4CH對應的中斷子程式,此處是結束使用者程式返回作業系統
CODE ENDS;程式碼段結束
END START(CS:IP);整個程式結束
;初始化CS,ip,放置這段程式碼。其實是交給OS來做了,使用者無權。

3. 其他偽指令

上面的完整程式結構中已經提了很多偽指令,簡單提一提其他偽指令。

  • EQU 和 = 偽指令

    VAR_NAME EQU/= Exp;給表示式賦一個名稱
    ;用EQU定義時,這個VAR_NAME只能EQU定義一次,用=定義則可以再次定義
    
  • ORG偽指令

    ORG 表示式
    

    為後續指令指定段內偏移地址,可以人為使字型資料對齊。

    舉個例子:作業系統中的boot.asm中對於 org07c00h是這樣註釋的:“告訴編譯器程式載入到7c00h處”:

    org 07c00h
    ;告訴編譯器程式載入到7c00h處
    mov ax,cs
    mov ds,ax
    mov es,ax
    
    call screen
    jmp $
    
    screen:mov ax,bootmsg
    mov bp,ax
    mov cx,16
    mov ax,01301h
    mov bx,000ch
    mov dl,0
    int 10h
    ret
    
    bootmsg:db'Hello OS world!' 
    times 510-($-$$) db 0 
    dw 0xaa55
    

想直接聽後面的了,不想聽前面的指令系統了,折磨。

4. 子程式設計及其呼叫返回指令

這部分是3.11和4.5合在一起講,前者是子程式呼叫返回指令,後者是子程式程式設計。

子程式/過程就是組合語言裡的函式,將一些功能性、重複性的程式碼放到子程式中呼叫,可以讓自己的程式碼更加清晰。我們需要在主幹程式碼呼叫子程式,在子程式結束時返回

4.1 子程式指令

  • 子程式定義:

    以過程形式定義:

    son_name1 PROC [型別] 
    	;主體
    	RET;子程式返回,段間子程式可以用RETF
    son_name1 ENDP
    

    上面程式碼中的[型別]:

    • 段內子程式:NEAR,可以預設
    • 段間子程式:FAR,不能預設

    以標號形式定義(比較方便):

    LABEL:
    	;主體
    	RET
    
  • 子程式呼叫與返回

    • 段內子程式呼叫與返回

      CODE SEGMENT
           ASSUME CS:CODE
      START:
      
      	 ……
      	CALL SUB1;段內直接定址
      	; call near sub1的near可以預設
      	; ……CS不發生改變
      	
      	MOV AH,4CH
          INT 21H	
      	
      SUB1 PROC
      	RET
      SUB1 ENDP
      CODE ENDS 
      END START
      
      • 執行call時,IP放入堆疊(SP-2)字單元;
      • IP+DISP(到跳轉標號的相對位移)得到新的IP;
      • RET 執行後,棧頂彈出放回IP,SP+2;
    • 段間子程式呼叫與返回

      CODE1 SEGMENT
           ASSUME CS:CODE1
      START:
      
      	 ……
      	CALL FAR PTR SUB1;段內直接定址
      	 ;……CS:IP(CS和IP都要儲存)
      	
      	MOV AH,4CH
          INT 21H	
      CODE1 ENDS
      
      CODE2 SEGMENT
      	ASSUME CS:CODE2
      SUB1 PROC FAR
      	……
      	RETF;也可以直接寫為RET
      SUB1 ENDP; 子程式/過程結束指令
      CODE2 ENDS
      
      • 執行call時,cs先入棧(sp-2),ip再入棧(sp-2),
      • RET 時,完成CS IP出棧。
      • 這裡注意一個事情,在子程式內使用了堆疊push pop後,需要在最後復原至呼叫初,才能將子程式正確返回。

4.2 資訊的保護和恢復

也就是主程式和子程式會複用一些暫存器和儲存器單元,呼叫子程式時需要儲存主程式的這些東西,這個過程在主程式或子程式中都可以進行,通常在子程式中進行。

4.3 主程式和子程式的引數傳遞

暫存器、儲存器、堆疊都能傳遞引數。

至於怎麼傳遞,其實就是一個程式設計者自己規定的過程,比如主程式將引數放到AX,子程式從AX來拿。在堆疊引數傳遞引數方式時,需要用到BP指標。

4.4 子程式說明檔案

作為學生,這部分當然不寫,瞭解一下吧。

  1. 子程式名
  2. 子程式功能
  3. 入口引數與傳遞方式
  4. 出口引數與傳遞方式
  5. 子程式用到的暫存器

4.5 巢狀、遞迴、可再用

  • 巢狀子程式設計時注意堆疊溢位
  • 設計自上而下,除錯自下而上,保證呼叫的程式無問題
  • 遞迴子程式:自己呼叫自己,需要考慮出口條件
  • 可再用是一種提醒,感覺子程式可再用有點複雜,要在一次呼叫還未結束時又呼叫該子程式,要注意正確返回。

這一節偏實踐,紙上論道不可取。用到再說吧,個人彙編程式設計經驗感覺子程式這部分不難,這是要保護資訊恢復資訊。可再入不大明白(那就儘量不設計)。

這裡有幾個子程式設計例程,我感覺還不錯。例3.32、例3.33、例3.34。

5. 中斷呼叫與返回

3.12,結合了一部分第8章中斷系統的知識。

說實話,"中斷" 這個詞透過作業系統OS這個課已經比較瞭解了,這裡從微機角度溫習一下。

但感覺這部分講的有點亂。

5.1 中斷

  • 中斷:是CPU執行程式時,由於某個事件的發生,暫停當前正在執行的程式,去執行處理這個事件的中斷服務程式,執行完畢後再返回原程式繼續執行。

  • 中斷分為外部可遮蔽中斷、外部不可遮蔽、內部中斷(軟體中斷)。具體如下圖:

    2

  • 為了識別中斷形式並採取對應服務,CPU都編排了中斷型別號,這個具體CPU要查手冊。

    8086中:

    • 除法錯:n=0

    • 單步中斷:n=1

    • 斷點中斷:n=3

      int 3指令可以臨時替換原有的指令,叫做設定斷點,程式執行到int 3時,會進入型別3的中斷服務程式,可以在中斷服務程式中讀原執行環節,來觀察哪裡有問題,之後再返回原程式,繼續執行。

    • int n

      更泛指,比如DOS系統功能呼叫INT 21H

  • 後續會學上圖中的中斷處理硬體,8259A。

5.2 中斷向量

中斷向量其實是中斷服務程式的指標,存放中斷服務程式的入口地址。

3

系統將中斷向量組織成中斷向量表(換算表),放置在儲存器最低地址0000H開始的1024個單元。每個中斷向量佔4B:

  • 入口地址的偏移地址在低2位元組,段基址在高2位元組。所以呼叫時也是對應交給IP和CS。

  • 換算關係是:中斷型別號*4就是該中斷向量在中斷向量表中的開始地址

  • 中斷向量表在OS初始化時就設定了。在自己寫作業系統時,可以設定這個表。

5.3 中斷響應過程

A. 內部中斷

  • 中斷呼叫指令: INT n

  • CPU執行以下操作:

    1. 保護現場:PSW IP CS 入棧

    2. 清除 IF TF 標誌(TF清0說明中斷期間CPU不會再中斷)

    3. 從中斷向量表中取出中斷向量給CS IP賦值。

      如果是外部硬體中斷,不知道這個查表要用的n,CPU需要從外部硬體中獲取中斷型別號。

    4. 轉到中斷服務程式執行。

  • 中斷服務最後一句,中斷返回:IRET,將IP CS PSW出棧。返回原程式。

B. 外部中斷

再來說說外部中斷的中斷號獲取:

4

  • 簡單先說一下上圖的情況,是在說8086的中斷引腳情況

  • NMI不可遮蔽中斷就不提了。

  • INTR引腳:

    • 受IF中斷標誌位控制,當IF=1時,CPU執行完當前指令後,就會響應硬體中斷;
    • 下面就是硬體中斷的響應過程
      1. 保護現場:PSW IP CS 入棧
      2. 清除 IF TF 標誌(此時IF=0)
      3. CPU從外部中斷控制器8259中獲取中斷號;
        • 首先需要CPU告訴8259,它現在響應了中斷;即INTA引腳發出負脈衝給8259.
        • 其次CPU要問要響應哪個硬體中斷,即再次發出INTA負脈衝給8259(第二次)
        • 8259收到後將內部暫存器儲存的N值送到資料線。N不固定。
      4. 根據拿到的N,從中斷向量表中取出中斷向量給CS IP賦值。
    • IRET中斷返回。
  • 只有一個8259則只能控制8個硬體,可以採用8259級聯的方式來增加控制硬體數。

    這裡可以算控制硬體個數,但是不難。

5.4 中斷程式結構/框架

中斷很像子程式,但是有所不同,因為中斷隨時可能發生,自己寫的主程式無法預料何時發生中斷,所以中斷程式內部開始時應當有保護現場操作,返回時有恢復現場操作

INT_NAME:
	PUSH AX ;保護現場操作
	;......
	PUSH SI
	
	; 主體
	; ...
	POP SI ;恢復現場操作
	POP AX
	IRET ;中斷返回

5.5 系統呼叫

系統呼叫分為兩類

  • BIOS功能呼叫,這部分主要是操作硬體。
  • DOS功能呼叫,講道理這是作業系統提供的服務,偏向軟體。自己寫的OS可以自行設計功能呼叫。8086彙編主要面向DOS作業系統(這點是過時的地方...)。

A. DOS功能呼叫

DOS系統功能呼叫的方法一般可分為以下幾步:

  1. 設定所要呼叫功能的入口引數。
  2. 在AH暫存器中存入所要呼叫功能的功能號。
  3. INT 21H指令自動轉入中斷子程式入口。
  4. 相應中斷子程式執行完畢,可按規定取得出口引數。

常用的DOS功能呼叫有 INT 21H,AH裡放引數決定具體服務(如果需要引數),比如放入01H就是等待使用者從鍵盤輸入一個字元,回顯,按鍵的ASIIC碼放到AL中。

補充一個小知識:

  • ASCII數字如何得到數字:

    AND AL,0FH;消除高位即可
    

這裡舉了一個鍵盤按鍵並回顯的例程,跳轉表法,挺巧妙的,老師講了兩個跳轉表法的實現方式,第一個是在課本4.25,第二個就是將跳轉表定義到了程式碼段。

其他的不再寫了,主要就是設定引數,設定呼叫號再輔以子程式實現相應的功能:

  • 02H向螢幕輸出一個字元
  • 06H 控制檯輸入、輸出
  • 09H 向螢幕輸出一串字元(字串資料變數定義時必須要有結束符 '$'

DOS與BIOS 系統呼叫一覽:https://blog.csdn.net/icurious/article/details/51628343

0AH 從鍵盤輸入一串字元(有等待功能,鍵盤輸入回車時結束輸入並顯示),此時必須有鍵盤緩衝區(儲存器中):

DATA  SEGMENT
……
    KEYbuf1 DB 20H;允許使用者輸入的字元個數
	        DB ?;用來統計實際輸入的字元數
	KEYbuf2 DB 20H DUP(?);存放實際輸入的字串
    ;鍵盤輸入快取區
DATA ENDS
CODE SEGMENT 
     ASSUME CS:CODE,DS:DATA
START:
     MOV AX,DATA
	 MOV DS,AX
	 ……
	 ;從鍵盤輸入一串字元,有回顯
	 MOV AH,0AH
	 MOV DX,OFFSET KEYbuf1
	 INT 21H
......

課後題4.39題回頭看看。

顯示系統時間:(這裡是DOS系統呼叫獲取,當然也可以用cmos獲取--王爽彙編實驗14,不過還沒有講到與外設互動)

;返回值:CX = year (1980-2099) DH = month DL = day AL = day of week (00h=Sunday)
data segment
data ends
 
code segment
        assume ds:data, cs:code
 
main:
        mov ax, data
        mov ds, ax
 
        mov ah, 2ah
        int 21h
 
        mov ah, 4ch
        int 21h
code ends
end main

B. BIOS 功能呼叫

  • 00H 設定顯示器,清屏功能

    MOV AH,00H
    MOV AL,3;彩色文字,且大小是80X25
    MOV BX,0;頁數,與顯示卡資料快取區有關
    INT 10H
    
  • 02H 設定游標位置

    MOV AH,02H
    MOV DH,行
    MOV DL,列
    MOV BL,0
    INT 10H
    

6. 字串操作指令

對傳送指令再升級,對一組資料進行整體操作。

  1. 源字串和目的字串均為隱含定址

    • 如果源串在儲存器中,則儲存器的的地址在DS:SI;

      若源串在REG中,位元組:AL,字:AX

    • 目的字串,必須在ES:DI(必須對應)

      在REG同上

  2. 如果源串目的串在儲存器中,則CPU執行時,SI、DI的地址會自動改變,這個改變受DF的控制:

    • DF=0時, +;

    • DF=1時, -;

      這裡在程式設計時需要向其賦值確保其值。如確保DF==0: CLD

    • ± 的程度受運算元型別大小的控制(放在頭部或尾部):若為位元組操作自動+1/-1,若為字操作自動+2/-2

  3. 在串操作指令的左邊可以增加重複字首,重複次數隱含放在CX中

    • REP字首:等效於LOOP,放在MOOSB LODSW STOSB前
    • REPZ/REPE:等效於LOOPZ,放在CMPSB,SCASB之前,(相等重複)
    • REPNZ/REPNE:等效於LOOPNZ,放在CMPSW SCASB之前,(不相等重複)
  • 串傳送指令

    MOVSB
    MOVSW
    
  • 串比較指令

    CMPSB
    CMPSW
    

    源字串減去目的字串(對應位置依次比較),設定狀態暫存器

  • 串掃描指令

    SCASB
    SCASW
    
  • 串裝入指令

    LOOSB
    LOOSW
    
  • 串存貯指令

    STOSB
    STOSW
    

6.1 字串傳送指令

三種書寫格式:MOVSB、MOVSW,MOVS DST,SRC

  • 以MOVSB儲存器操作為例:

    1. ES:DI<-- DS:SI

      源串段地址可以更改(如使用第三種書寫格式),而目的串暫存器不可更改,見書P74頁ES段到ES段的傳送。

    2. SI和DI會自動+/-

    3. 若加上REP重複字首,則執行第三步,CX-1->CX,若CX≠0,則重複執行MOVSB

      例題見P75 例3.36

      DATA SEGMENT 
           BUFFER1 DB 100 DUP(?)
           BUFFER2 DB 100 DUP(?)
      DATA ENS
      CODE SEGMENT
           ASSUME CS:CODE,DS:DATA,ES:DATA
      START:
           MOV AX,DATA
           MOV DS,AX
           MOV ES,AX
           LEA SI,BUFFER1
           MOV DI,OFFSET BUFFER2
      	 CMP SI,DI
      	 //JB DZJXFX
      	 MOV CX,100
         /*CLD ;DF=0
      	 REP MOVSB	 字串指令*/
      	 ;下面是資料指令完成的
      N1:	 MOV AL.[SI]
      	 MOV ES:[DI],AL
      	 INC SI
      	 INC DI
      	 LOOP N1
      	 MOV AH,4CH
      	 INT 21H
      CODE ENDS
      END STRAT
      

      源串和目的串重疊,視情況正向搬運或者逆向搬運。(有兩種情況:

      • 若源串起始地址比目的串起始地址高:SI DI增大方向搬
      • 低:SI DI 減小方向搬

6.2 字串比較指令

CMPSB CMPSW CMPS DST,SRC

以CMPSW為例:(源減去目的,這與資料比較指令不同)

  1. SRC-DST,也即是(DS:SI)-(ES:DI),設定6個狀態暫存器,若ZF==1,說明相等。

  2. SI DI 自動+/- 2

  3. 若有重複字首REPZ,(CX-1)-->CX,若CX≠0且ZF=1,重複執行CMPSW

    若字首REPNZ,則修改上面條件改為ZF=0即可:若CX≠0且ZF=0

例程在書P76 例3.37

MOV SI,OFFSET BUFFER1
MOV DI,OFFSET BUFFER2
MOV CX,100
CLD
REPNZ CMPSW
JZ Find
MOV ADDR,0FFFFH
JMP EXIT

Find:SUB SI,2
MOV ADDR,SI
……
MOV AH,4CH
INT 21H

6.3 字串掃描指令

SCASB、SCASW、SCAS DST,適用於在字串中找某個字元或篩出某個字元。

以SCASB為例:

  1. 源減去目的:AL-(ES:DI),結果設定6個標誌,主要是ZF,ZF=1,則找到
  2. DI+/-1,沒用到SI
  3. 若有重複字首REPNZ,(CX-1)-->CX,若CX≠0且ZF=0,重複執行SCASB;退出迴圈體可以測試ZF==1來判斷找到。

6.4 字串裝入指令

LODSB LODSW,意為將源字串(儲存器中)讀出到AL/AX中。

以LODSB為例:

  1. AL<--(DS:SI)
  2. (SI+/-1)-->SI

這裡老師講了兩個計算機通訊的例子,8250,emm,略去。

6.5 字串存貯指令

STOSB STOSW STOS

以STOSB為例:

  1. AL-->(ES:DI)
  2. DI±1-->DI
  3. 可以帶REP重複字首

例程P79 例3.39

DATA SEGMENT 
    BUFFER1 DW 
    BUFFER2 DB	
DATA ENDS

CODE SEGMENT
    ASSUME CS:CODE,DS:DATA,ES:DATA
STRAT:
    LEA DI,BUFFER1
	MOV AX,0000H
	MOV CX,200
	CLD
    REP STOSW
    /*也可以改為
    L1:MOV ES:[DI],AX
    	INC DI
    	INC DI;字,所以減了兩次
    	LOOP L1
    */
	LEA DI,BUFFER2
	MOV AL,55H
	MOV CX,256
	CLD
	REP STOSB

例子2,P112例4.7

7.輸入輸出指令、其他指令

輸入輸出指令針對I/O操作。

7.1 輸入指令

格式:IN DST, SRC,這裡SRC是埠地址(8086的前16根地址線定址),

  • 若埠地址在0~255之間,SRC可以採用直接定址:

    IN AL,埠地址
    
  • 若埠地址>255,需採用間接定址(都可以採用間接定址)。

    MOV DX,埠地址
    IN AL,DX;觸發IO操作的地址暫存器
    ;符合語法,雖然DX沒加括號,但是合法
    ;這裡的意思比較抽象,就是地址放在DX中,in進來的內容放在AL中
    

上述所說是位元組操作,若為字操作,則AL改為AX。

7.2 輸出指令

格式:OUT DST, SRC,運算元交換一下,其他要求與輸入完全一致。

5

  • 輸入輸出指令要使用外部匯流排,一個匯流排週期包括4個基本的時鐘週期,而輸入輸出指令包括了5個時鐘週期,也就是輸入輸出指令工作時間要多一個週期T1、T2、T3、TW(多出的等待週期)、T4

這裡老師舉了一個外設操作例子(P61最後~P62),講得很好,很整體,甚至包括了IO地址譯碼電路(控制訊號也要參與譯碼d),但學到外設再說。

7.3 其他指令

這部分直接看書更快。

  • 標誌位處理指令,詳見筆記3的標誌位處理指令(7個)

  • 處理器控制指令 5個

    • 空操作指令 NOP,什麼也不做,佔用機器3個時鐘週期,用於延時、等待。

      看一個延時子程式(雙層迴圈):

          MOV BX,3;一個值,外層迴圈的次數
      L2: MOV CX,0;65536
      L1: NOP
          LOOP
      	DEC BX
      	JNZ L2
      
    • 暫停指令 HLT,使CPU進入暫停狀態,不再向下執行。退出條件:

      • RESET訊號有效直接復位,或者NMI不可遮蔽中斷。此時退出但也不會執行後續程式。

      • INTR可遮蔽中斷髮生,CPU保護當前程式現場(IP指標在hlt的下一句),保護了當前程式,返回後則繼續執行後續程式。

        這個可以用於CPU跟外設的同步(用的不多)

    • WAIT等待指令,每隔5個時鐘週期對TEST引腳狀態進行測試,當TEST線低電平則退出等待,執行下一條指令。

7.4 宏指令

宏定義指令定義出來的自定義指令。宏指令必須在程式最開頭定義。

宏指令名 MACRO <形式引數>;引數多於1項,用逗號分隔
; ... ;指令或偽指令構成的宏體
ENDM ;宏定義指令結束,注意這裡結束不需要寫宏名

例子:

;定義一條宏指令,將<某通用暫存器REG><左/右移><若干次>
SHIFT MACRO REG,DIR,N
      MOV CL,N
	  DIR REG,CL;或者:S&DIR REG,Cl
	  ; 因為 SHL,SHR,SAL,SAR 都以S開頭
      ENDM
      
;某程式體中,將AX邏輯左移3次:宏呼叫如下:
SHIFT AX,HL,3

當彙編器彙編到這個宏呼叫時,會將前面的宏定義的形參替換掉,並插入此處(宏展開)。

宏呼叫和子程式都可以來放一個經常呼叫的功能模組,但區別在於:

  • 宏呼叫每次呼叫就都將語句插入(宏展開),浪費空間,但節省呼叫時間(沒有CALL和RET)。
  • 子程式彙編後空間固定,所以節省空間,但浪費時間。
;定義一條宏指令,完成游標回車換行
CRLF MACRO
     MOV AH,2
	 MOV DL,0DH
	 INT 21H
	 MOV AH,2
	 MOV DL,0AH
	 INT 21H
ENDM

一個值得研討的點:當宏指令中有迴圈或分支,也就是有標號,主程式多次宏呼叫時,會提示標號重複:

  • 此時應當將標號宣告為 區域性標號 LOCAL NEXT,彙編時自動分配不同標號。

    ;資料塊搬家
    DATAMOV MACRO DATA1,DATA2,N
            LOCAL NEXT;宣告NEXT是區域性標號
            LEA SI,DATA1
    		LEA DI,DATA2
    		MOV CX,N
    NEXT:	MOV AL,[SI]
    		MOV [DI],AL
    		INC SI
    		INC DI
    		LOOP NEXT
    		ENDM
    	; ……
    	; 主程式中的宏呼叫
    	DATAMOV buf1,buf2,100
    	;彙編展開;
    	; LOOP NEXT變為LOOP ??0000
    	; ......
    	DATAMOV buf3,buf4,200
    	;彙編展開
    	;LOOP NEXT變為LOOP ??0001
    

除了上面介紹的,還有重複宏,格式是:

; 重複宏
REPT <重複次數>:
	;重複程式體
ENDM ; 也是以此結尾

; 不定重複宏
IRP <形參>, < <引數1>,<引數2>,...>
	;重複程式體
ENDM ; 也是以此結尾

;不定字元重複宏
IRPC <形參>,<字串>;將字串中的各個字元依次賦給形參重複執行指定程式體
;重複程式體
ENDM ; 也是以此結尾

IRPC CC,AAB
	ADD AX,CC&X
ENDM
/* 相當於
	ADD AX,AX
	ADD AX,AX
	ADD AX,BX
*/

8. 分支程式設計

  • 單分支程式;多分支程式

  • 要點1是分支條件:

    1. 某種運算產生條件
    2. 判斷條件是否成立
    3. 根據條件產生分支

    多分支程式結構與上面幾步類似,此外還可以透過跳轉表來實現多分支

  • 要點2是轉移範圍:

    • 條件轉移只能在當前IP的-128~+127之間轉移
  • 要點3是程式除錯時要保證每個分支結果的正確性(因為單次除錯可能只走了其中一條分支)

  • 要點4:分支程式設計要點

    1. 正確選擇分支條件,確定條件轉移指令
    2. 編寫程式時,要保證每個分支的正確性
    3. 除錯時逐個分支除錯

例子:書P112例4.7,兩遞增陣列合並。

STACK SEGMENT STACK 'STACK'
	DW 100H UP(?)
TOP LABLE WORD
STACK ENDS

DATA SEGMENT
DAT1 DW 10
	 DB 10H,25H,67H,73H,83H,95H,0A8H,0C2H,0E6H
DAT2 DW 13
     DB 05,12H,45H,58H,65H,67H,70H,76H,88H,92H,0CDH,0DEH

DAT DW ?
	DB 200 DUP(?)
DATA ENDS

CODE SEGMENT
	ASSUME CS:CODE,DS:DATA,ES:DATA,SS:STACK

START:
	; 一些初始化
	MOV AX, DATA
	MOV DS, AX
	MOV ES, AX
	MOV AX, STACK
	MOV SS, AX
	LEA SP, TOP
	; 計算 目標陣列 大小
	MOV CX, DAT1
	MOV DX, DAT2
	MOV DAT, CX
	ADD DAT, DX
	; 置位三個指標,ES:SI,DS:BX,ES:DI
	LEA SI, DAT1+2
	LEA BX, DAT2+2
	LEA DI, DAT+2
	CLD
	
L1:
	MOV AL, [BX]
	INC BX
L2:
	CMP AL,[SI]
	JB L3; AL<[SI],去L3
	; AL>=[SI],向下執行
	MOVSB
    DEC CX ;控制迴圈次數,DAT1陣列是否比較完成
    JZ L4 ;結束
    JMP L2 ;無條件迴圈L2
L3:
	STOSB ;DAT2區資料傳送到DAT
	DEC DX
	JZ L5	;如果兩邊陣列有剩餘
	JMP L1
L4: 
	MOV SI,BX
	DEC SI
	MOV CX,DX
L5:
	REP MOVSB
	MOV AH,4CH
	INT 21H
CODE ENDS
     END START

9. 迴圈程式設計

  • 迴圈程式組成

    1. 初始化部分
    2. 迴圈工作部分
    3. 迴圈引數調整
    4. 迴圈出口判定,返回2或者繼續到5
    5. 結果處理/出口
  • 例程,氣泡排序法程式:書P108頁例4.5

    核心程式碼:

    ; 初始化部分
    MOV CH,N-1;外層
    NEXT2:
    	MOV CL,CH; 內層
    	LEA SI,BUF1; 陣列首址
    	MOV BL,0; 交換標誌
    
    ; 迴圈工作
    NEXT1:
    	MOV AL,[SI]
    	CMP AL,[SI+1]
    	JAE NOCHG	;AL>=[SI+1]不交換
    ;否則交換
    	XCHG AL,[SI+1]
    	MOV [SI],AL
    	MOV BL,-1 ;有交換,說明未排完
    
    NOCHG:
    	; 調整迴圈條件並判定出口
    	INC SI;
    	DEC CL
    	JNZ NEXT1 ;未到0繼續內層交換
        ;否則繼續外層迴圈
        CMP BL,0;是否已經排好序
    	JZ EXIT ;如果排好序則退出
    	DEC CH	
    	JNZ NEXT2
    
    EXIT: MOV AH,4CH
          INT 21H
    

警惕程式挖空填寫題。

相關文章