這是 x86 匯編連載系列第六篇文章,前五篇文章見文末。
一個小細(xì)節(jié)
從開始到現(xiàn)在我們接觸到了兩種匯編指令的編寫方式,一種是在 dosbox 上的 debug 模式下通過 debug -a 的方式來編寫,如下圖所示:
這種方式能讓你在 dosbox 中直接編寫匯編代碼,簡單直接,不需要寫偽指令,方便快捷。
還有一種方式需要我們在 dosbox 外部編寫匯編源文件,源文件中的代碼經(jīng)由 MASM 匯編編譯、LINK 指令鏈接后一種,如下圖所示:
乍一看這兩種方式編寫的匯編源代碼應(yīng)該都能正確的執(zhí)行,于是我們分別用兩種不同的方式寫下了
這幾條指令。這三條指令很簡單,我們的目的很明確,我們想把內(nèi)存地址為 ds:[1], ds[2], ds[3] 的數(shù)據(jù)分別送入 al,bl,cl 寄存器。下面我們執(zhí)行一下:
使用 debug 方式的截圖如下:
如圖所示,在使用 debug 方式中,"[ ]" 內(nèi)的指令會被直接當(dāng)做內(nèi)存地址進行 mov。
使用 masm 編譯器方式的截圖如下:
如圖所示,當(dāng)我們使用 MASM 進行編譯和鏈接后,[ ] 號中的 1 會被直接編譯為數(shù)值 01,而不是 [1] 這個內(nèi)存地址。這個是編譯規(guī)定,大家要記住這個細(xì)節(jié)。
也就是說,誠如 [idata] 這種形式,debug 和 masm 匯編器對其有不同的解釋,debug 認(rèn)為 [1] 中的就是一個內(nèi)存地址,而 masm 認(rèn)為 [1] 就是一個 idata 立即數(shù)。
話又說回來了,如果我們想在匯編源文件中表示內(nèi)存地址,該怎么辦呢?
這就需要借助一個寄存器了 --- bx。
比如下面這段匯編代碼
首先將 ds 寄存器設(shè)置為 2000 ,也就是 ds = 2000h,然后把 0 放入 bx 中,最后的 mov al,[bx] 就會默認(rèn)從內(nèi)存地址ds:[0] 處提取數(shù)據(jù)進行移動。
這樣當(dāng)然是可以的,不過仍然比較繁瑣,我們不想要每次 mov 內(nèi)存數(shù)據(jù)還要經(jīng)過 bx 中轉(zhuǎn),我們想要像 debug 那樣直接 mov ,該怎么做呢?其實也比較簡單,直接顯示指出段寄存器:[內(nèi)存偏移]即可。
看下面這段匯編代碼:
如果你想要通過 MASM 的方式來取得 ds:[0] 處內(nèi)存地址的話,就需要顯示指定段寄存器,如果沒有顯示指定的話,默認(rèn)按照 01 數(shù)值來處理。
所以我們可以總結(jié)一下上面所探討的內(nèi)容(基于 MASM 匯編編譯器下)
mov al,[0] :將數(shù)值 0 送入 al 寄存器中,(al) = 0。
mov al,ds:[0]:(al) = ((ds) * 16 + 0) , 將內(nèi)存單元中的數(shù)據(jù)送入 al 中,段地址為 ds。
mov al,[bx]:(al) = ((ds) * 16 + bx) , 將內(nèi)存單元的數(shù)據(jù)送入 al 中,段地址為 ds。
mov al,ds:[bx] :和 mov al,[bx] 含義相同
還可以更為精簡的總結(jié)一點:
MASM 匯編編譯器會將 [idata] 編譯為 idata,若想訪問內(nèi)存地址,則必須顯示指定段地址或者使用 bx 進行中轉(zhuǎn)。
上面這些內(nèi)容在本人其他文章中已經(jīng)涉及到了,不過講的不太細(xì)致,這篇文章算是細(xì)致的講了下。
段前綴
上面的內(nèi)容多次提到了一個名詞就是 段,段所表示的其實也是一段內(nèi)存空間,不過這種劃分的方式是由 CPU 來決定的,內(nèi)存并不會分為多個段。段的劃分是主要為了 CPU 能夠更方便的尋址,要想尋找段內(nèi)的每個地址和數(shù)據(jù),都需要有兩個概念:段基址和段內(nèi)偏移。
在匯編語言中,一般通過 [bx] 來給出偏移地址,它的段基礎(chǔ)在 ds 中,ds 是默認(rèn)的段寄存器。
不過,只有一個 ds 段顯然是無法應(yīng)對復(fù)雜程序的尋址方式的,所以還可能會有多個段,如下所示:
上面列舉了四種不同的段寄存器和尋址方式。
第一條指令把段基址為 ds,偏移地址為 bx 的內(nèi)存地址的內(nèi)容送入 ax ,長度為 2 個字節(jié)單元,也就是一個字,16 位。
第二條指令把段基址為 cs,偏移地址為 bx 的內(nèi)存地址的內(nèi)容送入 ax ,長度為 2 個字節(jié)單元,一個字,16 位。
第三條指令把段基址為 ss,偏移地址為 bx 的內(nèi)存地址的內(nèi)容送入 ax ,長度為 2 個字節(jié)單元,一個字,16 位。
第四條指令把段基址為 es,偏移地址為 bx 的內(nèi)存地址的內(nèi)容送入 ax ,長度為 2 個字節(jié)單元,一個字,16 位。
由于 ds、cs、ss、es 都是顯示指出的,所以 ds、cs、ss、es 又被稱為段前綴。
一段安全的存儲空間
我們寫出的程序經(jīng)過編譯連接后,會由操作系統(tǒng)分配內(nèi)存空間,我們并不知道哪些內(nèi)存空間是有用的,哪些內(nèi)存空間是保留的,哪些內(nèi)存空間是可以使用的,由于有些內(nèi)存空間存儲著重要的系統(tǒng)數(shù)據(jù)或代碼,所以我們最好不要隨意的向內(nèi)存空間寫入數(shù)據(jù),這是很危險的,比如下面這幾條指令:
之前為了方便,我們沒有判斷 1000:[0] 這個內(nèi)存空間有沒有存放重要代碼或數(shù)據(jù)就將數(shù)據(jù)寫入其中,這種做法是錯誤的,如果 1000:[0] 處剛好存放著文件系統(tǒng)的起始代碼,那么 mov ds:[0],al 就會將其改寫,引發(fā)系統(tǒng)崩潰。
再看一段程序:
我們編寫好代碼后,進行編譯鏈接,debug 這段代碼:
當(dāng)我們執(zhí)行完 mov ds:[26h],ax 后,說什么也執(zhí)行不下去了。
并不是我不想執(zhí)行了,而是系統(tǒng)不讓我執(zhí)行了,因為系統(tǒng)死機了。。。。。。大家可以試試。
所以,在不清楚這段內(nèi)存空間是干什么的時候,最不好要隨意向內(nèi)存空間寫入數(shù)據(jù)。由于內(nèi)存空間是由操作系統(tǒng)直接分配的,所以要想向一段內(nèi)存空間寫入數(shù)據(jù)的話,要使用操作系統(tǒng)給我們分配的內(nèi)存空間。
那么話又說回來了,操作系統(tǒng)給我們分配了哪些空間可以安全的寫入數(shù)據(jù)呢?
在一般的 PC 機,DOS 方式下,DOS 和其他合法程序一般都不會使用0:200 ~ 0:2ff(00200h ~ 002ffh)這段 256 個字節(jié)的空間,可以認(rèn)為這段內(nèi)存區(qū)域是安全的。
不過為了謹(jǐn)慎起見,我們寫入的時候,最好使用 debug -d 來看一下這段內(nèi)存區(qū)域有沒有存儲數(shù)據(jù)。
段前綴的使用
考慮一個問題,如何將內(nèi)存 ffff:0 ~ ffff:b 單元中的數(shù)據(jù)復(fù)制到 0:200 ~ 0:20b 單元中?
需要考慮以下幾點:
0:200 ~ 0:20b 其實就是 200:0 ~ 200:b ,這就是對同一段內(nèi)存空間的兩種不同的描述。
上面是兩段不同的內(nèi)存空間,所以需要兩個段基址,通過一個寄存器 dl 來進行中轉(zhuǎn),把 ffff:0 ~ ffff:b 地址空間的數(shù)據(jù)復(fù)制到 dl 中,然后把 dl 中的數(shù)據(jù)再復(fù)制到 0:200 ~ 0:20b 中。
一共復(fù)制 (b - 0) + 1 = 12 次。
開碼!
從上面代碼可以看到,我們顯示使用了兩種段前綴 ds 和 es ,這就是一個段前綴的使用案例。
我們分別將 0ffffh 和 200h 賦給了 ds 和 es 寄存器,然后設(shè)置 cx 循環(huán)次數(shù)為 12 次,s 是一個偽指令,表示循環(huán)的開始處,每個循環(huán)中都會把 0ffff:[bx] 中的數(shù)據(jù)賦值給 dl ,因為這是一個內(nèi)存地址,所以使用 8 位寄存器就可以接收,然后將 dl 中的數(shù)據(jù)賦值給 200:[bx] 處,再執(zhí)行循環(huán)。
審核編輯 :李倩
-
寄存器
+關(guān)注
關(guān)注
31文章
5369瀏覽量
121264 -
代碼
+關(guān)注
關(guān)注
30文章
4834瀏覽量
69114 -
編譯器
+關(guān)注
關(guān)注
1文章
1642瀏覽量
49312
原文標(biāo)題:簡單聊聊什么是段
文章出處:【微信號:cxuangoodjob,微信公眾號:程序員cxuan】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
簡單的聊聊什么是功率因數(shù)
![<b class='flag-5'>簡單</b>的<b class='flag-5'>聊聊</b>什么是功率因數(shù)](https://file1.elecfans.com/web2/M00/8C/C4/wKgaomSxDSKAZE9wAACS73W1DCk685.jpg)
為什么加入程序段code會反而減小呢?
聊聊CMSIS
聊聊對按鍵掃描軟件結(jié)構(gòu)的理解
聊聊基于STM32F103的紅外循跡避障小車的Proteus仿真
聊聊存儲器的相關(guān)知識
段碼LCD驅(qū)動簡單原理是什么
聊聊光敏傳感器最簡單的使用
簡單聊聊伺服電機啟動電流的問題
一文了解多個段的相關(guān)程序
聊聊半導(dǎo)體產(chǎn)品的8大封裝工藝
![<b class='flag-5'>聊聊</b>半導(dǎo)體產(chǎn)品的8大封裝工藝](https://file1.elecfans.com/web2/M00/C1/AA/wKgaomXYPtSABmDhAAAbebQUpDg108.png)
評論