寄存器(内存访问)---汇编学习笔记
寄存器(內(nèi)存訪問)
序言
第二章,我們主要從CPU如何執(zhí)行指令的角度講解了8086CPU的邏輯結(jié)構(gòu)、形成物理地址的方法、相關(guān)的寄存器以及一些指令。
這一章,我們從訪問內(nèi)存的角度繼續(xù)學(xué)習(xí)幾個寄存器。
3.1 內(nèi)存中字的存儲
CPU中,用16位寄存器來存儲一個字。字是有2個內(nèi)存單元組成。高8位存放高位字節(jié),低8位存放低位字節(jié)。
例如:問題 3.1 所描述的。
(1) 0 地址單元中存放的字節(jié)型數(shù)據(jù)是多少?
(2) 0 地址字單元中存放的字型數(shù)據(jù)是多少?
(3) 2 地址單元中存放的字節(jié)型數(shù)據(jù)是多少?
(4) 2 地址字單元中存放的字型數(shù)據(jù)是多少?
(5) 1 地址字單元中存放的字型數(shù)據(jù)是多少?
答案一目了然,分別是20H、4E20H、12H、0012H、124E。
3.2 DS和[address]
CPU要讀寫一個內(nèi)存單元時,必須給出這個內(nèi)存單元的地址,內(nèi)存地址由段地址和偏移地址組成。 DS 存放要訪問數(shù)據(jù)的段地址, [address] 中address是偏移地址并且是一個具體的數(shù)。
這里注意,DS并不能直接給定一個數(shù)值。比如 mov ds,1000H 語句在8086CPU中是錯的。我們只能通過寄存器去改變ds的值,比如 mov ds,ax 語句。
問題 3.2
寫幾條指令,將 al 中的數(shù)據(jù)送入內(nèi)存單元 10000H 中。
mov ax,1000H ;為了讓ds為1000H,先讓ax為1000H mov ds,ax ;ds只接受寄存器向它傳送的數(shù)據(jù) mov al,[0] ;[0]是偏移地址,1000:0的內(nèi)存單元是10000H3.3 字的傳送
8086CPU是 16 位結(jié)構(gòu),有16根數(shù)據(jù)線,所以,可以一次性傳送16位數(shù)據(jù),也就是一個字。
問題 3.3
內(nèi)存中的情況如下圖所示,寫出下面指令執(zhí)行后寄存器ax,bx,cx中的值。
問題 3.4
內(nèi)存中的情況如圖所示,寫出下面指令執(zhí)行后內(nèi)存中的值。
我們知道高字節(jié)放入高地址,低字節(jié)放入低地址。也就是[0]的高地址為1000:1,低地址為1000:0。
3.4 mov、add、sub指令
我們在沒什么了解的情況之前就是是要幾個了mov、add、sub等指令。
先了解mov指令的幾種形式:
mov 寄存器,數(shù)據(jù) mov 寄存器,寄存器 mov 寄存器,內(nèi)存單元 mov 內(nèi)存單元,寄存器 mov 段寄存器,寄存器(1)我們猜想,既然有 mov 段寄存器,寄存器 指令,那么會有mov 寄存器,段寄存器 指令這樣的相反通路嗎?
實驗如下(再此聲明一次:借用了實驗樓的環(huán)境):
如圖所示:
- 第一個紅框的 AX 為 0000H;
- 第二個紅框是 DS 為 1000H;
- 第三個紅框發(fā)生AX的值改變。
由此可知,mov 寄存器,段寄存器指令是可用的。
(2)同樣,既然有 mov 寄存器,內(nèi)存單元 ,會擁有 mov 內(nèi)存單元,寄存器 指令嗎?
實驗如下:
如圖所示,內(nèi)存單元 1000:0 和 1000:1 發(fā)生變化,也就是1000:[0]發(fā)生變化。
(3)那么 mov 段寄存器,內(nèi)存單元 也應(yīng)該可以。
實驗如下:
發(fā)現(xiàn)指令出錯。
同mov一樣, add 和 sub 也有以下幾種形式:
add 寄存器,數(shù)據(jù) add 寄存器,寄存器 add 寄存器,內(nèi)存單元 add 內(nèi)存單元,寄存器sub 寄存器,數(shù)據(jù) sub 寄存器,寄存器 sub 寄存器,內(nèi)存單元 sub 內(nèi)存單元,寄存器我們嘗試一下 add 段寄存器,ax 指令,看看可行嗎?
實驗如下:
指令出錯。
3.5 數(shù)據(jù)段
前面提到,可以根據(jù)需要,將一組內(nèi)存單元定義為一個段。我們可以將一組的長度為N(N<=64KB)、地址連續(xù)、起始地址為16的倍數(shù)的內(nèi)存單元當(dāng)作專門存儲數(shù)據(jù)的內(nèi)存空間。
例如將 123B0H~123B9H 的內(nèi)存單元定義為數(shù)據(jù)段。現(xiàn)在要累加這個數(shù)據(jù)段中的前3個單元中的數(shù)據(jù),如下:
mov ax,123BH mov ds,ax mov al,0 ;注意,由于題目要求是"單元",而不是"字",所以采用al add al,[0] add al,[1] add al,[2]問題 3.5
寫幾條指令,累加數(shù)據(jù)段中的前3個字型數(shù)據(jù)。代碼如下:
mov ax,123BH mov ds,ax mov ax,[0] ;因為這里采用的是"字" add ax,[2] add ax,[4]3.1~3.5 小結(jié)
(1)字在內(nèi)存中存儲時,采用兩個地址連續(xù)的內(nèi)存單元來存放,字的低位字節(jié)存放在低地址單元中,高位字節(jié)存放在高地址單元中。
(2)用 mov 指令訪問內(nèi)存單元,可以在 mov 指令中只給出單元的偏移地址,此時,段地址默認(rèn)在 DS 寄存器中。
(3)[address] 表示一個偏移地址為 address 的內(nèi)存單元。
(4)在內(nèi)存和寄存器之間傳送字型數(shù)據(jù)時,高地址單元和高8位寄存器、低地址單元和低8位寄存器相對應(yīng)。
(5)mov、add、sub 是具有兩個操作對象的指令。jmp 是具有一個操作對象的指令。
(6)可以根據(jù)自己的推測,在 Debug 中實驗指令的新格式。
其實,最麻煩的就是第6點,每種CPU的匯編指令都會有一些不同,所以在針對不同的CPU時需要去猜測所謂的“新格式”。
檢測點 3.1
(1)在 Debug 中,用“d 0:0 1f”查看內(nèi)存,結(jié)果如下。
寫出下面每條匯編指令執(zhí)行后寄存器的值。
(2)內(nèi)存中的情況如圖 3.6 所示。
各寄存器的初始值:CS=2000H , IP=0 , DS=1000H , AX=0 , BX=0;
①:我們知道CS=2000H,IP=0,所以從2000:0000處開始執(zhí)行。因此,代碼如下。
mov ax,6622H jmp 0FF0:0100 ;0FF0:0100 == 1000:0 mov ax,2000H mov ds,ax mov ax,[0008] mov ax,[0002]②:CS:IP的值如下。
| mov ax,6622 | 2000H | 0003H | 6622H | 1000H |
| jmp 0FF0:0100 | OFFOH | 0100H | 6622H | 1000H |
| mov ax,2000H | 0FF0H | 0103H | 2000H | 1000H |
| mov ds,ax | 0FF0H | 0105H | 2000H | 2000H |
| mov ax,[0008] | 0FF0H | 0108H | C389H | 2000H |
| mov ax,[0002] | OFFOH | 010BH | EA66H | 2000H |
③:初步猜測,我認(rèn)為數(shù)據(jù)和指令無區(qū)別。原因,是因為可以將mov bx,ax當(dāng)做數(shù)據(jù)傳送到[0008]內(nèi)存中。那我們?nèi)绾未_定是數(shù)據(jù)還是指令呢?當(dāng)然是通過我們的CS:IP來確認(rèn)咯,IP指向的第一個值是我們的指令,例如A1是為 mov ax,內(nèi)存地址 的指令。執(zhí)行指令后,IP根據(jù)指令來確定向后移動幾個位置。(以上是本人的猜想,不知道是否成立)
猜想:CP是時鐘脈沖,以A1為例子,我們知道讀取一個數(shù)據(jù)需要一個脈沖,A1是需要3個脈沖時間才能完成的操作?
以上是本人猜想,未經(jīng)過驗證。
3.6 棧
棧是一種具有特殊訪問方式的存儲空間。特殊點在于,數(shù)據(jù)入棧出棧的次序是“先進(jìn)后出”或者說“后進(jìn)先出”(LIFO,Last In First Out)。擁有兩種操作:入棧(PUSH)和出棧(POP)。
3.7 CPU 提供的棧機(jī)制
CPU 提供相關(guān)的指令來以棧的方式訪問內(nèi)存空間。說明了,我們可以將一段內(nèi)存當(dāng)做棧來使用。(其實我們編程在寫遞歸的時候,總是很經(jīng)常沒有考慮遞歸基的情況就運(yùn)行程序,導(dǎo)致內(nèi)存溢出。我們通過前面所說內(nèi)存被當(dāng)做棧來使用,推導(dǎo)出:其實就是棧內(nèi)存被使用了無限次,導(dǎo)致的內(nèi)存溢出結(jié)果。)
如下所示是棧的操作:
可以看出高地址單元存放高8位,低地址單元存放低8位。
在這里,我們會疑惑!總結(jié)一下,大概是兩個問題。
這里給出答案,8086CPU 中,有兩個寄存器SS、IP分別是棧的段地址和偏移地址。在任意時刻,SS:IP 指向棧頂元素。
我們來看看PUSH操作的過程,如圖:
反之,則是POP操作過程,如圖:
我們看到,POP操作并沒有將棧頂之前的值 清 0 。
問題 3.6
如果將 10000H~1000FH 這段空間當(dāng)做棧,初始狀態(tài)棧是空的,此時,SS=1000H,SP=?
答案顯然是最高地址的下一個單元,即10010H。
3.8 棧頂超界的問題
我們知道棧頂?shù)闹赶蛴?SS:IP 控制。但并沒有控制這棧空間pop次數(shù)過多導(dǎo)致下溢以及push多次導(dǎo)致上溢的檢測。這樣會出現(xiàn)一個問題,我們腦海中的棧空間之外的數(shù)據(jù)會被棧的pop或者push操作給覆蓋,從而導(dǎo)致一系列的錯誤發(fā)生。這非常嚴(yán)重,試想一下,如果C語言編寫的一段遞歸程序沒有遞歸基就運(yùn)行之后,秒級的時候就會講內(nèi)存占滿,從而導(dǎo)致RAM死機(jī),計算機(jī)只能重啟。這也是為什么VC++給程序只分配了4K空間的原因(我記得是4K,若有錯誤請指正)。
因此,我們在編寫匯編程序的時候,需要注意我們的PUSH和POP操作不會導(dǎo)致上溢以及下溢。
3.9 push、pop 指令
push和pop 指令的格式可以是如下形式:
push 寄存器 ;將一個寄存器中的數(shù)據(jù)入棧 pop 寄存器 ;出棧,用一個寄存器接受出棧的數(shù)據(jù) push 段寄存器 pop 段寄存器 push 內(nèi)存單元 pop 內(nèi)存單元問題 3.7
編程,將 10000H~1000FH 這段空間當(dāng)做棧,初始狀態(tài)棧是空的,將 AX、BX、DS 中的數(shù)據(jù)入棧。
問題 3.8
編程:
(1)將 10000H~1000FH 這段空間當(dāng)作棧,初始狀態(tài)棧是空的;
(2)設(shè)置 AX=001AH,BX=001BH;
(3)將 AX、BX 中的數(shù)據(jù)入棧;
(4)然后將AX、BX清零;
(5)從棧中恢復(fù)AX、BX原來的內(nèi)容。
問題 3.9
編程:
(1)將 10000H~1000FH 這段空間當(dāng)做棧,初始狀態(tài)為空;
(2)設(shè)置 AX=001AH,BX=001BH;
(3)利用棧,交換 AX 和 BX 中的數(shù)據(jù)。
問題 3.10
如果要在 10000H 處寫入字型數(shù)據(jù) 2266H,可以用以下代碼完成:
mov ax,1000H mov ds,ax mov ax,2266H mov [0],ax補(bǔ)全下面的代碼,實現(xiàn)功能:在 10000H 處寫入字型數(shù)據(jù) 2266H 。
要求:不能使用“mov 內(nèi)存單元,寄存器”這類指令。
棧的綜述
(1)8086CPU 提供了棧操作機(jī)制,方案如下。
- 在 SS、IP 中存放棧頂?shù)亩蔚刂泛推频刂?#xff1b;
- 提供入棧和出棧指令,它們根據(jù) SS:SP 指示的地址,按照棧的方式訪問內(nèi)存單元。
(2)push 指令的執(zhí)行步驟:
- SP=SP-2;
- 向SS:SP指向的字單元中送入數(shù)據(jù)。
(3)pop 指令的執(zhí)行步驟:
- 從 SS:SP 指向的子單元中讀取數(shù)據(jù);
- SP=SP+2。
(4)任意時刻,SS:SP 指向棧頂元素。
(5)8086CPU 只記錄棧頂,棧空間的大小我們要自己管理。
(6)用棧來暫存以后需要回復(fù)的寄存器的內(nèi)容時,寄存器出棧的順序要和入棧的順序相反。
(7)push、pop 實質(zhì)上是一種內(nèi)存?zhèn)魉椭噶?#xff0c;注意它們的靈活應(yīng)用。
棧是一種非常重要的機(jī)制,一定要深入理解,靈活掌握。
3.10 棧段
在編程時,我們可以根據(jù)需要,將一組內(nèi)存單元定義為一個段。我們要注意控制棧不會超界。
問題 3.11
如果將 10000H~1FFFFH 這段空間當(dāng)作棧段,初始狀態(tài)棧是空的,此時,SS=1000H,SP=?
答案很顯然,SP= FFFF+1 = 10000H = 0000H。這是一個64K大小的棧段。
問題 3.12
一個棧最大可以設(shè)為多少?為什么?
最大可以設(shè)為64K,因為SP的范圍只能是 0000H~FFFFH 之間。
段的綜述
- 數(shù)據(jù)段,段地址放在DS中,偏移地址[address]。
- 代碼段,段地址放在CS中,偏移地址IP。
- 棧段,段地址放在SS中,偏移地址SP。
例如一段代碼,假設(shè)CS=1000H,IP=0000H:
mov ax,1000H mov ss,ax mov sp,0020H mov ax,cs mov ds,ax mov ax,[0] add ax,[2] mov bx,[4] add bx,[6] push ax push bx pop ax pop bx這段代碼給我們的信息是:在 10000H~1001FH這段內(nèi)存中,既是代碼段,又是棧段和數(shù)據(jù)段。這樣的代碼可能會出現(xiàn)數(shù)據(jù)發(fā)生錯誤,盡量讓一段內(nèi)存當(dāng)做三者中的一種段。
檢測點 3.2
(1)補(bǔ)全下面的程序,使其可以將 10000H~1000FH 中的 8 個字,逆序復(fù)制到 20000H~2000FH 中。逆序復(fù)制的含義如下圖所示。
(2)補(bǔ)全下面的代碼,使其可以將 10000H~1000FH 中的 8 個字,逆序復(fù)制到 20000H~2000FH 中。
;題目代碼 mov ax,2000H mov ds,ax;三行代碼 mov ax,1000H mov ss,ax mov sp,0000H;題目代碼 pop [E] pop [C] pop [A] pop [8] pop [6] pop [4] pop [2] pop [0]實驗 2 用機(jī)器指令和匯編指令編程
1. 預(yù)備知識:Debug 的使用
- D命令,查看內(nèi)存數(shù)據(jù)。
- E命令,修改內(nèi)存數(shù)據(jù)。
- A命令,輸入?yún)R編指令。
- U命令,查看匯編程序。
2. 實驗任務(wù)
(1)使用 Debug,將上面的程序段寫入內(nèi)存,逐條執(zhí)行,根據(jù)指令執(zhí)行后的實際運(yùn)行情況填空。
mov ax,FFFF mov ds,ax mov ax,2200 mov ss,ax mov sp,0100;填空 mov ax,[0] ;我們不考慮AX與BX的值 add ax,[2] mov bx,[4] add bx,[6]push ax ;研究SP的變化,SP = 00FEH push bx ;SP = 00FCH pop ax ;SP = 00FEH pop bx ;SP = 0100Hpush [4] ;SP = 00FEH push [6] ;SP = 00FCH(2)仔細(xì)觀察下圖的實驗過程,然后分析:為什么 2000:0~2000:f 中的內(nèi)容會發(fā)生改變?
我們發(fā)現(xiàn)指令 mov ss,ax 被執(zhí)行后,下一條指令是 mov ax,3123 也就是說 一次T指令對ss操作連同sp也執(zhí)行了。這變相的說明ss和sp需要連續(xù)的改變,也就是mov ss,ax之后必須是mov sp,10。
那我們從中尋找原因:如果ss執(zhí)行后,不立馬執(zhí)行sp會發(fā)生什么情況?
百度了一下,回答是這樣的,我們的 T指令 是中斷指令,也就是 T指令 執(zhí)行后需要把相關(guān)寄存器信息壓入棧中。那么,我們就知道了,SS之后必須跟SP,否則會出現(xiàn)錯誤。執(zhí)行后還需要把 T指令 相關(guān)的東西壓入棧中,所以出現(xiàn)了一堆數(shù)據(jù)。比如:0B39 是 DS 的值等。
總結(jié)
以上是生活随笔為你收集整理的寄存器(内存访问)---汇编学习笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互联网日报 | 天猫双11成交额4982
- 下一篇: 产品经理才看的懂