https://blog.csdn.net/inv1796915552/article/details/109559495?utm_source=app&app_version=5.0.1&code=app_1562916241&uLinkId=usr1mkqgl919blen
匯編語(yǔ)言學(xué)習(xí)筆記(【匯編語(yǔ)言】小甲魚(yú)零基礎(chǔ)匯編)
目錄
第十章 call和ret指令
第十一章 標(biāo)志寄存器
第十二章 內(nèi)中斷
第十三章 int指令
第十四章 端口
第十五章 外中斷
第十六章 直接定址表
第十七章 使用BIOS進(jìn)入鍵盤(pán)輸入和磁盤(pán)讀寫(xiě)
綜合研究(自己看書(shū))
第〇章 課程資料
1.課件+源代碼
2.《匯編語(yǔ)言(第3版) 》王爽著 電子書(shū)
3.課后習(xí)題答案
百度云:https://pan.baidu.com/s/1TE1Egc0ZmeJfLP5zvamo0Q
第一章 基礎(chǔ)知識(shí)
【學(xué)習(xí)匯編主要是:學(xué)習(xí)匯編的編程思想,掌握機(jī)器運(yùn)行的思維】
匯編語(yǔ)言是直接在硬件上工作的編程語(yǔ)言,首先要了解硬件系統(tǒng)的結(jié)構(gòu),才能有效的應(yīng)用匯編語(yǔ)言對(duì)其編程。
1.匯編課程的研究重點(diǎn)
如何利用硬件系統(tǒng)的編程結(jié)構(gòu)和指令集有效靈活的控制系統(tǒng)進(jìn)行工作
2.匯編語(yǔ)言的主體是匯編指令
3.匯編指令和機(jī)器指令的差別在于指令的表示方法上
匯編指令是機(jī)器指令便于記憶的書(shū)寫(xiě)格式
4.匯編語(yǔ)言時(shí)機(jī)器指令的助記符
5.匯編語(yǔ)言的組成
1.匯編指令(機(jī)器碼的助記符)
2.偽指令(由編譯器執(zhí)行)
3.其他符號(hào)(由編譯器識(shí)別,如:+ - * /)
匯編語(yǔ)言的核心是匯編指令,他決定了匯編語(yǔ)言的特性
6.CPU對(duì)存儲(chǔ)器的讀寫(xiě)
CPU要想進(jìn)行數(shù)據(jù)的讀寫(xiě),必須和外部器件(即芯片)進(jìn)行三類(lèi)信息的交互
1.地址信息:存儲(chǔ)單元的地址
2.控制信息:芯片的選擇,讀或?qū)懨? 3.數(shù)據(jù)信息:讀或?qū)懙臄?shù)據(jù)
第二章 寄存器(CPU工作原理)
CPU=運(yùn)算器+控制器+【寄存器】,器件之間通過(guò)總線相連
8086CPU有14個(gè)寄存器,名稱(chēng)分別為:
AX,BX,CX,DX,SI,DI,SP,BP,IP,CS,SS,DS,ES,PSW
2.1 通用寄存器
1.8086CPU所有的寄存器都是16位的,可以存放2個(gè)字節(jié)
2.AX、BX、CX、DX通常用來(lái)存放一般性數(shù)據(jù)
被稱(chēng)為通用寄存器
3.8086上一代CPU中的寄存器都是8位的,為了保證兼容性
這四個(gè)寄存器都是可以分為2個(gè)獨(dú)立的8位寄存器使用
AX=AH+AL
BX=BH+BL
CX=CH+CL
DX=DH+DL
4.AX的低8位(0-7)構(gòu)成AL寄存器
高8位(8-15)構(gòu)成了AH寄存器
AH和AL寄存器是可以獨(dú)立使用的8位寄存器
2.2 字在寄存器中的存儲(chǔ)
8086一個(gè)字16位
2.3 幾條匯編指令
1.匯編指令不區(qū)分大小寫(xiě)
2.幾條匯編指令
mov ax,18 ;AX=18
mov ah,78 ;AH=78
add ax,8 ;AX=AX+8
mov ax,bx ;AX=BX
add ax,bx ;AX+=BX
3.用目前學(xué)過(guò)的匯編指令,最多使用四條指令,編程計(jì)算2的4次方
mov ax,2 ;ax=2
add ax,ax ;ax=4
add ax,ax ;ax=8
add ax,ax ;ax=16
2.4 物理地址
1.CPU訪問(wèn)內(nèi)存單元時(shí),要給出內(nèi)存單元的地址。
2.所有的內(nèi)存單元夠成的存儲(chǔ)空間是一個(gè)一維的線性空間
3.我們將這個(gè)唯一的地址稱(chēng)為物理地址
2.5 16位結(jié)構(gòu)的CPU
16位結(jié)構(gòu)描述了一個(gè)淳樸具有以下幾個(gè)方面特征:
1.運(yùn)算器一次最多可以處理16位的數(shù)據(jù)
2.寄存器的最大寬度為16位
3.寄存器和運(yùn)算器之間的通路是16位的
2.6 8086CPU給出物理地址的方法
1.8086有20位地址總線,可傳送20位地址,實(shí)際上的尋址能力為1M
2.8086內(nèi)部為16位結(jié)構(gòu),它只能傳送16位的地址,理論上表現(xiàn)出的尋址能力卻只有64K
3.問(wèn)題:8086CPU如何用內(nèi)部16位的數(shù)據(jù)轉(zhuǎn)換成20位的地址?
1.8086CPU采用一種在內(nèi)部用兩個(gè)16位地址合成的方法,來(lái)形成20位的物理地址
即:段地址+偏移地址=物理地址
2.地址加法器合成物理地址的方法:
物理地址=段地址×16+偏移地址
3.“地址段×16”即是數(shù)據(jù)左移4位(二進(jìn)制位的左移4位,十六進(jìn)制的左移1位)
在地址加法器中,如何完成“段地址×16”?
二進(jìn)制形式的段地址左移4位
2.7 “段地址×16+偏移地址=物理地址”的本質(zhì)含義
1.即可以用兩個(gè)16位的二進(jìn)制數(shù)來(lái)表示一個(gè)20位的二進(jìn)制數(shù)
2.8086CPU中內(nèi)部為16位結(jié)構(gòu),但地址線卻是20位的,使用地址加法器可以把16位地址變成20位地址
具體操作就是:段地址×16+偏移地址
2.8 段的概念
1.內(nèi)存并沒(méi)有分段,段的劃分來(lái)自于CPU,由于8086CPU用“段地址×16+偏移地址=物理地址”
的方式給出內(nèi)存單元的物理地址,使得我們可以用分段的方式來(lái)管理內(nèi)存
2.以后,在編程時(shí)可以根據(jù)需要,將若干地址連續(xù)的內(nèi)存單元看作一個(gè)段,
使用段地址×16定位段的起始地址(基礎(chǔ)地址),用偏移地址定位段中的內(nèi)存單元
3.注意
1.段地址必然是16的倍數(shù),即一個(gè)段的起始地址必然是16的倍數(shù)
2.偏移地址為16位,16位地址的尋址能力為64K,所以一個(gè)段的長(zhǎng)度最大為64K
3.CPU可以用不同的段地址和偏移地址形成同一個(gè)物理地址
2.9 段寄存器
1.段寄存器就是提供段地址的
8086CPU有4個(gè)段寄存器:
1.CS(code segment)
2.DS(data segment)
3.SS(stack segment)
4.ES(extra segment)
2.當(dāng)8086CPU要訪問(wèn)內(nèi)存時(shí),有這4個(gè)段寄存器提供內(nèi)存單元的段地址
2.10 CS和IP
1.CS和IP時(shí)候8086CPU中最關(guān)鍵的寄存器
他們指示了CPU當(dāng)前讀取指令的地址。
2.CS和IP的含義
CS:代碼段寄存器
IP:指令指針寄存器【專(zhuān)用寄存器】
3.8086CPU工作過(guò)程的簡(jiǎn)要描述
1.從CS:IP指向內(nèi)存單元,讀取指令,讀取的指令進(jìn)入指令緩沖器
2.IP=IP+所讀取指令的長(zhǎng)度,從而指向下一條指令
3.執(zhí)行指令,轉(zhuǎn)到步驟1,重復(fù)這個(gè)過(guò)程
4.開(kāi)機(jī)時(shí)的CS和IP
1.在8086CPU加電啟動(dòng)或復(fù)位后(即CPU剛開(kāi)始工作時(shí))CS和IP被設(shè)置為
CS=FFFFH,IP=0000H
2.即在8086PC機(jī)剛啟動(dòng)時(shí),CPU從內(nèi)存FFFF0H單元中讀取指令執(zhí)行
3.FFFF0H單元中的指令是8086PC機(jī)開(kāi)機(jī)后執(zhí)行的第一條指令
5.修改CS、IP的指令
1.在CPU中,程序員能夠【用指令讀寫(xiě)】的部件只有【寄存器】,
程序員可以通過(guò)改變寄存器中的內(nèi)容實(shí)現(xiàn)對(duì)CPU的控制
2.CPU從何處執(zhí)行指令是由CS、IP中的內(nèi)容決定的,程序員可以通過(guò)改變CS、IP中的內(nèi)容
控制CPU執(zhí)行目標(biāo)指令
3.如何修改CS和IP?
1.通過(guò)mov改變AX等,但是不能通過(guò)mov改變CS和IP
2.【jmp 段地址:偏移地址】 可以用來(lái)同時(shí)修改CS和IP
指令中的段地址修改CS
偏移地址修改IP
3.【jmp 某一合法的寄存器】 僅修改IP的內(nèi)容
比如:jmp ax 或者 jmp bx(類(lèi)似于mov IP ax)
4.jmp是只具有一個(gè)操作對(duì)象的指令
2.11 代碼段
1.可以將長(zhǎng)度為N(N<=64KB)的一組代碼,存放在一組地址連續(xù)、其實(shí)地址為16的倍數(shù)的內(nèi)存單元中
這段內(nèi)存是用來(lái)存放代碼的,從而定義了一個(gè)代碼段
2.CPU中只認(rèn)被CS:IP指向的內(nèi)存單元中的內(nèi)容為指令
【實(shí)驗(yàn)一】查看CPU和內(nèi)存,用機(jī)器指令和匯編指令編程
1.R命令:查看、改變CPU寄存器的內(nèi)容
r后面加寄存器的名稱(chēng)可以改變CPU寄存器的內(nèi)容
2.D命令:查看內(nèi)存中的內(nèi)容
3.E命令:改寫(xiě)內(nèi)存中的內(nèi)容
4.U命令:將內(nèi)存匯總的機(jī)器指令翻譯成匯編指令
5.T命令:執(zhí)行一條機(jī)器指令
6.A命令:以匯編指令的格式在內(nèi)存中寫(xiě)入一條機(jī)器指令
1.debug中輸入的默認(rèn)是16位數(shù)
2.空格數(shù)量任意
7.按Q可以退出
第三章 寄存器(內(nèi)存訪問(wèn))
3.1 內(nèi)存中字的存儲(chǔ)
1.任何兩個(gè)地址連續(xù)的內(nèi)存單元,N號(hào)單元和N+1號(hào)單元,可以將他們看成兩個(gè)存儲(chǔ)單元
也可以看成一個(gè)地址為N的字單元中的高位字節(jié)單元和低位字節(jié)單元
2.注意:在內(nèi)存的表示中,從高到低,是從0號(hào)單元開(kāi)始,然后逐漸變大,
即在書(shū)寫(xiě)時(shí),低位寫(xiě)在高的地方,高位寫(xiě)在低的地方,
如上圖所示:4E20H即是0號(hào)字節(jié)存儲(chǔ)20,1號(hào)字節(jié)存儲(chǔ)4E
3.2 DS和[address]
1.8086中有一個(gè)DS寄存器,通常用來(lái)存放要訪問(wèn)的數(shù)據(jù)的段地址
2.例如:我們要讀取10000H單元的內(nèi)容可以用如下程序段進(jìn)行:
mov bx,1000H
mov ds,bx
mov al,[0]
上面的三條指令將10000H(1000:0)中的數(shù)據(jù)讀到al中
1.復(fù)習(xí):已知mov指令可以完成的兩種傳送功能
1.將數(shù)據(jù)直接送入寄存器
2.將一個(gè)寄存器中的內(nèi)容送入另一個(gè)寄存器中
2.除此之外,mov指令還可以將一個(gè)內(nèi)存單元中的內(nèi)容送入一個(gè)寄存器
mov指令格式:mov 寄存器名,內(nèi)存單元地址
[...]表示一個(gè)內(nèi)存單元,“[...]”中的...表示內(nèi)存單元的【偏移地址】
執(zhí)行指令時(shí),8086CPU自動(dòng)取DS中的數(shù)據(jù)為內(nèi)存單元的【段地址】
3.如何把1000H放入DS中?
要通過(guò)通用寄存器把段地址傳入到DS中
8086CPU不支持將數(shù)據(jù)直接送入段寄存器的操作,DS是一個(gè)段寄存器
即:mov ds,1000H 是非法的
數(shù)據(jù)->通用寄存器->段寄存器
3.寫(xiě)幾條指令,將AL中的數(shù)據(jù)送入內(nèi)存單元10000H?
mov bx,1000H
mov ds,bx
mov [0],al ;al中的字節(jié)型數(shù)據(jù)送入到1000H:0中
3.3 字的傳送
1.8086CPU是16位結(jié)構(gòu),有16根數(shù)據(jù)線,所以可以一次性傳送16位的數(shù)據(jù)
即:一次可以傳送一個(gè)字
2.比如
mov bx,1000H
mov ds,bx
mov ax,[0] ;1000H:0處的字型數(shù)據(jù)送入ax中
mov [0],cx ;cx中的16位數(shù)據(jù)送入到1000H:0中
3.4 mov、add、sub指令
1.復(fù)習(xí):已學(xué)mov指令的幾個(gè)形式
1.mov 寄存器,數(shù)據(jù) ;立即尋址
2.mov 寄存器,寄存器 ;寄存器尋址
3.mov 寄存器,內(nèi)存單元 ;直接尋址
4.mov 內(nèi)存單元,寄存器 ;寄存器尋址?
5.mov 段寄存器,寄存器 ;寄存器尋址
6.mov 寄存器,段寄存器 ;寄存器尋址
2.add、sub同mov一樣,都有兩個(gè)操作對(duì)象
1.add的用法
1.add 寄存器,數(shù)據(jù) ;立即尋址
2.add 寄存器,寄存器 ;寄存器尋址
3.add 寄存器,內(nèi)存單元 ;直接尋址
4.add 內(nèi)存單元,寄存器 ;
2.sub的用法
【不帶借位的減法】
指令格式 sub op1,op2 ;意為:op1=op1-op2
1.sub 寄存器,數(shù)據(jù) ;立即尋址
2.sub 寄存器,寄存器 ;寄存器尋址
3.sub 寄存器,內(nèi)存單元 ;直接尋址
4.sub 內(nèi)存單元,寄存器 ;
3.5 數(shù)據(jù)段
如何訪問(wèn)數(shù)據(jù)段中的數(shù)據(jù)?
將一段內(nèi)存當(dāng)作數(shù)據(jù)段,是我們?cè)诰幊虝r(shí)的一種安排
具體操作:用DS存放數(shù)據(jù)段的段地址,再根據(jù)需要,用相關(guān)指令訪問(wèn)數(shù)據(jù)段中的具體單元
3.6 棧
1.8086CPU提供相關(guān)的指令來(lái)以棧的方式訪問(wèn)內(nèi)存空間
這意味著,我們?cè)诨?086CPU編程的時(shí)候,可以將一段內(nèi)存當(dāng)作棧來(lái)使用
2.8086CPU提供入棧和出棧指令:(最基本的)
push(入棧)
pop(出棧)
1.push ax:將寄存器ax中的數(shù)據(jù)送入棧中
2.pop ax:從棧頂取出數(shù)據(jù)送入ax
3.8086CPU的入棧和出棧操作都是以【字(16位)】為單位進(jìn)行的
4.pop和push可以在寄存器和內(nèi)存之間傳送數(shù)據(jù)
3.CPU如何知道一段內(nèi)存空間被當(dāng)做棧使用?
1.8086CPU中,有兩個(gè)寄存器
1.段寄存器SS:存放棧頂?shù)亩蔚刂? 2.寄存器SP:存放棧頂?shù)钠频刂贰緦?zhuān)用寄存器】
2.任意時(shí)刻SS:SP指向棧頂元素,當(dāng)棧為空的時(shí)候,也就不存在棧頂元素
ss:sp也就指向棧最高地址單元的下一個(gè)單元
4.執(zhí)行push和pop的時(shí)候,如何知道哪個(gè)單元是棧頂單元?
1.執(zhí)行push ax時(shí)
1.sp=sp-2
2.將ax中的內(nèi)容送入到ss:sp指向的內(nèi)存單元
ss:sp此時(shí)指向新棧頂
2.執(zhí)行pop ax時(shí)
1.將ss:sp指向的內(nèi)存單元的內(nèi)容送入到ax中
注意:這里取出的內(nèi)容在內(nèi)存中還是存在的,并沒(méi)有被重置
下一輪push會(huì)覆蓋
2.sp=sp+2
5.如果棧是空的,sp指向哪里?
sp指向最高地址單元的下一個(gè)單元
3.7 棧頂超界的問(wèn)題
ss、sp只記錄了棧頂?shù)牡刂?,依靠ss、sp可以保證在入棧和出棧時(shí)找到棧頂
可以,如何能夠保證在入棧、出棧時(shí),棧頂不會(huì)超出??臻g?
1.8086CPU不保證棧的操作不會(huì)越界
2.當(dāng)棧空的時(shí)候,再執(zhí)行pop出棧 或者 當(dāng)棧滿的時(shí)候再使用push入棧
都會(huì)發(fā)生棧頂超界問(wèn)題,會(huì)操作到棧以外的數(shù)據(jù),
這些數(shù)據(jù)可能是其他用途的數(shù)據(jù)或者代碼
棧頂超界是危險(xiǎn)的?。。? 3.8086CPU沒(méi)有記錄棧頂上下限的寄存器
3.8 棧段
1.將一段內(nèi)存當(dāng)做棧段,僅僅是我們?cè)诰幊虝r(shí)的一種安排,
2.ss:sp指向我們定義的棧段的棧頂;
3.當(dāng)??諘r(shí),sp指向最高地址的下一個(gè)單元
4.思考:一個(gè)棧段最大可以設(shè)為多少?
64KB
5.設(shè)棧頂?shù)淖兓秶?-FFFFH,從??諘r(shí)sp=0(最高地址單元FFFFH的下一個(gè)單元0000H)
一直壓棧,直到棧滿,sp=0;
如果再次壓棧,棧頂將環(huán)繞,覆蓋原來(lái)?xiàng)V械膬?nèi)容
6.一段內(nèi)存,既可以是代碼的存儲(chǔ)空間,又可以是數(shù)據(jù)的存儲(chǔ)空間,還可以是??臻g
也可以是什么都屬實(shí)。
關(guān)鍵在于CPU中寄存器的設(shè)置,即:cs、ip、ss、sp、ds的設(shè)置
**可以通過(guò)mov直接給sp賦值【立即數(shù)尋址】,但是不能通過(guò)mov給cs、ip、ss、ds賦值
給cs和ip賦值需要使用jum指令
給ss和ds賦值需要使用mov ss或ds,寄存器 ;【寄存器尋址】
【實(shí)驗(yàn)二】
第四章 第一個(gè)匯編程序
4.1 一個(gè)源程序從寫(xiě)出到執(zhí)行的過(guò)程
1.一個(gè)匯編語(yǔ)言程序從寫(xiě)出到最終執(zhí)行的簡(jiǎn)要過(guò)程
編寫(xiě)->編譯連接->執(zhí)行
2.對(duì)源程序進(jìn)行編譯連接
1.使用匯編語(yǔ)言編譯程序(MASM.EXE)對(duì)源程序文件中的源程序進(jìn)行編譯,產(chǎn)生目標(biāo)文件【.obj文件】
2.再用連接程序(LINK.EXE)對(duì)目標(biāo)文件進(jìn)行連接,生成可在操作系統(tǒng)中直接運(yùn)行的可執(zhí)行文件【.EXE文件】。
3.可執(zhí)行文件包含兩部分內(nèi)容
1.程序(從源程序的匯編指令翻譯過(guò)來(lái)的機(jī)器碼)和數(shù)據(jù)(源程序中定義的數(shù)據(jù))
2.相關(guān)的描述信息(比如:程序有多大、要占多少內(nèi)存空間等)
4.執(zhí)行可執(zhí)行文件中的程序
1.在操作系統(tǒng)(如:MSDOS)中,執(zhí)行可執(zhí)行文件中的程序
2.操作系統(tǒng)依照可執(zhí)行文件中的描述信息,將可執(zhí)行文件中的機(jī)器碼和數(shù)據(jù)加載入內(nèi)存
并進(jìn)行相關(guān)的初始化(比如:設(shè)置CS:IP指向第一條要執(zhí)行的指令),然后由CPU執(zhí)行程序
4.2 源程序的主要結(jié)構(gòu)
源程序由 匯編指令+偽指令+宏指令 組成
偽指令:編譯器處理
匯編指令:編譯為機(jī)器碼
1.偽指令
1.沒(méi)有對(duì)應(yīng)的機(jī)器碼的指令,不能由CPU直接執(zhí)行
2.偽指令是由編譯器來(lái)執(zhí)行的指令,編譯器根據(jù)偽指令來(lái)進(jìn)行相關(guān)的編譯工作
2.segment和ends【定義一個(gè)段】
1.segment和ends是一對(duì)成對(duì)使用的偽指令
2.編寫(xiě)匯編程序【必須】使用到的指令
3.segment和ends的功能是定義一個(gè)段
segment:說(shuō)明一個(gè)段開(kāi)始
ends:說(shuō)明一個(gè)段結(jié)束
4.一個(gè)段必須有一個(gè)名稱(chēng)來(lái)標(biāo)識(shí),使用格式為
段名 segment
段名 ends
5.一個(gè)匯編程序由多個(gè)段組成
這些段用來(lái)存放【代碼、數(shù)據(jù)、或當(dāng)作??臻g】來(lái)使用
一個(gè)有意義的匯編程序至少要有一個(gè)段,這個(gè)段用來(lái)存放代碼。
3.end【真正的沒(méi)了】
1.end是一個(gè)匯編程序的結(jié)束標(biāo)記
2.編譯器在編譯匯編程序的過(guò)程中,如果碰到了偽指令end,就結(jié)束對(duì)源程序的編譯
3.如果程序?qū)懲炅?,要在結(jié)尾處加上偽指令end
否則,編譯器無(wú)法知道程序在何處結(jié)束
4.【切記】不要把end和ends搞混了
end:匯編程序的結(jié)束標(biāo)記
ends:與segment成對(duì)出現(xiàn)
4.assume【寄存器和段的關(guān)聯(lián)假設(shè)】
1.它假設(shè)某一段寄存器和程序中的某一個(gè)用segment...ends定義的段相關(guān)聯(lián)
2.通過(guò)assume說(shuō)明這種關(guān)聯(lián),在需要的情況下,
編譯程序可以將段寄存器和某一具體的段相聯(lián)系
5.程序和源程序
1.我們將源程序文件中的所有內(nèi)容稱(chēng)為【源程序】
2.將源程序中最終由計(jì)算機(jī)執(zhí)行處理的指令或數(shù)據(jù)稱(chēng)為【程序】
3.程序最先以匯編指令的形式,存儲(chǔ)在源程序中
然后經(jīng)過(guò)編譯、連接后轉(zhuǎn)變?yōu)闄C(jī)器碼,存儲(chǔ)在可執(zhí)行文件中
6.標(biāo)號(hào),標(biāo)號(hào)與段名稱(chēng)有所區(qū)別
1.一個(gè)標(biāo)號(hào)指代了一個(gè)地址,即是段名稱(chēng)。
2.段名稱(chēng) 放在segment的前面,作為一個(gè)段的名稱(chēng)
這個(gè)段的名稱(chēng)最終將被匯編、連接程序處理為一個(gè)段的段地址
7.DOS中的程序運(yùn)行
1.DOS是一個(gè)單任務(wù)操作系統(tǒng)
2.一個(gè)程序結(jié)束后,將CPU的控制權(quán)交還給是他得以運(yùn)行的程序
我們稱(chēng)這個(gè)過(guò)程為:程序返回
8.程序返回
mov ax,4c00H
int 21H ;【中斷機(jī)制】是DOS最偉大的機(jī)制,Windows系統(tǒng)上是【消息機(jī)制】
這兩條指令所實(shí)現(xiàn)的功能就是程序返回
9.幾個(gè)和結(jié)束相關(guān)的內(nèi)容
1.段結(jié)束:偽指令
通知編譯器一個(gè)段的結(jié)束【ends】
2.程序結(jié)束:偽指令
通知編譯器程序的結(jié)束【end】
3.程序返回:匯編指令
mov ax,4c00H
int 21H
10.語(yǔ)法錯(cuò)誤和邏輯錯(cuò)誤
1.語(yǔ)法錯(cuò)誤
1.程序在編譯時(shí)被編譯器發(fā)現(xiàn)的錯(cuò)誤
2.容易發(fā)現(xiàn)
2.邏輯錯(cuò)誤
1.在編寫(xiě)時(shí)不會(huì)表現(xiàn)出來(lái)的錯(cuò)誤、在運(yùn)行時(shí)會(huì)發(fā)生的錯(cuò)誤
2.不容易發(fā)現(xiàn)
4.3 以簡(jiǎn)化的方式進(jìn)行匯編和連接
匯編使用的程序:masm.exe
連接使用的程序:link.exe
簡(jiǎn)化方式進(jìn)行匯編和連接的程序:ml.exe
4.4 匯編和連接的作用
連接的作用
1.當(dāng)源程序很大時(shí),可以將他們分成多個(gè)源程序文件夾編譯
每個(gè)源程序編譯成為目標(biāo)文件后,再用連接程序?qū)⑺鼈冞B接在一起,
生成一個(gè)可執(zhí)行文件
2.程序中調(diào)用了某個(gè)庫(kù)文件中的子程序,需要將這個(gè)庫(kù)文件和該程序生成的目標(biāo)文件連接到一起
生成一個(gè)可執(zhí)行文件
3.一個(gè)源程序編譯后,得到了存有機(jī)器碼的目標(biāo)文件,目標(biāo)文件中的有些內(nèi)容還不能直接
用來(lái)生成可執(zhí)行文件,連接程序?qū)⑦@些內(nèi)容處理為最終的可執(zhí)行信息。
所以在只有一個(gè)源程序文件,而又不需要調(diào)用某個(gè)庫(kù)中的子程序的情況下,也必須用
連接程序?qū)δ繕?biāo)文件進(jìn)行處理,生成可執(zhí)行文件
4.5 可執(zhí)行文件中的程序裝入內(nèi)存并運(yùn)行的原理
1.在DOS中,可執(zhí)行文件中的程序P1若要運(yùn)行,必須有一個(gè)正在運(yùn)行的程序P2
將P1從可執(zhí)行文件中加載入內(nèi)存,將CPU的控制權(quán)交給P1,P1才能得以運(yùn)行
2.當(dāng)P1運(yùn)行完畢后,應(yīng)該將CPU的控制權(quán)交還給使他得以運(yùn)行的程序
3.操作系統(tǒng)的外殼
1.操作系統(tǒng)是由多個(gè)功能模塊組成的龐大、復(fù)雜的軟件系統(tǒng)
任何通用的操作系統(tǒng),都需要提供一個(gè)稱(chēng)為shell(外殼)的程序,
用戶(操作人員)使用這個(gè)程序來(lái)操作計(jì)算機(jī)系統(tǒng)工作
2.DOS中有一個(gè)程序command.com,這個(gè)程序在DOS中稱(chēng)為命令解釋器
也就是DOS系統(tǒng)的shell
4.執(zhí)行可執(zhí)行文件1.exe時(shí),
(1)什么程序?qū)PU的控制權(quán)交給了1.exe?
(2)將程序1.exe加載入內(nèi)存后,如何使程序得以運(yùn)行?
(3)1.exe程序運(yùn)行結(jié)束后,返回到了哪里?
1.在DOS中直接執(zhí)行1.exe時(shí),是正在運(yùn)行的cmd.exe將1.exe中的程序加載入內(nèi)存
2.cmd.exe設(shè)置CPU的CS:IP指向程序的第一條指令(即,程序的入口)
從而使程序得以運(yùn)行
3.程序運(yùn)行結(jié)束后,返回cmd.exe中,CPU繼續(xù)運(yùn)行cmd.exe
【實(shí)驗(yàn)三】
第五章 【bx】和loop指令
5.1 [bx]
1.和[0]類(lèi)似,[0]表示內(nèi)存單元,它的偏移地址是0;
2.[bx]同樣也表示一個(gè)內(nèi)存單元,它的段地址在DS中
它的偏移地址在bx中,至于是取字還是取字節(jié),
要看他放入的寄存器是8位還是16位
3.補(bǔ)充:inc指令:相當(dāng)于C語(yǔ)言中的++運(yùn)算符
5.2 Loop指令
這個(gè)指令和循環(huán)有關(guān)
1.指令格式:loop 標(biāo)號(hào)
CPU執(zhí)行l(wèi)oop指令的時(shí)候,要進(jìn)行兩步操作
1.(cx)=(cx)-1;
2.判斷cx中的值,若不為零,則轉(zhuǎn)至標(biāo)號(hào)處執(zhí)行程序
若為零,則向下執(zhí)行。
2.通常,loop指令實(shí)現(xiàn)循環(huán),cx中存放循環(huán)的次數(shù)
3.標(biāo)號(hào)
在匯編語(yǔ)言中,標(biāo)號(hào)代表了一個(gè)地址,標(biāo)號(hào)標(biāo)識(shí)了一個(gè)地址
4.使用cx和loop指令相配合實(shí)現(xiàn)循環(huán)功能的三個(gè)要點(diǎn)
1.在cx中存放循環(huán)次數(shù)
2.loop指令中的標(biāo)號(hào)所標(biāo)識(shí)地址要在前面
3.要循環(huán)執(zhí)行的程序段,要寫(xiě)在標(biāo)號(hào)和loop指令的中間
5.用cx和loop指令相配合實(shí)現(xiàn)循環(huán)功能的程序框架
mov cx,循環(huán)次數(shù)
S:循環(huán)執(zhí)行的程序段
loop s
5.3 在Debug中跟蹤供loop指令實(shí)現(xiàn)的循環(huán)程序
**注意:在匯編程序中,數(shù)據(jù)不能以字母開(kāi)頭,如果要輸入像FFFFH這樣的數(shù)
則要在前面添加一個(gè)0
在debug程序中引入G命令和P命令
1.G命令
G命令如果后面不帶參數(shù),則一直執(zhí)行程序,直到程序結(jié)束
G命令后面如果帶參數(shù),則執(zhí)行到ip為那個(gè)參數(shù)地址停止
2.P命令
T命令相當(dāng)于單步進(jìn)入(step into)
P命令相當(dāng)于單步通過(guò)(step over)
5.4 Debug和匯編編譯器Masm對(duì)指令的不同處理
1.在debug中,可以直接用指令 mov ax,[0] 將偏移地址為0號(hào)單元的內(nèi)容賦值給ax
2.但通過(guò)masm編譯器,mov ax,[0] 會(huì)被編譯成 mov ax,0
1.要寫(xiě)成這樣才能實(shí)現(xiàn):mov ax,ds:[0]
2.也可以寫(xiě)成這樣:
mov bx,0
mov ax,[bx] ;或者mov ax,ds:[bx]
5.5 loop和[bx]的聯(lián)合應(yīng)用
1.計(jì)算ffff:0~ffff:b單元中的數(shù)據(jù)的和,結(jié)果存儲(chǔ)在dx中
1.注意兩個(gè)問(wèn)題
1.12個(gè)8位數(shù)據(jù)加載一起,最后的結(jié)果可能會(huì)超出8位(越界),故要用16位寄存器存放結(jié)果
2.將一個(gè)8位的數(shù)據(jù)加入到16位寄存器中,類(lèi)型不匹配,8位的數(shù)據(jù)不能與16位相加
2.【解決辦法】
把原來(lái)8位的數(shù)據(jù),先通過(guò)通用寄存器ax,將它們轉(zhuǎn)化成16位的
3.代碼如下
assume cs:codesg
codesg segment
start:
;指定數(shù)據(jù)段
mov ax,0ffffh
mov ds,ax
;初始化
mov ax,0
mov dx,0
mov bx,0
;指定循環(huán)次數(shù),12次
mov cx,0ch
circ:
;把8位數(shù)據(jù)存入al中,即ax中存放的是[bx]轉(zhuǎn)化之后的16位數(shù)據(jù),前8位都是0
mov al,[bx]
;進(jìn)行累加
add dx,ax
;bx自增,變化內(nèi)存的偏移地址
inc bx
loop circ
;程序返回
mov ax,4c00h
int 21H
codesg ends
end start
5.6 段前綴
1.指令“mov ax,[bx]”中,內(nèi)存單元的偏移地址由bx給出,而段地址默認(rèn)在ds中
2.我們可以在訪問(wèn)內(nèi)存單元的指令中顯式地給出內(nèi)存單元的段地址所在的段寄存器
比如 mov ax,ds:[0]
mov ax,ds:[bx]
這里的ds就叫做【段前綴】
5.7 一段安全的空間
1.8086模式中,隨意向一段內(nèi)存空間寫(xiě)入內(nèi)容是很危險(xiǎn)的
因?yàn)檫@段空間中可能存放著【重要的系統(tǒng)數(shù)據(jù)或代碼】
2.在一般的PC機(jī)中,DOS方式下,DOS和其他合法的程序一般都不會(huì)使用【0:200~0:2FF】
的256個(gè)字節(jié)的空間。所以,我們使用這段空間是安全的
第六章 包含多個(gè)段的程序
6.1在代碼段中使用數(shù)據(jù)
1.dw的含義【定義字型數(shù)據(jù):define word,16字節(jié)】
在數(shù)據(jù)段中使用dw定義數(shù)據(jù),則數(shù)據(jù)在數(shù)據(jù)段中
在代碼段中使用dw定義數(shù)據(jù),則數(shù)據(jù)在代碼段中
堆棧段也是一樣
2.在程序的第一條指令前加一個(gè)標(biāo)號(hào)start,并且這個(gè)標(biāo)號(hào)在偽指令end后面出現(xiàn)
可以通知編譯器程序在什么地方結(jié)束,并且也可以通知編譯器程序的入口在哪里
6.2在代碼段中使用棧
**補(bǔ)充:如果題目要求【逆序】存放,就要想到棧(FILO)
使用dw向系統(tǒng)申請(qǐng)一段空間,然后把這個(gè)空間當(dāng)做棧
6.3將數(shù)據(jù)、代碼、棧放入不同的段
1.在前面的6.1和6.2中,我們?cè)诔绦蛑杏玫搅藬?shù)據(jù)和棧,我們?cè)诰幊痰臅r(shí)候要注意
何處是數(shù)據(jù),何處是棧、何處是代碼
2.這樣做顯然有兩個(gè)問(wèn)題
1.把他們放在一個(gè)段中是程序顯得混亂
2.前面程序中處理的數(shù)據(jù)很少,用到的棧空間也小,放在一個(gè)段里面沒(méi)有問(wèn)題
但數(shù)據(jù)、棧、代碼需要的空間超過(guò)64KB,就不能放在一個(gè)段中
(8086中一個(gè)段的容量不能大于64KB)
3.我們可以和定義代碼段一樣的方法來(lái)定義多個(gè)段
然后在這些段里面定義需要的數(shù)據(jù),或通過(guò)定義數(shù)據(jù)來(lái)取得棧空間
4.將數(shù)據(jù)、代碼、棧放入不同的段
1.我們可以在源程序中為這三個(gè)段起具有含義的名稱(chēng)
用來(lái)存放數(shù)據(jù)的段,我們將其命名為“data”
用來(lái)存放代碼的段,我們將其命名為“code”
用來(lái)作??臻g的段,我們將其命名為“stack”
但是CPU看得懂嗎?【不能】
2.我們?cè)谠闯绦蛑杏脗沃噶? “assume cs:code,ds:data,ss:stack”將cs、ds和ss分別和code、data、stack段相連
這樣做了之后,CPU是都就會(huì)將cs指向code,ds指向data,ss指向stack
從而按照我們的意圖來(lái)處理這些段呢?【不能】
偽指令CPU看不懂,偽指令是給編譯器看的
3.若要CPU按照我們的安排行事,就要用機(jī)器指令控制它,源程序中的匯編指令
才是CPU要執(zhí)行的內(nèi)容
需在在code段中給DS,CS、SS設(shè)置相應(yīng)的值才能讓CPU識(shí)別出數(shù)據(jù)段、代碼段、堆棧段
其中匯編程序開(kāi)始的地方(即代碼段開(kāi)始的地方)由end后面的標(biāo)號(hào)所指向的地方給出
5.assume指令不可省略,至于為什么,需要以后多多體會(huì)
【實(shí)驗(yàn)五】
1.如果段中的數(shù)據(jù)占N個(gè)字節(jié),則程序加載后,這段實(shí)際占有的空間為:N%16==0?N:16×(N/16+1);
因?yàn)橐粋€(gè)段最小占用16字節(jié),即有16個(gè)字節(jié)只有這個(gè)段可以訪問(wèn)到
2.在編輯源程序的時(shí)候,如果調(diào)換各個(gè)段的編寫(xiě)位置,最后CS、DS、SS的值會(huì)發(fā)生變化
3.如果去掉start,編譯器會(huì)從上到下執(zhí)行,如果第一個(gè)段是代碼段,則可以正常運(yùn)行
若第一個(gè)段不是代碼段,則不會(huì)正常運(yùn)行
4.代碼示例1
assume cs:code,ds:data,ss:stack
;數(shù)據(jù)段
data segment
;8個(gè)數(shù)據(jù)
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends
;棧段
stack segment
;8個(gè)數(shù)據(jù)
dw 0,0,0,0,0,0,0,0
stack ends
;代碼段
code segment
start:
;棧空間初始化
mov ax,stack
mov ss,ax
mov sp,16
;數(shù)據(jù)段初始化
mov ax,data
mov ds,ax
push ds:[0];一個(gè)棧單元是一個(gè)字
push ds:[2]
;存放數(shù)據(jù)不會(huì)改變
pop ds:[2]
pop ds:[0]
;程序返回
mov ax,4c00h
int 21h
code ends
end
5.將a,b數(shù)據(jù)段中的內(nèi)容分別相加,結(jié)果放入data數(shù)據(jù)段中
assume cs:code
;數(shù)據(jù)段
a segment
db 1,2,3,4,5,6,7,8
a ends
;數(shù)據(jù)段
b segment
db 1,2,3,4,5,6,7,8
b ends
;數(shù)據(jù)段
data segment
db 0,0,0,0,0,0,0,0
data ends
;代碼段
code segment
start:
mov bx,0
mov ax,0
mov dx,a
mov ss,dx
mov dx,b
mov es,dx
mov dx,data
mov ds,dx
mov cx,8
circ:
add al,ss:[bx]
add al,es:[bx]
mov [bx],al
inc bx
mov al,0
loop circ
;程序返回
mov ax,4c00h
int 21h
code ends
end start
6.將a數(shù)據(jù)段中的前8個(gè)字型數(shù)據(jù)逆序存儲(chǔ)到b段中
assume cs:code
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
mov ax,0
mov ax,a
mov ss,ax
mov sp,0
mov ax,0
mov ax,b
mov ds,ax
mov bx,0
mov cx,8
circ:
pop [bx]
add bx,2
loop circ
mov ax,4c00h
int 21h
code ends
end start
第七章 更靈活地定位內(nèi)存地址
本章主要講解一些更靈活的定位內(nèi)存地址的方法和相關(guān)的編程方法
7.1 and和or指令
1.and指令:邏輯與指令,按位進(jìn)行與運(yùn)算
1.如:mov al,01100011B
and al,00111011B
執(zhí)行后:
al=00100011B
2.通過(guò)and指令可將操作對(duì)象的相應(yīng)位設(shè)為0,其他位保持不變
例如al的第6位設(shè)為0:and al,10111111B
例如al的第7位設(shè)為0:and al,01111111B
例如al的第0位設(shè)為0:and al,11111110B
2.or指令,邏輯或運(yùn)算,按位進(jìn)行或運(yùn)算
1.如:mov al,01100011B
or al,00111011B
執(zhí)行后:
al=01111011B
2.通過(guò)該指令可將操作對(duì)象的相應(yīng)位設(shè)為1,其他位不變
or al,01000000B;將al的第6位設(shè)為1
or al,10000000B;將al的第7位設(shè)為1
or al,00000001B;將al的第0位設(shè)為1
7.2 關(guān)于ASCII碼
一種編碼方案,在計(jì)算機(jī)系統(tǒng)中通常被采用,8位
7.3 以字符形式給出的數(shù)據(jù)
1.在匯編程序中,可以使用'×××'的方式指明數(shù)據(jù)是以字符的形式給出的
2.編譯器會(huì)將它們轉(zhuǎn)化為相應(yīng)的ASCII碼
3.例如
1.db 'unIX' ;相當(dāng)于:db 75H,6EH,49H,58H
'u'、'n'、'I'、'X'的ASCII碼分別為75H,6EH,49H,58H
2.mov al,'a' ;相當(dāng)于:mov al,61H
'a'的ASCII碼為61H
4.ASCII碼中,大寫(xiě)字母和小寫(xiě)字母之間的規(guī)律
小寫(xiě)字母=大寫(xiě)字母+32
小寫(xiě)字母=大寫(xiě)字母+20H
大寫(xiě)字母從41H開(kāi)始排,小寫(xiě)字母從61H開(kāi)始排
大寫(xiě) | 二進(jìn)制 | 小寫(xiě) | 二進(jìn)制 |
---|
A | 01000001 | a | 01100001 | B | 01000010 | b | 01100010 | C | 01000011 | c | 01100011 | D | 01000100 | d | 01100100 |
7.4 大小寫(xiě)轉(zhuǎn)換的問(wèn)題
1.方案一:
1.識(shí)別出是該字節(jié)是表示一個(gè)的大寫(xiě)英文字符,還是小寫(xiě)的
用于條件判斷的匯編程序,目前還沒(méi)有學(xué)到
2.根據(jù)+20H 或者 -20H進(jìn)行大小寫(xiě)轉(zhuǎn)換
2.方案二:
1.若全部轉(zhuǎn)化為大寫(xiě),則將第5位置0
and al,11011111B
2.若全部轉(zhuǎn)化為小寫(xiě),則將第5位置1
or al,00100000B
7.5 [bx+常數(shù)]
mov ax,[bx+200]的含義:
1.將一個(gè)內(nèi)存單元的內(nèi)容送入ax,這個(gè)內(nèi)存單元的長(zhǎng)度為2字節(jié),存放一個(gè)入一個(gè)子單元
該字單元的偏移地址為bx中的數(shù)值加上200,段地址在ds中
2.也可以寫(xiě)成
1.mov ax,200[bx]
2.mov ax,[bx].200
7.6 用[bx+idata]的方式進(jìn)行數(shù)組的處理
在codesg中填寫(xiě)代碼,將datasg中定義的第一個(gè)字符串轉(zhuǎn)化為大寫(xiě),第二個(gè)字符串轉(zhuǎn)化為小寫(xiě)
1.我們觀察datasg段中的兩個(gè)字符串,一個(gè)的起始地址為0,另一個(gè)的起始地址為5
2.我們可以將這兩個(gè)字符串看作兩個(gè)數(shù)組,一個(gè)從0地址開(kāi)始存放,另一個(gè)從5開(kāi)始存放
3.我們可以用[0+bx]和[5+bx]的方式在同一個(gè)循環(huán)中定位這兩個(gè)字符串中的字符
4.注意這個(gè)數(shù)組的定位方式,對(duì)比C語(yǔ)言
C語(yǔ)言的數(shù)組定位方式:a[i],b[i], a、b是地址常量
匯編語(yǔ)言的數(shù)組定位方式:0[bx],5[bx]
所以:[bx+常數(shù)]的方式為高級(jí)語(yǔ)言實(shí)現(xiàn)數(shù)組提供了便利的機(jī)制
assume cs:codesg,ds:datasg
datasg segment
db 'BaSiC'
db 'MinIX'
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5 ;做5次循環(huán)
circ:
mov al,[bx]
and al,11011111b
mov [bx],al
mov al,[bx+5];等價(jià)于mov al,5[bx];等價(jià)于mov al,[bx].5
or al,00100000b
mov 5[bx],al
inc bx
loop circ
mov ax,4c00h
int 21h
codesg ends
end start
7.7 SI和DI
已經(jīng)學(xué)過(guò)的10個(gè)寄存器:AX、BX、CX、DX、DS、CS、SS、ES、IP、SP
1.SI和DI是8086CPU中和bx功能相近的寄存器
bx不夠用,所以引進(jìn)了SI和DI
2.SI和DI(16位)不能夠分成兩個(gè)8位寄存器來(lái)使用【和bx的區(qū)別】
3.下面三組指令實(shí)現(xiàn)了相同的功能
1.mov bx,0
mov ax,[bx]
2.mov si,0
mov ax,[si]
3.mov di,0
mov ax,[di]
4.下面三組指令也實(shí)現(xiàn)了相同的功能
1.mov bx,0
mov ax,[bx+123]
2.mov si,0
mov ax,[si+123]
3.mov di,0
mov ax,[di+123]
5.用寄存器SI和DI實(shí)現(xiàn)將字符串'welcome to masm!'復(fù)制到它后面的數(shù)據(jù)區(qū)中
通常用ds:si指向要復(fù)制的源始字符串
通常用ds:di指向要復(fù)制的目的空間
**注意si、di是16位寄存器,循環(huán)中自增時(shí),應(yīng)該+2
assume cs:code,ds:data
data segment
db 'welcome to masm!'
db '................'
data ends
code segment
start:
mov ax,data
mov ds,ax
mov si,0
mov di,16
mov cx,8
circ:
mov ax,0[si]
mov [di],ax
inc di
inc di
inc si
inc si
loop circ
mov ax,4c00h
int 21h
code ends
end start
7.8 [bx+si]和[bx+di]
1.[bx+si]和[bx+di]的含義類(lèi)似,我們以[bx+si]為例進(jìn)行講解
[bx+si]表示一個(gè)內(nèi)存單元,它的偏移地址為bx中的數(shù)值加上si中的數(shù)值
它的偏移地址在ds中
2.[bx+si]也可以寫(xiě)成[bx][si]
7.9 [bx+si+常數(shù)]和[bx+di+常數(shù)]
1.以[bx+Si+常數(shù)]為例講解
[bx+si+常量]表示一個(gè)內(nèi)存單元,偏移地址為bx的值+si的值+常數(shù)
2.指令mov ax,[bx+si+常數(shù)]也可以寫(xiě)成如下形式
1.mov ax,200[bx+si]
2.mov ax,200[bx][si]
3.mov ax,[bx].200[si]
7.10 不同的尋址方式的靈活應(yīng)用
1.總結(jié)幾種定位內(nèi)存的方法
1.ds:[常數(shù)] 【直接尋址】
用一個(gè)常量來(lái)表示地址,可用于直接定位一個(gè)內(nèi)存單元
2.[bx] 【寄存器間接尋址】
用一個(gè)寄存器的值來(lái)表示內(nèi)存地址,可以間接定位一個(gè)內(nèi)存單元
3.[bx+常數(shù)] 【??】
用一節(jié)寄存器的值和常量表示內(nèi)存地址,可在一個(gè)起始地址的基礎(chǔ)上用變量間接定位一個(gè)內(nèi)存單元
4.[bx+si]
5.[bx+si+常數(shù)]
2.編程,給定數(shù)據(jù)段data,將data段中每個(gè)單詞的頭一個(gè)字母改寫(xiě)成大寫(xiě)字母
assume cs:code,ds:data
data segment
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,6
circ:
mov al,[bx+3]
and al,11011111b
mov [bx+3],al
add bx,16
loop circ
mov ax,4c00h
int 21h
code ends
end start
3.編程,給定數(shù)據(jù)段data,將data段中的每個(gè)單詞改為大寫(xiě)字母
1.【loop指令cx-1之后,在判斷是否為0】
2.雙重循環(huán)用匯編怎么實(shí)現(xiàn)?
應(yīng)該在每次開(kāi)始內(nèi)循環(huán)的時(shí)候,將外層循環(huán)的cx的值保存起來(lái),
在執(zhí)行外層循環(huán)的loop指令前,在恢復(fù)外層循環(huán)的cx數(shù)值。
**可以用寄存器來(lái)臨時(shí)保存,也可以用棧空間(內(nèi)存)保存【沒(méi)有多余的寄存器】
更好的方法是使用:棧
1.使用寄存器實(shí)現(xiàn)
assume cs:code,ds:data
data segment
db 4,4,6,4,7,4;單詞的字母數(shù)
db ' ';補(bǔ)齊
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,16
mov si,0
mov di,0
mov cx,6;外層循環(huán)6次
outer:;外層循環(huán)
mov dx,cx;用寄存器將外層循環(huán)的次數(shù)保存,C語(yǔ)言中是用棧來(lái)保存的
mov cx,0
mov cl,[di];內(nèi)循環(huán)的次數(shù)
inner:;內(nèi)層循環(huán)
mov al,[bx][si+3]
and al,11011111b
mov [bx][si+3],al
inc si
loop inner
add bx,16
mov si,0
inc di
mov cx,dx;恢復(fù)外層循環(huán)的次數(shù)
loop outer
mov ax,4c00h
int 21h
code ends
end start
2.使用棧實(shí)現(xiàn)【更好的方法】
assume cs:code,ds:data,ss:stack
data segment
db 4,4,6,4,7,4;單詞的字母數(shù)
db ' ';補(bǔ)齊
db '1. file '
db '2. edit '
db '3. search '
db '4. view '
db '5. options '
db '6. help '
data ends
stack segment
dw 1,2,3,4,5,6,7,8
stack ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov sp,16
mov bx,16
mov si,0
mov cx,6;外層循環(huán)6次
outer:;外層循環(huán)
push cx;將外層循環(huán)的次數(shù)保存
mov cx,0
mov cl,[di];內(nèi)循環(huán)的次數(shù)
inner:;內(nèi)層循環(huán)
mov al,[bx][si+3]
and al,11011111b
mov [bx][si+3],al
inc si
loop inner
add bx,16
mov si,0
inc di
pop cx;恢復(fù)外層循環(huán)的次數(shù)
loop outer
mov ax,4c00h
int 21h
code ends
end start
第八章 數(shù)據(jù)處理的兩個(gè)基本問(wèn)題
本章對(duì)前面的所有內(nèi)容是具有總結(jié)性的
計(jì)算機(jī)是進(jìn)行數(shù)據(jù)處理、運(yùn)算的機(jī)器,那么有兩個(gè)基本的問(wèn)題就包含在其中:
1.處理的數(shù)據(jù)在什么地方?
2.要處理的數(shù)據(jù)有多長(zhǎng)?
這兩個(gè)問(wèn)題,在機(jī)器指令中必須給以明確或隱含的說(shuō)明,否則計(jì)算機(jī)就無(wú)法工作
8.1 bx、si、di、bp
1.在8086CPU中,只有這4個(gè)寄存器(bx、bp、si、di)可以用在“[...]”
中,用來(lái)進(jìn)行內(nèi)存單元的尋址
2.在“[...]”中,這四個(gè)寄存器(bx、bp、si、di)可以單個(gè)出現(xiàn),
或者只能以以下4種組合出現(xiàn)
1.bx和si
2.bx和di
3.bp和si
4.bp和di
3.錯(cuò)誤的用法
mov ax,[bx+bp]
mov ax,[si+di]
4.只要在[...]中使用寄存器bp,則指令中沒(méi)有顯性給出段地址,那么
段地址就默認(rèn)在ss中,比如:
mov ax,[bp] ax的值為??臻g中,偏移地址為bp的內(nèi)存單元
mov ax,[bp+常數(shù)]
mov ax,[bp+si]
mov ax,[bp+si+常數(shù)]
8.2 機(jī)器指令處理的數(shù)據(jù)所在的位置
1.絕大部分機(jī)器指令進(jìn)行數(shù)據(jù)處理的指令大致可分為3大類(lèi)
讀取、寫(xiě)入、運(yùn)算
2.在機(jī)器指令這一層,并不關(guān)心數(shù)據(jù)的值是多少,而關(guān)心指令執(zhí)行前一刻
它將要處理的數(shù)據(jù)所在的位置
3.指令在執(zhí)行前,所要處理的數(shù)據(jù)可以在三個(gè)地方
CPU內(nèi)部(寄存器)、內(nèi)存、端口
8.3 匯編語(yǔ)言中數(shù)據(jù)位置的表達(dá)
匯編語(yǔ)言中用三個(gè)概念來(lái)表達(dá)數(shù)據(jù)的位置
1.立即數(shù)
2.寄存器
3.段地址(SA)和偏移地址(EA)
1.存放段地址的寄存器可以是默認(rèn)的,
既可以是默認(rèn)在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 指令要處理的數(shù)據(jù)有多長(zhǎng)?
1.8086CPU的指令,可以處理兩種尺寸的數(shù)據(jù),byte和word
所以在機(jī)器指令中要指明,指令進(jìn)行的是字操作還是字節(jié)操作
2.8086CPU確定數(shù)據(jù)長(zhǎng)度的幾種方法
1.通過(guò)寄存器名指明要處理的數(shù)據(jù)的尺寸
mov al,1 ;指明數(shù)據(jù)是字節(jié)型的
mov bx,ds:[0] ;指明數(shù)據(jù)是字型的
2.在沒(méi)有寄存器名存在的情況下,用操作符X ptr指明內(nèi)存單元的長(zhǎng)度
X在匯編指令中可以為word或byte
1.下面的指令中,用byte ptr指明了指令訪問(wèn)的內(nèi)存單元是字節(jié)型單元
mov byte ptr ds:[0],1
inc byte ptr [bx]
inc byte ptr ds:[0]
add byte ptr [bx],2
2.下面的指令中,用word ptr指明了指令訪問(wèn)的內(nèi)存單元是字型單元
mov word ptr ds:[0],1
inc word ptr [bx]
inc word ptr ds:[0]
add word ptr [bx],2
3.其他方法
有些指令默認(rèn)了訪問(wèn)的內(nèi)存單元類(lèi)型
pop、push指令,一定是字型數(shù)據(jù)
3.在沒(méi)有寄存器參與的內(nèi)存單元訪問(wèn)指令中,用word ptr或者byte ptr
顯性地指明所要訪問(wèn)的內(nèi)存單元的長(zhǎng)度,是非常有必須要的
否則,CPU無(wú)法得知所要訪問(wèn)的單元是字單元,還是字節(jié)單元
8.6 尋址方式的綜合應(yīng)用
8.7 div指令
1.div是除法指令(division),使用div作除法的時(shí)候,要求
1.除數(shù):8位或16位,在寄存器或內(nèi)存單元中
2.被除數(shù):(默認(rèn))放在AX或DX和AX中
3.除數(shù)與被除數(shù)的相互關(guān)系
除數(shù) 被除數(shù)
8位 16位(AX)
16位 32位(DX+AX)
4.結(jié)果存放的位置
運(yùn)算 8位 16位
商 AL AX
余數(shù) AH DX
2.div指令格式
1.div 寄存器
2.div 內(nèi)存單元
除數(shù)是寄存器或內(nèi)存單元的內(nèi)容
3.div指令示例
1.div byte ptr ds:[0] ;被除數(shù)是16位,除數(shù)是ds:[0]的內(nèi)容(8位)
含義:(al)=(ax)/((ds)*16+0)的商
(ah)=(ax)/((ds)*16+0)的余數(shù)
2.div word ptr es:[0] ;被除數(shù)是32位,除數(shù)是es:[0]的內(nèi)容(16位)
含義:(ax)=[(dx)*10000H+(ax)]/((es)*16+0)的商
(dx)=[(dx)*10000H+(ax)]/((es)*16+0)的余數(shù)
4.利用除法指令計(jì)算100001/100
1.被除數(shù)100001大于65535,要使用dx和ax兩個(gè)寄存器聯(lián)合存放
即說(shuō)要進(jìn)行的16位的除法
2.除數(shù)100小于255,可以在一個(gè)8位寄存器中存放,但是,因?yàn)楸怀龜?shù)是32位
除數(shù)應(yīng)為16位,所以要用16位寄存器來(lái)存放除法100
3.現(xiàn)將100001表示成十六進(jìn)制數(shù):186A1H,即dx中存放1H,ax中存放86A1H
mov dx,1
mov ax,86A1H
mov bx,100
div bx ;默認(rèn)除數(shù)是16位的
8.8 偽指令dd
1.dd是用來(lái)定義雙字型數(shù)據(jù)的
2.示例
data segment
db 1 ;字節(jié)型數(shù)據(jù)
dw 1 ;字型數(shù)據(jù)
dd 1 ;雙字型數(shù)據(jù)
data ends
3.已知data段數(shù)據(jù),用div計(jì)算data中第一個(gè)數(shù)據(jù)除以第二個(gè)數(shù)據(jù)后的結(jié)果,
商存放在第3個(gè)數(shù)據(jù)的內(nèi)存單元中
assume cs:code,ds:data
data segment
dd 100001
dw 100
dw 0
data ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov ax,[bx] ;低位存放在ax中
mov dx,[bx+2] ;高位存放在dx中
div word ptr [bx+4]
mov [bx+6],ax ;商存放在ax中,把a(bǔ)x中的內(nèi)容放入內(nèi)存中
mov ax,4c00h
int 21h
code ends
end start
8.9 dup
1.dup是一個(gè)操作符,在匯編語(yǔ)言中,同db、dw、dd等一樣,也是有編譯器識(shí)別處理的符號(hào)
2.dup和db、dw、dd等數(shù)據(jù)定義偽指令配合使用的,用來(lái)進(jìn)行數(shù)據(jù)的重復(fù)
3.dup示例
1.db 3 dup(0) ;定義了3個(gè)字節(jié),他們的值都是0
2.db 3 dup(0,1,2) ;定義了9個(gè)字節(jié),他們是0、1、2、0、1、2、0、1、2
3.db 3 dup('abc','ABC') ;定義了18個(gè)字節(jié),相當(dāng)于db'abcABCabcABCabcABC'
4.dup的使用格式
db 重復(fù)的次數(shù) dup(重復(fù)的字節(jié)型數(shù)據(jù))
dw 重復(fù)的次數(shù) dup(重復(fù)的字型數(shù)據(jù))
dd 重復(fù)的次數(shù) dup(重復(fù)的雙字型數(shù)據(jù))
【實(shí)驗(yàn)七】
沒(méi)調(diào)試成功
assume cs:code,ds:data,ss:stack,es:table
stack segment
;空棧時(shí),sp指向16
dw 8 dup(0)
stack ends
data segment
;表示21年的21個(gè)字符串
;起始地址0,終止地址21*4-1:83
db '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個(gè)雙字型數(shù)據(jù)
;起始地址21*4:84,終止地址21*4+21*4-1:167
dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
;表示21年公司雇員人數(shù)的21個(gè)字型數(shù)據(jù)
;起止地址21*8:168,終止地址21*8+21*2-1:209
dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
dw 11542,14430,15257,17800
data ends
table segment
db 21 dup('year summ ne ?? ')
table ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,table
mov es,ax
mov ax,stack
mov ss,ax
mov sp,16
mov si,0
mov di,0
mov bx,0
mov bp,0
mov cx,21
outer:
push si
add si,si
mov ax,ds:[bp]
mov es:[bx][di],ax
mov ax,ds:84[bp]
mov es:[bx][di+5],ax
pop si
mov al,168[si]
mov es:[bx][di+10],al
inc si
add di,2
push si
add si,si
mov ax,ds:[bp]
mov es:[bx][di],ax
mov ax,ds:84[bp]
mov es:[bx][di+5],ax
pop si
mov al,168[si]
mov es:[bx][di+10],al
inc si
add di,2
add bx,16
loop outer
mov ax,4c00h
int 21h
code ends
end start
第九章 轉(zhuǎn)移指令的原理
8086CPU的轉(zhuǎn)移指令分為以下幾類(lèi):
1.無(wú)條件跳轉(zhuǎn)指令(如:jmp)
2.條件跳轉(zhuǎn)指令
3.循環(huán)指令(如:loop)
4.過(guò)程,就像C語(yǔ)言中的函數(shù)
5.中斷
9.1 操作符offset
操作符offset在匯編語(yǔ)言中由編譯器處理,它的功能是取標(biāo)號(hào)的偏移地址
如:s:mov ax,offset s
9.2 jmp指令
1.無(wú)條件轉(zhuǎn)移,可以只修改ip,也可以同時(shí)修改cs和ip
1.【jmp 段地址:偏移地址】 可以用來(lái)同時(shí)修改CS和IP
指令中的段地址修改CS
偏移地址修改IP
這種用法編譯器不認(rèn)識(shí),只能做在debug中使用
2.【jmp 某一合法的寄存器】 僅修改IP的內(nèi)容
比如:jmp ax 或者 jmp bx(類(lèi)似于mov IP ax)
2.jmp指令要給出兩種信息:
1.轉(zhuǎn)移的目的地址
2.轉(zhuǎn)移的距離(段間轉(zhuǎn)移、段內(nèi)短轉(zhuǎn)移、段內(nèi)近轉(zhuǎn)移)
9.3 依據(jù)位移進(jìn)行轉(zhuǎn)移的jmp指令
1.jmp short 標(biāo)號(hào)【轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行指令,段內(nèi)短轉(zhuǎn)移】
此格式實(shí)現(xiàn)的是:段內(nèi)短轉(zhuǎn)移,它對(duì)ip的修改范圍為-128~127
2.也就是說(shuō),它向前轉(zhuǎn)移時(shí)可以最多越過(guò)128個(gè)字節(jié),負(fù)數(shù)使用補(bǔ)碼表示
向后轉(zhuǎn)移可以最多越過(guò)127個(gè)字節(jié)
3.CPU不需要目的地址就可以實(shí)現(xiàn)對(duì)ip的修改
jmp指令的機(jī)器碼中不包含目的地址,但是可以實(shí)現(xiàn)跳轉(zhuǎn)
實(shí)現(xiàn)的方式,是在原地址的基礎(chǔ)上進(jìn)行一個(gè)偏移量,即位移
4.還有一種和指令“jmp short 標(biāo)號(hào)”功能類(lèi)似的指令格式:
jmp near ptr 標(biāo)號(hào),它實(shí)現(xiàn)的是段內(nèi)近轉(zhuǎn)移
功能為:(ip)=(ip)+16位位移
jmp short 標(biāo)號(hào)是8位的位移,而jmp near ptr 標(biāo)號(hào)是16位位移
9.4 轉(zhuǎn)移的目的地址在指令中的jmp指令
前面講的jmp指令,其對(duì)應(yīng)的機(jī)器碼中并沒(méi)有轉(zhuǎn)移的目的地址,而是相對(duì)于當(dāng)前ip的轉(zhuǎn)移位移
1.指令“jmp far ptr 標(biāo)號(hào)”
實(shí)現(xiàn)的是段間轉(zhuǎn)移,又稱(chēng)為遠(yuǎn)轉(zhuǎn)移,這時(shí)機(jī)器碼中應(yīng)該明確給出【段地址】
2.指令“jmp far ptr 標(biāo)號(hào)”功能如下:
(CS)=標(biāo)號(hào)所在段的段地址
(IP)=標(biāo)號(hào)所在段中的偏移地址
far ptr 指明了指令用標(biāo)號(hào)的段地址和偏移地址修改cs和ip
9.5 轉(zhuǎn)移地址在寄存器中的jmp指令
指令格式:jmp 16位寄存器
功能:修改ip寄存器中的值,把16位寄存器中的值送入到ip寄存器中
9.6 轉(zhuǎn)移地址在內(nèi)存中的jmp指令
轉(zhuǎn)移地址在內(nèi)存中的jmp指令有兩種格式:
1.jmp word ptr 內(nèi)存單元地址(段內(nèi)轉(zhuǎn)移)
功能:將內(nèi)存中的那個(gè)字視為一個(gè)偏移地址,然后跳轉(zhuǎn)到那個(gè)偏移地址
與【jmp 寄存器】功能相似
內(nèi)存單元地址可用尋址方式的任意格式給出
2.jmp dword ptr 內(nèi)存單元地址(段間轉(zhuǎn)移)
(ip)=(內(nèi)存單元地址) ;雙字中的低位字是給ip的
(cs)=(內(nèi)存單元地址+2) ;雙字中的高位字是給cs的
跟【jmp 段地址:偏移地址】功能類(lèi)似
內(nèi)存單元地址可用尋址方式的任意格式給出
**補(bǔ)充:不能直接向內(nèi)存單元中加入立即數(shù)
要通過(guò)寄存器,把立即數(shù)加進(jìn)去
9.7 jcxz指令
1.有條件跳轉(zhuǎn)指令,所有的有條件跳轉(zhuǎn)指令都是短轉(zhuǎn)移
對(duì)應(yīng)的機(jī)器碼中包含轉(zhuǎn)移的位移,而不是目的地址。對(duì)ip的修改范圍都為:-128~127
**另一個(gè)有條件跳轉(zhuǎn)指令【loop指令】
2.指令格式:jcxz 標(biāo)號(hào)
如果(cx)=0,則跳轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行
3.jcxz 標(biāo)號(hào) 指令的操作:
1.當(dāng)(cx)=0時(shí),(ip)=(ip)+8位位移
2.當(dāng)(cx)!=0時(shí),什么也不做(程序繼續(xù)向下執(zhí)行)
9.8 loop指令
1.循環(huán)指令,所有的循環(huán)指令都是短轉(zhuǎn)移,在對(duì)應(yīng)的機(jī)器碼中包含轉(zhuǎn)移的位移
2.指令格式:loop 標(biāo)號(hào)
3.指令的內(nèi)部操作
1.cx=cx-1
2.如果cx!=0,(ip)=(ip)+8位位移,跳轉(zhuǎn)
3.(cx)=0,什么也不做,程序向下執(zhí)行
cx用來(lái)控制循環(huán)的次數(shù)
9.9 根據(jù)位移進(jìn)行轉(zhuǎn)移的意義
1.根據(jù)位移進(jìn)行轉(zhuǎn)移,這樣設(shè)計(jì),方便了程序段在內(nèi)存中的浮動(dòng)裝配
可以實(shí)現(xiàn)代碼的復(fù)用
2.如果在機(jī)器碼中直接給出【段地址:偏移地址】,
這段程序在內(nèi)存中換一個(gè)位置,則會(huì)運(yùn)行不正確
3.段內(nèi)近轉(zhuǎn)移、段內(nèi)短轉(zhuǎn)移都是根據(jù)位移進(jìn)行轉(zhuǎn)移,一共有四種方式
1.jmp short ptr 標(biāo)號(hào)
2.jmp near ptr 標(biāo)號(hào)
3.jcxz 標(biāo)號(hào)
4.loop 標(biāo)號(hào)
9.10 編譯器對(duì)轉(zhuǎn)移位移超界的檢測(cè)
注意,根據(jù)位移進(jìn)行轉(zhuǎn)移的指令,他們的轉(zhuǎn)移范圍會(huì)受到限制
如果在源程序中出現(xiàn)了轉(zhuǎn)移范圍超界的問(wèn)題,在編譯的時(shí)候,編譯器將報(bào)錯(cuò)
【實(shí)驗(yàn)八、九】【這個(gè)實(shí)驗(yàn)要重點(diǎn)看】
第十章 call和ret指令
call和ret指令都是轉(zhuǎn)移指令,它們都能修改ip,或同時(shí)修改cs和ip
10.1 ret和ref
1.ret指令用棧中的數(shù)據(jù),修改ip的內(nèi)容,從而實(shí)現(xiàn)【近轉(zhuǎn)移】
CPU執(zhí)行ret指令時(shí),進(jìn)行下面兩步操作:
1.(ip)=((ss)*16+(sp)) ;ip的值修改為棧頂?shù)膬?nèi)容
2.(sp)=(sp)+2 ;棧頂移動(dòng)
2.retf指令用棧中的數(shù)據(jù),修改cs和ip的內(nèi)容,從而實(shí)現(xiàn)【遠(yuǎn)轉(zhuǎn)移】
CPU執(zhí)行retf指令時(shí),進(jìn)行下面四步操作
1.(ip)=((ss)*16+(sp)) ;ip的內(nèi)容修改為棧頂?shù)膬?nèi)容
2.(sp)=(sp)+2 ;棧頂移動(dòng)
3.(cs)=((ss)*16+(sp)) ;cs的內(nèi)容修改為棧頂移動(dòng)之后,棧頂?shù)膬?nèi)容
4.(sp)=(sp)+2 ;棧頂移動(dòng)
棧頂?shù)膬蓚€(gè)字,低位字修改為ip,高位字修改為cs
3.可以看出,如果我們用匯編語(yǔ)法來(lái)解釋ret和retf指令,則
1.CPU執(zhí)行ret指令,相當(dāng)于
pop ip
2.執(zhí)行retf指令時(shí),相當(dāng)于
pop ip
pop cs
10.2 call指令
1.call指令經(jīng)常跟ret指令配合使用,因此CPU執(zhí)行call指令,進(jìn)行兩步操作:
1.將當(dāng)前的ip或cs和ip壓入棧中
2.轉(zhuǎn)移
2.call指令不能實(shí)現(xiàn)短轉(zhuǎn)移,除此之外,
call指令實(shí)現(xiàn)轉(zhuǎn)移的方法和jmp指令的原理相同
【依據(jù)位移進(jìn)行轉(zhuǎn)移的call指令】
3.CPU執(zhí)行“call 標(biāo)號(hào)”這種格式的call指令時(shí),進(jìn)行如下操作:
1.(sp)=(sp)-2 ;棧頂移動(dòng)
2.((ss)*16+(sp))=(ip) ;當(dāng)前ip內(nèi)容壓棧
3.(ip)=(ip)+16位位移 ;跳轉(zhuǎn)到標(biāo)號(hào)處
4.call指令格式:call 標(biāo)號(hào)
相當(dāng)于執(zhí)行:
push ip
jmp near ptr 標(biāo)號(hào)
10.4 轉(zhuǎn)移的目的地址在指令中的call指令
1.指令格式:call far ptr 標(biāo)號(hào)
實(shí)現(xiàn)的是段間轉(zhuǎn)移
2.執(zhí)行這種格式的call指令時(shí)CPU的操作
1.(sp)=(sp)-2 ;棧頂移動(dòng)
2.((ss)×16+(sp))=(cs) ;先把cs壓棧
3.(sp)=(sp)-2 ;棧頂移動(dòng)
4.((ss)×16+(sp))=(ip) ;然后把ss壓棧
3.CPU執(zhí)行“call far ptr 標(biāo)號(hào)”時(shí),相當(dāng)于進(jìn)行
push cs
push ip
jmp far ptr 標(biāo)號(hào)
10.5 轉(zhuǎn)移地址在寄存器中的call指令
1.指令格式:call 16位寄存器
2.執(zhí)行這種指令時(shí),在CPU中的操作
1.(sp)=(sp)-2
2.((ss)×16+(sp))=(ip)
3.(ip)=(16位寄存器)
3.相當(dāng)于
push ip
jmp 16位寄存器
10.6 轉(zhuǎn)移地址在內(nèi)存中的call指令
轉(zhuǎn)移地址在內(nèi)存中的call指令有兩種格式:
1.call word ptr 內(nèi)存單元地址
匯編語(yǔ)法解釋
push ip
jmp word ptr 內(nèi)存單元地址
2.call dword ptr 內(nèi)存單元地址
匯編語(yǔ)法解釋
push cs ;cs存放在高位
push ip ;ip存放在低位
jmp dword ptr 內(nèi)存單元地址
10.7 call和ret的配合使用
10.8 mul指令
相乘的兩個(gè)數(shù);要么都是8位,要么都是16位
1.8位:AL中和8位寄存器或內(nèi)存字節(jié)單元中
AL中的內(nèi)容作為被乘數(shù)
結(jié)果放在AX中
2.16位:AX中和16位寄存器或內(nèi)存字單元中
AX中的內(nèi)容作為被乘數(shù)
結(jié)果放在DX(高位)和AX(低位)中。
3.格式如下:
mul 寄存器
mul 內(nèi)存單元(byte ptr或 word ptr指明是字還是字節(jié))
10.9 模塊化程序設(shè)計(jì)
10.10 參數(shù)和結(jié)果傳遞的問(wèn)題
【編程】計(jì)算data段中第一組數(shù)據(jù)的3次方,結(jié)果保存在后面一組dword單元中
data sgement
dw 1,2,3,4,5,6,7,8
dd 0,0,0,0,0,0,0,0
data ends
10.11 批量數(shù)據(jù)的傳遞
使用寄存器、內(nèi)存、棧傳遞數(shù)據(jù)
【編程】將一個(gè)全是字母,以0結(jié)尾的字符串,轉(zhuǎn)化為大寫(xiě)
【實(shí)驗(yàn)十 編寫(xiě)子程序】
1.顯示字符串
2.解決除法溢出問(wèn)題
3.數(shù)值顯示
【課程設(shè)計(jì)1】
第十一章 標(biāo)志寄存器
8086CPU的標(biāo)志寄存器有16位,其中存儲(chǔ)的信息通常被稱(chēng)為程序狀態(tài)字(PSW)
本章中的標(biāo)志寄存器(以下簡(jiǎn)稱(chēng)為flag)是我們要學(xué)習(xí)的最有一個(gè)寄存器
flag寄存器是按位起作用的,也就是說(shuō),它的每一位都有專(zhuān)門(mén)的含義,記錄特定的信息
8086CPU的flag寄存器的結(jié)構(gòu):
1.flag的1、3、4、12、13、14、15位共7位在8086CPU中沒(méi)有使用,不具有任何含義
而0、2、4、6、7、8、9、10、11位共9位都具有特殊的含義
2.示意圖
11.1 ZF標(biāo)志
1.flag的第6位是ZF,零標(biāo)志位。
它記錄相關(guān)指令執(zhí)行后,
1.結(jié)果為0,ZF=1
2.結(jié)果不為0,ZF=0
2.示例:
mov ax,1
sub ax,1
指令執(zhí)行后,結(jié)果為0,則ZF=1
mov ax,2
sub ax,1
指令執(zhí)行后,結(jié)果不為0,則ZF=0
3.注意,在8086CPU的指令集中,有的指令的執(zhí)行會(huì)影響標(biāo)志寄存器
比如:add、sub、mul、div、inc、or、and等
他們大都是運(yùn)算指令(邏輯運(yùn)算或者算術(shù)運(yùn)算)
有的指令的執(zhí)行對(duì)標(biāo)志寄存器沒(méi)有影響,
比如:mov、push、pop等,他們大都是傳送指令
11.2 PF標(biāo)志
flag的第2位是PF,奇偶標(biāo)志位
它記錄指令執(zhí)行后,結(jié)果的所有二進(jìn)制位中1的個(gè)數(shù)
1.為偶數(shù),PF=1
2.為奇數(shù),PF=0
11.3 SF標(biāo)志
1.flag的第7位是SF,符號(hào)標(biāo)志位
2.它記錄指令執(zhí)行后
1.結(jié)果為負(fù)。sf=1
2.結(jié)果為正,sf=0
sf標(biāo)志,就是CPU對(duì)有符號(hào)數(shù)運(yùn)算結(jié)果的一種記錄,它記錄數(shù)據(jù)的正負(fù)
sf標(biāo)志把所有數(shù)當(dāng)作有符號(hào)數(shù)
如果把數(shù)據(jù)當(dāng)作無(wú)符號(hào)數(shù)運(yùn)算,sf的值則沒(méi)有意義,雖然相關(guān)指令會(huì)影響它的值
3.也就是說(shuō),CPU在執(zhí)行add等指令時(shí),是必然要影響sf標(biāo)志位的值
至于我們需不需要這種影響,那就看我們?nèi)绾慰创噶钏M(jìn)行的運(yùn)算
11.4 CF標(biāo)志
1.flag的第0位是CF,進(jìn)位標(biāo)志位
一般請(qǐng)況下,在進(jìn)行無(wú)符號(hào)數(shù)運(yùn)算的時(shí)候,
它記錄了運(yùn)算結(jié)果的最高有效位向更高位的進(jìn)位值,
或從更高位的借位值
代表假想的更高位
2.CPU在運(yùn)算時(shí),不會(huì)丟棄進(jìn)位值,而是記錄在一個(gè)特殊的寄存器的某一位上
8086CPU就用flag的cf為來(lái)記錄這個(gè)進(jìn)位值,借位也一樣
3.在debug中的顯示
4.無(wú)符號(hào)的時(shí)候產(chǎn)生的結(jié)果
11.5 OF標(biāo)志
flag中的第11位
進(jìn)行有符號(hào)數(shù)運(yùn)算的時(shí)候,如果結(jié)果超過(guò)了機(jī)器所能表示的范圍稱(chēng)為溢出
1.這里所講的溢出,只是對(duì)有符號(hào)數(shù)運(yùn)算而言
就像進(jìn)位只是相對(duì)于無(wú)符號(hào)數(shù)而言!
2.一定要注意cf和of的區(qū)別
當(dāng)需要把機(jī)器碼看成有符號(hào)數(shù)則使用of
當(dāng)需要把機(jī)器碼看成無(wú)符號(hào)數(shù)則使用cf
11.6 adc標(biāo)志
adc是帶進(jìn)位的加法指令,他利用了cf上記錄的進(jìn)位值
1.格式:adc 操作對(duì)象1,操作對(duì)象2
2.功能:操作對(duì)象1=操作對(duì)象1+操作對(duì)象2+cf
比如:adc ax,bx實(shí)現(xiàn)的功能是:
(ax)=(ax)+(bx)+cf
3.執(zhí)行adc指令的時(shí)候,加上的cf的值的含義,由adc指令前的指令決定
也就是說(shuō),關(guān)鍵在于所加上的cf值是被什么指令設(shè)置的
4.如果cf是被sub指令設(shè)置的,那么他的含義就是借位值
如果是被add指令設(shè)置的,那么它的含義就是進(jìn)位值
5.下面的指令和add ax,bx具有相同的結(jié)果
add al,bl
adc ah,bh
CPU提供adc指令的目的,就是來(lái)進(jìn)行加法的第二步運(yùn)算的
adc指令和add指令相配合就可以對(duì)更大的數(shù)據(jù)進(jìn)行加法運(yùn)算
【實(shí)驗(yàn):編程計(jì)算1EF000H+201000H,結(jié)果放在ax(高16位)和bx(低16位)中】
11.7 sbb標(biāo)志
sbb是帶借位減法指令,他利用了cf位上記錄的借位值
1.格式:sbb 操作對(duì)象1,操作對(duì)象2
2.功能:操作對(duì)象1=操作對(duì)象1-操作對(duì)象2-cf
3.利用sbb指令,我們可以對(duì)任意大的數(shù)據(jù)進(jìn)行減法運(yùn)算
4.sbb和adc是基于相同的思想設(shè)計(jì)的兩條指令,
在應(yīng)用思路上和adc類(lèi)似
11.8 cmp標(biāo)志
1.cmp是比較指令,功能相當(dāng)于減法指令,只是不保存結(jié)果
2.cmp指令執(zhí)行后,將對(duì)標(biāo)志寄存器產(chǎn)生影響
3.其他相關(guān)指令通過(guò)識(shí)別這些被影響的標(biāo)志寄存器,來(lái)得知比較結(jié)果
4.cmp指令格式:cmp 操作對(duì)象1,操作對(duì)象2
5.功能:計(jì)算操作對(duì)象1-操作對(duì)象2,但并不保存結(jié)果,僅僅根據(jù)計(jì)算結(jié)果對(duì)標(biāo)志寄存器進(jìn)行設(shè)置
6.比如:cmp ax,ax
做(ax)-(ax)的運(yùn)算,結(jié)果為0,但并不在ax中保存,僅影響flag的相關(guān)位
指令執(zhí)行后
zf=1 ;結(jié)果為0
pf=1 ;結(jié)果的1的個(gè)數(shù)為偶數(shù)
sf=0 ;結(jié)果為正號(hào)
cf=0 ;結(jié)果沒(méi)有產(chǎn)生進(jìn)位或借位
of=0 ;結(jié)果沒(méi)有溢出
7.根據(jù)flag,判斷cmp指令的結(jié)果(無(wú)符號(hào)數(shù))
8.cmp既可以對(duì)無(wú)符號(hào)數(shù)進(jìn)行比較,也可以對(duì)有符號(hào)數(shù)進(jìn)行比較
cmp 操作數(shù)1,操作數(shù)2 ;操作數(shù)1、操作數(shù)2都是有符號(hào)數(shù)
1.of=0,說(shuō)明沒(méi)有溢出,邏輯上真正結(jié)果的正負(fù)=實(shí)際結(jié)果的正負(fù)
of=0,sf=1 則 操作數(shù)1比操作數(shù)2小
of=0,sf=0 則 操作數(shù)1比操作數(shù)2大
2.of=1,說(shuō)明有溢出,邏輯上真正結(jié)果的正負(fù)與實(shí)際結(jié)果的正負(fù)相反
of=1,sf=1 則 操作數(shù)1比操作數(shù)2大
of=1,sf=0 則 操作數(shù)1比操作數(shù)2小
11.9 檢測(cè)比較結(jié)果的條件轉(zhuǎn)移指令
1.這些條件轉(zhuǎn)移指令通常和cmp相配合使用
2.因?yàn)閏mp指令可以同時(shí)進(jìn)行兩種比較,無(wú)符號(hào)數(shù)和有符號(hào)數(shù)的比較
所以,這些轉(zhuǎn)移指令也分為兩種,即:
1.根據(jù)【無(wú)符號(hào)數(shù)】的比較結(jié)果進(jìn)行轉(zhuǎn)移的條件轉(zhuǎn)移指令,
他們檢測(cè)zf、cf的值
2.根據(jù)【有符號(hào)數(shù)】的比較結(jié)果進(jìn)行轉(zhuǎn)移的條件轉(zhuǎn)移指令
他們檢測(cè)sf、of和zf的值
3.無(wú)符號(hào)比較,條件轉(zhuǎn)移指令小結(jié)【無(wú)符號(hào),6個(gè)】
1.je 等于則轉(zhuǎn)移 zf=1
2.jne 不等于則轉(zhuǎn)移 zf=0
3.jb 低于則轉(zhuǎn)移 cf=1 【b表示below】
4.jnb 不低于則轉(zhuǎn)移 cf=0
5.ja 高于則轉(zhuǎn)移 cf=0,zf=0【a表示above】
6.jna 不高于則轉(zhuǎn)移 cf=1或zf=1
11.10 DF標(biāo)志和串傳送指令
1.flag的第10位DF,方向標(biāo)志位
在串處理指令(movsb,movsw)中,控制每次操作后si、di的增減
df=0:每次操作后si,di遞增
df=1:每次操作后si,di遞減
2.格式:movsb
3.功能:(以字節(jié)為單位傳送)
1.((es)*16+(di))=((ds)*16+(si))
2.如果df=0,則:(si)=(si)+1
(di)=(di)+1
如果df=1,則:(si)=(si)-1
(di)=(di)-1
3.功能文字描述
movsb的功能是將ds:si指向的內(nèi)存單元中的字節(jié)
送入es:di中,然后根據(jù)標(biāo)志寄存器df位的值,
將si和di遞增或遞減
4.movsw 傳送一個(gè)字
5.movsb和movsw都和rep配合使用
格式:rep movsb
rep的作用根據(jù)cx的值,重復(fù)執(zhí)行后面的串傳送指令
6.cld指令和std指令
cld指令:將標(biāo)志寄存器的df置為0【c:clear】
std指令:將標(biāo)志寄存器的df置為1【s:set】
11.11 pushf和popf
pushf:將標(biāo)志寄存器的值壓棧
popf:從棧中彈出數(shù)據(jù),送入標(biāo)志寄存器中
pushf和popf為直接訪問(wèn)標(biāo)志寄存器提供了一種方法
11.12 標(biāo)志寄存器在debug中的表示
第十二章 內(nèi)中斷
**引言和簡(jiǎn)介
1.中斷是CPU處理外部突發(fā)事件的一個(gè)重要技術(shù)
2.它能使CPU在運(yùn)行過(guò)程中對(duì)外部事件發(fā)出的中斷請(qǐng)求及時(shí)地進(jìn)行處理,處理完成后
又立即返回?cái)帱c(diǎn),繼續(xù)進(jìn)行CPU原來(lái)的工作。
3.引起中斷的原因【即:發(fā)出中斷請(qǐng)求的來(lái)源叫作中斷源】
4.根據(jù)中斷源的不同,可以把中斷分為:【軟件中斷】和【硬件中斷】?jī)纱箢?lèi)
而硬件中斷又可以分為【外部中斷】和【內(nèi)部中斷】?jī)深?lèi)
12.1 內(nèi)中斷的產(chǎn)生
1.外部中斷一般是指計(jì)算機(jī)外設(shè)發(fā)出的中斷請(qǐng)求,如:鍵盤(pán)中斷、打印機(jī)中斷、定時(shí)器中斷。
外部中斷是可以屏蔽的中斷,也就是說(shuō),利用中斷控制器可以屏蔽這些外部設(shè)備的中斷請(qǐng)求。
2.內(nèi)部中斷是指因硬件出錯(cuò)(如突然掉電、奇偶校驗(yàn)錯(cuò)等)或運(yùn)算出錯(cuò)(除數(shù)為零、運(yùn)算溢出、單步中斷)所引起的中斷。
內(nèi)部中斷是不可屏蔽的中斷
3.軟件中斷其實(shí)并不是真正的中斷,他們只是可被調(diào)用執(zhí)行的一般程序,
DOS的系統(tǒng)功能調(diào)用(int 21h)都是軟件中斷
4.CPU為了處理并發(fā)的中斷請(qǐng)求,規(guī)定了中斷的優(yōu)先權(quán),優(yōu)先權(quán)由高到低的順序是:
1.除法錯(cuò)、溢出中斷、軟件中斷
2.不可屏蔽中斷
3.可屏蔽中斷
4.單步中斷
12.2 中斷處理程序簡(jiǎn)介
1.CPU的設(shè)計(jì)者必須在中斷信息和其處理程序的入口地址之間建立某種聯(lián)系
使得CPU根據(jù)中斷信息可以找到要執(zhí)行的處理程序。
2.中斷信息中包含有表示中斷的類(lèi)型碼。根據(jù)CPU的設(shè)計(jì),中斷類(lèi)型碼的作用就是用來(lái)定位中斷處理程序的。
3.CPU用8位的中斷類(lèi)型碼通過(guò)中斷向量表找到相應(yīng)的中斷處理程序的入口地址
即中斷類(lèi)型碼是中斷向量在中斷向量表中的索引
12.3 中斷向量表【中斷向量表就是中斷向量的列表】
1.中斷向量表在內(nèi)存中保存,其中存放著256個(gè)【2^8,8位中斷類(lèi)型碼】中斷源所對(duì)應(yīng)的中斷處理程序的入口
對(duì)于8086PC機(jī),中斷向量表指定放在內(nèi)存地址0處
2.從0:0-0:03ffh的1024個(gè)字節(jié)【256*4,物理地址使用段地址和偏移地址存放,需要4個(gè)字節(jié)】中存放著中斷向量表
12.4 中斷過(guò)程
1.可以用中斷類(lèi)型碼,在中斷向量表中找到中斷處理程序的入口
找到這個(gè)入口地址的最終目的是用它設(shè)置cs和ip,使CPU執(zhí)行中斷處理程序
2.用中斷類(lèi)型碼找到中斷向量,并用它設(shè)置cs和ip,這個(gè)工作時(shí)由CPU的硬件自動(dòng)完成的
CPU硬件完成這個(gè)工作的過(guò)程被稱(chēng)為【中斷過(guò)程】
3.中斷過(guò)程
8086CPU的中斷過(guò)程
1.(從中斷信息中)取得中斷類(lèi)型碼
2.標(biāo)志寄存器的值入棧(保護(hù)標(biāo)志位)
3.設(shè)置標(biāo)志寄存器的第8位TF和第9位IF設(shè)置為0(后面講解本步的目的)
4.cs內(nèi)容入棧
5.ip內(nèi)容入棧
6.從內(nèi)存地址為中斷類(lèi)型碼*4和中斷類(lèi)型碼*4+2的兩個(gè)子單元中
讀取中斷處理程序的入口地址設(shè)置cs和ip
4.使用匯編語(yǔ)言描述中斷過(guò)程,如下
1.取得中斷類(lèi)型碼N
2.pushf
3.TF=0,IF=0
4.push cs
5.push ip
6.(ip)=(N*4),(cs)=(N*4+2)
12.5 中斷處理程序
1.由于CPU隨時(shí)都可能檢測(cè)到中斷信息,也就是說(shuō),CPU隨時(shí)都可能執(zhí)行中斷處理程序,
所以,中斷處理程序必須一致存儲(chǔ)在內(nèi)存某段空間中
2.而中斷處理程序的入口地址,即【中斷向量】,必須存儲(chǔ)在對(duì)應(yīng)的中斷向量表表項(xiàng)中
3.中斷處理程序的編寫(xiě)方法和子程序的比較類(lèi)似,下面是常規(guī)的步驟
1.保存用到的寄存器
2.處理中斷
3.恢復(fù)用到的寄存器
4.用iret指令返回
**iret指令的功能用匯編語(yǔ)法描述為
pop ip
pop cs
popf
iret通常和硬件自動(dòng)完成的中斷過(guò)程配合使用
iret指令執(zhí)行后,CPU回到執(zhí)行中斷處理程序前的執(zhí)行點(diǎn)繼續(xù)執(zhí)行程序
12.6 除法錯(cuò)誤中斷的處理
當(dāng)CPU執(zhí)行div等除法指令的時(shí)候,如果發(fā)生了除法溢出錯(cuò)誤,將產(chǎn)生中斷類(lèi)型碼為0的終端信息
CPU將檢測(cè)到這個(gè)信息,然后引發(fā)中斷程序,轉(zhuǎn)去執(zhí)行0號(hào)中斷對(duì)應(yīng)的中斷處理程序
例如:
mov ax 1000h
mov bh,1
div bh
此程序會(huì)產(chǎn)生溢出
運(yùn)行之后,會(huì)顯示
12.7 編程處理0號(hào)中斷
現(xiàn)在重新編寫(xiě)一個(gè)0號(hào)中斷處理程序,它的功能是在屏幕中間顯示“Welcome to here!”的廣告詞,然后返回到操作系統(tǒng)
把中斷處理程序放到安全空間中
中斷程序的框架
12.8 安裝
計(jì)算中斷程序的長(zhǎng)度:offset 標(biāo)號(hào)1-offset 標(biāo)號(hào)2
在代碼段中存放數(shù)據(jù)
12.9 do0
12.10 設(shè)置中斷向量
12.11 單步中斷
如果檢測(cè)到標(biāo)志寄存器的tf位為1,則產(chǎn)生單步中斷,引發(fā)中斷過(guò)程
12.12 響應(yīng)中斷的特殊情況
第十三章 int指令
13.1 int指令
1.int格式:int n ;n為中斷類(lèi)型碼
它的功能是引發(fā)中斷過(guò)程
2.CPU執(zhí)行int n指令,相當(dāng)于引發(fā)一個(gè)n號(hào)中斷的中斷過(guò)程,執(zhí)行過(guò)程如下
1.取中斷類(lèi)型碼
2.標(biāo)志寄存器入棧,if=0,tf=0
3.cs,ip入棧
4.從此處轉(zhuǎn)去執(zhí)行n號(hào)中斷的中斷處理過(guò)程
3.可以在程序中使用int指令調(diào)用任何一個(gè)中斷的中斷處理程序
可以用int指令調(diào)用這些子程序,也可以自己編寫(xiě)一些中斷處理程序供別人使用
13.2 編寫(xiě)供應(yīng)用程序調(diào)用的中斷例程
【實(shí)例1】編寫(xiě)、安裝中斷7ch的中斷例程,實(shí)現(xiàn)求一個(gè)word型數(shù)據(jù)的平方
1.功能:求一word型數(shù)據(jù)的平方
2.參數(shù):(ax)=要計(jì)算的數(shù)據(jù)
3.返回值:dx、ax中存放結(jié)果的高16位和低16位
4.應(yīng)用舉例:求2*3456^2
;程序1:調(diào)用中斷程序計(jì)算平方
code segment
assume cs: code
start:
mov ax,3456; (ax)=3456
int 7ch;調(diào)用中斷7ch的中斷例程,計(jì)算ax中的數(shù)據(jù)的平方
add ax,ax
adc dx,dx ;存放結(jié)果,講結(jié)果乘以2
mov ax,4c00h
int 21h
code ends
end start
;程序2:編寫(xiě)中斷程序
;程序2中要做三部分工作
; 1.編程實(shí)現(xiàn)求平方功能的程序
; 2.安裝程序,我們將其安裝在0:200處
; 3.設(shè)置中斷向量表,將程序的入口地址保存在7ch表項(xiàng)中,使其成為中斷7ch的中斷例程。
code segment
assume cs:code
start:
mov ax,cs
mov ds,ax
mov si,offset sqr ;設(shè)置ds:si指向源地址
mov ax,0
mov es,ax
mov di,200h ;設(shè)置es:di指向目的地址
mov cx,offset sqrend - offset sqr ;設(shè)置cx為傳輸長(zhǎng)度
cld ;設(shè)置傳輸方向?yàn)檎? rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h ;設(shè)置中斷向量地址,偏移地址
mov word ptr es:[7ch*4+2],0 ;設(shè)置中斷向量地址,段地址
mov ax,4c00h
int 21h
sqr:
mul ax
iret
sqrend: nop
code ends
end start
【實(shí)例2】編寫(xiě)、安裝中斷7ch的中斷例程,實(shí)現(xiàn)將一個(gè)全是字母,以0結(jié)尾的字符串,轉(zhuǎn)化為大寫(xiě)。
code segment
assume cs:code
start:
mov ax,cs
mov ds,ax
mov si,offset capital
mov ax,0
mov es,ax
mov di,200h
mov cx,offset capitalend - offset capital
cld
rep movsb
mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h
mov word ptr es:[7ch*4+2],0
mov ax,4c00h
int 21h
capital:
push cx
push si
change:
mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111b
inc si
jmp short change
ok:
pop si
pop cx
iret
capitalend:
nop
code ends
end start
13.3 對(duì)int、iret和棧的深入理解
【問(wèn)題】用7ch中斷例程完成loop指令的功能
不要隨便修改sp,可以使用bp進(jìn)行間接訪問(wèn)
13.4 BIOS和DOS所提供的中斷例程
13.5 BIOS和DOS中斷例程的安裝過(guò)程
1.開(kāi)機(jī)后,CPU一加電,初始化(cs)=0ffffh,ip=0,自動(dòng)從ffff:0單元開(kāi)始執(zhí)行程序
ffff:0處有一條跳轉(zhuǎn)指令,CPU執(zhí)行該指令后,轉(zhuǎn)去執(zhí)行bios中的硬件系統(tǒng)的檢測(cè)和初始化程序。
2.初始化程序?qū)⒔ios所支持的中斷向量,即將bios提供的中斷例程的入口地址登記在中斷向量表中。
3.硬件系統(tǒng)檢測(cè)和初始化完成后,調(diào)用19h進(jìn)行操作系統(tǒng)的引導(dǎo)。從此將計(jì)算機(jī)交由操作系統(tǒng)控制。
4.DOS啟動(dòng)后,除完成其他工作外,還將它所提供的中斷例程裝入內(nèi)存,并建立相應(yīng)的中斷向量
13.6 BIOS中斷例程的應(yīng)用
1.int 10h中斷例程是bios提供的中斷例程,其中包含了多個(gè)和屏幕輸出相關(guān)的子程序
一般來(lái)說(shuō),一個(gè)供程序員調(diào)用的中斷例程中,往往包括多個(gè)子程序,中斷例程內(nèi)部用傳遞進(jìn)來(lái)的參數(shù)來(lái)決定執(zhí)行哪個(gè)子程序
2.bios和dos提供的中斷例程,都用ah來(lái)傳遞內(nèi)部子程序的編號(hào)
13.7 DOS中斷例程應(yīng)用
int 21h中斷例程是dos提供的中斷例程,其中包含了dos提供給程序員造編程時(shí)調(diào)用的子程序
【實(shí)驗(yàn)13】
**介紹一本匯編語(yǔ)言的書(shū)《The Art of Assembly Language》
第十四章 端口
CPU可以直接讀寫(xiě)3個(gè)地方的數(shù)據(jù)
1.CPU內(nèi)部的寄存器
2.內(nèi)存單元
3.端口
14.1 端口的讀寫(xiě)
1.對(duì)端口的讀寫(xiě)不能用mov、push、pop等內(nèi)存讀寫(xiě)指令
端口的讀寫(xiě)指令只有兩條:【in】和【out】分別用于從端口讀取數(shù)據(jù)和往端口寫(xiě)入數(shù)據(jù)
2.CPU執(zhí)行內(nèi)存訪問(wèn)指令和端口訪問(wèn)指令時(shí),總線上的信息:
1.訪問(wèn)內(nèi)存
mov ax,ds:[8];
假設(shè)執(zhí)行前(ds)=0
執(zhí)行時(shí),與總線相關(guān)的操作:
1.CPU通過(guò)地址線將地址信息8發(fā)出
2.CPU通過(guò)控制線發(fā)出內(nèi)存讀命令,選中存儲(chǔ)器芯片,并通知它,將要從中讀取數(shù)據(jù)
3.存儲(chǔ)器將8號(hào)單元中的數(shù)據(jù)通過(guò)數(shù)據(jù)線送入CPU
2.訪問(wèn)端口
這里的【端口】是對(duì)硬件開(kāi)放的端口
in al,60h; 從60h號(hào)端口讀入一個(gè)字節(jié)
執(zhí)行時(shí)與總線相關(guān)的操作
1.CPU通過(guò)地址線將地址信息60h發(fā)出
2.CPU通過(guò)控制線發(fā)出端口讀命令,選中端口所在的芯片,并通知它,將要從中讀取數(shù)據(jù)
3.端口所在的芯片將60h端口中的數(shù)據(jù)通過(guò)數(shù)據(jù)線送入CPU
**注意:在in和out指令中,只能使用ax或al來(lái)存放從端口中讀入的數(shù)據(jù)或要發(fā)送到端口中的數(shù)據(jù)
訪問(wèn)8位端口時(shí)用al,訪問(wèn)16位端口時(shí)用ax
3.對(duì)0-255以內(nèi)的端口進(jìn)行讀寫(xiě)
in al,20h ;從20h端口讀一個(gè)字節(jié)
out 20h,al ;往20h端口寫(xiě)一個(gè)字節(jié)
4.對(duì)256-65535的端口進(jìn)行讀寫(xiě)時(shí),端口號(hào)放在【dx】中
mov dx,3f8h ;將端口號(hào)3f8送入dx
in al,dx ;從3f8h端口讀一個(gè)字節(jié)
out dx,al ;從3f8h端口寫(xiě)一個(gè)字節(jié)
14.2 CMOS RAM芯片
1.PC機(jī)中有一個(gè)CMOS RAM芯片,其有如下特征
1.包含一個(gè)實(shí)時(shí)鐘和一個(gè)有128個(gè)存儲(chǔ)單元的RAM存儲(chǔ)器。(早期的計(jì)算機(jī)為64字節(jié))
2.該芯片靠電池供電。因此,關(guān)機(jī)后其內(nèi)部的實(shí)時(shí)鐘仍可以正常工作,RAM中的信息不丟失
3.128字節(jié)的RAM中,內(nèi)部實(shí)時(shí)鐘占用0-0dh單元來(lái)保存時(shí)間信息,其余大部分分單元用于
保存系統(tǒng)配置信息,供系統(tǒng)啟動(dòng)時(shí)bios程序讀取
bios也提供了相關(guān)的程序,使我們可以在開(kāi)機(jī)的時(shí)候配置CMOS RAM中的系統(tǒng)信息
**補(bǔ)充:BIOS
BIOS是英文'Basic Input Output System'的縮略詞,直譯過(guò)來(lái)后中文名稱(chēng)就是'基本輸入輸出系統(tǒng)'。
在IBM PC兼容系統(tǒng)上,是一種業(yè)界標(biāo)準(zhǔn)的固件接口。BIOS這個(gè)字眼是在1975年第一次由CP/M操作系統(tǒng)中出現(xiàn)。
BIOS是個(gè)人電腦啟動(dòng)時(shí)加載的第一個(gè)軟件
4.該芯片內(nèi)部有兩個(gè)端口,端口地址為70h和71h。CPU通過(guò)這兩個(gè)端口讀寫(xiě)CMOS RAM。
5.70h為地址端口,存放要訪問(wèn)的CMOS RAM單元的地址;71h為數(shù)據(jù)端口,存放從選定的CMOS RAM單元中讀取的數(shù)據(jù)
或要寫(xiě)入到其中的數(shù)據(jù)
2.比如:讀CMOS RAM的2號(hào)單元:
1.將2送入端口70h
2.從71h讀取2號(hào)單元的內(nèi)容
14.3 shl和shr指令
shl和shr是邏輯移位指令,后面的課程中我們要用到移位指令
1.shl邏輯左移指令,功能為:
1.將一個(gè)寄存器或內(nèi)存單元中的數(shù)據(jù)向左移位
2.將最后移出的移位寫(xiě)入cf中
3.最低位用0補(bǔ)充
例如有如下指令:
mov al,01001000b
shl al,1 ;將al中的數(shù)據(jù)左移一位
執(zhí)行后(al)=100100000b,cf=0.
如果移動(dòng)位數(shù)大于1時(shí),必須將移動(dòng)位數(shù)放在cl中
2.shr邏輯右移指令,與shl剛好相反
14.4 CMOS RAM中存儲(chǔ)的時(shí)間信息
在CMOS RAM中存放著當(dāng)前時(shí)間
秒:00h
分:02h
時(shí):04h
日:07h
月:08h
年:09h
這6個(gè)信息的長(zhǎng)度都為1個(gè)字節(jié)
這些數(shù)據(jù)以BCD碼的方式存放,一個(gè)字節(jié)可以表示兩個(gè)BCD碼
CMOS RAM存儲(chǔ)時(shí)間信息的單元中存儲(chǔ)了用兩個(gè)BCD碼表示的兩個(gè)十進(jìn)制數(shù)
高4位的BCD碼表示十位,低四位的BCD碼表示個(gè)位
【編程】:在屏幕中間顯示當(dāng)前的月份
1.CMOS RAM芯片回顧:
1.70h為地址端口,存放要訪問(wèn)的CMOS RAM單元的地址
2.71h為數(shù)據(jù)端口,存放從選定的CMOS RAM單元中【讀取】的數(shù)據(jù),或【寫(xiě)入】其中的數(shù)據(jù)
2.分析
這個(gè)程序主要做兩部分工作
1.從CMOS RAM的8號(hào)單元讀取當(dāng)前月份的BCD碼
要讀取CMOS RAM的信息,我們首先要向地址端口70h寫(xiě)入要訪問(wèn)的單元的地址
mov al,8
out 70h,al
然后從數(shù)據(jù)端口71h中取得指定單元中的數(shù)據(jù)
in al,71h
2.將用BCD碼表示的月份以十進(jìn)制的形式顯示到屏幕上
;編程:在屏幕中間顯示當(dāng)前的月份
code segment
assume cs:code
start:
mov al,8
out 70h,al
in al,71h
mov ah,al
mov cl,4
shr ah,cl
and al,00001111b
add ah,30h
add al,30h
mov bx,0b800h ;顯存
mov es,bx
mov byte ptr es:[160*12+40*2],ah ;顯示月份的十位數(shù)碼
mov byte ptr es:[160*12+40*2+2],al ;顯示月份的個(gè)位數(shù)碼
mov ax,4c00h
int 21h
code ends
end start
【實(shí)驗(yàn)十四】編程:以“年/月/日 時(shí):分:秒”的格式,顯示當(dāng)前日期和時(shí)間
第十五章 外中斷
**CPU除了有運(yùn)算能力,還有I/O能力
15.1 接口芯片和端口
1.在PC系統(tǒng)的接口卡和主板上,裝有各種接口芯片,這些外設(shè)接口芯片的內(nèi)部裝有若干寄存器
CPU將這些寄存器當(dāng)做【端口】訪問(wèn)
2.外設(shè)的輸入不直接送入內(nèi)存和CPU,而是送入相關(guān)的接口芯片的【端口】中
3.CPU向外設(shè)的輸出也是要先送入【端口】中,再由相關(guān)芯片送入到外設(shè)
4.CPU可以向外設(shè)輸出控制命令,這些控制命令也是先送到【端口】中,然后相關(guān)芯片根據(jù)命令進(jìn)行相關(guān)工作
5.可見(jiàn):CPU與外部設(shè)備的交流是通過(guò)【端口】進(jìn)行的
CPU在執(zhí)行完當(dāng)前指令后,可以檢測(cè)到發(fā)送過(guò)來(lái)的中斷信息,引發(fā)中斷過(guò)程,處理外設(shè)的輸入
15.2 外中斷信息
1.在PC系統(tǒng)中,外中斷源一共有兩類(lèi)
1.可屏蔽中斷
2.不可屏蔽中斷
2.可屏蔽中斷是CPU可以不響應(yīng)的外中斷。CPU是否響應(yīng)可屏蔽中斷
要看標(biāo)志寄存器的IF位的設(shè)置
當(dāng)CPU檢測(cè)到可屏蔽中斷信息時(shí):
1.若IF=1,則CPU在執(zhí)行完當(dāng)前指令后相應(yīng)中斷,引發(fā)中斷過(guò)程
2.若IF=0,則不響應(yīng)可屏蔽中斷
3.可屏蔽中斷所引發(fā)的中斷過(guò)程,除在第一步的實(shí)現(xiàn)上與內(nèi)中斷有所不同外,基本上和內(nèi)中斷的中斷過(guò)程相同
4.因?yàn)榭善帘沃袛嘈畔?lái)自于CPU外部,中斷類(lèi)型碼是通過(guò)數(shù)據(jù)總線送入CPU的
而內(nèi)中斷的中斷碼是在CPU內(nèi)部產(chǎn)生的
5.IF設(shè)置為0的原因:在進(jìn)入中斷處理程序后,禁止其他的可屏蔽中斷
當(dāng)然,如果中斷處理程序中需要處理可屏蔽中斷,可以用指令將IF設(shè)置為1
6.8086CPU提供的設(shè)置IF的指令如下:
sti ;用于設(shè)置IF=1
cli ;用于設(shè)置IF=0
7.不可屏蔽中斷是CPU必須相應(yīng)的外中斷。
當(dāng)CPU檢測(cè)到不可屏蔽中斷信息時(shí),則在執(zhí)行完當(dāng)前指令后
立即響應(yīng),應(yīng)發(fā)中斷過(guò)程
8.8086CPU不可屏蔽中斷的中斷類(lèi)型碼固定為2,所以中斷過(guò)程中,不需要取中斷類(lèi)型碼
9.不可屏蔽中斷的中斷過(guò)程
1.標(biāo)志寄存器入棧,IF=0,TF=0
2.CS,IP入棧
3.(IP)=(8),(CS)=(0AH) ;固定地址
10.幾乎所有外中斷,都是可屏蔽中斷。當(dāng)外設(shè)有需要處理的事件發(fā)生時(shí)
相關(guān)芯片向CPU發(fā)出可屏蔽中斷信息。
不可屏蔽中斷是系統(tǒng)中有必須處理的緊急情況發(fā)生時(shí)用來(lái)通知CPU的中斷信息,本門(mén)課程中,主要討論可屏蔽中斷
15.3 PC機(jī)鍵盤(pán)的處理過(guò)程
1.下面看一個(gè)鍵盤(pán)輸入的處理過(guò)程,并以此來(lái)體會(huì)PC機(jī)處理外設(shè)輸入的基本方法
1.鍵盤(pán)輸入
2.引發(fā)9號(hào)中斷
3.執(zhí)行int 9中斷例程
2.PC機(jī)鍵盤(pán)的處理過(guò)程
1.鍵盤(pán)上每一個(gè)鍵相當(dāng)于一個(gè)開(kāi)關(guān),鍵盤(pán)中有一個(gè)芯片對(duì)鍵盤(pán)上的每一觸鍵的開(kāi)關(guān)狀態(tài)進(jìn)行掃描。
2.按下一個(gè)鍵時(shí),開(kāi)關(guān)接通,該芯片就產(chǎn)生一個(gè)掃描碼,掃描碼說(shuō)明按下的鍵在鍵盤(pán)上的位置
掃描碼被送入主板上的相關(guān)接口芯片的寄存器中,該寄存器的端口地址為60H
3.松開(kāi)控下的鍵時(shí),也產(chǎn)生一個(gè)掃描碼,掃描碼說(shuō)明了松開(kāi)的鍵在鍵盤(pán)上的位置,松開(kāi)按鍵時(shí)
產(chǎn)生的掃描碼也被送入60H端口中。
一般按下一個(gè)鍵時(shí),產(chǎn)生的掃描碼稱(chēng)為通碼,松開(kāi)一個(gè)鍵產(chǎn)生的掃描碼稱(chēng)為斷碼
掃描碼長(zhǎng)度為一個(gè)字節(jié),通碼的第七位為0,斷碼的第七位為1
即:斷碼=通碼+80H
**BIOS提供了int9中斷例程,用來(lái)進(jìn)行基本的鍵盤(pán)輸入處理,主要的工作如下:
1.讀出60H端口中的掃描碼
2.如果是字符鍵的掃描碼,將該掃描碼對(duì)應(yīng)的字符碼(即:ASCII碼)送入內(nèi)存中的BIOS鍵盤(pán)緩沖區(qū)
3,如果是控制鍵和切換鍵的掃描碼,則將其轉(zhuǎn)變?yōu)闋顟B(tài)字節(jié),寫(xiě)入內(nèi)存中存儲(chǔ)狀態(tài)字節(jié)的單元
4.鍵盤(pán)的輸入到達(dá)60H端口時(shí),相關(guān)的芯片會(huì)向CPU發(fā)出中斷類(lèi)型碼為9的可屏蔽中斷信息。
5.CPU檢測(cè)到中斷信息后,如果IF=1,則相應(yīng)中斷,同時(shí)將IF設(shè)置為0(不讓其他可屏蔽中斷進(jìn)行干擾),引發(fā)中斷過(guò)程,轉(zhuǎn)去執(zhí)行int9中斷例程
3.BIOS鍵盤(pán)緩沖區(qū)是系統(tǒng)啟動(dòng)后,BIOS用于存放int9中斷例程所接受的鍵盤(pán)輸入的內(nèi)存區(qū)
4.該內(nèi)存區(qū)可以存儲(chǔ)15個(gè)鍵盤(pán)輸入,int9中斷例程除了接收掃描碼外,還要產(chǎn)生和掃描碼對(duì)應(yīng)的字符碼,
所以在BIOS鍵盤(pán)緩沖區(qū)中,一個(gè)鍵盤(pán)輸入用一個(gè)字單元存放,高字節(jié)存放掃描碼,低字節(jié)存放字符碼
5.0040:17單元存儲(chǔ)鍵盤(pán)狀態(tài)字節(jié),該字節(jié)記錄了控制鍵和切換鍵的狀態(tài)。鍵盤(pán)狀態(tài)字節(jié)各位記錄的信息如下:
15.4 編寫(xiě)int9中斷例程,并安裝
梳理鍵盤(pán)輸入的處理過(guò)程
1.鍵盤(pán)產(chǎn)生掃描碼
2.掃描碼送入60H端口
3.一旦偵測(cè)到60H端口有動(dòng)靜,引發(fā)9號(hào)中斷
4.CPU執(zhí)行int9中斷例程處理輸入
以上的過(guò)程,前三步都由硬件系統(tǒng)自動(dòng)完成,能夠修改的只有第四步,修改int9中斷程序
【任務(wù)演示】在屏幕中依次顯示“a”~“z”并可以讓人看清。在顯示過(guò)程中,按下Esc鍵后,該表顯示的顏色
;程序1:實(shí)現(xiàn)連續(xù)顯示“a”~“z”
;編程:在屏幕中間依次顯示“a”~“z”,并可以讓人看清。在顯示的過(guò)程中,按下'Esc'鍵后,改變顯示的顏色。
;部分功能代碼:
stack segment
db 128 dup (0)
stack ends
code segment
assume cs:code
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,0b800h
mov es,ax
mov ah,'a'
s: mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna s
mov ax,4c00h
int 21h
delay:
push ax
push dx
mov dx,0010h ;循環(huán)10000000h次
mov ax,0
s1:
sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret
code ends
end start
;程序2:實(shí)現(xiàn)改變顏色
;編程:在屏幕中間依次顯示“a”~“z”,并可以讓人看清。在顯示的過(guò)程中,按下'Esc'鍵后,改變顯示的顏色。
stack segment
db 128 dup (0)
stack ends
data segment
dw 0,0
data ends
code segment
assume cs:code
start:
mov ax,stack
mov ss,ax
mov sp,128
mov ax,data
mov ds,ax
mov ax,0
mov es,ax
push es:[9*4]
pop ds:[0]
push es:[9*4+2]
pop ds:[2] ;將原來(lái)的int 9中斷例程的入口地址保存在ds:0、ds:2單元中
mov word ptr es:[9*4],offset int9
mov es:[9*4+2],cs ;在中斷向量表中設(shè)置新的int 9中斷例程的入口地址
mov ax,0b800h
mov es,ax
mov ah,'a'
s:
mov es:[160*12+40*2],ah
call delay
inc ah
cmp ah,'z'
jna s
mov ax,0
mov es,ax
push ds:[0]
pop es:[9*4]
push ds;[2]
pop es;[9*4+2] ;將中斷向量表中int 9中斷例程的入口恢復(fù)為原來(lái)的地址
mov ax,4c00h
int 21h
delay:
push ax
push dx
mov dx,0010h
mov ax,0
s1:
sub ax,1
sbb dx,0
cmp ax,0
jne s1
cmp dx,0
jne s1
pop dx
pop ax
ret
;------以下為新的int 9中斷例程--------------------
;int9中斷例程是在進(jìn)行鍵盤(pán)輸入之后,由系統(tǒng)自動(dòng)調(diào)用
int9:
push ax
push bx
push es
in al,60h
pushf
pushf
pop bx
and bh,11111100b
push bx
popf
call dword ptr ds:[0] ;對(duì)int指令進(jìn)行模擬,調(diào)用原來(lái)的int 9中斷例程
cmp al,1
jne int9ret
mov ax,0b800h
mov es,ax
inc byte ptr es:[160*12+40*2+1] ;屬性增加1,改變顏色
int9ret:
pop es
pop bx
pop ax
iret
code ends
end start
第十六章 直接定址表
16.1 描述了單元長(zhǎng)度的標(biāo)號(hào)
1.本章討論如何有效合理地組織數(shù)據(jù),以及相關(guān)的編程技術(shù)
1.前面的課程中,我們一直在代碼段中使用標(biāo)號(hào)來(lái)標(biāo)記指令、數(shù)據(jù)、段的起始地址
2.還可以使用一種標(biāo)號(hào),這種標(biāo)號(hào)不但可以表示內(nèi)存單元的地址,還表示了內(nèi)存單元的長(zhǎng)度
即:表示在此標(biāo)號(hào)處的單元,是一個(gè)字節(jié)單元,還是字單元還是雙字單元
2.例如
1.標(biāo)號(hào)1
a : db 1,2,3,4,5,6,7,8
b : dw 0
此種標(biāo)號(hào)只能標(biāo)記地址
此種加有“:”的地址標(biāo)號(hào),只能在代碼段中使用,不能在其他段中使用
2.標(biāo)號(hào)2
a db 1,2,3,4,5,6,7,8 ;標(biāo)號(hào)a,描述了地址code:0,和從這個(gè)地址開(kāi)始,以后的內(nèi)存單元都是字節(jié)單元
b dw 0 ;標(biāo)號(hào)b描述了地址code:8,和從這個(gè)地址開(kāi)始,以后的內(nèi)存單元都是字單元
此種標(biāo)號(hào)既可以標(biāo)記地址,也可以表示此標(biāo)號(hào)處的單元
3.使用這種包含單元長(zhǎng)度的標(biāo)號(hào),可以使我們以簡(jiǎn)潔的形式訪問(wèn)內(nèi)存中的數(shù)據(jù)
這種標(biāo)號(hào)此后稱(chēng)為數(shù)據(jù)標(biāo)號(hào),它標(biāo)記了存儲(chǔ)數(shù)據(jù)的單元的地址和長(zhǎng)度
4.數(shù)據(jù)標(biāo)號(hào)的用法
指令:mov ax,b ;相當(dāng)于:mov ax,cs:[8]
指令:mov b,2 ;相當(dāng)于:mov word ptr cs:[8],2
指令:inc b ;相當(dāng)于:inc word ptr cs:[8]
指令:mov al,a [si] ;相當(dāng)于:mov al,cs:0[si]
指令:mov al,a[3] ;相當(dāng)于:mov al,cs:0[3]
指令:mov al,a[bx+si+3] ;相當(dāng)于:mov al,cs:0[bx+si+3]
16.2 在其他段中使用數(shù)據(jù)標(biāo)號(hào)
1.注意,如果想在代碼段中,直接用數(shù)據(jù)標(biāo)號(hào)訪問(wèn)數(shù)據(jù),
則需要用偽指令assume 將標(biāo)號(hào)所在的段和一個(gè)段寄存器聯(lián)系起來(lái)。
否則編譯器在編譯的時(shí)候,無(wú)法確定標(biāo)號(hào)的段地址在哪一個(gè)寄存器中。
2. 當(dāng)然,這種聯(lián)系是編譯器需要的,但絕對(duì)不是說(shuō),我們因?yàn)榫幾g器的工作需要,
用assume指令將段寄存器和某個(gè)段相聯(lián)系,段寄存器中就會(huì)真的存放該段的地址。
3.我們可以將數(shù)據(jù)標(biāo)號(hào)當(dāng)作數(shù)據(jù)來(lái)定義,此時(shí),編譯器將標(biāo)號(hào)所表示的地址當(dāng)作數(shù)據(jù)的值。
1.把數(shù)據(jù)標(biāo)號(hào)當(dāng)做數(shù)據(jù)來(lái)定義時(shí),使用【dw】定義數(shù)據(jù)
比如: data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw a,b ;數(shù)據(jù)標(biāo)號(hào)c處存儲(chǔ)的兩個(gè)字型數(shù)據(jù)為標(biāo)號(hào)a、b 的偏移地址。
data ends
數(shù)據(jù)標(biāo)號(hào)c處存儲(chǔ)的兩個(gè)字型數(shù)據(jù)為標(biāo)號(hào)a、b 的偏移地址。
相當(dāng)于:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw offset a, offset b
data ends
2.把數(shù)據(jù)標(biāo)號(hào)當(dāng)做數(shù)據(jù)來(lái)定義時(shí),使用【dd】定義數(shù)據(jù)
再比如:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dd a,b ;數(shù)據(jù)標(biāo)號(hào)c處存儲(chǔ)的兩個(gè)雙字型數(shù)據(jù)為標(biāo)號(hào)a的偏移地址和段地址、標(biāo)號(hào)b 的偏移地址和段地址。
data ends
數(shù)據(jù)標(biāo)號(hào)c處存儲(chǔ)的兩個(gè)雙字型數(shù)據(jù)為標(biāo)號(hào)a的偏移地址和段地址、標(biāo)號(hào)b 的偏移地址和段地址。
相當(dāng)于:
data segment
a db 1,2,3,4,5,6,7,8
b dw 0
c dw offset a, seg a, offset b, seg b ;seg操作符,功能為取得某一標(biāo)號(hào)的段地址。
data ends
seg操作符,功能為取得某一標(biāo)號(hào)的段地址。
16.3 直接定址表
本節(jié)課,我們將使用“查表”的方法,編寫(xiě)相關(guān)程序的技巧
【任務(wù)】編寫(xiě)子程序,以十六進(jìn)制的形式在屏幕中間顯示給定的byte型數(shù)據(jù)
code segment
assume cs:code
start:
mov al,0eh ;al中存放了byte型數(shù)據(jù)
call showbyte
mov ax,4c00h
int 21h
;子程序:
;用al傳送要顯示的數(shù)據(jù)
showbyte:
jmp short show
table db '0123456789ABCDEF' ;字符表
show: push bx ;保護(hù)現(xiàn)場(chǎng)
push es
mov ah,al
shr ah,1
shr ah,1
shr ah,1
shr ah,1 ;右移4位,ah中得到高4位的值
and al,00001111b ;al中為低4位的值
mov bl,ah
mov bh,0
mov ah,table[bx] ;用高4位的值作為相對(duì)于table的偏移,取得對(duì)應(yīng)的字符
mov bx,0b800h
mov es,bx
mov es:[160*12+40*2],ah
mov bl,al
mov bh,0
mov al,table[bx] ;用低4位的值作為相對(duì)于table的偏移,取得對(duì)應(yīng)的字符
mov es:[160*12+40*2+2],al
pop es
pop bx
ret
code ends
end start
16.4 程序入口地址的直接定址表
【編程】實(shí)現(xiàn)一個(gè)子程序setscreen,為顯示輸出提供如下功能:
1.清屏
2.設(shè)置前景色
3.設(shè)置背景色
4.向上滾動(dòng)一行
1.入口參數(shù)說(shuō)明:
1.用ah寄存器傳遞功能號(hào)
0:清屏;1:設(shè)置前景色;2:設(shè)置背景色;3:向上滾動(dòng)一行
2.對(duì)于2、3號(hào)功能,用al傳遞顏色值
al∈{0,1,2,3,4,5,6,7}
2.各種功能如何實(shí)現(xiàn)
1.清屏:
將顯存中當(dāng)前屏幕中的字符設(shè)為空格符;
2.設(shè)置前景色:
設(shè)置顯存中當(dāng)前屏幕中處于奇地址的屬性字節(jié)的第0、1、2位;
012位存放前景色
3.設(shè)置背景色:
設(shè)置顯存中當(dāng)前屏幕中處于奇地址的屬性字節(jié)的第4、5、6位;
456位存放背景色
4.向上滾動(dòng)一行:
依次將第 n+1行的內(nèi)容復(fù)制到第n行處:最后一行為空。
;功能子程序1:清屏
sub1: push bx ;保護(hù)現(xiàn)場(chǎng),調(diào)用子程序的時(shí)候,注意要保護(hù)現(xiàn)場(chǎng),運(yùn)行子程序的時(shí)候,可能會(huì)修改一些寄存器的值
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
sub1s: mov byte ptr es:[bx],' ' ;循壞2000次
add bx,2
loop sub1s
pop es ;恢復(fù)現(xiàn)場(chǎng)
pop cx
pop bx
ret
;功能子程序2:設(shè)置前景
sub2: push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub2s: and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret
;功能子程序3:設(shè)置背景色
sub3: push bx
push cx
push es
mov cl,4
shl al,cl
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s: and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret
;功能子程序4:向上滾動(dòng)一行
sub4:
push cx
push si
push di
push es
push ds
mov si,0b800h
mov es,si
mov ds,si
mov si,160 ;ds:si指向第n+1行,第1行
mov di,0 ;es:di指向第n行,第0行
cld
mov cx,24;共復(fù)制24行
sub4s:
push cx
mov cx,160
rep movsb ;復(fù)制
pop cx
loop sub4s
mov cx,80
mov si,0
sub4s1:
mov byte ptr es:[160*24+si],' ' ;最后一行清空
add si,2
loop sub4s1
pop ds
pop es
pop di
pop si
pop cx
ret ;sub4 ends
3.可以將這些功能子程序的入口地址存儲(chǔ)在一個(gè)表中,他們?cè)诒碇械奈恢煤凸δ芴?hào)相對(duì)應(yīng)
;編程:實(shí)現(xiàn)一個(gè)子程序setscreen,為顯示輸出提供如下功能:
;(1) 清屏。
;(2) 設(shè)置前景色。
;(3) 設(shè)置背景色。
;(4) 向上滾動(dòng)一行。
;
;入口參數(shù)說(shuō)明:
;(1) 用 ah 寄存器傳遞功能號(hào):0 表示清屏,1表示設(shè)置前景色,2 表示設(shè)置背景色,3 表示向上滾動(dòng)一行;
;(2) 對(duì)于2、3號(hào)功能,用 al 傳送顏色值,(al) ∈{0,1,2,3,4,5,6,7}
setscreen: jmp short set
table dw sub1,sub2,sub3,sub4
set:
push bx
cmp ah,3 ;判斷傳遞的是否大于 3
ja sret
mov bl,ah
mov bh,0
add bx,bx ;根據(jù)ah中的功能號(hào)計(jì)算對(duì)應(yīng)子程序的地址在table表中的偏移
call word ptr table[bx] ;調(diào)用對(duì)應(yīng)的功能子程序,學(xué)會(huì)本句代碼,是本章節(jié)的【精髓】
sret:
pop bx
iret
;功能子程序1:清屏
sub1:
push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,0
mov cx,2000
sub1s:
mov byte ptr es:[bx],' '
add bx,2
loop sub1s
pop es
pop cx
pop bx
ret ;sub1 ends
;功能子程序2:設(shè)置前景色
sub2:
push bx
push cx
push es
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub2s:
and byte ptr es:[bx],11111000b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret ;sub2 ends
;功能子程序3:設(shè)置背景色
sub3:
push bx
push cx
push es
mov cl,4
shl al,cl
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
sub3s:
and byte ptr es:[bx],10001111b
or es:[bx],al
add bx,2
loop sub2s
pop es
pop cx
pop bx
ret ; sub3 ends
;功能子程序4:向上滾動(dòng)一行
sub4:
push cx
push si
push di
push es
push ds
mov si,0b800h
mov es,si
mov ds,si
mov si,160 ;ds:si指向第n+1行
mov di,0 ;es:di指向第n行
cld
mov cx,24;共復(fù)制24行
sub4s:
push cx
mov cx,160
rep movsb ;復(fù)制
pop cx
loop sub4s
mov cx,80
mov si,0
sub4s1:
mov byte ptr es:[160*24+si],' ' ;最后一行清空
add si,2
loop sub4s1
pop ds
pop es
pop di
pop si
pop cx
ret ;sub4 ends
第十七章 使用BIOS進(jìn)入鍵盤(pán)輸入和磁盤(pán)讀寫(xiě)
**引言
1.大多數(shù)有用的程序都需要處理用戶的輸入,鍵盤(pán)輸入是最基本的輸入。
2.程序和數(shù)據(jù)通常需要長(zhǎng)期存儲(chǔ),磁盤(pán)是最常用的存儲(chǔ)設(shè)備。
3.BIOS 為這兩種外設(shè)的I/O提供了最基本的中斷例程,在本章中,我們對(duì)它們的應(yīng)用和相關(guān)的問(wèn)題進(jìn)行討論。
17.1 int9中斷例程對(duì)鍵盤(pán)輸入的處理
CPU 在9 號(hào)中斷發(fā)生后,執(zhí)行int 9中斷例程,從60h 端口讀出掃描碼,
并將其轉(zhuǎn)化為相應(yīng)的ASCII 碼或狀態(tài)信息,存儲(chǔ)在內(nèi)存的指定空間(鍵盤(pán)緩沖區(qū)或狀態(tài)字節(jié))中。
17.2 使用int16h中斷例程讀取鍵盤(pán)緩沖區(qū)
1.BIOS提供了int 16h 中斷例程供程序員調(diào)用。
2.int 16h 中斷例程中包含的一個(gè)最重要的功能是從鍵盤(pán)緩沖區(qū)中讀取一個(gè)鍵盤(pán)輸入,該功能的編號(hào)為0。
3.下面的指令從鍵盤(pán)緩沖區(qū)(緩沖區(qū)的最低位)中讀取一個(gè)鍵盤(pán)輸入,并且將其從緩沖區(qū)中刪除:
mov ah,0
int 16h
結(jié)果:(ah)=掃描碼,(al)=ASCII碼。
4.int 16h 中斷例程的 0 號(hào)功能,進(jìn)行如下的工作:
(1)檢測(cè)鍵盤(pán)緩沖區(qū)中是否有數(shù)據(jù);
(2)沒(méi)有則繼續(xù)做第1 步;(緩沖區(qū)隨時(shí)有可能輸入數(shù)據(jù))
(3)讀取緩沖區(qū)第一個(gè)字單元中的鍵盤(pán)輸入;
(4)將讀取的掃描碼送入ah,ASCII 碼送入al;
(5)將己讀取的鍵盤(pán)輸入從緩沖區(qū)中刪除。
5.可見(jiàn),B1OS 的int 9 中斷例程和int 16h 中斷例程是一對(duì)相互配合的程序,
int 9 中斷例程向鍵盤(pán)緩沖區(qū)中寫(xiě)入,
int 16h 中斷例程從緩沖區(qū)中讀出。
它們寫(xiě)入和讀出的時(shí)機(jī)不同,int 9 中斷例程在有鍵按下的時(shí)候向鍵盤(pán)緩沖區(qū)中寫(xiě)入數(shù)據(jù);
而int 16h 中斷例程是在應(yīng)用程序?qū)ζ溥M(jìn)行調(diào)用的時(shí)候,將數(shù)據(jù)從鍵盤(pán)緩沖區(qū)中讀出。
【編程】接收用戶的鍵盤(pán)輸入,輸入“r”,將屏幕上的字符設(shè)置為紅色:輸入“g”,
將屏幕上的字符設(shè)置為綠色;輸入“b ”,將屏幕上的字符設(shè)置為藍(lán)色。
;編程:
;接收用戶的鍵盤(pán)輸入,輸入“r”,將屏幕上的字符設(shè)置為紅色:輸入“g”,
;將屏幕上的字符設(shè)置為綠色;輸入“b ”,將屏幕上的字符設(shè)置為藍(lán)色。
;A、B、C處的程序指令比較有技巧,請(qǐng)讀者自行分析
code segment
assume cs:code
start:
mov ah,0
int 16h ;int 16h 0號(hào)功能實(shí)現(xiàn)從鍵盤(pán)緩沖區(qū)讀取一個(gè)鍵盤(pán)輸入
mov ah,1 ;A
cmp al,'r'
je red
cmp al,'g'
je green
cmp al,'b'
je blue
jmp short sret
red:
shl ah,1 ;B
green:
shl ah,1 ;C
blue:
mov bx,0b800h
mov es,bx
mov bx,1
mov cx,2000
s: and byte ptr es:[bx],11111000b ;設(shè)置顏色
or es:[bx],ah ;設(shè)置顏色
add bx,2
loop s
sret:
mov ax,4c00h
int 21h
code ends
end start
17.3 字符串的輸入
int 21h的0a號(hào)功能可以實(shí)現(xiàn)字符串的輸入
也可以用int 16h,通過(guò)顯示鍵盤(pán)緩沖區(qū)中的內(nèi)容,實(shí)現(xiàn)字符串的顯示
1.使用int 16h顯示字符串程序的處理過(guò)程如下
① 調(diào)用int 16h讀取鍵盤(pán)輸入;
② 如果是字符,進(jìn)入字符棧,顯示字符棧中的所有字符;繼續(xù)執(zhí)行① ;
③ 如果是退格鍵,從字符棧中彈出一個(gè)字符,顯示字符棧中的所有字符;繼續(xù)執(zhí)行① ;
④ 如果是Enter 鍵,向字符棧中壓入0,返回。
2.子程序:字符棧的入棧、出棧和顯示
參數(shù)說(shuō)明
(ah)=功能號(hào),0表示入棧,1表示出棧,2表示顯示;
ds : si 指向字符??臻g;
對(duì)于0 號(hào)功能:(al)=入棧字符;
對(duì)于1 號(hào)功能:(al)=返回的字符;
對(duì)于2 號(hào)功能:(dh)、(dl) =字符串在屏幕上顯示的行、列位置。
;使用int 16h顯示字符串的子程序:字符棧
;最基本的字符串輸入程序,需要具備下面的功能:
;(1) 在輸入的同時(shí)需要顯示這個(gè)字符串;
;(2)一般在輸入回車(chē)符后,字符串輸入結(jié)束;
;(3)能夠刪除已經(jīng)輸入的字符。
;編寫(xiě)一個(gè)接收字符串的輸入子程序,實(shí)現(xiàn)上面三個(gè)基本功能。
;因?yàn)樵谳斎氲倪^(guò)程中需要顯示,子程序的參數(shù)如下:
; (dh)、(dl)=字符串在屏幕上顯示的行、列位置;
; ds:si 指向字符串的存儲(chǔ)空間,字符串以O(shè) 為結(jié)尾符。
;功能子程序?qū)崿F(xiàn)
charstack:
jmp short charstart
table dw charpush,charpop,charshow
top dw 0 ;棧頂
charstart:
push bx
push dx
push di
push es
cmp ah,2
ja sret
mov bl,ah
mov bh,0
add bx,bx
jmp word ptr table[bx] ;使用直接定址表
charpush:
mov bx,top
mov [si][bx],al
inc top
jmp sret
charpop:
cmp top,0
je sret
dec top
mov bx,top
mov al,[si][bx]
jmp sret
charshow:
mov bx,0b800h
mov es,bx
mov al,160
mov ah,0
mul dh
mov di,ax
add dl,dl
mov dh,0
add di,dx
mov bx,0
charshows:
cmp bx,top
jne noempty
mov byte ptr es:[di],' '
jmp sret
noempty:
mov al,[si][bx]
mov es:[di],al
mov byte ptr es:[di+2],' '
inc bx
add di,2
jmp charshows
sret:
pop es
pop di
pop dx
pop bx
ret
17.4 應(yīng)用int13h中斷例程對(duì)鍵盤(pán)進(jìn)行讀寫(xiě)
1.磁盤(pán)的實(shí)際訪問(wèn)由磁盤(pán)控制器進(jìn)行,我們可以通過(guò)控制磁盤(pán)控制器來(lái)訪問(wèn)磁盤(pán)。
2.注意,我們只能以扇區(qū)為單位對(duì)磁盤(pán)進(jìn)行讀寫(xiě)。
在讀寫(xiě)扇區(qū)的時(shí)候,要給出面號(hào)、磁道號(hào)和扇區(qū)號(hào)。面號(hào)和磁道號(hào)從0開(kāi)始,而扇區(qū)號(hào)從1開(kāi)始。
3.BIOS提供了對(duì)扇區(qū)進(jìn)行讀寫(xiě)的中斷例程,這些中斷例程完成了許多復(fù)雜的和硬件相關(guān)的工作。
4.我們可以通過(guò)調(diào)用BIOS中斷例程來(lái)訪問(wèn)磁盤(pán)。
BIOS 提供的訪問(wèn)磁盤(pán)的中斷例程為int 13h 。
如下,讀取0面0道1扇區(qū)的內(nèi)容到0:200:
返回參數(shù):
操作成功:(ah)=0,(al)=讀入的扇區(qū)數(shù)
操作失?。海╝h)=出錯(cuò)代碼
將0:200中的內(nèi)容寫(xiě)入0面0道1扇區(qū)示例
返回參數(shù):
操作成功: (ah)=0,(al)=寫(xiě)入的扇區(qū)數(shù)
操作失敗: (ah)=出錯(cuò)代碼
5.注意:使用int 13h 中斷例程對(duì)軟盤(pán)進(jìn)行讀寫(xiě)。直接向磁盤(pán)扇區(qū)寫(xiě)入數(shù)據(jù)是很危險(xiǎn)的,
很可能覆蓋掉重要的數(shù)據(jù)。
【編程】將當(dāng)前屏幕的內(nèi)容保存在磁盤(pán)上
分析:1 屏的內(nèi)容占4000個(gè)字節(jié),需要8 個(gè)扇區(qū)(一個(gè)扇區(qū)512B),我們用0面0道的1~8扇區(qū)存儲(chǔ)顯存中的內(nèi)容。
code segment
assume cs:code
start:
mov ax,0b800h
mov es,ax
mov bx,0 ;es:bx 指向?qū)?xiě)入磁盤(pán)的數(shù)據(jù)的內(nèi)存區(qū)
mov al,8 ;寫(xiě)入的扇區(qū)數(shù)
mov ch,0 ;磁道號(hào),從0開(kāi)始
mov cl,1 ;扇區(qū)號(hào) 從1開(kāi)始
mov dl,0 ;驅(qū)動(dòng)器號(hào)0:軟驅(qū)A, 1:軟驅(qū)B,硬盤(pán)從80h開(kāi)始, 80h:硬盤(pán)C,81h:硬盤(pán)D
mov dh,0 ;磁頭號(hào),(對(duì)于軟盤(pán)即面號(hào),因?yàn)橐粋€(gè)面用一個(gè)磁頭來(lái)讀寫(xiě))
mov ah,3 ;傳遞 int 13h 寫(xiě)入數(shù)據(jù)的功能號(hào)
int 13h
;返回參數(shù)
;操作成功:(ah) = 0,(al) = 寫(xiě)入的扇區(qū)數(shù)
;操作失敗:(ah) = 出錯(cuò)代碼
return:
mov ax,4c00h
int 21h
code ends
end start
【實(shí)驗(yàn)17和課程設(shè)計(jì)2】
課程設(shè)計(jì)1在第十章
綜合研究
研究試驗(yàn)1 搭建一個(gè)精簡(jiǎn)的C語(yǔ)言開(kāi)發(fā)環(huán)境
研究試驗(yàn)2 使用寄存器
研究試驗(yàn)3 使用內(nèi)存空間
研究試驗(yàn)4 不用main函數(shù)編程
研究試驗(yàn)5 函數(shù)如何接受不定數(shù)量的參數(shù)
|