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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

汇编语言学习笔记(【汇编语言】小甲鱼零基础汇编)

發布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 汇编语言学习笔记(【汇编语言】小甲鱼零基础汇编) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

匯編語言學習筆記(【匯編語言】小甲魚零基礎匯編)

目錄

第〇章 課程資料

第一章 基礎知識

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

第三章 寄存器(內存訪問)

第四章 第一個程序

第五章 [BX]和loop指令

第六章 包含多個段的程序

第七章 更靈活定位內存地址

第八章 數據處理的兩個基本問題

第九章 轉移指令的原理

第十章 call和ret指令

第十一章 標志寄存器

第十二章 內中斷

第十三章 int指令

第十四章 端口

第十五章 外中斷

第十六章 直接定址表

第十七章 使用BIOS進入鍵盤輸入和磁盤讀寫

綜合研究(自己看書)

?

第〇章 課程資料

1.課件+源代碼 2.《匯編語言(第3版) 》王爽著 電子書 3.課后習題答案

百度云:https://pan.baidu.com/s/1TE1Egc0ZmeJfLP5zvamo0Q
提取碼:3y72

第一章 基礎知識

【學習匯編主要是:學習匯編的編程思想,掌握機器運行的思維】 匯編語言是直接在硬件上工作的編程語言,首先要了解硬件系統的結構,才能有效的應用匯編語言對其編程。 1.匯編課程的研究重點如何利用硬件系統的編程結構和指令集有效靈活的控制系統進行工作 2.匯編語言的主體是匯編指令 3.匯編指令和機器指令的差別在于指令的表示方法上匯編指令是機器指令便于記憶的書寫格式 4.匯編語言時機器指令的助記符 5.匯編語言的組成1.匯編指令(機器碼的助記符)2.偽指令(由編譯器執行)3.其他符號(由編譯器識別,如:+ - * /)匯編語言的核心是匯編指令,他決定了匯編語言的特性 6.CPU對存儲器的讀寫CPU要想進行數據的讀寫,必須和外部器件(即芯片)進行三類信息的交互1.地址信息:存儲單元的地址2.控制信息:芯片的選擇,讀或寫命令3.數據信息:讀或寫的數據

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

CPU=運算器+控制器+【寄存器】,器件之間通過總線相連 8086CPU有14個寄存器,名稱分別為:AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW 2.1 通用寄存器1.8086CPU所有的寄存器都是16位的,可以存放2個字節2.AX、BX、CX、DX通常用來存放一般性數據被稱為通用寄存器3.8086上一代CPU中的寄存器都是8位的,為了保證兼容性這四個寄存器都是可以分為2個獨立的8位寄存器使用AX=AH+ALBX=BH+BLCX=CH+CLDX=DH+DL4.AX的低8位(0-7)構成AL寄存器高8位(8-15)構成了AH寄存器AH和AL寄存器是可以獨立使用的8位寄存器 2.2 字在寄存器中的存儲8086一個字16位

2.3 幾條匯編指令1.匯編指令不區分大小寫 2.幾條匯編指令mov ax,18 ;AX=18mov ah,78 ;AH=78add ax,8 ;AX=AX+8mov ax,bx ;AX=BXadd ax,bx ;AX+=BX3.用目前學過的匯編指令,最多使用四條指令,編程計算2的4次方mov ax,2 ;ax=2add ax,ax ;ax=4add ax,ax ;ax=8add ax,ax ;ax=16 2.4 物理地址1.CPU訪問內存單元時,要給出內存單元的地址。2.所有的內存單元夠成的存儲空間是一個一維的線性空間3.我們將這個唯一的地址稱為物理地址 2.5 16位結構的CPU16位結構描述了一個淳樸具有以下幾個方面特征:1.運算器一次最多可以處理16位的數據2.寄存器的最大寬度為16位3.寄存器和運算器之間的通路是16位的 2.6 8086CPU給出物理地址的方法1.8086有20位地址總線,可傳送20位地址,實際上的尋址能力為1M2.8086內部為16位結構,它只能傳送16位的地址,理論上表現出的尋址能力卻只有64K3.問題:8086CPU如何用內部16位的數據轉換成20位的地址?1.8086CPU采用一種在內部用兩個16位地址合成的方法,來形成20位的物理地址即:段地址+偏移地址=物理地址2.地址加法器合成物理地址的方法:物理地址=段地址×16+偏移地址3.“地址段×16”即是數據左移4位(二進制位的左移4位,十六進制的左移1位)在地址加法器中,如何完成“段地址×16”?二進制形式的段地址左移4位 2.7 “段地址×16+偏移地址=物理地址”的本質含義1.即可以用兩個16位的二進制數來表示一個20位的二進制數2.8086CPU中內部為16位結構,但地址線卻是20位的,使用地址加法器可以把16位地址變成20位地址具體操作就是:段地址×16+偏移地址 2.8 段的概念1.內存并沒有分段,段的劃分來自于CPU,由于8086CPU用“段地址×16+偏移地址=物理地址”的方式給出內存單元的物理地址,使得我們可以用分段的方式來管理內存2.以后,在編程時可以根據需要,將若干地址連續的內存單元看作一個段,使用段地址×16定位段的起始地址(基礎地址),用偏移地址定位段中的內存單元3.注意1.段地址必然是16的倍數,即一個段的起始地址必然是16的倍數2.偏移地址為16位,16位地址的尋址能力為64K,所以一個段的長度最大為64K3.CPU可以用不同的段地址和偏移地址形成同一個物理地址 2.9 段寄存器1.段寄存器就是提供段地址的8086CPU有4個段寄存器:1.CS(code segment)2.DS(data segment)3.SS(stack segment)4.ES(extra segment)2.當8086CPU要訪問內存時,有這4個段寄存器提供內存單元的段地址 2.10 CS和IP1.CS和IP時候8086CPU中最關鍵的寄存器他們指示了CPU當前讀取指令的地址。2.CS和IP的含義CS:代碼段寄存器IP:指令指針寄存器【專用寄存器】3.8086CPU工作過程的簡要描述1.從CS:IP指向內存單元,讀取指令,讀取的指令進入指令緩沖器2.IP=IP+所讀取指令的長度,從而指向下一條指令3.執行指令,轉到步驟1,重復這個過程4.開機時的CS和IP1.在8086CPU加電啟動或復位后(即CPU剛開始工作時)CS和IP被設置為CS=FFFFH,IP=0000H2.即在8086PC機剛啟動時,CPU從內存FFFF0H單元中讀取指令執行3.FFFF0H單元中的指令是8086PC機開機后執行的第一條指令5.修改CS、IP的指令1.在CPU中,程序員能夠【用指令讀寫】的部件只有【寄存器】,程序員可以通過改變寄存器中的內容實現對CPU的控制2.CPU從何處執行指令是由CS、IP中的內容決定的,程序員可以通過改變CS、IP中的內容控制CPU執行目標指令3.如何修改CS和IP?1.通過mov改變AX等,但是不能通過mov改變CS和IP2.【jmp 段地址:偏移地址】 可以用來同時修改CS和IP指令中的段地址修改CS偏移地址修改IP3.【jmp 某一合法的寄存器】 僅修改IP的內容比如:jmp ax 或者 jmp bx(類似于mov IP ax)4.jmp是只具有一個操作對象的指令 2.11 代碼段1.可以將長度為N(N<=64KB)的一組代碼,存放在一組地址連續、其實地址為16的倍數的內存單元中這段內存是用來存放代碼的,從而定義了一個代碼段2.CPU中只認被CS:IP指向的內存單元中的內容為指令 【實驗一】查看CPU和內存,用機器指令和匯編指令編程1.R命令:查看、改變CPU寄存器的內容r后面加寄存器的名稱可以改變CPU寄存器的內容2.D命令:查看內存中的內容3.E命令:改寫內存中的內容4.U命令:將內存匯總的機器指令翻譯成匯編指令5.T命令:執行一條機器指令6.A命令:以匯編指令的格式在內存中寫入一條機器指令1.debug中輸入的默認是16位數2.空格數量任意7.按Q可以退出

第三章 寄存器(內存訪問)

3.1 內存中字的存儲

1.任何兩個地址連續的內存單元,N號單元和N+1號單元,可以將他們看成兩個存儲單元也可以看成一個地址為N的字單元中的高位字節單元和低位字節單元2.注意:在內存的表示中,從高到低,是從0號單元開始,然后逐漸變大,即在書寫時,低位寫在高的地方,高位寫在低的地方,如上圖所示:4E20H即是0號字節存儲20,1號字節存儲4E 3.2 DS和[address]1.8086中有一個DS寄存器,通常用來存放要訪問的數據的段地址2.例如:我們要讀取10000H單元的內容可以用如下程序段進行:mov bx,1000Hmov ds,bxmov al,[0]上面的三條指令將10000H(1000:0)中的數據讀到al中1.復習:已知mov指令可以完成的兩種傳送功能1.將數據直接送入寄存器2.將一個寄存器中的內容送入另一個寄存器中2.除此之外,mov指令還可以將一個內存單元中的內容送入一個寄存器mov指令格式:mov 寄存器名,內存單元地址[...]表示一個內存單元,“[...]”中的...表示內存單元的【偏移地址】執行指令時,8086CPU自動取DS中的數據為內存單元的【段地址】3.如何把1000H放入DS中?要通過通用寄存器把段地址傳入到DS中8086CPU不支持將數據直接送入段寄存器的操作,DS是一個段寄存器即:mov ds,1000H 是非法的數據->通用寄存器->段寄存器3.寫幾條指令,將AL中的數據送入內存單元10000H?mov bx,1000Hmov ds,bxmov [0],al ;al中的字節型數據送入到1000H:0中 3.3 字的傳送1.8086CPU是16位結構,有16根數據線,所以可以一次性傳送16位的數據即:一次可以傳送一個字2.比如mov bx,1000Hmov ds,bxmov ax,[0] ;1000H:0處的字型數據送入ax中mov [0],cx ;cx中的16位數據送入到1000H:0中 3.4 mov、add、sub指令1.復習:已學mov指令的幾個形式1.mov 寄存器,數據 ;立即尋址2.mov 寄存器,寄存器 ;寄存器尋址3.mov 寄存器,內存單元 ;直接尋址4.mov 內存單元,寄存器 ;寄存器尋址?5.mov 段寄存器,寄存器 ;寄存器尋址6.mov 寄存器,段寄存器 ;寄存器尋址2.add、sub同mov一樣,都有兩個操作對象1.add的用法1.add 寄存器,數據 ;立即尋址2.add 寄存器,寄存器 ;寄存器尋址3.add 寄存器,內存單元 ;直接尋址4.add 內存單元,寄存器 ;2.sub的用法【不帶借位的減法】指令格式 sub op1,op2 ;意為:op1=op1-op21.sub 寄存器,數據 ;立即尋址2.sub 寄存器,寄存器 ;寄存器尋址3.sub 寄存器,內存單元 ;直接尋址4.sub 內存單元,寄存器 ; 3.5 數據段如何訪問數據段中的數據?將一段內存當作數據段,是我們在編程時的一種安排具體操作:用DS存放數據段的段地址,再根據需要,用相關指令訪問數據段中的具體單元 3.6 棧1.8086CPU提供相關的指令來以棧的方式訪問內存空間這意味著,我們在基于8086CPU編程的時候,可以將一段內存當作棧來使用2.8086CPU提供入棧和出棧指令:(最基本的)push(入棧)pop(出棧)1.push ax:將寄存器ax中的數據送入棧中2.pop ax:從棧頂取出數據送入ax3.8086CPU的入棧和出棧操作都是以【字(16位)】為單位進行的4.pop和push可以在寄存器和內存之間傳送數據3.CPU如何知道一段內存空間被當做棧使用?1.8086CPU中,有兩個寄存器1.段寄存器SS:存放棧頂的段地址2.寄存器SP:存放棧頂的偏移地址【專用寄存器】2.任意時刻SS:SP指向棧頂元素,當棧為空的時候,也就不存在棧頂元素ss:sp也就指向棧最高地址單元的下一個單元4.執行push和pop的時候,如何知道哪個單元是棧頂單元?1.執行push ax時1.sp=sp-22.將ax中的內容送入到ss:sp指向的內存單元ss:sp此時指向新棧頂2.執行pop ax時1.將ss:sp指向的內存單元的內容送入到ax中注意:這里取出的內容在內存中還是存在的,并沒有被重置下一輪push會覆蓋2.sp=sp+25.如果棧是空的,sp指向哪里?sp指向最高地址單元的下一個單元 3.7 棧頂超界的問題ss、sp只記錄了棧頂的地址,依靠ss、sp可以保證在入棧和出棧時找到棧頂可以,如何能夠保證在入棧、出棧時,棧頂不會超出棧空間?1.8086CPU不保證棧的操作不會越界2.當棧空的時候,再執行pop出棧 或者 當棧滿的時候再使用push入棧都會發生棧頂超界問題,會操作到棧以外的數據,這些數據可能是其他用途的數據或者代碼棧頂超界是危險的!!!3.8086CPU沒有記錄棧頂上下限的寄存器 3.8 棧段1.將一段內存當做棧段,僅僅是我們在編程時的一種安排,2.ss:sp指向我們定義的棧段的棧頂;3.當棧空時,sp指向最高地址的下一個單元4.思考:一個棧段最大可以設為多少?64KB5.設棧頂的變化范圍是0-FFFFH,從棧空時sp=0(最高地址單元FFFFH的下一個單元0000H)一直壓棧,直到棧滿,sp=0;如果再次壓棧,棧頂將環繞,覆蓋原來棧中的內容6.一段內存,既可以是代碼的存儲空間,又可以是數據的存儲空間,還可以是棧空間也可以是什么都屬實。關鍵在于CPU中寄存器的設置,即:cs、ip、ss、sp、ds的設置**可以通過mov直接給sp賦值【立即數尋址】,但是不能通過mov給cs、ip、ss、ds賦值給cs和ip賦值需要使用jum指令給ss和ds賦值需要使用mov ss或ds,寄存器 ;【寄存器尋址】 【實驗二】

