乙個典型的cpu由運算器、控制器、暫存器等器件構成,這也是cpu的工作原理。
這些器件是由cpu內部匯流排連線的。
運算器進行資訊的處理;暫存器進行資訊的儲存;控制器控制各種器件進行工作;
內部匯流排連線各種器件,在他們之間進行資料的傳送。
不同的cpu的暫存器數、結構是不同的。 本書介紹的8086cpu共有14個暫存器。
8086cpu的暫存器都是十六位的,乙個暫存器能儲存兩個位元組的資料,能儲存最大的十進位制數字就是\(2^-1=65536\).
ax,bx,cx,dx這四個暫存器通常用來存放一般性的資料,成為通用暫存器。
其中每個暫存器都可以分為高位和低位,例如ax可分為al和ah。
ah和al都是八位,也就是乙個位元組。
當對al操作的時候,cpu是把al和ah當作兩個獨立的暫存器對待的,無論對al進行何種操作都不會對ah中儲存的資料造成影響。
暫存器的資料用二進位制表示出來有十六位,閱讀起來會非常困難,
用十六進製制的數字來表示暫存器的內容會非常簡潔明瞭。
乙個十六進製制數字可以表示四位二進位制數字,那麼十六個二進位制數字就可以用四個十六進製制數字表示。
例如要在暫存器儲存的數字是\(20000\),那麼它的二進位制表示就是\(0100111000100000b\),而用十六進製制表示則為:\(4e20h\),非常簡潔方便。
彙編指令
操作說明
mov ax,bx
ax=bx
add ax,bx
ax=ax+bx
sub ax,bx
ax=ax-bx
其中bx都是可以變更為具體的值的。
需要注意的是,如果是兩個暫存器之間的操作,那麼他們之間的位數必須保持一致,例如:
mov al,bh是正確的,他們都是八位的;mov ax,bh就是錯誤的,因為ax是十六位的而bh是八位的。
每乙個儲存單元在這個空間中都有唯一的位址,我們稱這個唯一的位址為實體地址。
cpu通過位址匯流排送入儲存器的是乙個儲存單元的實體地址,在cpu向位址匯流排發出實體地址之前必須現在cpu內部生成這個實體地址。
8086cpu是乙個十六位的cpu,十六進製制的cpu有以下特性:
1.儲存器一次最多可以處理十六位的資料;
2.暫存器的最大寬度為十六位;
3.暫存器和運算器之間的通路為十六位。
8086cpu作為乙個十六位的cpu,理論上來說它的定址能力應該僅有64kb,而實際上它的定址能力卻又1mb這麼多,也就是二十位這麼多,那麼它是怎麼實現的?
實際上8086cpu的位址匯流排確實是二十位,否則它也不可能具有1mb的定址能力。那問題就轉換為它是怎麼生成的二十位位址。
段位址是乙個十六位的二進位制數字,它乘以\(16\)也就是\(2^4\)相當於把這個十六進製制的數字左移了4位,就變成了乙個二十位的二進位制數字。
實體地址=段位址*16+偏移位址,假如有乙個實體地址為:21f60h,那麼它還可以表示為:2000:1f60,其中2000為段位址,1f60為偏移位址。
資料儲存在21f60h記憶體單元,習慣上這麼說:
資料儲存在記憶體2000:1f60單元中;
資料儲存在記憶體的2000h段中的1f60h單元中。
這是一種用小數字表示大數字的通用方法,比如我有兩張紙片,一張紅色的一張藍色的,每張紙片只能寫兩個十進位制數字,現在我想用這這兩張卡片給你傳達乙個三位十進位制數字,那麼我可以提前和你約定好:紅色卡片上的數字乘以十加上藍色卡片上的數字就是我想要傳達給你的三位十進位制數字。這樣我只需要在紅色空片上寫15,藍色卡片上寫26,你就知道我想傳達給你的數字就是176.
段位址和偏移位址通過內部匯流排傳入乙個成為位址加法器的部件;
位址加法器將兩個十六位位址合成為乙個二十位實體地址;
位址加法器通過內部匯流排將二十位實體地址傳入輸入輸出控制電路;
輸入輸出電路將二十位實體地址送上位址匯流排;
二十位實體地址通過位址匯流排傳送到儲存器。
在8086cpu中,由於它是通過段位址*16+偏移位址的方式給出了實體地址,因此可以通過分段的方式管理記憶體。
在程式設計的時候,可以將一些連續的記憶體看作乙個段,這個段的起始位址就是段位址*16,然後通過改變偏移位址來定位段中的某個記憶體單元。
由於偏移位址為十六位位址,因此乙個段的最大長度就是\(2^16\)也就是64kb。
cs和ip是8086cpu的兩個暫存器,cs為**段暫存器,ip為指令指標暫存器,他們指示了cpu當前要讀取指令的位址,也就是說cpu將cs:ip指向的內容當作指令執行。
如果說記憶體中的一段資訊曾被cpu執行過的話,那麼它所在的記憶體單元必然被cs:ip指向過。
對於cs:ip的修改不能使用mov,add等常規指令實現。
在8086cpu中有專門的對cs:ip進行修改的指令,這種命令稱為轉移指令,
這裡給出這其中最簡單的指令jmp的介紹。
jmp 2ae2:3 執行後cs=2ae2h,ip=0003h。
若僅僅想修改ip的內容,可以執行jmp 某個合法暫存器指令完成,例如:
ax=2ae3h,則 jmp ax 執行後ip=2ae3h.
8086cpu除了cs這個段暫存器外還有三個段暫存器,分別為:ds、ss、es。
命令是儲存在儲存器中的,但是每條命令的長度並不都是相同的,那麼cpu是如何知道每條命令的長度的呢?
cpu是按照順序從前往後依次執行儲存在儲存器中的命令的,那麼cpu是如何知道執行到哪一條命令才停止的呢?
書中提到「8086機中,任意時刻,cpu將cs:ip指向的內容當作指令執行」,那麼這個任意時刻是如何定義的呢?也就是說,cpu是不斷地執行cs:ip指向的內容嗎?如果是,這個「不斷的」是怎樣的頻率呢?
debug是dos(disk operating system)、windows都提供的實模式(8086方式)程式的除錯工具。
使用它可以檢視cpu各種暫存器中的內容、記憶體的情況和機器碼級跟蹤程式的執行。
r:改變cpu暫存器的內容;
d:檢視記憶體中的內容;
e:改寫記憶體中的內容;
u:將記憶體中的機器指令翻譯成彙編指令;
t:執行一條機器指令;
a:以彙編指令的格式在記憶體中寫入一條機器指令。
- r
可以檢視cpu中暫存器的內容。在下方,debug還會列出cs:ip所指向的記憶體單元存放的機器碼,並將它翻譯為彙編指令。
- r ax
,按回車鍵,終端就會顯示:
,這樣就可以改變ax的值。
比如要將 ax的值改變為13h,就可以執行:
- r ax
: 13
ax的值就被改變成13了。
值得注意的是,r命令可以直接修改cs:ip的內容,沒有限制。
比如想要檢視1000:0000到1000:0009中的內容,可以用1000:0000 0009
實現。
使用e 起始位址 資料1 資料2 資料3 ...
指令,可以將從起始位址開始的儲存單元依次賦值為資料1,資料2,資料3,...。
採用提問的方式依次改變儲存單元中的資料。 向debug中輸入e 1000:0
,按回車鍵,之後debug會顯示起始位址1000:0000以及起始位址對應儲存單元儲存的值,在.
後面可以輸入值按空格,這個儲存單元的值就改變了,或者不輸入值直接按空格當前的儲存單元的值就不會被改變。按下空格之後debug就會顯示下乙個儲存單元的原始內容並提示修改。當所有要修改的值都修改完畢後,按回車鍵e命令操作就會結束。
向記憶體單元中寫入字元/字串。用上面第一種命令,裡面的資料可以是乙個字元。
- e 1000:0000 1 2 3 4 'a' 'b' 'c'
- e 1000:0000 "hello, world!"
使用u 1000:0000
即可檢視1000:0開始的記憶體儲存的命令。
查詢結果由三部分組成,最左邊是每個命令的儲存位址,中間是命令對應的機器指令,最右邊是每個機器指令對應的彙編指令。
每執行一次t指令,就會執行一次cs:ip指向的指令,之後cs:ip就會自動指向下一條命令的位置。
輸入a 1000:0000
之後按回車,之後就可以依次輸入彙編指令,輸入完畢按回車鍵,debug會將這些彙編指令翻譯為對應的機器指令儲存在對應的位置,之後自動將位置調整到下乙個能儲存指令的位置。若不輸入任何內容直接按回車鍵,則a命令操作就會結束。
組合語言 王爽 筆記
關於pop的知識 1.出棧後,ss sp指向新的棧頂,pop操作前的棧頂元素中的資料仍然存在,但是,它已不再棧中 2.ss和sp只記錄了棧頂的位址,依靠ss和sp可以保證在入棧和出棧時找到棧頂 3.當棧滿的時候再次使用push指令入棧,棧空的時候再次使用pop指令出棧,都將發生棧頂越界問題,它是非常...
組合語言 王爽 筆記
1.乙個組合語言程式從寫出到最終執行的簡要過程 編寫,編譯連線,執行 2.可執行檔案中包含兩部分內容 一是程式 從源程式中的彙編指令翻譯過來的機器碼 和資料 源程式中定義的資料 二是相關的描述資訊 eg 程式的大小,要佔的記憶體空間等 3.彙編指令是指有對應的機器碼的指令,可以被編譯為機器指令,最終...
組合語言 王爽 筆記
1.dos是乙個單任務作業系統,乙個程式a再可執行檔案中,必須要有另乙個正在執行的程式b,將a從可執行檔案中加載入記憶體中,將cpu的控制權交與a,a才能執行,a開始執行後,b停止執行,當a執行完畢後,將cpu控制權再交於b,b再繼續執行 2.程式返回 乙個程式結束後,將cpu的控制權交還給使它得以...