小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

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

 山峰云繞 2022-02-20

https://blog.csdn.net/inv1796915552/article/details/109559495?utm_source=app&app_version=5.0.1&code=app_1562916241&uLinkId=usr1mkqgl919blen 



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

目錄

第十章 call和ret指令

第十一章 標志寄存器

第十二章 內中斷

第十三章 int指令

第十四章 端口

第十五章 外中斷

第十六章 直接定址表

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

綜合研究(自己看書)

 

第〇章 課程資料

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

百度云:https://pan.baidu.com/s/1TE1Egc0ZmeJfLP5zvamo0Q

第一章 基礎知識

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

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

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

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

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

第四章 第一個匯編程序

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

第五章 【bx】和loop指令

5.1 [bx]
    1.和[0]類似,[0]表示內存單元,它的偏移地址是0;
    2.[bx]同樣也表示一個內存單元,它的段地址在DS中
        它的偏移地址在bx中,至于是取字還是取字節(jié),
        要看他放入的寄存器是8位還是16位
    3.補充:inc指令:相當于C語言中的++運算符
5.2 Loop指令
    這個指令和循環(huán)有關
    1.指令格式:loop 標號
        CPU執(zhí)行l(wèi)oop指令的時候,要進行兩步操作
        1.(cx)=(cx)-1;
        2.判斷cx中的值,若不為零,則轉至標號處執(zhí)行程序
            若為零,則向下執(zhí)行。
    2.通常,loop指令實現(xiàn)循環(huán),cx中存放循環(huán)的次數
    3.標號
        在匯編語言中,標號代表了一個地址,標號標識了一個地址
    4.使用cx和loop指令相配合實現(xiàn)循環(huán)功能的三個要點
        1.在cx中存放循環(huán)次數
        2.loop指令中的標號所標識地址要在前面
        3.要循環(huán)執(zhí)行的程序段,要寫在標號和loop指令的中間
    5.用cx和loop指令相配合實現(xiàn)循環(huán)功能的程序框架
        mov cx,循環(huán)次數
      S:循環(huán)執(zhí)行的程序段
        loop s
5.3 在Debug中跟蹤供loop指令實現(xiàn)的循環(huán)程序
    **注意:在匯編程序中,數據不能以字母開頭,如果要輸入像FFFFH這樣的數
        則要在前面添加一個0
    在debug程序中引入G命令和P命令
    1.G命令
        G命令如果后面不帶參數,則一直執(zhí)行程序,直到程序結束
        G命令后面如果帶參數,則執(zhí)行到ip為那個參數地址停止
    2.P命令
        T命令相當于單步進入(step into)
        P命令相當于單步通過(step over)
5.4 Debug和匯編編譯器Masm對指令的不同處理
    1.在debug中,可以直接用指令 mov ax,[0] 將偏移地址為0號單元的內容賦值給ax
    2.但通過masm編譯器,mov ax,[0] 會被編譯成 mov ax,0
        1.要寫成這樣才能實現(xiàn):mov ax,ds:[0]
        2.也可以寫成這樣:
            mov bx,0
            mov ax,[bx]  ;或者mov ax,ds:[bx]
5.5 loop和[bx]的聯(lián)合應用
    1.計算ffff:0~ffff:b單元中的數據的和,結果存儲在dx中
        1.注意兩個問題
            1.12個8位數據加載一起,最后的結果可能會超出8位(越界),故要用16位寄存器存放結果
            2.將一個8位的數據加入到16位寄存器中,類型不匹配,8位的數據不能與16位相加
        2.【解決辦法】
            把原來8位的數據,先通過通用寄存器ax,將它們轉化成16位的
        3.代碼如下
assume cs:codesg

codesg segment
start:
	;指定數據段
	mov ax,0ffffh
	mov ds,ax
	
	;初始化
	mov ax,0
	mov dx,0
	mov bx,0
	
	;指定循環(huán)次數,12次
	mov cx,0ch
circ:
	;把8位數據存入al中,即ax中存放的是[bx]轉化之后的16位數據,前8位都是0
	mov al,[bx]
	;進行累加
	add dx,ax
	;bx自增,變化內存的偏移地址
	inc bx
	loop circ
	
	;程序返回
	mov ax,4c00h
	int 21H
codesg ends

end start    
5.6 段前綴
    1.指令“mov ax,[bx]”中,內存單元的偏移地址由bx給出,而段地址默認在ds中
    2.我們可以在訪問內存單元的指令中顯式地給出內存單元的段地址所在的段寄存器
        比如  mov ax,ds:[0]
              mov ax,ds:[bx]
        這里的ds就叫做【段前綴】
5.7 一段安全的空間
    1.8086模式中,隨意向一段內存空間寫入內容是很危險的
        因為這段空間中可能存放著【重要的系統(tǒng)數據或代碼】
    2.在一般的PC機中,DOS方式下,DOS和其他合法的程序一般都不會使用【0:200~0:2FF】
        的256個字節(jié)的空間。所以,我們使用這段空間是安全的    

第六章 包含多個段的程序

6.1在代碼段中使用數據
    1.dw的含義【定義字型數據:define word,16字節(jié)】
        在數據段中使用dw定義數據,則數據在數據段中
        在代碼段中使用dw定義數據,則數據在代碼段中
        堆棧段也是一樣
    2.在程序的第一條指令前加一個標號start,并且這個標號在偽指令end后面出現(xiàn)
        可以通知編譯器程序在什么地方結束,并且也可以通知編譯器程序的入口在哪里
6.2在代碼段中使用棧
    **補充:如果題目要求【逆序】存放,就要想到棧(FILO)
    使用dw向系統(tǒng)申請一段空間,然后把這個空間當做棧
6.3將數據、代碼、棧放入不同的段
    1.在前面的6.1和6.2中,我們在程序中用到了數據和棧,我們在編程的時候要注意
        何處是數據,何處是棧、何處是代碼
    2.這樣做顯然有兩個問題
        1.把他們放在一個段中是程序顯得混亂
        2.前面程序中處理的數據很少,用到的棧空間也小,放在一個段里面沒有問題
            但數據、棧、代碼需要的空間超過64KB,就不能放在一個段中
            (8086中一個段的容量不能大于64KB)
    3.我們可以和定義代碼段一樣的方法來定義多個段
        然后在這些段里面定義需要的數據,或通過定義數據來取得??臻g
    4.將數據、代碼、棧放入不同的段
        1.我們可以在源程序中為這三個段起具有含義的名稱
            用來存放數據的段,我們將其命名為“data”
            用來存放代碼的段,我們將其命名為“code”
            用來作??臻g的段,我們將其命名為“stack”
          但是CPU看得懂嗎?【不能】
        2.我們在源程序中用偽指令
            “assume cs:code,ds:data,ss:stack”將cs、ds和ss分別和code、data、stack段相連
            這樣做了之后,CPU是都就會將cs指向code,ds指向data,ss指向stack
              從而按照我們的意圖來處理這些段呢?【不能】
              偽指令CPU看不懂,偽指令是給編譯器看的
        3.若要CPU按照我們的安排行事,就要用機器指令控制它,源程序中的匯編指令
            才是CPU要執(zhí)行的內容
            需在在code段中給DS,CS、SS設置相應的值才能讓CPU識別出數據段、代碼段、堆棧段
            其中匯編程序開始的地方(即代碼段開始的地方)由end后面的標號所指向的地方給出
    5.assume指令不可省略,至于為什么,需要以后多多體會
