匯編語言--寄存器(內(nèi)存訪問)
知識(shí)點(diǎn):內(nèi)存中字的存儲(chǔ)、DS和[address]、字的傳送、mov,add,sub指令、數(shù)據(jù)段、棧、CPU提供的棧機(jī)制、棧頂超界的問題、push,pop指令、棧段。
內(nèi)存中字的存儲(chǔ)
CPU中,用16位寄存器來存儲(chǔ)一個(gè)字。高8位存放高位字節(jié),低8位存放低位字節(jié)。
在內(nèi)存中存儲(chǔ)時(shí),由于內(nèi)存單元是字節(jié)單元(一個(gè)單元存放一個(gè)字節(jié)),則一個(gè)字要用兩個(gè)地址連接的內(nèi)存單元來存放,這個(gè)字的低位字節(jié)放在低地址單元中,高位字節(jié)存放在高地址單元中。
字單元:即存放一個(gè)字型數(shù)據(jù)(16位)的內(nèi)存單元,由兩個(gè)地址連續(xù)的內(nèi)存單元組成。高地址內(nèi)存單元中存放字型數(shù)據(jù)的高位字節(jié),低地址內(nèi)存單元中存放字型數(shù)據(jù)的低位字節(jié)。
任何兩個(gè)地址連續(xù)的內(nèi)存單元,N號(hào)單元和N+1號(hào)單元,可以將它們看成兩個(gè)內(nèi)存單元,也可看成一個(gè)地址為N的字單元中的高位字節(jié)單元和低位字節(jié)單元。
DS和[address]
CPU要讀寫一個(gè)內(nèi)存單元的時(shí)候,必須先給出這個(gè)內(nèi)存單元的地址,在8086CPU中,內(nèi)存地址由段地址和偏移地址組成。
8086CPU中有一個(gè)DS寄存器,通常用來存放要訪問數(shù)據(jù)的段地址。
mov指令,可完成三種傳送:
(1)將數(shù)據(jù)直接送入寄存器;
mov 寄存器名,數(shù)據(jù)
(2)將一個(gè)寄存器中的內(nèi)容送入另一個(gè)寄存器;
mov 寄存器名,寄存器名
(3)將一個(gè)內(nèi)存單元中的內(nèi)容送入一個(gè)寄存器中。
mov 寄存器,[內(nèi)存單元的偏移地址]
“[...]”表示一個(gè)內(nèi)存單元的偏移地址,我們知道,只有偏移地址是不能定位一個(gè)內(nèi)存單元的,那么內(nèi)存單元的段地址是多少呢?
指令執(zhí)行時(shí),8086CPU自動(dòng)取ds中的數(shù)據(jù)為內(nèi)存單元的段地址。
所以,我們需要根據(jù)情況,改變ds中的數(shù)據(jù)。
比如 mov ds, 1000H
但是,8086CPU不支持將數(shù)據(jù)直接送入段寄存器的操作,ds是一個(gè)段寄存器,所以mov ds, 1000H這條指令是非法的。
那么如何將1000H送入ds呢?只好用一個(gè)寄存器來進(jìn)行中轉(zhuǎn),即先將1000H送入一個(gè)一般的寄存器,如bx,再將bx中的內(nèi)容送入ds。
怎樣將數(shù)據(jù)從寄存器送入內(nèi)存單元?
從內(nèi)存單元到寄存器的格式是:mov 寄存器名,內(nèi)存單元地址
從寄存器到內(nèi)存單元?jiǎng)t是:mov 內(nèi)存單元地址,寄存器名。
將al中的數(shù)據(jù)送入內(nèi)存單元10000H。
10000H可表示為1000:0,用ds存放段地址1000H,偏移地址是0,則:mov [0], al可完成從al到10000H的數(shù)據(jù)傳送。
mov bx, 1000H
mov ds, bx
mov [0], al
字的傳送
mov指令在寄存器和內(nèi)存之間進(jìn)行字節(jié)型數(shù)據(jù)的傳送。
因?yàn)?086CPU是16位結(jié)構(gòu),有16根數(shù)據(jù)線,所以,可以一次性傳送16位的數(shù)據(jù),也就是說可以一次性傳送一個(gè)字。
我們只要在mov指令中給出16位的寄存器就可以進(jìn)行16位數(shù)據(jù)的傳送了。
mov, add, sub指令
mov指令目前可以有以下幾種形式:
mov 寄存器,數(shù)據(jù) mov ax,8
mov 寄存器,寄存器 mov ax,bx
mov 寄存器,內(nèi)存單元 mov ax,[0]
mov 內(nèi)存單元,寄存器 mov [0],ax
mov 段寄存器,寄存器 mov ds,ax
add和sub指令同mov一樣,都有兩個(gè)操作對(duì)象,它們也可以有上面的幾種形式。
這些形式中有些,兩個(gè)操作數(shù)可以相互交換操作,這些需要用debug中的a命令和t命令多實(shí)踐實(shí)踐。
數(shù)據(jù)段
前面講過,對(duì)于8086CPU,在編程時(shí),我們可以根據(jù)需要,將一組內(nèi)在單元定義為一個(gè)段。
我們可以將一組長(zhǎng)度為N(N≤64K)、地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元當(dāng)作專門存儲(chǔ)數(shù)據(jù)的內(nèi)存空間,從而定義了一個(gè)數(shù)據(jù)段。
比如我們用:123B0H~123BAH這段內(nèi)存空間來存放數(shù)據(jù),我們就可以認(rèn)為,123B0H~123BAH這段內(nèi)存是一個(gè)數(shù)據(jù)段,它的段地址為123B,長(zhǎng)度為10字節(jié)。
如何訪問數(shù)據(jù)段中的數(shù)據(jù)呢?
將一段內(nèi)存當(dāng)作數(shù)據(jù)段,是我們?cè)诰幊虝r(shí)的一種安排,我們可以在具體操作的時(shí)候,用ds存放數(shù)據(jù)段的段地址,再根據(jù)需要,用相關(guān)指令訪問數(shù)據(jù)段中的具體單元。
比如,我們將123B0H~123BAH的內(nèi)存單元定義為數(shù)據(jù)段。
我們現(xiàn)在要累加這個(gè)數(shù)據(jù)段中的前3個(gè)單元中的數(shù)據(jù)。
mov ax, 123BH
mov ds, ax ;將123BH送入ds中,作為數(shù)據(jù)段的段地址。
mov al, 0 ;用al存放累加結(jié)果,先把它清零。
add al, [0] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為0)中的數(shù)值加到al中。
add al, [1] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為1)中的數(shù)值加到al中。
add al, [2] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為2)中的數(shù)值加到al中。
累加數(shù)據(jù)段中的前3個(gè)字型數(shù)據(jù)。
mov ax, 123BH
mov ds, ax ;將123BH送入ds中,作為數(shù)據(jù)段的段地址。
mov ax, 0 ;用ax存放累加結(jié)果,先把它清零。
add al, [0] ;將數(shù)據(jù)段第一個(gè)字(偏移地址為0)加到ax中。
add al, [2] ;將數(shù)據(jù)段第一個(gè)字(偏移地址為2)加到ax中。
add al, [4] ;將數(shù)據(jù)段第一個(gè)字(偏移地址為4)加到ax中。
小結(jié)
(1)字在內(nèi)存中存儲(chǔ)時(shí),要用兩個(gè)地址連續(xù)的內(nèi)存單元來存放,字的低位字節(jié)存放在低地址單元中,高位字節(jié)存放在高地址單元中。
(2)用mov指令要訪問內(nèi)存單元,可以在mov指令中只給出單元的偏移地址。此時(shí),段地址默認(rèn)在DS寄存器中。
(3)[address]表示一個(gè)偏移地址為address的內(nèi)存單元。
(4)在內(nèi)存和寄存器之間傳送字型數(shù)據(jù)時(shí),高地址單元和高8位寄存器、低地址單元和低8位寄存器相對(duì)應(yīng)。
(5)mov, add, sub是具有兩個(gè)操作對(duì)象的指令,jmp是具有一個(gè)操作對(duì)象的指令。
(6)可以根據(jù)自己的推測(cè),在Debug中實(shí)驗(yàn)指令的新格式。
棧
在這里,我們對(duì)棧的研究?jī)H限于這個(gè)角度:棧是一種具有特殊的訪問方式的存儲(chǔ)空間。
它的特殊性就在于,最后進(jìn)入這個(gè)空間的數(shù)據(jù),最先出去。
棧有兩個(gè)基本的操作:入棧和出棧。
入棧就是將一個(gè)新的元素到棧頂,出棧就是從棧頂取出一個(gè)元素。
棧頂?shù)脑乜偸亲詈笕霔#枰鰲:蜁r(shí),又最先被從棧中取出。
棧的這種操作規(guī)則被稱為:LIFO(Last In First Out, 后進(jìn)先出)。
CPU提供的棧機(jī)制
現(xiàn)今的CPU中都有棧的設(shè)計(jì),8086CPU也不例外。
8086CPU提供相關(guān)的指令來以棧的方式訪問內(nèi)存空間。
這意味著,我們?cè)诨?086CPU編程的時(shí)候,可以將一段內(nèi)存當(dāng)作棧來使用。
8086CPU提供入棧和出棧指令,最基本的兩個(gè)是PUSH(入棧)和POP(出棧)。
比如:push ax 表示將寄存器ax中的數(shù)據(jù)據(jù)送入棧中,pop ax表示從棧頂取出數(shù)據(jù)送入ax。
8086CPU的入棧和出棧操作都是以字為單位進(jìn)行的。
注意,字型數(shù)據(jù)用兩個(gè)內(nèi)存存儲(chǔ)單元存放,高地址單元放高8位,低地址單元放低8位。
8086CPU中,有兩個(gè)寄存器,段寄存器SS和寄存器SP,棧頂?shù)亩蔚刂反娣旁赟S中,偏移地址存放在SP中。
任意時(shí)刻,SS:SP指向棧頂元素。push指令和pop指令執(zhí)行時(shí),CPU從SS和SP中得到棧頂?shù)牡刂贰?br>
push ax的執(zhí)行:
1)SP=SP-2, SS:SP指向當(dāng)前棧頂前面的單元,以當(dāng)前棧頂前面的單元為新的棧頂;
2)將ax中的內(nèi)容送入SS:SP指向的內(nèi)存單元處,SS:SP此時(shí)指向新棧頂。
pop ax的執(zhí)行過程和push ax剛好相反:
1)將SS:SP指向的內(nèi)存單元處的數(shù)據(jù)送入ax中;
2)SP=SP+2,SS:SP指向當(dāng)前棧頂下面的單元,以當(dāng)前棧頂下面的單元為新的棧頂。
棧頂超界的問題
8086CPU用SS和SP指示棧頂?shù)牡刂罚⑻峁﹑ush和pop指令實(shí)現(xiàn)入棧和出棧。
但是,SS和SP只是記錄了棧頂?shù)牡刂罚揽縎S和SP可以保證在入棧和出棧時(shí)找到棧頂,可是,如何能夠保證在入棧、出棧時(shí),棧頂不會(huì)超出棧空間?
當(dāng)棧滿的時(shí)候再使用push指令入棧,或棧空的時(shí)候再使用pop指令出棧,都將發(fā)生棧頂超界問題。
棧頂超界是危險(xiǎn)的,因?yàn)槲覀兗热粚⒁欢慰臻g安排為棧,那么在棧空間之外的空間里很可能存放了具有其他用途的數(shù)據(jù)、代碼等,這些數(shù)據(jù)、代碼可能是我們自己程序的,也可能是別的程序中的(畢竟一個(gè)計(jì)算機(jī)系統(tǒng)中并不是只有我們自己的程序在運(yùn)行)。但是由于我們?cè)谌霔3鰲r(shí)的不小心,而將這些數(shù)據(jù)、代碼意外地改寫,將會(huì)引發(fā)一連串的錯(cuò)誤。
8086CPU不保證我們對(duì)棧的操作不會(huì)超界。
也就是說,8086CPU只知道棧頂在何處(由SS:SP指示),而不知道讀者安排的棧空間有多大。
我們?cè)诰幊痰臅r(shí)候要自己操心棧頂超界的問題,要根據(jù)可能用到的最大棧空間,來安排棧的大小,防止入棧的數(shù)據(jù)太多而導(dǎo)致的超界;執(zhí)行出棧操作的時(shí)候也要注意,以防棧空的時(shí)候繼續(xù)出棧而導(dǎo)致的超界。
push、pop指令
push和pop指令是可以在寄存器和內(nèi)存(棧空間當(dāng)然也是內(nèi)存空間的一部分,它只是一段可以以一種特殊的方式進(jìn)行訪問的內(nèi)存空間。)之間傳送數(shù)據(jù)的。
push和pop指令的格式可以是如下形式:
push 寄存器 ;將一個(gè)寄存器中的數(shù)據(jù)入棧
pop 寄存器 ;出棧,用一個(gè)寄存器接收出棧的數(shù)據(jù)
push 段寄存器 ;將一個(gè)段寄存器中的數(shù)據(jù)入棧
pop 段寄存器 ;出棧,用一個(gè)段寄存器接收出棧的數(shù)據(jù)
push和pop也可以在內(nèi)存單元和內(nèi)存單元之間傳送數(shù)據(jù)
push 內(nèi)存單元 ;將一個(gè)內(nèi)存單元處的字入棧(注意,棧操作都是以字為單位)
pop 內(nèi)存單元 ;出棧,用一個(gè)內(nèi)存單元接收出棧的數(shù)據(jù)
指令執(zhí)行時(shí),CPU要知道內(nèi)存單元的地址,可以在push、pop指令只給出內(nèi)存單元的偏移地址,段地址在執(zhí)行指令時(shí),CPU從ds中取得。
棧的綜述
1)8086CPU提供了棧操作機(jī)制,方案如下:
在SS、SP中存放棧頂?shù)亩蔚刂泛推频刂罚?br> 提供入棧和出棧指令,它們根據(jù)SS:SP指示的地址,按照棧的方式訪問內(nèi)存單元。
2)push指令的執(zhí)行步驟:a、SP=SP-2;b、向SS:SP指向的字單元中送入數(shù)據(jù)。
3)pop指令的執(zhí)行步驟:a、從SS:SP指向的字單元中讀取數(shù)據(jù);b、SP=SP+2。
4)任意時(shí)刻,SS:SP指向棧頂元素。
5)8086CPU只記錄棧頂,棧空間的大小我們要自己管理。
6)用棧來暫存以后需要恢復(fù)的寄存器的內(nèi)容時(shí),寄存器出棧的順序和入棧的順序相反。
7)push、pop實(shí)質(zhì)上是一種內(nèi)存?zhèn)魉椭噶睿⒁馑鼈兊撵`活應(yīng)用。
棧是一種非常重要的機(jī)制,一定要深入理解,靈活掌握。
棧段
前面講過,對(duì)于8086CPU,在編程時(shí),我們可以根據(jù)需要,將一組內(nèi)存單元定義為一個(gè)段。我們可以將長(zhǎng)度為N(N<=64K)的一組地址連接、起始地址為16的倍數(shù)的內(nèi)存單元,當(dāng)作棧空間來用,從而定義了一個(gè)棧段。
比如,我們將10010H~1001FH這段長(zhǎng)度為16字節(jié)的內(nèi)存空間當(dāng)作棧來用,以棧的方式進(jìn)行訪問。這段空間就可以稱為一個(gè)棧段,段地址為1000H,大小為16字節(jié)。
將一段內(nèi)存當(dāng)作棧段,僅僅是我們?cè)诰幊虝r(shí)的一種安排,CPU并不會(huì)由于這種安排,就在執(zhí)行push、pop等棧操作指令時(shí)就自動(dòng)地將我們定義的棧段當(dāng)作棧空間來訪問。
如何使得如push、pop等棧操作指令訪問我們定義的棧段呢?就是要將SS:SP指向我們定義的棧段。
任意時(shí)刻,SS:SP指向棧頂元素,當(dāng)棧為空的時(shí)候,棧中沒有元素,也就不存在棧頂元素,所以SS:SP只能指向棧的最底部單元下面的單元,該單元的地址為棧最底部的字單元的地址+2。
如果將10000H~1FFFFH這段空間當(dāng)作棧段,棧最底部字單元的地址為1000:FFFE,所以棧空時(shí),SP=0000H。
一個(gè)棧段最大可以設(shè)為多少?
從棧操作指令所完成的功能的角度上來看,push、pop等指令在執(zhí)行的時(shí)候只修改SP,所以棧頂?shù)淖兓秶?~FFFFH,人棧空時(shí)候的SP=0,一直壓棧,直到棧滿時(shí)SP=0;如果再次壓棧,棧頂將循環(huán),覆蓋了原來?xiàng)V械膬?nèi)容。所以一個(gè)棧段的容量最大為64KB。
段的綜述
我們可以將一段內(nèi)存定義為一個(gè)段,用一個(gè)段地址指示段,用偏移地址訪問段內(nèi)的單元。這完全是我們自己的安排。
我們可以用一個(gè)段豐放數(shù)據(jù),將它定義為“數(shù)據(jù)段”;
我們可以用一個(gè)段存放代碼,將它定義為“代碼段”;
我們可以用一個(gè)段當(dāng)作棧,將它定義為“棧段”;
我們可以這樣安排,但若要讓CPU按照我們的安排來訪問這些段,就要:
對(duì)于數(shù)據(jù)段,將它的段地址放在DS中,用mov、add、sub等訪問內(nèi)存單元的指令時(shí),CPU就將我們定義的數(shù)據(jù)段中的內(nèi)容當(dāng)作數(shù)據(jù)來訪問;
對(duì)于代碼段,將它的段地址放在CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執(zhí)行我們定義的代碼段中的指令;
對(duì)于棧段,將它的段地址放在SS中,將棧頂單元的偏移地址放在SP中,這樣CPU在需要進(jìn)行棧操作的時(shí)候,比如執(zhí)行push、pop指令等,就將我們定義的棧段當(dāng)作棧空間來用。
可見,不管我們?nèi)绾伟才牛珻PU將的某段內(nèi)容當(dāng)作代碼,是為因?yàn)镃S:IP指向了那里;CPU將某段內(nèi)存當(dāng)作棧,是為因SS:SP指向了那里。
我們一定要清楚,什么是我們的安排,以及如何讓CPU按我們的安排行事。要非常地清楚CPU的工作機(jī)理,才能在控制CPU來按照我們的安排運(yùn)行的時(shí)候做到游刃有余。
比如我們將10000H~1001FH安排為代碼段,并在這里存儲(chǔ)如下代碼:
mov ax,1000H
mov ss,ax
mov sp,0020H ;初始化棧頂
mov ax,cs
mov ds,ax ;設(shè)置數(shù)據(jù)段段地址
mov ax,[0]
mov ax,[2]
mov bx,[4]
mov bx,[6]
push ax
push bx
pop bx
pop ax
設(shè)置CS=1000H,IP=0,這段代碼將得到執(zhí)行,可以看到,在這段代碼中,我們雙將10000H~1001FH安排為棧段和數(shù)據(jù)段,10000H~1001FH這段內(nèi)存,即是代碼段,又是棧段和數(shù)據(jù)段。
一段內(nèi)存,可騍既是代碼的存儲(chǔ)空間,又是數(shù)據(jù)的存儲(chǔ)空間,還可以是棧空間,也可以什么也不是。
關(guān)鍵在于CPU中寄存器的設(shè)置,即:CS、IP、SS、SP、DS的指向。
內(nèi)存中字的存儲(chǔ)
CPU中,用16位寄存器來存儲(chǔ)一個(gè)字。高8位存放高位字節(jié),低8位存放低位字節(jié)。
在內(nèi)存中存儲(chǔ)時(shí),由于內(nèi)存單元是字節(jié)單元(一個(gè)單元存放一個(gè)字節(jié)),則一個(gè)字要用兩個(gè)地址連接的內(nèi)存單元來存放,這個(gè)字的低位字節(jié)放在低地址單元中,高位字節(jié)存放在高地址單元中。
字單元:即存放一個(gè)字型數(shù)據(jù)(16位)的內(nèi)存單元,由兩個(gè)地址連續(xù)的內(nèi)存單元組成。高地址內(nèi)存單元中存放字型數(shù)據(jù)的高位字節(jié),低地址內(nèi)存單元中存放字型數(shù)據(jù)的低位字節(jié)。
任何兩個(gè)地址連續(xù)的內(nèi)存單元,N號(hào)單元和N+1號(hào)單元,可以將它們看成兩個(gè)內(nèi)存單元,也可看成一個(gè)地址為N的字單元中的高位字節(jié)單元和低位字節(jié)單元。
DS和[address]
CPU要讀寫一個(gè)內(nèi)存單元的時(shí)候,必須先給出這個(gè)內(nèi)存單元的地址,在8086CPU中,內(nèi)存地址由段地址和偏移地址組成。
8086CPU中有一個(gè)DS寄存器,通常用來存放要訪問數(shù)據(jù)的段地址。
mov指令,可完成三種傳送:
(1)將數(shù)據(jù)直接送入寄存器;
mov 寄存器名,數(shù)據(jù)
(2)將一個(gè)寄存器中的內(nèi)容送入另一個(gè)寄存器;
mov 寄存器名,寄存器名
(3)將一個(gè)內(nèi)存單元中的內(nèi)容送入一個(gè)寄存器中。
mov 寄存器,[內(nèi)存單元的偏移地址]
“[...]”表示一個(gè)內(nèi)存單元的偏移地址,我們知道,只有偏移地址是不能定位一個(gè)內(nèi)存單元的,那么內(nèi)存單元的段地址是多少呢?
指令執(zhí)行時(shí),8086CPU自動(dòng)取ds中的數(shù)據(jù)為內(nèi)存單元的段地址。
所以,我們需要根據(jù)情況,改變ds中的數(shù)據(jù)。
比如 mov ds, 1000H
但是,8086CPU不支持將數(shù)據(jù)直接送入段寄存器的操作,ds是一個(gè)段寄存器,所以mov ds, 1000H這條指令是非法的。
那么如何將1000H送入ds呢?只好用一個(gè)寄存器來進(jìn)行中轉(zhuǎn),即先將1000H送入一個(gè)一般的寄存器,如bx,再將bx中的內(nèi)容送入ds。
怎樣將數(shù)據(jù)從寄存器送入內(nèi)存單元?
從內(nèi)存單元到寄存器的格式是:mov 寄存器名,內(nèi)存單元地址
從寄存器到內(nèi)存單元?jiǎng)t是:mov 內(nèi)存單元地址,寄存器名。
將al中的數(shù)據(jù)送入內(nèi)存單元10000H。
10000H可表示為1000:0,用ds存放段地址1000H,偏移地址是0,則:mov [0], al可完成從al到10000H的數(shù)據(jù)傳送。
mov bx, 1000H
mov ds, bx
mov [0], al
字的傳送
mov指令在寄存器和內(nèi)存之間進(jìn)行字節(jié)型數(shù)據(jù)的傳送。
因?yàn)?086CPU是16位結(jié)構(gòu),有16根數(shù)據(jù)線,所以,可以一次性傳送16位的數(shù)據(jù),也就是說可以一次性傳送一個(gè)字。
我們只要在mov指令中給出16位的寄存器就可以進(jìn)行16位數(shù)據(jù)的傳送了。
mov, add, sub指令
mov指令目前可以有以下幾種形式:
mov 寄存器,數(shù)據(jù) mov ax,8
mov 寄存器,寄存器 mov ax,bx
mov 寄存器,內(nèi)存單元 mov ax,[0]
mov 內(nèi)存單元,寄存器 mov [0],ax
mov 段寄存器,寄存器 mov ds,ax
add和sub指令同mov一樣,都有兩個(gè)操作對(duì)象,它們也可以有上面的幾種形式。
這些形式中有些,兩個(gè)操作數(shù)可以相互交換操作,這些需要用debug中的a命令和t命令多實(shí)踐實(shí)踐。
數(shù)據(jù)段
前面講過,對(duì)于8086CPU,在編程時(shí),我們可以根據(jù)需要,將一組內(nèi)在單元定義為一個(gè)段。
我們可以將一組長(zhǎng)度為N(N≤64K)、地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元當(dāng)作專門存儲(chǔ)數(shù)據(jù)的內(nèi)存空間,從而定義了一個(gè)數(shù)據(jù)段。
比如我們用:123B0H~123BAH這段內(nèi)存空間來存放數(shù)據(jù),我們就可以認(rèn)為,123B0H~123BAH這段內(nèi)存是一個(gè)數(shù)據(jù)段,它的段地址為123B,長(zhǎng)度為10字節(jié)。
如何訪問數(shù)據(jù)段中的數(shù)據(jù)呢?
將一段內(nèi)存當(dāng)作數(shù)據(jù)段,是我們?cè)诰幊虝r(shí)的一種安排,我們可以在具體操作的時(shí)候,用ds存放數(shù)據(jù)段的段地址,再根據(jù)需要,用相關(guān)指令訪問數(shù)據(jù)段中的具體單元。
比如,我們將123B0H~123BAH的內(nèi)存單元定義為數(shù)據(jù)段。
我們現(xiàn)在要累加這個(gè)數(shù)據(jù)段中的前3個(gè)單元中的數(shù)據(jù)。
mov ax, 123BH
mov ds, ax ;將123BH送入ds中,作為數(shù)據(jù)段的段地址。
mov al, 0 ;用al存放累加結(jié)果,先把它清零。
add al, [0] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為0)中的數(shù)值加到al中。
add al, [1] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為1)中的數(shù)值加到al中。
add al, [2] ;將數(shù)據(jù)段第一個(gè)單元(偏移地址為2)中的數(shù)值加到al中。
累加數(shù)據(jù)段中的前3個(gè)字型數(shù)據(jù)。
mov ax, 123BH
mov ds, ax ;將123BH送入ds中,作為數(shù)據(jù)段的段地址。
mov ax, 0 ;用ax存放累加結(jié)果,先把它清零。
add al, [0] ;將數(shù)據(jù)段第一個(gè)字(偏移地址為0)加到ax中。
add al, [2] ;將數(shù)據(jù)段第一個(gè)字(偏移地址為2)加到ax中。
add al, [4] ;將數(shù)據(jù)段第一個(gè)字(偏移地址為4)加到ax中。
小結(jié)
(1)字在內(nèi)存中存儲(chǔ)時(shí),要用兩個(gè)地址連續(xù)的內(nèi)存單元來存放,字的低位字節(jié)存放在低地址單元中,高位字節(jié)存放在高地址單元中。
(2)用mov指令要訪問內(nèi)存單元,可以在mov指令中只給出單元的偏移地址。此時(shí),段地址默認(rèn)在DS寄存器中。
(3)[address]表示一個(gè)偏移地址為address的內(nèi)存單元。
(4)在內(nèi)存和寄存器之間傳送字型數(shù)據(jù)時(shí),高地址單元和高8位寄存器、低地址單元和低8位寄存器相對(duì)應(yīng)。
(5)mov, add, sub是具有兩個(gè)操作對(duì)象的指令,jmp是具有一個(gè)操作對(duì)象的指令。
(6)可以根據(jù)自己的推測(cè),在Debug中實(shí)驗(yàn)指令的新格式。
棧
在這里,我們對(duì)棧的研究?jī)H限于這個(gè)角度:棧是一種具有特殊的訪問方式的存儲(chǔ)空間。
它的特殊性就在于,最后進(jìn)入這個(gè)空間的數(shù)據(jù),最先出去。
棧有兩個(gè)基本的操作:入棧和出棧。
入棧就是將一個(gè)新的元素到棧頂,出棧就是從棧頂取出一個(gè)元素。
棧頂?shù)脑乜偸亲詈笕霔#枰鰲:蜁r(shí),又最先被從棧中取出。
棧的這種操作規(guī)則被稱為:LIFO(Last In First Out, 后進(jìn)先出)。
CPU提供的棧機(jī)制
現(xiàn)今的CPU中都有棧的設(shè)計(jì),8086CPU也不例外。
8086CPU提供相關(guān)的指令來以棧的方式訪問內(nèi)存空間。
這意味著,我們?cè)诨?086CPU編程的時(shí)候,可以將一段內(nèi)存當(dāng)作棧來使用。
8086CPU提供入棧和出棧指令,最基本的兩個(gè)是PUSH(入棧)和POP(出棧)。
比如:push ax 表示將寄存器ax中的數(shù)據(jù)據(jù)送入棧中,pop ax表示從棧頂取出數(shù)據(jù)送入ax。
8086CPU的入棧和出棧操作都是以字為單位進(jìn)行的。
注意,字型數(shù)據(jù)用兩個(gè)內(nèi)存存儲(chǔ)單元存放,高地址單元放高8位,低地址單元放低8位。
8086CPU中,有兩個(gè)寄存器,段寄存器SS和寄存器SP,棧頂?shù)亩蔚刂反娣旁赟S中,偏移地址存放在SP中。
任意時(shí)刻,SS:SP指向棧頂元素。push指令和pop指令執(zhí)行時(shí),CPU從SS和SP中得到棧頂?shù)牡刂贰?br>
push ax的執(zhí)行:
1)SP=SP-2, SS:SP指向當(dāng)前棧頂前面的單元,以當(dāng)前棧頂前面的單元為新的棧頂;
2)將ax中的內(nèi)容送入SS:SP指向的內(nèi)存單元處,SS:SP此時(shí)指向新棧頂。
pop ax的執(zhí)行過程和push ax剛好相反:
1)將SS:SP指向的內(nèi)存單元處的數(shù)據(jù)送入ax中;
2)SP=SP+2,SS:SP指向當(dāng)前棧頂下面的單元,以當(dāng)前棧頂下面的單元為新的棧頂。
棧頂超界的問題
8086CPU用SS和SP指示棧頂?shù)牡刂罚⑻峁﹑ush和pop指令實(shí)現(xiàn)入棧和出棧。
但是,SS和SP只是記錄了棧頂?shù)牡刂罚揽縎S和SP可以保證在入棧和出棧時(shí)找到棧頂,可是,如何能夠保證在入棧、出棧時(shí),棧頂不會(huì)超出棧空間?
當(dāng)棧滿的時(shí)候再使用push指令入棧,或棧空的時(shí)候再使用pop指令出棧,都將發(fā)生棧頂超界問題。
棧頂超界是危險(xiǎn)的,因?yàn)槲覀兗热粚⒁欢慰臻g安排為棧,那么在棧空間之外的空間里很可能存放了具有其他用途的數(shù)據(jù)、代碼等,這些數(shù)據(jù)、代碼可能是我們自己程序的,也可能是別的程序中的(畢竟一個(gè)計(jì)算機(jī)系統(tǒng)中并不是只有我們自己的程序在運(yùn)行)。但是由于我們?cè)谌霔3鰲r(shí)的不小心,而將這些數(shù)據(jù)、代碼意外地改寫,將會(huì)引發(fā)一連串的錯(cuò)誤。
8086CPU不保證我們對(duì)棧的操作不會(huì)超界。
也就是說,8086CPU只知道棧頂在何處(由SS:SP指示),而不知道讀者安排的棧空間有多大。
我們?cè)诰幊痰臅r(shí)候要自己操心棧頂超界的問題,要根據(jù)可能用到的最大棧空間,來安排棧的大小,防止入棧的數(shù)據(jù)太多而導(dǎo)致的超界;執(zhí)行出棧操作的時(shí)候也要注意,以防棧空的時(shí)候繼續(xù)出棧而導(dǎo)致的超界。
push、pop指令
push和pop指令是可以在寄存器和內(nèi)存(棧空間當(dāng)然也是內(nèi)存空間的一部分,它只是一段可以以一種特殊的方式進(jìn)行訪問的內(nèi)存空間。)之間傳送數(shù)據(jù)的。
push和pop指令的格式可以是如下形式:
push 寄存器 ;將一個(gè)寄存器中的數(shù)據(jù)入棧
pop 寄存器 ;出棧,用一個(gè)寄存器接收出棧的數(shù)據(jù)
push 段寄存器 ;將一個(gè)段寄存器中的數(shù)據(jù)入棧
pop 段寄存器 ;出棧,用一個(gè)段寄存器接收出棧的數(shù)據(jù)
push和pop也可以在內(nèi)存單元和內(nèi)存單元之間傳送數(shù)據(jù)
push 內(nèi)存單元 ;將一個(gè)內(nèi)存單元處的字入棧(注意,棧操作都是以字為單位)
pop 內(nèi)存單元 ;出棧,用一個(gè)內(nèi)存單元接收出棧的數(shù)據(jù)
指令執(zhí)行時(shí),CPU要知道內(nèi)存單元的地址,可以在push、pop指令只給出內(nèi)存單元的偏移地址,段地址在執(zhí)行指令時(shí),CPU從ds中取得。
棧的綜述
1)8086CPU提供了棧操作機(jī)制,方案如下:
在SS、SP中存放棧頂?shù)亩蔚刂泛推频刂罚?br> 提供入棧和出棧指令,它們根據(jù)SS:SP指示的地址,按照棧的方式訪問內(nèi)存單元。
2)push指令的執(zhí)行步驟:a、SP=SP-2;b、向SS:SP指向的字單元中送入數(shù)據(jù)。
3)pop指令的執(zhí)行步驟:a、從SS:SP指向的字單元中讀取數(shù)據(jù);b、SP=SP+2。
4)任意時(shí)刻,SS:SP指向棧頂元素。
5)8086CPU只記錄棧頂,棧空間的大小我們要自己管理。
6)用棧來暫存以后需要恢復(fù)的寄存器的內(nèi)容時(shí),寄存器出棧的順序和入棧的順序相反。
7)push、pop實(shí)質(zhì)上是一種內(nèi)存?zhèn)魉椭噶睿⒁馑鼈兊撵`活應(yīng)用。
棧是一種非常重要的機(jī)制,一定要深入理解,靈活掌握。
棧段
前面講過,對(duì)于8086CPU,在編程時(shí),我們可以根據(jù)需要,將一組內(nèi)存單元定義為一個(gè)段。我們可以將長(zhǎng)度為N(N<=64K)的一組地址連接、起始地址為16的倍數(shù)的內(nèi)存單元,當(dāng)作棧空間來用,從而定義了一個(gè)棧段。
比如,我們將10010H~1001FH這段長(zhǎng)度為16字節(jié)的內(nèi)存空間當(dāng)作棧來用,以棧的方式進(jìn)行訪問。這段空間就可以稱為一個(gè)棧段,段地址為1000H,大小為16字節(jié)。
將一段內(nèi)存當(dāng)作棧段,僅僅是我們?cè)诰幊虝r(shí)的一種安排,CPU并不會(huì)由于這種安排,就在執(zhí)行push、pop等棧操作指令時(shí)就自動(dòng)地將我們定義的棧段當(dāng)作棧空間來訪問。
如何使得如push、pop等棧操作指令訪問我們定義的棧段呢?就是要將SS:SP指向我們定義的棧段。
任意時(shí)刻,SS:SP指向棧頂元素,當(dāng)棧為空的時(shí)候,棧中沒有元素,也就不存在棧頂元素,所以SS:SP只能指向棧的最底部單元下面的單元,該單元的地址為棧最底部的字單元的地址+2。
如果將10000H~1FFFFH這段空間當(dāng)作棧段,棧最底部字單元的地址為1000:FFFE,所以棧空時(shí),SP=0000H。
一個(gè)棧段最大可以設(shè)為多少?
從棧操作指令所完成的功能的角度上來看,push、pop等指令在執(zhí)行的時(shí)候只修改SP,所以棧頂?shù)淖兓秶?~FFFFH,人棧空時(shí)候的SP=0,一直壓棧,直到棧滿時(shí)SP=0;如果再次壓棧,棧頂將循環(huán),覆蓋了原來?xiàng)V械膬?nèi)容。所以一個(gè)棧段的容量最大為64KB。
段的綜述
我們可以將一段內(nèi)存定義為一個(gè)段,用一個(gè)段地址指示段,用偏移地址訪問段內(nèi)的單元。這完全是我們自己的安排。
我們可以用一個(gè)段豐放數(shù)據(jù),將它定義為“數(shù)據(jù)段”;
我們可以用一個(gè)段存放代碼,將它定義為“代碼段”;
我們可以用一個(gè)段當(dāng)作棧,將它定義為“棧段”;
我們可以這樣安排,但若要讓CPU按照我們的安排來訪問這些段,就要:
對(duì)于數(shù)據(jù)段,將它的段地址放在DS中,用mov、add、sub等訪問內(nèi)存單元的指令時(shí),CPU就將我們定義的數(shù)據(jù)段中的內(nèi)容當(dāng)作數(shù)據(jù)來訪問;
對(duì)于代碼段,將它的段地址放在CS中,將段中第一條指令的偏移地址放在IP中,這樣CPU就將執(zhí)行我們定義的代碼段中的指令;
對(duì)于棧段,將它的段地址放在SS中,將棧頂單元的偏移地址放在SP中,這樣CPU在需要進(jìn)行棧操作的時(shí)候,比如執(zhí)行push、pop指令等,就將我們定義的棧段當(dāng)作棧空間來用。
可見,不管我們?nèi)绾伟才牛珻PU將的某段內(nèi)容當(dāng)作代碼,是為因?yàn)镃S:IP指向了那里;CPU將某段內(nèi)存當(dāng)作棧,是為因SS:SP指向了那里。
我們一定要清楚,什么是我們的安排,以及如何讓CPU按我們的安排行事。要非常地清楚CPU的工作機(jī)理,才能在控制CPU來按照我們的安排運(yùn)行的時(shí)候做到游刃有余。
比如我們將10000H~1001FH安排為代碼段,并在這里存儲(chǔ)如下代碼:
mov ax,1000H
mov ss,ax
mov sp,0020H ;初始化棧頂
mov ax,cs
mov ds,ax ;設(shè)置數(shù)據(jù)段段地址
mov ax,[0]
mov ax,[2]
mov bx,[4]
mov bx,[6]
push ax
push bx
pop bx
pop ax
設(shè)置CS=1000H,IP=0,這段代碼將得到執(zhí)行,可以看到,在這段代碼中,我們雙將10000H~1001FH安排為棧段和數(shù)據(jù)段,10000H~1001FH這段內(nèi)存,即是代碼段,又是棧段和數(shù)據(jù)段。
一段內(nèi)存,可騍既是代碼的存儲(chǔ)空間,又是數(shù)據(jù)的存儲(chǔ)空間,還可以是棧空間,也可以什么也不是。
關(guān)鍵在于CPU中寄存器的設(shè)置,即:CS、IP、SS、SP、DS的指向。
posted on 2010-07-18 14:51 luqingfei 閱讀(4007) 評(píng)論(0) 編輯 收藏 引用 所屬分類: 匯編語言基礎(chǔ)學(xué)習(xí)