第四章 第一個匯編程序

4.1 一個源程序從寫出到執行的過程1.一個匯編語言程序從寫出到最終執行的簡要過程編寫->編譯連接->執行2.對源程序進行編譯連接1.使用匯編語言編譯程序(MASM.EXE)對源程序文件中的源程序進行編譯,產生目標文件【.obj文件】2.再用連接程序(LINK.EXE)對目標文件進行連接,生成可在操作系統中直接運行的可執行文件【.EXE文件】。3.可執行文件包含兩部分內容1.程序(從源程序的匯編指令翻譯過來的機器碼)和數據(源程序中定義的數據)2.相關的描述信息(比如:程序有多大、要占多少內存空間等)4.執行可執行文件中的程序1.在操作系統(如:MSDOS)中,執行可執行文件中的程序2.操作系統依照可執行文件中的描述信息,將可執行文件中的機器碼和數據加載入內存并進行相關的初始化(比如:設置CS:IP指向第一條要執行的指令),然后由CPU執行程序 4.2 源程序的主要結構源程序由 匯編指令+偽指令+宏指令 組成偽指令:編譯器處理匯編指令:編譯為機器碼 1.偽指令1.沒有對應的機器碼的指令,不能由CPU直接執行2.偽指令是由編譯器來執行的指令,編譯器根據偽指令來進行相關的編譯工作2.segment和ends【定義一個段】1.segment和ends是一對成對使用的偽指令2.編寫匯編程序【必須】使用到的指令3.segment和ends的功能是定義一個段segment:說明一個段開始ends:說明一個段結束4.一個段必須有一個名稱來標識,使用格式為段名 segment段名 ends5.一個匯編程序由多個段組成這些段用來存放【代碼、數據、或當作棧空間】來使用一個有意義的匯編程序至少要有一個段,這個段用來存放代碼。3.end【真正的沒了】1.end是一個匯編程序的結束標記2.編譯器在編譯匯編程序的過程中,如果碰到了偽指令end,就結束對源程序的編譯3.如果程序寫完了,要在結尾處加上偽指令end否則,編譯器無法知道程序在何處結束4.【切記】不要把end和ends搞混了end:匯編程序的結束標記ends:與segment成對出現4.assume【寄存器和段的關聯假設】1.它假設某一段寄存器和程序中的某一個用segment...ends定義的段相關聯2.通過assume說明這種關聯,在需要的情況下,編譯程序可以將段寄存器和某一具體的段相聯系5.程序和源程序1.我們將源程序文件中的所有內容稱為【源程序】2.將源程序中最終由計算機執行處理的指令或數據稱為【程序】3.程序最先以匯編指令的形式,存儲在源程序中然后經過編譯、連接后轉變為機器碼,存儲在可執行文件中6.標號,標號與段名稱有所區別1.一個標號指代了一個地址,即是段名稱。2.段名稱 放在segment的前面,作為一個段的名稱這個段的名稱最終將被匯編、連接程序處理為一個段的段地址7.DOS中的程序運行1.DOS是一個單任務操作系統2.一個程序結束后,將CPU的控制權交還給是他得以運行的程序我們稱這個過程為:程序返回8.程序返回mov ax,4c00Hint 21H ;【中斷機制】是DOS最偉大的機制,Windows系統上是【消息機制】這兩條指令所實現的功能就是程序返回9.幾個和結束相關的內容1.段結束:偽指令通知編譯器一個段的結束【ends】2.程序結束:偽指令通知編譯器程序的結束【end】3.程序返回:匯編指令mov ax,4c00Hint 21H10.語法錯誤和邏輯錯誤1.語法錯誤1.程序在編譯時被編譯器發現的錯誤2.容易發現2.邏輯錯誤1.在編寫時不會表現出來的錯誤、在運行時會發生的錯誤2.不容易發現 4.3 以簡化的方式進行匯編和連接匯編使用的程序:masm.exe連接使用的程序:link.exe簡化方式進行匯編和連接的程序:ml.exe 4.4 匯編和連接的作用連接的作用1.當源程序很大時,可以將他們分成多個源程序文件夾編譯每個源程序編譯成為目標文件后,再用連接程序將它們連接在一起,生成一個可執行文件2.程序中調用了某個庫文件中的子程序,需要將這個庫文件和該程序生成的目標文件連接到一起生成一個可執行文件3.一個源程序編譯后,得到了存有機器碼的目標文件,目標文件中的有些內容還不能直接用來生成可執行文件,連接程序將這些內容處理為最終的可執行信息。所以在只有一個源程序文件,而又不需要調用某個庫中的子程序的情況下,也必須用連接程序對目標文件進行處理,生成可執行文件 4.5 可執行文件中的程序裝入內存并運行的原理1.在DOS中,可執行文件中的程序P1若要運行,必須有一個正在運行的程序P2將P1從可執行文件中加載入內存,將CPU的控制權交給P1,P1才能得以運行2.當P1運行完畢后,應該將CPU的控制權交還給使他得以運行的程序3.操作系統的外殼1.操作系統是由多個功能模塊組成的龐大、復雜的軟件系統任何通用的操作系統,都需要提供一個稱為shell(外殼)的程序,用戶(操作人員)使用這個程序來操作計算機系統工作2.DOS中有一個程序command.com,這個程序在DOS中稱為命令解釋器也就是DOS系統的shell4.執行可執行文件1.exe時,(1)什么程序將CPU的控制權交給了1.exe?(2)將程序1.exe加載入內存后,如何使程序得以運行?(3)1.exe程序運行結束后,返回到了哪里?1.在DOS中直接執行1.exe時,是正在運行的cmd.exe將1.exe中的程序加載入內存2.cmd.exe設置CPU的CS:IP指向程序的第一條指令(即,程序的入口)從而使程序得以運行3.程序運行結束后,返回cmd.exe中,CPU繼續運行cmd.exe 【實驗三】

第五章 【bx】和loop指令

5.1 [bx]1.和[0]類似,[0]表示內存單元,它的偏移地址是0;2.[bx]同樣也表示一個內存單元,它的段地址在DS中它的偏移地址在bx中,至于是取字還是取字節,要看他放入的寄存器是8位還是16位3.補充:inc指令:相當于C語言中的++運算符 5.2 Loop指令這個指令和循環有關1.指令格式:loop 標號CPU執行loop指令的時候,要進行兩步操作1.(cx)=(cx)-1;2.判斷cx中的值,若不為零,則轉至標號處執行程序若為零,則向下執行。2.通常,loop指令實現循環,cx中存放循環的次數3.標號在匯編語言中,標號代表了一個地址,標號標識了一個地址4.使用cx和loop指令相配合實現循環功能的三個要點1.在cx中存放循環次數2.loop指令中的標號所標識地址要在前面3.要循環執行的程序段,要寫在標號和loop指令的中間5.用cx和loop指令相配合實現循環功能的程序框架mov cx,循環次數S:循環執行的程序段loop s 5.3 在Debug中跟蹤供loop指令實現的循環程序**注意:在匯編程序中,數據不能以字母開頭,如果要輸入像FFFFH這樣的數則要在前面添加一個0在debug程序中引入G命令和P命令1.G命令G命令如果后面不帶參數,則一直執行程序,直到程序結束G命令后面如果帶參數,則執行到ip為那個參數地址停止2.P命令T命令相當于單步進入(step into)P命令相當于單步通過(step over) 5.4 Debug和匯編編譯器Masm對指令的不同處理1.在debug中,可以直接用指令 mov ax,[0] 將偏移地址為0號單元的內容賦值給ax2.但通過masm編譯器,mov ax,[0] 會被編譯成 mov ax,01.要寫成這樣才能實現:mov ax,ds:[0]2.也可以寫成這樣:mov bx,0mov ax,[bx] ;或者mov ax,ds:[bx] 5.5 loop和[bx]的聯合應用1.計算ffff:0~ffff:b單元中的數據的和,結果存儲在dx中1.注意兩個問題1.12個8位數據加載一起,最后的結果可能會超出8位(越界),故要用16位寄存器存放結果2.將一個8位的數據加入到16位寄存器中,類型不匹配,8位的數據不能與16位相加2.【解決辦法】把原來8位的數據,先通過通用寄存器ax,將它們轉化成16位的3.代碼如下 assume cs:codesgcodesg segment start:;指定數據段mov ax,0ffffhmov ds,ax;初始化mov ax,0mov dx,0mov bx,0;指定循環次數,12次mov cx,0ch circ:;把8位數據存入al中,即ax中存放的是[bx]轉化之后的16位數據,前8位都是0mov al,[bx];進行累加add dx,ax;bx自增,變化內存的偏移地址inc bxloop circ;程序返回mov ax,4c00hint 21H codesg endsend start 5.6 段前綴1.指令“mov ax,[bx]”中,內存單元的偏移地址由bx給出,而段地址默認在ds中2.我們可以在訪問內存單元的指令中顯式地給出內存單元的段地址所在的段寄存器比如 mov ax,ds:[0]mov ax,ds:[bx]這里的ds就叫做【段前綴】 5.7 一段安全的空間1.8086模式中,隨意向一段內存空間寫入內容是很危險的因為這段空間中可能存放著【重要的系統數據或代碼】2.在一般的PC機中,DOS方式下,DOS和其他合法的程序一般都不會使用【0:200~0:2FF】的256個字節的空間。所以,我們使用這段空間是安全的

第六章 包含多個段的程序