【實驗五】
    1.如果段中的數據占N個字節(jié),則程序加載后,這段實際占有的空間為:N%16==0?N:16×(N/16+1);
        因為一個段最小占用16字節(jié),即有16個字節(jié)只有這個段可以訪問到
    2.在編輯源程序的時候,如果調換各個段的編寫位置,最后CS、DS、SS的值會發(fā)生變化
    3.如果去掉start,編譯器會從上到下執(zhí)行,如果第一個段是代碼段,則可以正常運行
        若第一個段不是代碼段,則不會正常運行
    4.代碼示例1
assume cs:code,ds:data,ss:stack

;數據段
data segment
    ;8個數據
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends

;棧段
stack segment
	;8個數據
    dw 0,0,0,0,0,0,0,0
stack ends

;代碼段
code segment
start:
	;??臻g初始化
	mov ax,stack
	mov ss,ax
	mov sp,16
	
	;數據段初始化
	mov ax,data
	mov ds,ax
	
	push ds:[0];一個棧單元是一個字
	push ds:[2]
	;存放數據不會改變
	pop ds:[2]
	pop ds:[0]
	
	;程序返回
	mov ax,4c00h
	int 21h
code ends
end 
    5.將a,b數據段中的內容分別相加,結果放入data數據段中
assume cs:code

;數據段
a segment
	db 1,2,3,4,5,6,7,8
a ends

;數據段
b segment
	db 1,2,3,4,5,6,7,8
b ends

;數據段
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數據段中的前8個字型數據逆序存儲到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 

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

本章主要講解一些更靈活的定位內存地址的方法和相關的編程方法
7.1 and和or指令
    1.and指令:邏輯與指令,按位進行與運算
        1.如:mov al,01100011B
             and al,00111011B
          執(zhí)行后:
             al=00100011B
        2.通過and指令可將操作對象的相應位設為0,其他位保持不變
            例如al的第6位設為0:and al,10111111B
            例如al的第7位設為0:and al,01111111B
            例如al的第0位設為0:and al,11111110B
    2.or指令,邏輯或運算,按位進行或運算
        1.如:mov al,01100011B
             or  al,00111011B
          執(zhí)行后:
            al=01111011B
        2.通過該指令可將操作對象的相應位設為1,其他位不變
            or al,01000000B;將al的第6位設為1
            or al,10000000B;將al的第7位設為1
            or al,00000001B;將al的第0位設為1
7.2 關于ASCII碼
    一種編碼方案,在計算機系統(tǒng)中通常被采用,8位
7.3 以字符形式給出的數據
    1.在匯編程序中,可以使用'×××'的方式指明數據是以字符的形式給出的
    2.編譯器會將它們轉化為相應的ASCII碼
    3.例如
        1.db 'unIX'   ;相當于:db 75H,6EH,49H,58H
            'u'、'n'、'I'、'X'的ASCII碼分別為75H,6EH,49H,58H
        2.mov al,'a'  ;相當于:mov al,61H
            'a'的ASCII碼為61H
    4.ASCII碼中,大寫字母和小寫字母之間的規(guī)律
        小寫字母=大寫字母+32
        小寫字母=大寫字母+20H
        大寫字母從41H開始排,小寫字母從61H開始排
大寫二進制小寫二進制
A01000001a01100001
B01000010b01100010
C01000011c01100011
D01000100d01100100
7.4 大小寫轉換的問題
    1.方案一:
        1.識別出是該字節(jié)是表示一個的大寫英文字符,還是小寫的
            用于條件判斷的匯編程序,目前還沒有學到
        2.根據+20H 或者 -20H進行大小寫轉換
    2.方案二:
        1.若全部轉化為大寫,則將第5位置0
            and al,11011111B
        2.若全部轉化為小寫,則將第5位置1
            or  al,00100000B
7.5 [bx+常數]
    mov ax,[bx+200]的含義:
    1.將一個內存單元的內容送入ax,這個內存單元的長度為2字節(jié),存放一個入一個子單元
      該字單元的偏移地址為bx中的數值加上200,段地址在ds中
    2.也可以寫成
        1.mov ax,200[bx]
        2.mov ax,[bx].200
7.6 用[bx+idata]的方式進行數組的處理
    在codesg中填寫代碼,將datasg中定義的第一個字符串轉化為大寫,第二個字符串轉化為小寫
      1.我們觀察datasg段中的兩個字符串,一個的起始地址為0,另一個的起始地址為5
      2.我們可以將這兩個字符串看作兩個數組,一個從0地址開始存放,另一個從5開始存放
      3.我們可以用[0+bx]和[5+bx]的方式在同一個循環(huán)中定位這兩個字符串中的字符
      4.注意這個數組的定位方式,對比C語言
        C語言的數組定位方式:a[i],b[i],  a、b是地址常量
        匯編語言的數組定位方式:0[bx],5[bx]
        所以:[bx+常數]的方式為高級語言實現(xiàn)數組提供了便利的機制
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];等價于mov al,5[bx];等價于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
    已經學過的10個寄存器:AX、BX、CX、DX、DS、CS、SS、ES、IP、SP
    1.SI和DI是8086CPU中和bx功能相近的寄存器
        bx不夠用,所以引進了SI和DI
    2.SI和DI(16位)不能夠分成兩個8位寄存器來使用【和bx的區(qū)別】
    3.下面三組指令實現(xiàn)了相同的功能
        1.mov bx,0
          mov ax,[bx]
        2.mov si,0
          mov ax,[si]
        3.mov di,0
          mov ax,[di]
    4.下面三組指令也實現(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實現(xiàn)將字符串'welcome to masm!'復制到它后面的數據區(qū)中
        通常用ds:si指向要復制的源始字符串
        通常用ds:di指向要復制的目的空間
        **注意si、di是16位寄存器,循環(huán)中自增時,應該+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]的含義類似,我們以[bx+si]為例進行講解
        [bx+si]表示一個內存單元,它的偏移地址為bx中的數值加上si中的數值
          它的偏移地址在ds中
    2.[bx+si]也可以寫成[bx][si]
7.9 [bx+si+常數]和[bx+di+常數]
    1.以[bx+Si+常數]為例講解
        [bx+si+常量]表示一個內存單元,偏移地址為bx的值+si的值+常數
    2.指令mov ax,[bx+si+常數]也可以寫成如下形式
        1.mov ax,200[bx+si]
        2.mov ax,200[bx][si]
        3.mov ax,[bx].200[si]
7.10 不同的尋址方式的靈活應用
    1.總結幾種定位內存的方法
        1.ds:[常數]   【直接尋址】
            用一個常量來表示地址,可用于直接定位一個內存單元
        2.[bx]      【寄存器間接尋址】
            用一個寄存器的值來表示內存地址,可以間接定位一個內存單元
        3.[bx+常數]   【??】
            用一節(jié)寄存器的值和常量表示內存地址,可在一個起始地址的基礎上用變量間接定位一個內存單元
        4.[bx+si]
        5.[bx+si+常數]
    2.編程,給定數據段data,將data段中每個單詞的頭一個字母改寫成大寫字母
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.編程,給定數據段data,將data段中的每個單詞改為大寫字母
      1.【loop指令cx-1之后,在判斷是否為0】
      2.雙重循環(huán)用匯編怎么實現(xiàn)?
        應該在每次開始內循環(huán)的時候,將外層循環(huán)的cx的值保存起來,
        在執(zhí)行外層循環(huán)的loop指令前,在恢復外層循環(huán)的cx數值。
        **可以用寄存器來臨時保存,也可以用??臻g(內存)保存【沒有多余的寄存器】
            更好的方法是使用:棧
          1.使用寄存器實現(xiàn)
assume cs:code,ds:data
data segment
	db 4,4,6,4,7,4;單詞的字母數
	db '          ';補齊
	db '1. file         '
	db '2. edit         '
	db '3. search       '
	db '4. view         '
	db '5. options      '
	db '6. help         '
