日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

理解单片机系统—汇编语言

發布時間:2024/10/14 windows 68 豆豆
生活随笔 收集整理的這篇文章主要介紹了 理解单片机系统—汇编语言 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

理解單片機系統

一、理解CPU的三種工作模式

從80386開始,CPU有三種工作方式:實模式(real-mode)、保護模式(protected-mode)和虛擬8086模式。只有在剛剛啟動的時候是實模式,等到操作系統運行起來以后就切換到保護模式。實模式只能訪問地址在1M以下的內存稱為常規內存,我們把地址在1M 以上的內存稱為擴展內存。在保護模式下,全部32條地址線有效,可尋址高達4G字節的物理地址空間; 擴充存儲器分段管理機制存儲器分頁管理機制(可選的)不僅為存儲器共享和保護提供了硬件支持,而且為實現虛擬存儲器提供了硬件支持;支持多任務,能夠快速地進行任務切換(switch)和保護任務環境(context); 4個特權級和完善的特權檢查機制,既能實現資源共享又能保證代碼和數據的安全和保密及任務的隔離; 支持虛擬8086方式,便于執行8086程序。

實模式(Real Mode)

它是?Intel公司80286及以后的x86(如80386,80486和80586等)處理器為了兼容以前的處理器(CPU)的一種操作模式。實模式被特殊定義為20位地址內存可訪問空間上,這就意味著它的容量是2的20次冪(1M)的可訪問內存空間(物理內存和BIOS-ROM),軟件可通過這些地址直接訪問BIOS程序和外圍硬件。實模式下處理器沒有硬件級的內存保護概念和多道任務的工作模式。但是為了向下兼容以前的處理器,所以80286及以后的x86系列處理器在開機啟動時仍然先工作在實模式下。80186和早期的處理器僅有一種操作模式,就是后來我們所定義的實模式。實模式雖然能訪問到1M的地址空間,但是由于BIOS的映射作用(即BIOS占用了部分空間地址資源),所以真正能使用的物理內存空間(內存條),也就是在640k到924k之間。1M?地址空間組成是由?16位的段地址和16位的段內偏移地址組成的。用公式表示為:物理地址=左移4位的段地址+偏移地址。

80286處理器體系結構引入了地址保護模式的概念,處理器能夠對內存及一些其他外圍設備做硬件級的保護設置(保護設置實質上就是屏蔽一些地址的訪問)。使用這些新的特性,然而必不可少一些額外的在80186及以前處理器沒有的操作規程。自從最初的x86微處理器規格以后,它對程序開發完全向下兼容,80286芯片被制作成啟動時繼承了以前版本芯片的特性。它工作在實模式下時暫時先關閉了新增的保護功能特性等,因此能使以往的軟件繼續工作在新的芯片下。直到今天,甚至最新的x86處理器都是在計算機加電啟動時都是工作在實模式下,它能運行為以前處理器芯片寫的程序。

DOS操作系統(例如?MS-DOS,DR-DOS)工作在實模式下,微軟Windows早期的版本(它本質上是運行在DOS上的圖形用戶界面應用程序,實際上本身并不是一個操作系統)也是運行在實模式下,直到Windows3.0,它運行期間既有實模式又有保護模式,所以說它是一種混合模式工作。它的保護模式運行有兩種不同意義(因為80286并沒有完全地實現80386及以后的保護模式功能):
“標準保護模式”:這就是程序運行在保護模式下;
“虛擬保護模式”:它實質上還是實模式,是實模式上模擬的保護模式也使用32位地址尋址方式。Windows3.1徹底刪除了對實模式的支持。在80286處理器芯片以后,Windows3.1成為主流操作系統(Windows/80286不是主流產品)。目前差不多所有的X86系列處理器操作系統(Linux,Windows95 and later,OS/2等)都是在啟動時進行處理器設置而進入保護模式的。

實模式工作機理:

  • 對于8086/8088來說計算實際地址是用絕對地址對1M求模。8086的地址線的物理結構:20根,也就是它可以物理尋址的內存范圍為2^20個字節,即1 M空間,但由于8086/8088所使用的寄存器都是16位,能夠表示的地址范圍只有0-64K,這和1M地址空間來比較也太小了,所以為了在8086/8088下能夠訪問1M內存,Intel采取了分段尋址的模式:16位段基地址:16位偏移EA,其絕對地址計算方法為:16位基地址左移4位+16位偏移=20位地址。比如:DS=1000H EA=FFFFH?那么絕對地址就為:10000H +0FFFFH = 1FFFFH?地址單元?。通過這種方法來實現使用16位寄存器訪問1M的地址空間,這種技術是處理器內部實現的,通過上述分段技術模式,能夠表示的最大內存為:?FFFFh: FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes(1M多余出來的部分被稱做高端內存區HMA)。但8086/8088只有20位地址線,只能夠訪問1M地址范圍的數據,所以如果訪問100000h~10FFEFh之間的內存(大于1M空間),則必須有第21根地址線來參與尋址(8086/8088沒有)。因此,當程序員給出超過1M(100000H-10FFEFH)的地址時,因為邏輯上正常,系統并不認為其訪問越界而產生異常,而是自動從0開始計算,也就是說系統計算實際地址的時候是按照對1M求模的方式進行的,這種技術被稱為wrap-around。
  • ?對于80286或以上的CPU通過A20 GATE來控制A20地址線。技術發展到了?80286,雖然系統的地址總線由原來的20根發展為24根,這樣能夠訪問的內存可以達到2^24=16M,但是Intel在設計80286時提出的目標是向下兼容,所以在實模式下系統所表現的行為應該和8086/8088所表現的完全一樣,也就是說,當啟動運行在實模式下時,80386以及后續系列應該和8086/8088完全兼容仍然使用A20地址線。所以說80286芯片存在一個BUG:它開設A20地址線時,在保護模式下,如果程序員訪問100000H-10FFEFH之間的內存,系統將實際訪問這塊內存(沒有wrap-around技術);如果在實模式下,也可以訪問100000H-10FFEFH間的內存,這時采用的是wrap-around技術,這時兩種模式下訪問同一個內存區卻會得到不同的數據,因此說是一個bug。我們來看一副圖:


    為了解決上述兼容性問題,IBM使用鍵盤控制器上剩余的一些輸出線來管理第21根地址線(從0開始數是第20根)?的有效性,被稱為A20 Gate:
  • 如果A20 Gate被打開,則當程序員給出100000H-10FFEFH之間的地址的時候,系統將真正訪問這塊內存區域;
  • 如果A20 Gate被禁止,則當程序員給出100000H-10FFEFH之間的地址的時候,系統仍然使用8086/8088的方式即取模方式(8086仿真)。絕大多數IBM PC兼容機默認的A20 Gate是被禁止的。現在許多新型PC上存在直接通過BIOS功能調用來控制A20 Gate的功能。

