理解单片机系统—汇编语言
理解單片機系統(tǒng)
一、理解CPU的三種工作模式
從80386開始,CPU有三種工作方式:實模式(real-mode)、保護模式(protected-mode)和虛擬8086模式。只有在剛剛啟動的時候是實模式,等到操作系統(tǒng)運行起來以后就切換到保護模式。實模式只能訪問地址在1M以下的內(nèi)存稱為常規(guī)內(nèi)存,我們把地址在1M 以上的內(nèi)存稱為擴展內(nèi)存。在保護模式下,全部32條地址線有效,可尋址高達4G字節(jié)的物理地址空間; 擴充存儲器分段管理機制和存儲器分頁管理機制(可選的)不僅為存儲器共享和保護提供了硬件支持,而且為實現(xiàn)虛擬存儲器提供了硬件支持;支持多任務(wù),能夠快速地進行任務(wù)切換(switch)和保護任務(wù)環(huán)境(context); 4個特權(quán)級和完善的特權(quán)檢查機制,既能實現(xiàn)資源共享又能保證代碼和數(shù)據(jù)的安全和保密及任務(wù)的隔離; 支持虛擬8086方式,便于執(zhí)行8086程序。
實模式(Real Mode)
它是?Intel公司80286及以后的x86(如80386,80486和80586等)處理器為了兼容以前的處理器(CPU)的一種操作模式。實模式被特殊定義為20位地址內(nèi)存可訪問空間上,這就意味著它的容量是2的20次冪(1M)的可訪問內(nèi)存空間(物理內(nèi)存和BIOS-ROM),軟件可通過這些地址直接訪問BIOS程序和外圍硬件。實模式下處理器沒有硬件級的內(nèi)存保護概念和多道任務(wù)的工作模式。但是為了向下兼容以前的處理器,所以80286及以后的x86系列處理器在開機啟動時仍然先工作在實模式下。80186和早期的處理器僅有一種操作模式,就是后來我們所定義的實模式。實模式雖然能訪問到1M的地址空間,但是由于BIOS的映射作用(即BIOS占用了部分空間地址資源),所以真正能使用的物理內(nèi)存空間(內(nèi)存條),也就是在640k到924k之間。1M?地址空間組成是由?16位的段地址和16位的段內(nèi)偏移地址組成的。用公式表示為:物理地址=左移4位的段地址+偏移地址。
80286處理器體系結(jié)構(gòu)引入了地址保護模式的概念,處理器能夠?qū)?nèi)存及一些其他外圍設(shè)備做硬件級的保護設(shè)置(保護設(shè)置實質(zhì)上就是屏蔽一些地址的訪問)。使用這些新的特性,然而必不可少一些額外的在80186及以前處理器沒有的操作規(guī)程。自從最初的x86微處理器規(guī)格以后,它對程序開發(fā)完全向下兼容,80286芯片被制作成啟動時繼承了以前版本芯片的特性。它工作在實模式下時暫時先關(guān)閉了新增的保護功能特性等,因此能使以往的軟件繼續(xù)工作在新的芯片下。直到今天,甚至最新的x86處理器都是在計算機加電啟動時都是工作在實模式下,它能運行為以前處理器芯片寫的程序。
DOS操作系統(tǒng)(例如?MS-DOS,DR-DOS)工作在實模式下,微軟Windows早期的版本(它本質(zhì)上是運行在DOS上的圖形用戶界面應(yīng)用程序,實際上本身并不是一個操作系統(tǒng))也是運行在實模式下,直到Windows3.0,它運行期間既有實模式又有保護模式,所以說它是一種混合模式工作。它的保護模式運行有兩種不同意義(因為80286并沒有完全地實現(xiàn)80386及以后的保護模式功能):
“標準保護模式”:這就是程序運行在保護模式下;
“虛擬保護模式”:它(實質(zhì)上還是實模式,是實模式上模擬的保護模式)也使用32位地址尋址方式。Windows3.1徹底刪除了對實模式的支持。在80286處理器芯片以后,Windows3.1成為主流操作系統(tǒng)(Windows/80286不是主流產(chǎn)品)。目前差不多所有的X86系列處理器操作系統(tǒng)(Linux,Windows95 and later,OS/2等)都是在啟動時進行處理器設(shè)置而進入保護模式的。
實模式工作機理:
- 對于8086/8088來說計算實際地址是用絕對地址對1M求模。8086的地址線的物理結(jié)構(gòu):20根,也就是它可以物理尋址的內(nèi)存范圍為2^20個字節(jié),即1 M空間,但由于8086/8088所使用的寄存器都是16位,能夠表示的地址范圍只有0-64K,這和1M地址空間來比較也太小了,所以為了在8086/8088下能夠訪問1M內(nèi)存,Intel采取了分段尋址的模式:16位段基地址:16位偏移EA,其絕對地址計算方法為:16位基地址左移4位+16位偏移=20位地址。比如:DS=1000H EA=FFFFH?那么絕對地址就為:10000H +0FFFFH = 1FFFFH?地址單元?。通過這種方法來實現(xiàn)使用16位寄存器訪問1M的地址空間,這種技術(shù)是處理器內(nèi)部實現(xiàn)的,通過上述分段技術(shù)模式,能夠表示的最大內(nèi)存為:?FFFFh: FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes(1M多余出來的部分被稱做高端內(nèi)存區(qū)HMA)。但8086/8088只有20位地址線,只能夠訪問1M地址范圍的數(shù)據(jù),所以如果訪問100000h~10FFEFh之間的內(nèi)存(大于1M空間),則必須有第21根地址線來參與尋址(8086/8088沒有)。因此,當程序員給出超過1M(100000H-10FFEFH)的地址時,因為邏輯上正常,系統(tǒng)并不認為其訪問越界而產(chǎn)生異常,而是自動從0開始計算,也就是說系統(tǒng)計算實際地址的時候是按照對1M求模的方式進行的,這種技術(shù)被稱為wrap-around。
- ?對于80286或以上的CPU通過A20 GATE來控制A20地址線。技術(shù)發(fā)展到了?80286,雖然系統(tǒng)的地址總線由原來的20根發(fā)展為24根,這樣能夠訪問的內(nèi)存可以達到2^24=16M,但是Intel在設(shè)計80286時提出的目標是向下兼容,所以在實模式下系統(tǒng)所表現(xiàn)的行為應(yīng)該和8086/8088所表現(xiàn)的完全一樣,也就是說,當啟動運行在實模式下時,80386以及后續(xù)系列應(yīng)該和8086/8088完全兼容仍然使用A20地址線。所以說80286芯片存在一個BUG:它開設(shè)A20地址線時,在保護模式下,如果程序員訪問100000H-10FFEFH之間的內(nèi)存,系統(tǒng)將實際訪問這塊內(nèi)存(沒有wrap-around技術(shù));如果在實模式下,也可以訪問100000H-10FFEFH間的內(nèi)存,這時采用的是wrap-around技術(shù),這時兩種模式下訪問同一個內(nèi)存區(qū)卻會得到不同的數(shù)據(jù),因此說是一個bug。我們來看一副圖:
為了解決上述兼容性問題,IBM使用鍵盤控制器上剩余的一些輸出線來管理第21根地址線(從0開始數(shù)是第20根)?的有效性,被稱為A20 Gate: - 如果A20 Gate被打開,則當程序員給出100000H-10FFEFH之間的地址的時候,系統(tǒng)將真正訪問這塊內(nèi)存區(qū)域;
- 如果A20 Gate被禁止,則當程序員給出100000H-10FFEFH之間的地址的時候,系統(tǒng)仍然使用8086/8088的方式即取模方式(8086仿真)。絕大多數(shù)IBM PC兼容機默認的A20 Gate是被禁止的。現(xiàn)在許多新型PC上存在直接通過BIOS功能調(diào)用來控制A20 Gate的功能。
保護模式(Protected Mode)
在實模式下,在80286以及更高系列的PC中,即使A20 Gate被打開,在實模式下所能夠訪問的內(nèi)存最大也只能為10FFEFH,盡管它們的地址總線所能夠訪問的能力都大大超過這個限制。為了能夠訪問10FFEFH以上的內(nèi)存,則必須進入保護模式。
(286是Intel 80286的另一種叫法)?它又被稱作為虛擬地址保護模式。盡管在Intel 80286手冊中已經(jīng)提出了虛地址保護模式,但實際上它只是一個指引,真正的32位地址出現(xiàn)在Intel 80386上。保護模式本身是80286及以后兼容處理器序列之后產(chǎn)成的一種操作模式,它具有許多特性設(shè)計為提高系統(tǒng)的多道任務(wù)和系統(tǒng)的穩(wěn)定性。例如內(nèi)存的保護,分頁機制和硬件虛擬存儲的支持。現(xiàn)代多數(shù)的x86處理器操作系統(tǒng)都運行在保護模式下,包括Linux,Free BSD,和Windows3.0(它也運行在實模式下,為了和Windows 2.x應(yīng)用程序兼容)及以后的版本。
80286及以后的處理器另一種工作模式是實模式(僅當系統(tǒng)啟動的一瞬間),本著向下兼容的原則屏蔽保護模式特性,從而容許老的軟件能夠運行在新的芯片上。作為一個設(shè)計規(guī)范,所有的x86系列處理器,除嵌入式Intel80387之外,都是系統(tǒng)啟動工作在實模式下,確保遺留下的操作系統(tǒng)向下兼容。它們都必須被啟動程序(操作系統(tǒng)程序最初運行代碼)重新設(shè)置而相應(yīng)進入保護模式的,在這之前任何的保護模式特性都是無效的。在現(xiàn)代計算機中,這種匹配進入保護模式是操作系統(tǒng)啟動時最前沿的動作之一。
在被調(diào)停的多道任務(wù)程序中,它可以從新工作在實模式下是相當可能的。保護模式的特性是阻止被其他任務(wù)或系統(tǒng)內(nèi)核破壞已經(jīng)不健全的程序的運行,保護模式也有對硬件的支持,例如中斷運行程序,移動運行進程文檔到另一個進程和置空多任務(wù)的保護功能。
386及以后系列處理器不僅具有保護模式又具有32位寄存器,結(jié)果導(dǎo)致了處理功能的混亂,因為80286雖然支持保護模式,但是它的寄存器都是16位的,它是通過自身程序設(shè)定而模擬出的32位,并非32位寄存器處理。歸咎于這種混亂現(xiàn)象,它促使Windows/386?及以后的版本徹底拋棄80286的虛擬保護模式,以后保護模式的操作系統(tǒng)都是運行在80386以上,不再運行在80286(盡管80286模式支持保護模式),所以說80286是一個過渡芯片,它是一個過渡產(chǎn)品。
盡管?286和386處理器能夠?qū)崿F(xiàn)保護模式和兼容以前的版本,但是內(nèi)存的1M以上空間還是不易存取,由于內(nèi)存地址的回繞,IBM PC XT?(現(xiàn)以廢棄)設(shè)計一種模擬系統(tǒng),它能過欺騙手段訪問到1M以上的地址空間,就是開通了A20地址線。在保護模式里,前32個中斷為處理器異常預(yù)留,例如,中斷0D(十進制13)常規(guī)保護故障和中斷00是除數(shù)為零異常。
如果要訪問更多的內(nèi)存,則必須進入保護模式,那么,在保護模式下,A20 Gate對于內(nèi)存訪問有什么影響呢?
為了搞清楚這一點,我們先來看一看A20的工作原理。A20,從它的名字就可以看出來,其實它就是對于A20(從0開始數(shù))的特殊處理(也就是對第21根地址線的處理)。如果A20 Gate被禁止,對于80286來說,其地址為24根地址線,其地址表示為EFFFFF;對于80386極其隨后的32根地址線芯片來說,其地址表示為FFEFFFFF。這種表示的意思是:
- ?如果A20 Gate被禁止。則其第A20在CPU做地址訪問的時候是無效的,永遠只能被作為0。所以,在保護模式下,如果A20 Gate被禁止,則可以訪問的內(nèi)存只能是奇數(shù)1M段,即1M,3M,5M…,也就是00000-FFFFF,200000-2FFFFF,300000-3FFFFF…
- 如果A20 Gate被打開。則其第20-bit是有效的,其值既可以是0,又可以是1。那么就可以使A20線傳遞實際的地址信號。如果A20 Gate被打開,則可以訪問的內(nèi)存則是連續(xù)的。
實模式和保護模式的區(qū)別
從表面上看,保護模式和實模式并沒有太大的區(qū)別,二者都使用了內(nèi)存段、中斷和設(shè)備驅(qū)動來處理硬件,但二者有很多不同之處。我們知道,在實模式中,內(nèi)存被劃分成段,每個段的大小為?64KB?,而這樣的段地址可以用?16位來表示。內(nèi)存段的處理是通過和段寄存器相關(guān)聯(lián)的內(nèi)部機制來處理的,這些段寄存器(?CS?、?DS?、?SS?和ES?)的內(nèi)容形成了物理地址的一部分。具體來說,最終的物理地址是由?16?位的段地址和?16?位的段內(nèi)偏移地址組成的。用公式表示為:物理地址?=?左移?4?位的段地址?+?偏移地址。在保護模式下,段是通過一系列被稱之為?“?描述符表?”?的表所定義的。段寄存器存儲的是指向這些表的指針。用于定義內(nèi)存段的表有兩種:全局描述符表?(GDT)?和局部描述符表?(LDT)?。GDT?是一個段描述符數(shù)組,其中包含所有應(yīng)用程序都可以使用的基本描述符。在實模式中,段長是固定的?(?為?64KB)?,而在保護模式中,段長是可變的,其最大可達?4GB?。LDT?也是段描述符的一個數(shù)組。與?GDT?不同,LDT?是一個段,其中存放的是局部的、不需要全局共享的段描述符。每一個操作系統(tǒng)都必須定義一個?GDT?,而每一個正在運行的任務(wù)都會有一個相應(yīng)的?LDT?。每一個描述符的長度是?8?個字節(jié),格式如圖?3?所示。當段寄存器被加載的時候,段基地址就會從相應(yīng)的表入口獲得。描述符的內(nèi)容會被存儲在一個程序員不可見的影像寄存器?(shadow register)?之中,以便下一次同一個段可以使用該信息而不用每次都到表中提取。物理地址由?16?位或者?32?位的偏移加上影像寄存器中的基址組成。實模式和保護模式的不同可以從下圖很清楚地看出來。
實模式地址
?
保護模式地址
總結(jié):保護模式同實模式的根本區(qū)別是進程內(nèi)存受保護與否。可尋址空間的區(qū)別只是這一原因的果。實模式將整個物理內(nèi)存看成分段的區(qū)域,程序代碼和數(shù)據(jù)位于不同區(qū)域,系統(tǒng)程序和用戶程序沒有區(qū)別對待,而且每一個指針都是指向"實在"的物理地址。這樣一來,用戶程序的一個指針如果指向了系統(tǒng)程序區(qū)域或其他用戶程序區(qū)域,如果這個指針修改了這個區(qū)域的某一個值,那么對于這個被修改的系統(tǒng)程序或用戶程序,其后果就很可能是災(zāi)難性的。為了克服這種低劣的內(nèi)存管理方式,處理器廠商開發(fā)出保護模式。這樣,物理內(nèi)存地址不能直接被程序訪問,程序內(nèi)部的地址(虛擬地址)要由操作系統(tǒng)轉(zhuǎn)化為物理地址去訪問,程序?qū)Υ艘粺o所知。
至此,進程有了嚴格的邊界,任何其他進程根本沒有辦法訪問不屬于自己的物理內(nèi)存區(qū)域,甚至在自己的虛擬地址范圍內(nèi)也不是可以任意訪問的,因為有一些虛擬區(qū)域已經(jīng)被放進一些公共系統(tǒng)運行庫。這些區(qū)域也不能隨便修改,若修改就會有: SIGSEGV(linux 段錯誤);非法內(nèi)存訪問對話框(windows 對話框)。
CPU啟動環(huán)境為16位實模式,之后可以切換到保護模式。但從保護模式無法切換回實模式 。對于80X86處理器來說,從80386處理器開始,除了以前的實模式外,還增添了保護模式和V86模式。實模式和V86模式都是為了和8086兼容而設(shè)置的。
實模式:?
????? 內(nèi)存尋址方式為:段式尋址,即物理地址=段地址*16?? +?? 段內(nèi)偏移地址?
????? 可尋址任意地址,所有指令都相當于工作在特權(quán)級。
????? dos工作在實模式下?
保護模式:?
????? 內(nèi)存尋址方式為:支持內(nèi)存分頁和虛擬內(nèi)存?
????? 支持多任務(wù),可依靠硬件用一條指令即可實現(xiàn)任務(wù)切換,不同任務(wù)可工作在不同的優(yōu)先級下,操作系統(tǒng)工作在最高優(yōu)先級0上,應(yīng)用程序則運行在較低優(yōu)先級上。從實模式到保護模式,需要建立GDT、IDT等數(shù)據(jù)表,然后通過修改控制寄存?器CR0的控制位(位0)來實現(xiàn)。
????? Windows工作在保護模式下。
虛擬8086模式:?
內(nèi)存尋址方式:段式尋址,與實模式一樣?
??????? 支持多任務(wù)和內(nèi)存分頁?
??????? v86模式主要是為了在保護模式下兼容以前的實模式應(yīng)用,即可支持多任務(wù),但每個任務(wù)都是實模式的工作方式。另外,中斷和異常等的處理對于不同的工作模式都是不同的,具體的可以去參看一些相關(guān)書籍。
二、理解8086微機系統(tǒng)的組成
1、對于匯編程序而言,我們需要關(guān)心CPU中的寄存器、存儲器地址、端口(I/O地址):
【內(nèi)存單元的兩個元素】:?地址(編號)和值(內(nèi)容)。
【字節(jié)、字、雙字】:8086的內(nèi)存以字節(jié)編址,每個內(nèi)存單元有唯一的地址(物理地址),可以存放一個字節(jié)。字:一個字占據(jù)兩個連續(xù)的字節(jié)。雙字:雙字占據(jù)兩個連續(xù)的字。
【數(shù)據(jù)的地址對齊】:字單元安排在偶地址(xxx0B),雙字單元安排在模4地址(xx00B)等。對于非對齊地址的數(shù)據(jù),處理器訪問時需要多次訪問存儲器,這樣做花費時間較多。
【總線】:8086的系統(tǒng)總線有3種:數(shù)據(jù)總線;地址總線:8086CPU外部一共有20條地址總線,但在CPU內(nèi)部一次只能傳送16位地址;控制總線
【I/O】:I/O地址叫做端口,通常采用十六進制數(shù)來表達端口:8086的I/O端口為16位,可支持64k個8位端口;I/O地址范圍為:0000H ~ FFFFH
【8086的功能結(jié)構(gòu)】:總線接口單元BIU:主要負責讀取指令和操作數(shù)。執(zhí)行單元EU:主要負責指令譯碼和執(zhí)行。
2、匯編語言程序、匯編程序、連接程序、調(diào)試程序
匯編程序:匯編程序?qū)R編語言源程序翻譯(或稱作“匯編”)成機器代碼目標模塊。
.ASM -> .OBJ;注意區(qū)分匯編程序與匯編語言源程序。
連接程序:連接程序?qū)R編后的目標模塊轉(zhuǎn)換為可執(zhí)行程序。
調(diào)試程序:調(diào)試程序以便排錯、分析等。
3、寄存器組
【16位通用寄存器】
AX、BX、CX、DX、SI、DI、BP、SP
其中AX、BX、CX、DX可以分作高8位和低8位的兩個獨立寄存器。如:AH和AL。我們對其中8位的操作,并不影響另外對應(yīng)的8位數(shù)據(jù)。
1、數(shù)據(jù)寄存器ax、bx、cx、dx
數(shù)據(jù)寄存器用來存放計算的結(jié)果和操作數(shù),也可以存放地址。
每個寄存器又有它們各自的專用目的。
AX——累加器,使用頻度最高,用于算術(shù)、邏輯運算以及與外設(shè)傳送信息等;
BX——基址寄存器,常用做存放存儲器地址;
CX——計數(shù)器,作為循環(huán)和串操作等指令中的隱含計數(shù)器;
DX——數(shù)據(jù)寄存器,常用來存放雙字長數(shù)據(jù)的高16位,或存放外設(shè)端口地址。
2、變址寄存器si、di
變址寄存器常用于存儲器尋址時提供地址
SI是源變址寄存器
DI是目的變址寄存器
串操作類指令中,SI和DI具有特別的功能
3、指針寄存器sp、bp
指針寄存器用于尋址內(nèi)存堆棧內(nèi)的數(shù)據(jù):
SP為堆棧指針寄存器,指示棧頂?shù)钠频刂贰?br /> SP不能再用于其他目的,具有專用目的。SP始終指向棧頂。
BP為基址指針寄存器,表示數(shù)據(jù)在堆棧段中的基地址。
SP和BP寄存器與SS段寄存器聯(lián)合使用以確定堆棧段中的存儲單元地址
4、堆棧
8086中堆棧通常有處理器自動維持,由堆棧段寄存器SS和堆棧指針寄存器SP共同指示。
5、指令指針寄存器IP
指示代碼段中指令的偏移地址。與代碼段寄存器CS連用(CS:IP)。
6、標志寄存器
標志(flag)用于反映指令執(zhí)行結(jié)果或控制指令執(zhí)行形式。
16位的標志寄存器——程序狀態(tài)字PSW寄存器。
其中,狀態(tài)標志(6個,CF ZF SF PF OF AF)——用來記錄程序運行結(jié)果的狀態(tài)信息,許多指令的執(zhí)行都將相應(yīng)地設(shè)置它。控制標志(3個,DF IF TF)——可由程序根據(jù)需要用指令設(shè)置,用于控制處理器執(zhí)行指令的方式。
- 進位標志CF(Carry Flag):當運算結(jié)果的最高有效位有進位(加法)或借位(減法)時,CF=1,or CF=0;
- 零標志位ZF(Zero Flag):若運算結(jié)果為0時,ZF=1,or ZF=0;
- 符號標志位SF(Sign Flag):運算結(jié)果最高位為1,則SF=1,or SF=0。
有符號數(shù)據(jù)用最高有效位表示數(shù)據(jù)的符號。所以,最高有效位就是符號標志的狀態(tài) - 奇偶標志位PF(Parity Flag):當運算結(jié)果最低字節(jié)中“1”的個數(shù)為零或偶數(shù)時,PF=1;or PF=0。
PF標志僅反映最低8位中“1”的個數(shù)是偶或奇,即使是進行16位字操作。 - 溢出標志OF(Overflow Flag):若算術(shù)結(jié)果有溢出,OF=1,or OF=0;【什么是溢出?溢出判斷】
- 輔助進位標志AF(Auxiliary Carry Flag):運算時D3位(低半字節(jié))有進位或借位時,AF=1,or AF=0。這個標志主要由處理器內(nèi)部使用,用于十進制算術(shù)運算調(diào)整指令中,用戶一般不必關(guān)心。
- 方向標志DF(Direction Flag):用于串操作指令中,控制地址的變化方向:設(shè)置DF=0,存儲器地址自動增加;設(shè)置DF=1,存儲器地址自動減少。CLD指令復(fù)位方向標志:DF=0;STD指令置位方向標志:DF=1。
- 中斷允許標志IF(Interrupt-enable Flag):用于控制外部可屏蔽中斷是否可以被處理器響應(yīng):設(shè)置IF=1,允許中斷;設(shè)置IF=0,禁止中斷。CLI指令復(fù)位中斷標志:IF=0;STI指令置位中斷標志:IF=1。
- 陷阱標志TF(Trap Flag):用于控制處理器進入單步操作模式:設(shè)置TF=0,處理器正常工作;設(shè)置TF=1,處理器單步執(zhí)行指令。單步執(zhí)行指令——處理器在每條指令執(zhí)行結(jié)束時,便產(chǎn)生一個編號為1的內(nèi)部中斷。這種內(nèi)部中斷稱為單步中斷。所以TF也稱為單步標志。
7、段寄存器
段地址——段的起始地址的高16位地址。段內(nèi)再由16位二進制數(shù)來尋址。
偏移地址——段內(nèi)存儲單元到段首地址的字節(jié)的距離。
物理地址——用20位二進制數(shù)表示。地址范圍為00000H ~ FFFFFH。物理地址唯一標識一個存儲單元。
邏輯地址——段地址:偏移地址,邏輯地址不唯一。
物理地址=段地址x16+偏移地址
8086有4個16位的段寄存器。
代碼段CS(Code Segment):指明代碼段的 起始地址。
代碼段用來存放程序的指令序列。指令指針寄存器IP指示下一條指令的偏移地址。處理器利用CS:IP取得下一條要執(zhí)行的指令。
堆棧段SS(Stack Segment):指明堆棧段的起始地址。
堆棧段確定堆棧所在的主存區(qū)域。堆棧指針寄存器SP指示堆棧棧頂?shù)钠频刂贰L幚砥骼肧S:SP操作堆棧棧頂?shù)臄?shù)據(jù)。
數(shù)據(jù)段DS(Data Segment):指明數(shù)據(jù)段的起始地址。
數(shù)據(jù)段存放運行程序所用的數(shù)據(jù)。各種主存尋址方式(有效地址EA)得到存儲器中操作數(shù)的偏移地址。處理器利用DS:EA存取數(shù)據(jù)段中的數(shù)據(jù)。
附加段ES(Extra Segment):指明附加段的起始地址。
附加段是附加的數(shù)據(jù)段,也用于數(shù)據(jù)的保存。各種主存尋址方式(有效地址EA)得到存儲器中操作數(shù)的偏移地址。處理器利用ES:EA存取附加段中的數(shù)據(jù)。
串操作指令將附加段作為其目的操作數(shù)的存放區(qū)域。
【關(guān)于分段】
1、8086對邏輯段的要求:
① 段地址低4位均為0
② 每段最大不超過64KB(216?B),但并不要求必須為64KB
③ 各段之間可以獨立,也可以有重疊
2、如何分配各個邏輯段:
①?程序的指令序列必須安排在代碼段。
② 程序使用的堆棧一定在堆棧段。
③ 程序中的數(shù)據(jù)默認是安排在數(shù)據(jù)段,也經(jīng)常安排在附加段,尤其是串操作的目的區(qū)必須是附加段。數(shù)據(jù)的存放比較靈活,實際上可以存放在任何一種邏輯段中。
8、段超越前綴指令
沒有指明時,一般的數(shù)據(jù)訪問在DS段;使用BP訪問主存,則在SS段
默認的情況允許改變,需要使用段超越前綴指令;8086指令系統(tǒng)中有4個,用于明確指定數(shù)據(jù)所在的邏輯段:
CS: ;代碼段超越,使用代碼段的數(shù)據(jù)
SS: ;堆棧段超越,使用堆棧段的數(shù)據(jù)
DS: ;數(shù)據(jù)段超越,使用數(shù)據(jù)段的數(shù)據(jù)
ES: ;附加段超越,使用附加段的數(shù)據(jù)
【段超越示例】
【不允許使用段超越的情況】
串處理指令的目的串必須用ES段;PUSH指令的目的和POP指令的源必須用SS段;指令必須存放在CS段。
【段寄存器的使用規(guī)定】
【補充】
【什么是溢出】
處理器內(nèi)部以補碼表示有符號數(shù)。8位表達的整數(shù)范圍是:+127~-12816位表達的范圍是:+32767~-32768。如果運算結(jié)果超出這個范圍,就產(chǎn)生了溢出。有溢出,說明有符號數(shù)的運算結(jié)果不正確。
【溢出和進位】
溢出標志OF和進位標志CF是兩個意義不同的標志。
進位標志表示無符號數(shù)運算結(jié)果是否超出范圍,運算結(jié)果仍然正確;
溢出標志表示有符號數(shù)運算結(jié)果是否超出范圍,運算結(jié)果已經(jīng)不正確。
【如何運用溢出和進位】
處理器對兩個操作數(shù)進行運算時,按照無符號數(shù)求得結(jié)果,并相應(yīng)設(shè)置進位標志CF;同時,根據(jù)是否超出有符號數(shù)的范圍設(shè)置溢出標志OF。應(yīng)該利用哪個標志,則由程序員來決定。也就是說,如果將參加運算的操作數(shù)認為是無符號數(shù),就應(yīng)該關(guān)心進位;認為是有符號數(shù),則要注意是否溢出。
【溢出判斷】
判斷運算結(jié)果是否溢出有一個簡單的規(guī)則:
只有當兩個相同符號數(shù)相加(包括不同符號數(shù)相減),而運算結(jié)果的符號與原數(shù)據(jù)符號相反時,產(chǎn)生溢出;因為,此時的運算結(jié)果顯然不正確。其他情況下,則不會產(chǎn)生溢出。
【補充】
1.什么是邏輯地址?
邏輯地址是用戶編程時使用的地址,分為段地址和偏移地址兩部分。
邏輯地址表示形式:3020:055AH---------(匯編語言中,數(shù)字后面加H表示16進制)
2.為什么要用邏輯地址?(邏輯地址的產(chǎn)生背景)
8086cpu訪問存儲器時,地址寄存器(16位)要先向地址總線發(fā)出地址信號(地址總線是專門用來存取內(nèi)存地址的,故與內(nèi)存單元有關(guān),20位),而地址寄存器只有16位,從地址寄存器發(fā)出的地址信號,所能訪問的存儲空間只有2^16 = 65536 = 64KB,達不到20位地址總線所提供的地址范圍。針對這種情況,就把內(nèi)存地址分為若干段,每段有一些存儲單元構(gòu)成。用段地址指出是哪一段,偏移地址標明是段中的哪一個單元。
3.什么叫段地址,偏移地址?之間有什么關(guān)系?
Ⅰ.把內(nèi)存地址分為若干段,每段有一些存儲單元構(gòu)成。用段地址指出是哪一段(若是指向同一個存儲單元,段地址可以不一樣,無非就是偏移地址不一樣而已,但是都可以指向同一個物理地址,因此段地址只是一個被訪問的存儲單元(變量)的起始地址,并不是固定的),偏移地址標明是段中的哪一個單元。
Ⅱ.段地址和偏移地址都是16位2進制數(shù)。
Ⅲ.段地址和偏移地址有多種組合,故存在多個地址組合指向同一個存儲單元上。
4.邏輯地址唯一么?
不唯一,因為段地址和偏移有多種組合,故存在多個地址組合指向同一個存儲單元上。例如:3020:055AH和3000:07AAH就是兩種組合,但都是指向同一個存儲單元,
5.cpu執(zhí)行程序時,采用的是邏輯地址還是物理地址?
物理地址---用戶編程時采用的邏輯地址在cpu執(zhí)行程序時都要轉(zhuǎn)換成物理地址。這是由cpu的地址加法器完成的。
6.邏輯地址怎樣轉(zhuǎn)換為物理地址?
轉(zhuǎn)換時,先將16位的段地址左移4位,相當于乘以16或者16進制的10H,再和偏移地址相加。轉(zhuǎn)換公式為:物理地址 = 段地址*10H + 偏移地址。如:將3020:055AH轉(zhuǎn)換為物理地址:----= 3020*10H(左移四位)+055AH = 3075AH
7.段與偏移地址是什么關(guān)系?
段是由存儲單元構(gòu)成的,段包含偏移地址對應(yīng)的存儲單元。即偏移地址對應(yīng)的字節(jié)存儲單元在段中。.
8.段的大小指的是什么?
指的是這個段包含存儲單元的多少。
9.將內(nèi)存分段的依據(jù)?以及段的相關(guān)知識
段地址和偏移地址都是16位二進制數(shù),每段最大64K字節(jié)單元(2^16=65536 = 64KB),每段最小16個字節(jié)單元(硬性規(guī)定),也可以100個,1000個到最多達到65536個。偏移地址范圍:0000H --- FFFFH
10.什么叫小段?
規(guī)定每16個字節(jié)單元為一小段。
三、理解“邏輯地址、線性地址、物理地址和虛擬地址”
1、各種地址概念
① 物理地址(physical address)
用于內(nèi)存芯片級的單元尋址,與處理器和CPU連接的地址總線相對應(yīng)。
——這個概念應(yīng)該是這幾個概念中最好理解的一個,但是值得一提的是,雖然可以直接把物理地址理解成插在機器上那根內(nèi)存本身,把內(nèi)存看成一個從0字節(jié)一直到最大空量逐字節(jié)的編號的大數(shù)組,然后把這個數(shù)組叫做物理地址,但是事實上,這只是一個硬件提供給軟件的抽像,內(nèi)存的尋址方式并不是這樣。所以,說它是“與地址總線相對應(yīng)”,是更貼切一些,不過拋開對物理內(nèi)存尋址方式的考慮,直接把物理地址與物理的內(nèi)存一一對應(yīng),也是可以接受的。也許錯誤的理解更利于形而上的抽像。
② 虛擬內(nèi)存(virtual memory)
這是對整個內(nèi)存的抽像描述。它是相對于物理內(nèi)存來講的,可以直接理解成“不直實的”,“假的”內(nèi)存,例如,一個0x08000000內(nèi)存地址,它并不對就物理地址上那個大數(shù)組中0x08000000 - 1那個地址元素;之所以是這樣,是因為現(xiàn)代操作系統(tǒng)都提供了一種內(nèi)存管理的抽像,即虛擬內(nèi)存(virtual memory)。進程使用虛擬內(nèi)存中的地址,由操作系統(tǒng)協(xié)助相關(guān)硬件,把它“轉(zhuǎn)換”成真正的物理地址。這個“轉(zhuǎn)換”,是所有問題討論的關(guān)鍵。有了這樣的抽像,一個程序,就可以使用比真實物理地址大得多的地址空間,甚至多個進程可以使用相同的地址。不奇怪,因為轉(zhuǎn)換后的物理地址并非相同的。
——可以把連接后的程序反編譯看一下,發(fā)現(xiàn)連接器已經(jīng)為程序分配了一個地址,例如,要調(diào)用某個函數(shù)A,代碼不是call A,而是call 0x0811111111 ,也就是說,函數(shù)A的地址已經(jīng)被定下來了。沒有這樣的“轉(zhuǎn)換”,沒有虛擬地址的概念,這樣做是根本行不通的。打住了,這個問題再說下去,就收不住了。
③ 邏輯地址(logical address)
Intel為了兼容,將遠古時代的段式內(nèi)存管理方式保留了下來。邏輯地址指的是機器語言指令中,用來指定一個操作數(shù)或者是一條指令的地址。以上例,我們說的連接器為A分配的0x08111111這個地址就是邏輯地址。不過不好意思,這樣說,好像又違背了Intel中段式管理中,對邏輯地址要求,“一個邏輯地址,是由一個段標識符加上一個指定段內(nèi)相對地址的偏移量,表示為 [段標識符:段內(nèi)偏移量],也就是說,上例中那個0x08111111,應(yīng)該表示為[A的代碼段標識符: 0x08111111],這樣,才完整一些”
④ 線性地址(linear address)
線性地址或也叫虛擬地址(virtual address),跟邏輯地址類似,它也是一個不真實的地址,如果邏輯地址是對應(yīng)的硬件平臺段式管理轉(zhuǎn)換前地址的話,那么線性地址則對應(yīng)了硬件頁式內(nèi)存的轉(zhuǎn)換前地址。
CPU將一個虛擬內(nèi)存空間中的地址轉(zhuǎn)換為物理地址,需要進行兩步:首先將給定一個邏輯地址(其實是段內(nèi)偏移量,這個一定要理解!!!),CPU要利用其段式內(nèi)存管理單元,先將為個邏輯地址轉(zhuǎn)換成一個線程地址,再利用其頁式內(nèi)存管理單元,轉(zhuǎn)換為最終物理地址。
這樣做兩次轉(zhuǎn)換,的確是非常麻煩而且沒有必要的,因為直接可以把線性地址抽像給進程。之所以這樣冗余,Intel完全是為了兼容而已。
//導(dǎo)讀注意:下面2-5部分,分別依次按照CPU段式內(nèi)存管理、Linux段式內(nèi)存管理、CPU頁式內(nèi)存管理、Linux頁式內(nèi)存管理
2、CPU段式內(nèi)存管理,邏輯地址如何轉(zhuǎn)換為線性地址
一個邏輯地址由兩部份組成,段標識符: 段內(nèi)偏移量。段標識符是由一個16位長的字段組成,稱為段選擇符。其中前13位是一個索引號。后面3位包含一些硬件細節(jié),如圖:
索引號,或者直接理解成數(shù)組下標——那它總要對應(yīng)一個數(shù)組吧,它又是什么東東的索引呢?這個東東就是“段描述符(segment descriptor)”,呵呵,段描述符具體地址描述了一個段(對于“段”這個字眼的理解,我是把它想像成,拿了一把刀,把虛擬內(nèi)存,砍成若干的截——段)。這樣,很多個段描述符,就組了一個數(shù)組,叫“段描述符表”,這樣,可以通過段標識符的前13位,直接在段描述符表中找到一個具體的段描述符,這個描述符就描述了一個段,我剛才對段的抽像不太準確,因為看看描述符里面究竟有什么東東——也就是它究竟是如何描述的,就理解段究竟有什么東東了,每一個段描述符由8個字節(jié)組成,如下圖:
這些東東很復(fù)雜,雖然可以利用一個數(shù)據(jù)結(jié)構(gòu)來定義它,不過,我這里只關(guān)心一樣,就是Base字段,它描述了一個段的開始位置的線性地址。
Intel設(shè)計的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每個進程自己的,就放在所謂的“局部段描述符表(LDT)”中。那究竟什么時候該用GDT,什么時候該用LDT呢?這是由段選擇符中的T1字段表示的,=0,表示用GDT,=1表示用LDT。
GDT在內(nèi)存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT則在ldtr寄存器中。
好多概念,像繞口令一樣。這張圖看起來要直觀些:
首先,給定一個完整的邏輯地址[段選擇符:段內(nèi)偏移地址],
1、看段選擇符的T1=0還是1,知道當前要轉(zhuǎn)換是GDT中的段,還是LDT中的段,再根據(jù)相應(yīng)寄存器,得到其地址和大小。我們就有了一個數(shù)組了。
2、拿出段選擇符中前13位,可以在這個數(shù)組中,查找到對應(yīng)的段描述符,這樣,它了Base,即基地址就知道了。
3、把Base + offset,就是要轉(zhuǎn)換的線性地址了。
還是挺簡單的,對于軟件來講,原則上就需要把硬件轉(zhuǎn)換所需的信息準備好,就可以讓硬件來完成這個轉(zhuǎn)換了。OK,來看看Linux怎么做的。
3、Linux的段式內(nèi)存管理
Intel要求兩次轉(zhuǎn)換,這樣雖說是兼容了,但是卻是很冗余,呵呵,沒辦法,硬件要求這樣做了,軟件就只能照辦,怎么著也得形式主義一樣。另一方面,其它某些硬件平臺,沒有二次轉(zhuǎn)換的概念,Linux也需要提供一個高層抽像,來提供一個統(tǒng)一的界面。所以,Linux的段式管理,事實上只是“哄騙”了一下硬件而已。按照Intel的本意,全局的用GDT,每個進程自己的用LDT——不過Linux則對所有的進程都使用了相同的段來對指令和數(shù)據(jù)尋址。即用戶數(shù)據(jù)段,用戶代碼段,對應(yīng)的,內(nèi)核中的是內(nèi)核數(shù)據(jù)段和內(nèi)核代碼段。這樣做沒有什么奇怪的,本來就是走形式嘛,像我們寫年終總結(jié)一樣。
include/asm-i386/segment.h
復(fù)制代碼
把其中的宏替換成數(shù)值,則為:
復(fù)制代碼
方括號后是這四個段選擇符的16位二制表示,它們的索引號和T1字段值也可以算出來了
復(fù)制代碼
T1均為0,則表示都使用了GDT,再來看初始化GDT的內(nèi)容中相應(yīng)的12-15項(arch/i386/head.S):
復(fù)制代碼
按照前面段描述符表中的描述,可以把它們展開,發(fā)現(xiàn)其16-31位全為0,即四個段的基地址全為0。
這樣,給定一個段內(nèi)偏移地址,按照前面轉(zhuǎn)換公式,0 + 段內(nèi)偏移,轉(zhuǎn)換為線性地址,可以得出重要的結(jié)論,“在Linux下,邏輯地址與線性地址總是一致(是一致,不是有些人說的相同)的,即邏輯地址的偏移量字段的值與線性地址的值總是相同的。!!!”
忽略了太多的細節(jié),例如段的權(quán)限檢查。呵呵。
Linux中,絕大部份進程并不例用LDT,除非使用Wine ,仿真Windows程序的時候。
4.CPU的頁式內(nèi)存管理
CPU的頁式內(nèi)存管理單元,負責把一個線性地址,最終翻譯為一個物理地址(注意,相互獨立的進程都有自己的頁目錄和頁表,就算多個進程具有相同的線性地址,最后轉(zhuǎn)換到物理地址也是不一樣的,所以不會互相干擾)。從管理和效率的角度出發(fā),線性地址被分為以固定長度為單位的組,稱為頁(page),例如一個32位的機器,線性地址最大可為4G,可以用4KB為一個頁來劃分,這頁,整個線性地址就被劃分為一個tatol_page[2^20]的大數(shù)組,共有2的20個次方個頁。這個大數(shù)組我們稱之為頁目錄。目錄中的每一個目錄項,就是一個地址——對應(yīng)的頁的地址。另一類“頁”,我們稱之為物理頁,或者是頁框、頁楨的。是分頁單元把所有的物理內(nèi)存也劃分為固定長度的管理單位,它的長度一般與內(nèi)存頁是一一對應(yīng)的。
這里注意到,這個total_page數(shù)組有2^20個成員,每個成員是一個地址(32位機,一個地址也就是4字節(jié)),那么要單單要表示這么一個數(shù)組,就要占去4MB的內(nèi)存空間。為了節(jié)省空間,引入了一個二級管理模式的機器來組織分頁單元。文字描述太累,看圖直觀一些:
如上圖,
1、分頁單元中,頁目錄是唯一的,它的地址放在CPU的cr3寄存器中,是進行地址轉(zhuǎn)換的開始點。
2、每一個活動的進程,因為都有其獨立的對應(yīng)的虛似內(nèi)存(頁目錄也是唯一的),那么它也對應(yīng)了一個獨立的頁目錄地址。——運行一個進程,需要將它的頁目錄地址放到cr3寄存器中,將別個的保存下來。
3、每一個32位的線性地址被劃分為三部份,面目錄索引(10位):頁表索引(10位):偏移(12位)
依據(jù)以下步驟進行轉(zhuǎn)換:
1、從cr3中取出進程的頁目錄地址(操作系統(tǒng)負責在調(diào)度進程的時候,把這個地址裝入對應(yīng)寄存器);
2、根據(jù)線性地址前十位,在數(shù)組中,找到對應(yīng)的索引項,因為引入了二級管理模式,頁目錄中的項,不再是頁的地址,而是一個頁表的地址。(又引入了一個數(shù)組),頁的地址被放到頁表中去了。
3、根據(jù)線性地址的中間十位,在頁表(也是數(shù)組)中找到頁的起始地址;
4、將頁的起始地址與線性地址中最后12位相加,得到最終我們想要的葫蘆;
這個轉(zhuǎn)換過程,應(yīng)該說還是非常簡單地。全部由硬件完成,雖然多了一道手續(xù),但是節(jié)約了大量的內(nèi)存,還是值得的。那么再簡單地驗證一下:
1、這樣的二級模式是否仍能夠表示4G的地址;
頁目錄共有:2^10項,也就是說有這么多個頁表
每個目表對應(yīng)了:2^10頁;
每個頁中可尋址:2^12個字節(jié)。
還是2^32 = 4GB
2、這樣的二級模式是否真的節(jié)約了空間;
也就是算一下頁目錄項和頁表項共占空間 (2^10 * 4 + 2 ^10 *4) = 8KB。哎,……怎么說呢!!!
紅色錯誤,標注一下,后文貼中有此討論。。。。。。
按<深入理解計算機系統(tǒng)>中的解釋,二級模式空間的節(jié)約是從兩個方面實現(xiàn)的:
A、如果一級頁表中的一個頁表條目為空,那么那所指的二級頁表就根本不會存在。這表現(xiàn)出一種巨大的潛在節(jié)約,因為對于一個典型的程序,4GB虛擬地址空間的大部份都會是未分配的;
B、只有一級頁表才需要總是在主存中。虛擬存儲器系統(tǒng)可以在需要時創(chuàng)建,并頁面調(diào)入或調(diào)出二級頁表,這就減少了主存的壓力。只有最經(jīng)常使用的二級頁表才需要緩存在主存中。——不過Linux并沒有完全享受這種福利,它的頁表目錄和與已分配頁面相關(guān)的頁表都是常駐內(nèi)存的。
值得一提的是,雖然頁目錄和頁表中的項,都是4個字節(jié),32位,但是它們都只用高20位,低12位屏蔽為0——把頁表的低12屏蔽為0,是很好理解的,因為這樣,它剛好和一個頁面大小對應(yīng)起來,大家都成整數(shù)增加。計算起來就方便多了。但是,為什么同時也要把頁目錄低12位屏蔽掉呢?因為按同樣的道理,只要屏蔽其低10位就可以了,不過我想,因為12>10,這樣,可以讓頁目錄和頁表使用相同的數(shù)據(jù)結(jié)構(gòu),方便。
本貼只介紹一般性轉(zhuǎn)換的原理,擴展分頁、頁的保護機制、PAE模式的分頁這些麻煩點的東東就不啰嗦了……可以參考其它專業(yè)書籍。
5.Linux的頁式內(nèi)存管理
原理上來講,Linux只需要為每個進程分配好所需數(shù)據(jù)結(jié)構(gòu),放到內(nèi)存中,然后在調(diào)度進程的時候,切換寄存器cr3,剩下的就交給硬件來完成了(呵呵,事實上要復(fù)雜得多,不過偶只分析最基本的流程)。
前面說了i386的二級頁管理架構(gòu),不過有些CPU,還有三級,甚至四級架構(gòu),Linux為了在更高層次提供抽像,為每個CPU提供統(tǒng)一的界面。提供了一個四層頁管理架構(gòu),來兼容這些二級、三級、四級管理架構(gòu)的CPU。這四級分別為:
頁全局目錄PGD(對應(yīng)剛才的頁目錄)
頁上級目錄PUD(新引進的)
頁中間目錄PMD(也就新引進的)
頁表PT(對應(yīng)剛才的頁表)。
整個轉(zhuǎn)換依據(jù)硬件轉(zhuǎn)換原理,只是多了二次數(shù)組的索引罷了,如下圖:
那么,對于使用二級管理架構(gòu)32位的硬件,現(xiàn)在又是四級轉(zhuǎn)換了,它們怎么能夠協(xié)調(diào)地工作起來呢?嗯,來看這種情況下,怎么來劃分線性地址吧!
從硬件的角度,32位地址被分成了三部份——也就是說,不管理軟件怎么做,最終落實到硬件,也只認識這三位老大。
從軟件的角度,由于多引入了兩部份,,也就是說,共有五部份。——要讓二層架構(gòu)的硬件認識五部份也很容易,在地址劃分的時候,將頁上級目錄和頁中間目錄的長度設(shè)置為0就可以了。
這樣,操作系統(tǒng)見到的是五部份,硬件還是按它死板的三部份劃分,也不會出錯,也就是說大家共建了和諧計算機系統(tǒng)。
這樣,雖說是多此一舉,但是考慮到64位地址,使用四層轉(zhuǎn)換架構(gòu)的CPU,我們就不再把中間兩個設(shè)為0了,這樣,軟件與硬件再次和諧——抽像就是強大呀!!!
例如,一個邏輯地址已經(jīng)被轉(zhuǎn)換成了線性地址,0x08147258,換成二制進,也就是:
0000100000 0101000111 001001011000
內(nèi)核對這個地址進行劃分
PGD = 0000100000
PUD = 0
PMD = 0
PT = 0101000111
offset = 001001011000
現(xiàn)在來理解Linux針對硬件的花招,因為硬件根本看不到所謂PUD,PMD,所以,本質(zhì)上要求PGD索引,直接就對應(yīng)了PT的地址。而不是再到PUD和PMD中去查數(shù)組(雖然它們兩個在線性地址中,長度為0,2^0 =1,也就是說,它們都是有一個數(shù)組元素的數(shù)組),那么,內(nèi)核如何合理安排地址呢?
從軟件的角度上來講,因為它的項只有一個,32位,剛好可以存放與PGD中長度一樣的地址指針。那么所謂先到PUD,到到PMD中做映射轉(zhuǎn)換,就變成了保持原值不變,一一轉(zhuǎn)手就可以了。這樣,就實現(xiàn)了“邏輯上指向一個PUD,再指向一個PDM,但在物理上是直接指向相應(yīng)的PT的這個抽像,因為硬件根本不知道有PUD、PMD這個東西”。
然后交給硬件,硬件對這個地址進行劃分,看到的是:
頁目錄 = 0000100000
PT = 0101000111
offset = 001001011000
嗯,先根據(jù)0000100000(32),在頁目錄數(shù)組中索引,找到其元素中的地址,取其高20位,找到頁表的地址,頁表的地址是由內(nèi)核動態(tài)分配的,接著,再加一個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"
?
總結(jié)
以上是生活随笔為你收集整理的理解单片机系统—汇编语言的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。