data 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)的次數保存,C語言中是用棧來保存的
	
	mov cx,0
	mov cl,[di];內循環(huán)的次數	
	inner:;內層循環(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;恢復外層循環(huán)的次數
	loop outer
	
	mov ax,4c00h
	int 21h
code ends
end start
          2.使用棧實現(xiàn)【更好的方法】
assume cs:code,ds:data,ss:stack
data segment
	db 4,4,6,4,7,4;單詞的字母數
	db '          ';補齊
	db '1. file         '
	db '2. edit         '
	db '3. search       '
	db '4. view         '
	db '5. options      '
	db '6. help         '
data 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)的次數保存
	
	mov cx,0
	mov cl,[di];內循環(huán)的次數	
	inner:;內層循環(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;恢復外層循環(huán)的次數
	loop outer
	
	mov ax,4c00h
	int 21h
code ends
end start

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

本章對前面的所有內容是具有總結性的
計算機是進行數據處理、運算的機器,那么有兩個基本的問題就包含在其中:
    1.處理的數據在什么地方?
    2.要處理的數據有多長?
  這兩個問題,在機器指令中必須給以明確或隱含的說明,否則計算機就無法工作
8.1 bx、si、di、bp
    1.在8086CPU中,只有這4個寄存器(bx、bp、si、di)可以用在“[...]”
        中,用來進行內存單元的尋址
    2.在“[...]”中,這四個寄存器(bx、bp、si、di)可以單個出現(xiàn),
        或者只能以以下4種組合出現(xiàn)
        1.bx和si
        2.bx和di
        3.bp和si
        4.bp和di
    3.錯誤的用法
        mov ax,[bx+bp]
        mov ax,[si+di]
    4.只要在[...]中使用寄存器bp,則指令中沒有顯性給出段地址,那么
        段地址就默認在ss中,比如:
        mov ax,[bp]       ax的值為??臻g中,偏移地址為bp的內存單元
        mov ax,[bp+常數]      
        mov ax,[bp+si]
        mov ax,[bp+si+常數]
8.2 機器指令處理的數據所在的位置
    1.絕大部分機器指令進行數據處理的指令大致可分為3大類
        讀取、寫入、運算
    2.在機器指令這一層,并不關心數據的值是多少,而關心指令執(zhí)行前一刻
        它將要處理的數據所在的位置
    3.指令在執(zhí)行前,所要處理的數據可以在三個地方
        CPU內部(寄存器)、內存、端口
8.3 匯編語言中數據位置的表達
    匯編語言中用三個概念來表達數據的位置
      1.立即數
      2.寄存器
      3.段地址(SA)和偏移地址(EA)
        1.存放段地址的寄存器可以是默認的,
            既可以是默認在ds中,也可以是在ss中(使用bp寄存器)
        2.存放段地址的寄存器也可以顯性的給出
            mov ax,ds:[bp]
            mov ax,es:[bx]
            mov ax,ss:[bx+si]
            mov ax,cs:[bx+si+8]
8.4 尋址方式
8.5 指令要處理的數據有多長?
    1.8086CPU的指令,可以處理兩種尺寸的數據,byte和word
        所以在機器指令中要指明,指令進行的是字操作還是字節(jié)操作
    2.8086CPU確定數據長度的幾種方法
        1.通過寄存器名指明要處理的數據的尺寸
            mov al,1        ;指明數據是字節(jié)型的
            mov bx,ds:[0]   ;指明數據是字型的
        2.在沒有寄存器名存在的情況下,用操作符X ptr指明內存單元的長度
            X在匯編指令中可以為word或byte
            1.下面的指令中,用byte ptr指明了指令訪問的內存單元是字節(jié)型單元
              mov byte ptr ds:[0],1
              inc byte ptr [bx]
              inc byte ptr ds:[0]
              add byte ptr [bx],2
            2.下面的指令中,用word ptr指明了指令訪問的內存單元是字型單元
              mov word ptr ds:[0],1
              inc word ptr [bx]
              inc word ptr ds:[0]
              add word ptr [bx],2
        3.其他方法
            有些指令默認了訪問的內存單元類型
            pop、push指令,一定是字型數據
    3.在沒有寄存器參與的內存單元訪問指令中,用word ptr或者byte ptr
        顯性地指明所要訪問的內存單元的長度,是非常有必須要的
        否則,CPU無法得知所要訪問的單元是字單元,還是字節(jié)單元
8.6 尋址方式的綜合應用
8.7 div指令
    1.div是除法指令(division),使用div作除法的時候,要求
        1.除數:8位或16位,在寄存器或內存單元中
        2.被除數:(默認)放在AX或DX和AX中
        3.除數與被除數的相互關系
            除數  被除數
            8位   16位(AX)
            16位  32位(DX+AX)
        4.結果存放的位置
            運算  8位  16位
            商    AL   AX
            余數  AH   DX
    2.div指令格式
        1.div 寄存器
        2.div 內存單元
        除數是寄存器或內存單元的內容
    3.div指令示例
        1.div byte ptr ds:[0]   ;被除數是16位,除數是ds:[0]的內容(8位)
          含義:(al)=(ax)/((ds)*16+0)的商
               (ah)=(ax)/((ds)*16+0)的余數
       2.div word ptr es:[0]    ;被除數是32位,除數是es:[0]的內容(16位)
          含義:(ax)=[(dx)*10000H+(ax)]/((es)*16+0)的商
               (dx)=[(dx)*10000H+(ax)]/((es)*16+0)的余數   
    4.利用除法指令計算100001/100
        1.被除數100001大于65535,要使用dx和ax兩個寄存器聯(lián)合存放
          即說要進行的16位的除法
        2.除數100小于255,可以在一個8位寄存器中存放,但是,因為被除數是32位
          除數應為16位,所以要用16位寄存器來存放除法100
        3.現(xiàn)將100001表示成十六進制數:186A1H,即dx中存放1H,ax中存放86A1H
mov dx,1
mov ax,86A1H
mov bx,100
div bx  ;默認除數是16位的
8.8 偽指令dd
    1.dd是用來定義雙字型數據的
    2.示例
        data segment
            db 1    ;字節(jié)型數據
            dw 1    ;字型數據
            dd 1    ;雙字型數據
        data ends
    3.已知data段數據,用div計算data中第一個數據除以第二個數據后的結果,
        商存放在第3個數據的內存單元中
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中,把ax中的內容放入內存中
	
	mov ax,4c00h
	int 21h
code ends
end start
8.9 dup
    1.dup是一個操作符,在匯編語言中,同db、dw、dd等一樣,也是有編譯器識別處理的符號
    2.dup和db、dw、dd等數據定義偽指令配合使用的,用來進行數據的重復
    3.dup示例
        1.db 3 dup(0)   ;定義了3個字節(jié),他們的值都是0
        2.db 3 dup(0,1,2)   ;定義了9個字節(jié),他們是0、1、2、0、1、2、0、1、2
        3.db 3 dup('abc','ABC') ;定義了18個字節(jié),相當于db'abcABCabcABCabcABC'
    4.dup的使用格式
        db 重復的次數 dup(重復的字節(jié)型數據)
        dw 重復的次數 dup(重復的字型數據)
        dd 重復的次數 dup(重復的雙字型數據)
【實驗七】
沒調試成功
assume cs:code,ds:data,ss:stack,es:table

stack segment
	;空棧時,sp指向16
	dw 8 dup(0)
stack ends

data segment
	;表示21年的21個字符串
	;起始地址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個雙字型數據
	;起始地址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年公司雇員人數的21個字型數據
	;起止地址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

第九章 轉移指令的原理

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

第十章 call和ret指令

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

第十一章 標志寄存器

8086CPU的標志寄存器有16位,其中存儲的信息通常被稱為程序狀態(tài)字(PSW)
本章中的標志寄存器(以下簡稱為flag)是我們要學習的最有一個寄存器
flag寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息
8086CPU的flag寄存器的結構:
    1.flag的1、3、4、12、13、14、15位共7位在8086CPU中沒有使用,不具有任何含義
        而0、2、4、6、7、8、9、10、11位共9位都具有特殊的含義
    2.示意圖
11.1 ZF標志
    1.flag的第6位是ZF,零標志位。
        它記錄相關指令執(zhí)行后,
          1.結果為0,ZF=1
          2.結果不為0,ZF=0
    2.示例:
        mov ax,1
        sub ax,1
        指令執(zhí)行后,結果為0,則ZF=1
        mov ax,2
        sub ax,1
        指令執(zhí)行后,結果不為0,則ZF=0
    3.注意,在8086CPU的指令集中,有的指令的執(zhí)行會影響標志寄存器
        比如:add、sub、mul、div、inc、or、and等
        他們大都是運算指令(邏輯運算或者算術運算)
      有的指令的執(zhí)行對標志寄存器沒有影響,
        比如:mov、push、pop等,他們大都是傳送指令
11.2 PF標志
    flag的第2位是PF,奇偶標志位
    它記錄指令執(zhí)行后,結果的所有二進制位中1的個數
      1.為偶數,PF=1
      2.為奇數,PF=0
11.3 SF標志
    1.flag的第7位是SF,符號標志位
    2.它記錄指令執(zhí)行后
      1.結果為負。sf=1
      2.結果為正,sf=0
      sf標志,就是CPU對有符號數運算結果的一種記錄,它記錄數據的正負
        sf標志把所有數當作有符號數
        如果把數據當作無符號數運算,sf的值則沒有意義,雖然相關指令會影響它的值
    3.也就是說,CPU在執(zhí)行add等指令時,是必然要影響sf標志位的值
        至于我們需不需要這種影響,那就看我們如何看待指令所進行的運算    
11.4 CF標志   
    1.flag的第0位是CF,進位標志位
      一般請況下,在進行無符號數運算的時候,
        它記錄了運算結果的最高有效位向更高位的進位值,
          或從更高位的借位值
        代表假想的更高位
    2.CPU在運算時,不會丟棄進位值,而是記錄在一個特殊的寄存器的某一位上
        8086CPU就用flag的cf為來記錄這個進位值,借位也一樣
    3.在debug中的顯示
    4.無符號的時候產生的結果
11.5 OF標志
    flag中的第11位
    進行有符號數運算的時候,如果結果超過了機器所能表示的范圍稱為溢出
    1.這里所講的溢出,只是對有符號數運算而言
      就像進位只是相對于無符號數而言!
    2.一定要注意cf和of的區(qū)別
        當需要把機器碼看成有符號數則使用of
        當需要把機器碼看成無符號數則使用cf
11.6 adc標志
    adc是帶進位的加法指令,他利用了cf上記錄的進位值
    1.格式:adc 操作對象1,操作對象2
    2.功能:操作對象1=操作對象1+操作對象2+cf
      比如:adc ax,bx實現(xiàn)的功能是:
        (ax)=(ax)+(bx)+cf
    3.執(zhí)行adc指令的時候,加上的cf的值的含義,由adc指令前的指令決定
        也就是說,關鍵在于所加上的cf值是被什么指令設置的
    4.如果cf是被sub指令設置的,那么他的含義就是借位值
      如果是被add指令設置的,那么它的含義就是進位值
    5.下面的指令和add ax,bx具有相同的結果
        add al,bl
        adc ah,bh
        CPU提供adc指令的目的,就是來進行加法的第二步運算的
        adc指令和add指令相配合就可以對更大的數據進行加法運算
    【實驗:編程計算1EF000H+201000H,結果放在ax(高16位)和bx(低16位)中】
11.7 sbb標志
    sbb是帶借位減法指令,他利用了cf位上記錄的借位值
    1.格式:sbb 操作對象1,操作對象2
    2.功能:操作對象1=操作對象1-操作對象2-cf
    3.利用sbb指令,我們可以對任意大的數據進行減法運算
    4.sbb和adc是基于相同的思想設計的兩條指令,
        在應用思路上和adc類似
11.8 cmp標志
    1.cmp是比較指令,功能相當于減法指令,只是不保存結果
    2.cmp指令執(zhí)行后,將對標志寄存器產生影響
    3.其他相關指令通過識別這些被影響的標志寄存器,來得知比較結果
    4.cmp指令格式:cmp 操作對象1,操作對象2
    5.功能:計算操作對象1-操作對象2,但并不保存結果,僅僅根據計算結果對標志寄存器進行設置
    6.比如:cmp ax,ax
        做(ax)-(ax)的運算,結果為0,但并不在ax中保存,僅影響flag的相關位
        指令執(zhí)行后
        zf=1    ;結果為0
        pf=1    ;結果的1的個數為偶數
        sf=0    ;結果為正號
        cf=0    ;結果沒有產生進位或借位
        of=0    ;結果沒有溢出
    7.根據flag,判斷cmp指令的結果(無符號數)
    8.cmp既可以對無符號數進行比較,也可以對有符號數進行比較
        cmp 操作數1,操作數2   ;操作數1、操作數2都是有符號數
        1.of=0,說明沒有溢出,邏輯上真正結果的正負=實際結果的正負
            of=0,sf=1   則 操作數1比操作數2小
            of=0,sf=0   則 操作數1比操作數2大
        2.of=1,說明有溢出,邏輯上真正結果的正負與實際結果的正負相反
            of=1,sf=1   則 操作數1比操作數2大
            of=1,sf=0   則 操作數1比操作數2小
11.9 檢測比較結果的條件轉移指令
    1.這些條件轉移指令通常和cmp相配合使用
    2.因為cmp指令可以同時進行兩種比較,無符號數和有符號數的比較
      所以,這些轉移指令也分為兩種,即:
        1.根據【無符號數】的比較結果進行轉移的條件轉移指令,
          他們檢測zf、cf的值
        2.根據【有符號數】的比較結果進行轉移的條件轉移指令
          他們檢測sf、of和zf的值
    3.無符號比較,條件轉移指令小結【無符號,6個】
      1.je  等于則轉移     zf=1
      2.jne 不等于則轉移   zf=0
      3.jb  低于則轉移     cf=1      【b表示below】
      4.jnb 不低于則轉移   cf=0
      5.ja  高于則轉移     cf=0,zf=0【a表示above】
      6.jna 不高于則轉移   cf=1或zf=1
11.10 DF標志和串傳送指令
    1.flag的第10位DF,方向標志位
        在串處理指令(movsb,movsw)中,控制每次操作后si、di的增減
        df=0:每次操作后si,di遞增
        df=1:每次操作后si,di遞減
    2.格式: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指向的內存單元中的字節(jié)
            送入es:di中,然后根據標志寄存器df位的值,
              將si和di遞增或遞減
    4.movsw 傳送一個字
    5.movsb和movsw都和rep配合使用
        格式:rep movsb
        rep的作用根據cx的值,重復執(zhí)行后面的串傳送指令
    6.cld指令和std指令
        cld指令:將標志寄存器的df置為0【c:clear】
        std指令:將標志寄存器的df置為1【s:set】
11.11 pushf和popf
    pushf:將標志寄存器的值壓棧
    popf:從棧中彈出數據,送入標志寄存器中
    pushf和popf為直接訪問標志寄存器提供了一種方法
11.12 標志寄存器在debug中的表示

第十二章 內中斷

**引言和簡介
    1.中斷是CPU處理外部突發(fā)事件的一個重要技術
    2.它能使CPU在運行過程中對外部事件發(fā)出的中斷請求及時地進行處理,處理完成后
        又立即返回斷點,繼續(xù)進行CPU原來的工作。
    3.引起中斷的原因【即:發(fā)出中斷請求的來源叫作中斷源】
    4.根據中斷源的不同,可以把中斷分為:【軟件中斷】和【硬件中斷】兩大類
        而硬件中斷又可以分為【外部中斷】和【內部中斷】兩類
12.1 內中斷的產生
    1.外部中斷一般是指計算機外設發(fā)出的中斷請求,如:鍵盤中斷、打印機中斷、定時器中斷。
        外部中斷是可以屏蔽的中斷,也就是說,利用中斷控制器可以屏蔽這些外部設備的中斷請求。
    2.內部中斷是指因硬件出錯(如突然掉電、奇偶校驗錯等)或運算出錯(除數為零、運算溢出、單步中斷)所引起的中斷。
        內部中斷是不可屏蔽的中斷
    3.軟件中斷其實并不是真正的中斷,他們只是可被調用執(zhí)行的一般程序,
        DOS的系統(tǒng)功能調用(int 21h)都是軟件中斷
    4.CPU為了處理并發(fā)的中斷請求,規(guī)定了中斷的優(yōu)先權,優(yōu)先權由高到低的順序是:
        1.除法錯、溢出中斷、軟件中斷
        2.不可屏蔽中斷
        3.可屏蔽中斷
        4.單步中斷
12.2 中斷處理程序簡介
    1.CPU的設計者必須在中斷信息和其處理程序的入口地址之間建立某種聯(lián)系
        使得CPU根據中斷信息可以找到要執(zhí)行的處理程序。
    2.中斷信息中包含有表示中斷的類型碼。根據CPU的設計,中斷類型碼的作用就是用來定位中斷處理程序的。
    3.CPU用8位的中斷類型碼通過中斷向量表找到相應的中斷處理程序的入口地址
        即中斷類型碼是中斷向量在中斷向量表中的索引
12.3 中斷向量表【中斷向量表就是中斷向量的列表】
    1.中斷向量表在內存中保存,其中存放著256個【2^8,8位中斷類型碼】中斷源所對應的中斷處理程序的入口
        對于8086PC機,中斷向量表指定放在內存地址0處
    2.從0:0-0:03ffh的1024個字節(jié)【256*4,物理地址使用段地址和偏移地址存放,需要4個字節(jié)】中存放著中斷向量表
12.4 中斷過程
    1.可以用中斷類型碼,在中斷向量表中找到中斷處理程序的入口
        找到這個入口地址的最終目的是用它設置cs和ip,使CPU執(zhí)行中斷處理程序
    2.用中斷類型碼找到中斷向量,并用它設置cs和ip,這個工作時由CPU的硬件自動完成的
        CPU硬件完成這個工作的過程被稱為【中斷過程】
    3.中斷過程
        8086CPU的中斷過程
        1.(從中斷信息中)取得中斷類型碼
        2.標志寄存器的值入棧(保護標志位)
        3.設置標志寄存器的第8位TF和第9位IF設置為0(后面講解本步的目的)
        4.cs內容入棧
        5.ip內容入棧
        6.從內存地址為中斷類型碼*4和中斷類型碼*4+2的兩個子單元中
            讀取中斷處理程序的入口地址設置cs和ip
    4.使用匯編語言描述中斷過程,如下
        1.取得中斷類型碼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隨時都可能檢測到中斷信息,也就是說,CPU隨時都可能執(zhí)行中斷處理程序,
        所以,中斷處理程序必須一致存儲在內存某段空間中
    2.而中斷處理程序的入口地址,即【中斷向量】,必須存儲在對應的中斷向量表表項中
    3.中斷處理程序的編寫方法和子程序的比較類似,下面是常規(guī)的步驟
        1.保存用到的寄存器
        2.處理中斷
        3.恢復用到的寄存器
        4.用iret指令返回
        **iret指令的功能用匯編語法描述為
            pop ip
            pop cs
            popf
          iret通常和硬件自動完成的中斷過程配合使用
          iret指令執(zhí)行后,CPU回到執(zhí)行中斷處理程序前的執(zhí)行點繼續(xù)執(zhí)行程序
12.6 除法錯誤中斷的處理
    當CPU執(zhí)行div等除法指令的時候,如果發(fā)生了除法溢出錯誤,將產生中斷類型碼為0的終端信息
    CPU將檢測到這個信息,然后引發(fā)中斷程序,轉去執(zhí)行0號中斷對應的中斷處理程序
    例如:
        mov ax 1000h
        mov bh,1
        div bh
      此程序會產生溢出
      運行之后,會顯示
12.7 編程處理0號中斷
    現(xiàn)在重新編寫一個0號中斷處理程序,它的功能是在屏幕中間顯示“Welcome to here!”的廣告詞,然后返回到操作系統(tǒng)
    把中斷處理程序放到安全空間中
    中斷程序的框架
12.8 安裝
    計算中斷程序的長度:offset 標號1-offset 標號2
    在代碼段中存放數據        
12.9 do0
12.10 設置中斷向量
12.11 單步中斷
    如果檢測到標志寄存器的tf位為1,則產生單步中斷,引發(fā)中斷過程
12.12 響應中斷的特殊情況

第十三章 int指令

13.1 int指令
    1.int格式:int n  ;n為中斷類型碼
        它的功能是引發(fā)中斷過程
    2.CPU執(zhí)行int n指令,相當于引發(fā)一個n號中斷的中斷過程,執(zhí)行過程如下
        1.取中斷類型碼
        2.標志寄存器入棧,if=0,tf=0
        3.cs,ip入棧
        4.從此處轉去執(zhí)行n號中斷的中斷處理過程
    3.可以在程序中使用int指令調用任何一個中斷的中斷處理程序
        可以用int指令調用這些子程序,也可以自己編寫一些中斷處理程序供別人使用
13.2 編寫供應用程序調用的中斷例程
    【實例1】編寫、安裝中斷7ch的中斷例程,實現(xiàn)求一個word型數據的平方
        1.功能:求一word型數據的平方
        2.參數:(ax)=要計算的數據
        3.返回值:dx、ax中存放結果的高16位和低16位
        4.應用舉例:求2*3456^2
;程序1:調用中斷程序計算平方
code segment
    assume cs: code
start:
    mov ax,3456; (ax)=3456
    int 7ch;調用中斷7ch的中斷例程,計算ax中的數據的平方
    
    add ax,ax
    adc dx,dx ;存放結果,講結果乘以2
    mov ax,4c00h
    int 21h
code ends
end start

;程序2:編寫中斷程序
;程序2中要做三部分工作
;   1.編程實現(xiàn)求平方功能的程序
;   2.安裝程序,我們將其安裝在0:200處
;   3.設置中斷向量表,將程序的入口地址保存在7ch表項中,使其成為中斷7ch的中斷例程。
code segment
    assume cs:code
start:
    mov ax,cs
    mov ds,ax
    mov si,offset sqr					;設置ds:si指向源地址
    mov ax,0
    mov es,ax
    mov di,200h							;設置es:di指向目的地址
    mov cx,offset sqrend - offset sqr	;設置cx為傳輸長度
    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

  sqr:  
		mul ax
		iret
sqrend:	nop

code ends
end start
    【實例2】編寫、安裝中斷7ch的中斷例程,實現(xiàn)將一個全是字母,以0結尾的字符串,轉化為大寫。
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 對int、iret和棧的深入理解
    【問題】用7ch中斷例程完成loop指令的功能
        不要隨便修改sp,可以使用bp進行間接訪問
13.4 BIOS和DOS所提供的中斷例程
13.5 BIOS和DOS中斷例程的安裝過程
    1.開機后,CPU一加電,初始化(cs)=0ffffh,ip=0,自動從ffff:0單元開始執(zhí)行程序
        ffff:0處有一條跳轉指令,CPU執(zhí)行該指令后,轉去執(zhí)行bios中的硬件系統(tǒng)的檢測和初始化程序。
    2.初始化程序將建立bios所支持的中斷向量,即將bios提供的中斷例程的入口地址登記在中斷向量表中。
    3.硬件系統(tǒng)檢測和初始化完成后,調用19h進行操作系統(tǒng)的引導。從此將計算機交由操作系統(tǒng)控制。
    4.DOS啟動后,除完成其他工作外,還將它所提供的中斷例程裝入內存,并建立相應的中斷向量
13.6 BIOS中斷例程的應用
    1.int 10h中斷例程是bios提供的中斷例程,其中包含了多個和屏幕輸出相關的子程序
        一般來說,一個供程序員調用的中斷例程中,往往包括多個子程序,中斷例程內部用傳遞進來的參數來決定執(zhí)行哪個子程序
    2.bios和dos提供的中斷例程,都用ah來傳遞內部子程序的編號
13.7 DOS中斷例程應用
    int 21h中斷例程是dos提供的中斷例程,其中包含了dos提供給程序員造編程時調用的子程序
    【實驗13】
**介紹一本匯編語言的書《The Art of Assembly Language》

第十四章 端口

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

    mov ax,4c00h
    int 21h
code ends
end start
    【實驗十四】編程:以“年/月/日 時:分:秒”的格式,顯示當前日期和時間

第十五章 外中斷

**CPU除了有運算能力,還有I/O能力
15.1 接口芯片和端口
    1.在PC系統(tǒng)的接口卡和主板上,裝有各種接口芯片,這些外設接口芯片的內部裝有若干寄存器
        CPU將這些寄存器當做【端口】訪問
    2.外設的輸入不直接送入內存和CPU,而是送入相關的接口芯片的【端口】中
    3.CPU向外設的輸出也是要先送入【端口】中,再由相關芯片送入到外設
    4.CPU可以向外設輸出控制命令,這些控制命令也是先送到【端口】中,然后相關芯片根據命令進行相關工作
    5.可見:CPU與外部設備的交流是通過【端口】進行的
        CPU在執(zhí)行完當前指令后,可以檢測到發(fā)送過來的中斷信息,引發(fā)中斷過程,處理外設的輸入  
15.2 外中斷信息
    1.在PC系統(tǒng)中,外中斷源一共有兩類
        1.可屏蔽中斷
        2.不可屏蔽中斷
    2.可屏蔽中斷是CPU可以不響應的外中斷。CPU是否響應可屏蔽中斷
        要看標志寄存器的IF位的設置
        當CPU檢測到可屏蔽中斷信息時:
        1.若IF=1,則CPU在執(zhí)行完當前指令后相應中斷,引發(fā)中斷過程
        2.若IF=0,則不響應可屏蔽中斷
    3.可屏蔽中斷所引發(fā)的中斷過程,除在第一步的實現(xiàn)上與內中斷有所不同外,基本上和內中斷的中斷過程相同
    4.因為可屏蔽中斷信息來自于CPU外部,中斷類型碼是通過數據總線送入CPU的
        而內中斷的中斷碼是在CPU內部產生的
    5.IF設置為0的原因:在進入中斷處理程序后,禁止其他的可屏蔽中斷
        當然,如果中斷處理程序中需要處理可屏蔽中斷,可以用指令將IF設置為1
    6.8086CPU提供的設置IF的指令如下:
        sti         ;用于設置IF=1
        cli         ;用于設置IF=0
    7.不可屏蔽中斷是CPU必須相應的外中斷。
        當CPU檢測到不可屏蔽中斷信息時,則在執(zhí)行完當前指令后
        立即響應,應發(fā)中斷過程
    8.8086CPU不可屏蔽中斷的中斷類型碼固定為2,所以中斷過程中,不需要取中斷類型碼
    9.不可屏蔽中斷的中斷過程
        1.標志寄存器入棧,IF=0,TF=0
        2.CS,IP入棧
        3.(IP)=(8),(CS)=(0AH)   ;固定地址
    10.幾乎所有外中斷,都是可屏蔽中斷。當外設有需要處理的事件發(fā)生時
        相關芯片向CPU發(fā)出可屏蔽中斷信息。
        不可屏蔽中斷是系統(tǒng)中有必須處理的緊急情況發(fā)生時用來通知CPU的中斷信息,本門課程中,主要討論可屏蔽中斷
15.3 PC機鍵盤的處理過程
    1.下面看一個鍵盤輸入的處理過程,并以此來體會PC機處理外設輸入的基本方法
        1.鍵盤輸入
        2.引發(fā)9號中斷
        3.執(zhí)行int 9中斷例程
    2.PC機鍵盤的處理過程
        1.鍵盤上每一個鍵相當于一個開關,鍵盤中有一個芯片對鍵盤上的每一觸鍵的開關狀態(tài)進行掃描。
        2.按下一個鍵時,開關接通,該芯片就產生一個掃描碼,掃描碼說明按下的鍵在鍵盤上的位置
            掃描碼被送入主板上的相關接口芯片的寄存器中,該寄存器的端口地址為60H
        3.松開控下的鍵時,也產生一個掃描碼,掃描碼說明了松開的鍵在鍵盤上的位置,松開按鍵時
            產生的掃描碼也被送入60H端口中。
          一般按下一個鍵時,產生的掃描碼稱為通碼,松開一個鍵產生的掃描碼稱為斷碼
          掃描碼長度為一個字節(jié),通碼的第七位為0,斷碼的第七位為1
            即:斷碼=通碼+80H
          **BIOS提供了int9中斷例程,用來進行基本的鍵盤輸入處理,主要的工作如下:
            1.讀出60H端口中的掃描碼
            2.如果是字符鍵的掃描碼,將該掃描碼對應的字符碼(即:ASCII碼)送入內存中的BIOS鍵盤緩沖區(qū)
            3,如果是控制鍵和切換鍵的掃描碼,則將其轉變?yōu)闋顟B(tài)字節(jié),寫入內存中存儲狀態(tài)字節(jié)的單元
        4.鍵盤的輸入到達60H端口時,相關的芯片會向CPU發(fā)出中斷類型碼為9的可屏蔽中斷信息。
        5.CPU檢測到中斷信息后,如果IF=1,則相應中斷,同時將IF設置為0(不讓其他可屏蔽中斷進行干擾),引發(fā)中斷過程,轉去執(zhí)行int9中斷例程
    3.BIOS鍵盤緩沖區(qū)是系統(tǒng)啟動后,BIOS用于存放int9中斷例程所接受的鍵盤輸入的內存區(qū)
    4.該內存區(qū)可以存儲15個鍵盤輸入,int9中斷例程除了接收掃描碼外,還要產生和掃描碼對應的字符碼,
        所以在BIOS鍵盤緩沖區(qū)中,一個鍵盤輸入用一個字單元存放,高字節(jié)存放掃描碼,低字節(jié)存放字符碼
    5.0040:17單元存儲鍵盤狀態(tài)字節(jié),該字節(jié)記錄了控制鍵和切換鍵的狀態(tài)。鍵盤狀態(tài)字節(jié)各位記錄的信息如下:
