while 迴圈、do- while 迴圈 和 for 迴圈之間的那點事
阿新 • • 發佈:2020-12-11
一、實驗目的
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 21hint 21h中的2號子功能 功能:從鍵盤輸出單個字元 用法:
mov ah,2 mov dl,xx //xx是待輸出的字元 int 21h5. 實驗任務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]