【自學組合語言Day-02】第二章:暫存器(CPU工作原理)

Au9_0xD發表於2020-12-10

參考書籍:《組合語言(第2版)》——王爽,參考視訊:《零基礎入門學習組合語言》——小甲魚

第二章:暫存器(CPU工作原理)

前言

 一個典型的CPU由運算器、控制器、暫存器等器件組成,這些器件靠內部匯流排相連。
內部匯流排實現CPU內部各個器件之間的聯絡。
外部匯流排實現CPU和主機板上其它器件的聯絡。

 不同的CPU,暫存器的個數和結構是不相同的。
 8086CPU有14個暫存器,它們的名稱分別為:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。

2.1 通用暫存器

 8086CPU的所有暫存器都是16位的,可以存放兩個位元組
 AX、BX、CX、DX這4個暫存器通常用來存放一般性的資料,被稱為通用暫存器

資料:20000
二進位制表示:0100111000100000
在暫存器AX中的儲存:
在這裡插入圖片描述
一個16位暫存器能儲存的資料最大值為2^16-1。

 8086上一代CPU中的暫存器都是8位的,為保證相容性,8086CPU中的AX、BX、CX、DX這4個暫存器都可以分為兩個獨立的8位暫存器使用,H為高8位,L為低8位。

  • AX可分為AH和AL;
  • BX可分為BH和BL;
  • CX可分為CH和CL;
  • DX可分為DH和DL;

以AX為例,8086CPU的16位暫存器分為兩個8位暫存器的情況如下圖所示。
在這裡插入圖片描述
在這裡插入圖片描述

2.2 字在暫存器中的儲存

 字:記為word,一個字由兩個位元組組成,這兩個位元組分別成為高位位元組和低位位元組。

關於數制的討論

 由於一個記憶體單元可以存放8位資料,CPU中的暫存器又可存放n個8位資料。也就是說,計算機中的資料大多是由1~n個8位資料構成的。

 用16進位制來表示資料可以直觀的看出這個資料是由哪些8位資料構成的,每4個二進位制位能夠表示一個16進位制數

2.3 幾條彙編指令

在這裡插入圖片描述

彙編指令或暫存器的名稱不區分大小寫。

課本例題
 add al,93H ,在執行前,al中的資料為C5H,相加後所得的值為158H,但是al為8位暫存器,只能存放兩位16進位制數,所以最高位的1丟失,ax的資料為:0058H。

這裡的丟失,指的是進位值不能在8位暫存器中儲存,但是CPU並不是真的丟棄這個進位值,關於這個問題,我們將在後面的課程中討論。

 注意,此時al作為一個獨立的8位暫存器來使用,和ah沒有關係。CPU在執行這條指令時認為ah和al是兩個不相關的暫存器。不要錯誤地認為,諸如add al,93H 的指令產生的進位會儲存在ah中。

 在進行資料傳送或運算時,要注意指令的兩個操作物件的位數應當是一致的。
例如,以下指令都是錯誤的

mov ax,bl            (在8位暫存器和16位暫存器之間傳送資料)
mov ah,ax            (在16位暫存器和8位暫存器之間傳送資料)
mov al,20000         (8位暫存器最大可存放值為255的資料)
add al,100H          (將一個高於8位的資料加到一個8位暫存器中)

檢測點2.1

(1)寫出每條彙編指令執行後相關暫存器中的值。
mov ax,62627   AX=F4A3H
mov ah,31H     AX=31A3H
mov al,23H      AX=3123H
add ax,ax       AX=6246H  
mov bx,826CH   BX=826CH  
mov cx,ax       CX=6246H  
mov ax,bx       AX=826CH  
add ax,bx       AX=04D8H  
mov al,bh       AX=0482H
mov ah,bl       AX=6C82H
add ah,ah      AX=D882H  
add al,6        AX=D888H  
add al,al        AX=D810H  
mov ax,cx      AX=6246H

解析: 將暫存器看成4個16進位制數相加減,注意進位的丟失問題即可。
舉例:第8空:add ax,bx  AX = 826CH + 826CH = 104D8H,最高位1會丟失,所以最後答案為04D8H。

(2)只能使用目前學過的彙編指令,最多使用4條指令,程式設計計算2的4次方。

mov  ax,2         AX=2  
add  ax,ax        AX=4  
add  ax,ax        AX=8  
add  ax,ax        AX=16

2.4 實體地址

 CPU訪問記憶體單元時,要給出記憶體單元的地址。所有的記憶體單元構成的儲存空間是一個一維的線性空間,每一個記憶體單元在這個空間中都有唯一的地址,我們將這個唯一的地址稱為實體地址