15.4 編寫int9中斷例程,并安裝
    梳理鍵盤輸入的處理過程
        1.鍵盤產生掃描碼
        2.掃描碼送入60H端口
        3.一旦偵測到60H端口有動靜,引發(fā)9號中斷
        4.CPU執(zhí)行int9中斷例程處理輸入
      以上的過程,前三步都由硬件系統(tǒng)自動完成,能夠修改的只有第四步,修改int9中斷程序
    【任務演示】在屏幕中依次顯示“a”~“z”并可以讓人看清。在顯示過程中,按下Esc鍵后,該表顯示的顏色
;程序1:實現(xiàn)連續(xù)顯示“a”~“z”
;編程:在屏幕中間依次顯示“a”~“z”,并可以讓人看清。在顯示的過程中,按下'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:實現(xiàn)改變顏色
;編程:在屏幕中間依次顯示“a”~“z”,并可以讓人看清。在顯示的過程中,按下'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]			;將原來的int 9中斷例程的入口地址保存在ds:0、ds:2單元中

	mov word ptr es:[9*4],offset int9
	mov es:[9*4+2],cs	;在中斷向量表中設置新的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中斷例程的入口恢復為原來的地址

	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中斷例程是在進行鍵盤輸入之后,由系統(tǒng)自動調用
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] 				;對int指令進行模擬,調用原來的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 描述了單元長度的標號
    1.本章討論如何有效合理地組織數據,以及相關的編程技術
        1.前面的課程中,我們一直在代碼段中使用標號來標記指令、數據、段的起始地址
        2.還可以使用一種標號,這種標號不但可以表示內存單元的地址,還表示了內存單元的長度
            即:表示在此標號處的單元,是一個字節(jié)單元,還是字單元還是雙字單元
    2.例如
        1.標號1
        a : db 1,2,3,4,5,6,7,8
        b : dw 0
      此種標號只能標記地址
      此種加有“:”的地址標號,只能在代碼段中使用,不能在其他段中使用
        2.標號2
        a db 1,2,3,4,5,6,7,8    ;標號a,描述了地址code:0,和從這個地址開始,以后的內存單元都是字節(jié)單元
        b dw 0                  ;標號b描述了地址code:8,和從這個地址開始,以后的內存單元都是字單元
      此種標號既可以標記地址,也可以表示此標號處的單元
    3.使用這種包含單元長度的標號,可以使我們以簡潔的形式訪問內存中的數據
      這種標號此后稱為數據標號,它標記了存儲數據的單元的地址和長度
    4.數據標號的用法
        指令:mov ax,b             ;相當于:mov ax,cs:[8]
        指令:mov b,2              ;相當于:mov word ptr cs:[8],2
        指令:inc b                ;相當于:inc word ptr cs:[8]
        指令:mov al,a [si]        ;相當于:mov al,cs:0[si]
        指令:mov al,a[3]          ;相當于:mov al,cs:0[3]
        指令:mov al,a[bx+si+3]    ;相當于:mov al,cs:0[bx+si+3]