6.1在代碼段中使用數據1.dw的含義【定義字型數據:define word,16字節】在數據段中使用dw定義數據,則數據在數據段中在代碼段中使用dw定義數據,則數據在代碼段中堆棧段也是一樣2.在程序的第一條指令前加一個標號start,并且這個標號在偽指令end后面出現可以通知編譯器程序在什么地方結束,并且也可以通知編譯器程序的入口在哪里 6.2在代碼段中使用棧**補充:如果題目要求【逆序】存放,就要想到棧(FILO)使用dw向系統申請一段空間,然后把這個空間當做棧 6.3將數據、代碼、棧放入不同的段1.在前面的6.1和6.2中,我們在程序中用到了數據和棧,我們在編程的時候要注意何處是數據,何處是棧、何處是代碼2.這樣做顯然有兩個問題1.把他們放在一個段中是程序顯得混亂2.前面程序中處理的數據很少,用到的棧空間也小,放在一個段里面沒有問題但數據、棧、代碼需要的空間超過64KB,就不能放在一個段中(8086中一個段的容量不能大于64KB)3.我們可以和定義代碼段一樣的方法來定義多個段然后在這些段里面定義需要的數據,或通過定義數據來取得棧空間4.將數據、代碼、棧放入不同的段1.我們可以在源程序中為這三個段起具有含義的名稱用來存放數據的段,我們將其命名為“data”用來存放代碼的段,我們將其命名為“code”用來作棧空間的段,我們將其命名為“stack”但是CPU看得懂嗎?【不能】2.我們在源程序中用偽指令“assume cs:code,ds:data,ss:stack”將cs、ds和ss分別和code、data、stack段相連這樣做了之后,CPU是都就會將cs指向code,ds指向data,ss指向stack從而按照我們的意圖來處理這些段呢?【不能】偽指令CPU看不懂,偽指令是給編譯器看的3.若要CPU按照我們的安排行事,就要用機器指令控制它,源程序中的匯編指令才是CPU要執行的內容需在在code段中給DS,CS、SS設置相應的值才能讓CPU識別出數據段、代碼段、堆棧段其中匯編程序開始的地方(即代碼段開始的地方)由end后面的標號所指向的地方給出5.assume指令不可省略,至于為什么,需要以后多多體會 【實驗五】1.如果段中的數據占N個字節,則程序加載后,這段實際占有的空間為:N%16==0?N:16×(N/16+1);因為一個段最小占用16字節,即有16個字節只有這個段可以訪問到2.在編輯源程序的時候,如果調換各個段的編寫位置,最后CS、DS、SS的值會發生變化3.如果去掉start,編譯器會從上到下執行,如果第一個段是代碼段,則可以正常運行若第一個段不是代碼段,則不會正常運行4.代碼示例1 assume cs:code,ds:data,ss:stack;數據段 data segment;8個數據dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h data ends;棧段 stack segment;8個數據dw 0,0,0,0,0,0,0,0 stack ends;代碼段 code segment start:;棧空間初始化mov ax,stackmov ss,axmov sp,16;數據段初始化mov ax,datamov ds,axpush ds:[0];一個棧單元是一個字push ds:[2];存放數據不會改變pop ds:[2]pop ds:[0];程序返回mov ax,4c00hint 21h code ends end 5.將a,b數據段中的內容分別相加,結果放入data數據段中 assume cs:code;數據段 a segmentdb 1,2,3,4,5,6,7,8 a ends;數據段 b segmentdb 1,2,3,4,5,6,7,8 b ends;數據段 data segmentdb 0,0,0,0,0,0,0,0 data ends;代碼段 code segment start:mov bx,0mov ax,0mov dx,amov ss,dxmov dx,bmov es,dxmov dx,datamov ds,dxmov cx,8 circ:add al,ss:[bx]add al,es:[bx]mov [bx],alinc bxmov al,0loop circ;程序返回mov ax,4c00hint 21h code ends end start 6.將a數據段中的前8個字型數據逆序存儲到b段中 assume cs:code a segmentdw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh a endsb segmentdw 0,0,0,0,0,0,0,0 b endscode segment start:mov ax,0mov ax,amov ss,axmov sp,0mov ax,0mov ax,bmov ds,axmov bx,0mov cx,8 circ:pop [bx]add bx,2loop circmov ax,4c00hint 21h code ends end start

第七章 更靈活地定位內存地址

本章主要講解一些更靈活的定位內存地址的方法和相關的編程方法 7.1 and和or指令1.and指令:邏輯與指令,按位進行與運算1.如:mov al,01100011Band al,00111011B執行后:al=00100011B2.通過and指令可將操作對象的相應位設為0,其他位保持不變例如al的第6位設為0:and al,10111111B例如al的第7位設為0:and al,01111111B例如al的第0位設為0:and al,11111110B2.or指令,邏輯或運算,按位進行或運算1.如:mov al,01100011Bor al,00111011B執行后:al=01111011B2.通過該指令可將操作對象的相應位設為1,其他位不變or al,01000000B;將al的第6位設為1or al,10000000B;將al的第7位設為1or al,00000001B;將al的第0位設為1 7.2 關于ASCII碼一種編碼方案,在計算機系統中通常被采用,8位

7.3 以字符形式給出的數據1.在匯編程序中,可以使用'×××'的方式指明數據是以字符的形式給出的2.編譯器會將它們轉化為相應的ASCII碼3.例如1.db 'unIX' ;相當于:db 75H,6EH,49H,58H'u'、'n'、'I'、'X'的ASCII碼分別為75H,6EH,49H,58H2.mov al,'a' ;相當于:mov al,61H'a'的ASCII碼為61H4.ASCII碼中,大寫字母和小寫字母之間的規律小寫字母=大寫字母+32小寫字母=大寫字母+20H大寫字母從41H開始排,小寫字母從61H開始排 大寫二進制小寫二進制
A01000001a01100001
B01000010b01100010
C01000011c01100011
D01000100d01100100
7.4 大小寫轉換的問題1.方案一:1.識別出是該字節是表示一個的大寫英文字符,還是小寫的用于條件判斷的匯編程序,目前還沒有學到2.根據+20H 或者 -20H進行大小寫轉換2.方案二:1.若全部轉化為大寫,則將第5位置0and al,11011111B2.若全部轉化為小寫,則將第5位置1or al,00100000B 7.5 [bx+常數]mov ax,[bx+200]的含義:1.將一個內存單元的內容送入ax,這個內存單元的長度為2字節,存放一個入一個子單元該字單元的偏移地址為bx中的數值加上200,段地址在ds中2.也可以寫成1.mov ax,200[bx]2.mov ax,[bx].200 7.6 用[bx+idata]的方式進行數組的處理在codesg中填寫代碼,將datasg中定義的第一個字符串轉化為大寫,第二個字符串轉化為小寫1.我們觀察datasg段中的兩個字符串,一個的起始地址為0,另一個的起始地址為52.我們可以將這兩個字符串看作兩個數組,一個從0地址開始存放,另一個從5開始存放3.我們可以用[0+bx]和[5+bx]的方式在同一個循環中定位這兩個字符串中的字符4.注意這個數組的定位方式,對比C語言C語言的數組定位方式:a[i],b[i], a、b是地址常量匯編語言的數組定位方式:0[bx],5[bx]所以:[bx+常數]的方式為高級語言實現數組提供了便利的機制 assume cs:codesg,ds:datasgdatasg segmentdb 'BaSiC'db 'MinIX' datasg endscodesg segment start:mov ax,datasgmov ds,axmov bx,0mov cx,5 ;做5次循環 circ:mov al,[bx]and al,11011111bmov [bx],almov al,[bx+5];等價于mov al,5[bx];等價于mov al,[bx].5or al,00100000bmov 5[bx],alinc bxloop circmov ax,4c00hint 21h codesg ends end start 7.7 SI和DI已經學過的10個寄存器:AX、BX、CX、DX、DS、CS、SS、ES、IP、SP1.SI和DI是8086CPU中和bx功能相近的寄存器bx不夠用,所以引進了SI和DI2.SI和DI(16位)不能夠分成兩個8位寄存器來使用【和bx的區別】3.下面三組指令實現了相同的功能1.mov bx,0mov ax,[bx]2.mov si,0mov ax,[si]3.mov di,0mov ax,[di]4.下面三組指令也實現了相同的功能1.mov bx,0mov ax,[bx+123]2.mov si,0mov ax,[si+123]3.mov di,0mov ax,[di+123]5.用寄存器SI和DI實現將字符串'welcome to masm!'復制到它后面的數據區中通常用ds:si指向要復制的源始字符串通常用ds:di指向要復制的目的空間**注意si、di是16位寄存器,循環中自增時,應該+2 assume cs:code,ds:data data segmentdb 'welcome to masm!'db '................' data endscode segment start:mov ax,datamov ds,axmov si,0mov di,16mov cx,8 circ:mov ax,0[si]mov [di],axinc diinc diinc siinc siloop circmov ax,4c00hint 21h code ends end start 7.8 [bx+si]和[bx+di]1.[bx+si]和[bx+di]的含義類似,我們以[bx+si]為例進行講解[bx+si]表示一個內存單元,它的偏移地址為bx中的數值加上si中的數值它的偏移地址在ds中2.[bx+si]也可以寫成[bx][si] 7.9 [bx+si+常數]和[bx+di+常數]1.以[bx+Si+常數]為例講解[bx+si+常量]表示一個內存單元,偏移地址為bx的值+si的值+常數2.指令mov ax,[bx+si+常數]也可以寫成如下形式1.mov ax,200[bx+si]2.mov ax,200[bx][si]3.mov ax,[bx].200[si] 7.10 不同的尋址方式的靈活應用1.總結幾種定位內存的方法1.ds:[常數] 【直接尋址】用一個常量來表示地址,可用于直接定位一個內存單元2.[bx] 【寄存器間接尋址】用一個寄存器的值來表示內存地址,可以間接定位一個內存單元3.[bx+常數] 【??】用一節寄存器的值和常量表示內存地址,可在一個起始地址的基礎上用變量間接定位一個內存單元4.[bx+si]5.[bx+si+常數]2.編程,給定數據段data,將data段中每個單詞的頭一個字母改寫成大寫字母 assume cs:code,ds:data data segmentdb '1. file 'db '2. edit 'db '3. search 'db '4. view 'db '5. options 'db '6. help ' data endscode segment start:mov ax,datamov ds,axmov bx,0mov cx,6 circ:mov al,[bx+3]and al,11011111bmov [bx+3],aladd bx,16loop circmov ax,4c00hint 21h code ends end start 3.編程,給定數據段data,將data段中的每個單詞改為大寫字母1.【loop指令cx-1之后,在判斷是否為0】2.雙重循環用匯編怎么實現?應該在每次開始內循環的時候,將外層循環的cx的值保存起來,在執行外層循環的loop指令前,在恢復外層循環的cx數值。**可以用寄存器來臨時保存,也可以用棧空間(內存)保存【沒有多余的寄存器】更好的方法是使用:棧1.使用寄存器實現 assume cs:code,ds:data data segmentdb 4,4,6,4,7,4;單詞的字母數db ' ';補齊db '1. file 'db '2. edit 'db '3. search 'db '4. view 'db '5. options 'db '6. help ' data endscode segment start:mov ax,datamov ds,axmov bx,16mov si,0mov di,0mov cx,6;外層循環6次 outer:;外層循環mov dx,cx;用寄存器將外層循環的次數保存,C語言中是用棧來保存的mov cx,0mov cl,[di];內循環的次數 inner:;內層循環mov al,[bx][si+3]and al,11011111bmov [bx][si+3],alinc siloop inneradd bx,16mov si,0inc dimov cx,dx;恢復外層循環的次數loop outermov ax,4c00hint 21h code ends end start 2.使用棧實現【更好的方法】 assume cs:code,ds:data,ss:stack data segmentdb 4,4,6,4,7,4;單詞的字母數db ' ';補齊db '1. file 'db '2. edit 'db '3. search 'db '4. view 'db '5. options 'db '6. help ' data endsstack segmentdw 1,2,3,4,5,6,7,8 stack endscode segment start:mov ax,datamov ds,axmov ax,stackmov ss,axmov sp,16mov bx,16mov si,0mov cx,6;外層循環6次 outer:;外層循環push cx;將外層循環的次數保存mov cx,0mov cl,[di];內循環的次數 inner:;內層循環mov al,[bx][si+3]and al,11011111bmov [bx][si+3],alinc siloop inneradd bx,16mov si,0inc dipop cx;恢復外層循環的次數loop outermov ax,4c00hint 21h code ends end start

第八章 數據處理的兩個基本問題

