構(gòu)、尋址方式與匯編指令系統(tǒng)詳解)
真正扛過這門課的人都懂前三章才是整個“微機原理與接口技術(shù)”最勸退的地方。很多人學(xué)到接口芯片、可編程定時器時反而覺得輕松因為前面的處理器結(jié)構(gòu)、尋址方式、指令系統(tǒng)已經(jīng)把底子打好了。某高校名師的這套課程1~3章把8086/8088微處理器的“上半身”講得極為扎實——從數(shù)制編碼到內(nèi)部結(jié)構(gòu)再到尋址和指令集每一頁PPT都值得反復(fù)翻。如果你正在被段寄存器、物理地址、各種尋址方式搞得頭疼或者打算自學(xué)硬件基礎(chǔ)但找不到入口這篇就按前三章的知識地圖把重點、難點和我踩過的坑一次講透。1. 內(nèi)容整體設(shè)計與思路拆解1.1 前三章在整門課里的定位處理器才是核心很多初學(xué)者會犯一個方向性錯誤覺得“微機原理”就該趕緊進入8255、8254、8259這些接口芯片前面的處理器內(nèi)部結(jié)構(gòu)看看就行了。這是大錯特錯的。前三章實際上是整門課的“地基”后面所有的接口實驗、中斷系統(tǒng)、存儲器擴展全部建立在處理器如何工作這個基礎(chǔ)上。這套教材前三章的設(shè)計邏輯非常清楚第一章是數(shù)制與編碼第二章是8086/8088微處理器結(jié)構(gòu)第三章是尋址方式與指令系統(tǒng)。這三章串起來回答了一個最根本的問題一條指令從存儲器里被取出來到最終執(zhí)行完畢中間到底發(fā)生了什么這個問題的答案鏈就是指令以二進制形式存在內(nèi)存里→處理器的總線接口單元按地址取出來→指令隊列暫存→執(zhí)行單元譯碼并按尋址方式找到操作數(shù)→執(zhí)行算術(shù)邏輯運算→結(jié)果寫回。你會發(fā)現(xiàn)寄存器、標志位、內(nèi)存分段、尋址方式全部在這個鏈條里各司其職。吳寧老師在第一節(jié)課就用一句話點過睛微處理器就像一個加工廠指令是圖紙數(shù)據(jù)是原料寄存器是工作臺地址就是原料所在貨架的編號。1.2 為什么1~3章學(xué)得扎實后面實驗才不掉隊接口實驗里最常見的現(xiàn)象是照著實驗指導(dǎo)書抄代碼編譯下載實驗箱上的燈亮了但問你“這個端口地址為什么是XX”“這個數(shù)據(jù)為什么從這個端口進”“中斷服務(wù)程序的入口地址怎么定位”就說不清楚了。根源就是前三章的概念沒有形成體系。我見過太多同學(xué)做8255的流水燈實驗?zāi)芘芡ǖ珦Q一個題目就不會。因為他不清楚MOV DX, 控制字寄存器地址、OUT DX, AL 這條指令到底發(fā)生了什么——控制字這個“數(shù)據(jù)”通過數(shù)據(jù)總線送到了某個“端口”而端口本質(zhì)就是I/O空間的一個地址。如果你理解指令系統(tǒng)和總線周期這完全是順理成章的事。所以我勸所有初學(xué)者前三章啃不扎實后面實驗只會越來越痛苦前三章學(xué)透了后面的接口芯片就是換個角度看總線時序而已。1.3 這套課程前三章的知識地圖把前三章拆開來看每一章的“核心交付物”是不同的第一章交付的是“數(shù)字表示觀”二進制、十六進制、補碼、BCD碼、ASCII碼讓你看到一個字節(jié)就知道它代表什么看到十六進制數(shù)就能心算出補碼是多少。第二章交付的是“處理器運行觀”8086/8088的內(nèi)部結(jié)構(gòu)、寄存器模型、物理地址形成、總線周期、時序概念讓你腦子里有一張?zhí)幚砥鞯倪\行時快照。第三章交付的是“指令執(zhí)行觀”操作數(shù)的查找方式尋址、指令的語法和語義、標志位的變化規(guī)則讓你讀懂匯編程序并且能自己動手寫。這三層正好是遞進關(guān)系。沒有第一章你連第三章里的立即數(shù)都看不明白沒有第二章第三章里的 MOV AX, [BXSI] 為什么能訪問到某個內(nèi)存單元你永遠只能死記硬背。2. 數(shù)制與編碼硬件的思維方式從這里建立2.1 為什么微機原理課不從十進制講起第一個讓人覺得“枯燥”的點就是數(shù)制。教材第一章上來就是二進制、十六進制轉(zhuǎn)換很多人覺得這有什么好學(xué)的計算器一按不就出來了。但課程里反復(fù)講轉(zhuǎn)換目的根本不是讓你手動算而是建立“硬件只認識電平高低”的本能反應(yīng)。計算機內(nèi)部沒有任何一個部件能存“十進制數(shù)”它只能存高電平和低電平的組合也就是0和1。十六進制只是二進制的縮寫形式方便人閱讀而已一個十六進制位正好對應(yīng)四個二進制位轉(zhuǎn)換起來零誤差。比如 0x3F 讀出來就是 0011 1111拆成四位一組根本不用計算器。真正要在第一章較勁的是補碼。我當年學(xué)原碼、反碼、補碼的時候也覺得純屬折騰直到學(xué)到減法電路設(shè)計才明白補碼的存在讓 CPU 只需要做加法器不需要單獨的減法器。A-B 直接變成 A (-B的補碼)連符號位都參與運算硬件實現(xiàn)瞬間簡單。這就是為什么你看到一個字節(jié)的最高位不是“符號位”那么簡單——它是數(shù)據(jù)的一部分只是被同時賦予符號含義。2.2 補碼運算里的溢出判斷最容易丟分的實用點第一章的必考題型“兩個補碼相加判斷是否溢出”這絕不只是為了考試而是寫匯編時真實會遇到的場景。判斷方法很多我最推薦用符號位邏輯兩個同號數(shù)相加結(jié)果的符號位與加數(shù)符號位相反就是溢出。正正負或者 負負正必溢出。一個正數(shù)和一個負數(shù)相加永遠不會溢出因為結(jié)果絕對值不可能超過其中任一數(shù)的絕對值。舉一個實際的例子計算 100 加 50兩個都是正數(shù)補碼分別是 0110 0100 和 0011 0010相加結(jié)果是 1001 0110符號位是1看起來是負數(shù)所以溢出。為什么因為8位補碼能表示的最大正數(shù)是127100加50是150超范圍了。調(diào)試程序時看到這種結(jié)果第一反應(yīng)不要懷疑指令錯了而是檢查數(shù)據(jù)范圍。BCD碼和ASCII碼也不能跳過。BCD碼其實為后面8255驅(qū)動數(shù)碼管做了伏筆——數(shù)碼管要顯示數(shù)字你得先把二進制數(shù)拆成各位十進制數(shù)再轉(zhuǎn)換成七段碼而鍵盤輸入一個字符“5”CPU收到的實際上是ASCII碼 0x35你要顯示或參與計算就得減去0x30。這些細節(jié)在做課程設(shè)計時全都會冒出來。2.3 這一章的實操練習(xí)建議不要只刷轉(zhuǎn)換題建議手寫一個20行左右的小程序思路把一個字節(jié)的二進制數(shù)逐位判斷如果是1就在屏幕上輸出一個字符。這個過程強迫你搞清楚位權(quán)、移位、與運算還能提前熟悉邏輯指令。這比單純做100道轉(zhuǎn)換題有效得多。3. 處理器內(nèi)部結(jié)構(gòu)8086的硬件運行模型3.1 EU和BIU的分工理解了它就看懂了處理器第二章的核心是8086內(nèi)部結(jié)構(gòu)兩個最重要的模塊執(zhí)行單元EU和總線接口單元BIU。很多教材把這部分畫成一張大圖每個功能塊縮寫都標出來了但就是看不出重點。我的理解方式是這樣BIU是“外部世界的搬運工”負責從內(nèi)存取指令、讀數(shù)據(jù)、寫數(shù)據(jù)這些都需要通過總線完成所以它管理著段寄存器、指令指針、指令隊列和地址加法器。EU是“內(nèi)部大腦”負責翻譯指令、執(zhí)行運算它包含ALU、通用寄存器、標志寄存器和控制電路。關(guān)鍵是它們?nèi)绾螀f(xié)作BIU預(yù)先把后面的幾條指令取進指令隊列所以叫預(yù)取隊列EU從隊列里拿指令執(zhí)行兩者是并行工作的。這就是最早的流水線思想——不是執(zhí)行完一條再去取下一條而是“邊取邊執(zhí)行”。這個設(shè)計帶來的直接效果就是大多數(shù)指令不需要等待取指總線周期加快了執(zhí)行速度。想通這一點后面你學(xué)總線周期時就會很自然取指周期、讀存儲器周期、寫存儲器周期都是BIU在干活。3.2 寄存器模型哪些是程序員可見的哪些不是第一章結(jié)束到第二章忽然冒出來十多個寄存器。我第一次學(xué)的時候背得死去活來后來發(fā)現(xiàn)按用途分組記憶就簡單多了通用寄存器AX、BX、CX、DX每個都可以拆成高8位和低8位單獨使用。AX常用于累加運算和字乘法除法BX常作基址寄存器CX常作計數(shù)器循環(huán)次數(shù)DX在字乘法中保存高位積或在I/O指令中放端口地址。指針和變址寄存器SP棧指針、BP基址指針、SI源變址、DI目的變址這4個主要是配合尋址用的。段寄存器CS代碼段、DS數(shù)據(jù)段、SS堆棧段、ES附加段告訴CPU當前程序、數(shù)據(jù)、棧分別在哪一段內(nèi)存。指令指針I(yè)P和控制標志位前者指向下一條要執(zhí)行的指令在代碼段內(nèi)的偏移后者記錄運算結(jié)果的狀態(tài)。這里有個關(guān)鍵認知CPU不是把所有東西都擺在“桌面”上程序員可見的寄存器就那么十幾個而內(nèi)部還有一些程序員看不到的部件比如隊列緩沖器、地址加法器。學(xué)習(xí)時注意力應(yīng)該放在程序員可見的模型上因為匯編指令操作的就是這些寄存器。3.3 物理地址的形成1MB空間是怎么來的8086有20根地址線理論上能尋址1MB2的20次方但內(nèi)部寄存器只有16位直接表示的最大范圍只有64KB。怎么解決分段加偏移。物理地址 段基址 × 16 偏移量這里的乘16就是左移4位用十六進制表示特別直觀段基址 1234H偏移量 5678H物理地址就是 1234H 左移一位十六進制位變成 12340H再加 5678H得到 179B8H。我建議這個計算必須練成肌肉記憶。因為后面的每個尋址方式都在反復(fù)用這個公式。而且要注意段和偏移的對應(yīng)關(guān)系不是唯一的同一個物理地址可以由不同的“段:偏移”組合指向。比如 1000:0000 和 0F00:1000 指向的是同一個物理地址10000H。這在程序里很常見尤其是段間跳轉(zhuǎn)或者中斷向量表操作時。補充一個容易忽略的點為什么段必須被16整除因為左移4位段的起始地址低4位必定是0所以段起點總是16字節(jié)對齊的。這也是為什么匯編源程序里段定義時連接器會自動幫你對齊。3.4 8086和8088的差別課本前幾章就會埋下的伏筆第一章和第二章通常會順帶提一下8088和8086的區(qū)別別跳過。8086外部數(shù)據(jù)總線16位8088外部數(shù)據(jù)總線只有8位但內(nèi)部都是16位結(jié)構(gòu)。這就是“準16位”的由來。這個區(qū)別直接影響了總線周期的設(shè)計8086一次總線操作可以傳輸一個16位字8088需要兩次才能傳輸16位數(shù)據(jù)。你后面學(xué)存儲器擴展時就會遇到字操作和字節(jié)操作的時序差異?,F(xiàn)在先把結(jié)論記住8086取指在同一個總線周期內(nèi)能搬2個字節(jié)8088只能搬1個字節(jié)所以8088的指令隊列空得更頻繁整體性能略低。3.5 用DEBUG或模擬器觀察寄存器變化這一章學(xué)完一定不要停在PPT上。找一臺裝好DOS模擬環(huán)境的機器模擬器也行隨便寫一個簡單的MOV指令然后用單步跟蹤看指令指針I(yè)P如何變化、AX內(nèi)容如何被寫入。你能親眼看到CS:IP從程序裝載地址開始一步一步推進段寄存器DS的值怎么和MOV指令里的偏移組合成實際物理地址。這個“看見”的過程比盯著結(jié)構(gòu)圖背十遍都管用。4. 尋址方式與指令系統(tǒng)寫匯編前必須先過這一關(guān)4.1 七種尋址方式的記憶錨點第三章上來就是尋址方式這是整本書的第一個分水嶺。學(xué)到這里如果只背例子很容易混淆。我的方法是給每種尋址方式配一個“生活化場景”立即尋址直接把菜價寫在清單上“西紅柿3元”——操作數(shù)就在指令里。寄存器尋址告訴你去工作臺A柜子里拿東西柜子號就在指令里。直接尋址門牌號寫在紙上按這個門牌號進屋拿東西。內(nèi)存單元的偏移量直接寫在指令里。寄存器間接尋址門牌號放在某個寄存器里先看寄存器里的數(shù)再去這個地址拿東西。也就是 MOV AX, [BX]。寄存器相對尋址門牌號 寄存器內(nèi)容 一個固定偏移量。比如 MOV AX, [BX100H]。基址變址尋址門牌號 一個基址寄存器 一個變址寄存器。MOV AX, [BXSI]。相對基址變址尋址門牌號 基址 變址 偏移量。MOV AX, [BXSI100H]。把這七個場景串起來“操作數(shù)到底在哪”這個問題就有了統(tǒng)一的答案要么在指令里要么在寄存器里要么在內(nèi)存里而內(nèi)存地址要按規(guī)則算出來。搞清楚了這一點閱讀匯編程序時就不會再迷茫。4.2 段寄存器的隱含規(guī)則哪些組合能用哪些不能用尋址方式里最容易被坑的是段寄存器的隱含使用。訪問內(nèi)存時段基址默認由某個段寄存器提供但有些情況下需要用段超越前綴人為改變?nèi)≈噶疃位饭潭ㄓ蒀S提供偏移來自IP這個無法改變。讀數(shù)據(jù)默認用DS比如 MOV AX, [BX] 默認 DS:BX。進棧出棧默認用SS因為棧操作寫的就是 SS:SP。BP做基址時默認段寄存器是SS。這意味著 MOV AX, [BP] 訪問的是棧段里的數(shù)據(jù)而不是DS指向的數(shù)據(jù)段。很多初學(xué)者第一次在這里翻車看到BP就覺得是數(shù)據(jù)段。為什么BP默認關(guān)聯(lián)SS因為BP最初設(shè)計就是用來訪問棧中的參數(shù)和局部變量的高級語言函數(shù)調(diào)用時經(jīng)常用BP定位棧幀。理解這個背景就不會覺得是死規(guī)則了。4.3 常見指令的“副作用”比指令本身更重要的是標志位第三章后半部分是8086指令系統(tǒng)MOV、ADD、SUB、CMP、JMP、循環(huán)、移位等。初學(xué)者常犯的錯誤是只記指令“干什么”不記“對標志位有什么影響”。但后面學(xué)條件轉(zhuǎn)移指令時轉(zhuǎn)移的根據(jù)恰恰就是標志位。比如 CMP AX, BX 這條指令它執(zhí)行的是 AX-BX 但不保存結(jié)果只改變標志位。然后你緊跟一條 JE 或 JNECPU根據(jù)零標志位ZF判斷兩個數(shù)是否相等。再看移位指令 SHL AL, 1每左移一位最高位移入進位標志CF移出的位丟了。這種“副作用”才是匯編的真正邏輯。我整理過一張速查表自己寫程序時經(jīng)常對著看這里貼一部分指令類型運算作用主要影響的標志位ADD/SUB加/減CF、ZF、SF、OF、AF、PFCMP比較做減法不保存結(jié)果CF、ZF、SF、OF、AF、PFINC/DEC加1/減1不影響CFZF、SF、OF、AF、PFAND/OR/XOR邏輯運算清CF、OFZF、SF、PFSHL/SHR邏輯移位CF、ZF、SF、OF、PFMOV傳送不影響任何標志位無PUSH/POP壓棧/出棧無關(guān)鍵點MOV不影響標志位LEA不影響標志位邏輯運算會把CF和OF清0。你在排錯的時候看到標志位莫名其妙不對先檢查是不是某條邏輯指令把CF改了。這種小坑寫50行以上的匯編就會遇到。4.4 循環(huán)與數(shù)據(jù)塊操作從單條指令到程序思維到了循環(huán)指令和數(shù)據(jù)串操作就要從“單條指令怎么看”升級為“一段程序怎么寫”了。LOOP指令的經(jīng)典搭配是MOV CX, 10 L1: ; 循環(huán)體 LOOP L1LOOP的執(zhí)行過程是 CX CX - 1如果CX不等于0就跳轉(zhuǎn)到L1否則順序執(zhí)行下一條。這個機制要求你在進入循環(huán)前先把循環(huán)次數(shù)放進CX。容易出錯的地方循環(huán)體里千萬別隨便用CX否則會破壞計數(shù)器嵌套循環(huán)時先把外層計數(shù)器壓棧保存。串操作指令 MOVSB、STOSW、LODSB 配合方向標志DF使用CLD清0方向標志讓指針自增STD置1讓指針自減。REP前綴則相當于一個自動遞減CX的循環(huán)。理解REP MOVSB時可以把它展開成“重復(fù)搬運內(nèi)存塊從源地址搬到目的地址”而且源地址在DS:SI目的地址在ES:DI——這時候就能看出ES段寄存器的用途了。很多教材這里會特別強調(diào)源串可以在當前數(shù)據(jù)段但目的串必須在附加段源和目的都允許用段超越前綴。這一點考察頻率很高也特別容易錯。4.5 匯編程序的整體結(jié)構(gòu)第三章結(jié)束前你至少要知道一個完整的匯編程序長什么樣。我貼一個最小可運行的模板很多實驗都是在此基礎(chǔ)上擴展的DATA SEGMENT BUF DB 10H, 20H, 30H SUM DB ? DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA START: MOV AX, DATA MOV DS, AX ; 主程序邏輯 MOV AL, BUF[0] ADD AL, BUF[1] ADD AL, BUF[2] MOV SUM, AL MOV AH, 4CH INT 21H CODE ENDS END START注意看 ASSUME 和 MOV DS, AX 這兩行ASSUME只是告訴匯編器“我打算讓DS指向DATA段”真正做這件事的是可執(zhí)行指令 MOV DS, AX。很多人忘了寫MOV DS, AX程序一運行就訪問錯誤內(nèi)存調(diào)試半天才反應(yīng)過來——段寄存器的裝載必須由程序自己完成。5. 實操過程搭建環(huán)境、編寫程序、觀察運行5.1 用模擬器跑16位匯編為什么不用現(xiàn)代Visual Studio現(xiàn)在很多人的電腦是64位系統(tǒng)直接把16位匯編程序拿來做實驗會遇到各種兼容性問題。我建議用一個輕量級的模擬器環(huán)境比如經(jīng)典的DOS模擬環(huán)境加MASM匯編器組合。這套組合在高校實驗里用了很多年穩(wěn)定、輕量、可單步調(diào)試。安裝路徑建議放在一個簡單目錄下比如 D:\MASM把 MASM.EXE、LINK.EXE、DEBUG.EXE 放進去寫好的 .ASM 源文件也放一起。路徑越短越不容易出問題別給自己找麻煩。5.2 手動編譯連接流程不求快但要知道每步做什么第一次做實驗不要急著用自動化腳本老老實實走一遍編譯鏈接過程MASM TEST.ASM LINK TEST.OBJ TEST.EXEMASM 生成目標文件時如果有語法錯誤會在屏幕上提示錯誤行號和類型常見的是“error A2006: undefined symbol”或者“error A2105: expected”之類。先讀提示少猜。鏈接時如果提示“LNK2001: unresolved external”通常是你調(diào)用了庫函數(shù)但沒引入或者符號名拼錯。16位匯編里最容易拼錯的是“_TEXT”之類的段名和過程名。調(diào)試用DEBUG帶上相關(guān)參數(shù)后單步執(zhí)行觀察各寄存器和標志位的變化。DEBUG的常用命令就那幾個U 反匯編當前指令T 單步執(zhí)行一條指令G 運行到斷點D 查看內(nèi)存R 查看寄存器課程作業(yè)里最經(jīng)典的驗證實驗就是寫一段程序在數(shù)據(jù)段定義幾個字節(jié)數(shù)據(jù)用不同尋址方式把它們依次送到AX、BX等寄存器同時觀察每條指令執(zhí)行后物理地址如何變化。這個過程做完尋址方式就再也忘不掉了。5.3 一個簡單的內(nèi)存訪問驗證例子舉個例子定義 BUF 為 12H、34H、56H、78H然后用不同尋址方式讀取。DATA SEGMENT BUF DB 12H, 34H, 56H, 78H DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA START: MOV AX, DATA MOV DS, AX MOV BX, OFFSET BUF ; BX BUF的偏移地址 MOV SI, 0 MOV AL, [BXSI] ; 寄存器間接AL12H MOV AL, [BXSI1] ; 相對基址變址AL34H MOV AL, BUF[SI2] ; 寄存器相對AL56H MOV AH, 4CH INT 21H CODE ENDS END START單步執(zhí)行時注意觀察 AL 的變化和 DS:BXSI 組合成的有效地址。你會發(fā)現(xiàn)尋址方式本質(zhì)上就是“偏移地址怎么算”的差別人才不會在考試里做錯。5.4 實驗報告怎么寫才能拿高分實驗報告的重點不是你貼了多少行代碼而是你有沒有把指令執(zhí)行前后的寄存器、內(nèi)存狀態(tài)說清楚。例如運行到 MOV AL, [BXSI1] 之前先寫出BX、SI當前值預(yù)測訪問的物理地址是什么再寫出訪問后AL的值。把“預(yù)測”和“實測”放在一起對比老師一眼就能看出你是真懂了還是抄的。6. 常見問題與排查技巧實錄6.1 標志位總是和你“想當然”的結(jié)果不一致這是前3章實驗里最常遇到的問題。比如你執(zhí)行 ADD AL, BL兩個數(shù)看起來沒超過255但CF或OF卻置1了。先看是不是無符號數(shù)和有符號數(shù)混用了。比如 AL0F0HBL20H相加得到110HAL只保留10HCF置1表示無符號溢出但如果你把0F0H當有符號數(shù)看它是-16-16加32等于16OF不置1結(jié)論完全不同。所以調(diào)試時先明確兩個操作數(shù)的“解釋方式”再看相應(yīng)標志位。6.2 物理地址永遠對不上先查段寄存器裝載寫了 MOV AX, DATA 但忘了 MOV DS, AX程序里的[BX]、BUF[SI]全部訪問的是舊DS指向的段自然全錯。這類問題DEBUG里最常見所以在DEBUG窗口看DS值是什么再對照 .EXE 程序裝載后的段值是否一致。一致了再往下查偏移。6.3 數(shù)據(jù)寬度不匹配匯編器的嚴格檢查16位匯編對操作數(shù)寬度非常敏感。比如 MOV AX, BL 因為AX是16位、BL是8位匯編器直接報錯。解決辦法是先通過 MOV BH, 0 或 MOV AH, 0 把8位擴展成16位或者改用 MOV AL, BL 只傳送低8位。還有一種情況是把立即數(shù)寫大了 MOV AL, 200 如果被當成有符號數(shù)編譯也會不通過。這時候要明確數(shù)據(jù)到底是要8位還是16位AL存200其實沒問題關(guān)鍵是200超過127但沒超過255要用無符號解釋但匯編器對立即數(shù)寬度有嚴格限制可以寫成 MOV AL, 0C8H 來避免歧義。6.4 循環(huán)、地址和堆棧的隱形坑用LOOP時循環(huán)體里如果調(diào)用了子程序而子程序內(nèi)部也用了CX外層循環(huán)計數(shù)就會被破壞。對策很簡單外層循環(huán)計數(shù)進棧保存或換用別的寄存器計數(shù)。調(diào)試棧操作時用DEBUG的T命令單步執(zhí)行 CALL、RET留意SS:SP的變化。一個常見錯誤是棧操作不配對PUSH了三次但只POP了兩次程序返回時RET會把棧里殘留的值當作返回地址直接跳飛到不知道哪里去然后程序就“神秘崩潰”了。排查思路就是檢查所有PUSH/POP是否成對出現(xiàn)。6.5 常見問題速查表現(xiàn)象可能原因排查思路訪問內(nèi)存結(jié)果不對DS未正確裝載檢查 MOV DS, AX 是否執(zhí)行循環(huán)次數(shù)不對CX被循環(huán)體內(nèi)指令修改避免在循環(huán)體中使用CX或用棧保存程序跳飛/崩潰PUSH/POP不配對逐行檢查棧操作標志位異常邏輯指令清CF/OF檢查AND/OR/XOR是否影響了標志位物理地址算不對段寄存器默認選擇錯誤明確是DS還是SS參與尋址比較跳轉(zhuǎn)不對CMP后沒注意ZF/SF/OF對照速查表逐項確認7. 學(xué)習(xí)方法與進階方向還能往哪走7.1 從“背指令”到“讀程序”的轉(zhuǎn)變前三章學(xué)完之后很多同學(xué)有個共同瓶頸單條指令都認識連在一起就看不懂。我的建議是找一份現(xiàn)成的匯編程序比如課程例子里那些幾十行的計算、排序、查表程序先不要看注釋自己在紙上逐條翻譯成自然語言把每條指令的操作數(shù)、尋址方式、影響的標志位寫出來。然后再對照注釋看自己哪里理解偏了。這個練習(xí)做五個程序以上閱讀速度就會有質(zhì)的飛躍。7.2 為后續(xù)接口實驗打基礎(chǔ)端口和中斷是下一個關(guān)鍵詞前三章的內(nèi)容學(xué)到一定程度你其實已經(jīng)在給后面鋪路了第四章之后的存儲器擴展需要你理解物理地址、段、總線寬度可編程并行接口實驗需要你理解端口地址、I/O指令 IN/OUT、控制字中斷系統(tǒng)實驗需要你理解中斷向量表、CS:IP如何被修改、標志位如何入棧。也就是說前三章學(xué)的不是獨立知識點而是后面每章都會反復(fù)使用的“底層語言”。與其在后面實驗課上邊做邊補前面的概念不如現(xiàn)在就把地基打牢。7.3 我的個人經(jīng)驗總結(jié)說句實在話我當年啃前三章花了整整兩周每天看一節(jié)、做一節(jié)筆記、再用DEBUG驗證一個例子。堅持完之后第四章、第五章基本是一路順風(fēng)。這里分享兩個我最受用的技巧一是每學(xué)完一個概念強制自己用DEBUG里的單步執(zhí)行親眼確認一次二是把每個指令的“副作用”——尤其標志位變化——記到小本子上寫程序前翻一翻。這套課程的前三章已經(jīng)夠密實了但真正把它變成自己的還是得靠動手。找一個最小程序反復(fù)改、反復(fù)看寄存器變化比抄十遍筆記都有用。希望你也能在一次次的單步調(diào)試中找到那種“原來CPU是這么跑起來”的通透感。