1. 實驗任務1
教材「實驗9 根據材料程式設計」(P187-189)
程式設計:在螢幕中間分别顯示綠色、綠底紅色、白底藍色的字元串\'welcome to masm!\'。
程式正确編寫後,預期輸出結果如下:

實驗代碼4如下:
1 ;task1.ASM
2 assume cs:code, ds:data
3 data segment
4 db \'welcome to masm!\'
5 db 2h,24h,71h
6 ;綠色:2h,綠底紅字:24h,白底藍字:71h
7 data ends
8
9 code segment
10 start:
11 mov ax, data
12 mov ds, ax ;傳遞data段的段位址給ds
13 mov ax,0b872h ;根據提示得出的顯示器中心區域的顯存位址(第十二行)
14 mov es,ax ;字元将要存入的顯存位址段
15
16 mov di,0
17 mov ah,ds:[di+10h] ;将資料段中用于修改顯示顔色的資料存入寄存器中
18 mov al,ds:[di+11h]
19 mov bh,ds:[di+12h]
20 mov si,0
21 mov cx,16 ;循環次數
22 s: mov bl,ds:[si] ;從資料段中取資料
23 mov es:[di],bl ;存入顯存位址段中(在第十二行顯示)
24 mov es:[di+1],ah ;修改字元顔色
25 mov es:[di+0a0h],bl ;存入顯存位址段中(在第十三行顯示)
26 mov es:[di+0a1h],al ;修改字元顔色
27 mov es:[di+140h],bl ;存入顯存位址段中(在第十四行顯示)
28 mov es:[di+141h],bh ;修改字元顔色
29 inc si
30 add di,2
31 loop s
32
33 mov ax,4c00h
34 int 21h
35 code ends
36 end start
編譯、連接配接後執行task1.exe,結果如圖:
根據提示:doSbox顯示視窗是80×25彩色字元模式顯示,重複三次顯示\'welcom to masm!\'需要将資料存入的現存位址為:
十二行中心區域:b800:0720~b800:0740(32個位元組(十六個字元十六個顔色))
十三行中心區域:b800:07C0~b800:07E0
十四行中心區域:b800:0860~b800:0880
使用debug,在使用g指令執行完程式後檢視對應位址的資料:(虛拟機中win7系統的cmd指令視窗)
2. 實驗任務2 編寫子程式printStr,實作以指定顔色在螢幕上輸出字元串。調用它,完成字元串輸出。
編寫子程式printStr,實作以指定顔色在螢幕上輸出字元串。調用它,完成字元串輸出。
子程式printSar
功能:以指定顔色在螢幕上(從螢幕左上角開始)輸出字元串
要求:字元串以0結尾
入口參數
字元串的起始位址—> ds: si (其中,字元串所在段的段位址—> ds, 字元串起始位址的偏移位址—> si,字元串顔色—> al
出口參數:無
使用任意文本編輯器,錄入彙程式設計式task2.asm。
1 assume cs:code, ds:data
2 data segment
3 str db \'try\', 0
4 data ends
5
6 code segment
7 start:
8 mov ax, data
9 mov ds, ax
10
11 mov si, offset str ;取data資料段的偏移位址
12 mov al, 2 ;用于顯示字元的顔色
13 call printStr ;跳轉到标号printStr(原IP入棧,再給IP賦新值,即标号printStr得偏移位址)
14
15 mov ah, 4ch
16 int 21h
17
18 printStr:
19 push bx ;儲存子程式調用前相關寄存器的狀态,用于在執行完子程式塊後回到該狀态
20 push cx
21 push si
22 push di
23
24 mov bx, 0b800H ;字元顯示處的位址段
25 mov es, bx ;es記錄顯存位址
26 mov di, 0
27 s: mov cl, [si] ;取出資料段中的段資料(存入cx的低位中)
28 mov ch, 0 ;cx高位為0
29 jcxz over ;cx為0則轉到标号over處執行(當data資料段的取完即取出的是最後一個資料(0)時跳轉)
30 mov ch, al ;已經判斷cx不為0,data資料段的資料未取完
31 mov es:[di], cx
32 inc si ;移向下一個位元組
33 add di, 2
34 jmp s ;無條件跳轉
35
36 over: pop di ;跳轉到此處說明data段的字元已經顯示在顯示器上
37 pop si ;調用完成,恢複到調用前狀态
38 pop cx
39 pop bx
40 ret ;利用棧中的資料修改IP(使用 call指令時将IP壓入棧中)
41
42 code ends
43 end start
彙編、運作程式,觀察運作結果。
對源程式做如下修改:
把line3改為:
str 1 db \'another try\', 0
把line12改為:
mov al, 4
再次彙編、運作程式,觀察運作結果。
基于運作結果,了解源代碼,以及,組合使用轉移指令call和ret實作子程式的原理與方法。具體地,在line18-40中:
line19-22, line36-39,這組對稱使用的push、pop,這樣用的目的是什麼?line31的功能是什麼?
ine19-22中使用push将寄存器的值壓入棧中,目的是儲存程式跳轉前的狀态;
line36-39,使用pop指令對資料進行出棧操作,目的是回到程式跳轉前的狀态;
line31:mov es:[di] ,cx作用是将取出的data段中的字元資料以及該字元顯示的顔色資料存入顯存位址中。
實驗代碼中用cl存儲data段中取出的資料,ch先置為0,經過jcxz指令通過後在将控制顔色的資料傳給ch;
當cl還未取到data資料段的最後一個資料時,cx的值就不為零,在将控制顔色的資料傳給ch,然後将cx的資料存入顯存位址段中。
當cl取到data資料段的末尾時(最後一個資料為0),在執行jcxz指令時cx為0 ,跳轉(同時說明資料取完)。
3. 實驗任務3
使用任意文本編輯器,錄入彙編源程式task3.asm。
子程式num2str:
功能:把0~2559之間的任意整數轉換成數字字元串,例如,把1984轉換成\'1984\'
入口參數
要轉換的整數 —> ax
數字字元串的起始位址 —> ds:di (其中:數字字元串所在段的段位址—> ds,字元串起始位址的偏移位址—>di)
出口參數:無 1 assume cs:code, ds:data 2 data segment
1 assume cs:code, ds:data
2 data segment
3 x dw 1984
4 str db 16 dup(0)
5 data ends
6
7 code segment
8 start:
9 mov ax, data
10 mov ds, ax
11 mov ax, x
12 mov di, offset str
13 call num2str
14
15 mov ah, 4ch
16 int 21h
17
18 num2str:
19 push ax
20 push bx
21 push cx
22 push dx
23
24 mov cx, 0
25 mov bl, 10
26 s1:
27 div bl ;做除法,除數8位,被除數預設ax中,結果:al商,ah餘數
28 inc cx ;統計個數
29 mov dl, ah ;将餘數存入dl中(整數中拆分出來的單個數字)
30 push dx ;商壓入棧中
31 mov ah, 0 ;ah置為零
32 cmp al, 0 ;比較al是否為0
33 jne s1 ;不等于則轉移到s1(為0說明整數全部都被拆分出來了)
34
35 mov ax,0b800h
36 mov es,ax
37 mov si,0
38 mov ah,2h
39 s2:
40 pop dx ;出棧,取拆分出來的數字(在bl中)
41 or dl, 30h ;或運算,轉換數字對應的字元(0000 xxxx或0011 0000,0011 xxxx)數子字元30H~39H(0~9)
42 mov [di], dl ;将拆分出來的數字字元送入data資料段中
43 mov es:[si],dl
44 mov es:[si+1],ah
45 add si,2h
46 inc di
47 loop s2
48
49 pop dx
50 pop cx
51 pop bx
52 pop ax
53
54 ret
55 code ends
56 end start
閱讀源代碼,了解子程式num2str的彙編實作。
子任務1
對task3.asm進行彙編、連結,得到可執行程式後,在debug中使用u指令反彙編,使用g指令執行
到line15(程式退出之前),使用d指令檢視資料段内容,觀察是否把轉換後的數字字元串\'1984\'存放
在資料段中str标号後面的單元。
子任務2
對task3.asm源代碼進行修改、完善,把task2.asm中用于輸出以0結尾的字元串的子程式加進來,
實作對轉換後的字元串進行輸出。
預期輸入結果如下:
把task3.asm源代碼中,line3中整數改成0~2559之間的任意數值,運作測試,觀察結果。
任務1:
對task3.asm進行彙編、連結,得到可執行程式後,在debug中對程式進行調試:
1、使用u指令反彙編:
2、使用g指令執行到line15(程式退出之前),使用d指令檢視資料段内容,
子任務2
對task3.asm源代碼進行修改、完善,把task2.asm中用于輸出以0結尾的字元串的子程式加進來,實作對轉換後的字元串進行輸出。
在代碼中增加line35~38,line43~45,将拆分出來的數字顯示在視窗上,如圖:
将line3中的整數改為2000,編譯連接配接後運作結果如圖:
l
将line3中的整數改為199,編譯連接配接後運作結果如圖:
4. 實驗任務4
使用任意文本編輯器,錄入彙編源程式task4.asm。
1 assume cs:code, ds:data
2 data segment
3 str db 80 dup(?) ;内容不确定
4 data ends
5
6 code segment
7 start:
8 mov ax, data
9 mov ds, ax
10 mov si, 0
11
12 s1:
13 mov ah, 1 ;int 21的1号子功能,從鍵盤輸入單個字元
14 int 21h ;出口函數al,鍵入的字元,存放在al中
15 mov [si], al ;将鍵入的字元存入data資料段中
16 cmp al, \'#\' ;al與\'#\'比較
17 je next ;等于則轉移,不是\'#\'則繼續循環輸入字元
18 inc si
19 jmp s1
20 next:
21 mov cx, si ;鍵入的字元個數
22 mov si, 0
23 s2: mov ah, 2 ;int 21的2号子功能,将dl寄存器中的内容在螢幕上輸出
24 mov dl, [si]
25 int 21h
26 inc si
27 loop s2
28
29 mov ah, 4ch
30 int 21h
31 code ends
32 end start
彙編、連結、運作程式,輸入一個字元串并以#結束(比如,2020, bye#)觀察運作結果。
結合運作結果,了解程式功能,了解軟中斷指令。具體地:
line12-19實作的功能是?
line21-27實作的功能是?
編譯連接配接後運作結果如圖:輸入字元串為2020,bye#
輸入結束符号#後程式立即輸出出入的字元串(除#)
line12-19實作的功能是:使用int 21h的1号子功能從鍵盤中輸入字元,儲存到data資料段中,輸入的字元以#結尾(#不存入,也不做輸出)。
line21-27實作的功能是:使用int 21h的2号子功能将存入data資料段中的字元在螢幕上輸出。
5. 實驗任務5
在visual studio內建環境中,編寫一個簡單的包含有函數調用的c程式。代碼如下:
1 #include<stdio.h>
2 int sum(int, int);
3
4 int main() {
5 int a = 2, b = 7, c;
6 c = sum(a, b);
7 return 0;
8 }
9
10 int sum(int x, int y) {
11 return (x + y);
12 }
在line7, line13分别設定斷點,在調試模式下,檢視反彙編代碼。
分析反彙編代碼,從彙編的角度,觀察進階語言中參數傳遞和傳回值是通過什麼實作的,以及,參數入
棧順序,傳回值的帶回方式,等等。
該段c程式反彙編結果如圖:
程式開始:将棧底指針ebp壓入棧中,以棧底指針作為新的棧頂指針将各寄存器的狀态壓入棧中
傳值:将值存入位址單元中。然後執行到語句調用sum函數
在位址單元中取出零個參數的值,存放在eax,ecx中,同時将連個寄存器的值壓入棧中(參數副本),
在調試中檢視入棧時的位址空間如圖:
call跳轉到位址00321078,如圖:
再次跳轉到003216f:調用sum函數反彙編如圖:
調用sum函數,也是先儲存寄存器狀态,再執行。執行完之後再還原寄存器狀态,傳回到main函數。
以下兩條彙編語句:取值得記憶體單元為:115320和115324(參數副本存入得記憶體單元)
sum函數得結果存放在eax寄存器中。
main函數的傳回語句:
程式執行結束,恢複相關寄存器的值:
退出程式。