保護模式(Protected Mode)

在實模式下,在80286以及更高系列的PC中,即使A20 Gate被打開,在實模式下所能夠訪問的內存最大也只能為10FFEFH,盡管它們的地址總線所能夠訪問的能力都大大超過這個限制。為了能夠訪問10FFEFH以上的內存,則必須進入保護模式。

(286是Intel 80286的另一種叫法)?它又被稱作為虛擬地址保護模式。盡管在Intel 80286手冊中已經提出了虛地址保護模式,但實際上它只是一個指引,真正的32位地址出現在Intel 80386上。保護模式本身是80286及以后兼容處理器序列之后產成的一種操作模式,它具有許多特性設計為提高系統的多道任務和系統的穩定性。例如內存的保護,分頁機制和硬件虛擬存儲的支持?,F代多數的x86處理器操作系統都運行在保護模式下,包括Linux,Free BSD,和Windows3.0(它也運行在實模式下,為了和Windows 2.x應用程序兼容)及以后的版本。

80286及以后的處理器另一種工作模式是實模式(僅當系統啟動的一瞬間),本著向下兼容的原則屏蔽保護模式特性,從而容許老的軟件能夠運行在新的芯片上。作為一個設計規范,所有的x86系列處理器,除嵌入式Intel80387之外,都是系統啟動工作在實模式下,確保遺留下的操作系統向下兼容。它們都必須被啟動程序(操作系統程序最初運行代碼)重新設置而相應進入保護模式的,在這之前任何的保護模式特性都是無效的。在現代計算機中,這種匹配進入保護模式是操作系統啟動時最前沿的動作之一。

在被調停的多道任務程序中,它可以從新工作在實模式下是相當可能的。保護模式的特性是阻止被其他任務或系統內核破壞已經不健全的程序的運行,保護模式也有對硬件的支持,例如中斷運行程序,移動運行進程文檔到另一個進程和置空多任務的保護功能。

386及以后系列處理器不僅具有保護模式又具有32位寄存器,結果導致了處理功能的混亂,因為80286雖然支持保護模式,但是它的寄存器都是16位的,它是通過自身程序設定而模擬出的32位,并非32位寄存器處理。歸咎于這種混亂現象,它促使Windows/386?及以后的版本徹底拋棄80286的虛擬保護模式,以后保護模式的操作系統都是運行在80386以上,不再運行在80286(盡管80286模式支持保護模式),所以說80286是一個過渡芯片,它是一個過渡產品。

盡管?286和386處理器能夠實現保護模式和兼容以前的版本,但是內存的1M以上空間還是不易存取,由于內存地址的回繞,IBM PC XT?(現以廢棄)設計一種模擬系統,它能過欺騙手段訪問到1M以上的地址空間,就是開通了A20地址線。在保護模式里,前32個中斷為處理器異常預留,例如,中斷0D(十進制13)常規保護故障和中斷00是除數為零異常。

如果要訪問更多的內存,則必須進入保護模式,那么,在保護模式下,A20 Gate對于內存訪問有什么影響呢?

為了搞清楚這一點,我們先來看一看A20的工作原理。A20,從它的名字就可以看出來,其實它就是對于A20(從0開始數)的特殊處理(也就是對第21根地址線的處理)。如果A20 Gate被禁止,對于80286來說,其地址為24根地址線,其地址表示為EFFFFF;對于80386極其隨后的32根地址線芯片來說,其地址表示為FFEFFFFF。這種表示的意思是:

  • ?如果A20 Gate被禁止。則其第A20在CPU做地址訪問的時候是無效的,永遠只能被作為0。所以,在保護模式下,如果A20 Gate被禁止,則可以訪問的內存只能是奇數1M段,即1M,3M,5M…,也就是00000-FFFFF,200000-2FFFFF,300000-3FFFFF…
  • 如果A20 Gate被打開。則其第20-bit是有效的,其值既可以是0,又可以是1。那么就可以使A20線傳遞實際的地址信號。如果A20 Gate被打開,則可以訪問的內存則是連續的。

實模式和保護模式的區別