16.2 在其他段中使用數據標號
    1.注意,如果想在代碼段中,直接用數據標號訪問數據,
        則需要用偽指令assume 將標號所在的段和一個段寄存器聯(lián)系起來。
      否則編譯器在編譯的時候,無法確定標號的段地址在哪一個寄存器中。
    2. 當然,這種聯(lián)系是編譯器需要的,但絕對不是說,我們因為編譯器的工作需要,
        用assume指令將段寄存器和某個段相聯(lián)系,段寄存器中就會真的存放該段的地址。
    3.我們可以將數據標號當作數據來定義,此時,編譯器將標號所表示的地址當作數據的值。 
      1.把數據標號當做數據來定義時,使用【dw】定義數據
      比如:     data segment
              		a db 1,2,3,4,5,6,7,8
      			  	b dw 0
                    c dw a,b        ;數據標號c處存儲的兩個字型數據為標號a、b 的偏移地址。
      			 data ends
      數據標號c處存儲的兩個字型數據為標號a、b 的偏移地址。
      相當于:
                data segment
                    a db 1,2,3,4,5,6,7,8
                    b dw 0
                    c dw offset a, offset b
                 data ends
      2.把數據標號當做數據來定義時,使用【dd】定義數據
        再比如:
            data segment
                a db 1,2,3,4,5,6,7,8
                b dw 0
                c dd a,b    ;數據標號c處存儲的兩個雙字型數據為標號a的偏移地址和段地址、標號b 的偏移地址和段地址。
             data ends
        數據標號c處存儲的兩個雙字型數據為標號a的偏移地址和段地址、標號b 的偏移地址和段地址。
        相當于:
            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操作符,功能為取得某一標號的段地址。
                 data ends
            seg操作符,功能為取得某一標號的段地址。
