組合語言(王爽第三版)實驗4
1.程式設計:向記憶體0:200H~0:23fH依次傳送資料0~63(3FH)
程式分析:
【1】記憶體0:200H~0:23fH空間與0020:0-0020:3f記憶體空間是一樣的,(這個不會?oh!My God!,實體地址是唯一的,但邏輯地址組合是多種的。)
【2】因為偏移地址是連續記憶體單元;我們可以把偏移地址做下文章。bx暫存器儲存偏移地址(通過偏移地址的間接訪問記憶體單元,這主要是寫入的記憶體單元)。dx暫存器作為儲存中間變數的容器(源資料,常量0-63)來向記憶體寫入。
彙編程式碼如下:
assume cs:code
code segment
mov ax,0020H
mov ds,ax ;記憶體單元的段地址寫入ds
mov bx,0 ;bx暫存器存放偏移地址,初始化為0
mov dx,0 ;dx暫存器儲存常量數值0~63
mov cx,40H ;這裡40H==64,cx暫存器存放迴圈次數。也可以為64;
s: mov [bx],dx ;向[bx]記憶體單元寫入dx值
inc bx ;累加bx
inc dx ;累加dx
loop s
mov ax,4c00H
int 21H
code ends
end
2. 向記憶體0:200H~0:23fH依次傳送資料0~63(3FH),9條命令的程式的簡化版本(不包括虛擬碼):
程式分析:
【1】記憶體0:200H~0:23fH空間與0020:0-0020:3f
修改後的彙編程式碼如下:
assume cs:code
code segment
mov ax,0020H
mov ds,ax ;ds指向0020記憶體段
mov bx,0 ;bx暫存器存放偏移地址,初始化為0,也當做源資料:常量數值
mov cx,64 ;迴圈次數64
s: mov [bx],bx ; 向[bx]記憶體單元寫入bx數值
inc bx
loop s
mov ax,4c00h
int 21H
code ends
end
實驗體會:
1. bx暫存器一般用作偏移地址的儲存,[bx]也就代表了段地址與[bx]組合後指向的記憶體單元。 為什麼這樣,還有一個原因就是在源程式中[xxxx]在masm編譯器中當做了常量xxxx,而[bx]則沒有這個問題,編譯器當做是一個記憶體地址段的偏移地址。
2.cx暫存器在迴圈語句中,常作為計數器使用,loop迴圈語句把cx暫存器中的值作為判斷是否迴圈的依據。
CX暫存器在debug除錯一個可執行程式時,CX的初始值為該程式的位元組尺寸大小。
3.ds暫存器存放的是指向的記憶體單元的段地址。
4.在debug模式下,注意源程式中loop s語句的體現,直接轉移到了一個地址(偏移地址)了。
5.結束debug程式,鍵入q,退回到dos或命令提示符狀態。
6.在debug狀態下,如果想要直接執行的到某個語句,可使用g命令,例如:
g XXXX(偏移地址) 在此之前的命令都執行了。
7.在彙編源程式中,資料不能以字母開頭,前面可以加0,例如:0ff02H。
8.在debug下除錯程式,遇到int 21H命令,鍵入p命令結束程式。
9.使用p命令可以結束loop的迴圈(當你遇到了loop語句的時候)。
10.在寫彙編程式時,十進位制和十六進位制(用h、H標註)可以混用。編譯器自動給你轉換了。
程式執行後結果:
-d ds:0
0020:0000 00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F ................
0020:0010 10 11 12 13 14 15 16 17-18 19 1A 1B 1C 1D 1E 1F ................
0020:0020 20 21 22 23 24 25 26 27-28 29 2A 2B 2C 2D 2E 2F !"#$%&'()*+,-./
0020:0030 30 31 32 33 34 35 36 37-38 39 3A 3B 3C 3D 3E 3F 0123456789:;<=>?
3.下面程式的功能是將mov ax,4c00H之前的指令複製到記憶體0:200處,補全程式,上機除錯,跟蹤執行結果:
程式分析:
【1】使用debug除錯一個EXE檔案時候,使用r命令檢視暫存器狀態,其中cx暫存器的值(初始值)就是該程式程式碼的大小(按照位元組數)。我們可以通過執行debug程式來除錯生成的EXE檔案,前提你先將CX暫存器賦個值。
側面驗證CX暫存器的另一個作用。
【2】cs段暫存器中儲存的是指向程式程式碼段的段地址。此實驗是將程式的程式碼(按位元組)複製,故將cs暫存器中的指向程式碼的段地址賦值給ax,再通過ax暫存器賦值給ds段暫存器。(為什麼不能支援從段暫存器cs直接賦值給段暫存器ds呢?回憶下,在8086CPU中,ds、ss、cs、es四個段暫存器存放的都是段地址,在CPU和我們來看。其他的暫存器一般存放的都是資料。
這4個段暫存器支援從其他暫存器中賦值,但不允許立即數直接賦值給段暫存器。)
【3】[bx]作為偏移地址為bx的記憶體單元,它支援的段地址預設是儲存在ds段暫存器中的。 本例中ds:[bx]指向的是儲存程式碼段的記憶體單元(源記憶體段)。由於ds被佔用了,故被寫入的記憶體單元的段地址就沒有儲存的段暫存器了,es暫存器上場了,es儲存了地址為0020H的段地址(目標記憶體段),那麼同樣使用[bx]偏移地址的話,必須明確的指出它的字首,故es:[bx]就指向了記憶體是0200H的記憶體單元地址段。
實驗步驟如下:
(1)首先使用debug除錯該程式:假如這個可執行程式(經編譯、連線無誤後的)為test5.exe
debug test5.exe
(2)使用r命令顯示暫存器狀態,顯示整個程式程式碼所佔位元組數。
-r
AX=0000 BX=0000 CX=001C DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B55 ES=0B55 SS=0B65 CS=0B65 IP=0000 NV UP EI PL NZ NA PO NC
0B65:0000 8CC8 MOV AX,CS
這裡我們發現CX= 001CH。
(3)使用u命令顯示彙編指令,求出需要複製的機器碼位元組數。
-u cs:0000
0B65:0000 8CC8 MOV AX,CS
0B65:0002 8ED8 MOV DS,AX
0B65:0004 B82000 MOV AX,0020
0B65:0007 8EC0 MOV ES,AX
0B65:0009 BB0000 MOV BX,0000
0B65:000C B90300 MOV CX,0003
0B65:000F 8A07 MOV AL,[BX]
0B65:0011 26 ES:
0B65:0012 8807 MOV [BX],AL
0B65:0014 43 INC BX
0B65:0015 E2F8 LOOP 000F
0B65:0017 B8004C MOV AX,4C00
0B65:001A CD21 INT 21
我們發現mov ax,4cooH/int 21H它們共佔用了5個位元組。所以在本實驗中我們需要複製的程式碼位元組數是001CH-0005H=0017H==23個位元組,故cx計數暫存器賦值為23或17H。
(4)完整的彙編程式碼如下:
assume cs:code
code segment
mov ax,cs ;將cs段地址賦值給ax
mov ds,ax ;用cs暫存器中的值初始化ds段暫存器,
mov ax,0020H
mov es,ax ;es指向0020H記憶體段
mov bx,0 ;偏移地址暫存器清零
mov cx,17H ;此處是迴圈次數:程式機器碼的位元組數,儲存在CX中
s: mov al,[bx] ;將[bx]按照位元組單元傳送給al
mov es:[bx],al ;複製到es段記憶體中
inc bx
loop s
mov ax,4c00H
int 21H
code ends
end
實驗結果測試:
-d 20:0
0020:0000 8C C8 8E D8 B8 20 00 8E-C0 BB 00 00 B9 17 00 8A ..... ..........
0020:0010 07 26 88 07 43 E2 F8 00-00 00 00 00 00 00 00 00 .&..C...........
……
我們發現偏移地址從0000H~0017H儲存了程式的執行程式碼。與程式執行程式碼儲存的記憶體單元比較,我們發現一樣的。
-d cs:0
0B65:0000 8C C8 8E D8 B8 20 00 8E-C0 BB 00 00 B9 17 00 8A ..... ..........
0B65:0010 07 26 88 07 43 E2 F8 B8-00 4C CD 21 FF 06 48 91 .&..C....L.!..H.