本章對前面的所有內容是具有總結性的 計算機是進行數據處理、運算的機器,那么有兩個基本的問題就包含在其中:1.處理的數據在什么地方?2.要處理的數據有多長?這兩個問題,在機器指令中必須給以明確或隱含的說明,否則計算機就無法工作 8.1 bx、si、di、bp1.在8086CPU中,只有這4個寄存器(bx、bp、si、di)可以用在“[...]”中,用來進行內存單元的尋址2.在“[...]”中,這四個寄存器(bx、bp、si、di)可以單個出現,或者只能以以下4種組合出現1.bx和si2.bx和di3.bp和si4.bp和di3.錯誤的用法mov ax,[bx+bp]mov ax,[si+di]4.只要在[...]中使用寄存器bp,則指令中沒有顯性給出段地址,那么段地址就默認在ss中,比如:mov ax,[bp] ax的值為棧空間中,偏移地址為bp的內存單元mov ax,[bp+常數] mov ax,[bp+si]mov ax,[bp+si+常數] 8.2 機器指令處理的數據所在的位置1.絕大部分機器指令進行數據處理的指令大致可分為3大類讀取、寫入、運算2.在機器指令這一層,并不關心數據的值是多少,而關心指令執行前一刻它將要處理的數據所在的位置3.指令在執行前,所要處理的數據可以在三個地方CPU內部(寄存器)、內存、端口 8.3 匯編語言中數據位置的表達匯編語言中用三個概念來表達數據的位置1.立即數2.寄存器3.段地址(SA)和偏移地址(EA)1.存放段地址的寄存器可以是默認的,既可以是默認在ds中,也可以是在ss中(使用bp寄存器)2.存放段地址的寄存器也可以顯性的給出mov ax,ds:[bp]mov ax,es:[bx]mov ax,ss:[bx+si]mov ax,cs:[bx+si+8] 8.4 尋址方式

8.5 指令要處理的數據有多長?1.8086CPU的指令,可以處理兩種尺寸的數據,byte和word所以在機器指令中要指明,指令進行的是字操作還是字節操作2.8086CPU確定數據長度的幾種方法1.通過寄存器名指明要處理的數據的尺寸mov al,1 ;指明數據是字節型的mov bx,ds:[0] ;指明數據是字型的2.在沒有寄存器名存在的情況下,用操作符X ptr指明內存單元的長度X在匯編指令中可以為word或byte1.下面的指令中,用byte ptr指明了指令訪問的內存單元是字節型單元mov byte ptr ds:[0],1inc byte ptr [bx]inc byte ptr ds:[0]add byte ptr [bx],22.下面的指令中,用word ptr指明了指令訪問的內存單元是字型單元mov word ptr ds:[0],1inc word ptr [bx]inc word ptr ds:[0]add word ptr [bx],23.其他方法有些指令默認了訪問的內存單元類型pop、push指令,一定是字型數據3.在沒有寄存器參與的內存單元訪問指令中,用word ptr或者byte ptr顯性地指明所要訪問的內存單元的長度,是非常有必須要的否則,CPU無法得知所要訪問的單元是字單元,還是字節單元 8.6 尋址方式的綜合應用 8.7 div指令1.div是除法指令(division),使用div作除法的時候,要求1.除數:8位或16位,在寄存器或內存單元中2.被除數:(默認)放在AX或DX和AX中3.除數與被除數的相互關系除數 被除數8位 16位(AX)16位 32位(DX+AX)4.結果存放的位置運算 8位 16位商 AL AX余數 AH DX2.div指令格式1.div 寄存器2.div 內存單元除數是寄存器或內存單元的內容3.div指令示例1.div byte ptr ds:[0] ;被除數是16位,除數是ds:[0]的內容(8位)含義:(al)=(ax)/((ds)*16+0)的商(ah)=(ax)/((ds)*16+0)的余數2.div word ptr es:[0] ;被除數是32位,除數是es:[0]的內容(16位)含義:(ax)=[(dx)*10000H+(ax)]/((es)*16+0)的商(dx)=[(dx)*10000H+(ax)]/((es)*16+0)的余數 4.利用除法指令計算100001/1001.被除數100001大于65535,要使用dx和ax兩個寄存器聯合存放即說要進行的16位的除法2.除數100小于255,可以在一個8位寄存器中存放,但是,因為被除數是32位除數應為16位,所以要用16位寄存器來存放除法1003.現將100001表示成十六進制數:186A1H,即dx中存放1H,ax中存放86A1H mov dx,1 mov ax,86A1H mov bx,100 div bx ;默認除數是16位的 8.8 偽指令dd1.dd是用來定義雙字型數據的2.示例data segmentdb 1 ;字節型數據dw 1 ;字型數據dd 1 ;雙字型數據data ends3.已知data段數據,用div計算data中第一個數據除以第二個數據后的結果,商存放在第3個數據的內存單元中 assume cs:code,ds:data data segmentdd 100001dw 100dw 0 data endscode segment start:mov ax,datamov ds,axmov bx,0mov ax,[bx] ;低位存放在ax中mov dx,[bx+2] ;高位存放在dx中div word ptr [bx+4]mov [bx+6],ax ;商存放在ax中,把ax中的內容放入內存中mov ax,4c00hint 21h code ends end start 8.9 dup1.dup是一個操作符,在匯編語言中,同db、dw、dd等一樣,也是有編譯器識別處理的符號2.dup和db、dw、dd等數據定義偽指令配合使用的,用來進行數據的重復3.dup示例1.db 3 dup(0) ;定義了3個字節,他們的值都是02.db 3 dup(0,1,2) ;定義了9個字節,他們是0、1、2、0、1、2、0、1、23.db 3 dup('abc','ABC') ;定義了18個字節,相當于db'abcABCabcABCabcABC'4.dup的使用格式db 重復的次數 dup(重復的字節型數據)dw 重復的次數 dup(重復的字型數據)dd 重復的次數 dup(重復的雙字型數據) 【實驗七】 沒調試成功 assume cs:code,ds:data,ss:stack,es:tablestack segment;空棧時,sp指向16dw 8 dup(0) stack endsdata segment;表示21年的21個字符串;起始地址0,終止地址21*4-1:83db '1975','1976','1977','1978','1979','1980','1981','1982','1983'db '1984','1985','1986','1987','1988','1989','1990','1991','1992'db '1993','1994','1995';表示21年公司總收入的21個雙字型數據;起始地址21*4:84,終止地址21*4+21*4-1:167dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000;表示21年公司雇員人數的21個字型數據;起止地址21*8:168,終止地址21*8+21*2-1:209dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226dw 11542,14430,15257,17800 data endstable segmentdb 21 dup('year summ ne ?? ') table endscode segment start:mov ax,datamov ds,axmov ax,tablemov es,axmov ax,stackmov ss,axmov sp,16mov si,0mov di,0mov bx,0mov bp,0 mov cx,21 outer:push siadd si,simov ax,ds:[bp]mov es:[bx][di],axmov ax,ds:84[bp]mov es:[bx][di+5],axpop simov al,168[si]mov es:[bx][di+10],alinc siadd di,2push siadd si,simov ax,ds:[bp]mov es:[bx][di],axmov ax,ds:84[bp]mov es:[bx][di+5],axpop simov al,168[si]mov es:[bx][di+10],alinc siadd di,2add bx,16loop outermov ax,4c00hint 21h code ends end start

第九章 轉移指令的原理

8086CPU的轉移指令分為以下幾類:1.無條件跳轉指令(如:jmp)2.條件跳轉指令3.循環指令(如:loop)4.過程,就像C語言中的函數5.中斷 9.1 操作符offset操作符offset在匯編語言中由編譯器處理,它的功能是取標號的偏移地址如:s:mov ax,offset s 9.2 jmp指令1.無條件轉移,可以只修改ip,也可以同時修改cs和ip1.【jmp 段地址:偏移地址】 可以用來同時修改CS和IP指令中的段地址修改CS偏移地址修改IP這種用法編譯器不認識,只能做在debug中使用2.【jmp 某一合法的寄存器】 僅修改IP的內容比如:jmp ax 或者 jmp bx(類似于mov IP ax)2.jmp指令要給出兩種信息:1.轉移的目的地址2.轉移的距離(段間轉移、段內短轉移、段內近轉移) 9.3 依據位移進行轉移的jmp指令1.jmp short 標號【轉到標號處執行指令,段內短轉移】此格式實現的是:段內短轉移,它對ip的修改范圍為-128~1272.也就是說,它向前轉移時可以最多越過128個字節,負數使用補碼表示向后轉移可以最多越過127個字節3.CPU不需要目的地址就可以實現對ip的修改jmp指令的機器碼中不包含目的地址,但是可以實現跳轉實現的方式,是在原地址的基礎上進行一個偏移量,即位移4.還有一種和指令“jmp short 標號”功能類似的指令格式:jmp near ptr 標號,它實現的是段內近轉移 功能為:(ip)=(ip)+16位位移jmp short 標號是8位的位移,而jmp near ptr 標號是16位位移 9.4 轉移的目的地址在指令中的jmp指令前面講的jmp指令,其對應的機器碼中并沒有轉移的目的地址,而是相對于當前ip的轉移位移1.指令“jmp far ptr 標號”實現的是段間轉移,又稱為遠轉移,這時機器碼中應該明確給出【段地址】2.指令“jmp far ptr 標號”功能如下:(CS)=標號所在段的段地址(IP)=標號所在段中的偏移地址far ptr 指明了指令用標號的段地址和偏移地址修改cs和ip 9.5 轉移地址在寄存器中的jmp指令指令格式:jmp 16位寄存器功能:修改ip寄存器中的值,把16位寄存器中的值送入到ip寄存器中 9.6 轉移地址在內存中的jmp指令轉移地址在內存中的jmp指令有兩種格式:1.jmp word ptr 內存單元地址(段內轉移)功能:將內存中的那個字視為一個偏移地址,然后跳轉到那個偏移地址與【jmp 寄存器】功能相似內存單元地址可用尋址方式的任意格式給出2.jmp dword ptr 內存單元地址(段間轉移)(ip)=(內存單元地址) ;雙字中的低位字是給ip的(cs)=(內存單元地址+2) ;雙字中的高位字是給cs的跟【jmp 段地址:偏移地址】功能類似內存單元地址可用尋址方式的任意格式給出**補充:不能直接向內存單元中加入立即數要通過寄存器,把立即數加進去 9.7 jcxz指令1.有條件跳轉指令,所有的有條件跳轉指令都是短轉移對應的機器碼中包含轉移的位移,而不是目的地址。對ip的修改范圍都為:-128~127**另一個有條件跳轉指令【loop指令】2.指令格式:jcxz 標號如果(cx)=0,則跳轉到標號處執行3.jcxz 標號 指令的操作:1.當(cx)=0時,(ip)=(ip)+8位位移2.當(cx)!=0時,什么也不做(程序繼續向下執行) 9.8 loop指令1.循環指令,所有的循環指令都是短轉移,在對應的機器碼中包含轉移的位移2.指令格式:loop 標號3.指令的內部操作1.cx=cx-12.如果cx!=0,(ip)=(ip)+8位位移,跳轉3.(cx)=0,什么也不做,程序向下執行cx用來控制循環的次數 9.9 根據位移進行轉移的意義1.根據位移進行轉移,這樣設計,方便了程序段在內存中的浮動裝配可以實現代碼的復用2.如果在機器碼中直接給出【段地址:偏移地址】,這段程序在內存中換一個位置,則會運行不正確3.段內近轉移、段內短轉移都是根據位移進行轉移,一共有四種方式1.jmp short ptr 標號2.jmp near ptr 標號3.jcxz 標號4.loop 標號 9.10 編譯器對轉移位移超界的檢測注意,根據位移進行轉移的指令,他們的轉移范圍會受到限制如果在源程序中出現了轉移范圍超界的問題,在編譯的時候,編譯器將報錯 【實驗八、九】【這個實驗要重點看】

第十章 call和ret指令