從表面上看,保護模式和實模式并沒有太大的區別,二者都使用了內存段、中斷和設備驅動來處理硬件,但二者有很多不同之處。我們知道,在實模式中,內存被劃分成段,每個段的大小為?64KB?,而這樣的段地址可以用?16位來表示。內存段的處理是通過和段寄存器相關聯的內部機制來處理的,這些段寄存器(?CS?、?DS?、?SS?和ES?)的內容形成了物理地址的一部分。具體來說,最終的物理地址是由?16?位的段地址和?16?位的段內偏移地址組成的。用公式表示為:物理地址?=?左移?4?位的段地址?+?偏移地址。在保護模式下,段是通過一系列被稱之為?“?描述符表?”?的表所定義的。段寄存器存儲的是指向這些表的指針。用于定義內存段的表有兩種:全局描述符表?(GDT)?和局部描述符表?(LDT)?。GDT?是一個段描述符數組,其中包含所有應用程序都可以使用的基本描述符。在實模式中,段長是固定的?(?為?64KB)?,而在保護模式中,段長是可變的,其最大可達?4GB?。LDT?也是段描述符的一個數組。與?GDT?不同,LDT?是一個段,其中存放的是局部的、不需要全局共享的段描述符。每一個操作系統都必須定義一個?GDT?,而每一個正在運行的任務都會有一個相應的?LDT?。每一個描述符的長度是?8?個字節,格式如圖?3?所示。當段寄存器被加載的時候,段基地址就會從相應的表入口獲得。描述符的內容會被存儲在一個程序員不可見的影像寄存器?(shadow register)?之中,以便下一次同一個段可以使用該信息而不用每次都到表中提取。物理地址由?16?位或者?32?位的偏移加上影像寄存器中的基址組成。實模式和保護模式的不同可以從下圖很清楚地看出來。

實模式地址
?

保護模式地址

總結:保護模式同實模式的根本區別是進程內存受保護與否。可尋址空間的區別只是這一原因的果。實模式將整個物理內存看成分段的區域,程序代碼和數據位于不同區域,系統程序和用戶程序沒有區別對待,而且每一個指針都是指向"實在"的物理地址。這樣一來,用戶程序的一個指針如果指向了系統程序區域或其他用戶程序區域,如果這個指針修改了這個區域的某一個值,那么對于這個被修改的系統程序或用戶程序,其后果就很可能是災難性的。為了克服這種低劣的內存管理方式,處理器廠商開發出保護模式。這樣,物理內存地址不能直接被程序訪問,程序內部的地址(虛擬地址)要由操作系統轉化為物理地址去訪問,程序對此一無所知。

至此,進程有了嚴格的邊界,任何其他進程根本沒有辦法訪問不屬于自己的物理內存區域,甚至在自己的虛擬地址范圍內也不是可以任意訪問的,因為有一些虛擬區域已經被放進一些公共系統運行庫。這些區域也不能隨便修改,若修改就會有: SIGSEGV(linux 段錯誤);非法內存訪問對話框(windows 對話框)。

CPU啟動環境為16位實模式,之后可以切換到保護模式。但從保護模式無法切換回實模式 。對于80X86處理器來說,從80386處理器開始,除了以前的實模式外,還增添了保護模式和V86模式。實模式和V86模式都是為了和8086兼容而設置的。

實模式:?
????? 內存尋址方式為:段式尋址,即物理地址=段地址*16?? +?? 段內偏移地址?
????? 可尋址任意地址,所有指令都相當于工作在特權級。
????? dos工作在實模式下?
保護模式:?
????? 內存尋址方式為:支持內存分頁和虛擬內存?
????? 支持多任務,可依靠硬件用一條指令即可實現任務切換,不同任務可工作在不同的優先級下,操作系統工作在最高優先級0上,應用程序則運行在較低優先級上。從實模式到保護模式,需要建立GDT、IDT等數據表,然后通過修改控制寄存?器CR0的控制位(位0)來實現。
????? Windows工作在保護模式下。
虛擬8086模式:?
內存尋址方式:段式尋址,與實模式一樣?
??????? 支持多任務和內存分頁?
??????? v86模式主要是為了在保護模式下兼容以前的實模式應用,即可支持多任務,但每個任務都是實模式的工作方式。另外,中斷和異常等的處理對于不同的工作模式都是不同的,具體的可以去參看一些相關書籍。

二、理解8086微機系統的組成

1、對于匯編程序而言,我們需要關心CPU中的寄存器、存儲器地址、端口(I/O地址)
【內存單元的兩個元素】:?地址(編號)和值(內容)。
【字節、字、雙字】:8086的內存以字節編址,每個內存單元有唯一的地址(物理地址),可以存放一個字節。字:一個字占據兩個連續的字節。雙字:雙字占據兩個連續的字。
【數據的地址對齊】:字單元安排在偶地址(xxx0B),雙字單元安排在模4地址(xx00B)等。對于非對齊地址的數據,處理器訪問時需要多次訪問存儲器,這樣做花費時間較多。
【總線】:8086的系統總線有3種:數據總線;地址總線:8086CPU外部一共有20條地址總線,但在CPU內部一次只能傳送16位地址;控制總線
【I/O】:I/O地址叫做端口,通常采用十六進制數來表達端口:8086的I/O端口為16位,可支持64k個8位端口;I/O地址范圍為:0000H ~ FFFFH
【8086的功能結構】:總線接口單元BIU:主要負責讀取指令和操作數。執行單元EU:主要負責指令譯碼和執行。

2、匯編語言程序、匯編程序、連接程序、調試程序
匯編程序:匯編程序將匯編語言源程序翻譯(或稱作“匯編”)成機器代碼目標模塊。
.ASM -> .OBJ;注意區分匯編程序與匯編語言源程序。
連接程序:連接程序將匯編后的目標模塊轉換為可執行程序。
調試程序:調試程序以便排錯、分析等。

3、寄存器組

