暫存器及資料儲存
CPU組成
- 運算器進行資訊處理
- 暫存器進行資訊儲存
- 控制器協調各種器件進行工作
- 內部匯流排先實現 CPU 內各個器件之間的聯絡
暫存器
暫存器是 CPU 內部的資訊儲存單元。
8086 CPU 有 14 個暫存器:
- 通用暫存器:AX, BX, CX, DX
- 變址暫存器:SI,DI
- 指標暫存器:SP,BP
- 指令指標暫存器: IP
- 段暫存器: CS, SS, DS, ES
- 標誌暫存器: PSW
8086 CPU 所有的暫存器都是 16 位的,可以存放兩個位元組。
通用暫存器
一個 16 位暫存器儲存一個 16 位的資料
每一位儲存一個二進位制位能儲存的最大值為 \(2^{16} - 1\) (FFFFH)
如何相容 8 位暫存器環境下編寫的程式?
通用暫存器均可以分為兩個獨立的 8 位暫存器使用,AX 可以分為 AH 和 AL,BX, CX, DX 同理
“字”在暫存器中的儲存
8086 是 16 位 CPU,字長 為 16 bit
一個字可以存在一個 16 位暫存器中
- 這個字的高位位元組存在這個暫存器的高 8 位暫存器
- 這個字的低位位元組存在這個暫存器的低 8 位暫存器
mov指令
- MOV (通用暫存器) , (資料)
- MOV (暫存器) , (暫存器)
- MOV (暫存器) , (記憶體單元)
- MOV (記憶體單元) , (暫存器)
- MOV (段暫存器) , (通用暫存器)
彙編指令不區分大小寫
數值預設為 10 進位制,寫入 16 進位制需在末尾新增 'H'
在低位暫存器出現溢位時,不會進位到高位
確定實體地址的方法
CPU 訪問記憶體單元時要給出記憶體單元的地址。
所有記憶體單元構成的儲存空間是一個一維的線性空間。
每一個記憶體單元都有唯一的地址,叫實體地址。
8086 有 20 位地址匯流排,可傳送 20 位地址,定址能力是 \(2^{20} = 1 M\) 。
8086 是 16 位結構的 CPU,運算器一次最多處理 16 位的資料,暫存器的最大寬度為 16 位。
在 8086 內部處理的、傳輸、暫存的地址也是 16 位,定址能力只有 64 KB
如何處理地址匯流排 20 位的定址能力受限於 16 位地址長度這一問題?
用兩個 16 位地址(段地址和偏移地址)合成一個 20 位的實體地址。
地址加法器合成實體地址的方法: \(實體地址 = 段地址 * 16 + 偏移地址\)
記憶體的分段表示法
記憶體並沒有分段,段的劃分來自於 CPU
段地址 : 偏移地址
同一段記憶體可以有多種分段方案
- \(段地址 * 16\) 必然是 16 的倍數, 所以一個段的起始地址也一定是 16 的倍數。
- 偏移地址為 16 位,16 位地址的定址能力為 64K,所以一個段的長度最大為 64K。
- 可以用不同的段地址和偏移地址形成同一個實體地址。
8086 有豐富的取址方式,因此偏移地址可以用多種方法提供。
段暫存器
段暫存器存放段地址,段暫存器只能由暫存器賦值,不支援用字面值直接賦值。
8086 CPU 有 4 個段地暫存器
- CS 程式碼段暫存器
- DS 資料段暫存器
- SS 棧段暫存器
- ES 附加段暫存器
Debug
Debug 是 DOS 系統中著名的除錯程式。
使用 Debug 程式,可以檢視 CPU 各種暫存器中的內容、記憶體的情況,並且在機器指令級跟蹤程式的執行。
常用 Debug 命令
- R 命令 檢視、改變 CPU 暫存器的內容
-R 檢視所有暫存器內容
-R (暫存器名) (資料) 修改特定暫存器中的資料 - D 命令 檢視記憶體中的內容
-D 列出預設地址記憶體處的 128 個位元組的內容,每次偏移 128 個位元組
-D (段地址:偏移地址) 列出記憶體中指定地址處的內容
-D (段地址:偏移地址) (結尾偏移地址) 列出記憶體中指定地址範圍內的內容 - E 命令 改變記憶體中的內容
-E (段地址:偏移地址) (資料1) (資料2) .... 改變記憶體中的內容
-E (段地址:偏移地址) 逐個詢問式修改,空格代表接受並繼續,回車代表修改結束 - U 命令將記憶體中的機器指令翻譯成彙編指令
-U (段地址:偏移地址) - A 命令以彙編指令的格式在記憶體中寫入機器指令
-A (段地址:偏移地址) 逐行輸入彙編指令 - T 命令執行機器指令
-T 執行 CS:IP 處的指令 - Q 命令推出 Debug
CS、IP 與程式碼段
CPU 將記憶體中 CS:IP 指向的內容當作指令執行。
8086 讀取和執行指令
- 從 CS:IP 指向記憶體單元讀取指令,讀取的指令進入指令緩衝器
- IP = IP + 所讀指令的長度,從而指向下一條指令
- 執行指令,跳轉到步驟 1
- 重複此過程
jmp 指令
執行何處的指令,取決於 CS:IP 指向的資料
可以透過改變 CS 和 IP 中的內容,來控制 CPU 要執行的目標指令
Bebug 可以改變 CS 和 IP 的值,但是 Debug 是除錯手段,並非程式方式。
8086 CPU 不提供對 IP 修改的指令,不能使用 MOV 指令修改,只能由 CPU 自行修改
使用轉移指令 jmp
- jmp (段地址:偏移地址) 用段地址修改 CS ,偏移地址修改 IP
- jmp (某一合法暫存器) 用某一合法暫存器的資料修改 IP
記憶體中”字“的儲存
8086 CPU 中,16 位作為 1 個字。
16 位的字,高 8 位存放高位元組,低 8 位存放低位元組,從而實現儲存在 1 個 16 位的暫存器中。
字單元
子單元由兩個地址連續的記憶體單元組成,存放一個字型資料
在一個字單元中,低地址單元存放低位位元組,高地址單元存放高位位元組
用 DS 和 [addres] 實現字的傳送
CPU 要讀取一個記憶體單元的時候,必須給出這個記憶體單元的地址
在 8086 PC 中,記憶體地址由段地址和偏移地址組成(段地址:偏移地址)
用 DS 和 [address] 配合,用 DS 暫存器存放要訪問的資料的段地址
偏移地址用 [...] 形式給出
MOV 指令中,只寫 [address] 時,預設段地址就是 DS 。
DS 和資料段
對記憶體單元中資料的訪問
對於 8086 , 可以根據需要將一組記憶體單元定義為一個段
- \(實體地址 = 段地址 * 16 + 偏移地址\)
- 將一組長度為 \(N(N \leq 64K)\) 、地址連續、起始地址為 16 的倍數的記憶體單元當作專門儲存資料的記憶體空間,從而定義了一個資料段。
用 DS 存放資料段的段地址,用指令訪問資料段中的具體單元,單元地址由 [address]指出。
加法 add 和減法 sub 指令
- add (暫存器) , (資料)
- add (暫存器) , (暫存器)
- add (暫存器) , (記憶體單元)
- add (記憶體單元) , (暫存器)
段暫存器不能作為被加數,add 指令的引數不能全為記憶體單元 - sub (暫存器) , (資料)
- sub (暫存器) , (暫存器)
- sub (暫存器) , (記憶體單元)
- sub (記憶體單元) , (暫存器)
段暫存器不能作為被減數,sub 指令的引數不能全為記憶體單元。
棧及棧操作的實現
棧是一種只能在一端進行插入或刪除操作的資料結構。
棧有兩個基本的操作
- 入棧:將一個新的元素放到棧頂
- 出棧:從棧頂取出一個元素
棧頂元素總是最後入棧,需要出棧時,又最先被從棧中取出。
棧的操作規則:LIFO (Last In First Out ,後進先出)
CPU 提供的棧機制,支援用棧的方式訪問記憶體空間,可以將一段記憶體當作棧來使用。
PUSH (暫存器) 將暫存器中的資料送入棧,實質上就是一種記憶體傳送指令,與 MOV 不同的是,PUSH 指令訪問的記憶體單元的地址不作為引數,而是由 SS:SP 所指向的。
POP (暫存器) 從棧頂取出資料送入暫存器,實質同 PUSH。
棧頂暫存器 SS 存放棧頂的段地址,棧頂指標暫存器 SP 存放棧頂的偏移地址,
SS:SP 任意時刻都指向棧頂元素。
棧指令執行過程
- PUSH (暫存器)
- SP = SP - 2
- 將暫存器中的內容送入 SS:SP 指向的記憶體單元處,SS:SP 此時指向新棧頂
- POP (暫存器)
- 將 SS:SP 指向的記憶體單元處的資料送入暫存器中
- SP = SP + 2
- SS:SP 指向當前棧頂下面的單元,以當前棧頂下面的單元為新的棧頂。
棧頂越界問題
棧空時使用 POP 指令或棧滿時使用 PUSH 指令,都會導致棧頂越界
8086 CPU 不保證對棧的操作不會越界,需要在程式設計時注意。