8086 暫存器
暫存器是在 CPU 中一個暫時儲存資料的地方。它有點兒像記憶體(DRAM),但是不像記憶體這麼多,暫存器只有一個、兩個或四個位元組的大小而已。 CPU 可以對暫存器作加、減、乘、除、且、或等等運算。8086/8088 共有 14 個 16 位元的暫存器,其名稱都以兩個英文字表示,大致可分為以下四類。
1.通用暫存器
共有四個,其名稱分別是 AX、BX、CX、DX,在組合語言程式中大致沒有太大的差別,但是其中只有 AX(accumulator,也稱為累加器) 可作為除法或乘法中的被除數與被乘數,當 16 位元不夠大時,常常用 DX:AX 來表示 32 位元。此外這四個暫存器,只有 BX(base register,也稱為基底暫存器) 可以被作為位址存取之用。CX 也稱為計數暫存器(count register),用於計算迴圈之次數或字串處理之次數。DX 也稱為資料暫存器(data register),可用來存取埠。
這四個暫存器也可以分成兩個 8 位元的暫存器來使用,例如 AX 可被分成較低的 8 位元稱為 AL,以及較高的 8 位元 AH 來使用。其餘 BX、CX、DX 也都類似。
堆疊的部份,以獲得詳細的堆疊資料。堆疊是一塊區域,用來暫時存放資料之用,在 8086/8088 中,堆疊是由最高位址中開始存放,每次都必須存入一個字組的長度,並用一組指標,來表示堆疊已經使用到那兒了,這組指標就是 SS:SP。也就是說,當成是要將資料存入堆疊時,該資料應該存放在 SS:SP 所指的位址再低 2 個位元組,然後 CPU 再使 SP 之內容減 2,使 SP 再指到下一個未使用的空間。
堆疊的部份,以獲得詳細的堆疊資料。堆疊是一塊區域,用來暫時存放資料之用,在 8086/8088 中,堆疊是由最高位址中開始存放,每次都必須存入一個字組的長度,並用一組指標,來表示堆疊已經使用到那兒了,這組指標就是 SS:SP。也就是說,當成是要將資料存入堆疊時,該資料應該存放在 SS:SP 所指的位址再低 2 個位元組,然後 CPU 再使 SP 之內容減 2,使 SP 再指到下一個未使用的空間。 有五個,其名稱分別是 SP、BP、IP、SI、DI。前面兩個 SP (stack pointer,稱為堆疊指標)與(base pointer,也稱為基底指標)是與堆疊(stack)有關的暫存器,請參考第五章內容有關,以獲得詳細的堆疊資料。堆疊是一塊區域,用來暫時存放資料之用,在 8086/8088 中,堆疊是由最高位址中開始存放,每次都必須存入一個字組的長度,並用一組指標,來表示堆疊已經使用到那兒了,這組指標就是 SS:SP。也就是說,當成是要將資料存入堆疊時,該資料應該存放在 SS:SP 所指的位址再低 2 個位元組,然後 CPU 再使 SP 之內容減 2,使 SP 再指到下一個未使用的空間。那什麼情形會要將資料存入堆疊內呢?有好幾種情形,例如呼叫副程式時,會預先把返回位址存入堆疊﹔呼叫中斷時也是如此。BP 通常用於呼叫副程式時,傳遞參數之用。
IP (instruction pointer,稱為指令指標) 配合 CS 變成 CS:IP,指向將要執行的 8086/8088 位址。當 CPU 要執行程式時,必須到記憶體去提取要執行的指令,而要到那一個記憶體位址去提取指令呢?這時 CPU 就會到 CS:IP 指到的位址去提取。在程式中,一般是沒有辦法改變 CS:IP 的值,除非是跳躍 (jmp、jz等) 指令或是呼叫 (call、ret等) 指令。
SI (source index,稱為來源索引暫存器) 和 DI (destination index,稱為目的索引暫存器) 通常是用來當作位址指標,也可用作加減法。這五個暫存器,每一個都不能分開來當作兩個 8 位元的暫存器使用。
3.區段暫存器
有 CS、DS、ES、SS 四個,分別表示程式碼(code segment register)、資料(data segment register)、額外(extra segment register)、堆疊(stack segment register)區段之用。在 DOS 系統中,每一個區段容量只有 64KBytes。
當資料區段不夠用時,就可以用額外區段來補足,例如想要將一個區段的某些內容複製到另一區段中,就可以同時指定 DS、ES 分別表示這兩個區段。
4.旗標暫存器
旗標暫存器 (flag register) 是一個 16 位元的暫存器,但只有其中九個位元有用到,它們分散在這十六個位元中,採用這種分散方式是為了與舊式的 8080 CPU 的旗標相同,其所佔用的分布如下圖所示:
其中低位元的八個位元會受到算術、比較或邏輯運算的結果影響,而使旗標被設定(set,其值為1),或被清除(clear,其值為零)。較高的四個旗標是用來表示 CPU 的狀態。
第零位元是 CF (carry flag,進位旗標)表示進位或借位。如果加法有進位(80h+80h=100h),則此位元會被設為 1,當然減法乘法除法也是一樣,對減法來講就是借位。
第二位元 PF (parity flag,同位旗標)表示運算的結果換成二進位後,若有偶數個 1,則此位元設為 1,反之為 0。
第四位元是 AF (auxiliary carry flag,輔助進位旗標),是用於 BCD 運算中的進位。
第六位元是 ZF (zero flag,零旗標),運算結果為零時,此旗標會被設定為 1,若比較相同兩數, ZF 也會被設為一,若比較不相同的兩數,ZF 會被清除為零。
第七位元為 SF (sign flag,符號旗標),運算結果為負數,就是最高位元為 1 時,SF 會被設為 1,否則被清除。
第八位元為 TF (trap flag,陷阱旗標),用於單步追蹤除錯時,所以也稱為追蹤旗標 (trace flag),例如在 MS-DOS 的 DEBUG 中,就是利用 TF 達到單步追蹤的目的。當 TF 設為一時,每執行一個指令便會發生中斷,此中斷就將執行該指令後暫存器列出。
第九位元為 IF (interrupt flag,中斷旗標),
第十位元為 DF (direction flag,方向旗標),當 DF 被清除時,處理字串的索引暫存器會遞增,往高位元組方向處理,反之則遞減。
第十一位元為 OF (overflow flag,溢位旗標),可以反映出運算結果是否超出有號數之範圍。
在此列出 DEBUG 中執行 r 指令後的意義:
-r [Enter] AX=0000 BX=0000 CX=0025 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 DS=10F7 ES=10F7 SS=10F7 CS=10F7 IP=0100 NV UP EI PL NZ NA PO NC 10F7:0100 EB19 JMP 0119
白色的部分就是旗標的狀態,請看下表:
旗標名稱 | 設定(1) | 清除(0) |
---|---|---|
CF,進位(是/否) PF,同位(偶數/基數) AF,輔助進位(是/否) ZF,零(是/否) SF,符號(是/否) IF,中斷(允許/抑制) DF,方向(遞減/遞增) OF,溢位(是/否) | CY PE AC ZR NG EI DN OV | NC PO NA NZ PL DI UP NV |
8087 暫存器
8087 共有五類暫存器,它們是堆疊暫存器(register stack)、狀態字組(status word)、控制字組(control word)、標籤字組(tag word)、例外指標(exception pointer)。
1.堆疊暫存器
8087 共有八個堆疊暫存器,其名稱是 ST(0)、ST(1)、ST(2)……ST(7),其中 ST(0) 被稱為堆疊頂(TOS,Top Of Stack),在組合語言中也可簡寫成 ST。這八個堆疊暫存器每一個都有 80 位元的大小來存放浮點數,並且以暫時實數的形態存放,可以說相當的準確。有許多的運算都是會牽涉到 TOS ,有時也有 TOS 和其他堆疊暫存器做運算,所以 TOS 常常是可以省略,程式設計師得小心這種『隱含』的寫法,以免造成困擾。
2.狀態字組
顧名思義,這個暫存器是用來表示 8087 狀態的,其結構如下圖:
它包含了四項資訊。
- 忙碌指示器(busy indicator):在第 15 位元,這個位元表示 8087 是否正在執行命令或運算,並沒太大的用處。
- 條件碼(condition code):在第 14、10、9、8 位元,以 C3、C2、C1、C0 表示,這幾個位元會受 FTST、FCOM、FXAM 等指令的影響,一般都是用來作為程式控制流程。
- 堆疊頂端指標:第 13、12、11 位元,這三個位元是用來指示現在的堆疊頂是那一個堆疊暫存器。
- 指示例外:第 7、5、4、3、2、1、0 位元,其作用如下表:
3.控制字組
這個 16 位元的暫存器決定了 8087 對不同例外條件的處理、如何捨入、控制實數精確度等等。控制字組的各個欄位如下圖所示:
對於控制字組說明如下:
- IEM (允許中斷遮罩):0 表示允許中斷,1 表示不允許中斷。
- PC (精確度控制):這是為了配合某些電腦廠商所製造較低精密度的機器而設的,其實在 8087 內部的堆疊暫存器都是以 80 位元的精密度存放資料。其表示方式是:
00 表示 24 位元 01 保留未使用 10 表示 53 位元 11 表示 64 位元 內定值
- RC (捨入控制):第 10、11 位元是用來決定如何做捨入動作的。
RC 捨入控制 說明 例子 00 四捨五入 向最近的整數
逢四捨去,遇五進位4.5 ==> 5
-4.5 ==> -501 向負無限大捨入 正值捨去小數部分
負值捨去小數部分後再減一4.5 ==> 4
-4.4 ==> -510 向正無限大捨入 正值捨去小數部分後再加一
負值捨去小數部分4.5 ==> 5
-4.5 ==> -411 向零捨去 不論正負值均捨去小數部分 4.5 ==> 4
-4.5 ==> -4 - IC (無限大控制):8087 有兩種方式可以對『無限大』與有限數作比較,一種是把正無限大與負無限大看成數線上的兩端,沒有數比正無限大還大,也沒有數比負無限大還小,這樣的方式下有限數是可以和正、負無限大比較。另一種是把正、負無限大看成同一點,相當於把數線繞合,這時有限數不可以和正、負無限大比較。前者稱『affine closure』,IC 設為 1;後者稱『projective closure』,IC 設為 0,這種方式也是 FINIT 後的內定值。
- 第 0 到第 5 位元分別是處理例外遮罩的方式,所謂例外是指 8087 運算時發生除以零、高過上限、低於下限、反常值、精確度這五種情形時,是否要讓 8088 知道。如果要讓 8088 知道稱之為『未遮罩』(unmasked) 此時該對應位元設為零,那麼如果發生例外時,可造成程式中斷而跳到設計者所設計的程式來處理。如果不使 8088 知道稱為『被遮罩』,對應位元設為一,當例外發生時 8087 能自動處理,因為 8087 對各種例外可說設計得相當不錯,因此內定值設為一。以下對這五種例外情形遮罩反應做說明:
- 除以零:ZM (zero-divide mask)位元設為零時,適當傳回正無限大或負無限大。
- 高過上限:OM (overflow mask)位元設為零時,適當傳回正無限大或負無限大。
- 低於下限:UM (underflow mask)位元設為零時,傳回反常結果。
- 反常值:DM (denormalized-operand mask)位元設為零時,記憶體運算原照常工作,堆疊暫存器變成異常值。
- 精確度:PM (precision mask)位元設為零時,傳回捨入結果。
標籤字組
8087 有一個 16 位元的標籤字組,標籤字組裏有八個標籤,每兩個位元為一個標籤,分別對應到八個堆疊暫存器。如下圖:
每個標籤代表相對應的堆疊暫存器內存入的數值形態。
- 00:可用數值,包含正常(normal)或異常值(unnormal)。
- 01:零。
- 10:非數值、無限大、反常值(denormal)。
- 11:空的。
8087 所能處理的數值資料形態,除了七種基本形態 (字組整數、短整數、長整數、短實數、長實數、暫時實數、聚集 BCD 整數) 之外,還保留了某些特殊的編碼方式來表示特殊的資料,這些特殊的資料一般應用上較少使用,在這兒簡單說明:
- 反常值(denormal):
- 異常值(unnormal):
- 零:
- 虛零(pseudo zero):
- 無限大:
- 實數未定值(real indefinit):
- 非數值(not a number):