16位通用寄存器】
AX、BX、CX、DX、SI、DI、BP、SP
其中AX、BX、CX、DX可以分作高8位和低8位的兩個獨立寄存器。如:AH和AL。我們對其中8位的操作,并不影響另外對應的8位數據。

1、數據寄存器ax、bx、cx、dx
數據寄存器用來存放計算的結果和操作數,也可以存放地址。
每個寄存器又有它們各自的專用目的。

AX——累加器,使用頻度最高,用于算術、邏輯運算以及與外設傳送信息等;
BX——基址寄存器,常用做存放存儲器地址;
CX——計數器,作為循環和串操作等指令中的隱含計數器;
DX——數據寄存器,常用來存放雙字長數據的高16位,或存放外設端口地址。

2、變址寄存器si、di
變址寄存器常用于存儲器尋址時提供地址

SI是源變址寄存器

DI是目的變址寄存器
串操作類指令中,SI和DI具有特別的功能

3、指針寄存器sp、bp
指針寄存器用于尋址內存堆棧內的數據:
SP為堆棧指針寄存器,指示棧頂的偏移地址。
SP不能再用于其他目的,具有專用目的。SP始終指向棧頂。
BP為基址指針寄存器,表示數據在堆棧段中的基地址。
SP和BP寄存器與SS段寄存器聯合使用以確定堆棧段中的存儲單元地址

4、堆棧
8086中堆棧通常有處理器自動維持,由堆棧段寄存器SS和堆棧指針寄存器SP共同指示。

5、指令指針寄存器IP
指示代碼段中指令的偏移地址。與代碼段寄存器CS連用(CS:IP)。

6、標志寄存器
標志(flag)用于反映指令執行結果或控制指令執行形式。

16位的標志寄存器——程序狀態字PSW寄存器。

其中,狀態標志(6個,CF ZF SF PF OF AF)——用來記錄程序運行結果的狀態信息,許多指令的執行都將相應地設置它??刂茦酥?#xff08;3個,DF IF TF)——可由程序根據需要用指令設置,用于控制處理器執行指令的方式。

  • 進位標志CF(Carry Flag):當運算結果的最高有效位有進位(加法)或借位(減法)時,CF=1,or CF=0;
  • 零標志位ZF(Zero Flag):若運算結果為0時,ZF=1,or ZF=0;
  • 符號標志位SF(Sign Flag):運算結果最高位為1,則SF=1,or SF=0。
    有符號數據用最高有效位表示數據的符號。所以,最高有效位就是符號標志的狀態
  • 奇偶標志位PF(Parity Flag):當運算結果最低字節中“1”的個數為零或偶數時,PF=1;or PF=0。
    PF標志僅反映最低8位中“1”的個數是偶或奇,即使是進行16位字操作。
  • 溢出標志OF(Overflow Flag):若算術結果有溢出,OF=1,or OF=0;【什么是溢出?溢出判斷】
  • 輔助進位標志AF(Auxiliary Carry Flag):運算時D3位(低半字節)有進位或借位時,AF=1,or AF=0。這個標志主要由處理器內部使用,用于十進制算術運算調整指令中,用戶一般不必關心。
  • 方向標志DF(Direction Flag):用于串操作指令中,控制地址的變化方向:設置DF=0,存儲器地址自動增加;設置DF=1,存儲器地址自動減少。CLD指令復位方向標志:DF=0;STD指令置位方向標志:DF=1。
  • 中斷允許標志IF(Interrupt-enable Flag):用于控制外部可屏蔽中斷是否可以被處理器響應:設置IF=1,允許中斷;設置IF=0,禁止中斷。CLI指令復位中斷標志:IF=0;STI指令置位中斷標志:IF=1。
  • 陷阱標志TF(Trap Flag):用于控制處理器進入單步操作模式:設置TF=0,處理器正常工作;設置TF=1,處理器單步執行指令。單步執行指令——處理器在每條指令執行結束時,便產生一個編號為1的內部中斷。這種內部中斷稱為單步中斷。所以TF也稱為單步標志。

7、段寄存器
段地址——段的起始地址的高16位地址。段內再由16位二進制數來尋址。
偏移地址——段內存儲單元到段首地址的字節的距離。
物理地址——用20位二進制數表示。地址范圍為00000H ~ FFFFFH。物理地址唯一標識一個存儲單元。
邏輯地址——段地址:偏移地址,邏輯地址不唯一。
物理地址=段地址x16+偏移地址

8086有4個16位的段寄存器。

代碼段CS(Code Segment):指明代碼段的 起始地址。
代碼段用來存放程序的指令序列。指令指針寄存器IP指示下一條指令的偏移地址。處理器利用CS:IP取得下一條要執行的指令。

堆棧段SS(Stack Segment):指明堆棧段的起始地址。
堆棧段確定堆棧所在的主存區域。堆棧指針寄存器SP指示堆棧棧頂的偏移地址。處理器利用SS:SP操作堆棧棧頂的數據。

數據段DS(Data Segment):指明數據段的起始地址。
數據段存放運行程序所用的數據。各種主存尋址方式(有效地址EA)得到存儲器中操作數的偏移地址。處理器利用DS:EA存取數據段中的數據。

附加段ES(Extra Segment):指明附加段的起始地址。
附加段是附加的數據段,也用于數據的保存。各種主存尋址方式(有效地址EA)得到存儲器中操作數的偏移地址。處理器利用ES:EA存取附加段中的數據。
串操作指令將附加段作為其目的操作數的存放區域。

【關于分段】

1、8086對邏輯段的要求:
① 段地址低4位均為0
② 每段最大不超過64KB(216?B),但并不要求必須為64KB
③ 各段之間可以獨立,也可以有重疊