call和ret指令都是轉移指令,它們都能修改ip,或同時修改cs和ip 10.1 ret和ref1.ret指令用棧中的數據,修改ip的內容,從而實現【近轉移】CPU執行ret指令時,進行下面兩步操作:1.(ip)=((ss)*16+(sp)) ;ip的值修改為棧頂的內容2.(sp)=(sp)+2 ;棧頂移動2.retf指令用棧中的數據,修改cs和ip的內容,從而實現【遠轉移】CPU執行retf指令時,進行下面四步操作1.(ip)=((ss)*16+(sp)) ;ip的內容修改為棧頂的內容2.(sp)=(sp)+2 ;棧頂移動3.(cs)=((ss)*16+(sp)) ;cs的內容修改為棧頂移動之后,棧頂的內容4.(sp)=(sp)+2 ;棧頂移動棧頂的兩個字,低位字修改為ip,高位字修改為cs3.可以看出,如果我們用匯編語法來解釋ret和retf指令,則1.CPU執行ret指令,相當于pop ip2.執行retf指令時,相當于pop ippop cs 10.2 call指令1.call指令經常跟ret指令配合使用,因此CPU執行call指令,進行兩步操作:1.將當前的ip或cs和ip壓入棧中2.轉移2.call指令不能實現短轉移,除此之外,call指令實現轉移的方法和jmp指令的原理相同【依據位移進行轉移的call指令】3.CPU執行“call 標號”這種格式的call指令時,進行如下操作:1.(sp)=(sp)-2 ;棧頂移動2.((ss)*16+(sp))=(ip) ;當前ip內容壓棧3.(ip)=(ip)+16位位移 ;跳轉到標號處4.call指令格式:call 標號相當于執行:push ipjmp near ptr 標號 10.4 轉移的目的地址在指令中的call指令1.指令格式:call far ptr 標號實現的是段間轉移2.執行這種格式的call指令時CPU的操作1.(sp)=(sp)-2 ;棧頂移動2.((ss)×16+(sp))=(cs) ;先把cs壓棧3.(sp)=(sp)-2 ;棧頂移動4.((ss)×16+(sp))=(ip) ;然后把ss壓棧3.CPU執行“call far ptr 標號”時,相當于進行push cspush ipjmp far ptr 標號 10.5 轉移地址在寄存器中的call指令1.指令格式:call 16位寄存器2.執行這種指令時,在CPU中的操作1.(sp)=(sp)-22.((ss)×16+(sp))=(ip)3.(ip)=(16位寄存器)3.相當于push ipjmp 16位寄存器 10.6 轉移地址在內存中的call指令轉移地址在內存中的call指令有兩種格式:1.call word ptr 內存單元地址匯編語法解釋push ipjmp word ptr 內存單元地址2.call dword ptr 內存單元地址匯編語法解釋push cs ;cs存放在高位push ip ;ip存放在低位jmp dword ptr 內存單元地址 10.7 call和ret的配合使用 10.8 mul指令相乘的兩個數;要么都是8位,要么都是16位1.8位:AL中和8位寄存器或內存字節單元中AL中的內容作為被乘數結果放在AX中2.16位:AX中和16位寄存器或內存字單元中AX中的內容作為被乘數結果放在DX(高位)和AX(低位)中。3.格式如下:mul 寄存器mul 內存單元(byte ptr或 word ptr指明是字還是字節) 10.9 模塊化程序設計 10.10 參數和結果傳遞的問題【編程】計算data段中第一組數據的3次方,結果保存在后面一組dword單元中 data sgementdw 1,2,3,4,5,6,7,8dd 0,0,0,0,0,0,0,0 data ends 10.11 批量數據的傳遞使用寄存器、內存、棧傳遞數據【編程】將一個全是字母,以0結尾的字符串,轉化為大寫 【實驗十 編寫子程序】1.顯示字符串2.解決除法溢出問題3.數值顯示 【課程設計1】

第十一章 標志寄存器

8086CPU的標志寄存器有16位,其中存儲的信息通常被稱為程序狀態字(PSW) 本章中的標志寄存器(以下簡稱為flag)是我們要學習的最有一個寄存器 flag寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息 8086CPU的flag寄存器的結構:1.flag的1、3、4、12、13、14、15位共7位在8086CPU中沒有使用,不具有任何含義而0、2、4、6、7、8、9、10、11位共9位都具有特殊的含義2.示意圖

11.1 ZF標志1.flag的第6位是ZF,零標志位。它記錄相關指令執行后,1.結果為0,ZF=12.結果不為0,ZF=02.示例:mov ax,1sub ax,1指令執行后,結果為0,則ZF=1mov ax,2sub ax,1指令執行后,結果不為0,則ZF=03.注意,在8086CPU的指令集中,有的指令的執行會影響標志寄存器比如:add、sub、mul、div、inc、or、and等他們大都是運算指令(邏輯運算或者算術運算)有的指令的執行對標志寄存器沒有影響,比如:mov、push、pop等,他們大都是傳送指令 11.2 PF標志flag的第2位是PF,奇偶標志位它記錄指令執行后,結果的所有二進制位中1的個數1.為偶數,PF=12.為奇數,PF=0 11.3 SF標志1.flag的第7位是SF,符號標志位2.它記錄指令執行后1.結果為負。sf=12.結果為正,sf=0sf標志,就是CPU對有符號數運算結果的一種記錄,它記錄數據的正負sf標志把所有數當作有符號數如果把數據當作無符號數運算,sf的值則沒有意義,雖然相關指令會影響它的值3.也就是說,CPU在執行add等指令時,是必然要影響sf標志位的值至于我們需不需要這種影響,那就看我們如何看待指令所進行的運算 11.4 CF標志 1.flag的第0位是CF,進位標志位一般請況下,在進行無符號數運算的時候,它記錄了運算結果的最高有效位向更高位的進位值,或從更高位的借位值代表假想的更高位2.CPU在運算時,不會丟棄進位值,而是記錄在一個特殊的寄存器的某一位上8086CPU就用flag的cf為來記錄這個進位值,借位也一樣3.在debug中的顯示

4.無符號的時候產生的結果 11.5 OF標志flag中的第11位進行有符號數運算的時候,如果結果超過了機器所能表示的范圍稱為溢出1.這里所講的溢出,只是對有符號數運算而言就像進位只是相對于無符號數而言!2.一定要注意cf和of的區別當需要把機器碼看成有符號數則使用of當需要把機器碼看成無符號數則使用cf 11.6 adc標志adc是帶進位的加法指令,他利用了cf上記錄的進位值1.格式:adc 操作對象1,操作對象22.功能:操作對象1=操作對象1+操作對象2+cf比如:adc ax,bx實現的功能是:(ax)=(ax)+(bx)+cf3.執行adc指令的時候,加上的cf的值的含義,由adc指令前的指令決定也就是說,關鍵在于所加上的cf值是被什么指令設置的4.如果cf是被sub指令設置的,那么他的含義就是借位值如果是被add指令設置的,那么它的含義就是進位值5.下面的指令和add ax,bx具有相同的結果add al,bladc ah,bhCPU提供adc指令的目的,就是來進行加法的第二步運算的adc指令和add指令相配合就可以對更大的數據進行加法運算【實驗:編程計算1EF000H+201000H,結果放在ax(高16位)和bx(低16位)中】 11.7 sbb標志sbb是帶借位減法指令,他利用了cf位上記錄的借位值1.格式:sbb 操作對象1,操作對象22.功能:操作對象1=操作對象1-操作對象2-cf3.利用sbb指令,我們可以對任意大的數據進行減法運算4.sbb和adc是基于相同的思想設計的兩條指令,在應用思路上和adc類似 11.8 cmp標志1.cmp是比較指令,功能相當于減法指令,只是不保存結果2.cmp指令執行后,將對標志寄存器產生影響3.其他相關指令通過識別這些被影響的標志寄存器,來得知比較結果4.cmp指令格式:cmp 操作對象1,操作對象25.功能:計算操作對象1-操作對象2,但并不保存結果,僅僅根據計算結果對標志寄存器進行設置6.比如:cmp ax,ax做(ax)-(ax)的運算,結果為0,但并不在ax中保存,僅影響flag的相關位指令執行后zf=1 ;結果為0pf=1 ;結果的1的個數為偶數sf=0 ;結果為正號cf=0 ;結果沒有產生進位或借位of=0 ;結果沒有溢出7.根據flag,判斷cmp指令的結果(無符號數)

8.cmp既可以對無符號數進行比較,也可以對有符號數進行比較cmp 操作數1,操作數2 ;操作數1、操作數2都是有符號數1.of=0,說明沒有溢出,邏輯上真正結果的正負=實際結果的正負of=0,sf=1 則 操作數1比操作數2小of=0,sf=0 則 操作數1比操作數2大2.of=1,說明有溢出,邏輯上真正結果的正負與實際結果的正負相反of=1,sf=1 則 操作數1比操作數2大of=1,sf=0 則 操作數1比操作數2小 11.9 檢測比較結果的條件轉移指令1.這些條件轉移指令通常和cmp相配合使用2.因為cmp指令可以同時進行兩種比較,無符號數和有符號數的比較所以,這些轉移指令也分為兩種,即:1.根據【無符號數】的比較結果進行轉移的條件轉移指令,他們檢測zf、cf的值2.根據【有符號數】的比較結果進行轉移的條件轉移指令他們檢測sf、of和zf的值3.無符號比較,條件轉移指令小結【無符號,6個】1.je 等于則轉移 zf=12.jne 不等于則轉移 zf=03.jb 低于則轉移 cf=1 【b表示below】4.jnb 不低于則轉移 cf=05.ja 高于則轉移 cf=0,zf=0【a表示above】6.jna 不高于則轉移 cf=1或zf=1 11.10 DF標志和串傳送指令1.flag的第10位DF,方向標志位在串處理指令(movsb,movsw)中,控制每次操作后si、di的增減df=0:每次操作后si,di遞增df=1:每次操作后si,di遞減2.格式:movsb3.功能:(以字節為單位傳送)1.((es)*16+(di))=((ds)*16+(si))2.如果df=0,則:(si)=(si)+1(di)=(di)+1如果df=1,則:(si)=(si)-1(di)=(di)-13.功能文字描述movsb的功能是將ds:si指向的內存單元中的字節送入es:di中,然后根據標志寄存器df位的值,將si和di遞增或遞減4.movsw 傳送一個字5.movsb和movsw都和rep配合使用格式:rep movsbrep的作用根據cx的值,重復執行后面的串傳送指令6.cld指令和std指令cld指令:將標志寄存器的df置為0【c:clear】std指令:將標志寄存器的df置為1【s:set】 11.11 pushf和popfpushf:將標志寄存器的值壓棧popf:從棧中彈出數據,送入標志寄存器中pushf和popf為直接訪問標志寄存器提供了一種方法 11.12 標志寄存器在debug中的表示

第十二章 內中斷

**引言和簡介1.中斷是CPU處理外部突發事件的一個重要技術2.它能使CPU在運行過程中對外部事件發出的中斷請求及時地進行處理,處理完成后又立即返回斷點,繼續進行CPU原來的工作。3.引起中斷的原因【即:發出中斷請求的來源叫作中斷源】4.根據中斷源的不同,可以把中斷分為:【軟件中斷】和【硬件中斷】兩大類而硬件中斷又可以分為【外部中斷】和【內部中斷】兩類 12.1 內中斷的產生1.外部中斷一般是指計算機外設發出的中斷請求,如:鍵盤中斷、打印機中斷、定時器中斷。外部中斷是可以屏蔽的中斷,也就是說,利用中斷控制器可以屏蔽這些外部設備的中斷請求。2.內部中斷是指因硬件出錯(如突然掉電、奇偶校驗錯等)或運算出錯(除數為零、運算溢出、單步中斷)所引起的中斷。內部中斷是不可屏蔽的中斷3.軟件中斷其實并不是真正的中斷,他們只是可被調用執行的一般程序,DOS的系統功能調用(int 21h)都是軟件中斷4.CPU為了處理并發的中斷請求,規定了中斷的優先權,優先權由高到低的順序是:1.除法錯、溢出中斷、軟件中斷2.不可屏蔽中斷3.可屏蔽中斷4.單步中斷 12.2 中斷處理程序簡介1.CPU的設計者必須在中斷信息和其處理程序的入口地址之間建立某種聯系使得CPU根據中斷信息可以找到要執行的處理程序。2.中斷信息中包含有表示中斷的類型碼。根據CPU的設計,中斷類型碼的作用就是用來定位中斷處理程序的。3.CPU用8位的中斷類型碼通過中斷向量表找到相應的中斷處理程序的入口地址即中斷類型碼是中斷向量在中斷向量表中的索引 12.3 中斷向量表【中斷向量表就是中斷向量的列表】1.中斷向量表在內存中保存,其中存放著256個【2^8,8位中斷類型碼】中斷源所對應的中斷處理程序的入口對于8086PC機,中斷向量表指定放在內存地址0處2.從0:0-0:03ffh的1024個字節【256*4,物理地址使用段地址和偏移地址存放,需要4個字節】中存放著中斷向量表 12.4 中斷過程1.可以用中斷類型碼,在中斷向量表中找到中斷處理程序的入口找到這個入口地址的最終目的是用它設置cs和ip,使CPU執行中斷處理程序2.用中斷類型碼找到中斷向量,并用它設置cs和ip,這個工作時由CPU的硬件自動完成的CPU硬件完成這個工作的過程被稱為【中斷過程】3.中斷過程8086CPU的中斷過程1.(從中斷信息中)取得中斷類型碼2.標志寄存器的值入棧(保護標志位)3.設置標志寄存器的第8位TF和第9位IF設置為0(后面講解本步的目的)4.cs內容入棧5.ip內容入棧6.從內存地址為中斷類型碼*4和中斷類型碼*4+2的兩個子單元中讀取中斷處理程序的入口地址設置cs和ip4.使用匯編語言描述中斷過程,如下1.取得中斷類型碼N2.pushf3.TF=0,IF=04.push cs5.push ip6.(ip)=(N*4),(cs)=(N*4+2) 12.5 中斷處理程序1.由于CPU隨時都可能檢測到中斷信息,也就是說,CPU隨時都可能執行中斷處理程序,所以,中斷處理程序必須一致存儲在內存某段空間中2.而中斷處理程序的入口地址,即【中斷向量】,必須存儲在對應的中斷向量表表項中3.中斷處理程序的編寫方法和子程序的比較類似,下面是常規的步驟1.保存用到的寄存器2.處理中斷3.恢復用到的寄存器4.用iret指令返回**iret指令的功能用匯編語法描述為pop ippop cspopfiret通常和硬件自動完成的中斷過程配合使用iret指令執行后,CPU回到執行中斷處理程序前的執行點繼續執行程序 12.6 除法錯誤中斷的處理當CPU執行div等除法指令的時候,如果發生了除法溢出錯誤,將產生中斷類型碼為0的終端信息CPU將檢測到這個信息,然后引發中斷程序,轉去執行0號中斷對應的中斷處理程序例如:mov ax 1000hmov bh,1div bh此程序會產生溢出運行之后,會顯示