2.5 16位結構的CPU

 16位結構描述了一個CPU具有以下3個方面的結構特性:

  1. 運算器一次最多可以處理16位的資料;
  2. 暫存器的最大寬度為16位;
  3. 暫存器和運算器之間的通路為16位。

 記憶體單元的地址在送上地址匯流排之前,必須在CPU中處理、傳輸、暫時存放,8086是16位結構的CPU,能夠一次性處理、傳輸、暫時儲存16位的地址。

2.6 8086CPU給出實體地址的方法

 8086有20位地址匯流排,可以傳送20位地址,定址能力為1MB。
 而8086CPU的內部又是16位結構,如果將地址從內部簡單地發出,那麼它只能送出16位的地址,表現出的定址能力只有64KB。

8086CPU採用一種在內部用兩個16位地址合成的方法來形成一個20位的實體地址。
在這裡插入圖片描述
如圖所示,當8086CPU要讀寫記憶體時:
(1)CPU中相關部件提供兩個16位的地址,一個稱為段地址,另一個稱為偏移地址
(2)段地址和偏移地址通過內部匯流排送入一個稱為地址加法器的部件;
(3)地址加法器將兩個16位地址合成為一個20位的實體地址
(4)地址加法器通過內部匯流排將20位實體地址送入輸入輸出控制電路
(5)輸入輸出控制電路將20位實體地址送上地址匯流排
(6)20位實體地址被地址匯流排傳送到儲存器(記憶體)

地址加法器計算實體地址的方法:

    實體地址=段地址×16+偏移地址

例如:8086CPU要訪問地址為123C8H的記憶體單元,此時,地址加法器的工作過程如下圖所示(圖中資料皆為16進位制)。
在這裡插入圖片描述
"段地址×16"更為常用的說法是左移4位(2進位制位)。

將16進位制數2H進行左移運算:
在這裡插入圖片描述
(1)一個資料的二進位制形式左移1位,相當於該資料乘以2;
(2)一個資料的二進位制形式左移N位,相當於該資料乘以2的N次方;
(3)地址加法器如何完成段地址×16的運算?就是將以二進位制形式存放的段地址左移4位。

綜上,一個X進位制的資料左移1位,相當於該資料乘以X。

2.7 “段地址×16+偏移地址=實體地址”的本質含義”

 “段地址×16+偏移地址=實體地址”的本質含義是:CPU在訪問記憶體時,用一個基礎地址(段地址×16)和一個相對於基礎地址的偏移地址相加,給出記憶體單元的實體地址

2.8 段的概念

錯誤認識:
 記憶體被劃分成了一個一個的段,每一個段有一個段地址。

其實:
 記憶體並沒有分段,段的劃分來自於CPU,由於8086CPU用“基礎地址(段地址×16)+偏移地址=實體地址”的方式給出記憶體單元的實體地址,使得我們可以用分段的方式來管理記憶體。
在這裡插入圖片描述
以後,在程式設計時可以根據需要,將若干地址連續的記憶體看作一個段,用段地址×16定位段的起始地址(基礎地址),用偏移地址定位段中的記憶體單元。即起始地址+偏移地址=實體地址的思想。

兩個注意點:
(1)段地址×16必然是16的倍數,所以一個段的起始地址也一定是16的倍數;
(2)偏移地址為16位,16位地址的定址能力為64KB,所以一個段的長度最大為64KB。

記憶體單元地址小結

  1. CPU訪問記憶體單元時,必須向記憶體提供記憶體單元的實體地址。

  2. 8086CPU在內部用段地址和偏移地址移位相加的方法形成最終的實體地址。

  3. CPU可以用不同的段地址和偏移地址形成同一個實體地址。
    在這裡插入圖片描述

  4. 偏移地址為16位,變化範圍為0~FFFFH,僅用偏移地址來定址最多可尋64K個記憶體單元。

  5. “資料在21F60H記憶體單元中。”對於8086PC機的兩種描述:
    (a)資料存放在記憶體2000:1F60單元中;(較常用)
    (b)資料存放在記憶體的2000段中的1F60H單元中。

  6. 可根據需要,將地址連續、起始地址為16的倍數的一組記憶體單元定義為一個段。

檢測點2.2

(1)給定段地址為0001H,僅通過變化偏移地址定址,CPU的定址範圍為00010H1000FH

解析:
實體地址=SA * 16 + EA    
EA的變化範圍為0H ~ FFFFH    
實體地址範圍為(SA * 16 + 0H)~ (SA * 16 + FFFFH)    
現在SA=0001H,那麼定址範圍為(0001H * 16 + 0H)~(0001H * 16 + FFFFH) = 00010H ~ 1000FH 

(2)有一資料存放在記憶體20000H單元中,現給定段地址為SA,若想用偏移地址尋到此單元。則SA應滿足的條件是:最小是1001H2000H

