一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

匯編語(yǔ)言學(xué)習(xí)筆記(【匯編語(yǔ)言】小甲魚(yú)零基礎(chǔ)匯編)

 山峰云繞 2022-02-20

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)制
A01000001a01100001
B01000010b01100010
C01000011c01100011
D01000100d01100100
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ù)

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    成人精品欧美一级乱黄| 99久久国产综合精品二区| 久久国产成人精品国产成人亚洲| 亚洲少妇一区二区三区懂色| 精品一区二区三区人妻视频| 久久机热频这里只精品| 国产情侣激情在线对白| 国产成人精品国产成人亚洲| 亚洲人午夜精品射精日韩| 又大又长又粗又猛国产精品| 国产黄色高清内射熟女视频| 欧美日韩乱一区二区三区| 五月天婷亚洲天婷综合网| 国产在线日韩精品欧美| 免费黄色一区二区三区| 麻豆91成人国产在线观看| 国产熟女一区二区三区四区| 在线观看国产午夜福利| 丁香六月婷婷基地伊人| 翘臀少妇成人一区二区| 亚洲一级在线免费观看| 亚洲精品高清国产一线久久| 国产超薄黑色肉色丝袜| 欧美黑人黄色一区二区| 久久热在线视频免费观看| 中日韩美女黄色一级片| 精品少妇一区二区视频| 91天堂素人精品系列全集| 国产传媒欧美日韩成人精品| 日韩欧美一区二区不卡看片| 日韩人妻毛片中文字幕| 国产精品视频久久一区| 中文文精品字幕一区二区| 精品欧美在线观看国产| 国产日韩欧美在线亚洲| 在线中文字幕亚洲欧美一区| 无套内射美女视频免费在线观看| 中文字幕亚洲精品乱码加勒比| 国产女优视频一区二区| 99精品国产一区二区青青 | 欧美字幕一区二区三区|