2、如何分配各個邏輯段:
①?程序的指令序列必須安排在代碼段。
② 程序使用的堆棧一定在堆棧段。
③ 程序中的數據默認是安排在數據段,也經常安排在附加段,尤其是串操作的目的區必須是附加段。數據的存放比較靈活,實際上可以存放在任何一種邏輯段中。

8、段超越前綴指令
沒有指明時,一般的數據訪問在DS段;使用BP訪問主存,則在SS段
默認的情況允許改變,需要使用段超越前綴指令;8086指令系統中有4個,用于明確指定數據所在的邏輯段:
CS: ;代碼段超越,使用代碼段的數據
SS: ;堆棧段超越,使用堆棧段的數據
DS: ;數據段超越,使用數據段的數據
ES: ;附加段超越,使用附加段的數據

【段超越示例】

【不允許使用段超越的情況】
串處理指令的目的串必須用ES段;PUSH指令的目的和POP指令的源必須用SS段;指令必須存放在CS段。

【段寄存器的使用規定】

【補充】

【什么是溢出】
處理器內部以補碼表示有符號數。8位表達的整數范圍是:+127~-12816位表達的范圍是:+32767~-32768。如果運算結果超出這個范圍,就產生了溢出。有溢出,說明有符號數的運算結果不正確。

【溢出和進位】
溢出標志OF和進位標志CF是兩個意義不同的標志。
進位標志表示無符號數運算結果是否超出范圍,運算結果仍然正確;
溢出標志表示有符號數運算結果是否超出范圍,運算結果已經不正確。

【如何運用溢出和進位】
處理器對兩個操作數進行運算時,按照無符號數求得結果,并相應設置進位標志CF;同時,根據是否超出有符號數的范圍設置溢出標志OF。應該利用哪個標志,則由程序員來決定。也就是說,如果將參加運算的操作數認為是無符號數,就應該關心進位;認為是有符號數,則要注意是否溢出。

【溢出判斷】
判斷運算結果是否溢出有一個簡單的規則:
只有當兩個相同符號數相加(包括不同符號數相減),而運算結果的符號與原數據符號相反時,產生溢出;因為,此時的運算結果顯然不正確。其他情況下,則不會產生溢出。

補充

1.什么是邏輯地址?
邏輯地址是用戶編程時使用的地址,分為段地址和偏移地址兩部分。
邏輯地址表示形式:3020:055AH---------(匯編語言中,數字后面加H表示16進制)

2.為什么要用邏輯地址?(邏輯地址的產生背景)
8086cpu訪問存儲器時,地址寄存器(16位)要先向地址總線發出地址信號(地址總線是專門用來存取內存地址的,故與內存單元有關,20位),而地址寄存器只有16位,從地址寄存器發出的地址信號,所能訪問的存儲空間只有2^16 = 65536 = 64KB,達不到20位地址總線所提供的地址范圍。針對這種情況,就把內存地址分為若干段,每段有一些存儲單元構成。用段地址指出是哪一段,偏移地址標明是段中的哪一個單元。

3.什么叫段地址,偏移地址?之間有什么關系?
Ⅰ.把內存地址分為若干段,每段有一些存儲單元構成。用段地址指出是哪一段(若是指向同一個存儲單元,段地址可以不一樣,無非就是偏移地址不一樣而已,但是都可以指向同一個物理地址,因此段地址只是一個被訪問的存儲單元(變量)的起始地址,并不是固定的),偏移地址標明是段中的哪一個單元。
Ⅱ.段地址和偏移地址都是16位2進制數。
Ⅲ.段地址和偏移地址有多種組合,故存在多個地址組合指向同一個存儲單元上。

4.邏輯地址唯一么?
不唯一,因為段地址和偏移有多種組合,故存在多個地址組合指向同一個存儲單元上。例如:3020:055AH和3000:07AAH就是兩種組合,但都是指向同一個存儲單元,

5.cpu執行程序時,采用的是邏輯地址還是物理地址?
物理地址---用戶編程時采用的邏輯地址在cpu執行程序時都要轉換成物理地址。這是由cpu的地址加法器完成的。

6.邏輯地址怎樣轉換為物理地址?
轉換時,先將16位的段地址左移4位,相當于乘以16或者16進制的10H,再和偏移地址相加。轉換公式為:物理地址 = 段地址*10H + 偏移地址。如:將3020:055AH轉換為物理地址:----= 3020*10H(左移四位)+055AH = 3075AH

7.段與偏移地址是什么關系?
段是由存儲單元構成的,段包含偏移地址對應的存儲單元。即偏移地址對應的字節存儲單元在段中。.

8.段的大小指的是什么?
指的是這個段包含存儲單元的多少。

9.將內存分段的依據?以及段的相關知識
段地址和偏移地址都是16位二進制數,每段最大64K字節單元(2^16=65536 = 64KB),每段最小16個字節單元(硬性規定),也可以100個,1000個到最多達到65536個。偏移地址范圍:0000H --- FFFFH

10.什么叫小段?
規定每16個字節單元為一小段。

三、理解“邏輯地址、線性地址、物理地址和虛擬地址

1、各種地址概念