12.7 編程處理0號中斷現在重新編寫一個0號中斷處理程序,它的功能是在屏幕中間顯示“Welcome to here!”的廣告詞,然后返回到操作系統把中斷處理程序放到安全空間中中斷程序的框架

12.8 安裝計算中斷程序的長度:offset 標號1-offset 標號2在代碼段中存放數據 12.9 do0 12.10 設置中斷向量 12.11 單步中斷如果檢測到標志寄存器的tf位為1,則產生單步中斷,引發中斷過程 12.12 響應中斷的特殊情況


第十三章 int指令

13.1 int指令1.int格式:int n ;n為中斷類型碼它的功能是引發中斷過程2.CPU執行int n指令,相當于引發一個n號中斷的中斷過程,執行過程如下1.取中斷類型碼2.標志寄存器入棧,if=0,tf=03.cs,ip入棧4.從此處轉去執行n號中斷的中斷處理過程3.可以在程序中使用int指令調用任何一個中斷的中斷處理程序可以用int指令調用這些子程序,也可以自己編寫一些中斷處理程序供別人使用 13.2 編寫供應用程序調用的中斷例程【實例1】編寫、安裝中斷7ch的中斷例程,實現求一個word型數據的平方1.功能:求一word型數據的平方2.參數:(ax)=要計算的數據3.返回值:dx、ax中存放結果的高16位和低16位4.應用舉例:求2*3456^2 ;程序1:調用中斷程序計算平方 code segmentassume cs: code start:mov ax,3456; (ax)=3456int 7ch;調用中斷7ch的中斷例程,計算ax中的數據的平方add ax,axadc dx,dx ;存放結果,講結果乘以2mov ax,4c00hint 21h code ends end start;程序2:編寫中斷程序 ;程序2中要做三部分工作 ; 1.編程實現求平方功能的程序 ; 2.安裝程序,我們將其安裝在0:200處 ; 3.設置中斷向量表,將程序的入口地址保存在7ch表項中,使其成為中斷7ch的中斷例程。 code segmentassume cs:code start:mov ax,csmov ds,axmov si,offset sqr ;設置ds:si指向源地址mov ax,0mov es,axmov di,200h ;設置es:di指向目的地址mov cx,offset sqrend - offset sqr ;設置cx為傳輸長度cld ;設置傳輸方向為正rep movsbmov ax,0mov es,axmov word ptr es:[7ch*4],200h ;設置中斷向量地址,偏移地址mov word ptr es:[7ch*4+2],0 ;設置中斷向量地址,段地址mov ax,4c00hint 21hsqr: mul axiret sqrend: nopcode ends end start 【實例2】編寫、安裝中斷7ch的中斷例程,實現將一個全是字母,以0結尾的字符串,轉化為大寫。 code segmentassume cs:code start:mov ax,csmov ds,axmov si,offset capitalmov ax,0mov es,axmov di,200hmov cx,offset capitalend - offset capitalcldrep movsbmov ax,0mov es,axmov word ptr es:[7ch*4],200hmov word ptr es:[7ch*4+2],0mov ax,4c00hint 21hcapital:push cxpush sichange: mov cl,[si]mov ch,0jcxz okand byte ptr [si],11011111binc sijmp short change ok: pop sipop cxiretcapitalend:nopcode ends end start 13.3 對int、iret和棧的深入理解【問題】用7ch中斷例程完成loop指令的功能不要隨便修改sp,可以使用bp進行間接訪問 13.4 BIOS和DOS所提供的中斷例程 13.5 BIOS和DOS中斷例程的安裝過程1.開機后,CPU一加電,初始化(cs)=0ffffh,ip=0,自動從ffff:0單元開始執行程序ffff:0處有一條跳轉指令,CPU執行該指令后,轉去執行bios中的硬件系統的檢測和初始化程序。2.初始化程序將建立bios所支持的中斷向量,即將bios提供的中斷例程的入口地址登記在中斷向量表中。3.硬件系統檢測和初始化完成后,調用19h進行操作系統的引導。從此將計算機交由操作系統控制。4.DOS啟動后,除完成其他工作外,還將它所提供的中斷例程裝入內存,并建立相應的中斷向量 13.6 BIOS中斷例程的應用1.int 10h中斷例程是bios提供的中斷例程,其中包含了多個和屏幕輸出相關的子程序一般來說,一個供程序員調用的中斷例程中,往往包括多個子程序,中斷例程內部用傳遞進來的參數來決定執行哪個子程序2.bios和dos提供的中斷例程,都用ah來傳遞內部子程序的編號 13.7 DOS中斷例程應用int 21h中斷例程是dos提供的中斷例程,其中包含了dos提供給程序員造編程時調用的子程序【實驗13】 **介紹一本匯編語言的書《The Art of Assembly Language》

第十四章 端口

CPU可以直接讀寫3個地方的數據1.CPU內部的寄存器2.內存單元3.端口 14.1 端口的讀寫1.對端口的讀寫不能用mov、push、pop等內存讀寫指令端口的讀寫指令只有兩條:【in】和【out】分別用于從端口讀取數據和往端口寫入數據2.CPU執行內存訪問指令和端口訪問指令時,總線上的信息:1.訪問內存mov ax,ds:[8];假設執行前(ds)=0執行時,與總線相關的操作:1.CPU通過地址線將地址信息8發出2.CPU通過控制線發出內存讀命令,選中存儲器芯片,并通知它,將要從中讀取數據3.存儲器將8號單元中的數據通過數據線送入CPU2.訪問端口這里的【端口】是對硬件開放的端口in al,60h; 從60h號端口讀入一個字節執行時與總線相關的操作1.CPU通過地址線將地址信息60h發出2.CPU通過控制線發出端口讀命令,選中端口所在的芯片,并通知它,將要從中讀取數據3.端口所在的芯片將60h端口中的數據通過數據線送入CPU**注意:在in和out指令中,只能使用ax或al來存放從端口中讀入的數據或要發送到端口中的數據訪問8位端口時用al,訪問16位端口時用ax3.對0-255以內的端口進行讀寫in al,20h ;從20h端口讀一個字節out 20h,al ;往20h端口寫一個字節4.對256-65535的端口進行讀寫時,端口號放在【dx】中mov dx,3f8h ;將端口號3f8送入dxin al,dx ;從3f8h端口讀一個字節out dx,al ;從3f8h端口寫一個字節 14.2 CMOS RAM芯片1.PC機中有一個CMOS RAM芯片,其有如下特征1.包含一個實時鐘和一個有128個存儲單元的RAM存儲器。(早期的計算機為64字節)2.該芯片靠電池供電。因此,關機后其內部的實時鐘仍可以正常工作,RAM中的信息不丟失3.128字節的RAM中,內部實時鐘占用0-0dh單元來保存時間信息,其余大部分分單元用于保存系統配置信息,供系統啟動時bios程序讀取bios也提供了相關的程序,使我們可以在開機的時候配置CMOS RAM中的系統信息**補充:BIOSBIOS是英文"Basic Input Output System"的縮略詞,直譯過來后中文名稱就是"基本輸入輸出系統"。在IBM PC兼容系統上,是一種業界標準的固件接口。BIOS這個字眼是在1975年第一次由CP/M操作系統中出現。BIOS是個人電腦啟動時加載的第一個軟件4.該芯片內部有兩個端口,端口地址為70h和71h。CPU通過這兩個端口讀寫CMOS RAM。5.70h為地址端口,存放要訪問的CMOS RAM單元的地址;71h為數據端口,存放從選定的CMOS RAM單元中讀取的數據或要寫入到其中的數據2.比如:讀CMOS RAM的2號單元:1.將2送入端口70h2.從71h讀取2號單元的內容 14.3 shl和shr指令shl和shr是邏輯移位指令,后面的課程中我們要用到移位指令1.shl邏輯左移指令,功能為:1.將一個寄存器或內存單元中的數據向左移位2.將最后移出的移位寫入cf中3.最低位用0補充例如有如下指令:mov al,01001000bshl al,1 ;將al中的數據左移一位執行后(al)=100100000b,cf=0.如果移動位數大于1時,必須將移動位數放在cl中2.shr邏輯右移指令,與shl剛好相反 14.4 CMOS RAM中存儲的時間信息在CMOS RAM中存放著當前時間秒:00h分:02h時:04h日:07h月:08h年:09h這6個信息的長度都為1個字節這些數據以BCD碼的方式存放,一個字節可以表示兩個BCD碼CMOS RAM存儲時間信息的單元中存儲了用兩個BCD碼表示的兩個十進制數高4位的BCD碼表示十位,低四位的BCD碼表示個位【編程】:在屏幕中間顯示當前的月份1.CMOS RAM芯片回顧:1.70h為地址端口,存放要訪問的CMOS RAM單元的地址2.71h為數據端口,存放從選定的CMOS RAM單元中【讀取】的數據,或【寫入】其中的數據2.分析這個程序主要做兩部分工作1.從CMOS RAM的8號單元讀取當前月份的BCD碼要讀取CMOS RAM的信息,我們首先要向地址端口70h寫入要訪問的單元的地址mov al,8out 70h,al然后從數據端口71h中取得指定單元中的數據in al,71h2.將用BCD碼表示的月份以十進制的形式顯示到屏幕上 ;編程:在屏幕中間顯示當前的月份 code segmentassume cs:code start:mov al,8out 70h,alin al,71hmov ah,almov cl,4shr ah,cland al,00001111badd ah,30hadd al,30hmov bx,0b800h ;顯存mov es,bxmov byte ptr es:[160*12+40*2],ah ;顯示月份的十位數碼mov byte ptr es:[160*12+40*2+2],al ;顯示月份的個位數碼mov ax,4c00hint 21h code ends end start 【實驗十四】編程:以“年/月/日 時:分:秒”的格式,顯示當前日期和時間

第十五章 外中斷

