1. 程式人生 > 實用技巧 >while 迴圈、do- while 迴圈 和 for 迴圈之間的那點事

while 迴圈、do- while 迴圈 和 for 迴圈之間的那點事

一、實驗目的 1. 理解80×25彩色字元模式顯示原理 2. 理解轉移指令jmp, loop, jcxz的跳轉原理,掌握使用其實現分支和迴圈的用法 3. 理解轉移指令call, ret, retf的跳轉原理,掌握組合使用call和ret/retf編寫彙編子程式的方法,掌握引數傳遞方式 4. 理解標誌暫存器的作用 5. 理解條件轉移指令je, jz, ja, jb, jg, jl等的跳轉原理,掌握組合使用匯編指令cmp和條件轉移指令實現分支和迴圈的用法 6. 瞭解在visual studio/Xcode等環境或利用gcc命令列引數反彙編c語言程式的方法,理解編譯器生成的反彙編程式碼 7. 綜合應用定址方式和彙編指令完成應用程式設計 二、實驗準備
實驗前,請複習/學習教材以下內容: 第9章 轉移指令的原理 第10章 call和ret指令 第11章 標誌暫存器 三、實驗內容 1. 實驗任務1 教材「實驗9 根據材料程式設計」(P187-189) 程式設計:在螢幕中間分別顯示綠色、綠底紅色、白底藍色的字串'welcome to masm!'。 程式正確編寫後,預期輸出結果如下: 說明*: 因為是在虛擬dos平臺下做的實驗,經測試,本次實驗中涉及的80×25彩色字元模式,有些系統中,必須在全屏dos下才能看到效果。也存在一些系統中會出現螢幕上彩色塊不穩定的狀況。 tips: 這道程式設計練習需要考慮的兩個關鍵問題: 螢幕中間對應的視訊記憶體位置的計算 —— 第×行第×列對應的視訊記憶體地址單元 字串顏色屬性的設定 上述兩個問題,請參考教材188-189或第9章課件,內有: 80×25彩色字元模式顯示緩衝區結構說明 字元屬性位元組的設定依據 字串及其顏色屬性資訊,可以提前定義在資料段中。 要顯示字串,本質上,就是把字元及其顏色屬性資訊,複製到相應的視訊記憶體空間,藉助定址方式和迴圈實現。附: 本練習中顯示字串所對應的視訊記憶體空間偏移地址80×25的彩色字元模式,即一屏25行80列。 每一行顯示一個字元需要兩個位元組(低位位元組存放字元的ASCⅡ碼值,高位位元組存放字元的顯示屬性),一共160個位元組。 預設,在視訊記憶體第0列顯示時,對應的視訊記憶體空間為B8000H ~ B8F9FH。如果以B800H作為段地址,則一屏25行對應的偏移地址如下。請據此計算出螢幕中間位置對應的視訊記憶體地址。
assume cs:codesg,ss:stack
data segment
    db 
'welcome to masm!' //內容 db 00000010b,00100100b,01110001b //三種顏色 data ends stack segment db 16 dup(0) //棧用來存cx,用於雙層迴圈 stack ends codesg segment start: mov ax,stack mov ss,ax mov ax,16 mov sp,ax mov ax,0b872h //ds首地址=b800+1839(10進位制),因為第一行為11行中間位置為1760~1919,所以取其中間1839 mov ds,ax mov ax,data //es用來暫時存放資料 mov es,ax mov si,
0 mov di,0 mov cx,3 s: mov bx,0 //外層迴圈表示11行、12行、13行,cx為3 push cx //cx入棧來保證內外層迴圈的cx相互不影響,呼應的出棧cx在下方自行檢視 mov cx,16 s1: mov al,es:[bx] //ax低位存放ASCLL碼值 mov ah,es:[si+16] //ax高位存放屬性,這裡是指顏色屬性,而在data中,屬性和內容相差16個位元組 mov ds:[di],ax //ax存入視訊記憶體 inc bx add di,2 //存放ax,所以+2 loop s1 pop cx //讀出外層迴圈次數 inc si add di,080h //切換到下一行(一行共有80個字元,佔160位元組) loop s mov ax,4c00h int 21h codesg ends end start

結果:

2. 實驗任務2 編寫子程式printStr,實現以指定顏色在螢幕上輸出字串。呼叫它,完成字串輸出。 子程式printSar 功能:以指定顏色在螢幕上(從螢幕左上角開始)輸出字串 要求:字串以0結尾 入口引數 字串的起始地址—> ds: si (其中,字串所在段的段地址—> ds, 字串起始地址的偏移地址—> si 字串顏色—> al 出口引數:無 使用任意文字編輯器,錄入彙編源程式task2.asm。
assume cs:code, ds:data
data segment
    str db 'try', 0
data ends

code segment
start:  
    mov ax, data
    mov ds, ax

    mov si, offset str
    mov al, 2
    call printStr

    mov ah, 4ch
    int 21h

printStr:
    push bx
    push cx
    push si
    push di

    mov bx, 0b800H
    mov es, bx
    mov di, 0
s:              mov cl, [si]
    mov ch, 0
    jcxz over
    mov ch, al
    mov es:[di], cx
    inc si
    add di, 2
    jmp s

over:         pop di
    pop si
    pop cx
    pop bx
    ret

code ends
end start
彙編、執行程式,觀察執行結果。 對源程式做如下修改: 把line3改為:
 str db 'another try', 0
把line12改為:
mov al, 4
再次彙編、執行程式,觀察執行結果。 基於執行結果,理解原始碼,以及,組合使用轉移指令call和ret實現子程式的原理與方法。具體地,在 line18-40中: line19-22, line36-39,這組對稱使用的push、pop,這樣用的目的是什麼? 分析:備份資料以防在接下來的操作中對於資料造成不可回溯的更改 line30的功能是什麼? 分析:將cx(其中cl存資料,ch存顏色屬性)存入es:[di] 3. 實驗任務3 使用任意文字編輯器,錄入彙編源程式task3.asm。 子程式num2str: 功能:把0~2559之間的任意整數轉換成數字字串,例如,把1984轉換成'1984' 入口引數 要轉換的整數 —> ax 數字字串的起始地址 —> ds:di (其中:數字字串所在段的段地址—> ds,字串起始地址的偏移地址—>di) 出口引數:無
assume cs:code, ds:data
data segment
        x dw 1984
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx
        
        mov cx, 0
        mov bl, 10
s1:      
        div bl
        inc cx
        mov dl, ah
        push dx
        mov ah, 0
        cmp al, 0
        jne s1
s2:        
        pop dx
        or dl, 30h
        mov [di], dl
        inc di
        loop s2
        
        pop dx
        pop cx
        pop bx
        pop ax

        ret
code ends
end start
閱讀原始碼,理解子程式num2str的彙編實現。 子任務1 對task3.asm進行彙編、連結,得到可執行程式後,在debug中使用u命令反彙編,使用g命令執行 到line15(程式退出之前),使用d命令檢視資料段內容,觀察是否把轉換後的數字字串'1984'存放 在資料段中str標號後面的單元。

-u反彙編

-d命令檢視資料段,發現1984已傳入

子任務2 對task3.asm原始碼進行修改、完善,把task2.asm中用於輸出以0結尾的字串的子程式加進來,實現對轉換後的字串進行輸出。
assume cs:code, ds:data
data segment
        x dw 1984
        str db 16 dup(0)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov ax, x
        mov di, offset str
        call num2str
    
        mov al,2
        mov si,offset str
        call printStr

        mov ah, 4ch
        int 21h

num2str:
        push ax
        push bx
        push cx
        push dx
        
        mov cx, 0
        mov bl, 10
s1:      
        div bl
        inc cx
        mov dl, ah
        push dx
        mov ah, 0
        cmp al, 0
        jne s1
s2:        
        pop dx
        or dl, 30h
        mov [di], dl
        inc di
        loop s2
        
        pop dx
        pop cx
        pop bx
        pop ax

        ret
printStr:
    push bx
    push cx
    push si                                                     
    push di

    mov bx, 0b800H
    mov es, bx
    mov di, 0
s:      mov cl, [si]
    mov ch, 0
    jcxz over
    mov ch, al
    mov es:[di], cx
    inc si
    add di, 2
    jmp s

over:   pop di
    pop si
    pop cx
    pop bx
    ret

code ends
end start
預期輸入結果如下:

實際結果:

把task3.asm原始碼中,line3中整數改成0~2559之間的任意數值,執行測試,觀察結果。
data segment
        x dw 2555
        str db 16 dup(0)
data ends

結果:

4. 實驗任務4 使用任意文字編輯器,錄入彙編源程式task4.asm。
assume cs:code, ds:data
data segment
        str db 80 dup(?)
data ends

code segment
start:  
        mov ax, data
        mov ds, ax
        mov si, 0

s1:        
        mov ah, 1
        int 21h
        mov [si], al
        cmp al, '#'
        je next
        inc si
        jmp s1
next:
        mov cx, si
        mov si, 0
s2:     mov ah, 2
        mov dl, [si]
        int 21h
        inc si
        loop s2

        mov ah, 4ch
        int 21h
code ends
end start
彙編、連結、執行程式,輸入一個字串並以#結束(比如,2020, bye#)觀察執行結果。

輸入“”“zqfdegushi#”

結合執行結果,理解程式功能,瞭解軟中斷指令。具體地: line12-19實現的功能是? 分析:cmp對於輸入的字元進行判斷,如果是以“#”為結尾則表示輸入完成,je 跳入next,否則仍在迴圈中,繼續輸入字元 line21-27實現的功能是? 分析:設定迴圈次數為恰好為si這一偏移地址,利用輸出語句輸出一個個字元 附:task4.asm中用到的兩個系統功能呼叫 int 21h中的1號子功能 功能:從鍵盤輸入單個字元 用法:
mov ah,1
int 21h
int 21h中的2號子功能 功能:從鍵盤輸出單個字元 用法:
mov ah,2
mov dl,xx //xx是待輸出的字元
int 21h
5. 實驗任務5 在visual studio整合環境中,編寫一個簡單的包含有函式呼叫的c程式。程式碼如下:
#include<stdio.h>
int sum(int,int);

int main(){
       int a=2,b=7,c;
       
       c=sum(a,b);
       
       return 0;
}

int sum(int x,int y){
       return(x+y);
}
在line7, line13分別設定斷點,在除錯模式下,檢視反彙編程式碼。

分析反彙編程式碼,從彙編的角度,觀察高階語言中引數傳遞和返回值是通過什麼實現的,以及,引數入棧順序,返回值的帶回方式,等等。 分析: 高階語言中的引數傳遞是從雙字資料ptr [a]、ptr [b]傳入eax、ecx等暫存器,用call命令呼叫sun函式,最後返回真正的值為eax,再傳入雙字資料ptr[c] sum函式內同樣先初始化並分配棧空間,執行得到的結果儲存在eax暫存器內,呼叫ret指令返回。