物理地址(physical address)
用于內存芯片級的單元尋址,與處理器和CPU連接的地址總線相對應。
——這個概念應該是這幾個概念中最好理解的一個,但是值得一提的是,雖然可以直接把物理地址理解成插在機器上那根內存本身,把內存看成一個從0字節一直到最大空量逐字節的編號的大數組,然后把這個數組叫做物理地址,但是事實上,這只是一個硬件提供給軟件的抽像,內存的尋址方式并不是這樣。所以,說它是與地址總線相對應,是更貼切一些,不過拋開對物理內存尋址方式的考慮,直接把物理地址與物理的內存一一對應,也是可以接受的。也許錯誤的理解更利于形而上的抽像。
虛擬內存(virtual memory)
這是對整個內存的抽像描述。它是相對于物理內存來講的,可以直接理解成不直實的假的內存,例如,一個0x08000000內存地址,它并不對就物理地址上那個大數組中0x08000000 - 1那個地址元素;之所以是這樣,是因為現代操作系統都提供了一種內存管理的抽像,即虛擬內存(virtual memory)。進程使用虛擬內存中的地址,由操作系統協助相關硬件,把它轉換成真正的物理地址。這個轉換,是所有問題討論的關鍵。有了這樣的抽像,一個程序,就可以使用比真實物理地址大得多的地址空間,甚至多個進程可以使用相同的地址。不奇怪,因為轉換后的物理地址并非相同的。
——可以把連接后的程序反編譯看一下,發現連接器已經為程序分配了一個地址,例如,要調用某個函數A,代碼不是call A,而是call 0x0811111111 ,也就是說,函數A的地址已經被定下來了。沒有這樣的轉換,沒有虛擬地址的概念,這樣做是根本行不通的。打住了,這個問題再說下去,就收不住了。
邏輯地址(logical address)
Intel為了兼容,將遠古時代的段式內存管理方式保留了下來。邏輯地址指的是機器語言指令中,用來指定一個操作數或者是一條指令的地址。以上例,我們說的連接器為A分配的0x08111111這個地址就是邏輯地址。不過不好意思,這樣說,好像又違背了Intel中段式管理中,對邏輯地址要求,一個邏輯地址,是由一個段標識符加上一個指定段內相對地址的偏移量,表示為 [段標識符:段內偏移量],也就是說,上例中那個0x08111111,應該表示為[A的代碼段標識符: 0x08111111],這樣,才完整一些
線性地址(linear address)
線性地址或也叫虛擬地址(virtual address)跟邏輯地址類似,它也是一個不真實的地址,如果邏輯地址是對應的硬件平臺段式管理轉換前地址的話,那么線性地址則對應了硬件頁式內存的轉換前地址。
CPU將一個虛擬內存空間中的地址轉換為物理地址,需要進行兩步:首先將給定一個邏輯地址(其實是段內偏移量,這個一定要理解!!!),CPU要利用其段式內存管理單元,先將為個邏輯地址轉換成一個線程地址,再利用其頁式內存管理單元,轉換為最終物理地址。
這樣做兩次轉換,的確是非常麻煩而且沒有必要的,因為直接可以把線性地址抽像給進程。之所以這樣冗余,Intel完全是為了兼容而已。

//導讀注意:下面2-5部分,分別依次按照CPU段式內存管理、Linux段式內存管理、CPU頁式內存管理、Linux頁式內存管理

2CPU段式內存管理,邏輯地址如何轉換為線性地址

一個邏輯地址由兩部份組成,段標識符: 段內偏移量。段標識符是由一個16位長的字段組成,稱為段選擇符。其中前13位是一個索引號。后面3位包含一些硬件細節,如圖:

索引號,或者直接理解成數組下標——那它總要對應一個數組吧,它又是什么東東的索引呢?這個東東就是段描述符(segment descriptor)”,呵呵,段描述符具體地址描述了一個段(對于這個字眼的理解,我是把它想像成,拿了一把刀,把虛擬內存,砍成若干的截——段)。這樣,很多個段描述符,就組了一個數組,叫段描述符表,這樣,可以通過段標識符的前13位,直接在段描述符表中找到一個具體的段描述符,這個描述符就描述了一個段,我剛才對段的抽像不太準確,因為看看描述符里面究竟有什么東東——也就是它究竟是如何描述的,就理解段究竟有什么東東了,每一個段描述符由8個字節組成,如下圖:

這些東東很復雜,雖然可以利用一個數據結構來定義它,不過,我這里只關心一樣,就是Base字段,它描述了一個段的開始位置的線性地址。
Intel設計的本意是,一些全局的段描述符,就放在全局段描述符表(GDT)”中,一些局部的,例如每個進程自己的,就放在所謂的局部段描述符表(LDT)”中。那究竟什么時候該用GDT,什么時候該用LDT呢?這是由段選擇符中的T1字段表示的,=0,表示用GDT=1表示用LDT。
GDT在內存中的地址和大小存放在CPUgdtr控制寄存器中,而LDT則在ldtr寄存器中。
好多概念,像繞口令一樣。這張圖看起來要直觀些:

首先,給定一個完整的邏輯地址[段選擇符:段內偏移地址]
1、看段選擇符的T1=0還是1,知道當前要轉換是GDT中的段,還是LDT中的段,再根據相應寄存器,得到其地址和大小。我們就有了一個數組了。
2、拿出段選擇符中前13位,可以在這個數組中,查找到對應的段描述符,這樣,它了Base,即基地址就知道了。
3、把Base + offset,就是要轉換的線性地址了。
還是挺簡單的,對于軟件來講,原則上就需要把硬件轉換所需的信息準備好,就可以讓硬件來完成這個轉換了。OK,來看看Linux怎么做的。

3Linux的段式內存管理