16.3 直接定址表
    本節(jié)課,我們將使用“查表”的方法,編寫相關程序的技巧
    【任務】編寫子程序,以十六進制的形式在屏幕中間顯示給定的byte型數據
code segment
    assume cs:code
start:  
		mov al,0eh          ;al中存放了byte型數據

        call showbyte

        mov ax,4c00h
        int 21h

;子程序:
;用al傳送要顯示的數據

showbyte:
        jmp short show

        table db '0123456789ABCDEF'	;字符表

show:   push bx                 ;保護現(xiàn)場
        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位的值作為相對于table的偏移,取得對應的字符

        mov bx,0b800h
        mov es,bx
        mov es:[160*12+40*2],ah

        mov bl,al
        mov bh,0
        mov al,table[bx]		;用低4位的值作為相對于table的偏移,取得對應的字符
        
        mov es:[160*12+40*2+2],al

        pop es
        pop bx
        ret

code ends
end start
16.4 程序入口地址的直接定址表
    【編程】實現(xiàn)一個子程序setscreen,為顯示輸出提供如下功能:
        1.清屏
        2.設置前景色
        3.設置背景色
        4.向上滾動一行
    1.入口參數說明:
        1.用ah寄存器傳遞功能號
            0:清屏;1:設置前景色;2:設置背景色;3:向上滾動一行
        2.對于2、3號功能,用al傳遞顏色值
            al∈{0,1,2,3,4,5,6,7}
    2.各種功能如何實現(xiàn)
        1.清屏:
            將顯存中當前屏幕中的字符設為空格符;
        2.設置前景色:
            設置顯存中當前屏幕中處于奇地址的屬性字節(jié)的第0、1、2位;
            012位存放前景色
        3.設置背景色:
            設置顯存中當前屏幕中處于奇地址的屬性字節(jié)的第4、5、6位;
            456位存放背景色
        4.向上滾動一行:
            依次將第 n+1行的內容復制到第n行處:最后一行為空。  