**CPU除了有運算能力,還有I/O能力 15.1 接口芯片和端口1.在PC系統的接口卡和主板上,裝有各種接口芯片,這些外設接口芯片的內部裝有若干寄存器CPU將這些寄存器當做【端口】訪問2.外設的輸入不直接送入內存和CPU,而是送入相關的接口芯片的【端口】中3.CPU向外設的輸出也是要先送入【端口】中,再由相關芯片送入到外設4.CPU可以向外設輸出控制命令,這些控制命令也是先送到【端口】中,然后相關芯片根據命令進行相關工作5.可見:CPU與外部設備的交流是通過【端口】進行的CPU在執行完當前指令后,可以檢測到發送過來的中斷信息,引發中斷過程,處理外設的輸入 15.2 外中斷信息1.在PC系統中,外中斷源一共有兩類1.可屏蔽中斷2.不可屏蔽中斷2.可屏蔽中斷是CPU可以不響應的外中斷。CPU是否響應可屏蔽中斷要看標志寄存器的IF位的設置當CPU檢測到可屏蔽中斷信息時:1.若IF=1,則CPU在執行完當前指令后相應中斷,引發中斷過程2.若IF=0,則不響應可屏蔽中斷3.可屏蔽中斷所引發的中斷過程,除在第一步的實現上與內中斷有所不同外,基本上和內中斷的中斷過程相同4.因為可屏蔽中斷信息來自于CPU外部,中斷類型碼是通過數據總線送入CPU的而內中斷的中斷碼是在CPU內部產生的5.IF設置為0的原因:在進入中斷處理程序后,禁止其他的可屏蔽中斷當然,如果中斷處理程序中需要處理可屏蔽中斷,可以用指令將IF設置為16.8086CPU提供的設置IF的指令如下:sti ;用于設置IF=1cli ;用于設置IF=07.不可屏蔽中斷是CPU必須相應的外中斷。當CPU檢測到不可屏蔽中斷信息時,則在執行完當前指令后立即響應,應發中斷過程8.8086CPU不可屏蔽中斷的中斷類型碼固定為2,所以中斷過程中,不需要取中斷類型碼9.不可屏蔽中斷的中斷過程1.標志寄存器入棧,IF=0,TF=02.CS,IP入棧3.(IP)=(8),(CS)=(0AH) ;固定地址10.幾乎所有外中斷,都是可屏蔽中斷。當外設有需要處理的事件發生時相關芯片向CPU發出可屏蔽中斷信息。不可屏蔽中斷是系統中有必須處理的緊急情況發生時用來通知CPU的中斷信息,本門課程中,主要討論可屏蔽中斷 15.3 PC機鍵盤的處理過程1.下面看一個鍵盤輸入的處理過程,并以此來體會PC機處理外設輸入的基本方法1.鍵盤輸入2.引發9號中斷3.執行int 9中斷例程2.PC機鍵盤的處理過程1.鍵盤上每一個鍵相當于一個開關,鍵盤中有一個芯片對鍵盤上的每一觸鍵的開關狀態進行掃描。2.按下一個鍵時,開關接通,該芯片就產生一個掃描碼,掃描碼說明按下的鍵在鍵盤上的位置掃描碼被送入主板上的相關接口芯片的寄存器中,該寄存器的端口地址為60H3.松開控下的鍵時,也產生一個掃描碼,掃描碼說明了松開的鍵在鍵盤上的位置,松開按鍵時產生的掃描碼也被送入60H端口中。一般按下一個鍵時,產生的掃描碼稱為通碼,松開一個鍵產生的掃描碼稱為斷碼掃描碼長度為一個字節,通碼的第七位為0,斷碼的第七位為1即:斷碼=通碼+80H**BIOS提供了int9中斷例程,用來進行基本的鍵盤輸入處理,主要的工作如下:1.讀出60H端口中的掃描碼2.如果是字符鍵的掃描碼,將該掃描碼對應的字符碼(即:ASCII碼)送入內存中的BIOS鍵盤緩沖區3,如果是控制鍵和切換鍵的掃描碼,則將其轉變為狀態字節,寫入內存中存儲狀態字節的單元4.鍵盤的輸入到達60H端口時,相關的芯片會向CPU發出中斷類型碼為9的可屏蔽中斷信息。5.CPU檢測到中斷信息后,如果IF=1,則相應中斷,同時將IF設置為0(不讓其他可屏蔽中斷進行干擾),引發中斷過程,轉去執行int9中斷例程3.BIOS鍵盤緩沖區是系統啟動后,BIOS用于存放int9中斷例程所接受的鍵盤輸入的內存區4.該內存區可以存儲15個鍵盤輸入,int9中斷例程除了接收掃描碼外,還要產生和掃描碼對應的字符碼,所以在BIOS鍵盤緩沖區中,一個鍵盤輸入用一個字單元存放,高字節存放掃描碼,低字節存放字符碼5.0040:17單元存儲鍵盤狀態字節,該字節記錄了控制鍵和切換鍵的狀態。鍵盤狀態字節各位記錄的信息如下:

15.4 編寫int9中斷例程,并安裝梳理鍵盤輸入的處理過程1.鍵盤產生掃描碼2.掃描碼送入60H端口3.一旦偵測到60H端口有動靜,引發9號中斷4.CPU執行int9中斷例程處理輸入以上的過程,前三步都由硬件系統自動完成,能夠修改的只有第四步,修改int9中斷程序【任務演示】在屏幕中依次顯示“a”~“z”并可以讓人看清。在顯示過程中,按下Esc鍵后,該表顯示的顏色 ;程序1:實現連續顯示“a”~“z” ;編程:在屏幕中間依次顯示“a”~“z”,并可以讓人看清。在顯示的過程中,按下'Esc'鍵后,改變顯示的顏色。 ;部分功能代碼: stack segmentdb 128 dup (0) stack endscode segmentassume cs:code start: mov ax,stackmov ss,axmov sp,128mov ax,0b800hmov es,axmov ah,'a' s: mov es:[160*12+40*2],ahcall delayinc ahcmp ah,'z'jna smov ax,4c00hint 21hdelay: push axpush dxmov dx,0010h ;循環10000000h次mov ax,0 s1: sub ax,1sbb dx,0cmp ax,0jne s1cmp dx,0jne s1pop dxpop axretcode ends end start ;程序2:實現改變顏色 ;編程:在屏幕中間依次顯示“a”~“z”,并可以讓人看清。在顯示的過程中,按下'Esc'鍵后,改變顯示的顏色。 stack segmentdb 128 dup (0) stack endsdata segmentdw 0,0 data endscode segmentassume cs:code start: mov ax,stackmov ss,axmov sp,128mov ax,datamov ds,axmov ax,0mov es,axpush es:[9*4]pop ds:[0]push es:[9*4+2]pop ds:[2] ;將原來的int 9中斷例程的入口地址保存在ds:0、ds:2單元中mov word ptr es:[9*4],offset int9mov es:[9*4+2],cs ;在中斷向量表中設置新的int 9中斷例程的入口地址mov ax,0b800hmov es,axmov ah,'a' s: mov es:[160*12+40*2],ahcall delayinc ahcmp ah,'z'jna smov ax,0mov es,axpush ds:[0]pop es:[9*4]push ds;[2]pop es;[9*4+2] ;將中斷向量表中int 9中斷例程的入口恢復為原來的地址mov ax,4c00hint 21hdelay: push axpush dxmov dx,0010hmov ax,0 s1: sub ax,1sbb dx,0cmp ax,0jne s1cmp dx,0jne s1pop dxpop axret;------以下為新的int 9中斷例程-------------------- ;int9中斷例程是在進行鍵盤輸入之后,由系統自動調用 int9: push axpush bxpush esin al,60hpushfpushfpop bxand bh,11111100bpush bxpopfcall dword ptr ds:[0] ;對int指令進行模擬,調用原來的int 9中斷例程cmp al,1jne int9retmov ax,0b800hmov es,axinc byte ptr es:[160*12+40*2+1] ;屬性增加1,改變顏色int9ret:pop espop bxpop axiretcode ends end start

第十六章 直接定址表

16.1 描述了單元長度的標號1.本章討論如何有效合理地組織數據,以及相關的編程技術1.前面的課程中,我們一直在代碼段中使用標號來標記指令、數據、段的起始地址2.還可以使用一種標號,這種標號不但可以表示內存單元的地址,還表示了內存單元的長度即:表示在此標號處的單元,是一個字節單元,還是字單元還是雙字單元2.例如1.標號1a : db 1,2,3,4,5,6,7,8b : dw 0此種標號只能標記地址此種加有“:”的地址標號,只能在代碼段中使用,不能在其他段中使用2.標號2a db 1,2,3,4,5,6,7,8 ;標號a,描述了地址code:0,和從這個地址開始,以后的內存單元都是字節單元b dw 0 ;標號b描述了地址code:8,和從這個地址開始,以后的內存單元都是字單元此種標號既可以標記地址,也可以表示此標號處的單元3.使用這種包含單元長度的標號,可以使我們以簡潔的形式訪問內存中的數據這種標號此后稱為數據標號,它標記了存儲數據的單元的地址和長度4.數據標號的用法指令:mov ax,b ;相當于:mov ax,cs:[8]指令:mov b,2 ;相當于:mov word ptr cs:[8],2指令:inc b ;相當于:inc word ptr cs:[8]指令:mov al,a [si] ;相當于:mov al,cs:0[si]指令:mov al,a[3] ;相當于:mov al,cs:0[3]指令:mov al,a[bx+si+3] ;相當于:mov al,cs:0[bx+si+3] 16.2 在其他段中使用數據標號1.注意,如果想在代碼段中,直接用數據標號訪問數據,則需要用偽指令assume 將標號所在的段和一個段寄存器聯系起來。否則編譯器在編譯的時候,無法確定標號的段地址在哪一個寄存器中。2. 當然,這種聯系是編譯器需要的,但絕對不是說,我們因為編譯器的工作需要,用assume指令將段寄存器和某個段相聯系,段寄存器中就會真的存放該段的地址。3.我們可以將數據標號當作數據來定義,此時,編譯器將標號所表示的地址當作數據的值。 1.把數據標號當做數據來定義時,使用【dw】定義數據比如: data segmenta db 1,2,3,4,5,6,7,8b dw 0c dw a,b ;數據標號c處存儲的兩個字型數據為標號a、b 的偏移地址。data ends數據標號c處存儲的兩個字型數據為標號a、b 的偏移地址。相當于:data segmenta db 1,2,3,4,5,6,7,8b dw 0c dw offset a, offset bdata ends2.把數據標號當做數據來定義時,使用【dd】定義數據再比如:data segmenta db 1,2,3,4,5,6,7,8b dw 0c dd a,b ;數據標號c處存儲的兩個雙字型數據為標號a的偏移地址和段地址、標號b 的偏移地址和段地址。data ends數據標號c處存儲的兩個雙字型數據為標號a的偏移地址和段地址、標號b 的偏移地址和段地址。相當于:data segmenta db 1,2,3,4,5,6,7,8b dw 0c dw offset a, seg a, offset b, seg b ;seg操作符,功能為取得某一標號的段地址。data endsseg操作符,功能為取得某一標號的段地址。 16.3 直接定址表本節課,我們將使用“查表”的方法,編寫相關程序的技巧【任務】編寫子程序,以十六進制的形式在屏幕中間顯示給定的byte型數據 code segmentassume cs:code start: mov al,0eh ;al中存放了byte型數據call showbytemov ax,4c00hint 21h;子程序: ;用al傳送要顯示的數據showbyte:jmp short showtable db '0123456789ABCDEF' ;字符表show: push bx ;保護現場push esmov ah,alshr ah,1 shr ah,1shr ah,1shr ah,1 ;右移4位,ah中得到高4位的值and al,00001111b ;al中為低4位的值mov bl,ahmov bh,0mov ah,table[bx] ;用高4位的值作為相對于table的偏移,取得對應的字符mov bx,0b800hmov es,bxmov es:[160*12+40*2],ahmov bl,almov bh,0mov al,table[bx] ;用低4位的值作為相對于table的偏移,取得對應的字符mov es:[160*12+40*2+2],alpop espop bxretcode ends end start 16.4 程序入口地址的直接定址表【編程】實現一個子程序setscreen,為顯示輸出提供如下功能:1.清屏2.設置前景色3.設置背景色4.向上滾動一行1.入口參數說明:1.用ah寄存器傳遞功能號0:清屏;1:設置前景色;2:設置背景色;3:向上滾動一行2.對于2、3號功能,用al傳遞顏色值al∈{0,1,2,3,4,5,6,7}2.各種功能如何實現1.清屏:將顯存中當前屏幕中的字符設為空格符;2.設置前景色:設置顯存中當前屏幕中處于奇地址的屬性字節的第0、1、2位;012位存放前景色3.設置背景色:設置顯存中當前屏幕中處于奇地址的屬性字節的第4、5、6位;456位存放背景色4.向上滾動一行:依次將第 n+1行的內容復制到第n行處:最后一行為空。 ;功能子程序1:清屏 sub1: push bx ;保護現場,調用子程序的時候,注意要保護現場,運行子程序的時候,可能會修改一些寄存器的值push cxpush esmov bx,0b800hmov es,bxmov bx,0mov cx,2000 sub1s: mov byte ptr es:[bx],' ' ;循壞2000次add bx,2loop sub1spop es ;恢復現場pop cxpop bxret ;功能子程序2:設置前景 sub2: push bxpush cxpush esmov bx,0b800hmov es,bxmov bx,1mov cx,2000 sub2s: and byte ptr es:[bx],11111000b or es:[bx],al add bx,2loop sub2spop espop cxpop bxret ;功能子程序3:設置背景色 sub3: push bxpush cxpush esmov cl,4shl al,clmov bx,0b800hmov es,bxmov bx,1mov cx,2000 sub3s: and byte ptr es:[bx],10001111bor es:[bx],al add bx,2loop sub2spop espop cxpop bxret ;功能子程序4:向上滾動一行 sub4: push cxpush sipush dipush espush dsmov si,0b800hmov es,simov ds,simov si,160 ;ds:si指向第n+1行,第1行mov di,0 ;es:di指向第n行,第0行cldmov cx,24;共復制24行sub4s: push cxmov cx,160rep movsb ;復制pop cxloop sub4smov cx,80 mov si,0sub4s1: mov byte ptr es:[160*24+si],' ' ;最后一行清空add si,2loop sub4s1pop dspop espop dipop sipop cxret ;sub4 ends 3.可以將這些功能子程序的入口地址存儲在一個表中,他們在表中的位置和功能號相對應 ;編程:實現一個子程序setscreen,為顯示輸出提供如下功能: ;(1) 清屏。 ;(2) 設置前景色。 ;(3) 設置背景色。 ;(4) 向上滾動一行。 ; ;入口參數說明: ;(1) 用 ah 寄存器傳遞功能號:0 表示清屏,1表示設置前景色,2 表示設置背景色,3 表示向上滾動一行; ;(2) 對于2、3號功能,用 al 傳送顏色值,(al) ∈{0,1,2,3,4,5,6,7}setscreen: jmp short settable dw sub1,sub2,sub3,sub4set: push bx cmp ah,3 ;判斷傳遞的是否大于 3ja sretmov bl,ahmov bh,0add bx,bx ;根據ah中的功能號計算對應子程序的地址在table表中的偏移call word ptr table[bx] ;調用對應的功能子程序,學會本句代碼,是本章節的【精髓】sret: pop bx iret;功能子程序1:清屏 sub1: push bxpush cxpush esmov bx,0b800hmov es,bxmov bx,0mov cx,2000sub1s: mov byte ptr es:[bx],' 'add bx,2loop sub1spop espop cxpop bxret ;sub1 ends;功能子程序2:設置前景色 sub2: push bxpush cxpush esmov bx,0b800hmov es,bxmov bx,1mov cx,2000sub2s: and byte ptr es:[bx],11111000b or es:[bx],al add bx,2loop sub2spop espop cxpop bxret ;sub2 ends;功能子程序3:設置背景色 sub3: push bxpush cxpush esmov cl,4shl al,clmov bx,0b800hmov es,bxmov bx,1mov cx,2000sub3s: and byte ptr es:[bx],10001111bor es:[bx],al add bx,2loop sub2spop espop cxpop bxret ; sub3 ends;功能子程序4:向上滾動一行 sub4: push cxpush sipush dipush espush dsmov si,0b800hmov es,simov ds,simov si,160 ;ds:si指向第n+1行mov di,0 ;es:di指向第n行cldmov cx,24;共復制24行sub4s: push cxmov cx,160rep movsb ;復制pop cxloop sub4smov cx,80 mov si,0sub4s1: mov byte ptr es:[160*24+si],' ' ;最后一行清空add si,2loop sub4s1pop dspop espop dipop sipop cxret ;sub4 ends