Intel要求兩次轉換,這樣雖說是兼容了,但是卻是很冗余,呵呵,沒辦法,硬件要求這樣做了,軟件就只能照辦,怎么著也得形式主義一樣。另一方面,其它某些硬件平臺,沒有二次轉換的概念,Linux也需要提供一個高層抽像,來提供一個統一的界面。所以,Linux的段式管理,事實上只是哄騙了一下硬件而已。按照Intel的本意,全局的用GDT,每個進程自己的用LDT——不過Linux則對所有的進程都使用了相同的段來對指令和數據尋址。即用戶數據段,用戶代碼段,對應的,內核中的是內核數據段和內核代碼段。這樣做沒有什么奇怪的,本來就是走形式嘛,像我們寫年終總結一樣。
include/asm-i386/segment.h

  • #define GDT_ENTRY_DEFAULT_USER_CS? ? ? ? 14
  • #define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)
  • ?
  • #define GDT_ENTRY_DEFAULT_USER_DS? ? ? ? 15
  • #define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)
  • ?
  • #define GDT_ENTRY_KERNEL_BASE? ? ? ? 12
  • ?
  • #define GDT_ENTRY_KERNEL_CS? ? ???(GDT_ENTRY_KERNEL_BASE + 0)
  • #define __KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)
  • ?
  • #define GDT_ENTRY_KERNEL_DS? ???(GDT_ENTRY_KERNEL_BASE + 1)
  • #define __KERNEL_DS ????(GDT_ENTRY_KERNEL_DS * 8)
  • 復制代碼
    把其中的宏替換成數值,則為:

  • #define __USER_CS 115? ?? ???[00000000 1110??0??11]
  • #define __USER_DS 123? ?? ???[00000000 1111??0??11]
  • #define __KERNEL_CS 96? ?? ?[00000000 1100??0??00]
  • #define __KERNEL_DS 104? ? [00000000 1101??0??00]
  • 復制代碼
    方括號后是這四個段選擇符的16位二制表示,它們的索引號和T1字段值也可以算出來了

  • __USER_CS? ?? ?? ?? ???index= 14? ?T1=0
  • __USER_DS? ?? ?? ?? ?? index= 15? ?T1=0
  • __KERNEL_CS? ?? ?? ???index=??12??T1=0
  • __KERNEL_DS? ?? ?? ???index= 13? ?T1=0
  • 復制代碼
    T1均為0,則表示都使用了GDT,再來看初始化GDT的內容中相應的12-15(arch/i386/head.S)

  • .quad 0x00cf9a000000ffff? ? ? ? /* 0x60 kernel 4GB code at 0x00000000 */
  • .quad 0x00cf92000000ffff? ? ? ? /* 0x68 kernel 4GB data at 0x00000000 */
  • .quad 0x00cffa000000ffff? ? ? ? /* 0x73 user 4GB code at 0x00000000 */
  • .quad 0x00cff2000000ffff? ? ? ? /* 0x7b user 4GB data at 0x00000000 */
  • 復制代碼
    按照前面段描述符表中的描述,可以把它們展開,發現其16-31位全為0,即四個段的基地址全為0
    這樣,給定一個段內偏移地址,按照前面轉換公式,0 + 段內偏移,轉換為線性地址,可以得出重要的結論,Linux下,邏輯地址與線性地址總是一致(是一致,不是有些人說的相同)的,即邏輯地址的偏移量字段的值與線性地址的值總是相同的。!!!
    忽略了太多的細節,例如段的權限檢查。呵呵。
    Linux中,絕大部份進程并不例用LDT,除非使用Wine ,仿真Windows程序的時候。

    4.CPU的頁式內存管理

    CPU的頁式內存管理單元,負責把一個線性地址,最終翻譯為一個物理地址(注意,相互獨立的進程都有自己的頁目錄和頁表,就算多個進程具有相同的線性地址,最后轉換到物理地址也是不一樣的,所以不會互相干擾)。從管理和效率的角度出發,線性地址被分為以固定長度為單位的組,稱為頁(page),例如一個32位的機器,線性地址最大可為4G,可以用4KB為一個頁來劃分,這頁,整個線性地址就被劃分為一個tatol_page[2^20]的大數組,共有220個次方個頁。這個大數組我們稱之為頁目錄。目錄中的每一個目錄項,就是一個地址——對應的頁的地址。另一類“,我們稱之為物理頁,或者是頁框、頁楨的。是分頁單元把所有的物理內存也劃分為固定長度的管理單位,它的長度一般與內存頁是一一對應的。
    這里注意到,這個total_page數組有2^20個成員,每個成員是一個地址(32位機,一個地址也就是4字節),那么要單單要表示這么一個數組,就要占去4MB的內存空間。為了節省空間,引入了一個二級管理模式的機器來組織分頁單元。文字描述太累,看圖直觀一些:

    如上圖,
    1、分頁單元中,頁目錄是唯一的,它的地址放在CPUcr3寄存器中,是進行地址轉換的開始點。
    2、每一個活動的進程,因為都有其獨立的對應的虛似內存(頁目錄也是唯一的),那么它也對應了一個獨立的頁目錄地址。——運行一個進程,需要將它的頁目錄地址放到cr3寄存器中,將別個的保存下來。
    3、每一個32位的線性地址被劃分為三部份,面目錄索引(10):頁表索引(10):偏移(12)

    依據以下步驟進行轉換:
    1、從cr3中取出進程的頁目錄地址(操作系統負責在調度進程的時候,把這個地址裝入對應寄存器);
    2、根據線性地址前十位,在數組中,找到對應的索引項,因為引入了二級管理模式,頁目錄中的項,不再是頁的地址,而是一個頁表的地址。(又引入了一個數組),頁的地址被放到頁表中去了。
    3、根據線性地址的中間十位,在頁表(也是數組)中找到頁的起始地址;
    4、將頁的起始地址與線性地址中最后12位相加,得到最終我們想要的葫蘆;

    這個轉換過程,應該說還是非常簡單地。全部由硬件完成,雖然多了一道手續,但是節約了大量的內存,還是值得的。那么再簡單地驗證一下:
    1、這樣的二級模式是否仍能夠表示4G的地址;
    頁目錄共有:2^10項,也就是說有這么多個頁表
    每個目表對應了:2^10頁;
    每個頁中可尋址:2^12個字節。
    還是2^32 = 4GB

    2、這樣的二級模式是否真的節約了空間;
    也就是算一下頁目錄項和頁表項共占空間 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么說呢!!!
    紅色錯誤,標注一下,后文貼中有此討論。。。。。。
    <深入理解計算機系統>中的解釋,二級模式空間的節約是從兩個方面實現的:
    A
    、如果一級頁表中的一個頁表條目為空,那么那所指的二級頁表就根本不會存在。這表現出一種巨大的潛在節約,因為對于一個典型的程序,4GB虛擬地址空間的大部份都會是未分配的;
    B、只有一級頁表才需要總是在主存中。虛擬存儲器系統可以在需要時創建,并頁面調入或調出二級頁表,這就減少了主存的壓力。只有最經常使用的二級頁表才需要緩存在主存中。——不過Linux并沒有完全享受這種福利,它的頁表目錄和與已分配頁面相關的頁表都是常駐內存的。
    值得一提的是,雖然頁目錄和頁表中的項,都是4個字節,32位,但是它們都只用高20位,低12位屏蔽為0——把頁表的低12屏蔽為0,是很好理解的,因為這樣,它剛好和一個頁面大小對應起來,大家都成整數增加。計算起來就方便多了。但是,為什么同時也要把頁目錄低12位屏蔽掉呢?因為按同樣的道理,只要屏蔽其低10位就可以了,不過我想,因為12>10,這樣,可以讓頁目錄和頁表使用相同的數據結構,方便。
    本貼只介紹一般性轉換的原理,擴展分頁、頁的保護機制、PAE模式的分頁這些麻煩點的東東就不啰嗦了……可以參考其它專業書籍。

    5.Linux的頁式內存管理

    原理上來講,Linux只需要為每個進程分配好所需數據結構,放到內存中,然后在調度進程的時候,切換寄存器cr3,剩下的就交給硬件來完成了(呵呵,事實上要復雜得多,不過偶只分析最基本的流程)。
    前面說了i386的二級頁管理架構,不過有些CPU,還有三級,甚至四級架構,Linux為了在更高層次提供抽像,為每個CPU提供統一的界面。提供了一個四層頁管理架構,來兼容這些二級、三級、四級管理架構的CPU。這四級分別為:
    頁全局目錄PGD(對應剛才的頁目錄)
    頁上級目錄PUD
    (新引進的)
    頁中間目錄PMD
    (也就新引進的)
    頁表PT
    (對應剛才的頁表)。

    整個轉換依據硬件轉換原理,只是多了二次數組的索引罷了,如下圖:

    那么,對于使用二級管理架構32位的硬件,現在又是四級轉換了,它們怎么能夠協調地工作起來呢?嗯,來看這種情況下,怎么來劃分線性地址吧!
    從硬件的角度,32位地址被分成了三部份——也就是說,不管理軟件怎么做,最終落實到硬件,也只認識這三位老大。
    從軟件的角度,由于多引入了兩部份,,也就是說,共有五部份。——要讓二層架構的硬件認識五部份也很容易,在地址劃分的時候,將頁上級目錄和頁中間目錄的長度設置為0就可以了。
    這樣,操作系統見到的是五部份,硬件還是按它死板的三部份劃分,也不會出錯,也就是說大家共建了和諧計算機系統。
    這樣,雖說是多此一舉,但是考慮到64位地址,使用四層轉換架構的CPU,我們就不再把中間兩個設為0了,這樣,軟件與硬件再次和諧——抽像就是強大呀!!!
    例如,一個邏輯地址已經被轉換成了線性地址,0x08147258,換成二制進,也就是:
    0000100000 0101000111 001001011000
    內核對這個地址進行劃分
    PGD = 0000100000
    PUD = 0
    PMD = 0
    PT = 0101000111
    offset = 001001011000

    現在來理解Linux針對硬件的花招,因為硬件根本看不到所謂PUD,PMD,所以,本質上要求PGD索引,直接就對應了PT的地址。而不是再到PUDPMD中去查數組(雖然它們兩個在線性地址中,長度為02^0 =1,也就是說,它們都是有一個數組元素的數組),那么,內核如何合理安排地址呢?
    從軟件的角度上來講,因為它的項只有一個,32位,剛好可以存放與PGD中長度一樣的地址指針。那么所謂先到PUD,到到PMD中做映射轉換,就變成了保持原值不變,一一轉手就可以了。這樣,就實現了邏輯上指向一個PUD,再指向一個PDM,但在物理上是直接指向相應的PT的這個抽像,因為硬件根本不知道有PUDPMD這個東西

    然后交給硬件,硬件對這個地址進行劃分,看到的是:
    頁目錄 = 0000100000
    PT = 0101000111
    offset = 001001011000

    嗯,先根據0000100000(32),在頁目錄數組中索引,找到其元素中的地址,取其高20位,找到頁表的地址,頁表的地址是由內核動態分配的,接著,再加一個offset,就是最終的物理地址了。

    參見:
    http://www.cnblogs.com/diyingyun/archive/2012/01/03/2311327.html
    "
    http://blog.sina.com.cn/s/blog_79ba23780102vz77.html"
    "https://www.cnblogs.com/exRunner/p/7531850.html"

    ?

    總結

    以上是生活随笔為你收集整理的理解单片机系统—汇编语言的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。