在8086cpu中,位址匯流排寬度為20,可以傳送20位的位址,達到1mb的定址能力,但暫存器都是16位的,所以表現出來的定址能力只有64kb。在cpu定址過程中,cpu會根據16位的段位址和16位的偏移位址來進行位址的合成,生成20位的實體地址。
這裡的段位址和偏移位址常用16進製制來表示,數值後邊加h來表示,乙個16位進製對應4位二進位制,各個進製的轉換關係不在此進行說明。
記憶體單元
對應資料
10000h
10h10001h
20h10002h
30h10003h
40h10004h
50h10005h
60h當把記憶體單元10000h資料送入ax暫存器,需要獲取2個記憶體單元的資料,即10000h為低位址存入ax的低8位,10001h為高位址存入ax的高8位,此時ax的值為2010h。反過來當把ax的值送入記憶體位址10000h時,需要2個記憶體單元存放,高位址10001h存放ax的高8位資料:20h,低位址10000h存放ax的低8位資料。
記憶體資料的獲取需要通過資料段暫存器值 × 16 + 偏移位址
段位址×16:對於段資料 1000h 來說,1000h × 16 = 10000h,從二進位制的角度來看,相當於段資料左移了4。假設此時給出的偏移位址為 0001h,那麼最終的記憶體位址就是:10001h。
既然知道了記憶體位址的表示方法,但應該怎麼去修改記憶體資料?
傳送指令 mov
可以通過該指令來對指定暫存器或記憶體資料進行修改,mov指令後有兩個操作物件,語法如下:
mov 操作物件1,操作物件2
該指令把操作物件2表示的資料傳送到操作物件1中,不修改操作物件2的資料。
注意:cs和ip暫存器不能通過mov指令來進行修改,cpu並未提供這樣的操作,可用jmp指令來進行修改,後續將會介紹到。
根據該語法可以修改暫存器 ax 的值為 0 :
mov ax, 0h
對於段暫存器來說,不能直接使用mov指令來進行立即數的賦值,因為對於cpu硬體來說,沒有提供這樣的操作,但可以通過其它暫存器來修改,此時 ds = 1000h:
mov ax, 1000h
mov ds, ax
mov ax, 1000h
mov ds, ax
mov [0], ax
這樣就實現了對記憶體資料的修改,然後把該記憶體資料的資料,放入到 bx 中:
mov bx, [0]
mov ds, [0]
加法指令 add
該指令接受和mov指令一樣,接受兩個操作物件,來對兩個操作物件資料相加,結果送入到操作物件1中,語法如下:
add 運算元1,運算元2
因段位址的不同用途,add 指令不支援對包含段位址的運算指令。
計算兩數之和,並把結果儲存到 ax 中:
mov ax, 1
mov bx, 2
add ax, bx
減法指令 sub
該指令和 add 語法一樣,計算操作物件1減操作物件2的值,並把結果儲存到操作物件1中。語法如下:
sub 操作物件1,操作物件2
計算兩數之差,並把結果保持到 ax 中:
mov ax, 2
mov bx, 1
sub ax, bx
注意,以上的操作都是依賴於暫存器來實現的,因為暫存器指定了操作物件中資料的型別,如字型資料、位元組型資料。在操作的過程中,我們需要保證兩個操作物件資料型別的一致性,不然就是非法的,因為對於cpu來說,無法確認應該使用多少個記憶體單元來滿足一次運算。除了暫存器外,我們還可以使用其它的方式來指定運算元據的型別,如word ptr(字型資料)、byte ptr(位元組型資料)。後續將會對這些進行說明。
在計算機中,所有的資料和指令都是通過二進位制數值進行存放的,所以我們在記憶體資料中寫入的彙編指令到計算機中也是翻譯為二進位制形式的機器指令,那麼cpu是根據什麼來判斷某一塊記憶體中的資料是不是要當成指令來執行呢?
剛剛介紹過,cpu把ds:[偏移位址]指向的記憶體內容當成資料處理,對於指令,cpu通過cs (**段暫存器)和ip (指令指標暫存器)指向記憶體內容當成機器指令來處理。
cpu通過cs和ip合成實體地址
讀取該實體地址下的指令並放入指令緩衝器,在執行指令前使ip的值增加該條指令的長度
執行指令
很多情況下,我們所執行的指令並處於 cs:ip 的指向的位址,這個時候可以通過無條件跳轉指令 jmp來更改暫存器 cs 和 ip 的值來指向我們指令所處的記憶體單元。格式如下:
段間轉移:
也可以單獨修改暫存器 ip 的值,格式如下:
段內轉移:
jmp 操作物件
例一:修改暫存器 cs=1000h, ip=0001h:
jmp 1000h:0001h
例二:修改暫存器ip=0010h
mov ax, 0010
jmp ax
關於更多 jmp 指令和特性,將放在後續的學習中在進行介紹,在此章中只介紹cpu的指令執行過程。
棧是一種具有特殊訪問方式的儲存空間,有棧底和棧頂之分,遵循先進後出的原則。棧一般有壓棧和出棧兩種操作。
cpu提供了支援棧操作的暫存器和指令。
ss(棧段暫存器)制定了乙個棧的段位址,sp(棧頂指標暫存器)指向棧頂位址。通過ss × 16 + sp來定址棧頂。
壓棧指令 push
push指令接受乙個操作物件,該操作物件可以暫存器、段暫存器、記憶體單元,格式如下:
push 操作物件
如,把暫存器ax中的值壓入棧中
mov ax, 1020h
push ax
在把乙個操作物件壓入棧中時,cpu實際做以下操作:
在ss:sp指向的新棧頂處壓入操作物件資料
對於棧頂指標暫存器 sp 來說,始終指向棧的棧頂位置,如:對於乙個新棧,記憶體單元範圍為10000h~10010h,若棧段暫存器ss = 1000h,則sp應為10010h記憶體單元的下乙個記憶體單元,所以sp = 0011h。當對暫存器ax進行壓棧後,sp指向新的棧頂位置,sp = 000eh。此時1000eh記憶體單元存放ax的低8為資料,即20h,10010h記憶體單元存放ax的高8位資料,即10h。
出棧指令 pop
pop指令接受乙個操作物件,該操作物件可以暫存器、段暫存器、記憶體單元,格式如下:
pop 操作物件
如:把剛剛壓入棧的資料出棧到bx中,此時bx=1020h:
pop bx
在把乙個棧中資料出棧時,cpu實際做一下操作:
通過棧頂指標指向的記憶體單元取出對應的資料,並存放到操作物件中
計算出棧後的棧頂指標位置,計算如下:sp = sp + 2
對於8086cpu來說,因為暫存器都是16位的,所以棧頂指標暫存器最多可以使用2的16次方個記憶體單元,若在達到最大的棧頂位置還在進行壓棧時,新壓棧的資料會覆蓋原先已入棧的資料。
如,乙個無資料的棧空間:1000h~1ffffh,假設ss = 1000h,此時棧頂指標暫存器sp=0000h,指向新棧的棧頂。第一次執行push後,sp = sp - 2 = fffeh,當一直進行壓棧操作時,sp會重新賦值位0000h,當在進行壓棧時,就會對以前入棧的資料進行覆蓋。
所以,對於棧的操作,要注意棧頂超界的問題,否則可能會造成對自己棧和其他記憶體單元的資料照成影響。
目錄上一章
下一章
我是如何學習儲存的
通過這麼多年接觸儲存,我總結自己學習儲存的經歷如下 1 動手操作。動手是人學習一項技能時最有效 最快速的方法,同時也是記憶一件事情時最有效 最快速 記憶時間最長的方法。我一直以來都體行動手第一的思想,有機會一定要自己動手操作。以前專案實施時,有把握的自己做。沒把握的,讓同事在旁邊看著自己做,遇到問題...
我是如何學習Linux的
原文寫不完了,簡而言之,對於入門而言 1 去網上下 鳥哥的linux私房菜 有簡體版本的 2 參照這本書,安裝centos 並一章一章學習吧 如果你是用來玩兒的,去裝 ubuntu 吧!分割線 以下是原文 知道linux很久了 第一次聽說linux 大二的時候 5年前了 我室友在一台古董機上裝著玩兒...
我是如何學習git
一開始學習git的時候,網上的git教程很多,看得人眼花繚亂,不知道如何下手。現在對git已經很熟悉了,回過頭來總結一下學習方法。一般官方文件是最全面,但是不一定適合快速上手。我們可以學習一些快速上手的教程,這些教程沒有官方文件那麼全面,但是可以學習到最常用的操作,適合入門。入門之後,如果還想高階,...