解析:
實體地址=SA * 16 + EA    
20000h=SA * 16 + EA    
SA = (20000H - EA) / 16 = 2000H - EA / 16    
EA取最大值時,SA = 2000H - FFFFH / 16 = 1001H,SA為最小值    
EA取最小值時,SA = 2000H - 0H / 16 = 2000H,SA為最大值    
這裡的FFFFH / 16 = FFFH是通過WIN自帶計算器算的,按位移來算確實應該為FFF.FH,這裡小數點後的F應該是省略了,單就除法來說,應有商和餘數,但此題要求的是地址最大和最小,所以餘數忽略了。   
如果根據位移的演算法(段地址×16=16進位制左移一位),小數點後應該是不能省略的,我們可以反過來再思考下,如果SA為1000H的話,小數點後省略SA=1000H,EA取最大FFFFH,實體地址為1FFFFH,將無法尋到20000H單元 這道題不應看成是單純的計算題。

2.9 段暫存器

 8086CPU有4個段暫存器:CS、DS、SS、ES。當8086CPU要訪問記憶體時由這4個段暫存器提供記憶體單元的段地址。

2.10 CS和IP

 CS和IP是8086中最關鍵的兩個暫存器,它們指示了CPU當前要讀取指令的地址。CS為程式碼段暫存器,IP為指令指標暫存器。

 任意時刻,設CS中的內容為M,IP中的內容為N,8086CPU將從記憶體M×16+N單元開始,讀取一條指令並執行。
在這裡插入圖片描述
8086CPU的工作過程可以簡要描述如下。
(1)從CS:IP指向的記憶體單元讀取指令,讀取的指令進入指令緩衝器;
(2)IP=IP+所讀取指令的長度,從而指向下一條指令;
(3)執行指令。轉到步驟(1),重複這個過程。

在8086CPU加電啟動或復位後(即CPU剛開始工作時),CS和IP被設定為CS=FFFFH,IP=0000H。

即在8086PC機剛啟動時,CPU從記憶體FFFF0H單元中讀取指令執行,該單元中的指令是8086PC機開機後執行的第一條指令。

在任何時候,CPU將CS、IP中的內容當作指令的段地址和偏移地址,用它們合成指令的實體地址到記憶體中讀取指令碼並執行。

如果說,記憶體中的一段資訊曾被CPU執行過的話,那麼,它所在的記憶體單元必然被CS:IP指向過。

2.11 修改CS、IP的指令

 在CPU中,程式設計師能夠用指令讀寫的部件只有暫存器,程式設計師可以通過改變暫存器中的內容實現對CPU的控制。

 CPU從何處執行指令是由CS、IP中的內容決定的,程式設計師可以通過改變CS、IP中的內容來控制CPU執行目標指令。

  1. 同時修改CS、IP的內容:
    jmp 段地址:偏移地址
      jmp 2AE3:3
      jmp 3:0B16
    注意:jmp 3:0B16
    是指CS=0003H,IP=0B16。

  2. 僅修改IP的內容:
    jmp 某一合法暫存器
      jmp ax (類似於mov IP,ax)
      jmp bx

    例題:
    初始:CS=2000H,IP=0000H
    在這裡插入圖片描述
    執行步驟為:
    (1)mov ax,6622
    (2)jmp 1000:3
    (3)mov ax,0000
    (4)mov bx,ax
    (5)jmp bx
    (6)mov ax,0123H
    (7)轉到第(3)步執行(不斷迴圈)

2.12 程式碼段

 對於8086PC機,在程式設計時,可以根據需要,將一組記憶體單元定義為一個段。

 可以將長度為NN≤64KB)的一組程式碼,存在一組地址連續、起始地址為16的倍數的記憶體單元中,這段記憶體是用來存放程式碼的,從而定義了一個程式碼段

例如:
在這裡插入圖片描述
這段長度為10位元組的指令,存在從123B0H ~ 123B9H的一組記憶體單元中,我們就可以認為,123B0H ~ 123B9H這段記憶體單元是用來存放程式碼的,是一個程式碼段,它的段地址為123BH,長度為10位元組。

  • 如何使得程式碼段中的指令被執行呢?

     將一段記憶體當作程式碼段,僅僅是我們在程式設計時的一種安排,CPU並不會由於這種安排,就自動地將我們定義的程式碼段中的指令當作指令來執行。

    CPU只認被CS:IP指向的記憶體單元中的內容為指令。

2.9 ~ 2.12 小結

  1. 段地址在8086CPU的暫存器中存放。當8086CPU要訪問記憶體時,由段暫存器提供記憶體單元的段地址。8086CPU有4個段暫存器,其中CS用來存放指令的段地址。

  2. CS存放指令的段地址,IP存放指令的偏移地址。
    8086機中,任意時刻,CPU將CS:IP指向的內容當作指令執行。

  3. 8086CPU的工作過程:
    (1)從CS:IP指向記憶體單元讀取指令,讀取的指令進入指令緩衝器;
    (2)IP指向下一條指令;
    (3)執行指令。(轉到步驟(1),重複這個過程。)

  4. 8086CPU提供轉移指令修改CS、IP的內容。(jmp)

