王爽《匯編語言》第四版 超級筆記
第5章[BX]和 loop 指令
1、[bx]和 內存單元的描述
[bx]是什么呢?和[0]有些類似,[0]表示內存單元,它的偏移地址是0。
比如在下面的指令中(在Debug中使用):
mov ax,[0]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址為0,段地址在ds中。
mov al,[0]
將一個內存單元的內容送入al,這個內存單元的長度為1字節(字節單元),存放一個字節,偏移地址為0,段地址在ds中。
要完整地描述一個內存單元,需要兩種信息:
- ①內存單元的地址;
- ②內存單元的長度(類型)。
用[0]表示一個內存單元時,0表示單元的偏移地址,段地址默認在ds中,單元的長度(類型)可以由具體指令中的其他操作對象(比如說寄存器)指出。
[bx]同樣也表示一個內存單元,它的偏移地址在bx中,比如下面的指令:
mov ax,[bx]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址在bx中,段地址在ds中。
mov al,[bx]
2、loop
英文單詞“loop”有循環的含義,顯然這個指令和循環有關。
3、我們定義的描述性的符號:"()”
我們將使用一個描述性的符號“()”來表示一個寄存器或一個內存單元中的內容。比如:
(ax)表示ax中的內容、(al)表示al中的內容;
(20000H)表示內存20000H單元的內容(()中的內存單元的地址為物理地址);
注意,“()”中的元素可以有3種類型:
- ①寄存器名;
- ②段寄存器名;
- ③內存單元的物理地址(一個20位數據)。
我們看一下(X)的應用,比如:
(1) ax中的內容為0010H,可以這樣來描述:(ax)=0010H;
(2) 2000:1000處的內容為0010H,可以這樣來描述:(21000H)=0010H;
(3) 對於mov ax,[2]的功能,可以這樣來描述:(ax)=((ds)x16+2);
(4) 對於mov [2],ax的功能,可以這樣來描述:((ds)x16+2)=(ax);
(5) 對於add ax,2的功能,可以這樣來描述:(ax)=(ax)+2;
(6) 對於add ax,bx的功能,可以這樣來描述:(ax)=(ax)+(bx);
(7) 對於push ax的功能,可以這樣來描述:
(sp)=(sp)-2
((ss)x16+(sp))=(ax)(8) 對於pop ax的功能,可以這樣來描述:
(ax)=((ss)x16+(sp))
(sp)=(sp)+2
“(X)”所表示的數據有兩種類型:①字節;②字。
是哪種類型由寄存器名或具體的運算決定。
4、約定符號idata表示常量
我們在Debug中寫過類似的指令:mov ax,[0],表示將ds:0處的數據送入ax中。
指令中,在里用一個常量0表示內存單元的偏移地址。
以后,我們用idata表示常量。比如:
mov ax,[idata] 就代表 mov ax,[1]、mov ax,[2]、mov ax,[3] 等。
mov bx,idata 就代表 mov bx,1、mov bx,2、mov bx,3 等。
mov ds,idata 就代表mov ds,1、mov ds,2 等,它們都是非法指令。
5.1 [BX]
mov ax,[bx]
功能:bx中存放的數據作為一個偏移地址EA,段地址SA默認在ds中,將SA:EA處的數據送入ax中。即:(ax)=((ds)x16+(bx))。
mov [bx],ax
功能:bx中存放的數據作為一個偏移地址EA,段地址SA默認在ds中,將ax中的數據送入內存
SA:EA處。即:((ds)x16+(bx))=(ax)。
問題5.1
程序和內存中的情況如圖5.1所示,寫出程序執行后,21000H-21007H單元中的內容。
思考后看分析。
注意,inc bx 的含義是bx中的內容加1,比如下面兩條指令:
mov bx,1
inc bx
執行后,bx=2。
分析:
(1)先看一下程序的前3條指令:
mov ax,2000H
mov ds,ax
mov bx,1000H
這3條指令執行后,ds=2000H,bx=1000H。
(2)接下來,第4條指令:
mov ax,[bx]
指令執行前:ds=2000H,bx=1000H,則mov ax,[bx] 將把內存2000:1000處的字型數據送入ax中。該指令執行后,ax=00beH。
(3)接下來,第5、6條指令:
inc bx
inc bx
這兩條指令執行前bx=1000H,執行后bx=1002H。
(4)接下來,第7條指令:
mov [bx],ax
指令執行前:ds=2000H,bx=1002H,則mov [bx],ax 將把ax中的數據送入內存2000:1002處。
指令執行后,2000:1002單元的內容為BE,2000:1003單元的內容為00。
(5)接下來,第8、9條指令:
inc bx
inc bx
這兩條指令執行前bx=1002H,執行后bx=1004H。
(6)接下來,第10條指令:
mov [bx],ax
指令執行前:ds=2000H,bx=1004H,則mov [bx],ax 將把ax中的數據送入內存2000:1004處。
指令執行后,2000:1004單元的內容為BE,2000:1005單元的內容為00。
(7)接下來,第11條指令:
inc bx
這條指令執行前bx=1004H,執行后bx=1005H。
(8)接下來,第12條指令:
mov [bx],al
指令執行前:ds=2000H,bx=1005H,則mov [bx],al 將把al中的數據送入內存2000:1005處。
指令執行后,2000:1005單元的內容為BE。
(9)接下來,第13條指令:
inc bx
這條指令執行前bx=1005H,執行后bx=1006H。
(10)接下來,第14條指令:
mov [bx],al
指令執行前:ds=2000H,bx=1006H,則mov [bx],al 將把al中的數據送入內存2000:1006處。
指令執行后,2000:1006單元的內容為BE。
程序執行后,內存中的情況如圖5.2所示。
5.2 loop 指令
loop指令的格式是:loop標號,CPU執行loop指令的時候,要進行兩步操作:
- ①(cx)=(cx)-l;
- ②判斷CX中的值,不為零則轉至標號處執行程序,如果為零則向下執行。
從上面的描述中,可以看到,CX中的值影響着loop指令的執行結果。
通常(注意,我們說的是通常)我們用loop指令來實現循環功能,CX中存放循環次數。
下面我們通過一個程序來看一下loop指令的具體應用。
編程計算2^12。
分析:212=2x2x2x2x2x2x2x2x2x2x2x2,若設(ax)=2,可計算(ax)=(ax)x2x2x2x2x2x2x2x2x2x2x2,最后(ax)中為212的值。Nx2可用N+N實現,程序如下。
assume cs:code
code segment
mov ax,2
;做11次 add ax,ax
mov ax,4c00h
int 21h
code ends
end
可見,按照我們的算法,計算2^12需要11條重復的指令add ax,ax,我們顯然不希望這樣來寫程序,這里,可用loop來簡化我們的程序。
程序5.1
assume cs:code
code segment
mov ax,2
mov cx,11
s: add ax,ax
loop s
mov ax,4c00h
int 21h
code ends
end
下面分析一下程序5.1。
(1)標號
在匯編語言中,標號代表一個地址,程序5.1中有一個標號s。它實際上標識了一個地址,這個地址處有一條指令:add ax,ax。
(2)loop s
CPU執行loop s的時候,要進行兩步操作:
- ①(cx)=(cx)-l ;
- ②判斷ex中的值,不為0則轉至標號s所標識的地址處執行(這里的指令是add ax,ax),如果為0則執行下一條指令(下一條指令是mov ax,4c00h)。
(3)以下3條指令
mov cx,11
s : add ax,ax
loop s
執行loop s時,首先要將(cx)減1,然后若(cx)不為0,則向前轉至s處執行add ax,ax。
所以,可以利用cx來控制add ax,ax的執行次數。
下面我們詳細分析一下這段程序的執行過程,從中體會如何用cx和loop s相配合實現循環功能。
從上面的過程中,我們可以總結出用cx和loop指令相配合實現循環功能的3個要點:
(1) 在cx中存放循環次數;
(2) loop指令中的標號所標識地址要在前面;
(3) 要循環執行的程序段,要寫在標號和loop指令的中間。
用cx和loop指令相配合實現循環功能的程序框架如下。
mov cx,循環次數
s:
循環執行的程序段
loop s
編程,用加法計算123x236,結果存在ax中。思考后看分析。
分析:
可用循環完成,將123加236次。可先設(ax)=0,然后循環做236次(ax)=(ax)+123。
程序如下
程序5.2
assume cs:code
code segment
mov ax,0
mov cx,236
s:add ax,123
loop s
mov ax,4c00h
int 21h
code ends
end
5.3 Debug和匯編編譯器masm對指令的不同處理
我們在Debug中寫過類似的指令:
mov ax,[0]
表示將ds:0處的數據送入ax中。
但是在匯編源程序中,指令“mov ax,[0]”被編譯器當作指令“mov ax,0”處理。
下面通過具體的例子來看一下Debug和匯編編譯器masm對形如“mov ax,[0]”這類指令的不同處理。
任務:將內存2000:0、2000:1、2000:2、2000:3單元中的數據送入al、bl、cl、dl中。
(1)在Debug中編程實現:
mov ax,2000
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl,[2]
mov dl,[3]
(2)匯編源程序實現:
assume cs:code
code segment
mov ax,2000h
mov ds,ax
mov al,[0]
mov bl,[1]
mov cl,[2]
mov dl,[3]
mov axz 4c00h
int 21h
code ends
end
我們看一下兩種實現的實際實施情況:
(1)Debug中的情況如圖5.16所示。
(2)將匯編源程序存儲為compare.asm,用masm、link生成compare.exe,用Debug加載compare.exe,如圖5.17所示。
從圖5.16、圖5.17中我們可以明顯地看出,Debug和編譯器masm對形如“mov ax,[0]”這類指令在解釋上的不同。
我們在Debug中和源程序中寫入同樣形式的指令:"mov al,[0]"、"mov bl,[1]"、"mov cl,[2]"、"mov dl,[3]”,但Debug和編譯器對這些指令中的“[idata]”卻有不同的解釋。
Debug將它解釋為“[idata]”是一個內存單元,“idata”是內存單元的偏移地址;而編譯器將“[idata]”解釋為“idata”。
那么我們如何在源程序中實現將內存2000:0、2000:1、2000:2、2000:3單元中的數據送入al、bl、cl、dl中呢?
目前的方法是,可將偏移地址送入bx寄存器中,用[bx]的方式來訪問內存單元。比如我們可以這樣訪問2000:0單元:
mov ax,2000h
mov ds,ax ;段地址2000h送入ds
mov bx,0 ;偏移地址0送入bx
mov al,[bx] ;ds:bx單元中的數據送入al
這樣做是可以,可是比較麻煩,我們要用bx來間接地給出內存單元的偏移地址。
我們還是希望能夠像在Debug中那樣,在“[]”中直接給出內存單元的偏移地址。這樣做,在匯編源程序中也是可以的,只不過,要在“[]”的前面顯式地給出段地址所在的段寄存器。比如我們可以這樣訪問2000:0單元:
mov ax,2000h
mov ds,ax
mov al,ds:[0]
比較一下匯編源程序中以下指令的含義。
- “mov al,[0]”,含義:(al)=0,將常量0送入al中(與mov al,0含義相同);
- “mov al,ds:[0]”,含義:(al)=((ds)x16+0),將內存單元中的數據送入al中;
- “mov al,[bx]”,含義:(al)=((ds)x16+(bx)),將內存單元中的數據送入al中;
- “mov al,ds:[bx]”,含義:與“mov al,[bx]”相同。
從上面的比較中可以看出:
(1)在匯編源程序中,如果用指令訪問一個內存單元,則在指令中必須用“[…]”來表示內存單元,如果在“[]”里用一個常量idata直接給出內存單元的偏移地址,就要在“[]”的前面顯式地給出段地址所在的段寄存器。比如
mov al,ds:[0]
如果沒有在“[]”的前面顯式地給出段地址所在的段寄存器,比如
mov al,[0]
那么,編譯器masm將把指令中的“[idata]”解釋為“idata”。
(2)如果在“[]”里用寄存器,比如bx,間接給出內存單元的偏移地址,則段地址默認在ds中。當然,也可以顯式地給出段地址所在的段寄存器。
5.4 loop和[bx]的聯合應用
考慮這樣一個問題,計算ffff:0~ffff:b單元中的數據的和,結果存儲在dx中。
我們還是先分析一下。
(1)運算后的結果是否會超出dx所能存儲的范圍?
ffff:0~ffff:b內存單元中的數據是字節型數據,范圍在0~255之間,12個這樣的數據相加,結果不會大於65535,可以在dx中存放下。
(2)我們能否將ffff:0~ffff:b中的數據直接累加到dx中?
當然不行,因為ffff:0~ffff:b中的數據是8位的,不能直接加到16位寄存器dx中。
(3)我們能否將ffff:0~ffff:b中的數據累加到dl中,並設置(dh)=0,從而實現累加到dx中?
這也不行,因為dl是8位寄存器,能容納的數據的范圍在0~255之間,ffff:0~ffff:b中的數據也都是8位,如果僅向dl中累加12個8位數據,很有可能造成進位丟失。
(4)我們到底怎樣將ffff:0~ffff:b中的8位數據,累加到16位寄存器dx中?
從上面的分析中,可以看到,這里面有兩個問題:類型的匹配和結果的不超界。
具體地說,就是在做加法的時候,我們有兩種方法:
1、(dx)=(dx)+內存中的8位數據;
2、(dl)=(dl)+內存中的8位數據。
第一種方法中的問題是兩個運算對象的類型不匹配,第二種方法中的問題是結果有可能超界。
怎樣解決這兩個看似矛盾的問題?
目前的方法(在后面的課程中我們還有別的方法)就是得用一個16位寄存器來做中介。
將內存單元中的8位數據賦值到一個16位寄存器ax中,再將ax中的數據加到dx上,從而使兩個運算對象的類型匹配並且結果不會超界。
程序5.5
assume cs:code
code segment
mov ax,0ffffh
mov ds, ax ;設置(ds)=ffffh
mov dx,0 ;初始化累加寄存器,(dx)=0
mov al,ds:[0]
mov ah,0 ;(ax)=((ds)x16+0)=(ffff0h)
add dx,ax ;向dx中加上ffff:0單元的數值
mov al,ds:[1]
mov ah,0 ;(ax)=((ds)x16+1)=(ffff1h)
add dx,ax ;向dx中加上ffff:1單元的數值
mov al,ds:[2]
mov ah,0 ;(ax)=((ds)x16+2)=(ffff2h)
add dx,ax ;向dx中加上ffff:2單元的數值
mov al,ds:[3]
mov ah,0 ;(ax)=((ds)x16+3)=(ffff3h)
add dx,ax ;向dx中加上ffff:3單元的數值
mov al,ds:[4]
mov ah,0 ;(ax)=((ds)x16+4)=(ffff4h)
add dx,ax ;向dx中加上ffff:4單元的數值
mov al,ds:[5]
mov ah,0 ;(ax)=((ds)x16+5)=(ffff5h)
add dx,ax ;向dx中加上ffff:5單元的數值
mov al,ds:[6]
mov ah,0 ;(ax)=((ds)x16+6)=(ffff6h)
add dx,ax ;向dx中加上ffff:6單元的數值
mov al,ds:[7]
mov ah,0 ;(ax)=((ds)x16+7)=(ffff7h)
add dx,ax ;向dx中加上ffff:7單元的數值
mov al,ds:[8]
mov ah,0 ;(ax)=((ds)x16+8)=(ffff8h)
add dx,ax ;向dx中加上ffff:8單元的數值
mov al,ds:[9]
mov ah,0 ;(ax)=((ds)x16+9)=(ffff9h)
add dx,ax ;向dx中加上ffff:9單元的數值
mov al,ds:[0ah]
mov ah,0 ;(ax)=((ds)x16+0ah)=(ffffah)
add dx,ax ;向dx中加上ffff:a單元的數值
mov al,ds:[0bh]
mov ah,0 ;(ax)=((ds)x16+0bh)=(ffffbh)
add dx,ax ;向dx中加上ffff:b單元的數值
mov ax,4c00h ;程序返回
int 21h
code ends
end
上面的程序很簡單,不用解釋,你一看就懂。不過,在看懂了之后,你是否覺得這個程序編得有些問題?
它似乎沒有必要寫這么長。這是累加ffff:0~ffff:b中的12個數據,如果要累加0000:0~0000:7fff中的32K個數據,按照這個程序的思路,將要寫將近10萬行程序(寫一個簡單的操作系統也就這個長度了)。
問題5.4
應用loop指令,改進程序5.5,使它的指令行數讓人能夠接受。
分析:
可以看出,在程序中,有12個相似的程序段,我們將它們一般化地描述為:
mov al,ds:[X] ;ds:X指向ffff:X單元
mov ah,0 ;(ax)=((ds)x16+(X))=(ffffXh)
add dx,ax ;向dx中加上ffff:X單元的數值
從程序實現上,我們將循環做。
(al)=((ds)x16+X)
(ah)=0
(dx)=(dx)+(ax)
一共循環12次,在循環開始前(ds)=ffffh,X=0,ds:X指向第一個內存單元。每次循環后,X遞增,ds:X指向下一個內存單元。
完整的算法描述如下。
初始化:
(ds)=ffffh
X=0
(dx)=0
循環12次:
(al)=((ds)x16+X)
(ah)=0
(dx)=(dx)+(ax)
X=X+1
可見,表示內存單元偏移地址的X應該是一個變量,因為在循環的過程中,偏移地址必須能夠遞增。
這樣,在指令中,我們就不能用常量來表示偏移地址。我們可以將偏移地址放到bx中,用[bx]的方式訪問內存單元。在循環開始前設(bx)=0,每次循環,將bx中的內容加1即可。
最后一個問題是,如何實現循環12次?
我們的loop指令該發揮作用了。更詳細的算法描述如下。
初始化:
(ds)=ffffh
(bx)=0
(dx)=0
(cx)=12
循環12次:
s: (al)=((ds)x16+(bx))
(ah)=0
(dx)=(dx)+(ax)
(bx)=(bx)+1
loop s
最后,我們寫出程序。
程序5.6
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax
mov bx,0 ;初始化ds :bx指向ffff: 0
mov dx,0 ;初始化累加寄存器dx,(dx)=0
mov ex,12 ;初始化循環計數寄存器cx,(cx)=12
s: mov al,[bx]
mov ah,0
add dx,ax ;間接向dx中加上((ds)x16+(bx))單元的數值
inc bx ;ds:bx指向下一個單元
loop s
mov ax,4c00h
int 21h
code ends
end
在實際編程中,經常會遇到,用同一種方法處理地址連續的內存單元中的數據的問題。
我們需要用循環來解決這類問題,同時我們必須能夠在每次循環的時候按照同一種方法來改變要訪問的內存單元的地址。
這時,就不能用常量來給出內存單元的地址(比如,[0]、[1]、[2]中,0、1、2是常量),而應用變量。“mov al,[bx]”中的bx就可以看作一個代表內存單元地址的變量,我們可以不寫新的指令,僅通過改變bx中的數值,改變指令訪問的內存單元。
5.5 段前綴及使用
指令“mov ax,[bx]”中,內存單元的偏移地址由bx給出,而段地址默認在ds中。
我們可以在訪問內存單元的指令中顯式地給出內存單元的段地址所在的段寄存器。比如:
(1) mov ax,ds:[bx]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址在bx中,段地址在ds中。
(2)mov ax,cs:[bx]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址在bx中,段地址在cs中。
(3)mov ax,ss:[bx]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址在bx中,段地址在ss中。
(4)mov ax,es:[bx]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址在bx中,段地址在es中。
(5)mov ax,ss:[0]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址為0,段地址在ss中。
(6)mov ax,cs:[0]
將一個內存單元的內容送入ax,這個內存單元的長度為2字節(字單元),存放一個字,偏移地址為0,段地址在cs中。
這些岀現在訪問內存單元的指令中,用於顯式地指明內存單元的段地址的“ds:”“cs:”“ss:”“es:”,在匯編語言中稱為段前綴。
我們考慮一個問題,將內存ffff:0~ffff:b單元中的數據復制到0:200~0:20b單元中。
分析一下。
(1)0:200~0:20b單元等同於0020:0~0020:b單元,它們描述的是同一段內存空間。
(2)復制的過程應用循環實現,簡要描述如下。
初始化:
X=0
循環12次:
將ffff:X單元中的數據送入0020:X(需要用一個寄存器中轉)
X=X+1
(3)在循環中,源始單元ffff:X和目標單元0020:X的偏移地址X是變量。我們用bx來存放。
(4)將0:200~0:20b用0020:0~0020:b描述,就是為了使目標單元的偏移地址和源始單元的偏移地址從同一數值0開始。
程序5.8
assume cs:code
code segment
mov bx,0 ;(bx)=0,偏移地址從0開始
mov cx,12 ;(cx)=12,循環12次
s: mov ax,0ffffh
mov ds,ax ;(ds)=0ffffh
mov dl,[bx] ;(dl)=((ds)x16+(bx)),將 ffff:bx中的數據送入dl
mov ax,0020h
mov ds,ax ;(ds)=0020h
mov [bx],dl ;((ds)x16+(bx))=(dl),將中dl的數據送入0020:bx
inc bx ;(bx) = (bx)+1
loop s
mov ax,4c00h
int 21h
code ends
end
因源始單元ffff:X和目標單元0020:X相距大於64KB,在不同的64KB段里,程序5.8中,每次循環要設置兩次ds。
這樣做是正確的,但是效率不高。我們可以使用兩個段寄存器分別存放源始單元ffff:X和目標單元0020:X的段地址,這樣就可以省略循環中需要重復做12次的設置ds的程序段。
改進的程序如下。
程序5.9
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax ;(ds)=0ffffh
mov ax,0020h
mov es,ax ;(es)=0020h
mov bx,0 ;(bx)=0,此時ds:bx指向ffff:0,es:bx指向0020:0
mov cx,12 ;(cx)=12,循環 12 次
s: mov dl,[bx] ;(dl)=((ds)x16+(bx)),將ffff:bx中的數據送入dl
mov es:[bx],dl ;((es)x16+(bx))=(dl),將dl中的數據送入0020:bx
inc bx ;(bx)=(bx)+1
loop s
mov ax,4c00h
int 21h
code ends
end
程序5.9中,使用es存放目標空間0020:0~0020:b的段地址,用ds存放源始空間ffff:0~ffff:b的段地址。
在訪問內存單元的指令“mov es:[bx],al”中,顯式地用段前綴“es:”給出單元的段地址,這樣就不必在循環中重復設置ds。
5.6 一段安全的空間
在8086模式中,隨意向一段內存空間寫入內容是很危險的,因為這段空間中可能存放着重要的系統數據或代碼。比如下面的指令:
mov ax,1000h
mov ds,ax
mov al,0
mov ds:[0],al
我們以前在Debug中,為了講解上的方便,寫過類似的指令。但這種做法是不合理的,因為之前我們並沒有論證過1000:0中是否存放着重要的系統數據或代碼。
如果1000:0中存放着重要的系統數據或代碼,“mov ds:[0],al”將其改寫,將引發錯誤。
比如下面的程序。
程序5.7
assume cs:code
code segment
mov ax,0
mov ds,ax
mov ds:[26h],ax
mov ax,4c00h
int 21h
code ends
end
將源程序編輯為p7.asm,編譯、連接后生成p7.exe,用Debug加載,跟蹤它的運行,如圖5.18所示。
圖5.18中,我們可以看到,源程序中的“mov ds:[26h],ax”被masm翻譯為機器碼"a3 26 00",而Debug將這個機器碼解釋為"mov [0026],ax"。
可見,匯編源程序中的匯編指令“mov ds:[26h],ax”和Debug中的匯編指令"mov [0026],ax"同義。
我們看一下“mov [0026],ax”的執行結果,如圖5.19所示。
圖5.19中,是在windows2000的DOS方式中,在Debug里執行“mov [0026],ax”的結果。
如果在實模式(即純DOS方式)下執行程序p7.exe,將會引起死機。產生這種結果的原因是0:0026處存放着重要的系統數據,而“mov [0026],ax”將其改寫。
可見,在不能確定一段內存空間中是否存放着重要的數據或代碼的時候,不能隨意向其中寫入內容。
同樣不能忘記,我們正在學習的是匯編語言,要通過它來獲得底層的編程體驗,理解計算機底層的基本工作機理。所以我們盡量直接對硬件編程,而不去理會操作系統。
注意,我們在純DOS方式(實模式)下,可以不理會DOS,直接用匯編語言去操作真實的硬件,因為運行在CPU實模式下的DOS,沒有能力對硬件系統進行全面、嚴格的管理。
但在Windows2000、Unix這些運行於CPU保護模式下的操作系統中,不理會操作系統,用匯編語言去操作真實的硬件,是根本不可能的。硬件已被這些操作系統利用CPU保護模式所提供的功能全面而嚴格地管理了。
在一般的PC機中,DOS方式下,DOS和其他合法的程序一般都不會使用0:200~0:2ff(00200h~002ffh)的256個字節的空間。
所以,我們使用這段空間是安全的。不過為了謹慎起見,在進入DOS后,我們可以先用Debug查看一下,如果0:200~0:2ff單元的內容都是0的話,則證明DOS和其他合法的程序沒有使用這里。
好了,我們總結一下:
(1)我們需要直接向一段內存中寫入內容;
(2)這段內存空間不應存放系統或其他程序的數據或代碼,否則寫入操作很可能引發錯誤;
(3)DOS方式下,一般情況,0:200~0:2ff空間中沒有系統或其他程序的數據或代碼;
(4)以后,我們需要直接向一段內存中寫入內容時,就使用0:200~0:2ff這段空間。