;功能子程序1:清屏
sub1:      push bx      ;保護現(xiàn)場,調用子程序的時候,注意要保護現(xiàn)場,運行子程序的時候,可能會修改一些寄存器的值
        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          ;恢復現(xiàn)場
        pop cx
        pop bx
        ret 	
;功能子程序2:設置前景
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:設置背景色
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:向上滾動一行
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;共復制24行

sub4s:	
	push cx
	mov cx,160
	rep movsb 			;復制
  	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.可以將這些功能子程序的入口地址存儲在一個表中,他們在表中的位置和功能號相對應
;編程:實現(xiàn)一個子程序setscreen,為顯示輸出提供如下功能:
;(1) 清屏。
;(2) 設置前景色。
;(3) 設置背景色。
;(4) 向上滾動一行。
;
;入口參數說明:
;(1) 用 ah 寄存器傳遞功能號:0 表示清屏,1表示設置前景色,2 表示設置背景色,3 表示向上滾動一行;
;(2) 對于2、3號功能,用 al 傳送顏色值,(al) ∈{0,1,2,3,4,5,6,7}

setscreen: jmp short 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		;根據ah中的功能號計算對應子程序的地址在table表中的偏移
	
	call word ptr table[bx]	;調用對應的功能子程序,學會本句代碼,是本章節(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:設置前景色
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:設置背景色
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:向上滾動一行
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;共復制24行

sub4s:	
	push cx
	mov cx,160
	rep movsb 			;復制
  	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進入鍵盤輸入和磁盤讀寫

**引言
    1.大多數有用的程序都需要處理用戶的輸入,鍵盤輸入是最基本的輸入。
    2.程序和數據通常需要長期存儲,磁盤是最常用的存儲設備。
    3.BIOS 為這兩種外設的I/O提供了最基本的中斷例程,在本章中,我們對它們的應用和相關的問題進行討論。
17.1 int9中斷例程對鍵盤輸入的處理
    CPU 在9 號中斷發(fā)生后,執(zhí)行int 9中斷例程,從60h 端口讀出掃描碼,
        并將其轉化為相應的ASCII 碼或狀態(tài)信息,存儲在內存的指定空間(鍵盤緩沖區(qū)或狀態(tài)字節(jié))中。
17.2 使用int16h中斷例程讀取鍵盤緩沖區(qū)
    1.BIOS提供了int 16h 中斷例程供程序員調用。
    2.int 16h 中斷例程中包含的一個最重要的功能是從鍵盤緩沖區(qū)中讀取一個鍵盤輸入,該功能的編號為0。
    3.下面的指令從鍵盤緩沖區(qū)(緩沖區(qū)的最低位)中讀取一個鍵盤輸入,并且將其從緩沖區(qū)中刪除:
           mov ah,0
           int 16h
      結果:(ah)=掃描碼,(al)=ASCII碼。
    4.int 16h 中斷例程的 0 號功能,進行如下的工作:
        (1)檢測鍵盤緩沖區(qū)中是否有數據;
        (2)沒有則繼續(xù)做第1 步;(緩沖區(qū)隨時有可能輸入數據)
        (3)讀取緩沖區(qū)第一個字單元中的鍵盤輸入;
        (4)將讀取的掃描碼送入ah,ASCII 碼送入al;
        (5)將己讀取的鍵盤輸入從緩沖區(qū)中刪除。
    5.可見,B1OS 的int 9 中斷例程和int 16h 中斷例程是一對相互配合的程序,
        int 9 中斷例程向鍵盤緩沖區(qū)中寫入,
        int 16h 中斷例程從緩沖區(qū)中讀出。
      它們寫入和讀出的時機不同,int 9 中斷例程在有鍵按下的時候向鍵盤緩沖區(qū)中寫入數據;
      而int 16h 中斷例程是在應用程序對其進行調用的時候,將數據從鍵盤緩沖區(qū)中讀出。
    【編程】接收用戶的鍵盤輸入,輸入“r”,將屏幕上的字符設置為紅色:輸入“g”, 
        將屏幕上的字符設置為綠色;輸入“b ”,將屏幕上的字符設置為藍色。
;編程:
;接收用戶的鍵盤輸入,輸入“r”,將屏幕上的字符設置為紅色:輸入“g”, 
;將屏幕上的字符設置為綠色;輸入“b ”,將屏幕上的字符設置為藍色。
;A、B、C處的程序指令比較有技巧,請讀者自行分析
code segment
    assume cs:code
start:	
	mov ah,0
	int 16h				;int 16h 0號功能實現(xiàn)從鍵盤緩沖區(qū)讀取一個鍵盤輸入
		
	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      ;設置顏色
	or es:[bx],ah                       ;設置顏色
	add bx,2
	loop s

sret:	
	mov ax,4c00h
	int 21h
	
code ends
end start
17.3 字符串的輸入
    int 21h的0a號功能可以實現(xiàn)字符串的輸入
    也可以用int 16h,通過顯示鍵盤緩沖區(qū)中的內容,實現(xiàn)字符串的顯示
    1.使用int 16h顯示字符串程序的處理過程如下
        ① 調用int 16h讀取鍵盤輸入;
        ② 如果是字符,進入字符棧,顯示字符棧中的所有字符;繼續(xù)執(zhí)行① ;
        ③ 如果是退格鍵,從字符棧中彈出一個字符,顯示字符棧中的所有字符;繼續(xù)執(zhí)行① ;
        ④ 如果是Enter 鍵,向字符棧中壓入0,返回。
    2.子程序:字符棧的入棧、出棧和顯示
        參數說明
        (ah)=功能號,0表示入棧,1表示出棧,2表示顯示;
            ds : si 指向字符??臻g;
        對于0 號功能:(al)=入棧字符;
        對于1 號功能:(al)=返回的字符;
        對于2 號功能:(dh)、(dl) =字符串在屏幕上顯示的行、列位置。
;使用int 16h顯示字符串的子程序:字符棧
;最基本的字符串輸入程序,需要具備下面的功能:
;(1) 在輸入的同時需要顯示這個字符串;
;(2)一般在輸入回車符后,字符串輸入結束;
;(3)能夠刪除已經輸入的字符。

;編寫一個接收字符串的輸入子程序,實現(xiàn)上面三個基本功能。
;因為在輸入的過程中需要顯示,子程序的參數如下:
;	(dh)、(dl)=字符串在屏幕上顯示的行、列位置;
;	ds:si 指向字符串的存儲空間,字符串以O 為結尾符。

;功能子程序實現(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 應用int13h中斷例程對鍵盤進行讀寫
    1.磁盤的實際訪問由磁盤控制器進行,我們可以通過控制磁盤控制器來訪問磁盤。
    2.注意,我們只能以扇區(qū)為單位對磁盤進行讀寫。
      在讀寫扇區(qū)的時候,要給出面號、磁道號和扇區(qū)號。面號和磁道號從0開始,而扇區(qū)號從1開始。
    3.BIOS提供了對扇區(qū)進行讀寫的中斷例程,這些中斷例程完成了許多復雜的和硬件相關的工作。
    4.我們可以通過調用BIOS中斷例程來訪問磁盤。
        BIOS 提供的訪問磁盤的中斷例程為int 13h 。
      如下,讀取0面0道1扇區(qū)的內容到0:200: 
        返回參數:
            操作成功:(ah)=0,(al)=讀入的扇區(qū)數
            操作失?。海╝h)=出錯代碼
      將0:200中的內容寫入0面0道1扇區(qū)示例
        返回參數:
            操作成功: (ah)=0,(al)=寫入的扇區(qū)數
            操作失?。?(ah)=出錯代碼
    5.注意:使用int 13h 中斷例程對軟盤進行讀寫。直接向磁盤扇區(qū)寫入數據是很危險的,
       很可能覆蓋掉重要的數據。
    【編程】將當前屏幕的內容保存在磁盤上
        分析:1 屏的內容占4000個字節(jié),需要8 個扇區(qū)(一個扇區(qū)512B),我們用0面0道的1~8扇區(qū)存儲顯存中的內容。
code segment
    assume cs:code
start:	
	mov ax,0b800h
	mov es,ax
	mov bx,0	;es:bx	指向將寫入磁盤的數據的內存區(qū)

	mov al,8 	;寫入的扇區(qū)數
	mov ch,0 	;磁道號,從0開始
	mov cl,1 	;扇區(qū)號 從1開始
	mov dl,0 	;驅動器號0:軟驅A,  1:軟驅B,硬盤從80h開始, 80h:硬盤C,81h:硬盤D
	mov dh,0 	;磁頭號,(對于軟盤即面號,因為一個面用一個磁頭來讀寫)
	mov ah,3	;傳遞 int 13h 寫入數據的功能號
	int 13h

			;返回參數
			;操作成功:(ah) = 0,(al) = 寫入的扇區(qū)數
			;操作失敗:(ah) = 出錯代碼
	
return:	
	mov ax,4c00h
	int 21h

code ends
end start
    【實驗17和課程設計2】
        課程設計1在第十章

綜合研究

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

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多