檢測點2.3

下面的3條指令執行後,CPU幾次修改IP?都是在什麼時候?最後IP中的值是多少?(sub 為減)
mov ax,bx
sub ax,bx
jmp ax

答:一共修改四次。 
第一次:讀取mov ax,bx之後  
第二次:讀取sub ax,ax之後  
第三次:讀取jmp ax之後  
第四次:執行jmp ax修改IP
  
最後IP的值為0000H,因為最後ax中的值為0000H,所以IP中的值也為0000H。

實驗1 檢視CPU和記憶體,用機器指令和彙編指令程式設計

1. 預備知識:Debug的使用

Debug是DOS、Windows都提供的真實模式(8086方式)程式的除錯工具。使用它,可以檢視CPU各種暫存器中的內容、記憶體的情況和在機器碼級跟蹤程式的執行。

win10下Debug的安裝與配置:https://www.cnblogs.com/zhaijiahui/p/10148698.html
或者在虛擬機器裝個XP系統開啟cmd就可以直接用了。

Debug基本命令:

  • R命令:檢視、改變CPU暫存器的內容;
  • D命令:檢視記憶體中的內容;
  • E命令:改寫記憶體中的內容;
  • U命令:將記憶體中的機器指令翻譯成彙編指令;
  • T命令:執行一條機器指令;
  • A命令:以彙編指令的格式在記憶體中寫入一條機器指令。

2. 示例:

  1. 用R命令檢視、改變CPU暫存器的內容。
    在這裡插入圖片描述
    我們已經知道AX、BX、CX、DX、CS、IP這6個暫存器,其他暫存器如SP、BP、SI、DI、DS、ES、SS、標誌暫存器等先不予理會。

    此時,CS=073F,IP=0100,也就是說記憶體073F:0100處的指令為CPU當前要讀取、執行的指令。

    在所有的暫存器下方,Debug還列出了CS:IP所指向的記憶體單元073F:0100所存放的機器碼0000,並將它翻譯成彙編指令ADD [BX+SI],AL。

    還可以用R命令來改變暫存器中的內容,如輸入“r ax”後按Enter鍵,將出現“:”,然後我們輸入1111,這樣就把AX中的內容改成了1111,然後我們再用R命令檢視一下。
    在這裡插入圖片描述
  2. 用D命令檢視記憶體中的內容。

    格式:“d 段地址:偏移地址”
    如檢視FFFF0H處的內容,Debug將自動列出從指定記憶體單元開始的128個記憶體單元的內容,如下圖。從FFFF:0 ~ FFFF:F的內容在第一行,中間的橫線是分隔符,左邊是0 ~ 7,右邊是8 ~ F,這樣便於檢視。中間是每個記憶體單元的內容,用16進位制數表示。右邊是每個記憶體單元中的資料對應的可顯示的ASCⅡ碼字元。
    在這裡插入圖片描述
  3. 用E命令改寫記憶體中的內容。

    比如將記憶體1000:0 ~ 1000:9單元中的記憶體分別改成0、1、2、3、4、5、6、7、8、9,可以用“e 起始地址 資料 資料 資料 資料……”的格式來進行。
    然後用d命令檢視1000:0 ~ 1000:F中的內容。
    在這裡插入圖片描述
  4. 用E命令向記憶體中寫入機器碼,用U命令檢視記憶體中機器碼的含義,用T命令執行記憶體中的機器碼。

    (1)用E命令將機器碼b80100(mov ax,0001),b90200(mov cx,0002),01c8(add ax,cx)寫入記憶體1000:0中。然後用“u 1000:0”將從1000:0開始的記憶體單元中的內容翻譯為彙編指令。
    在這裡插入圖片描述
    可以看到前三條指令就是我們剛才寫入的。然後如何執行呢?要想用T命令控制CPU執行我們寫在1000:0的指令,必須先讓CS:IP指向1000:0。所以我們先用R命令檢視並修改CS、IP中的內容。
    在這裡插入圖片描述
    如上圖,修改完成後,就可以用T命令來執行我們寫入的指令了。如下圖,根據我們之前寫入的指令觀察相關暫存器內容的變化。
    在這裡插入圖片描述
  5. 用A命令以彙編指令的形式在記憶體中寫入機器指令。

    前面我們使用E命令寫入機器指令,這樣很不方便,我們可以用A命令直接寫入彙編指令。如下圖,我們先用A命令寫入幾條彙編指令,結束時按Enter即可,然後用D命令檢視記憶體中的機器碼,然後用U命令將記憶體中的機器碼翻譯成彙編指令,可以看出和我們輸入的彙編指令是一樣的。
    在這裡插入圖片描述

相關文章