第十七章 使用BIOS進入鍵盤輸入和磁盤讀寫

**引言1.大多數有用的程序都需要處理用戶的輸入,鍵盤輸入是最基本的輸入。2.程序和數據通常需要長期存儲,磁盤是最常用的存儲設備。3.BIOS 為這兩種外設的I/O提供了最基本的中斷例程,在本章中,我們對它們的應用和相關的問題進行討論。 17.1 int9中斷例程對鍵盤輸入的處理CPU 在9 號中斷發生后,執行int 9中斷例程,從60h 端口讀出掃描碼,并將其轉化為相應的ASCII 碼或狀態信息,存儲在內存的指定空間(鍵盤緩沖區或狀態字節)中。 17.2 使用int16h中斷例程讀取鍵盤緩沖區1.BIOS提供了int 16h 中斷例程供程序員調用。2.int 16h 中斷例程中包含的一個最重要的功能是從鍵盤緩沖區中讀取一個鍵盤輸入,該功能的編號為0。3.下面的指令從鍵盤緩沖區(緩沖區的最低位)中讀取一個鍵盤輸入,并且將其從緩沖區中刪除:mov ah,0int 16h結果:(ah)=掃描碼,(al)=ASCII碼。4.int 16h 中斷例程的 0 號功能,進行如下的工作:(1)檢測鍵盤緩沖區中是否有數據;(2)沒有則繼續做第1 步;(緩沖區隨時有可能輸入數據)(3)讀取緩沖區第一個字單元中的鍵盤輸入;(4)將讀取的掃描碼送入ah,ASCII 碼送入al;(5)將己讀取的鍵盤輸入從緩沖區中刪除。5.可見,B1OS 的int 9 中斷例程和int 16h 中斷例程是一對相互配合的程序,int 9 中斷例程向鍵盤緩沖區中寫入,int 16h 中斷例程從緩沖區中讀出。它們寫入和讀出的時機不同,int 9 中斷例程在有鍵按下的時候向鍵盤緩沖區中寫入數據;而int 16h 中斷例程是在應用程序對其進行調用的時候,將數據從鍵盤緩沖區中讀出。【編程】接收用戶的鍵盤輸入,輸入“r”,將屏幕上的字符設置為紅色:輸入“g”, 將屏幕上的字符設置為綠色;輸入“b ”,將屏幕上的字符設置為藍色。 ;編程: ;接收用戶的鍵盤輸入,輸入“r”,將屏幕上的字符設置為紅色:輸入“g”, ;將屏幕上的字符設置為綠色;輸入“b ”,將屏幕上的字符設置為藍色。 ;A、B、C處的程序指令比較有技巧,請讀者自行分析 code segmentassume cs:code start: mov ah,0int 16h ;int 16h 0號功能實現從鍵盤緩沖區讀取一個鍵盤輸入mov ah,1 ;Acmp al,'r'je redcmp al,'g'je greencmp al,'b'je bluejmp short sretred: shl ah,1 ;B green: shl ah,1 ;Cblue: mov bx,0b800hmov es,bxmov bx,1mov cx,2000 s: and byte ptr es:[bx],11111000b ;設置顏色or es:[bx],ah ;設置顏色add bx,2loop ssret: mov ax,4c00hint 21hcode ends end start 17.3 字符串的輸入int 21h的0a號功能可以實現字符串的輸入也可以用int 16h,通過顯示鍵盤緩沖區中的內容,實現字符串的顯示1.使用int 16h顯示字符串程序的處理過程如下① 調用int 16h讀取鍵盤輸入;② 如果是字符,進入字符棧,顯示字符棧中的所有字符;繼續執行① ;③ 如果是退格鍵,從字符棧中彈出一個字符,顯示字符棧中的所有字符;繼續執行① ;④ 如果是Enter 鍵,向字符棧中壓入0,返回。2.子程序:字符棧的入棧、出棧和顯示參數說明(ah)=功能號,0表示入棧,1表示出棧,2表示顯示;ds : si 指向字符棧空間;對于0 號功能:(al)=入棧字符;對于1 號功能:(al)=返回的字符;對于2 號功能:(dh)、(dl) =字符串在屏幕上顯示的行、列位置。 ;使用int 16h顯示字符串的子程序:字符棧 ;最基本的字符串輸入程序,需要具備下面的功能: ;(1) 在輸入的同時需要顯示這個字符串; ;(2)一般在輸入回車符后,字符串輸入結束; ;(3)能夠刪除已經輸入的字符。;編寫一個接收字符串的輸入子程序,實現上面三個基本功能。 ;因為在輸入的過程中需要顯示,子程序的參數如下: ; (dh)、(dl)=字符串在屏幕上顯示的行、列位置; ; ds:si 指向字符串的存儲空間,字符串以O 為結尾符。;功能子程序實現charstack:jmp short charstarttable dw charpush,charpop,charshowtop dw 0 ;棧頂charstart:push bxpush dxpush dipush escmp ah,2ja sretmov bl,ahmov bh,0add bx,bxjmp word ptr table[bx] ;使用直接定址表charpush:mov bx,topmov [si][bx],alinc topjmp sretcharpop:cmp top,0je sretdec topmov bx,topmov al,[si][bx] jmp sretcharshow:mov bx,0b800hmov es,bxmov al,160mov ah,0 mul dhmov di,axadd dl,dlmov dh,0add di,dxmov bx,0charshows:cmp bx,topjne noemptymov byte ptr es:[di],' ' jmp sretnoempty:mov al,[si][bx]mov es:[di],almov byte ptr es:[di+2],' 'inc bxadd di,2jmp charshowssret: pop espop dipop dxpop bxret 17.4 應用int13h中斷例程對鍵盤進行讀寫1.磁盤的實際訪問由磁盤控制器進行,我們可以通過控制磁盤控制器來訪問磁盤。2.注意,我們只能以扇區為單位對磁盤進行讀寫。在讀寫扇區的時候,要給出面號、磁道號和扇區號。面號和磁道號從0開始,而扇區號從1開始。3.BIOS提供了對扇區進行讀寫的中斷例程,這些中斷例程完成了許多復雜的和硬件相關的工作。4.我們可以通過調用BIOS中斷例程來訪問磁盤。BIOS 提供的訪問磁盤的中斷例程為int 13h 。如下,讀取0面0道1扇區的內容到0:200:

返回參數:操作成功:(ah)=0,(al)=讀入的扇區數操作失敗:(ah)=出錯代碼將0:200中的內容寫入0面0道1扇區示例返回參數:

操作成功: (ah)=0,(al)=寫入的扇區數操作失敗: (ah)=出錯代碼5.注意:使用int 13h 中斷例程對軟盤進行讀寫。直接向磁盤扇區寫入數據是很危險的,很可能覆蓋掉重要的數據。【編程】將當前屏幕的內容保存在磁盤上分析:1 屏的內容占4000個字節,需要8 個扇區(一個扇區512B),我們用0面0道的1~8扇區存儲顯存中的內容。 code segmentassume cs:code start: mov ax,0b800hmov es,axmov bx,0 ;es:bx 指向將寫入磁盤的數據的內存區mov al,8 ;寫入的扇區數mov ch,0 ;磁道號,從0開始mov cl,1 ;扇區號 從1開始mov dl,0 ;驅動器號0:軟驅A, 1:軟驅B,硬盤從80h開始, 80h:硬盤C,81h:硬盤Dmov dh,0 ;磁頭號,(對于軟盤即面號,因為一個面用一個磁頭來讀寫)mov ah,3 ;傳遞 int 13h 寫入數據的功能號int 13h;返回參數;操作成功:(ah) = 0,(al) = 寫入的扇區數;操作失敗:(ah) = 出錯代碼return: mov ax,4c00hint 21hcode ends end start 【實驗17和課程設計2】課程設計1在第十章

綜合研究

研究試驗1 搭建一個精簡的C語言開發環境 研究試驗2 使用寄存器 研究試驗3 使用內存空間 研究試驗4 不用main函數編程 研究試驗5 函數如何接受不定數量的參數

總結

以上是生活随笔為你收集整理的汇编语言学习笔记(【汇编语言】小甲鱼零基础汇编)的全部內容,希望文章能夠幫你解決所遇到的問題。

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